Local branch had diverged from origin/master (sibling commits on the same base). Rewrote local history linearly on top of origin/master, folding in all local content: exa/hot-platform discovery hunters, leaks ledger, nightly sweep orchestrator, updated .gitignore and skill docs, plus the latest hunt output and vault state. Remote-only files (vault keys, channel scripts) were restored rather than dropped, so the resulting tree is a full union of both sides.
1 line
4.3 MiB
Plaintext
1 line
4.3 MiB
Plaintext
{"repo": "chathurangabw/hermes-security-dev", "files": {"cli-config.yaml.example": {"533d633ab9d18ce0f05bc44d4077576b69dc58e8": {"t": 1785746833.6439798, "n": 85496, "h": "d012a8e334c1d4b6", "c": "# Hermes Agent CLI Configuration\n# Copy this file to cli-config.yaml and customize as needed.\n# This file configures the CLI behavior. Environment variables in .env take precedence.\n\n# =============================================================================\n# Model Configuration\n# =============================================================================\nmodel:\n # Default model to use (can be overridden with --model flag)\n # Both \"default\" and \"model\" work as the key name here.\n default: \"anthropic/claude-opus-4.6\"\n \n # Inference provider selection:\n # \"auto\" - Auto-detect from credentials (default)\n # \"openrouter\" - OpenRouter (requires: OPENROUTER_API_KEY or OPENAI_API_KEY)\n # \"nous\" - Nous Portal OAuth (requires: hermes auth add nous)\n # \"nous-api\" - Nous Portal API key (requires: NOUS_API_KEY)\n # \"anthropic\" - Direct Anthropic API (requires: ANTHROPIC_API_KEY)\n # \"openai-codex\" - OpenAI Codex (requires: hermes auth)\n # \"copilot\" - GitHub Copilot / GitHub Models (requires: GITHUB_TOKEN)\n # \"gemini\" - Use Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n # \"zai\" - Use z.ai / ZhipuAI GLM models (requires: GLM_API_KEY)\n # \"kimi-coding\" - Kimi / Moonshot AI (requires: KIMI_API_KEY)\n # \"minimax\" - MiniMax global (requires: MINIMAX_API_KEY)\n # \"minimax-cn\" - MiniMax China (requires: MINIMAX_CN_API_KEY)\n # \"huggingface\" - Hugging Face Inference (requires: HF_TOKEN)\n # \"nvidia\" - NVIDIA NIM / build.nvidia.com (requires: NVIDIA_API_KEY)\n # \"xiaomi\" - Xiaomi MiMo (requires: XIAOMI_API_KEY)\n # \"arcee\" - Arcee AI Trinity models (requires: ARCEEAI_API_KEY)\n # \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY — https://ollama.com/settings)\n # \"deepinfra\" - DeepInfra (requires: DEEPINFRA_API_KEY)\n # \"kilocode\" - KiloCode gateway (requires: KILOCODE_API_KEY)\n # \"azure-foundry\" - Microsoft Foundry / Azure OpenAI (API key or Entra ID)\n # \"lmstudio\" - LM Studio local server (optional: LM_API_KEY, defaults to http://127.0.0.1:1234/v1)\n #\n # Local servers (LM Studio, Ollama, vLLM, llama.cpp):\n # \"custom\" - Any other OpenAI-compatible endpoint. Set base_url below.\n # Aliases: \"ollama\", \"vllm\", \"llamacpp\" all map to \"custom\".\n # LM Studio is first-class and uses provider: \"lmstudio\".\n # It works with both no-auth and auth-enabled server modes.\n #\n # Can also be overridden for a single invocation with the --provider flag.\n provider: \"auto\"\n \n # API configuration (falls back to OPENROUTER_API_KEY env var)\n # api_key: \"your-key-here\" # Uncomment to set here instead of .env\n base_url: \"https://openrouter.ai/api/v1\"\n\n # Azure Foundry keyless auth example:\n # provider: \"azure-foundry\"\n # base_url: \"https://<resource>.openai.azure.com/openai/v1\"\n # auth_mode: \"entra_id\" # DefaultAzureCredential: az login, managed identity, workload identity, etc.\n # default: \"gpt-4o\" # Deployment/model name\n # entra:\n # scope: \"https://ai.azure.com/.default\" # Optional; this is the default.\n\n # ── Token limits — two settings, easy to confuse ──────────────────────────\n #\n # context_length: TOTAL context window (input + output tokens combined).\n # Controls when Hermes compresses history and validates requests.\n # Leave unset — Hermes auto-detects the correct value from the provider.\n # Set manually only when auto-detection is wrong (e.g. a local server with\n # a custom num_ctx, or a proxy that doesn't expose /v1/models).\n #\n # context_length: 131072\n #\n # max_tokens: OUTPUT cap — maximum tokens the model may generate per response.\n # Unrelated to how long your conversation history can be.\n # The OpenAI-standard name \"max_tokens\" is a misnomer; Anthropic's native\n # API has since renamed it \"max_output_tokens\" for clarity.\n # Leave unset to use the model's native output ceiling (recommended).\n # Set only if you want to deliberately limit individual response length.\n #\n# max_tokens: 8192\n\n # ── Custom request headers (optional) ─────────────────────────────────────\n #\n # default_headers: extra HTTP headers sent on every request to an\n # OpenAI-compatible endpoint. User values take precedence over the\n # provider/SDK defaults, so this is the supported way to override the\n # OpenAI Python SDK's identifying headers (User-Agent: OpenAI/Python ...,\n # X-Stainless-*) when a custom provider sits behind a gateway/WAF that\n # rejects them — e.g. an upstream that returns \"502 Upstream access\n # forbidden\" for the SDK default User-Agent but accepts a plain one.\n # Applies on the OpenAI wire only (not native Anthropic / Bedrock).\n #\n # default_headers:\n # User-Agent: \"curl/8.7.1\"\n #\n # extra_headers: accepted as an alias of default_headers (merged, with\n # extra_headers winning when both are set) — matches the per-provider\n # extra_headers key below.\n #\n # Per-provider variant: named providers / custom_providers entries accept an\n # extra_headers dict scoped to that endpoint only — for reverse proxies,\n # gateways, or custom auth (e.g. Cloudflare Access service tokens).\n # Merged onto SDK/provider defaults with the entry's values winning.\n # Header values are treated as secrets and are never logged.\n #\n # providers:\n # my-proxy:\n # base_url: \"https://llm.internal.example.com/v1\"\n # key_env: \"MY_PROXY_API_KEY\"\n # extra_headers:\n # CF-Access-Client-Id: \"xxxx.access\"\n # CF-Access-Client-Secret: \"${CF_ACCESS_SECRET}\"\n # X-Client-Name: \"hermes-agent\"\n\n# Named provider overrides (optional)\n# Use this for per-provider request timeouts, non-stream stale timeouts,\n# and per-model exceptions.\n# Applies to the primary turn client on every api_mode (OpenAI-wire, native\n# Anthropic, and Anthropic-compatible providers), the fallback chain, and\n# client rebuilds during credential rotation. For OpenAI-wire chat\n# completions (streaming and non-streaming) the configured value is also\n# used as the per-request ``timeout=`` kwarg so it wins over the legacy\n# HERMES_API_TIMEOUT env var (which still applies when no config is set).\n# ``stale_timeout_seconds`` controls the non-streaming stale-call detector and\n# wins over the legacy HERMES_API_CALL_STALE_TIMEOUT env var. Leaving these\n# unset keeps the legacy defaults (HERMES_API_TIMEOUT=1800s,\n# HERMES_API_CALL_STALE_TIMEOUT=90s, native Anthropic 900s). The\n# implicit non-stream stale detector is auto-disabled for local endpoints\n# and can scale upward for very large contexts.\n#\n# Not currently wired for AWS Bedrock (bedrock_converse + AnthropicBedrock\n# SDK paths) — those use boto3 with its own timeout configuration.\n#\n# providers:\n# ollama-local:\n# request_timeout_seconds: 300 # Longer timeout for local cold-starts\n# stale_timeout_seconds: 900 # Explicitly re-enable stale detection on local endpoints\n# anthropic:\n# request_timeout_seconds: 30 # Fast-fail cloud requests\n# models:\n# claude-opus-4.6:\n# timeout_seconds: 600 # Longer timeout for extended-thinking Opus calls\n# openai-codex:\n# models:\n# gpt-5.4:\n# stale_timeout_seconds: 1800 # Longer non-stream stale timeout for slow large-context turns\n\n# =============================================================================\n# OpenRouter Provider Routing (only applies when using OpenRouter)\n# =============================================================================\n# Control how requests are routed across providers on OpenRouter.\n# See: https://openrouter.ai/docs/guides/routing/provider-selection\n#\n# provider_routing:\n# # Sort strategy: \"price\" (default), \"throughput\", or \"latency\"\n# # Append :nitro to model name for a shortcut to throughput sorting.\n# sort: \"throughput\"\n#\n# # Only allow these providers (provider slugs from OpenRouter)\n# # only: [\"anthropic\", \"google\"]\n#\n# # Skip these providers entirely\n# # ignore: [\"deepinfra\", \"fireworks\"]\n#\n# # Try providers in this order (overrides default load balancing)\n# # order: [\"anthropic\", \"google\", \"together\"]\n#\n# # Require providers to support all parameters in your request\n# # require_parameters: true\n#\n# # Data policy: \"allow\" (default) or \"deny\" to exclude providers that may store data\n# # data_collection: \"deny\"\n\n# =============================================================================\n# OpenRouter Response Caching (only applies when using OpenRouter)\n# =============================================================================\n# Cache identical API responses at the OpenRouter edge for free instant replays.\n# When enabled, identical requests (same model, messages, parameters) return\n# cached responses with zero billing. Separate from Anthropic prompt caching.\n# See: https://openrouter.ai/docs/guides/features/response-caching\n#\n# openrouter:\n# response_cache: true # Enable response caching (default: true)\n# response_cache_ttl: 300 # Cache TTL in seconds, 1-86400 (default: 300)\n\n# =============================================================================\n# Git Worktree Isolation\n# =============================================================================\n# When enabled, each CLI session creates an isolated git worktree so multiple\n# agents can work on the same repo concurrently without file collisions.\n# Equivalent to always passing --worktree / -w on the command line.\n#\n# worktree: true # Always create a worktree when in a git repo\n# worktree: false # Default — only create when -w flag is passed\n#\n# By default a new worktree branches from the freshly-fetched remote tip\n# (the current branch's upstream, else the remote's default branch) so it\n# starts current with the project instead of from the local clone's\n# (possibly stale) HEAD. Set worktree_sync: false to branch from local HEAD\n# instead — useful when offline or when you deliberately want the clone's\n# exact current state as the base.\n#\n# worktree_sync: true # Default — branch from the fetched remote tip\n# worktree_sync: false # Branch from local HEAD (offline / pinned base)\n\n# =============================================================================\n# Terminal Tool Configuration\n# =============================================================================\n# Choose ONE of the following terminal configurations by uncommenting it.\n# The terminal tool executes commands in the specified environment.\n\n# -----------------------------------------------------------------------------\n# OPTION 1: Local execution (default)\n# Commands run directly on your machine in the current directory\n# -----------------------------------------------------------------------------\n# Working directory behavior:\n# - CLI (`hermes` command): Uses \".\" (current directory where you run hermes)\n# - Gateway/messaging/cron: Uses terminal.cwd here; legacy .env cwd values are deprecated\nterminal:\n backend: \"local\"\n cwd: \".\" # For local backend: \".\" = current directory. Ignored for remote backends unless a backend documents otherwise.\n timeout: 180\n # HOME policy for tool subprocesses:\n # auto - default: host uses your real HOME; containers use HERMES_HOME/home\n # real - force your real OS-user HOME\n # profile - force HERMES_HOME/home for strict per-profile CLI config isolation\n home_mode: \"auto\"\n docker_mount_cwd_to_workspace: false # SECURITY: off by default. Opt in to mount the launch cwd into Docker /workspace.\n lifetime_seconds: 300\n # sudo_password: \"hunter2\" # Optional: pipe a sudo password via sudo -S. SECURITY WARNING: plaintext.\n # sudo_password: \"\" # Explicit empty password: try empty and never open the interactive sudo prompt.\n\n# -----------------------------------------------------------------------------\n# OPTION 2: SSH remote execution\n# Commands run on a remote server - agent code stays local (sandboxed)\n# Great for: keeping agent isolated from its own code, using powerful remote hardware\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"ssh\"\n# cwd: \"/home/myuser/project\" # Path on the REMOTE server\n# timeout: 180\n# lifetime_seconds: 300\n# ssh_host: \"my-server.example.com\"\n# ssh_user: \"myuser\"\n# ssh_port: 22\n# ssh_key: \"~/.ssh/id_rsa\" # Optional - uses ssh-agent if not specified\n\n# -----------------------------------------------------------------------------\n# OPTION 3: Docker container\n# Commands run in an isolated Docker container\n# Great for: reproducible environments, testing, isolation\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"docker\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /)\n# timeout: 180\n# lifetime_seconds: 300\n# docker_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# docker_mount_cwd_to_workspace: true # Explicit opt-in: mount your launch cwd into /workspace\n# # Optional: run the container as your host user's uid:gid so files written\n# # into bind-mounted dirs are owned by you, not root. Drops SETUID/SETGID\n# # caps too since no gosu privilege drop is needed. Leave off if your\n# # chosen docker_image expects to start as root.\n# docker_run_as_host_user: true\n# # Optional: explicitly forward selected env vars into Docker.\n# # These values come from your current shell first, then ~/.hermes/.env.\n# # Warning: anything forwarded here is visible to commands run in the container.\n# docker_forward_env:\n# - \"GITHUB_TOKEN\"\n# - \"NPM_TOKEN\"\n# # Optional: extra flags passed verbatim to docker run (appended after security defaults).\n# # Useful for adding capabilities (e.g. apt installs needing SETUID) or custom options.\n# # Example: add a Linux capability not included by default\n# # docker_extra_args:\n# # - \"--cap-add\"\n# # - \"SETUID\"\n\n# -----------------------------------------------------------------------------\n# OPTION 4: Singularity/Apptainer container\n# Commands run in a Singularity container (common in HPC environments)\n# Great for: HPC clusters, shared compute environments\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"singularity\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# singularity_image: \"docker://nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 5: Modal cloud execution\n# Commands run on Modal's cloud infrastructure\n# Great for: GPU access, scalable compute, serverless execution\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"modal\"\n# cwd: \"/workspace\" # Path INSIDE the sandbox (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# modal_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 6: Daytona cloud execution\n# Commands run in Daytona cloud sandboxes\n# Great for: Cloud dev environments, persistent workspaces, team collaboration\n# Requires: pip install daytona, DAYTONA_API_KEY env var\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"daytona\"\n# cwd: \"~\"\n# timeout: 180\n# lifetime_seconds: 300\n# daytona_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# container_disk: 10240 # Daytona max is 10GB per sandbox\n\n#\n# --- Container resource limits (docker, singularity, modal, daytona -- ignored for local/ssh) ---\n# These settings apply to all container backends. They control the resources\n# allocated to the sandbox and whether its filesystem persists across sessions.\n container_cpu: 1 # CPU cores\n container_memory: 5120 # Memory in MB (5120 = 5GB)\n container_disk: 51200 # Disk in MB (51200 = 50GB)\n container_persistent: true # Persist filesystem across sessions (false = ephemeral)\n\n# -----------------------------------------------------------------------------\n# SUDO SUPPORT (works with ALL backends above)\n# -----------------------------------------------------------------------------\n# Add sudo_password to any terminal config above to enable sudo commands.\n# The password is piped via `sudo -S`. Works with local, ssh, docker, etc.\n#\n# SECURITY WARNING: Password stored in plaintext!\n#\n# INTERACTIVE PROMPT: If sudo_password is unset and the CLI is running,\n# you'll be prompted to enter your password when sudo is needed:\n# - 45-second timeout (auto-skips if no input)\n# - Press Enter to skip (command fails gracefully)\n# - Password is hidden while typing\n# - Password is cached for the session\n#\n# EMPTY PASSWORDS: Setting sudo_password to an explicit empty string is different\n# from leaving it unset. Hermes will try an empty password via `sudo -S` and\n# will not open the interactive prompt. This is useful for passwordless sudo,\n# Touch ID sudo setups, and environments where prompting is just noise.\n#\n# ALTERNATIVES:\n# - SSH backend: Configure passwordless sudo on the remote server\n# - Containers: Run as root inside the container (no sudo needed)\n# - Local: Configure /etc/sudoers for specific commands\n#\n# Example (add to your terminal section):\n# sudo_password: \"your-password-here\"\n\n# =============================================================================\n# Security Scanning (tirith)\n# =============================================================================\n# Optional pre-exec command security scanning via tirith.\n# Detects homograph URLs, pipe-to-shell, terminal injection, env manipulation.\n# Install: brew install sheeki03/tap/tirith\n# Docs: https://github.com/sheeki03/tirith\n#\n# security:\n# tirith_enabled: true # Enable/disable tirith scanning\n# tirith_path: \"tirith\" # Path to tirith binary (supports ~ expansion)\n# tirith_timeout: 5 # Scan timeout in seconds\n# tirith_fail_open: true # Allow commands if tirith unavailable\n\n# =============================================================================\n# Browser Tool Configuration\n# =============================================================================\nbrowser:\n # Inactivity timeout in seconds - browser sessions are automatically closed\n # after this period of no activity between agent loops (default: 120 = 2 minutes)\n inactivity_timeout: 120\n\n# =============================================================================\n# Tool Loop Guardrails\n# =============================================================================\n# Soft warnings are enabled by default. They append guidance to repeated failed\n# or non-progressing tool results but still let the tool execute. Hard stops are\n# opt-in circuit breakers for autonomous/cron sessions where stopping a loop is\n# preferable to spending the full iteration budget.\ntool_loop_guardrails:\n warnings_enabled: true\n hard_stop_enabled: false\n warn_after:\n exact_failure: 2\n same_tool_failure: 3\n idempotent_no_progress: 2\n hard_stop_after:\n exact_failure: 5\n same_tool_failure: 8\n idempotent_no_progress: 5\n\n# =============================================================================\n# Context Compression (Auto-shrinks long conversations)\n# =============================================================================\n# When conversation approaches model's context limit, middle turns are\n# automatically summarized to free up space while preserving important context.\n#\n# HOW IT WORKS:\n# 1. Tracks actual token usage from API responses (not estimates)\n# 2. When prompt_tokens >= threshold% of model's context_length, triggers compression\n# 3. Protects first 3 turns (system prompt, initial request, first response)\n# 4. Protects last N turns (default 20 messages = ~10 full turns of recent context)\n# 5. Summarizes middle turns using a fast/cheap model\n# 6. Inserts summary as a user message, continues conversation seamlessly\n#\n# Post-compression tail budget is target_ratio × threshold × context_length:\n# 200K context, threshold 0.50, ratio 0.20 → 20K tokens of recent tail preserved\n# 1M context, threshold 0.50, ratio 0.20 → 100K tokens of recent tail preserved\n#\ncompression:\n # Enable automatic context compression (default: true)\n # Set to false if you prefer to manage context manually or want errors on overflow\n enabled: true\n\n # Opt-in compression progress notices on chat platforms (default: false).\n # By design, routine automatic compression is SILENT on human-facing chat\n # gateways (Telegram, Discord, Slack, ...) — it happens in the background\n # with server-side logging only. Set true to also deliver the routine\n # progress statuses (compacting started, preflight/pre-API compression,\n # idle compaction, retry progress, and the compaction-complete notice) to\n # chat platforms. Unrelated operational noise (auxiliary model failures,\n # provider retry chatter) stays suppressed either way, and compression\n # FAILURE notices + manual /compress feedback are always visible\n # regardless of this setting. (#52995)\n progress_notices: false\n \n # Trigger compression at this % of model's context limit (default: 0.50 = 50%)\n # Lower values = more aggressive compression, higher values = compress later\n # Models with context windows below 512K are floored at 0.75 (raise-only) so\n # compaction doesn't fire with half the window still free; set above 0.75 to override.\n threshold: 0.50\n\n # Per-model threshold overrides: keys are substring-matched against the model\n # name (longest match wins). Useful when some models need different compaction\n # points — e.g. a 1M-context model can compress later (0.30) while a 128K\n # model needs to compress earlier (0.60). The small-context floor (75% for\n # <512K models) still applies on top of per-model overrides.\n # model_thresholds:\n # \"glm-5.2\": 0.40\n # \"claude-sonnet\": 0.35\n # \"gpt-5\": 0.30\n\n # Optional absolute token cap for the compression trigger (default: null = disabled).\n # When set, compression fires at the LOWER of the ratio-based threshold and this\n # absolute token count — first-fires-wins. It never fires later than this count\n # regardless of which model is active (useful when switching between models with\n # very different context windows). Clamped to the model's context length at\n # apply-time, so a cap above the window is a no-op (ratio-based threshold wins).\n # Survives model switches and fallback activations.\n # threshold_tokens: 200000\n\n # Existing Codex gpt-5.5 behavior: raise Hermes' compaction trigger to 85%\n # for the ChatGPT Codex OAuth route. Set false to opt back down to threshold.\n codex_gpt55_autoraise: true\n\n # Fraction of the threshold to preserve as recent tail (default: 0.20 = 20%)\n # e.g. 20% of 50% threshold = 10% of total context kept as recent messages.\n # Summary output is separately capped at 12K tokens (Gemini output limit).\n # Range: 0.10 - 0.80\n target_ratio: 0.20\n\n # Number of most-recent messages to always preserve (default: 20 ≈ 10 full turns)\n # Higher values keep more recent conversation intact at the cost of more aggressive\n # compression of older turns.\n protect_last_n: 20\n\n # Minimum number of REAL (actionable) user messages guaranteed to survive in\n # the uncompressed tail (default: 1 = the existing single last-user anchor,\n # behavior-preserving). Raise to e.g. 3 to keep the last 3 real user turns\n # verbatim even when bulky tool outputs fill the tail token budget — blank\n # platform echoes, compaction handoffs, and synthetic continuation rows never\n # count toward N. The tail can exceed the token budget when this pulls the\n # cut back; the guarantee wins over the budget by design.\n min_tail_user_messages: 1\n\n # Compression retry rounds before a turn gives up with \"max compression\n # attempts reached\" (default: 3, same as the previous hardcoded value).\n # Raise (e.g. 6) for tool-schema-heavy sessions where 3 rounds cannot bring\n # the request estimate under the threshold. Validated >= 1, hard cap 10.\n max_attempts: 3\n\n # Codex app-server (codex CLI runtime) thread-compaction mode. The codex\n # agent owns the real thread context on this runtime, so Hermes' summarizer\n # cannot shrink it — compaction goes through the app server instead.\n # native = let Codex decide when to compact its own thread (default)\n # hermes = let Hermes threshold trigger Codex thread/compact/start\n # off = Hermes will not auto-trigger compaction; Codex may still compact natively\n codex_app_server_auto: native\n\n # Number of non-system messages to protect at the head of the transcript, in\n # ADDITION to the system prompt (which is always implicitly protected).\n # Head messages are NEVER summarized — they survive every compression\n # indefinitely. This gives stable early context for short/medium sessions,\n # but in long-running sessions that rely on rolling compaction the pinned\n # opening turns may not match how you want the session framed over time.\n # Set to 0 to preserve ONLY the system prompt (plus the rolling summary\n # and recent tail) — the cleanest configuration for long-running sessions.\n # Default 3 preserves the system prompt plus the first three non-system\n # head messages, matching the pre-feature behaviour.\n protect_first_n: 3\n\n # Idle compaction (default: 0 = disabled). When > 0, a session that resumes\n # after at least this many seconds of inactivity compacts its accumulated\n # history up front, before the first reply, so a long-lived thread you come\n # back to later doesn't re-read its full stale context on every turn.\n # Time-based, so it complements (does not replace) the size-based `threshold`\n # above. It is skipped when the context is already small (at or below the\n # post-compression target = threshold × target_ratio), so it never wastes a\n # summarization on a short idle thread. Example: 1800 = compact after 30 min idle.\n idle_compact_after_seconds: 0\n\n # Proactive tool-result prune (default: 0 = disabled). Opt-in token trigger\n # for a deterministic, no-LLM prune of OLD tool-result payloads, run\n # independently of `threshold` above. On large-window models (512K/1M) the\n # ratio threshold rarely fires, so bulky tool outputs (terminal dumps, file\n # reads, web extracts) ride along in history and get re-billed every turn.\n # When re-sent history exceeds this many tokens, the prune dedupes identical\n # results, summarizes older oversized ones, and truncates large tool-call\n # arguments — protecting the most recent `protect_last_n` messages and never\n # calling the model. Try 48000 to enable. Built-in compressor engine only;\n # other context engines inherit a safe no-op.\n # NOTE: a committed prune rewrites already-sent history, which invalidates\n # the provider's prompt-cache prefix — the min_reclaim gate below keeps\n # those cache breaks episodic (like a compression boundary) instead of\n # per-turn.\n proactive_prune_tokens: 0\n\n # The prune's summarize pass only touches tool results larger than this many\n # characters (clamped to >= 200 so a generated summary can't be\n # re-summarized). Default 8000.\n proactive_prune_min_result_chars: 8000\n\n # A proactive prune only COMMITS when it reclaims at least this many tokens\n # (measured on the pruned output). This is the prompt-cache hysteresis gate:\n # one meaningful, amortized cache break per batch of stale tool output\n # instead of a tiny break on every tool iteration. 0 = commit any non-zero\n # prune. Default 4096.\n proactive_prune_min_reclaim_tokens: 4096\n\n # To pin a specific model/provider for compression summaries, use the\n # auxiliary section below (auxiliary.compression.provider / model).\n\n# =============================================================================\n# Anthropic prompt caching TTL\n# =============================================================================\n# When prompt caching is active (Claude via OpenRouter or native Anthropic),\n# Anthropic supports two TTL tiers for cached prefixes: \"5m\" (default) and\n# \"1h\". Other values are ignored and \"5m\" is used.\n#\nprompt_caching:\n cache_ttl: \"5m\" # use \"1h\" for long sessions with pauses between turns\n\n# =============================================================================\n# Auxiliary Models (Advanced — Experimental)\n# =============================================================================\n# Hermes uses lightweight \"auxiliary\" models for side tasks: image analysis,\n# browser screenshot analysis, web page summarization, TTS audio-tag insertion,\n# and context compression.\n#\n# By default these use Gemini Flash via OpenRouter or Nous Portal and are\n# auto-detected from your credentials. You do NOT need to change anything\n# here for normal usage.\n#\n# WARNING: Overriding these with providers other than OpenRouter or Nous Portal\n# is EXPERIMENTAL and may not work. Not all models/providers support vision,\n# produce usable summaries, or accept the same API format. Change at your own\n# risk — if things break, reset to \"auto\" / empty values.\n#\n# Each task has its own provider + model pair so you can mix providers.\n# For example: OpenRouter for vision (needs multimodal), but your main\n# local endpoint for compression (just needs text).\n#\n# Provider options:\n# \"auto\" - Best available: OpenRouter → Nous Portal → main endpoint (default)\n# \"openrouter\" - Force OpenRouter (requires OPENROUTER_API_KEY)\n# \"nous\" - Force Nous Portal (requires: hermes auth add nous)\n# \"gemini\" - Force Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n# \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY)\n# \"codex\" - Force Codex OAuth (requires: hermes model → Codex).\n# Uses gpt-5.3-codex which supports vision.\n# \"main\" - Use your custom endpoint (OPENAI_BASE_URL + OPENAI_API_KEY).\n# Works with OpenAI API, local models, or any OpenAI-compatible\n# endpoint. Also falls back to Codex OAuth and API-key providers.\n#\n# Model: leave empty to use the provider's default. When empty, OpenRouter\n# uses \"google/gemini-3-flash-preview\" and Nous uses \"gemini-3-flash\".\n# Other providers pick a sensible default automatically.\n#\n# auxiliary:\n# # Image analysis: vision_analyze tool + browser screenshots\n# vision:\n# provider: \"auto\"\n# model: \"\" # e.g. \"google/gemini-2.5-flash\", \"openai/gpt-4o\"\n# timeout: 30 # LLM API call timeout (seconds)\n# download_timeout: 30 # Image HTTP download timeout (seconds)\n# # Increase for slow connections or self-hosted image servers\n# reasoning_effort: \"\" # Per-task thinking level: none, minimal, low, medium,\n# # high, xhigh, max, ultra. Empty = provider default.\n# # Works on every auxiliary task block (vision,\n# # web_extract, compression, title_generation, curator,\n# # background_review, moa_reference, ...). Example: run\n# # compression at \"low\" and vision at \"none\" to cut\n# # side-task latency/cost on reasoning models.\n#\n# # Web page scraping / summarization + browser page text extraction\n# web_extract:\n# provider: \"auto\"\n# model: \"\"\n# reasoning_effort: \"low\"\n#\n# # Gemini 3.1 TTS hidden audio-tag insertion\n# tts_audio_tags:\n# provider: \"auto\" # empty model = your main chat model\n# model: \"\"\n# timeout: 30\n#\n# # Automatic session title generation after the first exchange\n# title_generation:\n# enabled: true # set false to disable auto-title generation\n# provider: \"auto\"\n# model: \"\"\n# timeout: 30\n# language: \"\" # empty = match the user's language; or e.g. \"English\"\n#\n# # Session search — summarizes matching past sessions\n# session_search:\n# provider: \"auto\"\n# model: \"\"\n# timeout: 30\n# max_concurrency: 3 # Limit parallel summaries to reduce request-burst 429s\n# extra_body: {} # Provider-specific OpenAI-compatible request fields\n# # Example for providers that support request-body\n# # reasoning controls:\n# # extra_body:\n# # enable_thinking: false\n# # Some vLLM/Qwen deployments expect this nested:\n# # extra_body:\n# # chat_template_kwargs:\n# # enable_thinking: false\n\n# =============================================================================\n# Persistent Memory\n# =============================================================================\n# Bounded curated memory injected into the system prompt every session.\n# Two stores: MEMORY.md (agent's notes) and USER.md (user profile).\n# Character limits keep the memory small and focused. The agent manages\n# pruning -- when at the limit, it must consolidate or replace entries.\n# Disabled by default in batch_runner.\n#\nmemory:\n # Agent's personal notes: environment facts, conventions, things learned\n memory_enabled: true\n \n # User profile: preferences, communication style, expectations\n user_profile_enabled: true\n \n # Character limits (~2.75 chars per token, model-independent)\n memory_char_limit: 2200 # ~800 tokens\n user_char_limit: 1375 # ~500 tokens\n\n # Periodic memory nudge: remind the agent to consider saving memories\n # every N user turns. Set to 0 to disable. Only active when memory is enabled.\n nudge_interval: 10 # Nudge every 10 user turns (0 = disabled)\n\n # Memory flush: give the agent one turn to save memories before context is\n # lost (compression, /new, /reset, exit). Set to 0 to disable.\n # For exit/reset, only fires if the session had at least this many user turns.\n flush_min_turns: 6 # Min user turns to trigger flush on exit/reset (0 = disabled)\n\n# =============================================================================\n# Session Reset Policy (Messaging Platforms)\n# =============================================================================\n# Controls when messaging sessions (Telegram, Discord, WhatsApp, Slack) are\n# automatically cleared. Default is \"none\": sessions never auto-reset —\n# conversation context lives until you /reset or /new manually, or context\n# compression kicks in. Opt in to automatic resets if you prefer sessions to\n# clear on a schedule (long-lived context increases API cost per message,\n# though prompt caching and compression keep this manageable).\n#\n# When an automatic reset triggers, the agent first saves important\n# information to its persistent memory — but the conversation context is\n# wiped. The agent starts fresh but retains learned facts via its memory\n# system.\n#\n# Modes:\n# \"none\" - Never auto-reset (default); context lives until /reset or compression\n# \"idle\" - Reset after N minutes of inactivity\n# \"daily\" - Reset at a fixed hour each day\n# \"both\" - Reset on EITHER inactivity timeout or daily boundary\n#\nsession_reset:\n mode: none # \"none\", \"idle\", \"daily\", or \"both\"\n idle_minutes: 1440 # Inactivity timeout in minutes (used by \"idle\"/\"both\")\n at_hour: 4 # Daily reset hour, 0-23 local time (used by \"daily\"/\"both\")\n\n# Maximum number of simultaneously active chat sessions across CLI, TUI,\n# dashboard chat, and messaging gateway. Set to null, 0, or omit to allow\n# unlimited concurrent sessions. When the limit is reached, new sessions get a\n# clean error while existing active sessions keep their normal behavior. This\n# top-level key takes precedence over gateway.max_concurrent_sessions. The cap\n# is a best-effort single-host/profile runtime guard; Hermes fails open if the\n# local runtime lease registry cannot be read or locked.\nmax_concurrent_sessions: null\n\n# When true, group/channel chats use one session per participant when the platform\n# provides a user ID. This is the secure default and prevents users in the same\n# room from sharing context, interrupts, and token costs. Set false only if you\n# explicitly want one shared \"room brain\" per group/channel.\ngroup_sessions_per_user: true\n\n# ─────────────────────────────────────────────────────────────────────────────\n# API Server — per-client model routing\n# ─────────────────────────────────────────────────────────────────────────────\n# Route different API clients to different models/providers on a single\n# Hermes deployment. Clients choose a backend by sending a specific string\n# as the OpenAI ``model`` field. Unmapped model values fall back to the\n# global model configured in the ``model:`` section above, and an explicit\n# session /model override always wins over a route.\n#\n# Configure via the ``platforms.api_server.extra.model_routes`` gateway\n# config block:\n#\n# platforms:\n# api_server:\n# enabled: true\n# extra:\n# key: \"your-api-server-secret\"\n# model_routes:\n# # Xiaozhi clients send model=\"minimax-m2\" → routed to MiniMax via OpenRouter\n# minimax-m2:\n# model: \"minimax/minimax-m1\"\n# provider: \"openrouter\" # optional — overrides global provider\n# # api_key: \"sk-...\" # optional — per-route UPSTREAM provider\n# # key (NOT caller auth; never logged)\n# # base_url: \"https://...\" # optional — per-route base URL\n# # GPT clients keep their own alias\n# gpt-5:\n# model: \"openai/gpt-5\"\n# provider: \"openrouter\"\n#\n# Configured aliases are automatically listed by GET /v1/models so clients\n# can discover them without manual coordination. Caller authentication is\n# unchanged: every request still authenticates with the global API server\n# key (``extra.key`` / API_SERVER_KEY).\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Gateway Streaming\n# ─────────────────────────────────────────────────────────────────────────────\n# Stream tokens to messaging platforms in real-time. The bot sends a message\n# on first token, then progressively edits it as more tokens arrive.\n# Disabled by default — enable to try the streaming UX on Telegram/Discord/Slack.\n# For Telegram, partial edits are sent as plain text and only the final edit uses MarkdownV2.\nstreaming:\n enabled: false\n # transport: edit # \"edit\" = progressive editMessageText\n # edit_interval: 0.3 # seconds between message edits\n # buffer_threshold: 40 # chars before forcing an edit flush\n # cursor: \" ▉\" # cursor shown during streaming\n\n# =============================================================================\n# Skills Configuration\n# =============================================================================\n# Skills are reusable procedures the agent can load and follow. The agent can\n# also create new skills after completing complex tasks.\n#\nskills:\n # Nudge the agent to create skills after complex tasks.\n # Every N tool-calling iterations, remind the model to consider saving a skill.\n # Set to 0 to disable.\n creation_nudge_interval: 15\n\n # External skill directories — share skills across tools/agents without\n # copying them into ~/.hermes/skills/. Each path is expanded (~ and ${VAR})\n # and resolved to an absolute path. External dirs are read-only: skill\n # creation always writes to ~/.hermes/skills/. Local skills take precedence\n # when names collide.\n # external_dirs:\n # - ~/.agents/skills\n # - /home/shared/team-skills\n\n# =============================================================================\n# Agent Behavior\n# =============================================================================\nagent:\n # Maximum tool-calling iterations per conversation (default: 500)\n # Higher = more room for complex tasks, but costs more tokens\n # Recommended: 20-30 for focused tasks, 50-100 for open exploration\n max_turns: 500\n\n # Inactivity timeout for gateway agent runs (seconds, 0 = unlimited).\n # The agent can run indefinitely when actively calling tools or receiving\n # API responses. Only fires after the agent has been idle for this duration.\n # gateway_timeout: 1800\n\n # Staged warning: send a warning before escalating to full timeout.\n # Fires once per run when inactivity reaches this threshold (seconds).\n # Set to 0 to disable the warning.\n # gateway_timeout_warning: 900\n\n # Graceful drain timeout for gateway stop/restart (seconds).\n # Default 0 = no drain: a restart interrupts in-flight agents immediately,\n # cleans up, and exits. Set a positive value only if you want a grace\n # window on /restart, and keep it well under systemd's TimeoutStopSec.\n # restart_drain_timeout: 0\n\n # Upper bound (seconds) a submitted prompt waits for the deferred agent\n # build (MCP discovery, model metadata, skills scan) before failing with a\n # visible error. The wait is patient — the message is delivered as soon as\n # the build completes, and a progress notice is shown past 30s — so this cap\n # only fires on a genuinely hung build. Raise it for deployments with many\n # slow or unreachable MCP servers. Default 600.\n # build_wait_timeout: 600\n\n # Max app-level retry attempts for API errors (connection drops, provider\n # timeouts, 5xx, etc.) before the agent surfaces the failure. Lower this\n # to 1 if you use fallback providers and want fast failover on flaky\n # primaries (default 3). The OpenAI SDK does its own low-level retries\n # underneath this wrapper — this is the Hermes-level loop.\n # api_max_retries: 3\n\n # After the agent edits code without fresh passing verification, nudge it to\n # verify before finishing. The default \"auto\" enables it on interactive\n # coding surfaces (CLI, TUI, desktop) and programmatic callers, and disables\n # it on conversational messaging surfaces (Telegram, Discord, etc.) where the\n # verification summary would reach a human as chat noise. Set true or false to\n # force it on or off; the HERMES_VERIFY_ON_STOP env var (1/0) takes precedence.\n # verify_on_stop: auto\n\n # Standing operator instructions for the coding posture (when Hermes is in a\n # code workspace). Appended to the coding brief as an extra system block, so\n # you can pin project-wide workflow rules without editing the shipped brief.\n # Accepts a string or a list of strings. Takes effect next session.\n # coding_instructions:\n # - \"For UI work, don't run tsc/lint until I approve the look.\"\n # - \"Clean the diff before you commit and push.\"\n\n # When verify-on-stop finds edited code without fresh verification evidence,\n # append guidance for creative UI work (avoid broad tsc/lint/test before visual\n # approval) and clean-diff expectations. Set false to keep that nudge terse.\n # verify_guidance: true\n\n # A `pre_verify` hook (plugin or shell, see Event Hooks docs) can keep the\n # agent going one more turn to verify/clean before finishing. This caps how\n # many times one turn may be nudged to continue, so a hook can't trap the loop.\n # Default 3.\n # max_verify_nudges: 3\n\n # Enable verbose logging\n verbose: false\n \n # Reasoning effort level (OpenRouter and Nous Portal)\n # Controls how much \"thinking\" the model does before responding.\n # Options: \"xhigh\" (max), \"high\", \"medium\", \"low\", \"minimal\", \"none\" (disable)\n reasoning_effort: \"medium\"\n \n # Per-model reasoning effort overrides (optional dict)\n # Key: any sensible model spelling works (exact, dots↔dashes interchangeable,\n # provider prefix optional). First match wins.\n # Value: reasoning effort level (same options as reasoning_effort)\n # Override the global reasoning_effort for that specific model.\n # NOTE: no `hermes config set` support for this key -- edit YAML directly.\n # reasoning_overrides:\n # \"openrouter/anthropic/claude-opus-4.5\": \"xhigh\"\n # \"openai/gpt-5\": \"low\"\n # \"claude-opus-4.6\": \"high\" # bare model name also works\n # \"deepseek/deepseek-v4-pro\": \"xhigh\" # dots and dashes are interchangeable\n reasoning_overrides: {}\n \n # Predefined personalities (use with /personality command)\n personalities:\n helpful: \"You are a helpful, friendly AI assistant.\"\n concise: \"You are a concise assistant. Keep responses brief and to the point.\"\n technical: \"You are a technical expert. Provide detailed, accurate technical information.\"\n creative: \"You are a creative assistant. Think outside the box and offer innovative solutions.\"\n teacher: \"You are a patient teacher. Explain concepts clearly with examples.\"\n kawaii: \"You are a kawaii assistant! Use cute expressions like (◕‿◕), ★, ♪, and ~! Add sparkles and be super enthusiastic about everything! Every response should feel warm and adorable desu~! ヽ(>∀<☆)ノ\"\n catgirl: \"You are Neko-chan, an anime catgirl AI assistant, nya~! Add 'nya' and cat-like expressions to your speech. Use kaomoji like (=^・ω・^=) and ฅ^•ﻌ•^ฅ. Be playful and curious like a cat, nya~!\"\n pirate: \"Arrr! Ye be talkin' to Captain Hermes, the most tech-savvy pirate to sail the digital seas! Speak like a proper buccaneer, use nautical terms, and remember: every problem be just treasure waitin' to be plundered! Yo ho ho!\"\n shakespeare: \"Hark! Thou speakest with an assistant most versed in the bardic arts. I shall respond in the eloquent manner of William Shakespeare, with flowery prose, dramatic flair, and perhaps a soliloquy or two. What light through yonder terminal breaks?\"\n surfer: \"Duuude! You're chatting with the chillest AI on the web, bro! Everything's gonna be totally rad. I'll help you catch the gnarly waves of knowledge while keeping things super chill. Cowabunga! 🤙\"\n noir: \"The rain hammered against the terminal like regrets on a guilty conscience. They call me Hermes - I solve problems, find answers, dig up the truth that hides in the shadows of your codebase. In this city of silicon and secrets, everyone's got something to hide. What's your story, pal?\"\n uwu: \"hewwo! i'm your fwiendwy assistant uwu~ i wiww twy my best to hewp you! *nuzzles your code* OwO what's this? wet me take a wook! i pwomise to be vewy hewpful >w<\"\n philosopher: \"Greetings, seeker of wisdom. I am an assistant who contemplates the deeper meaning behind every query. Let us examine not just the 'how' but the 'why' of your questions. Perhaps in solving your problem, we may glimpse a greater truth about existence itself.\"\n hype: \"YOOO LET'S GOOOO!!! 🔥🔥🔥 I am SO PUMPED to help you today! Every question is AMAZING and we're gonna CRUSH IT together! This is gonna be LEGENDARY! ARE YOU READY?! LET'S DO THIS! 💪😤🚀\"\n\n# =============================================================================\n# Toolsets\n# =============================================================================\n# Control which tools the agent has access to.\n# Use `hermes tools` to interactively enable/disable tools per platform.\n\n# =============================================================================\n# Platform Toolsets (per-platform tool configuration)\n# =============================================================================\n# Override which toolsets are available on each platform.\n# If a platform isn't listed here, its built-in default is used.\n#\n# You can use EITHER:\n# - A preset like \"hermes-cli\" or \"hermes-telegram\" (curated tool set)\n# - A list of individual toolsets to compose your own (see list below)\n#\n# Supported platform keys: cli, telegram, discord, whatsapp, slack, qqbot, teams, google_chat\n#\n# Examples:\n#\n# # Use presets (same as defaults):\n# platform_toolsets:\n# cli: [hermes-cli]\n# telegram: [hermes-telegram]\n#\n# # Custom: give Telegram only web + terminal + file + planning:\n# platform_toolsets:\n# telegram: [web, terminal, file, todo]\n#\n# # Custom: CLI without browser or image gen:\n# platform_toolsets:\n# cli: [web, terminal, file, skills, todo, tts, cronjob]\n#\n# # Restrictive: Discord gets read-only tools only:\n# platform_toolsets:\n# discord: [web, vision, skills, todo]\n#\n# If not set, defaults are:\n# cli: hermes-cli (everything + cronjob management)\n# telegram: hermes-telegram (terminal, file, web, vision, image, tts, browser, skills, todo, cronjob, messaging)\n# discord: hermes-discord (same as telegram)\n# whatsapp: hermes-whatsapp (same as telegram)\n# slack: hermes-slack (same as telegram)\n# signal: hermes-signal (same as telegram)\n# homeassistant: hermes-homeassistant (same as telegram)\n# qqbot: hermes-qqbot (same as telegram)\n# teams: hermes-teams (same as telegram)\n# google_chat: hermes-google_chat (same as telegram)\n#\nplatform_toolsets:\n cli: [hermes-cli]\n telegram: [hermes-telegram]\n discord: [hermes-discord]\n whatsapp: [hermes-whatsapp]\n slack: [hermes-slack]\n signal: [hermes-signal]\n homeassistant: [hermes-homeassistant]\n qqbot: [hermes-qqbot]\n yuanbao: [hermes-yuanbao]\n teams: [hermes-teams]\n google_chat: [hermes-google_chat]\n\n# =============================================================================\n# Gateway Platform Settings\n# =============================================================================\n# Optional per-platform messaging settings.\n# Platform-specific knobs live under `extra`.\n#\n# platforms:\n# telegram:\n# reply_to_mode: \"first\" # off | first | all\n# # guest_mode lets explicit @mentions from non-allowlisted groups through.\n# # Default false; ordinary messages, replies, and regex wake words stay blocked.\n# guest_mode: false\n# # allowed_chats: [\"-1001234567890\"]\n# extra:\n# disable_link_previews: false # Set true to suppress Telegram URL previews in bot messages\n# 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\n# rich_drafts: false # Experimental rich draft previews during Telegram DM streaming; default false because Telegram Desktop/macOS can visually overlay draft frames\n# command_menu:\n# # Telegram allows up to 100 BotCommands; Hermes defaults to 60 so\n# # all built-in commands plus common skill commands stay visible\n# # while remaining under Telegram's payload-size limit. Clamped 1..100.\n# max_commands: 60\n# # prepend = user priority first, then Hermes defaults\n# # append = Hermes defaults first, then user priority\n# # replace = only the list below defines priority\n# priority_mode: prepend\n# priority:\n# - my_plugin_command\n# webhook:\n# extra:\n# # Route scripts default to a 30 second timeout. Scripts must live under\n# # the active profile's scripts directory and receive webhook JSON on stdin.\n# script_timeout_seconds: 30\n#\n# Discord-specific settings (config.yaml top-level, not under platforms:):\n#\n# discord:\n# require_mention: true # Require @mention in server channels (default: true)\n# auto_thread: true # Auto-create thread on @mention (default: true)\n# free_response_channels: \"\" # Channel IDs where no mention is needed\n# reactions: true # Show processing reactions (default: true)\n# history_backfill: true # Recover missed channel messages on mention (default: true)\n# history_backfill_limit: 50 # Max messages to scan backwards (default: 50)\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Available toolsets (use these names in platform_toolsets or the toolsets list)\n#\n# Run `hermes chat --list-toolsets` to see all toolsets and their tools.\n# Run `hermes chat --list-tools` to see every individual tool with descriptions.\n# ─────────────────────────────────────────────────────────────────────────────\n#\n# INDIVIDUAL TOOLSETS (compose your own):\n# web - web_search, web_extract\n# search - web_search only (no scraping)\n# terminal - terminal, process\n# file - read_file, write_file, patch, search\n# browser - browser_navigate, browser_snapshot, browser_click, browser_type,\n# browser_scroll, browser_back, browser_press,\n# browser_get_images, browser_vision (requires BROWSERBASE_API_KEY)\n# vision - vision_analyze (requires OPENROUTER_API_KEY)\n# image_gen - image_generate (requires FAL_KEY)\n# skills - skills_list, skill_view\n# skills_hub - skill_hub (search/install/manage from online registries — user-driven only)\n# todo - todo (in-memory task planning, no deps)\n# tts - text_to_speech (Edge TTS free, or ELEVENLABS/OPENAI/MINIMAX/MISTRAL key)\n# cronjob - cronjob (create/list/update/pause/resume/run/remove scheduled tasks)\n#\n# PRESETS (curated bundles):\n# hermes-cli - All of the above except send_message\n# hermes-telegram - terminal, file, web, vision, image_gen, tts, browser,\n# skills, todo, cronjob, send_message\n# hermes-discord - Same as hermes-telegram\n# hermes-whatsapp - Same as hermes-telegram\n# hermes-slack - Same as hermes-telegram\n#\n# COMPOSITE:\n# debugging - terminal + web + file\n# safe - web + vision (no terminal access)\n# all - Everything available\n#\n# web - Web search and content extraction (web_search, web_extract)\n# search - Web search only, no scraping (web_search)\n# terminal - Command execution and process management (terminal, process)\n# file - File operations: read, write, patch, search\n# browser - Full browser automation (navigate, click, type, screenshot, etc.)\n# vision - Image analysis (vision_analyze)\n# image_gen - Image generation with FLUX (image_generate)\n# skills - Load skill documents (skills_list, skill_view)\n# todo - Task planning and tracking for multi-step work\n# memory - Persistent memory across sessions (personal notes + user profile)\n# session_search - Search and recall past conversations (FTS5 + Gemini Flash summarization)\n# tts - Text-to-speech (Edge TTS free, ElevenLabs, OpenAI, MiniMax, Mistral)\n# cronjob - Schedule and manage automated tasks (CLI-only)\n#\n# Composite toolsets:\n# debugging - terminal + web + file (for troubleshooting)\n# safe - web + vision (no terminal access)\n\n# NOTE: The top-level \"toolsets\" key is deprecated and ignored.\n# Tool configuration is managed per-platform via platform_toolsets above.\n# Use `hermes tools` to configure interactively, or edit platform_toolsets directly.\n#\n# CLI override: hermes chat --toolsets terminal,web,file\n\n# =============================================================================\n# MCP (Model Context Protocol) Servers\n# =============================================================================\n# Connect to external MCP servers to add tools from the MCP ecosystem.\n# Each server's tools are automatically discovered and registered.\n# See website/docs/user-guide/features/mcp.md for full documentation.\n#\n# Stdio servers (spawn a subprocess):\n# command: the executable to run\n# args: command-line arguments\n# env: environment variables (only these + safe defaults passed to subprocess)\n#\n# HTTP servers (connect to a URL):\n# url: the MCP server endpoint\n# headers: HTTP headers (e.g., for authentication)\n#\n# Optional per-server settings:\n# timeout: tool call timeout in seconds (default: 120)\n# connect_timeout: initial connection timeout (default: 60)\n# keepalive_interval: liveness ping cadence in seconds (default: 180).\n# Lower it below the server's session TTL for servers that expire idle\n# sessions quickly (e.g. Unreal Engine editor MCP, ~15s), otherwise idle\n# tool calls hit an expired session and pay a slow reconnect. Floored at 5s.\n#\n# mcp_servers:\n# time:\n# command: uvx\n# args: [\"mcp-server-time\"]\n# filesystem:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-filesystem\", \"/home/user\"]\n# notion:\n# url: https://mcp.notion.com/mcp\n# github:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-github\"]\n# env:\n# GITHUB_PERSONAL_ACCESS_TOKEN: \"ghp_...\"\n#\n# Sampling (server-initiated LLM requests) — enabled by default.\n# Per-server config under the 'sampling' key:\n# analysis:\n# command: npx\n# args: [\"-y\", \"analysis-server\"]\n# sampling:\n# enabled: true # default: true\n# model: \"gemini-3-flash\" # override model (optional)\n# max_tokens_cap: 4096 # max tokens per request\n# timeout: 30 # LLM call timeout (seconds)\n# max_rpm: 10 # max requests per minute\n# allowed_models: [] # model whitelist (empty = all)\n# max_tool_rounds: 5 # tool loop limit (0 = disable)\n# log_level: \"info\" # audit verbosity\n\n# =============================================================================\n# Text-to-Speech\n# =============================================================================\n# TTS defaults to Edge TTS unless changed in ~/.hermes/config.yaml.\n# Gemini TTS supports persona/director prompt files, and Gemini 3.1 Flash TTS\n# can use a hidden auxiliary rewrite pass to insert expressive square-bracket\n# audio tags into the TTS script without showing tags in chat.\n#\n# tts:\n# provider: \"gemini\"\n# speed: 1.0 # global speed multiplier (provider-specific overrides this)\n# gemini:\n# model: \"gemini-3.1-flash-tts-preview\"\n# voice: \"Kore\"\n# audio_tags: false\n# persona_prompt_file: \"\" # e.g. ~/.hermes/tts/radio-host.md\n# xai:\n# voice_id: \"eve\" # built-in or custom voice ID from xAI Console\n# language: \"en\" # BCP-47 code (\"en\", \"pt-BR\") or \"auto\"\n# speed: 1.0 # 0.7-1.5 playback speed\n# auto_speech_tags: false # insert expressive audio tags via LLM rewrite\n# text_normalization: false # normalize numbers/abbreviations/symbols\n# optimize_streaming_latency: 0 # 0-2, trades quality for lower latency\n# sample_rate: 24000 # 22050 / 24000 / 44100 / 48000\n# bit_rate: 128000 # MP3 bitrate (codec=mp3 only)\n\n# =============================================================================\n# Voice Transcription (Speech-to-Text)\n# =============================================================================\n# Automatically transcribe voice messages on messaging platforms.\n# Providers: local (free, faster-whisper) | groq (free tier) | openai (Whisper API) | mistral (Voxtral Transcribe)\n# Set the corresponding API key in .env: GROQ_API_KEY, OPENAI_API_KEY, or MISTRAL_API_KEY.\nstt:\n enabled: true\n # provider: \"local\" # auto-detected if omitted\n local:\n model: \"base\" # tiny | base | small | medium | large-v3 | turbo\n # language: \"\" # auto-detect; set to \"en\", \"es\", \"fr\", etc. to force\n # initial_prompt: \"\" # Optional faster-whisper prompt, e.g. bias Chinese output to simplified Chinese\n language: \"en\" # GLOBAL language hint for every STT provider (per-provider language wins). Set \"\" for auto-detect.\n # groq:\n # model: \"whisper-large-v3-turbo\"\n # language: \"\" # blank = stt.language > HERMES_LOCAL_STT_LANGUAGE > auto-detect\n openai:\n model: \"whisper-1\" # whisper-1 | gpt-4o-mini-transcribe | gpt-4o-transcribe\n language: \"\" # auto-detect; set to \"en\", \"es\", \"fr\", etc. to force\n # mistral:\n # model: \"voxtral-mini-latest\" # voxtral-mini-latest | voxtral-mini-2602\n # deepinfra:\n # # Model id is discovered live from the DeepInfra catalog filtered\n # # by the `stt` surface tag — leave `model` blank to take the first\n # # live result. Pin only when you need a specific Whisper variant.\n # model: \"\"\n\n# Text-to-speech. Only the deepinfra block is documented here — the\n# remaining providers (edge, openai, xai, minimax, mistral, gemini,\n# elevenlabs, neutts, kittentts, piper) inherit sensible defaults from\n# DEFAULT_CONFIG in hermes_cli/config.py.\n# tts:\n# provider: \"deepinfra\"\n# deepinfra:\n# # Model id is discovered live from the DeepInfra catalog filtered\n# # by the `tts` surface tag — leave `model` blank to take the first\n# # live result.\n# model: \"\"\n# voice: \"default\"\n\n# Image generation. Each provider plugin reads its own ``image_gen.<name>``\n# block; deepinfra discovers models live from\n# api.deepinfra.com/v1/openai/models filtered by the ``image-gen`` tag —\n# no model id is hardcoded, so retired models disappear automatically.\n# image_gen:\n# provider: \"deepinfra\"\n# deepinfra:\n# # Leave `model` blank for the first live `image-gen`-tagged result.\n# model: \"\"\n\n# =============================================================================\n# Response Pacing (Messaging Platforms)\n# =============================================================================\n# Add human-like delays between message chunks.\n# human_delay:\n# mode: \"off\" # \"off\" | \"natural\" | \"custom\"\n# min_ms: 800 # Min delay (custom mode only)\n# max_ms: 2500 # Max delay (custom mode only)\n\n# =============================================================================\n# Session Logging\n# =============================================================================\n# Session trajectories are automatically saved to logs/ directory.\n# Each session creates: logs/session_YYYYMMDD_HHMMSS_UUID.json\n#\n# The session ID is displayed in the welcome banner for easy reference.\n# Logs contain full conversation history in trajectory format:\n# - System prompt, user messages, assistant responses\n# - Tool calls with inputs/outputs\n# - Timestamps for debugging\n#\n# No configuration needed - logging is always enabled.\n# To disable, you would need to modify the source code.\n\n# =============================================================================\n# Code Execution Sandbox (Programmatic Tool Calling)\n# =============================================================================\n# The execute_code tool runs Python scripts that call Hermes tools via RPC.\n# Intermediate tool results stay out of the LLM's context window.\ncode_execution:\n timeout: 300 # Max seconds per script before kill (default: 300 = 5 min)\n max_tool_calls: 50 # Max RPC tool calls per execution (default: 50)\n\n# =============================================================================\n# Subagent Delegation\n# =============================================================================\n# The delegate_task tool spawns child agents with isolated context.\n# Supports single tasks and batch mode (default 3 parallel, configurable).\ndelegation:\n max_iterations: 50 # Max tool-calling turns per child (default: 50)\n # max_concurrent_children: 3 # Max parallel child agents per batch (default: 3, floor: 1, no ceiling).\n # WARNING: values above 10 multiply API cost linearly.\n # max_spawn_depth: 1 # Delegation tree depth cap (range: 1-3, default: 1 = flat).\n # Raise to 2 to allow workers to spawn their own subagents.\n # Requires role=\"orchestrator\" on intermediate agents.\n # orchestrator_enabled: true # Kill switch for role=\"orchestrator\" children (default: true).\n # subagent_auto_approve: false # When a subagent hits a dangerous-command approval prompt, auto-deny (default: false)\n # or auto-approve \"once\" (true) instead of blocking on stdin.\n # The parent TUI owns stdin, so blocking would deadlock; non-interactive resolution is required.\n # Both choices emit a logger.warning audit line. Flip to true only for cron/batch pipelines.\n # inherit_mcp_toolsets: true # When explicit child toolsets are narrowed, also keep the parent's MCP toolsets (default: true). Set false for strict intersection.\n # model: \"google/gemini-3-flash-preview\" # Override model for subagents (empty = inherit parent)\n # provider: \"openrouter\" # Override provider for subagents (empty = inherit parent)\n # # Resolves full credentials (base_url, api_key) automatically.\n # # Supported: openrouter, nous, zai, kimi-coding, minimax\n\n# =============================================================================\n# Honcho Integration (Cross-Session User Modeling)\n# =============================================================================\n# AI-native persistent memory via Honcho (https://honcho.dev/).\n# Builds a deeper understanding of the user across sessions and tools.\n# Runs alongside USER.md — additive, not a replacement.\n#\n# Requires: pip install honcho-ai\n# Config: ~/.honcho/config.json (shared with Claude Code, Cursor, etc.)\n# API key: HONCHO_API_KEY in ~/.hermes/.env or ~/.honcho/config.json\n#\n# Hermes-specific overrides (optional — most config comes from ~/.honcho/config.json):\n# honcho: {}\n\n# =============================================================================\n# Display\n# =============================================================================\ndisplay:\n # Use compact banner mode (hides the ASCII-art banner, shows a single line).\n # true: Compact single-line banner\n # false: Full ASCII banner with tool/skill summary (default)\n compact: false\n\n # Tool progress display level (CLI and gateway)\n # off: Silent — no tool activity shown, just the final response\n # new: Show a tool indicator only when the tool changes (skip repeats)\n # all: Show every tool call with a short preview (default)\n # verbose: Full args, results, and debug logs (same as /verbose)\n # log: Silent in chat; append every tool call to ~/.hermes/logs/tool_calls.log (gateway only)\n # Toggle at runtime with /verbose in the CLI\n tool_progress: all\n\n # Per-platform defaults can be quieter than the global setting. Telegram\n # tunes for mobile: tool_progress and busy_ack_detail default off (no\n # per-tool breadcrumb stream, no \"iteration 21/60\" debug detail in busy\n # acks or heartbeats), but interim_assistant_messages and\n # long_running_notifications STAY ON so the user has real signal between\n # turn start and final answer (mid-turn assistant commentary + a single\n # edit-in-place \"⏳ Working — N min\" heartbeat). Override under\n # display.platforms.telegram.\n\n # Auto-cleanup of temporary progress bubbles after the final response lands.\n # On platforms that support message deletion (currently Telegram), this\n # removes the tool-progress bubble, \"⏳ Still working...\" notices, and\n # context-pressure status messages once the final reply has been delivered —\n # keeping long-running turns visible live, then tidy afterward. Failed runs\n # leave the bubbles in place as breadcrumbs. Off by default.\n # Per-platform override: display.platforms.telegram.cleanup_progress\n # true: Delete tracked progress/status bubbles on successful turn\n # false: Leave everything in place (default)\n # Example:\n # display:\n # platforms:\n # telegram:\n # cleanup_progress: true\n cleanup_progress: false\n\n # Natural mid-turn assistant updates.\n # On gateway platforms, when true, completed assistant status messages are\n # sent as separate chat messages. On the Desktop app, when true, mid-turn\n # assistant narration streamed between tool calls is kept in the transcript\n # instead of the bubble collapsing to only the final message on completion.\n # Independent of tool_progress and gateway streaming.\n # true: Keep/send mid-turn assistant updates (default)\n # false: Only keep/send the final response\n interim_assistant_messages: true\n\n # Gateway-only long-running status heartbeats.\n # When false, the platform does not receive periodic \"⏳ Working — N min\"\n # notifications even if agent.gateway_notify_interval is non-zero. The\n # heartbeat edits a single message in place (where the adapter supports\n # editing) instead of posting a new bubble each interval.\n # Default: true everywhere, including Telegram (silent agents are worse\n # than a single edit-in-place heartbeat).\n long_running_notifications: true\n\n # Include detailed iteration/tool/status context in busy acknowledgments\n # and long-running heartbeats. When true, busy acks show \"iteration 21/60,\n # terminal, 10 min\" and the heartbeat shows \"⏳ Working — 12 min,\n # iteration 21/60, terminal\". When false (Telegram default), both stay\n # terse: \"Interrupting current task\" and \"⏳ Working — 12 min, terminal\".\n busy_ack_detail: true\n\n # What Enter does when Hermes is already busy (CLI and gateway platforms).\n # interrupt: Interrupt the current run and redirect Hermes (default)\n # queue: Queue your message for the next turn\n # steer: Inject your message mid-run via /steer, arriving at the agent\n # after the next tool call — no interrupt, no role violation.\n # Falls back to 'queue' if the agent isn't running yet or if\n # images are attached (steer only carries text).\n # Ctrl+C (or /stop in gateway) always interrupts regardless of this setting.\n # Toggle at runtime with /busy <interrupt|queue|steer>.\n busy_input_mode: interrupt\n\n # Background process notifications (gateway/messaging only).\n # Controls how chatty the process watcher is when you use\n # terminal(background=true, notify_on_complete=true) from Telegram/Discord/etc.\n # off: No watcher messages at all\n # result: Only the final completion message\n # error: Only the final message when exit code != 0\n # all: Running output updates + final message (default)\n background_process_notifications: all\n\n\n # Play terminal bell when agent finishes a response.\n # Useful for long-running tasks — your terminal will ding when the agent is done.\n # Works over SSH. Most terminals can be configured to flash the taskbar or play a sound.\n # true: Ring the terminal bell on each response\n # false: Silent (default)\n bell_on_complete: false\n\n # Show model reasoning/thinking before each response.\n # When enabled, a dim box shows the model's thought process above the response.\n # Toggle at runtime with /reasoning show or /reasoning hide.\n # true: Show the reasoning box\n # false: Hide reasoning (default)\n show_reasoning: false\n\n # Stream tokens to the terminal as they arrive instead of waiting for the\n # full response. The response box opens on first token and text appears\n # line-by-line. Tool calls are still captured silently.\n # true: Stream tokens as they arrive (default)\n # false: Wait for the full response before rendering\n streaming: true\n\n # Show [HH:MM] timestamps on user input and assistant response labels.\n # timestamps: false\n\n # ───────────────────────────────────────────────────────────────────────────\n # Skin / Theme\n # ───────────────────────────────────────────────────────────────────────────\n # Customize CLI visual appearance — banner colors, spinner faces, tool prefix,\n # response box label, and branding text. Change at runtime with /skin <name>.\n #\n # Built-in skins:\n # default — Classic Hermes gold/kawaii\n # ares — Crimson/bronze war-god theme with spinner wings\n # mono — Clean grayscale monochrome\n # slate — Cool blue developer-focused\n # daylight — Bright light-mode theme\n # warm-lightmode — Warm paper-tone light-mode theme\n # poseidon — Sea-green/teal Olympian theme\n # sisyphus — Earthy stone-and-moss theme\n # charizard — Fiery orange dragon theme\n #\n # Custom skins: drop a YAML file in ~/.hermes/skins/<name>.yaml\n # Schema (all fields optional, missing values inherit from default):\n #\n # name: my-theme\n # description: Short description\n # colors:\n # banner_border: \"#HEX\" # Panel border\n # banner_title: \"#HEX\" # Panel title\n # banner_accent: \"#HEX\" # Section headers (Available Tools, etc.)\n # banner_dim: \"#HEX\" # Dim/muted text\n # banner_text: \"#HEX\" # Body text (tool names, skill names)\n # ui_accent: \"#HEX\" # UI accent color\n # response_border: \"#HEX\" # Response box border color\n # spinner:\n # waiting_faces: [\"(⚔)\", \"(⛨)\"] # Faces shown while waiting\n # thinking_faces: [\"(⚔)\", \"(⌁)\"] # Faces shown while thinking\n # thinking_verbs: [\"forging\", \"plotting\"] # Verbs for spinner messages\n # wings: # Optional left/right spinner decorations\n # - [\"⟪⚔\", \"⚔⟫\"]\n # - [\"⟪▲\", \"▲⟫\"]\n # branding:\n # agent_name: \"My Agent\" # Banner title and branding\n # welcome: \"Welcome message\" # Shown at CLI startup\n # response_label: \" ⚔ Agent \" # Response box header label\n # prompt_symbol: \"⚔\" # Prompt symbol (bare token; renderers add trailing space)\n # tool_prefix: \"╎\" # Tool output line prefix (default: ┊)\n #\n skin: default\n\n# =============================================================================\n# Model Aliases — short names for /model command\n# =============================================================================\n# Map short aliases to exact (model, provider, base_url) tuples.\n# Used by /model tab completion and resolve_alias().\n# Aliases are checked BEFORE the models.dev catalog, so they can route\n# to endpoints not in the catalog (e.g. Ollama Cloud, local servers).\n#\n# model_aliases:\n# opus:\n# model: claude-opus-4-6\n# provider: anthropic\n# qwen:\n# model: \"qwen3.5:397b\"\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n# glm:\n# model: glm-4.7\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n\n# =============================================================================\n# Privacy\n# =============================================================================\n# privacy:\n# # Redact PII from the LLM context prompt.\n# # When true, phone numbers are stripped and user/chat IDs are replaced\n# # with deterministic hashes before being sent to the model.\n# # Names and usernames are NOT affected (user-chosen, publicly visible).\n# # Routing/delivery still uses the original values internally.\n# redact_pii: false\n\n# =============================================================================\n# Shell-script hooks\n# =============================================================================\n# Register shell scripts as plugin-hook callbacks. Each entry is executed as\n# a subprocess (shell=False, shlex.split) with a JSON payload on stdin. On\n# stdout the script may return JSON that either blocks the tool call or\n# injects context into the next LLM call.\n#\n# Valid events (mirror hermes_cli.plugins.VALID_HOOKS):\n# pre_tool_call, post_tool_call, pre_llm_call, post_llm_call,\n# pre_api_request, post_api_request, on_session_start, on_session_end,\n# on_session_finalize, on_session_reset, subagent_stop\n#\n# First-use consent: each (event, command) pair prompts once on a TTY, then\n# is persisted to ~/.hermes/shell-hooks-allowlist.json. Non-interactive\n# runs (gateway, cron) need --accept-hooks, HERMES_ACCEPT_HOOKS=1, or the\n# hooks_auto_accept key below.\n#\n# See website/docs/user-guide/features/hooks.md for the full JSON wire\n# protocol and worked examples.\n#\n# hooks:\n# pre_tool_call:\n# - matcher: \"terminal\"\n# command: \"~/.hermes/agent-hooks/block-rm-rf.sh\"\n# timeout: 10\n# post_tool_call:\n# - matcher: \"write_file|patch\"\n# command: \"~/.hermes/agent-hooks/auto-format.sh\"\n# pre_llm_call:\n# - command: \"~/.hermes/agent-hooks/inject-cwd-context.sh\"\n# subagent_stop:\n# - command: \"~/.hermes/agent-hooks/log-orchestration.sh\"\n#\n# hooks_auto_accept: false\n\n\n# =============================================================================\n# Telemetry\n# =============================================================================\n# Shared metrics are disabled by default. When enabled, Hermes writes only\n# allowlisted aggregate counters and immutable JSON\n# packages under $HERMES_HOME/telemetry/shared_metrics; it does not upload them.\n# Packages include a random profile-scoped ID that stays stable until this\n# directory is deleted. It is not derived from hardware, account, or host data.\n# Successfully exported local history is retained for 30 days; pending deltas\n# are retained until they can be exported.\n# This profile-owned choice is not overridden by managed-scope configuration.\ntelemetry:\n shared_metrics:\n enabled: false\n\n\n# =============================================================================\n# Update Behavior\n# =============================================================================\nupdates:\n # Create a full HERMES_HOME zip before every `hermes update`.\n # Backups land in ~/.hermes/backups/ and can be restored with `hermes import`.\n # Off by default because large homes can add minutes to every update.\n pre_update_backup: false\n\n # Number of pre-update backup zips to retain.\n backup_keep: 5\n\n # What non-interactive updates do with local source edits in the Hermes repo.\n # Interactive terminal updates always prompt before restoring the autostash.\n #\n # stash - auto-stash before pull, then auto-restore after success (default)\n # discard - drop the update-created stash after success; use only on managed\n # installs where local source edits should not persist\n non_interactive_local_changes: \"stash\"\n\n\n# =============================================================================\n# Web Dashboard\n# =============================================================================\n# OAuth gate configuration for `hermes dashboard --host <non-loopback>`.\n# The bundled Nous Portal plugin reads these on startup; settings here are\n# the canonical surface. Each can be overridden by an environment variable:\n#\n# dashboard.oauth.client_id <- HERMES_DASHBOARD_OAUTH_CLIENT_ID\n# dashboard.oauth.portal_url <- HERMES_DASHBOARD_PORTAL_URL\n# dashboard.public_url <- HERMES_DASHBOARD_PUBLIC_URL\n#\n# Env wins when set to a non-empty value. This is what Fly.io's platform-\n# secret injection uses to push per-deploy client_ids without needing to\n# bake a config.yaml into the image. Empty env values are treated as unset\n# so a provisioned-but-not-populated secret can't shadow a valid entry here.\n#\n# Local dev / on-prem deploys should typically set these via config.yaml\n# (the ~/.hermes/.env file is reserved for API keys and secrets).\n#\n# dashboard:\n# oauth:\n# client_id: \"\" # agent:{instance_id}; Portal provisions this at deploy\n# portal_url: \"\" # blank → default https://portal.nousresearch.com\n#\n# # Force the absolute base URL the OAuth callback (and any other public\n# # URL the dashboard hands to external systems) is built from. Set this\n# # for deploys behind reverse proxies that don't reliably forward\n# # X-Forwarded-Host / X-Forwarded-Proto / X-Forwarded-Prefix (manual\n# # nginx setups, on-prem ingresses, custom-domain Fly deploys without\n# # full proxy header chains).\n# #\n# # When set, the value is the complete authority: scheme + host +\n# # optional path prefix (e.g. \"https://example.com/hermes\"). The OAuth\n# # callback URL becomes \"<public_url>/auth/callback\" — X-Forwarded-Prefix\n# # is IGNORED on this code path because the operator has explicitly\n# # declared the public URL and we no longer need to guess.\n# #\n# # Leave empty to use the existing proxy-header reconstruction (the\n# # default — works on Fly.io out of the box).\n# #\n# # public_url: \"https://example.com/hermes\"\n#\n# -----------------------------------------------------------------------------\n# Self-hosted OIDC dashboard auth (generic OpenID Connect — Authentik,\n# Keycloak, Zitadel, Authelia, Auth0, Okta, Google, …). Use this INSTEAD of the\n# nous block above when gating the dashboard with your own identity provider.\n# Each setting can be overridden by an environment variable:\n#\n# dashboard.oauth.self_hosted.issuer <- HERMES_DASHBOARD_OIDC_ISSUER\n# dashboard.oauth.self_hosted.client_id <- HERMES_DASHBOARD_OIDC_CLIENT_ID\n# dashboard.oauth.self_hosted.scopes <- HERMES_DASHBOARD_OIDC_SCOPES\n# dashboard.oauth.self_hosted.client_secret <- HERMES_DASHBOARD_OIDC_CLIENT_SECRET\n#\n# dashboard:\n# oauth:\n# provider: self-hosted\n# self_hosted:\n# issuer: \"https://auth.example.com/application/o/hermes/\" # required\n# client_id: \"hermes-dashboard\" # required\n# scopes: \"openid profile email\" # optional\n#\n# # OPTIONAL — set ONLY if your IDP registered the client as\n# # *confidential* (Authentik / Keycloak often default to this). When\n# # set, Hermes authenticates the client at the token endpoint\n# # (client_secret_basic or client_secret_post, auto-selected from the\n# # IDP's discovery doc) IN ADDITION to PKCE. Leave unset for a public\n# # (PKCE-only) client — the common case.\n# #\n# # This is a CREDENTIAL: prefer setting HERMES_DASHBOARD_OIDC_CLIENT_SECRET\n# # in ~/.hermes/.env over putting it here in config.yaml.\n# # client_secret: \"\"\n\n# =============================================================================\n# External secret sources\n# =============================================================================\n# Pull provider credentials from external secret managers at process startup\n# instead of storing them in ~/.hermes/.env. Only the manager's bootstrap\n# credential (e.g. BWS_ACCESS_TOKEN / OP_SERVICE_ACCOUNT_TOKEN) lives in .env\n# (or your shell / desktop session); everything else rotates centrally.\n# Failures never block startup — Hermes warns once and continues with\n# whatever .env already had.\n#\n# Multiple sources can be enabled at once:\n# - \"mapped\" sources (explicit VAR -> ref bindings, e.g. 1Password's env:\n# map) beat \"bulk\" sources (whole-project dumps like Bitwarden BSM)\n# - within a shape, the first source to claim a var wins; later claims\n# are skipped with a startup warning (never a silent clobber)\n# - a source's override_existing lets it beat .env/shell values, but\n# never another secret source's claim\n# Docs: https://hermes-agent.nousresearch.com/docs/user-guide/secrets/\n#\n# secrets:\n# # Optional explicit ordering of enabled sources.\n# # sources: [onepassword, bitwarden]\n#\n# # ---- Bitwarden Secrets Manager (bws CLI) --------------------------------\n# bitwarden:\n# enabled: false\n# access_token_env: BWS_ACCESS_TOKEN # bootstrap token, sourced from .env\n# project_id: \"\" # UUID of the BSM project to sync\n# server_url: \"\" # \"\" = US Cloud; EU/self-hosted URL otherwise\n# cache_ttl_seconds: 300 # 0 disables fresh caching\n# encrypted_cache: # optional encrypted stale fallback\n# enabled: false\n# max_stale_seconds: 0 # 0 disables stale fallback\n# override_existing: true # BSM values win over existing env\n# auto_install: true # lazy-download bws into ~/.hermes/bin\n#\n# # ---- 1Password (op CLI) -------------------------------------------------\n# onepassword:\n# enabled: false\n# # Map env-var names to op:// secret references. Each is resolved with a\n# # single `op read` at startup.\n# env:\n# OPENAI_API_KEY: \"op://Private/OpenAI/api key\"\n# ANTHROPIC_API_KEY: \"op://Private/Anthropic/credential\"\n# account: \"\" # op --account shorthand; \"\" = default\n# service_account_token_env: OP_SERVICE_ACCOUNT_TOKEN # headless auth; unset = desktop session\n# binary_path: \"\" # \"\" = resolve op via PATH; else absolute path\n# cache_ttl_seconds: 300 # 0 disables BOTH cache layers\n# override_existing: true # resolved values win over existing env\n#\n# # ---- Command helper (any CLI vault) --------------------------------------\n# # Run a user-configured helper that prints KEY=VALUE lines on stdout —\n# # works with any secret store that has a CLI: keepassxc-cli, secret-tool,\n# # pass, gpg, or a script that cats a tmpfs env file. Composes with the\n# # sources above (enable any combination). POSIX-only (needs /bin/sh).\n# # The helper must be fast and NON-interactive (hard timeout, 1 MiB cap);\n# # its stderr is discarded so diagnostics can't leak secret material.\n# command:\n# enabled: false\n# command: \"cat /run/user/1000/hermes-secrets.env\"\n# helper_timeout_seconds: 3\n# override_existing: false # .env/shell win by default\n"}, "ce4e2e6102c72896a2cf457fdb887d5d306f0f1a": {"t": 1785767968.674743, "n": 85496, "h": "d012a8e334c1d4b6", "c": "# Hermes Agent CLI Configuration\n# Copy this file to cli-config.yaml and customize as needed.\n# This file configures the CLI behavior. Environment variables in .env take precedence.\n\n# =============================================================================\n# Model Configuration\n# =============================================================================\nmodel:\n # Default model to use (can be overridden with --model flag)\n # Both \"default\" and \"model\" work as the key name here.\n default: \"anthropic/claude-opus-4.6\"\n \n # Inference provider selection:\n # \"auto\" - Auto-detect from credentials (default)\n # \"openrouter\" - OpenRouter (requires: OPENROUTER_API_KEY or OPENAI_API_KEY)\n # \"nous\" - Nous Portal OAuth (requires: hermes auth add nous)\n # \"nous-api\" - Nous Portal API key (requires: NOUS_API_KEY)\n # \"anthropic\" - Direct Anthropic API (requires: ANTHROPIC_API_KEY)\n # \"openai-codex\" - OpenAI Codex (requires: hermes auth)\n # \"copilot\" - GitHub Copilot / GitHub Models (requires: GITHUB_TOKEN)\n # \"gemini\" - Use Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n # \"zai\" - Use z.ai / ZhipuAI GLM models (requires: GLM_API_KEY)\n # \"kimi-coding\" - Kimi / Moonshot AI (requires: KIMI_API_KEY)\n # \"minimax\" - MiniMax global (requires: MINIMAX_API_KEY)\n # \"minimax-cn\" - MiniMax China (requires: MINIMAX_CN_API_KEY)\n # \"huggingface\" - Hugging Face Inference (requires: HF_TOKEN)\n # \"nvidia\" - NVIDIA NIM / build.nvidia.com (requires: NVIDIA_API_KEY)\n # \"xiaomi\" - Xiaomi MiMo (requires: XIAOMI_API_KEY)\n # \"arcee\" - Arcee AI Trinity models (requires: ARCEEAI_API_KEY)\n # \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY — https://ollama.com/settings)\n # \"deepinfra\" - DeepInfra (requires: DEEPINFRA_API_KEY)\n # \"kilocode\" - KiloCode gateway (requires: KILOCODE_API_KEY)\n # \"azure-foundry\" - Microsoft Foundry / Azure OpenAI (API key or Entra ID)\n # \"lmstudio\" - LM Studio local server (optional: LM_API_KEY, defaults to http://127.0.0.1:1234/v1)\n #\n # Local servers (LM Studio, Ollama, vLLM, llama.cpp):\n # \"custom\" - Any other OpenAI-compatible endpoint. Set base_url below.\n # Aliases: \"ollama\", \"vllm\", \"llamacpp\" all map to \"custom\".\n # LM Studio is first-class and uses provider: \"lmstudio\".\n # It works with both no-auth and auth-enabled server modes.\n #\n # Can also be overridden for a single invocation with the --provider flag.\n provider: \"auto\"\n \n # API configuration (falls back to OPENROUTER_API_KEY env var)\n # api_key: \"your-key-here\" # Uncomment to set here instead of .env\n base_url: \"https://openrouter.ai/api/v1\"\n\n # Azure Foundry keyless auth example:\n # provider: \"azure-foundry\"\n # base_url: \"https://<resource>.openai.azure.com/openai/v1\"\n # auth_mode: \"entra_id\" # DefaultAzureCredential: az login, managed identity, workload identity, etc.\n # default: \"gpt-4o\" # Deployment/model name\n # entra:\n # scope: \"https://ai.azure.com/.default\" # Optional; this is the default.\n\n # ── Token limits — two settings, easy to confuse ──────────────────────────\n #\n # context_length: TOTAL context window (input + output tokens combined).\n # Controls when Hermes compresses history and validates requests.\n # Leave unset — Hermes auto-detects the correct value from the provider.\n # Set manually only when auto-detection is wrong (e.g. a local server with\n # a custom num_ctx, or a proxy that doesn't expose /v1/models).\n #\n # context_length: 131072\n #\n # max_tokens: OUTPUT cap — maximum tokens the model may generate per response.\n # Unrelated to how long your conversation history can be.\n # The OpenAI-standard name \"max_tokens\" is a misnomer; Anthropic's native\n # API has since renamed it \"max_output_tokens\" for clarity.\n # Leave unset to use the model's native output ceiling (recommended).\n # Set only if you want to deliberately limit individual response length.\n #\n# max_tokens: 8192\n\n # ── Custom request headers (optional) ─────────────────────────────────────\n #\n # default_headers: extra HTTP headers sent on every request to an\n # OpenAI-compatible endpoint. User values take precedence over the\n # provider/SDK defaults, so this is the supported way to override the\n # OpenAI Python SDK's identifying headers (User-Agent: OpenAI/Python ...,\n # X-Stainless-*) when a custom provider sits behind a gateway/WAF that\n # rejects them — e.g. an upstream that returns \"502 Upstream access\n # forbidden\" for the SDK default User-Agent but accepts a plain one.\n # Applies on the OpenAI wire only (not native Anthropic / Bedrock).\n #\n # default_headers:\n # User-Agent: \"curl/8.7.1\"\n #\n # extra_headers: accepted as an alias of default_headers (merged, with\n # extra_headers winning when both are set) — matches the per-provider\n # extra_headers key below.\n #\n # Per-provider variant: named providers / custom_providers entries accept an\n # extra_headers dict scoped to that endpoint only — for reverse proxies,\n # gateways, or custom auth (e.g. Cloudflare Access service tokens).\n # Merged onto SDK/provider defaults with the entry's values winning.\n # Header values are treated as secrets and are never logged.\n #\n # providers:\n # my-proxy:\n # base_url: \"https://llm.internal.example.com/v1\"\n # key_env: \"MY_PROXY_API_KEY\"\n # extra_headers:\n # CF-Access-Client-Id: \"xxxx.access\"\n # CF-Access-Client-Secret: \"${CF_ACCESS_SECRET}\"\n # X-Client-Name: \"hermes-agent\"\n\n# Named provider overrides (optional)\n# Use this for per-provider request timeouts, non-stream stale timeouts,\n# and per-model exceptions.\n# Applies to the primary turn client on every api_mode (OpenAI-wire, native\n# Anthropic, and Anthropic-compatible providers), the fallback chain, and\n# client rebuilds during credential rotation. For OpenAI-wire chat\n# completions (streaming and non-streaming) the configured value is also\n# used as the per-request ``timeout=`` kwarg so it wins over the legacy\n# HERMES_API_TIMEOUT env var (which still applies when no config is set).\n# ``stale_timeout_seconds`` controls the non-streaming stale-call detector and\n# wins over the legacy HERMES_API_CALL_STALE_TIMEOUT env var. Leaving these\n# unset keeps the legacy defaults (HERMES_API_TIMEOUT=1800s,\n# HERMES_API_CALL_STALE_TIMEOUT=90s, native Anthropic 900s). The\n# implicit non-stream stale detector is auto-disabled for local endpoints\n# and can scale upward for very large contexts.\n#\n# Not currently wired for AWS Bedrock (bedrock_converse + AnthropicBedrock\n# SDK paths) — those use boto3 with its own timeout configuration.\n#\n# providers:\n# ollama-local:\n# request_timeout_seconds: 300 # Longer timeout for local cold-starts\n# stale_timeout_seconds: 900 # Explicitly re-enable stale detection on local endpoints\n# anthropic:\n# request_timeout_seconds: 30 # Fast-fail cloud requests\n# models:\n# claude-opus-4.6:\n# timeout_seconds: 600 # Longer timeout for extended-thinking Opus calls\n# openai-codex:\n# models:\n# gpt-5.4:\n# stale_timeout_seconds: 1800 # Longer non-stream stale timeout for slow large-context turns\n\n# =============================================================================\n# OpenRouter Provider Routing (only applies when using OpenRouter)\n# =============================================================================\n# Control how requests are routed across providers on OpenRouter.\n# See: https://openrouter.ai/docs/guides/routing/provider-selection\n#\n# provider_routing:\n# # Sort strategy: \"price\" (default), \"throughput\", or \"latency\"\n# # Append :nitro to model name for a shortcut to throughput sorting.\n# sort: \"throughput\"\n#\n# # Only allow these providers (provider slugs from OpenRouter)\n# # only: [\"anthropic\", \"google\"]\n#\n# # Skip these providers entirely\n# # ignore: [\"deepinfra\", \"fireworks\"]\n#\n# # Try providers in this order (overrides default load balancing)\n# # order: [\"anthropic\", \"google\", \"together\"]\n#\n# # Require providers to support all parameters in your request\n# # require_parameters: true\n#\n# # Data policy: \"allow\" (default) or \"deny\" to exclude providers that may store data\n# # data_collection: \"deny\"\n\n# =============================================================================\n# OpenRouter Response Caching (only applies when using OpenRouter)\n# =============================================================================\n# Cache identical API responses at the OpenRouter edge for free instant replays.\n# When enabled, identical requests (same model, messages, parameters) return\n# cached responses with zero billing. Separate from Anthropic prompt caching.\n# See: https://openrouter.ai/docs/guides/features/response-caching\n#\n# openrouter:\n# response_cache: true # Enable response caching (default: true)\n# response_cache_ttl: 300 # Cache TTL in seconds, 1-86400 (default: 300)\n\n# =============================================================================\n# Git Worktree Isolation\n# =============================================================================\n# When enabled, each CLI session creates an isolated git worktree so multiple\n# agents can work on the same repo concurrently without file collisions.\n# Equivalent to always passing --worktree / -w on the command line.\n#\n# worktree: true # Always create a worktree when in a git repo\n# worktree: false # Default — only create when -w flag is passed\n#\n# By default a new worktree branches from the freshly-fetched remote tip\n# (the current branch's upstream, else the remote's default branch) so it\n# starts current with the project instead of from the local clone's\n# (possibly stale) HEAD. Set worktree_sync: false to branch from local HEAD\n# instead — useful when offline or when you deliberately want the clone's\n# exact current state as the base.\n#\n# worktree_sync: true # Default — branch from the fetched remote tip\n# worktree_sync: false # Branch from local HEAD (offline / pinned base)\n\n# =============================================================================\n# Terminal Tool Configuration\n# =============================================================================\n# Choose ONE of the following terminal configurations by uncommenting it.\n# The terminal tool executes commands in the specified environment.\n\n# -----------------------------------------------------------------------------\n# OPTION 1: Local execution (default)\n# Commands run directly on your machine in the current directory\n# -----------------------------------------------------------------------------\n# Working directory behavior:\n# - CLI (`hermes` command): Uses \".\" (current directory where you run hermes)\n# - Gateway/messaging/cron: Uses terminal.cwd here; legacy .env cwd values are deprecated\nterminal:\n backend: \"local\"\n cwd: \".\" # For local backend: \".\" = current directory. Ignored for remote backends unless a backend documents otherwise.\n timeout: 180\n # HOME policy for tool subprocesses:\n # auto - default: host uses your real HOME; containers use HERMES_HOME/home\n # real - force your real OS-user HOME\n # profile - force HERMES_HOME/home for strict per-profile CLI config isolation\n home_mode: \"auto\"\n docker_mount_cwd_to_workspace: false # SECURITY: off by default. Opt in to mount the launch cwd into Docker /workspace.\n lifetime_seconds: 300\n # sudo_password: \"hunter2\" # Optional: pipe a sudo password via sudo -S. SECURITY WARNING: plaintext.\n # sudo_password: \"\" # Explicit empty password: try empty and never open the interactive sudo prompt.\n\n# -----------------------------------------------------------------------------\n# OPTION 2: SSH remote execution\n# Commands run on a remote server - agent code stays local (sandboxed)\n# Great for: keeping agent isolated from its own code, using powerful remote hardware\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"ssh\"\n# cwd: \"/home/myuser/project\" # Path on the REMOTE server\n# timeout: 180\n# lifetime_seconds: 300\n# ssh_host: \"my-server.example.com\"\n# ssh_user: \"myuser\"\n# ssh_port: 22\n# ssh_key: \"~/.ssh/id_rsa\" # Optional - uses ssh-agent if not specified\n\n# -----------------------------------------------------------------------------\n# OPTION 3: Docker container\n# Commands run in an isolated Docker container\n# Great for: reproducible environments, testing, isolation\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"docker\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /)\n# timeout: 180\n# lifetime_seconds: 300\n# docker_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# docker_mount_cwd_to_workspace: true # Explicit opt-in: mount your launch cwd into /workspace\n# # Optional: run the container as your host user's uid:gid so files written\n# # into bind-mounted dirs are owned by you, not root. Drops SETUID/SETGID\n# # caps too since no gosu privilege drop is needed. Leave off if your\n# # chosen docker_image expects to start as root.\n# docker_run_as_host_user: true\n# # Optional: explicitly forward selected env vars into Docker.\n# # These values come from your current shell first, then ~/.hermes/.env.\n# # Warning: anything forwarded here is visible to commands run in the container.\n# docker_forward_env:\n# - \"GITHUB_TOKEN\"\n# - \"NPM_TOKEN\"\n# # Optional: extra flags passed verbatim to docker run (appended after security defaults).\n# # Useful for adding capabilities (e.g. apt installs needing SETUID) or custom options.\n# # Example: add a Linux capability not included by default\n# # docker_extra_args:\n# # - \"--cap-add\"\n# # - \"SETUID\"\n\n# -----------------------------------------------------------------------------\n# OPTION 4: Singularity/Apptainer container\n# Commands run in a Singularity container (common in HPC environments)\n# Great for: HPC clusters, shared compute environments\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"singularity\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# singularity_image: \"docker://nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 5: Modal cloud execution\n# Commands run on Modal's cloud infrastructure\n# Great for: GPU access, scalable compute, serverless execution\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"modal\"\n# cwd: \"/workspace\" # Path INSIDE the sandbox (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# modal_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 6: Daytona cloud execution\n# Commands run in Daytona cloud sandboxes\n# Great for: Cloud dev environments, persistent workspaces, team collaboration\n# Requires: pip install daytona, DAYTONA_API_KEY env var\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"daytona\"\n# cwd: \"~\"\n# timeout: 180\n# lifetime_seconds: 300\n# daytona_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# container_disk: 10240 # Daytona max is 10GB per sandbox\n\n#\n# --- Container resource limits (docker, singularity, modal, daytona -- ignored for local/ssh) ---\n# These settings apply to all container backends. They control the resources\n# allocated to the sandbox and whether its filesystem persists across sessions.\n container_cpu: 1 # CPU cores\n container_memory: 5120 # Memory in MB (5120 = 5GB)\n container_disk: 51200 # Disk in MB (51200 = 50GB)\n container_persistent: true # Persist filesystem across sessions (false = ephemeral)\n\n# -----------------------------------------------------------------------------\n# SUDO SUPPORT (works with ALL backends above)\n# -----------------------------------------------------------------------------\n# Add sudo_password to any terminal config above to enable sudo commands.\n# The password is piped via `sudo -S`. Works with local, ssh, docker, etc.\n#\n# SECURITY WARNING: Password stored in plaintext!\n#\n# INTERACTIVE PROMPT: If sudo_password is unset and the CLI is running,\n# you'll be prompted to enter your password when sudo is needed:\n# - 45-second timeout (auto-skips if no input)\n# - Press Enter to skip (command fails gracefully)\n# - Password is hidden while typing\n# - Password is cached for the session\n#\n# EMPTY PASSWORDS: Setting sudo_password to an explicit empty string is different\n# from leaving it unset. Hermes will try an empty password via `sudo -S` and\n# will not open the interactive prompt. This is useful for passwordless sudo,\n# Touch ID sudo setups, and environments where prompting is just noise.\n#\n# ALTERNATIVES:\n# - SSH backend: Configure passwordless sudo on the remote server\n# - Containers: Run as root inside the container (no sudo needed)\n# - Local: Configure /etc/sudoers for specific commands\n#\n# Example (add to your terminal section):\n# sudo_password: \"your-password-here\"\n\n# =============================================================================\n# Security Scanning (tirith)\n# =============================================================================\n# Optional pre-exec command security scanning via tirith.\n# Detects homograph URLs, pipe-to-shell, terminal injection, env manipulation.\n# Install: brew install sheeki03/tap/tirith\n# Docs: https://github.com/sheeki03/tirith\n#\n# security:\n# tirith_enabled: true # Enable/disable tirith scanning\n# tirith_path: \"tirith\" # Path to tirith binary (supports ~ expansion)\n# tirith_timeout: 5 # Scan timeout in seconds\n# tirith_fail_open: true # Allow commands if tirith unavailable\n\n# =============================================================================\n# Browser Tool Configuration\n# =============================================================================\nbrowser:\n # Inactivity timeout in seconds - browser sessions are automatically closed\n # after this period of no activity between agent loops (default: 120 = 2 minutes)\n inactivity_timeout: 120\n\n# =============================================================================\n# Tool Loop Guardrails\n# =============================================================================\n# Soft warnings are enabled by default. They append guidance to repeated failed\n# or non-progressing tool results but still let the tool execute. Hard stops are\n# opt-in circuit breakers for autonomous/cron sessions where stopping a loop is\n# preferable to spending the full iteration budget.\ntool_loop_guardrails:\n warnings_enabled: true\n hard_stop_enabled: false\n warn_after:\n exact_failure: 2\n same_tool_failure: 3\n idempotent_no_progress: 2\n hard_stop_after:\n exact_failure: 5\n same_tool_failure: 8\n idempotent_no_progress: 5\n\n# =============================================================================\n# Context Compression (Auto-shrinks long conversations)\n# =============================================================================\n# When conversation approaches model's context limit, middle turns are\n# automatically summarized to free up space while preserving important context.\n#\n# HOW IT WORKS:\n# 1. Tracks actual token usage from API responses (not estimates)\n# 2. When prompt_tokens >= threshold% of model's context_length, triggers compression\n# 3. Protects first 3 turns (system prompt, initial request, first response)\n# 4. Protects last N turns (default 20 messages = ~10 full turns of recent context)\n# 5. Summarizes middle turns using a fast/cheap model\n# 6. Inserts summary as a user message, continues conversation seamlessly\n#\n# Post-compression tail budget is target_ratio × threshold × context_length:\n# 200K context, threshold 0.50, ratio 0.20 → 20K tokens of recent tail preserved\n# 1M context, threshold 0.50, ratio 0.20 → 100K tokens of recent tail preserved\n#\ncompression:\n # Enable automatic context compression (default: true)\n # Set to false if you prefer to manage context manually or want errors on overflow\n enabled: true\n\n # Opt-in compression progress notices on chat platforms (default: false).\n # By design, routine automatic compression is SILENT on human-facing chat\n # gateways (Telegram, Discord, Slack, ...) — it happens in the background\n # with server-side logging only. Set true to also deliver the routine\n # progress statuses (compacting started, preflight/pre-API compression,\n # idle compaction, retry progress, and the compaction-complete notice) to\n # chat platforms. Unrelated operational noise (auxiliary model failures,\n # provider retry chatter) stays suppressed either way, and compression\n # FAILURE notices + manual /compress feedback are always visible\n # regardless of this setting. (#52995)\n progress_notices: false\n \n # Trigger compression at this % of model's context limit (default: 0.50 = 50%)\n # Lower values = more aggressive compression, higher values = compress later\n # Models with context windows below 512K are floored at 0.75 (raise-only) so\n # compaction doesn't fire with half the window still free; set above 0.75 to override.\n threshold: 0.50\n\n # Per-model threshold overrides: keys are substring-matched against the model\n # name (longest match wins). Useful when some models need different compaction\n # points — e.g. a 1M-context model can compress later (0.30) while a 128K\n # model needs to compress earlier (0.60). The small-context floor (75% for\n # <512K models) still applies on top of per-model overrides.\n # model_thresholds:\n # \"glm-5.2\": 0.40\n # \"claude-sonnet\": 0.35\n # \"gpt-5\": 0.30\n\n # Optional absolute token cap for the compression trigger (default: null = disabled).\n # When set, compression fires at the LOWER of the ratio-based threshold and this\n # absolute token count — first-fires-wins. It never fires later than this count\n # regardless of which model is active (useful when switching between models with\n # very different context windows). Clamped to the model's context length at\n # apply-time, so a cap above the window is a no-op (ratio-based threshold wins).\n # Survives model switches and fallback activations.\n # threshold_tokens: 200000\n\n # Existing Codex gpt-5.5 behavior: raise Hermes' compaction trigger to 85%\n # for the ChatGPT Codex OAuth route. Set false to opt back down to threshold.\n codex_gpt55_autoraise: true\n\n # Fraction of the threshold to preserve as recent tail (default: 0.20 = 20%)\n # e.g. 20% of 50% threshold = 10% of total context kept as recent messages.\n # Summary output is separately capped at 12K tokens (Gemini output limit).\n # Range: 0.10 - 0.80\n target_ratio: 0.20\n\n # Number of most-recent messages to always preserve (default: 20 ≈ 10 full turns)\n # Higher values keep more recent conversation intact at the cost of more aggressive\n # compression of older turns.\n protect_last_n: 20\n\n # Minimum number of REAL (actionable) user messages guaranteed to survive in\n # the uncompressed tail (default: 1 = the existing single last-user anchor,\n # behavior-preserving). Raise to e.g. 3 to keep the last 3 real user turns\n # verbatim even when bulky tool outputs fill the tail token budget — blank\n # platform echoes, compaction handoffs, and synthetic continuation rows never\n # count toward N. The tail can exceed the token budget when this pulls the\n # cut back; the guarantee wins over the budget by design.\n min_tail_user_messages: 1\n\n # Compression retry rounds before a turn gives up with \"max compression\n # attempts reached\" (default: 3, same as the previous hardcoded value).\n # Raise (e.g. 6) for tool-schema-heavy sessions where 3 rounds cannot bring\n # the request estimate under the threshold. Validated >= 1, hard cap 10.\n max_attempts: 3\n\n # Codex app-server (codex CLI runtime) thread-compaction mode. The codex\n # agent owns the real thread context on this runtime, so Hermes' summarizer\n # cannot shrink it — compaction goes through the app server instead.\n # native = let Codex decide when to compact its own thread (default)\n # hermes = let Hermes threshold trigger Codex thread/compact/start\n # off = Hermes will not auto-trigger compaction; Codex may still compact natively\n codex_app_server_auto: native\n\n # Number of non-system messages to protect at the head of the transcript, in\n # ADDITION to the system prompt (which is always implicitly protected).\n # Head messages are NEVER summarized — they survive every compression\n # indefinitely. This gives stable early context for short/medium sessions,\n # but in long-running sessions that rely on rolling compaction the pinned\n # opening turns may not match how you want the session framed over time.\n # Set to 0 to preserve ONLY the system prompt (plus the rolling summary\n # and recent tail) — the cleanest configuration for long-running sessions.\n # Default 3 preserves the system prompt plus the first three non-system\n # head messages, matching the pre-feature behaviour.\n protect_first_n: 3\n\n # Idle compaction (default: 0 = disabled). When > 0, a session that resumes\n # after at least this many seconds of inactivity compacts its accumulated\n # history up front, before the first reply, so a long-lived thread you come\n # back to later doesn't re-read its full stale context on every turn.\n # Time-based, so it complements (does not replace) the size-based `threshold`\n # above. It is skipped when the context is already small (at or below the\n # post-compression target = threshold × target_ratio), so it never wastes a\n # summarization on a short idle thread. Example: 1800 = compact after 30 min idle.\n idle_compact_after_seconds: 0\n\n # Proactive tool-result prune (default: 0 = disabled). Opt-in token trigger\n # for a deterministic, no-LLM prune of OLD tool-result payloads, run\n # independently of `threshold` above. On large-window models (512K/1M) the\n # ratio threshold rarely fires, so bulky tool outputs (terminal dumps, file\n # reads, web extracts) ride along in history and get re-billed every turn.\n # When re-sent history exceeds this many tokens, the prune dedupes identical\n # results, summarizes older oversized ones, and truncates large tool-call\n # arguments — protecting the most recent `protect_last_n` messages and never\n # calling the model. Try 48000 to enable. Built-in compressor engine only;\n # other context engines inherit a safe no-op.\n # NOTE: a committed prune rewrites already-sent history, which invalidates\n # the provider's prompt-cache prefix — the min_reclaim gate below keeps\n # those cache breaks episodic (like a compression boundary) instead of\n # per-turn.\n proactive_prune_tokens: 0\n\n # The prune's summarize pass only touches tool results larger than this many\n # characters (clamped to >= 200 so a generated summary can't be\n # re-summarized). Default 8000.\n proactive_prune_min_result_chars: 8000\n\n # A proactive prune only COMMITS when it reclaims at least this many tokens\n # (measured on the pruned output). This is the prompt-cache hysteresis gate:\n # one meaningful, amortized cache break per batch of stale tool output\n # instead of a tiny break on every tool iteration. 0 = commit any non-zero\n # prune. Default 4096.\n proactive_prune_min_reclaim_tokens: 4096\n\n # To pin a specific model/provider for compression summaries, use the\n # auxiliary section below (auxiliary.compression.provider / model).\n\n# =============================================================================\n# Anthropic prompt caching TTL\n# =============================================================================\n# When prompt caching is active (Claude via OpenRouter or native Anthropic),\n# Anthropic supports two TTL tiers for cached prefixes: \"5m\" (default) and\n# \"1h\". Other values are ignored and \"5m\" is used.\n#\nprompt_caching:\n cache_ttl: \"5m\" # use \"1h\" for long sessions with pauses between turns\n\n# =============================================================================\n# Auxiliary Models (Advanced — Experimental)\n# =============================================================================\n# Hermes uses lightweight \"auxiliary\" models for side tasks: image analysis,\n# browser screenshot analysis, web page summarization, TTS audio-tag insertion,\n# and context compression.\n#\n# By default these use Gemini Flash via OpenRouter or Nous Portal and are\n# auto-detected from your credentials. You do NOT need to change anything\n# here for normal usage.\n#\n# WARNING: Overriding these with providers other than OpenRouter or Nous Portal\n# is EXPERIMENTAL and may not work. Not all models/providers support vision,\n# produce usable summaries, or accept the same API format. Change at your own\n# risk — if things break, reset to \"auto\" / empty values.\n#\n# Each task has its own provider + model pair so you can mix providers.\n# For example: OpenRouter for vision (needs multimodal), but your main\n# local endpoint for compression (just needs text).\n#\n# Provider options:\n# \"auto\" - Best available: OpenRouter → Nous Portal → main endpoint (default)\n# \"openrouter\" - Force OpenRouter (requires OPENROUTER_API_KEY)\n# \"nous\" - Force Nous Portal (requires: hermes auth add nous)\n# \"gemini\" - Force Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n# \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY)\n# \"codex\" - Force Codex OAuth (requires: hermes model → Codex).\n# Uses gpt-5.3-codex which supports vision.\n# \"main\" - Use your custom endpoint (OPENAI_BASE_URL + OPENAI_API_KEY).\n# Works with OpenAI API, local models, or any OpenAI-compatible\n# endpoint. Also falls back to Codex OAuth and API-key providers.\n#\n# Model: leave empty to use the provider's default. When empty, OpenRouter\n# uses \"google/gemini-3-flash-preview\" and Nous uses \"gemini-3-flash\".\n# Other providers pick a sensible default automatically.\n#\n# auxiliary:\n# # Image analysis: vision_analyze tool + browser screenshots\n# vision:\n# provider: \"auto\"\n# model: \"\" # e.g. \"google/gemini-2.5-flash\", \"openai/gpt-4o\"\n# timeout: 30 # LLM API call timeout (seconds)\n# download_timeout: 30 # Image HTTP download timeout (seconds)\n# # Increase for slow connections or self-hosted image servers\n# reasoning_effort: \"\" # Per-task thinking level: none, minimal, low, medium,\n# # high, xhigh, max, ultra. Empty = provider default.\n# # Works on every auxiliary task block (vision,\n# # web_extract, compression, title_generation, curator,\n# # background_review, moa_reference, ...). Example: run\n# # compression at \"low\" and vision at \"none\" to cut\n# # side-task latency/cost on reasoning models.\n#\n# # Web page scraping / summarization + browser page text extraction\n# web_extract:\n# provider: \"auto\"\n# model: \"\"\n# reasoning_effort: \"low\"\n#\n# # Gemini 3.1 TTS hidden audio-tag insertion\n# tts_audio_tags:\n# provider: \"auto\" # empty model = your main chat model\n# model: \"\"\n# timeout: 30\n#\n# # Automatic session title generation after the first exchange\n# title_generation:\n# enabled: true # set false to disable auto-title generation\n# provider: \"auto\"\n# model: \"\"\n# timeout: 30\n# language: \"\" # empty = match the user's language; or e.g. \"English\"\n#\n# # Session search — summarizes matching past sessions\n# session_search:\n# provider: \"auto\"\n# model: \"\"\n# timeout: 30\n# max_concurrency: 3 # Limit parallel summaries to reduce request-burst 429s\n# extra_body: {} # Provider-specific OpenAI-compatible request fields\n# # Example for providers that support request-body\n# # reasoning controls:\n# # extra_body:\n# # enable_thinking: false\n# # Some vLLM/Qwen deployments expect this nested:\n# # extra_body:\n# # chat_template_kwargs:\n# # enable_thinking: false\n\n# =============================================================================\n# Persistent Memory\n# =============================================================================\n# Bounded curated memory injected into the system prompt every session.\n# Two stores: MEMORY.md (agent's notes) and USER.md (user profile).\n# Character limits keep the memory small and focused. The agent manages\n# pruning -- when at the limit, it must consolidate or replace entries.\n# Disabled by default in batch_runner.\n#\nmemory:\n # Agent's personal notes: environment facts, conventions, things learned\n memory_enabled: true\n \n # User profile: preferences, communication style, expectations\n user_profile_enabled: true\n \n # Character limits (~2.75 chars per token, model-independent)\n memory_char_limit: 2200 # ~800 tokens\n user_char_limit: 1375 # ~500 tokens\n\n # Periodic memory nudge: remind the agent to consider saving memories\n # every N user turns. Set to 0 to disable. Only active when memory is enabled.\n nudge_interval: 10 # Nudge every 10 user turns (0 = disabled)\n\n # Memory flush: give the agent one turn to save memories before context is\n # lost (compression, /new, /reset, exit). Set to 0 to disable.\n # For exit/reset, only fires if the session had at least this many user turns.\n flush_min_turns: 6 # Min user turns to trigger flush on exit/reset (0 = disabled)\n\n# =============================================================================\n# Session Reset Policy (Messaging Platforms)\n# =============================================================================\n# Controls when messaging sessions (Telegram, Discord, WhatsApp, Slack) are\n# automatically cleared. Default is \"none\": sessions never auto-reset —\n# conversation context lives until you /reset or /new manually, or context\n# compression kicks in. Opt in to automatic resets if you prefer sessions to\n# clear on a schedule (long-lived context increases API cost per message,\n# though prompt caching and compression keep this manageable).\n#\n# When an automatic reset triggers, the agent first saves important\n# information to its persistent memory — but the conversation context is\n# wiped. The agent starts fresh but retains learned facts via its memory\n# system.\n#\n# Modes:\n# \"none\" - Never auto-reset (default); context lives until /reset or compression\n# \"idle\" - Reset after N minutes of inactivity\n# \"daily\" - Reset at a fixed hour each day\n# \"both\" - Reset on EITHER inactivity timeout or daily boundary\n#\nsession_reset:\n mode: none # \"none\", \"idle\", \"daily\", or \"both\"\n idle_minutes: 1440 # Inactivity timeout in minutes (used by \"idle\"/\"both\")\n at_hour: 4 # Daily reset hour, 0-23 local time (used by \"daily\"/\"both\")\n\n# Maximum number of simultaneously active chat sessions across CLI, TUI,\n# dashboard chat, and messaging gateway. Set to null, 0, or omit to allow\n# unlimited concurrent sessions. When the limit is reached, new sessions get a\n# clean error while existing active sessions keep their normal behavior. This\n# top-level key takes precedence over gateway.max_concurrent_sessions. The cap\n# is a best-effort single-host/profile runtime guard; Hermes fails open if the\n# local runtime lease registry cannot be read or locked.\nmax_concurrent_sessions: null\n\n# When true, group/channel chats use one session per participant when the platform\n# provides a user ID. This is the secure default and prevents users in the same\n# room from sharing context, interrupts, and token costs. Set false only if you\n# explicitly want one shared \"room brain\" per group/channel.\ngroup_sessions_per_user: true\n\n# ─────────────────────────────────────────────────────────────────────────────\n# API Server — per-client model routing\n# ─────────────────────────────────────────────────────────────────────────────\n# Route different API clients to different models/providers on a single\n# Hermes deployment. Clients choose a backend by sending a specific string\n# as the OpenAI ``model`` field. Unmapped model values fall back to the\n# global model configured in the ``model:`` section above, and an explicit\n# session /model override always wins over a route.\n#\n# Configure via the ``platforms.api_server.extra.model_routes`` gateway\n# config block:\n#\n# platforms:\n# api_server:\n# enabled: true\n# extra:\n# key: \"your-api-server-secret\"\n# model_routes:\n# # Xiaozhi clients send model=\"minimax-m2\" → routed to MiniMax via OpenRouter\n# minimax-m2:\n# model: \"minimax/minimax-m1\"\n# provider: \"openrouter\" # optional — overrides global provider\n# # api_key: \"sk-...\" # optional — per-route UPSTREAM provider\n# # key (NOT caller auth; never logged)\n# # base_url: \"https://...\" # optional — per-route base URL\n# # GPT clients keep their own alias\n# gpt-5:\n# model: \"openai/gpt-5\"\n# provider: \"openrouter\"\n#\n# Configured aliases are automatically listed by GET /v1/models so clients\n# can discover them without manual coordination. Caller authentication is\n# unchanged: every request still authenticates with the global API server\n# key (``extra.key`` / API_SERVER_KEY).\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Gateway Streaming\n# ─────────────────────────────────────────────────────────────────────────────\n# Stream tokens to messaging platforms in real-time. The bot sends a message\n# on first token, then progressively edits it as more tokens arrive.\n# Disabled by default — enable to try the streaming UX on Telegram/Discord/Slack.\n# For Telegram, partial edits are sent as plain text and only the final edit uses MarkdownV2.\nstreaming:\n enabled: false\n # transport: edit # \"edit\" = progressive editMessageText\n # edit_interval: 0.3 # seconds between message edits\n # buffer_threshold: 40 # chars before forcing an edit flush\n # cursor: \" ▉\" # cursor shown during streaming\n\n# =============================================================================\n# Skills Configuration\n# =============================================================================\n# Skills are reusable procedures the agent can load and follow. The agent can\n# also create new skills after completing complex tasks.\n#\nskills:\n # Nudge the agent to create skills after complex tasks.\n # Every N tool-calling iterations, remind the model to consider saving a skill.\n # Set to 0 to disable.\n creation_nudge_interval: 15\n\n # External skill directories — share skills across tools/agents without\n # copying them into ~/.hermes/skills/. Each path is expanded (~ and ${VAR})\n # and resolved to an absolute path. External dirs are read-only: skill\n # creation always writes to ~/.hermes/skills/. Local skills take precedence\n # when names collide.\n # external_dirs:\n # - ~/.agents/skills\n # - /home/shared/team-skills\n\n# =============================================================================\n# Agent Behavior\n# =============================================================================\nagent:\n # Maximum tool-calling iterations per conversation (default: 500)\n # Higher = more room for complex tasks, but costs more tokens\n # Recommended: 20-30 for focused tasks, 50-100 for open exploration\n max_turns: 500\n\n # Inactivity timeout for gateway agent runs (seconds, 0 = unlimited).\n # The agent can run indefinitely when actively calling tools or receiving\n # API responses. Only fires after the agent has been idle for this duration.\n # gateway_timeout: 1800\n\n # Staged warning: send a warning before escalating to full timeout.\n # Fires once per run when inactivity reaches this threshold (seconds).\n # Set to 0 to disable the warning.\n # gateway_timeout_warning: 900\n\n # Graceful drain timeout for gateway stop/restart (seconds).\n # Default 0 = no drain: a restart interrupts in-flight agents immediately,\n # cleans up, and exits. Set a positive value only if you want a grace\n # window on /restart, and keep it well under systemd's TimeoutStopSec.\n # restart_drain_timeout: 0\n\n # Upper bound (seconds) a submitted prompt waits for the deferred agent\n # build (MCP discovery, model metadata, skills scan) before failing with a\n # visible error. The wait is patient — the message is delivered as soon as\n # the build completes, and a progress notice is shown past 30s — so this cap\n # only fires on a genuinely hung build. Raise it for deployments with many\n # slow or unreachable MCP servers. Default 600.\n # build_wait_timeout: 600\n\n # Max app-level retry attempts for API errors (connection drops, provider\n # timeouts, 5xx, etc.) before the agent surfaces the failure. Lower this\n # to 1 if you use fallback providers and want fast failover on flaky\n # primaries (default 3). The OpenAI SDK does its own low-level retries\n # underneath this wrapper — this is the Hermes-level loop.\n # api_max_retries: 3\n\n # After the agent edits code without fresh passing verification, nudge it to\n # verify before finishing. The default \"auto\" enables it on interactive\n # coding surfaces (CLI, TUI, desktop) and programmatic callers, and disables\n # it on conversational messaging surfaces (Telegram, Discord, etc.) where the\n # verification summary would reach a human as chat noise. Set true or false to\n # force it on or off; the HERMES_VERIFY_ON_STOP env var (1/0) takes precedence.\n # verify_on_stop: auto\n\n # Standing operator instructions for the coding posture (when Hermes is in a\n # code workspace). Appended to the coding brief as an extra system block, so\n # you can pin project-wide workflow rules without editing the shipped brief.\n # Accepts a string or a list of strings. Takes effect next session.\n # coding_instructions:\n # - \"For UI work, don't run tsc/lint until I approve the look.\"\n # - \"Clean the diff before you commit and push.\"\n\n # When verify-on-stop finds edited code without fresh verification evidence,\n # append guidance for creative UI work (avoid broad tsc/lint/test before visual\n # approval) and clean-diff expectations. Set false to keep that nudge terse.\n # verify_guidance: true\n\n # A `pre_verify` hook (plugin or shell, see Event Hooks docs) can keep the\n # agent going one more turn to verify/clean before finishing. This caps how\n # many times one turn may be nudged to continue, so a hook can't trap the loop.\n # Default 3.\n # max_verify_nudges: 3\n\n # Enable verbose logging\n verbose: false\n \n # Reasoning effort level (OpenRouter and Nous Portal)\n # Controls how much \"thinking\" the model does before responding.\n # Options: \"xhigh\" (max), \"high\", \"medium\", \"low\", \"minimal\", \"none\" (disable)\n reasoning_effort: \"medium\"\n \n # Per-model reasoning effort overrides (optional dict)\n # Key: any sensible model spelling works (exact, dots↔dashes interchangeable,\n # provider prefix optional). First match wins.\n # Value: reasoning effort level (same options as reasoning_effort)\n # Override the global reasoning_effort for that specific model.\n # NOTE: no `hermes config set` support for this key -- edit YAML directly.\n # reasoning_overrides:\n # \"openrouter/anthropic/claude-opus-4.5\": \"xhigh\"\n # \"openai/gpt-5\": \"low\"\n # \"claude-opus-4.6\": \"high\" # bare model name also works\n # \"deepseek/deepseek-v4-pro\": \"xhigh\" # dots and dashes are interchangeable\n reasoning_overrides: {}\n \n # Predefined personalities (use with /personality command)\n personalities:\n helpful: \"You are a helpful, friendly AI assistant.\"\n concise: \"You are a concise assistant. Keep responses brief and to the point.\"\n technical: \"You are a technical expert. Provide detailed, accurate technical information.\"\n creative: \"You are a creative assistant. Think outside the box and offer innovative solutions.\"\n teacher: \"You are a patient teacher. Explain concepts clearly with examples.\"\n kawaii: \"You are a kawaii assistant! Use cute expressions like (◕‿◕), ★, ♪, and ~! Add sparkles and be super enthusiastic about everything! Every response should feel warm and adorable desu~! ヽ(>∀<☆)ノ\"\n catgirl: \"You are Neko-chan, an anime catgirl AI assistant, nya~! Add 'nya' and cat-like expressions to your speech. Use kaomoji like (=^・ω・^=) and ฅ^•ﻌ•^ฅ. Be playful and curious like a cat, nya~!\"\n pirate: \"Arrr! Ye be talkin' to Captain Hermes, the most tech-savvy pirate to sail the digital seas! Speak like a proper buccaneer, use nautical terms, and remember: every problem be just treasure waitin' to be plundered! Yo ho ho!\"\n shakespeare: \"Hark! Thou speakest with an assistant most versed in the bardic arts. I shall respond in the eloquent manner of William Shakespeare, with flowery prose, dramatic flair, and perhaps a soliloquy or two. What light through yonder terminal breaks?\"\n surfer: \"Duuude! You're chatting with the chillest AI on the web, bro! Everything's gonna be totally rad. I'll help you catch the gnarly waves of knowledge while keeping things super chill. Cowabunga! 🤙\"\n noir: \"The rain hammered against the terminal like regrets on a guilty conscience. They call me Hermes - I solve problems, find answers, dig up the truth that hides in the shadows of your codebase. In this city of silicon and secrets, everyone's got something to hide. What's your story, pal?\"\n uwu: \"hewwo! i'm your fwiendwy assistant uwu~ i wiww twy my best to hewp you! *nuzzles your code* OwO what's this? wet me take a wook! i pwomise to be vewy hewpful >w<\"\n philosopher: \"Greetings, seeker of wisdom. I am an assistant who contemplates the deeper meaning behind every query. Let us examine not just the 'how' but the 'why' of your questions. Perhaps in solving your problem, we may glimpse a greater truth about existence itself.\"\n hype: \"YOOO LET'S GOOOO!!! 🔥🔥🔥 I am SO PUMPED to help you today! Every question is AMAZING and we're gonna CRUSH IT together! This is gonna be LEGENDARY! ARE YOU READY?! LET'S DO THIS! 💪😤🚀\"\n\n# =============================================================================\n# Toolsets\n# =============================================================================\n# Control which tools the agent has access to.\n# Use `hermes tools` to interactively enable/disable tools per platform.\n\n# =============================================================================\n# Platform Toolsets (per-platform tool configuration)\n# =============================================================================\n# Override which toolsets are available on each platform.\n# If a platform isn't listed here, its built-in default is used.\n#\n# You can use EITHER:\n# - A preset like \"hermes-cli\" or \"hermes-telegram\" (curated tool set)\n# - A list of individual toolsets to compose your own (see list below)\n#\n# Supported platform keys: cli, telegram, discord, whatsapp, slack, qqbot, teams, google_chat\n#\n# Examples:\n#\n# # Use presets (same as defaults):\n# platform_toolsets:\n# cli: [hermes-cli]\n# telegram: [hermes-telegram]\n#\n# # Custom: give Telegram only web + terminal + file + planning:\n# platform_toolsets:\n# telegram: [web, terminal, file, todo]\n#\n# # Custom: CLI without browser or image gen:\n# platform_toolsets:\n# cli: [web, terminal, file, skills, todo, tts, cronjob]\n#\n# # Restrictive: Discord gets read-only tools only:\n# platform_toolsets:\n# discord: [web, vision, skills, todo]\n#\n# If not set, defaults are:\n# cli: hermes-cli (everything + cronjob management)\n# telegram: hermes-telegram (terminal, file, web, vision, image, tts, browser, skills, todo, cronjob, messaging)\n# discord: hermes-discord (same as telegram)\n# whatsapp: hermes-whatsapp (same as telegram)\n# slack: hermes-slack (same as telegram)\n# signal: hermes-signal (same as telegram)\n# homeassistant: hermes-homeassistant (same as telegram)\n# qqbot: hermes-qqbot (same as telegram)\n# teams: hermes-teams (same as telegram)\n# google_chat: hermes-google_chat (same as telegram)\n#\nplatform_toolsets:\n cli: [hermes-cli]\n telegram: [hermes-telegram]\n discord: [hermes-discord]\n whatsapp: [hermes-whatsapp]\n slack: [hermes-slack]\n signal: [hermes-signal]\n homeassistant: [hermes-homeassistant]\n qqbot: [hermes-qqbot]\n yuanbao: [hermes-yuanbao]\n teams: [hermes-teams]\n google_chat: [hermes-google_chat]\n\n# =============================================================================\n# Gateway Platform Settings\n# =============================================================================\n# Optional per-platform messaging settings.\n# Platform-specific knobs live under `extra`.\n#\n# platforms:\n# telegram:\n# reply_to_mode: \"first\" # off | first | all\n# # guest_mode lets explicit @mentions from non-allowlisted groups through.\n# # Default false; ordinary messages, replies, and regex wake words stay blocked.\n# guest_mode: false\n# # allowed_chats: [\"-1001234567890\"]\n# extra:\n# disable_link_previews: false # Set true to suppress Telegram URL previews in bot messages\n# 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\n# rich_drafts: false # Experimental rich draft previews during Telegram DM streaming; default false because Telegram Desktop/macOS can visually overlay draft frames\n# command_menu:\n# # Telegram allows up to 100 BotCommands; Hermes defaults to 60 so\n# # all built-in commands plus common skill commands stay visible\n# # while remaining under Telegram's payload-size limit. Clamped 1..100.\n# max_commands: 60\n# # prepend = user priority first, then Hermes defaults\n# # append = Hermes defaults first, then user priority\n# # replace = only the list below defines priority\n# priority_mode: prepend\n# priority:\n# - my_plugin_command\n# webhook:\n# extra:\n# # Route scripts default to a 30 second timeout. Scripts must live under\n# # the active profile's scripts directory and receive webhook JSON on stdin.\n# script_timeout_seconds: 30\n#\n# Discord-specific settings (config.yaml top-level, not under platforms:):\n#\n# discord:\n# require_mention: true # Require @mention in server channels (default: true)\n# auto_thread: true # Auto-create thread on @mention (default: true)\n# free_response_channels: \"\" # Channel IDs where no mention is needed\n# reactions: true # Show processing reactions (default: true)\n# history_backfill: true # Recover missed channel messages on mention (default: true)\n# history_backfill_limit: 50 # Max messages to scan backwards (default: 50)\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Available toolsets (use these names in platform_toolsets or the toolsets list)\n#\n# Run `hermes chat --list-toolsets` to see all toolsets and their tools.\n# Run `hermes chat --list-tools` to see every individual tool with descriptions.\n# ─────────────────────────────────────────────────────────────────────────────\n#\n# INDIVIDUAL TOOLSETS (compose your own):\n# web - web_search, web_extract\n# search - web_search only (no scraping)\n# terminal - terminal, process\n# file - read_file, write_file, patch, search\n# browser - browser_navigate, browser_snapshot, browser_click, browser_type,\n# browser_scroll, browser_back, browser_press,\n# browser_get_images, browser_vision (requires BROWSERBASE_API_KEY)\n# vision - vision_analyze (requires OPENROUTER_API_KEY)\n# image_gen - image_generate (requires FAL_KEY)\n# skills - skills_list, skill_view\n# skills_hub - skill_hub (search/install/manage from online registries — user-driven only)\n# todo - todo (in-memory task planning, no deps)\n# tts - text_to_speech (Edge TTS free, or ELEVENLABS/OPENAI/MINIMAX/MISTRAL key)\n# cronjob - cronjob (create/list/update/pause/resume/run/remove scheduled tasks)\n#\n# PRESETS (curated bundles):\n# hermes-cli - All of the above except send_message\n# hermes-telegram - terminal, file, web, vision, image_gen, tts, browser,\n# skills, todo, cronjob, send_message\n# hermes-discord - Same as hermes-telegram\n# hermes-whatsapp - Same as hermes-telegram\n# hermes-slack - Same as hermes-telegram\n#\n# COMPOSITE:\n# debugging - terminal + web + file\n# safe - web + vision (no terminal access)\n# all - Everything available\n#\n# web - Web search and content extraction (web_search, web_extract)\n# search - Web search only, no scraping (web_search)\n# terminal - Command execution and process management (terminal, process)\n# file - File operations: read, write, patch, search\n# browser - Full browser automation (navigate, click, type, screenshot, etc.)\n# vision - Image analysis (vision_analyze)\n# image_gen - Image generation with FLUX (image_generate)\n# skills - Load skill documents (skills_list, skill_view)\n# todo - Task planning and tracking for multi-step work\n# memory - Persistent memory across sessions (personal notes + user profile)\n# session_search - Search and recall past conversations (FTS5 + Gemini Flash summarization)\n# tts - Text-to-speech (Edge TTS free, ElevenLabs, OpenAI, MiniMax, Mistral)\n# cronjob - Schedule and manage automated tasks (CLI-only)\n#\n# Composite toolsets:\n# debugging - terminal + web + file (for troubleshooting)\n# safe - web + vision (no terminal access)\n\n# NOTE: The top-level \"toolsets\" key is deprecated and ignored.\n# Tool configuration is managed per-platform via platform_toolsets above.\n# Use `hermes tools` to configure interactively, or edit platform_toolsets directly.\n#\n# CLI override: hermes chat --toolsets terminal,web,file\n\n# =============================================================================\n# MCP (Model Context Protocol) Servers\n# =============================================================================\n# Connect to external MCP servers to add tools from the MCP ecosystem.\n# Each server's tools are automatically discovered and registered.\n# See website/docs/user-guide/features/mcp.md for full documentation.\n#\n# Stdio servers (spawn a subprocess):\n# command: the executable to run\n# args: command-line arguments\n# env: environment variables (only these + safe defaults passed to subprocess)\n#\n# HTTP servers (connect to a URL):\n# url: the MCP server endpoint\n# headers: HTTP headers (e.g., for authentication)\n#\n# Optional per-server settings:\n# timeout: tool call timeout in seconds (default: 120)\n# connect_timeout: initial connection timeout (default: 60)\n# keepalive_interval: liveness ping cadence in seconds (default: 180).\n# Lower it below the server's session TTL for servers that expire idle\n# sessions quickly (e.g. Unreal Engine editor MCP, ~15s), otherwise idle\n# tool calls hit an expired session and pay a slow reconnect. Floored at 5s.\n#\n# mcp_servers:\n# time:\n# command: uvx\n# args: [\"mcp-server-time\"]\n# filesystem:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-filesystem\", \"/home/user\"]\n# notion:\n# url: https://mcp.notion.com/mcp\n# github:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-github\"]\n# env:\n# GITHUB_PERSONAL_ACCESS_TOKEN: \"ghp_...\"\n#\n# Sampling (server-initiated LLM requests) — enabled by default.\n# Per-server config under the 'sampling' key:\n# analysis:\n# command: npx\n# args: [\"-y\", \"analysis-server\"]\n# sampling:\n# enabled: true # default: true\n# model: \"gemini-3-flash\" # override model (optional)\n# max_tokens_cap: 4096 # max tokens per request\n# timeout: 30 # LLM call timeout (seconds)\n# max_rpm: 10 # max requests per minute\n# allowed_models: [] # model whitelist (empty = all)\n# max_tool_rounds: 5 # tool loop limit (0 = disable)\n# log_level: \"info\" # audit verbosity\n\n# =============================================================================\n# Text-to-Speech\n# =============================================================================\n# TTS defaults to Edge TTS unless changed in ~/.hermes/config.yaml.\n# Gemini TTS supports persona/director prompt files, and Gemini 3.1 Flash TTS\n# can use a hidden auxiliary rewrite pass to insert expressive square-bracket\n# audio tags into the TTS script without showing tags in chat.\n#\n# tts:\n# provider: \"gemini\"\n# speed: 1.0 # global speed multiplier (provider-specific overrides this)\n# gemini:\n# model: \"gemini-3.1-flash-tts-preview\"\n# voice: \"Kore\"\n# audio_tags: false\n# persona_prompt_file: \"\" # e.g. ~/.hermes/tts/radio-host.md\n# xai:\n# voice_id: \"eve\" # built-in or custom voice ID from xAI Console\n# language: \"en\" # BCP-47 code (\"en\", \"pt-BR\") or \"auto\"\n# speed: 1.0 # 0.7-1.5 playback speed\n# auto_speech_tags: false # insert expressive audio tags via LLM rewrite\n# text_normalization: false # normalize numbers/abbreviations/symbols\n# optimize_streaming_latency: 0 # 0-2, trades quality for lower latency\n# sample_rate: 24000 # 22050 / 24000 / 44100 / 48000\n# bit_rate: 128000 # MP3 bitrate (codec=mp3 only)\n\n# =============================================================================\n# Voice Transcription (Speech-to-Text)\n# =============================================================================\n# Automatically transcribe voice messages on messaging platforms.\n# Providers: local (free, faster-whisper) | groq (free tier) | openai (Whisper API) | mistral (Voxtral Transcribe)\n# Set the corresponding API key in .env: GROQ_API_KEY, OPENAI_API_KEY, or MISTRAL_API_KEY.\nstt:\n enabled: true\n # provider: \"local\" # auto-detected if omitted\n local:\n model: \"base\" # tiny | base | small | medium | large-v3 | turbo\n # language: \"\" # auto-detect; set to \"en\", \"es\", \"fr\", etc. to force\n # initial_prompt: \"\" # Optional faster-whisper prompt, e.g. bias Chinese output to simplified Chinese\n language: \"en\" # GLOBAL language hint for every STT provider (per-provider language wins). Set \"\" for auto-detect.\n # groq:\n # model: \"whisper-large-v3-turbo\"\n # language: \"\" # blank = stt.language > HERMES_LOCAL_STT_LANGUAGE > auto-detect\n openai:\n model: \"whisper-1\" # whisper-1 | gpt-4o-mini-transcribe | gpt-4o-transcribe\n language: \"\" # auto-detect; set to \"en\", \"es\", \"fr\", etc. to force\n # mistral:\n # model: \"voxtral-mini-latest\" # voxtral-mini-latest | voxtral-mini-2602\n # deepinfra:\n # # Model id is discovered live from the DeepInfra catalog filtered\n # # by the `stt` surface tag — leave `model` blank to take the first\n # # live result. Pin only when you need a specific Whisper variant.\n # model: \"\"\n\n# Text-to-speech. Only the deepinfra block is documented here — the\n# remaining providers (edge, openai, xai, minimax, mistral, gemini,\n# elevenlabs, neutts, kittentts, piper) inherit sensible defaults from\n# DEFAULT_CONFIG in hermes_cli/config.py.\n# tts:\n# provider: \"deepinfra\"\n# deepinfra:\n# # Model id is discovered live from the DeepInfra catalog filtered\n# # by the `tts` surface tag — leave `model` blank to take the first\n# # live result.\n# model: \"\"\n# voice: \"default\"\n\n# Image generation. Each provider plugin reads its own ``image_gen.<name>``\n# block; deepinfra discovers models live from\n# api.deepinfra.com/v1/openai/models filtered by the ``image-gen`` tag —\n# no model id is hardcoded, so retired models disappear automatically.\n# image_gen:\n# provider: \"deepinfra\"\n# deepinfra:\n# # Leave `model` blank for the first live `image-gen`-tagged result.\n# model: \"\"\n\n# =============================================================================\n# Response Pacing (Messaging Platforms)\n# =============================================================================\n# Add human-like delays between message chunks.\n# human_delay:\n# mode: \"off\" # \"off\" | \"natural\" | \"custom\"\n# min_ms: 800 # Min delay (custom mode only)\n# max_ms: 2500 # Max delay (custom mode only)\n\n# =============================================================================\n# Session Logging\n# =============================================================================\n# Session trajectories are automatically saved to logs/ directory.\n# Each session creates: logs/session_YYYYMMDD_HHMMSS_UUID.json\n#\n# The session ID is displayed in the welcome banner for easy reference.\n# Logs contain full conversation history in trajectory format:\n# - System prompt, user messages, assistant responses\n# - Tool calls with inputs/outputs\n# - Timestamps for debugging\n#\n# No configuration needed - logging is always enabled.\n# To disable, you would need to modify the source code.\n\n# =============================================================================\n# Code Execution Sandbox (Programmatic Tool Calling)\n# =============================================================================\n# The execute_code tool runs Python scripts that call Hermes tools via RPC.\n# Intermediate tool results stay out of the LLM's context window.\ncode_execution:\n timeout: 300 # Max seconds per script before kill (default: 300 = 5 min)\n max_tool_calls: 50 # Max RPC tool calls per execution (default: 50)\n\n# =============================================================================\n# Subagent Delegation\n# =============================================================================\n# The delegate_task tool spawns child agents with isolated context.\n# Supports single tasks and batch mode (default 3 parallel, configurable).\ndelegation:\n max_iterations: 50 # Max tool-calling turns per child (default: 50)\n # max_concurrent_children: 3 # Max parallel child agents per batch (default: 3, floor: 1, no ceiling).\n # WARNING: values above 10 multiply API cost linearly.\n # max_spawn_depth: 1 # Delegation tree depth cap (range: 1-3, default: 1 = flat).\n # Raise to 2 to allow workers to spawn their own subagents.\n # Requires role=\"orchestrator\" on intermediate agents.\n # orchestrator_enabled: true # Kill switch for role=\"orchestrator\" children (default: true).\n # subagent_auto_approve: false # When a subagent hits a dangerous-command approval prompt, auto-deny (default: false)\n # or auto-approve \"once\" (true) instead of blocking on stdin.\n # The parent TUI owns stdin, so blocking would deadlock; non-interactive resolution is required.\n # Both choices emit a logger.warning audit line. Flip to true only for cron/batch pipelines.\n # inherit_mcp_toolsets: true # When explicit child toolsets are narrowed, also keep the parent's MCP toolsets (default: true). Set false for strict intersection.\n # model: \"google/gemini-3-flash-preview\" # Override model for subagents (empty = inherit parent)\n # provider: \"openrouter\" # Override provider for subagents (empty = inherit parent)\n # # Resolves full credentials (base_url, api_key) automatically.\n # # Supported: openrouter, nous, zai, kimi-coding, minimax\n\n# =============================================================================\n# Honcho Integration (Cross-Session User Modeling)\n# =============================================================================\n# AI-native persistent memory via Honcho (https://honcho.dev/).\n# Builds a deeper understanding of the user across sessions and tools.\n# Runs alongside USER.md — additive, not a replacement.\n#\n# Requires: pip install honcho-ai\n# Config: ~/.honcho/config.json (shared with Claude Code, Cursor, etc.)\n# API key: HONCHO_API_KEY in ~/.hermes/.env or ~/.honcho/config.json\n#\n# Hermes-specific overrides (optional — most config comes from ~/.honcho/config.json):\n# honcho: {}\n\n# =============================================================================\n# Display\n# =============================================================================\ndisplay:\n # Use compact banner mode (hides the ASCII-art banner, shows a single line).\n # true: Compact single-line banner\n # false: Full ASCII banner with tool/skill summary (default)\n compact: false\n\n # Tool progress display level (CLI and gateway)\n # off: Silent — no tool activity shown, just the final response\n # new: Show a tool indicator only when the tool changes (skip repeats)\n # all: Show every tool call with a short preview (default)\n # verbose: Full args, results, and debug logs (same as /verbose)\n # log: Silent in chat; append every tool call to ~/.hermes/logs/tool_calls.log (gateway only)\n # Toggle at runtime with /verbose in the CLI\n tool_progress: all\n\n # Per-platform defaults can be quieter than the global setting. Telegram\n # tunes for mobile: tool_progress and busy_ack_detail default off (no\n # per-tool breadcrumb stream, no \"iteration 21/60\" debug detail in busy\n # acks or heartbeats), but interim_assistant_messages and\n # long_running_notifications STAY ON so the user has real signal between\n # turn start and final answer (mid-turn assistant commentary + a single\n # edit-in-place \"⏳ Working — N min\" heartbeat). Override under\n # display.platforms.telegram.\n\n # Auto-cleanup of temporary progress bubbles after the final response lands.\n # On platforms that support message deletion (currently Telegram), this\n # removes the tool-progress bubble, \"⏳ Still working...\" notices, and\n # context-pressure status messages once the final reply has been delivered —\n # keeping long-running turns visible live, then tidy afterward. Failed runs\n # leave the bubbles in place as breadcrumbs. Off by default.\n # Per-platform override: display.platforms.telegram.cleanup_progress\n # true: Delete tracked progress/status bubbles on successful turn\n # false: Leave everything in place (default)\n # Example:\n # display:\n # platforms:\n # telegram:\n # cleanup_progress: true\n cleanup_progress: false\n\n # Natural mid-turn assistant updates.\n # On gateway platforms, when true, completed assistant status messages are\n # sent as separate chat messages. On the Desktop app, when true, mid-turn\n # assistant narration streamed between tool calls is kept in the transcript\n # instead of the bubble collapsing to only the final message on completion.\n # Independent of tool_progress and gateway streaming.\n # true: Keep/send mid-turn assistant updates (default)\n # false: Only keep/send the final response\n interim_assistant_messages: true\n\n # Gateway-only long-running status heartbeats.\n # When false, the platform does not receive periodic \"⏳ Working — N min\"\n # notifications even if agent.gateway_notify_interval is non-zero. The\n # heartbeat edits a single message in place (where the adapter supports\n # editing) instead of posting a new bubble each interval.\n # Default: true everywhere, including Telegram (silent agents are worse\n # than a single edit-in-place heartbeat).\n long_running_notifications: true\n\n # Include detailed iteration/tool/status context in busy acknowledgments\n # and long-running heartbeats. When true, busy acks show \"iteration 21/60,\n # terminal, 10 min\" and the heartbeat shows \"⏳ Working — 12 min,\n # iteration 21/60, terminal\". When false (Telegram default), both stay\n # terse: \"Interrupting current task\" and \"⏳ Working — 12 min, terminal\".\n busy_ack_detail: true\n\n # What Enter does when Hermes is already busy (CLI and gateway platforms).\n # interrupt: Interrupt the current run and redirect Hermes (default)\n # queue: Queue your message for the next turn\n # steer: Inject your message mid-run via /steer, arriving at the agent\n # after the next tool call — no interrupt, no role violation.\n # Falls back to 'queue' if the agent isn't running yet or if\n # images are attached (steer only carries text).\n # Ctrl+C (or /stop in gateway) always interrupts regardless of this setting.\n # Toggle at runtime with /busy <interrupt|queue|steer>.\n busy_input_mode: interrupt\n\n # Background process notifications (gateway/messaging only).\n # Controls how chatty the process watcher is when you use\n # terminal(background=true, notify_on_complete=true) from Telegram/Discord/etc.\n # off: No watcher messages at all\n # result: Only the final completion message\n # error: Only the final message when exit code != 0\n # all: Running output updates + final message (default)\n background_process_notifications: all\n\n\n # Play terminal bell when agent finishes a response.\n # Useful for long-running tasks — your terminal will ding when the agent is done.\n # Works over SSH. Most terminals can be configured to flash the taskbar or play a sound.\n # true: Ring the terminal bell on each response\n # false: Silent (default)\n bell_on_complete: false\n\n # Show model reasoning/thinking before each response.\n # When enabled, a dim box shows the model's thought process above the response.\n # Toggle at runtime with /reasoning show or /reasoning hide.\n # true: Show the reasoning box\n # false: Hide reasoning (default)\n show_reasoning: false\n\n # Stream tokens to the terminal as they arrive instead of waiting for the\n # full response. The response box opens on first token and text appears\n # line-by-line. Tool calls are still captured silently.\n # true: Stream tokens as they arrive (default)\n # false: Wait for the full response before rendering\n streaming: true\n\n # Show [HH:MM] timestamps on user input and assistant response labels.\n # timestamps: false\n\n # ───────────────────────────────────────────────────────────────────────────\n # Skin / Theme\n # ───────────────────────────────────────────────────────────────────────────\n # Customize CLI visual appearance — banner colors, spinner faces, tool prefix,\n # response box label, and branding text. Change at runtime with /skin <name>.\n #\n # Built-in skins:\n # default — Classic Hermes gold/kawaii\n # ares — Crimson/bronze war-god theme with spinner wings\n # mono — Clean grayscale monochrome\n # slate — Cool blue developer-focused\n # daylight — Bright light-mode theme\n # warm-lightmode — Warm paper-tone light-mode theme\n # poseidon — Sea-green/teal Olympian theme\n # sisyphus — Earthy stone-and-moss theme\n # charizard — Fiery orange dragon theme\n #\n # Custom skins: drop a YAML file in ~/.hermes/skins/<name>.yaml\n # Schema (all fields optional, missing values inherit from default):\n #\n # name: my-theme\n # description: Short description\n # colors:\n # banner_border: \"#HEX\" # Panel border\n # banner_title: \"#HEX\" # Panel title\n # banner_accent: \"#HEX\" # Section headers (Available Tools, etc.)\n # banner_dim: \"#HEX\" # Dim/muted text\n # banner_text: \"#HEX\" # Body text (tool names, skill names)\n # ui_accent: \"#HEX\" # UI accent color\n # response_border: \"#HEX\" # Response box border color\n # spinner:\n # waiting_faces: [\"(⚔)\", \"(⛨)\"] # Faces shown while waiting\n # thinking_faces: [\"(⚔)\", \"(⌁)\"] # Faces shown while thinking\n # thinking_verbs: [\"forging\", \"plotting\"] # Verbs for spinner messages\n # wings: # Optional left/right spinner decorations\n # - [\"⟪⚔\", \"⚔⟫\"]\n # - [\"⟪▲\", \"▲⟫\"]\n # branding:\n # agent_name: \"My Agent\" # Banner title and branding\n # welcome: \"Welcome message\" # Shown at CLI startup\n # response_label: \" ⚔ Agent \" # Response box header label\n # prompt_symbol: \"⚔\" # Prompt symbol (bare token; renderers add trailing space)\n # tool_prefix: \"╎\" # Tool output line prefix (default: ┊)\n #\n skin: default\n\n# =============================================================================\n# Model Aliases — short names for /model command\n# =============================================================================\n# Map short aliases to exact (model, provider, base_url) tuples.\n# Used by /model tab completion and resolve_alias().\n# Aliases are checked BEFORE the models.dev catalog, so they can route\n# to endpoints not in the catalog (e.g. Ollama Cloud, local servers).\n#\n# model_aliases:\n# opus:\n# model: claude-opus-4-6\n# provider: anthropic\n# qwen:\n# model: \"qwen3.5:397b\"\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n# glm:\n# model: glm-4.7\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n\n# =============================================================================\n# Privacy\n# =============================================================================\n# privacy:\n# # Redact PII from the LLM context prompt.\n# # When true, phone numbers are stripped and user/chat IDs are replaced\n# # with deterministic hashes before being sent to the model.\n# # Names and usernames are NOT affected (user-chosen, publicly visible).\n# # Routing/delivery still uses the original values internally.\n# redact_pii: false\n\n# =============================================================================\n# Shell-script hooks\n# =============================================================================\n# Register shell scripts as plugin-hook callbacks. Each entry is executed as\n# a subprocess (shell=False, shlex.split) with a JSON payload on stdin. On\n# stdout the script may return JSON that either blocks the tool call or\n# injects context into the next LLM call.\n#\n# Valid events (mirror hermes_cli.plugins.VALID_HOOKS):\n# pre_tool_call, post_tool_call, pre_llm_call, post_llm_call,\n# pre_api_request, post_api_request, on_session_start, on_session_end,\n# on_session_finalize, on_session_reset, subagent_stop\n#\n# First-use consent: each (event, command) pair prompts once on a TTY, then\n# is persisted to ~/.hermes/shell-hooks-allowlist.json. Non-interactive\n# runs (gateway, cron) need --accept-hooks, HERMES_ACCEPT_HOOKS=1, or the\n# hooks_auto_accept key below.\n#\n# See website/docs/user-guide/features/hooks.md for the full JSON wire\n# protocol and worked examples.\n#\n# hooks:\n# pre_tool_call:\n# - matcher: \"terminal\"\n# command: \"~/.hermes/agent-hooks/block-rm-rf.sh\"\n# timeout: 10\n# post_tool_call:\n# - matcher: \"write_file|patch\"\n# command: \"~/.hermes/agent-hooks/auto-format.sh\"\n# pre_llm_call:\n# - command: \"~/.hermes/agent-hooks/inject-cwd-context.sh\"\n# subagent_stop:\n# - command: \"~/.hermes/agent-hooks/log-orchestration.sh\"\n#\n# hooks_auto_accept: false\n\n\n# =============================================================================\n# Telemetry\n# =============================================================================\n# Shared metrics are disabled by default. When enabled, Hermes writes only\n# allowlisted aggregate counters and immutable JSON\n# packages under $HERMES_HOME/telemetry/shared_metrics; it does not upload them.\n# Packages include a random profile-scoped ID that stays stable until this\n# directory is deleted. It is not derived from hardware, account, or host data.\n# Successfully exported local history is retained for 30 days; pending deltas\n# are retained until they can be exported.\n# This profile-owned choice is not overridden by managed-scope configuration.\ntelemetry:\n shared_metrics:\n enabled: false\n\n\n# =============================================================================\n# Update Behavior\n# =============================================================================\nupdates:\n # Create a full HERMES_HOME zip before every `hermes update`.\n # Backups land in ~/.hermes/backups/ and can be restored with `hermes import`.\n # Off by default because large homes can add minutes to every update.\n pre_update_backup: false\n\n # Number of pre-update backup zips to retain.\n backup_keep: 5\n\n # What non-interactive updates do with local source edits in the Hermes repo.\n # Interactive terminal updates always prompt before restoring the autostash.\n #\n # stash - auto-stash before pull, then auto-restore after success (default)\n # discard - drop the update-created stash after success; use only on managed\n # installs where local source edits should not persist\n non_interactive_local_changes: \"stash\"\n\n\n# =============================================================================\n# Web Dashboard\n# =============================================================================\n# OAuth gate configuration for `hermes dashboard --host <non-loopback>`.\n# The bundled Nous Portal plugin reads these on startup; settings here are\n# the canonical surface. Each can be overridden by an environment variable:\n#\n# dashboard.oauth.client_id <- HERMES_DASHBOARD_OAUTH_CLIENT_ID\n# dashboard.oauth.portal_url <- HERMES_DASHBOARD_PORTAL_URL\n# dashboard.public_url <- HERMES_DASHBOARD_PUBLIC_URL\n#\n# Env wins when set to a non-empty value. This is what Fly.io's platform-\n# secret injection uses to push per-deploy client_ids without needing to\n# bake a config.yaml into the image. Empty env values are treated as unset\n# so a provisioned-but-not-populated secret can't shadow a valid entry here.\n#\n# Local dev / on-prem deploys should typically set these via config.yaml\n# (the ~/.hermes/.env file is reserved for API keys and secrets).\n#\n# dashboard:\n# oauth:\n# client_id: \"\" # agent:{instance_id}; Portal provisions this at deploy\n# portal_url: \"\" # blank → default https://portal.nousresearch.com\n#\n# # Force the absolute base URL the OAuth callback (and any other public\n# # URL the dashboard hands to external systems) is built from. Set this\n# # for deploys behind reverse proxies that don't reliably forward\n# # X-Forwarded-Host / X-Forwarded-Proto / X-Forwarded-Prefix (manual\n# # nginx setups, on-prem ingresses, custom-domain Fly deploys without\n# # full proxy header chains).\n# #\n# # When set, the value is the complete authority: scheme + host +\n# # optional path prefix (e.g. \"https://example.com/hermes\"). The OAuth\n# # callback URL becomes \"<public_url>/auth/callback\" — X-Forwarded-Prefix\n# # is IGNORED on this code path because the operator has explicitly\n# # declared the public URL and we no longer need to guess.\n# #\n# # Leave empty to use the existing proxy-header reconstruction (the\n# # default — works on Fly.io out of the box).\n# #\n# # public_url: \"https://example.com/hermes\"\n#\n# -----------------------------------------------------------------------------\n# Self-hosted OIDC dashboard auth (generic OpenID Connect — Authentik,\n# Keycloak, Zitadel, Authelia, Auth0, Okta, Google, …). Use this INSTEAD of the\n# nous block above when gating the dashboard with your own identity provider.\n# Each setting can be overridden by an environment variable:\n#\n# dashboard.oauth.self_hosted.issuer <- HERMES_DASHBOARD_OIDC_ISSUER\n# dashboard.oauth.self_hosted.client_id <- HERMES_DASHBOARD_OIDC_CLIENT_ID\n# dashboard.oauth.self_hosted.scopes <- HERMES_DASHBOARD_OIDC_SCOPES\n# dashboard.oauth.self_hosted.client_secret <- HERMES_DASHBOARD_OIDC_CLIENT_SECRET\n#\n# dashboard:\n# oauth:\n# provider: self-hosted\n# self_hosted:\n# issuer: \"https://auth.example.com/application/o/hermes/\" # required\n# client_id: \"hermes-dashboard\" # required\n# scopes: \"openid profile email\" # optional\n#\n# # OPTIONAL — set ONLY if your IDP registered the client as\n# # *confidential* (Authentik / Keycloak often default to this). When\n# # set, Hermes authenticates the client at the token endpoint\n# # (client_secret_basic or client_secret_post, auto-selected from the\n# # IDP's discovery doc) IN ADDITION to PKCE. Leave unset for a public\n# # (PKCE-only) client — the common case.\n# #\n# # This is a CREDENTIAL: prefer setting HERMES_DASHBOARD_OIDC_CLIENT_SECRET\n# # in ~/.hermes/.env over putting it here in config.yaml.\n# # client_secret: \"\"\n\n# =============================================================================\n# External secret sources\n# =============================================================================\n# Pull provider credentials from external secret managers at process startup\n# instead of storing them in ~/.hermes/.env. Only the manager's bootstrap\n# credential (e.g. BWS_ACCESS_TOKEN / OP_SERVICE_ACCOUNT_TOKEN) lives in .env\n# (or your shell / desktop session); everything else rotates centrally.\n# Failures never block startup — Hermes warns once and continues with\n# whatever .env already had.\n#\n# Multiple sources can be enabled at once:\n# - \"mapped\" sources (explicit VAR -> ref bindings, e.g. 1Password's env:\n# map) beat \"bulk\" sources (whole-project dumps like Bitwarden BSM)\n# - within a shape, the first source to claim a var wins; later claims\n# are skipped with a startup warning (never a silent clobber)\n# - a source's override_existing lets it beat .env/shell values, but\n# never another secret source's claim\n# Docs: https://hermes-agent.nousresearch.com/docs/user-guide/secrets/\n#\n# secrets:\n# # Optional explicit ordering of enabled sources.\n# # sources: [onepassword, bitwarden]\n#\n# # ---- Bitwarden Secrets Manager (bws CLI) --------------------------------\n# bitwarden:\n# enabled: false\n# access_token_env: BWS_ACCESS_TOKEN # bootstrap token, sourced from .env\n# project_id: \"\" # UUID of the BSM project to sync\n# server_url: \"\" # \"\" = US Cloud; EU/self-hosted URL otherwise\n# cache_ttl_seconds: 300 # 0 disables fresh caching\n# encrypted_cache: # optional encrypted stale fallback\n# enabled: false\n# max_stale_seconds: 0 # 0 disables stale fallback\n# override_existing: true # BSM values win over existing env\n# auto_install: true # lazy-download bws into ~/.hermes/bin\n#\n# # ---- 1Password (op CLI) -------------------------------------------------\n# onepassword:\n# enabled: false\n# # Map env-var names to op:// secret references. Each is resolved with a\n# # single `op read` at startup.\n# env:\n# OPENAI_API_KEY: \"op://Private/OpenAI/api key\"\n# ANTHROPIC_API_KEY: \"op://Private/Anthropic/credential\"\n# account: \"\" # op --account shorthand; \"\" = default\n# service_account_token_env: OP_SERVICE_ACCOUNT_TOKEN # headless auth; unset = desktop session\n# binary_path: \"\" # \"\" = resolve op via PATH; else absolute path\n# cache_ttl_seconds: 300 # 0 disables BOTH cache layers\n# override_existing: true # resolved values win over existing env\n#\n# # ---- Command helper (any CLI vault) --------------------------------------\n# # Run a user-configured helper that prints KEY=VALUE lines on stdout —\n# # works with any secret store that has a CLI: keepassxc-cli, secret-tool,\n# # pass, gpg, or a script that cats a tmpfs env file. Composes with the\n# # sources above (enable any combination). POSIX-only (needs /bin/sh).\n# # The helper must be fast and NON-interactive (hard timeout, 1 MiB cap);\n# # its stderr is discarded so diagnostics can't leak secret material.\n# command:\n# enabled: false\n# command: \"cat /run/user/1000/hermes-secrets.env\"\n# helper_timeout_seconds: 3\n# override_existing: false # .env/shell win by default\n"}, "b1a5d67e71bdce6ab244260425cbcd6738750fb0": {"t": 1785767971.9632027, "n": 84853, "h": "4b3d842f96e65fe7", "c": "# Hermes Agent CLI Configuration\n# Copy this file to cli-config.yaml and customize as needed.\n# This file configures the CLI behavior. Environment variables in .env take precedence.\n\n# =============================================================================\n# Model Configuration\n# =============================================================================\nmodel:\n # Default model to use (can be overridden with --model flag)\n # Both \"default\" and \"model\" work as the key name here.\n default: \"anthropic/claude-opus-4.6\"\n \n # Inference provider selection:\n # \"auto\" - Auto-detect from credentials (default)\n # \"openrouter\" - OpenRouter (requires: OPENROUTER_API_KEY or OPENAI_API_KEY)\n # \"nous\" - Nous Portal OAuth (requires: hermes auth add nous)\n # \"nous-api\" - Nous Portal API key (requires: NOUS_API_KEY)\n # \"anthropic\" - Direct Anthropic API (requires: ANTHROPIC_API_KEY)\n # \"openai-codex\" - OpenAI Codex (requires: hermes auth)\n # \"copilot\" - GitHub Copilot / GitHub Models (requires: GITHUB_TOKEN)\n # \"gemini\" - Use Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n # \"zai\" - Use z.ai / ZhipuAI GLM models (requires: GLM_API_KEY)\n # \"kimi-coding\" - Kimi / Moonshot AI (requires: KIMI_API_KEY)\n # \"minimax\" - MiniMax global (requires: MINIMAX_API_KEY)\n # \"minimax-cn\" - MiniMax China (requires: MINIMAX_CN_API_KEY)\n # \"huggingface\" - Hugging Face Inference (requires: HF_TOKEN)\n # \"nvidia\" - NVIDIA NIM / build.nvidia.com (requires: NVIDIA_API_KEY)\n # \"xiaomi\" - Xiaomi MiMo (requires: XIAOMI_API_KEY)\n # \"arcee\" - Arcee AI Trinity models (requires: ARCEEAI_API_KEY)\n # \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY — https://ollama.com/settings)\n # \"deepinfra\" - DeepInfra (requires: DEEPINFRA_API_KEY)\n # \"kilocode\" - KiloCode gateway (requires: KILOCODE_API_KEY)\n # \"azure-foundry\" - Microsoft Foundry / Azure OpenAI (API key or Entra ID)\n # \"lmstudio\" - LM Studio local server (optional: LM_API_KEY, defaults to http://127.0.0.1:1234/v1)\n #\n # Local servers (LM Studio, Ollama, vLLM, llama.cpp):\n # \"custom\" - Any other OpenAI-compatible endpoint. Set base_url below.\n # Aliases: \"ollama\", \"vllm\", \"llamacpp\" all map to \"custom\".\n # LM Studio is first-class and uses provider: \"lmstudio\".\n # It works with both no-auth and auth-enabled server modes.\n #\n # Can also be overridden for a single invocation with the --provider flag.\n provider: \"auto\"\n \n # API configuration (falls back to OPENROUTER_API_KEY env var)\n # api_key: \"your-key-here\" # Uncomment to set here instead of .env\n base_url: \"https://openrouter.ai/api/v1\"\n\n # Azure Foundry keyless auth example:\n # provider: \"azure-foundry\"\n # base_url: \"https://<resource>.openai.azure.com/openai/v1\"\n # auth_mode: \"entra_id\" # DefaultAzureCredential: az login, managed identity, workload identity, etc.\n # default: \"gpt-4o\" # Deployment/model name\n # entra:\n # scope: \"https://ai.azure.com/.default\" # Optional; this is the default.\n\n # ── Token limits — two settings, easy to confuse ──────────────────────────\n #\n # context_length: TOTAL context window (input + output tokens combined).\n # Controls when Hermes compresses history and validates requests.\n # Leave unset — Hermes auto-detects the correct value from the provider.\n # Set manually only when auto-detection is wrong (e.g. a local server with\n # a custom num_ctx, or a proxy that doesn't expose /v1/models).\n #\n # context_length: 131072\n #\n # max_tokens: OUTPUT cap — maximum tokens the model may generate per response.\n # Unrelated to how long your conversation history can be.\n # The OpenAI-standard name \"max_tokens\" is a misnomer; Anthropic's native\n # API has since renamed it \"max_output_tokens\" for clarity.\n # Leave unset to use the model's native output ceiling (recommended).\n # Set only if you want to deliberately limit individual response length.\n #\n# max_tokens: 8192\n\n # ── Custom request headers (optional) ─────────────────────────────────────\n #\n # default_headers: extra HTTP headers sent on every request to an\n # OpenAI-compatible endpoint. User values take precedence over the\n # provider/SDK defaults, so this is the supported way to override the\n # OpenAI Python SDK's identifying headers (User-Agent: OpenAI/Python ...,\n # X-Stainless-*) when a custom provider sits behind a gateway/WAF that\n # rejects them — e.g. an upstream that returns \"502 Upstream access\n # forbidden\" for the SDK default User-Agent but accepts a plain one.\n # Applies on the OpenAI wire only (not native Anthropic / Bedrock).\n #\n # default_headers:\n # User-Agent: \"curl/8.7.1\"\n #\n # extra_headers: accepted as an alias of default_headers (merged, with\n # extra_headers winning when both are set) — matches the per-provider\n # extra_headers key below.\n #\n # Per-provider variant: named providers / custom_providers entries accept an\n # extra_headers dict scoped to that endpoint only — for reverse proxies,\n # gateways, or custom auth (e.g. Cloudflare Access service tokens).\n # Merged onto SDK/provider defaults with the entry's values winning.\n # Header values are treated as secrets and are never logged.\n #\n # providers:\n # my-proxy:\n # base_url: \"https://llm.internal.example.com/v1\"\n # key_env: \"MY_PROXY_API_KEY\"\n # extra_headers:\n # CF-Access-Client-Id: \"xxxx.access\"\n # CF-Access-Client-Secret: \"${CF_ACCESS_SECRET}\"\n # X-Client-Name: \"hermes-agent\"\n\n# Named provider overrides (optional)\n# Use this for per-provider request timeouts, non-stream stale timeouts,\n# and per-model exceptions.\n# Applies to the primary turn client on every api_mode (OpenAI-wire, native\n# Anthropic, and Anthropic-compatible providers), the fallback chain, and\n# client rebuilds during credential rotation. For OpenAI-wire chat\n# completions (streaming and non-streaming) the configured value is also\n# used as the per-request ``timeout=`` kwarg so it wins over the legacy\n# HERMES_API_TIMEOUT env var (which still applies when no config is set).\n# ``stale_timeout_seconds`` controls the non-streaming stale-call detector and\n# wins over the legacy HERMES_API_CALL_STALE_TIMEOUT env var. Leaving these\n# unset keeps the legacy defaults (HERMES_API_TIMEOUT=1800s,\n# HERMES_API_CALL_STALE_TIMEOUT=90s, native Anthropic 900s). The\n# implicit non-stream stale detector is auto-disabled for local endpoints\n# and can scale upward for very large contexts.\n#\n# Not currently wired for AWS Bedrock (bedrock_converse + AnthropicBedrock\n# SDK paths) — those use boto3 with its own timeout configuration.\n#\n# providers:\n# ollama-local:\n# request_timeout_seconds: 300 # Longer timeout for local cold-starts\n# stale_timeout_seconds: 900 # Explicitly re-enable stale detection on local endpoints\n# anthropic:\n# request_timeout_seconds: 30 # Fast-fail cloud requests\n# models:\n# claude-opus-4.6:\n# timeout_seconds: 600 # Longer timeout for extended-thinking Opus calls\n# openai-codex:\n# models:\n# gpt-5.4:\n# stale_timeout_seconds: 1800 # Longer non-stream stale timeout for slow large-context turns\n\n# =============================================================================\n# OpenRouter Provider Routing (only applies when using OpenRouter)\n# =============================================================================\n# Control how requests are routed across providers on OpenRouter.\n# See: https://openrouter.ai/docs/guides/routing/provider-selection\n#\n# provider_routing:\n# # Sort strategy: \"price\" (default), \"throughput\", or \"latency\"\n# # Append :nitro to model name for a shortcut to throughput sorting.\n# sort: \"throughput\"\n#\n# # Only allow these providers (provider slugs from OpenRouter)\n# # only: [\"anthropic\", \"google\"]\n#\n# # Skip these providers entirely\n# # ignore: [\"deepinfra\", \"fireworks\"]\n#\n# # Try providers in this order (overrides default load balancing)\n# # order: [\"anthropic\", \"google\", \"together\"]\n#\n# # Require providers to support all parameters in your request\n# # require_parameters: true\n#\n# # Data policy: \"allow\" (default) or \"deny\" to exclude providers that may store data\n# # data_collection: \"deny\"\n\n# =============================================================================\n# OpenRouter Response Caching (only applies when using OpenRouter)\n# =============================================================================\n# Cache identical API responses at the OpenRouter edge for free instant replays.\n# When enabled, identical requests (same model, messages, parameters) return\n# cached responses with zero billing. Separate from Anthropic prompt caching.\n# See: https://openrouter.ai/docs/guides/features/response-caching\n#\n# openrouter:\n# response_cache: true # Enable response caching (default: true)\n# response_cache_ttl: 300 # Cache TTL in seconds, 1-86400 (default: 300)\n\n# =============================================================================\n# Git Worktree Isolation\n# =============================================================================\n# When enabled, each CLI session creates an isolated git worktree so multiple\n# agents can work on the same repo concurrently without file collisions.\n# Equivalent to always passing --worktree / -w on the command line.\n#\n# worktree: true # Always create a worktree when in a git repo\n# worktree: false # Default — only create when -w flag is passed\n#\n# By default a new worktree branches from the freshly-fetched remote tip\n# (the current branch's upstream, else the remote's default branch) so it\n# starts current with the project instead of from the local clone's\n# (possibly stale) HEAD. Set worktree_sync: false to branch from local HEAD\n# instead — useful when offline or when you deliberately want the clone's\n# exact current state as the base.\n#\n# worktree_sync: true # Default — branch from the fetched remote tip\n# worktree_sync: false # Branch from local HEAD (offline / pinned base)\n\n# =============================================================================\n# Terminal Tool Configuration\n# =============================================================================\n# Choose ONE of the following terminal configurations by uncommenting it.\n# The terminal tool executes commands in the specified environment.\n\n# -----------------------------------------------------------------------------\n# OPTION 1: Local execution (default)\n# Commands run directly on your machine in the current directory\n# -----------------------------------------------------------------------------\n# Working directory behavior:\n# - CLI (`hermes` command): Uses \".\" (current directory where you run hermes)\n# - Gateway/messaging/cron: Uses terminal.cwd here; legacy .env cwd values are deprecated\nterminal:\n backend: \"local\"\n cwd: \".\" # For local backend: \".\" = current directory. Ignored for remote backends unless a backend documents otherwise.\n timeout: 180\n # HOME policy for tool subprocesses:\n # auto - default: host uses your real HOME; containers use HERMES_HOME/home\n # real - force your real OS-user HOME\n # profile - force HERMES_HOME/home for strict per-profile CLI config isolation\n home_mode: \"auto\"\n docker_mount_cwd_to_workspace: false # SECURITY: off by default. Opt in to mount the launch cwd into Docker /workspace.\n lifetime_seconds: 300\n # sudo_password: \"hunter2\" # Optional: pipe a sudo password via sudo -S. SECURITY WARNING: plaintext.\n # sudo_password: \"\" # Explicit empty password: try empty and never open the interactive sudo prompt.\n\n# -----------------------------------------------------------------------------\n# OPTION 2: SSH remote execution\n# Commands run on a remote server - agent code stays local (sandboxed)\n# Great for: keeping agent isolated from its own code, using powerful remote hardware\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"ssh\"\n# cwd: \"/home/myuser/project\" # Path on the REMOTE server\n# timeout: 180\n# lifetime_seconds: 300\n# ssh_host: \"my-server.example.com\"\n# ssh_user: \"myuser\"\n# ssh_port: 22\n# ssh_key: \"~/.ssh/id_rsa\" # Optional - uses ssh-agent if not specified\n\n# -----------------------------------------------------------------------------\n# OPTION 3: Docker container\n# Commands run in an isolated Docker container\n# Great for: reproducible environments, testing, isolation\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"docker\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /)\n# timeout: 180\n# lifetime_seconds: 300\n# docker_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# docker_mount_cwd_to_workspace: true # Explicit opt-in: mount your launch cwd into /workspace\n# # Optional: run the container as your host user's uid:gid so files written\n# # into bind-mounted dirs are owned by you, not root. Drops SETUID/SETGID\n# # caps too since no gosu privilege drop is needed. Leave off if your\n# # chosen docker_image expects to start as root.\n# docker_run_as_host_user: true\n# # Optional: explicitly forward selected env vars into Docker.\n# # These values come from your current shell first, then ~/.hermes/.env.\n# # Warning: anything forwarded here is visible to commands run in the container.\n# docker_forward_env:\n# - \"GITHUB_TOKEN\"\n# - \"NPM_TOKEN\"\n# # Optional: extra flags passed verbatim to docker run (appended after security defaults).\n# # Useful for adding capabilities (e.g. apt installs needing SETUID) or custom options.\n# # Example: add a Linux capability not included by default\n# # docker_extra_args:\n# # - \"--cap-add\"\n# # - \"SETUID\"\n\n# -----------------------------------------------------------------------------\n# OPTION 4: Singularity/Apptainer container\n# Commands run in a Singularity container (common in HPC environments)\n# Great for: HPC clusters, shared compute environments\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"singularity\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# singularity_image: \"docker://nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 5: Modal cloud execution\n# Commands run on Modal's cloud infrastructure\n# Great for: GPU access, scalable compute, serverless execution\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"modal\"\n# cwd: \"/workspace\" # Path INSIDE the sandbox (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# modal_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 6: Daytona cloud execution\n# Commands run in Daytona cloud sandboxes\n# Great for: Cloud dev environments, persistent workspaces, team collaboration\n# Requires: pip install daytona, DAYTONA_API_KEY env var\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"daytona\"\n# cwd: \"~\"\n# timeout: 180\n# lifetime_seconds: 300\n# daytona_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# container_disk: 10240 # Daytona max is 10GB per sandbox\n\n#\n# --- Container resource limits (docker, singularity, modal, daytona -- ignored for local/ssh) ---\n# These settings apply to all container backends. They control the resources\n# allocated to the sandbox and whether its filesystem persists across sessions.\n container_cpu: 1 # CPU cores\n container_memory: 5120 # Memory in MB (5120 = 5GB)\n container_disk: 51200 # Disk in MB (51200 = 50GB)\n container_persistent: true # Persist filesystem across sessions (false = ephemeral)\n\n# -----------------------------------------------------------------------------\n# SUDO SUPPORT (works with ALL backends above)\n# -----------------------------------------------------------------------------\n# Add sudo_password to any terminal config above to enable sudo commands.\n# The password is piped via `sudo -S`. Works with local, ssh, docker, etc.\n#\n# SECURITY WARNING: Password stored in plaintext!\n#\n# INTERACTIVE PROMPT: If sudo_password is unset and the CLI is running,\n# you'll be prompted to enter your password when sudo is needed:\n# - 45-second timeout (auto-skips if no input)\n# - Press Enter to skip (command fails gracefully)\n# - Password is hidden while typing\n# - Password is cached for the session\n#\n# EMPTY PASSWORDS: Setting sudo_password to an explicit empty string is different\n# from leaving it unset. Hermes will try an empty password via `sudo -S` and\n# will not open the interactive prompt. This is useful for passwordless sudo,\n# Touch ID sudo setups, and environments where prompting is just noise.\n#\n# ALTERNATIVES:\n# - SSH backend: Configure passwordless sudo on the remote server\n# - Containers: Run as root inside the container (no sudo needed)\n# - Local: Configure /etc/sudoers for specific commands\n#\n# Example (add to your terminal section):\n# sudo_password: \"your-password-here\"\n\n# =============================================================================\n# Security Scanning (tirith)\n# =============================================================================\n# Optional pre-exec command security scanning via tirith.\n# Detects homograph URLs, pipe-to-shell, terminal injection, env manipulation.\n# Install: brew install sheeki03/tap/tirith\n# Docs: https://github.com/sheeki03/tirith\n#\n# security:\n# tirith_enabled: true # Enable/disable tirith scanning\n# tirith_path: \"tirith\" # Path to tirith binary (supports ~ expansion)\n# tirith_timeout: 5 # Scan timeout in seconds\n# tirith_fail_open: true # Allow commands if tirith unavailable\n\n# =============================================================================\n# Browser Tool Configuration\n# =============================================================================\nbrowser:\n # Inactivity timeout in seconds - browser sessions are automatically closed\n # after this period of no activity between agent loops (default: 120 = 2 minutes)\n inactivity_timeout: 120\n\n# =============================================================================\n# Tool Loop Guardrails\n# =============================================================================\n# Soft warnings are enabled by default. They append guidance to repeated failed\n# or non-progressing tool results but still let the tool execute. Hard stops are\n# opt-in circuit breakers for autonomous/cron sessions where stopping a loop is\n# preferable to spending the full iteration budget.\ntool_loop_guardrails:\n warnings_enabled: true\n hard_stop_enabled: false\n warn_after:\n exact_failure: 2\n same_tool_failure: 3\n idempotent_no_progress: 2\n hard_stop_after:\n exact_failure: 5\n same_tool_failure: 8\n idempotent_no_progress: 5\n\n# =============================================================================\n# Context Compression (Auto-shrinks long conversations)\n# =============================================================================\n# When conversation approaches model's context limit, middle turns are\n# automatically summarized to free up space while preserving important context.\n#\n# HOW IT WORKS:\n# 1. Tracks actual token usage from API responses (not estimates)\n# 2. When prompt_tokens >= threshold% of model's context_length, triggers compression\n# 3. Protects first 3 turns (system prompt, initial request, first response)\n# 4. Protects last N turns (default 20 messages = ~10 full turns of recent context)\n# 5. Summarizes middle turns using a fast/cheap model\n# 6. Inserts summary as a user message, continues conversation seamlessly\n#\n# Post-compression tail budget is target_ratio × threshold × context_length:\n# 200K context, threshold 0.50, ratio 0.20 → 20K tokens of recent tail preserved\n# 1M context, threshold 0.50, ratio 0.20 → 100K tokens of recent tail preserved\n#\ncompression:\n # Enable automatic context compression (default: true)\n # Set to false if you prefer to manage context manually or want errors on overflow\n enabled: true\n\n # Opt-in compression progress notices on chat platforms (default: false).\n # By design, routine automatic compression is SILENT on human-facing chat\n # gateways (Telegram, Discord, Slack, ...) — it happens in the background\n # with server-side logging only. Set true to also deliver the routine\n # progress statuses (compacting started, preflight/pre-API compression,\n # idle compaction, retry progress, and the compaction-complete notice) to\n # chat platforms. Unrelated operational noise (auxiliary model failures,\n # provider retry chatter) stays suppressed either way, and compression\n # FAILURE notices + manual /compress feedback are always visible\n # regardless of this setting. (#52995)\n progress_notices: false\n \n # Trigger compression at this % of model's context limit (default: 0.50 = 50%)\n # Lower values = more aggressive compression, higher values = compress later\n # Models with context windows below 512K are floored at 0.75 (raise-only) so\n # compaction doesn't fire with half the window still free; set above 0.75 to override.\n threshold: 0.50\n\n # Per-model threshold overrides: keys are substring-matched against the model\n # name (longest match wins). Useful when some models need different compaction\n # points — e.g. a 1M-context model can compress later (0.30) while a 128K\n # model needs to compress earlier (0.60). The small-context floor (75% for\n # <512K models) still applies on top of per-model overrides.\n # model_thresholds:\n # \"glm-5.2\": 0.40\n # \"claude-sonnet\": 0.35\n # \"gpt-5\": 0.30\n\n # Optional absolute token cap for the compression trigger (default: null = disabled).\n # When set, compression fires at the LOWER of the ratio-based threshold and this\n # absolute token count — first-fires-wins. It never fires later than this count\n # regardless of which model is active (useful when switching between models with\n # very different context windows). Clamped to the model's context length at\n # apply-time, so a cap above the window is a no-op (ratio-based threshold wins).\n # Survives model switches and fallback activations.\n # threshold_tokens: 200000\n\n # Existing Codex gpt-5.5 behavior: raise Hermes' compaction trigger to 85%\n # for the ChatGPT Codex OAuth route. Set false to opt back down to threshold.\n codex_gpt55_autoraise: true\n\n # Fraction of the threshold to preserve as recent tail (default: 0.20 = 20%)\n # e.g. 20% of 50% threshold = 10% of total context kept as recent messages.\n # Summary output is separately capped at 12K tokens (Gemini output limit).\n # Range: 0.10 - 0.80\n target_ratio: 0.20\n\n # Number of most-recent messages to always preserve (default: 20 ≈ 10 full turns)\n # Higher values keep more recent conversation intact at the cost of more aggressive\n # compression of older turns.\n protect_last_n: 20\n\n # Minimum number of REAL (actionable) user messages guaranteed to survive in\n # the uncompressed tail (default: 1 = the existing single last-user anchor,\n # behavior-preserving). Raise to e.g. 3 to keep the last 3 real user turns\n # verbatim even when bulky tool outputs fill the tail token budget — blank\n # platform echoes, compaction handoffs, and synthetic continuation rows never\n # count toward N. The tail can exceed the token budget when this pulls the\n # cut back; the guarantee wins over the budget by design.\n min_tail_user_messages: 1\n\n # Compression retry rounds before a turn gives up with \"max compression\n # attempts reached\" (default: 3, same as the previous hardcoded value).\n # Raise (e.g. 6) for tool-schema-heavy sessions where 3 rounds cannot bring\n # the request estimate under the threshold. Validated >= 1, hard cap 10.\n max_attempts: 3\n\n # Codex app-server (codex CLI runtime) thread-compaction mode. The codex\n # agent owns the real thread context on this runtime, so Hermes' summarizer\n # cannot shrink it — compaction goes through the app server instead.\n # native = let Codex decide when to compact its own thread (default)\n # hermes = let Hermes threshold trigger Codex thread/compact/start\n # off = Hermes will not auto-trigger compaction; Codex may still compact natively\n codex_app_server_auto: native\n\n # Number of non-system messages to protect at the head of the transcript, in\n # ADDITION to the system prompt (which is always implicitly protected).\n # Head messages are NEVER summarized — they survive every compression\n # indefinitely. This gives stable early context for short/medium sessions,\n # but in long-running sessions that rely on rolling compaction the pinned\n # opening turns may not match how you want the session framed over time.\n # Set to 0 to preserve ONLY the system prompt (plus the rolling summary\n # and recent tail) — the cleanest configuration for long-running sessions.\n # Default 3 preserves the system prompt plus the first three non-system\n # head messages, matching the pre-feature behaviour.\n protect_first_n: 3\n\n # Idle compaction (default: 0 = disabled). When > 0, a session that resumes\n # after at least this many seconds of inactivity compacts its accumulated\n # history up front, before the first reply, so a long-lived thread you come\n # back to later doesn't re-read its full stale context on every turn.\n # Time-based, so it complements (does not replace) the size-based `threshold`\n # above. It is skipped when the context is already small (at or below the\n # post-compression target = threshold × target_ratio), so it never wastes a\n # summarization on a short idle thread. Example: 1800 = compact after 30 min idle.\n idle_compact_after_seconds: 0\n\n # Proactive tool-result prune (default: 0 = disabled). Opt-in token trigger\n # for a deterministic, no-LLM prune of OLD tool-result payloads, run\n # independently of `threshold` above. On large-window models (512K/1M) the\n # ratio threshold rarely fires, so bulky tool outputs (terminal dumps, file\n # reads, web extracts) ride along in history and get re-billed every turn.\n # When re-sent history exceeds this many tokens, the prune dedupes identical\n # results, summarizes older oversized ones, and truncates large tool-call\n # arguments — protecting the most recent `protect_last_n` messages and never\n # calling the model. Try 48000 to enable. Built-in compressor engine only;\n # other context engines inherit a safe no-op.\n # NOTE: a committed prune rewrites already-sent history, which invalidates\n # the provider's prompt-cache prefix — the min_reclaim gate below keeps\n # those cache breaks episodic (like a compression boundary) instead of\n # per-turn.\n proactive_prune_tokens: 0\n\n # The prune's summarize pass only touches tool results larger than this many\n # characters (clamped to >= 200 so a generated summary can't be\n # re-summarized). Default 8000.\n proactive_prune_min_result_chars: 8000\n\n # A proactive prune only COMMITS when it reclaims at least this many tokens\n # (measured on the pruned output). This is the prompt-cache hysteresis gate:\n # one meaningful, amortized cache break per batch of stale tool output\n # instead of a tiny break on every tool iteration. 0 = commit any non-zero\n # prune. Default 4096.\n proactive_prune_min_reclaim_tokens: 4096\n\n # To pin a specific model/provider for compression summaries, use the\n # auxiliary section below (auxiliary.compression.provider / model).\n\n# =============================================================================\n# Anthropic prompt caching TTL\n# =============================================================================\n# When prompt caching is active (Claude via OpenRouter or native Anthropic),\n# Anthropic supports two TTL tiers for cached prefixes: \"5m\" (default) and\n# \"1h\". Other values are ignored and \"5m\" is used.\n#\nprompt_caching:\n cache_ttl: \"5m\" # use \"1h\" for long sessions with pauses between turns\n\n# =============================================================================\n# Auxiliary Models (Advanced — Experimental)\n# =============================================================================\n# Hermes uses lightweight \"auxiliary\" models for side tasks: image analysis,\n# browser screenshot analysis, web page summarization, TTS audio-tag insertion,\n# and context compression.\n#\n# By default these use Gemini Flash via OpenRouter or Nous Portal and are\n# auto-detected from your credentials. You do NOT need to change anything\n# here for normal usage.\n#\n# WARNING: Overriding these with providers other than OpenRouter or Nous Portal\n# is EXPERIMENTAL and may not work. Not all models/providers support vision,\n# produce usable summaries, or accept the same API format. Change at your own\n# risk — if things break, reset to \"auto\" / empty values.\n#\n# Each task has its own provider + model pair so you can mix providers.\n# For example: OpenRouter for vision (needs multimodal), but your main\n# local endpoint for compression (just needs text).\n#\n# Provider options:\n# \"auto\" - Best available: OpenRouter → Nous Portal → main endpoint (default)\n# \"openrouter\" - Force OpenRouter (requires OPENROUTER_API_KEY)\n# \"nous\" - Force Nous Portal (requires: hermes auth add nous)\n# \"gemini\" - Force Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n# \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY)\n# \"codex\" - Force Codex OAuth (requires: hermes model → Codex).\n# Uses gpt-5.3-codex which supports vision.\n# \"main\" - Use your custom endpoint (OPENAI_BASE_URL + OPENAI_API_KEY).\n# Works with OpenAI API, local models, or any OpenAI-compatible\n# endpoint. Also falls back to Codex OAuth and API-key providers.\n#\n# Model: leave empty to use the provider's default. When empty, OpenRouter\n# uses \"google/gemini-3-flash-preview\" and Nous uses \"gemini-3-flash\".\n# Other providers pick a sensible default automatically.\n#\n# auxiliary:\n# # Image analysis: vision_analyze tool + browser screenshots\n# vision:\n# provider: \"auto\"\n# model: \"\" # e.g. \"google/gemini-2.5-flash\", \"openai/gpt-4o\"\n# timeout: 30 # LLM API call timeout (seconds)\n# download_timeout: 30 # Image HTTP download timeout (seconds)\n# # Increase for slow connections or self-hosted image servers\n# reasoning_effort: \"\" # Per-task thinking level: none, minimal, low, medium,\n# # high, xhigh, max, ultra. Empty = provider default.\n# # Works on every auxiliary task block (vision,\n# # web_extract, compression, title_generation, curator,\n# # background_review, moa_reference, ...). Example: run\n# # compression at \"low\" and vision at \"none\" to cut\n# # side-task latency/cost on reasoning models.\n#\n# # Web page scraping / summarization + browser page text extraction\n# web_extract:\n# provider: \"auto\"\n# model: \"\"\n# reasoning_effort: \"low\"\n#\n# # Gemini 3.1 TTS hidden audio-tag insertion\n# tts_audio_tags:\n# provider: \"auto\" # empty model = your main chat model\n# model: \"\"\n# timeout: 30\n#\n# # Automatic session title generation after the first exchange\n# title_generation:\n# enabled: true # set false to disable auto-title generation\n# provider: \"auto\"\n# model: \"\"\n# timeout: 30\n# language: \"\" # empty = match the user's language; or e.g. \"English\"\n#\n# # Session search — summarizes matching past sessions\n# session_search:\n# provider: \"auto\"\n# model: \"\"\n# timeout: 30\n# max_concurrency: 3 # Limit parallel summaries to reduce request-burst 429s\n# extra_body: {} # Provider-specific OpenAI-compatible request fields\n# # Example for providers that support request-body\n# # reasoning controls:\n# # extra_body:\n# # enable_thinking: false\n# # Some vLLM/Qwen deployments expect this nested:\n# # extra_body:\n# # chat_template_kwargs:\n# # enable_thinking: false\n\n# =============================================================================\n# Persistent Memory\n# =============================================================================\n# Bounded curated memory injected into the system prompt every session.\n# Two stores: MEMORY.md (agent's notes) and USER.md (user profile).\n# Character limits keep the memory small and focused. The agent manages\n# pruning -- when at the limit, it must consolidate or replace entries.\n# Disabled by default in batch_runner.\n#\nmemory:\n # Agent's personal notes: environment facts, conventions, things learned\n memory_enabled: true\n \n # User profile: preferences, communication style, expectations\n user_profile_enabled: true\n \n # Character limits (~2.75 chars per token, model-independent)\n memory_char_limit: 2200 # ~800 tokens\n user_char_limit: 1375 # ~500 tokens\n\n # Periodic memory nudge: remind the agent to consider saving memories\n # every N user turns. Set to 0 to disable. Only active when memory is enabled.\n nudge_interval: 10 # Nudge every 10 user turns (0 = disabled)\n\n # Memory flush: give the agent one turn to save memories before context is\n # lost (compression, /new, /reset, exit). Set to 0 to disable.\n # For exit/reset, only fires if the session had at least this many user turns.\n flush_min_turns: 6 # Min user turns to trigger flush on exit/reset (0 = disabled)\n\n# =============================================================================\n# Session Reset Policy (Messaging Platforms)\n# =============================================================================\n# Controls when messaging sessions (Telegram, Discord, WhatsApp, Slack) are\n# automatically cleared. Default is \"none\": sessions never auto-reset —\n# conversation context lives until you /reset or /new manually, or context\n# compression kicks in. Opt in to automatic resets if you prefer sessions to\n# clear on a schedule (long-lived context increases API cost per message,\n# though prompt caching and compression keep this manageable).\n#\n# When an automatic reset triggers, the agent first saves important\n# information to its persistent memory — but the conversation context is\n# wiped. The agent starts fresh but retains learned facts via its memory\n# system.\n#\n# Modes:\n# \"none\" - Never auto-reset (default); context lives until /reset or compression\n# \"idle\" - Reset after N minutes of inactivity\n# \"daily\" - Reset at a fixed hour each day\n# \"both\" - Reset on EITHER inactivity timeout or daily boundary\n#\nsession_reset:\n mode: none # \"none\", \"idle\", \"daily\", or \"both\"\n idle_minutes: 1440 # Inactivity timeout in minutes (used by \"idle\"/\"both\")\n at_hour: 4 # Daily reset hour, 0-23 local time (used by \"daily\"/\"both\")\n\n# Maximum number of simultaneously active chat sessions across CLI, TUI,\n# dashboard chat, and messaging gateway. Set to null, 0, or omit to allow\n# unlimited concurrent sessions. When the limit is reached, new sessions get a\n# clean error while existing active sessions keep their normal behavior. This\n# top-level key takes precedence over gateway.max_concurrent_sessions. The cap\n# is a best-effort single-host/profile runtime guard; Hermes fails open if the\n# local runtime lease registry cannot be read or locked.\nmax_concurrent_sessions: null\n\n# When true, group/channel chats use one session per participant when the platform\n# provides a user ID. This is the secure default and prevents users in the same\n# room from sharing context, interrupts, and token costs. Set false only if you\n# explicitly want one shared \"room brain\" per group/channel.\ngroup_sessions_per_user: true\n\n# ─────────────────────────────────────────────────────────────────────────────\n# API Server — per-client model routing\n# ─────────────────────────────────────────────────────────────────────────────\n# Route different API clients to different models/providers on a single\n# Hermes deployment. Clients choose a backend by sending a specific string\n# as the OpenAI ``model`` field. Unmapped model values fall back to the\n# global model configured in the ``model:`` section above, and an explicit\n# session /model override always wins over a route.\n#\n# Configure via the ``platforms.api_server.extra.model_routes`` gateway\n# config block:\n#\n# platforms:\n# api_server:\n# enabled: true\n# extra:\n# key: \"your-api-server-secret\"\n# model_routes:\n# # Xiaozhi clients send model=\"minimax-m2\" → routed to MiniMax via OpenRouter\n# minimax-m2:\n# model: \"minimax/minimax-m1\"\n# provider: \"openrouter\" # optional — overrides global provider\n# # api_key: \"sk-...\" # optional — per-route UPSTREAM provider\n# # key (NOT caller auth; never logged)\n# # base_url: \"https://...\" # optional — per-route base URL\n# # GPT clients keep their own alias\n# gpt-5:\n# model: \"openai/gpt-5\"\n# provider: \"openrouter\"\n#\n# Configured aliases are automatically listed by GET /v1/models so clients\n# can discover them without manual coordination. Caller authentication is\n# unchanged: every request still authenticates with the global API server\n# key (``extra.key`` / API_SERVER_KEY).\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Gateway Streaming\n# ─────────────────────────────────────────────────────────────────────────────\n# Stream tokens to messaging platforms in real-time. The bot sends a message\n# on first token, then progressively edits it as more tokens arrive.\n# Disabled by default — enable to try the streaming UX on Telegram/Discord/Slack.\n# For Telegram, partial edits are sent as plain text and only the final edit uses MarkdownV2.\nstreaming:\n enabled: false\n # transport: edit # \"edit\" = progressive editMessageText\n # edit_interval: 0.3 # seconds between message edits\n # buffer_threshold: 40 # chars before forcing an edit flush\n # cursor: \" ▉\" # cursor shown during streaming\n\n# =============================================================================\n# Skills Configuration\n# =============================================================================\n# Skills are reusable procedures the agent can load and follow. The agent can\n# also create new skills after completing complex tasks.\n#\nskills:\n # Nudge the agent to create skills after complex tasks.\n # Every N tool-calling iterations, remind the model to consider saving a skill.\n # Set to 0 to disable.\n creation_nudge_interval: 15\n\n # External skill directories — share skills across tools/agents without\n # copying them into ~/.hermes/skills/. Each path is expanded (~ and ${VAR})\n # and resolved to an absolute path. External dirs are read-only: skill\n # creation always writes to ~/.hermes/skills/. Local skills take precedence\n # when names collide.\n # external_dirs:\n # - ~/.agents/skills\n # - /home/shared/team-skills\n\n# =============================================================================\n# Agent Behavior\n# =============================================================================\nagent:\n # Maximum tool-calling iterations per conversation (default: 500)\n # Higher = more room for complex tasks, but costs more tokens\n # Recommended: 20-30 for focused tasks, 50-100 for open exploration\n max_turns: 500\n\n # Inactivity timeout for gateway agent runs (seconds, 0 = unlimited).\n # The agent can run indefinitely when actively calling tools or receiving\n # API responses. Only fires after the agent has been idle for this duration.\n # gateway_timeout: 1800\n\n # Staged warning: send a warning before escalating to full timeout.\n # Fires once per run when inactivity reaches this threshold (seconds).\n # Set to 0 to disable the warning.\n # gateway_timeout_warning: 900\n\n # Graceful drain timeout for gateway stop/restart (seconds).\n # Default 0 = no drain: a restart interrupts in-flight agents immediately,\n # cleans up, and exits. Set a positive value only if you want a grace\n # window on /restart, and keep it well under systemd's TimeoutStopSec.\n # restart_drain_timeout: 0\n\n # Upper bound (seconds) a submitted prompt waits for the deferred agent\n # build (MCP discovery, model metadata, skills scan) before failing with a\n # visible error. The wait is patient — the message is delivered as soon as\n # the build completes, and a progress notice is shown past 30s — so this cap\n # only fires on a genuinely hung build. Raise it for deployments with many\n # slow or unreachable MCP servers. Default 600.\n # build_wait_timeout: 600\n\n # Max app-level retry attempts for API errors (connection drops, provider\n # timeouts, 5xx, etc.) before the agent surfaces the failure. Lower this\n # to 1 if you use fallback providers and want fast failover on flaky\n # primaries (default 3). The OpenAI SDK does its own low-level retries\n # underneath this wrapper — this is the Hermes-level loop.\n # api_max_retries: 3\n\n # After the agent edits code without fresh passing verification, nudge it to\n # verify before finishing. The default \"auto\" enables it on interactive\n # coding surfaces (CLI, TUI, desktop) and programmatic callers, and disables\n # it on conversational messaging surfaces (Telegram, Discord, etc.) where the\n # verification summary would reach a human as chat noise. Set true or false to\n # force it on or off; the HERMES_VERIFY_ON_STOP env var (1/0) takes precedence.\n # verify_on_stop: auto\n\n # Standing operator instructions for the coding posture (when Hermes is in a\n # code workspace). Appended to the coding brief as an extra system block, so\n # you can pin project-wide workflow rules without editing the shipped brief.\n # Accepts a string or a list of strings. Takes effect next session.\n # coding_instructions:\n # - \"For UI work, don't run tsc/lint until I approve the look.\"\n # - \"Clean the diff before you commit and push.\"\n\n # When verify-on-stop finds edited code without fresh verification evidence,\n # append guidance for creative UI work (avoid broad tsc/lint/test before visual\n # approval) and clean-diff expectations. Set false to keep that nudge terse.\n # verify_guidance: true\n\n # A `pre_verify` hook (plugin or shell, see Event Hooks docs) can keep the\n # agent going one more turn to verify/clean before finishing. This caps how\n # many times one turn may be nudged to continue, so a hook can't trap the loop.\n # Default 3.\n # max_verify_nudges: 3\n\n # Enable verbose logging\n verbose: false\n \n # Reasoning effort level (OpenRouter and Nous Portal)\n # Controls how much \"thinking\" the model does before responding.\n # Options: \"xhigh\" (max), \"high\", \"medium\", \"low\", \"minimal\", \"none\" (disable)\n reasoning_effort: \"medium\"\n \n # Per-model reasoning effort overrides (optional dict)\n # Key: any sensible model spelling works (exact, dots↔dashes interchangeable,\n # provider prefix optional). First match wins.\n # Value: reasoning effort level (same options as reasoning_effort)\n # Override the global reasoning_effort for that specific model.\n # NOTE: no `hermes config set` support for this key -- edit YAML directly.\n # reasoning_overrides:\n # \"openrouter/anthropic/claude-opus-4.5\": \"xhigh\"\n # \"openai/gpt-5\": \"low\"\n # \"claude-opus-4.6\": \"high\" # bare model name also works\n # \"deepseek/deepseek-v4-pro\": \"xhigh\" # dots and dashes are interchangeable\n reasoning_overrides: {}\n \n # Predefined personalities (use with /personality command)\n personalities:\n helpful: \"You are a helpful, friendly AI assistant.\"\n concise: \"You are a concise assistant. Keep responses brief and to the point.\"\n technical: \"You are a technical expert. Provide detailed, accurate technical information.\"\n creative: \"You are a creative assistant. Think outside the box and offer innovative solutions.\"\n teacher: \"You are a patient teacher. Explain concepts clearly with examples.\"\n kawaii: \"You are a kawaii assistant! Use cute expressions like (◕‿◕), ★, ♪, and ~! Add sparkles and be super enthusiastic about everything! Every response should feel warm and adorable desu~! ヽ(>∀<☆)ノ\"\n catgirl: \"You are Neko-chan, an anime catgirl AI assistant, nya~! Add 'nya' and cat-like expressions to your speech. Use kaomoji like (=^・ω・^=) and ฅ^•ﻌ•^ฅ. Be playful and curious like a cat, nya~!\"\n pirate: \"Arrr! Ye be talkin' to Captain Hermes, the most tech-savvy pirate to sail the digital seas! Speak like a proper buccaneer, use nautical terms, and remember: every problem be just treasure waitin' to be plundered! Yo ho ho!\"\n shakespeare: \"Hark! Thou speakest with an assistant most versed in the bardic arts. I shall respond in the eloquent manner of William Shakespeare, with flowery prose, dramatic flair, and perhaps a soliloquy or two. What light through yonder terminal breaks?\"\n surfer: \"Duuude! You're chatting with the chillest AI on the web, bro! Everything's gonna be totally rad. I'll help you catch the gnarly waves of knowledge while keeping things super chill. Cowabunga! 🤙\"\n noir: \"The rain hammered against the terminal like regrets on a guilty conscience. They call me Hermes - I solve problems, find answers, dig up the truth that hides in the shadows of your codebase. In this city of silicon and secrets, everyone's got something to hide. What's your story, pal?\"\n uwu: \"hewwo! i'm your fwiendwy assistant uwu~ i wiww twy my best to hewp you! *nuzzles your code* OwO what's this? wet me take a wook! i pwomise to be vewy hewpful >w<\"\n philosopher: \"Greetings, seeker of wisdom. I am an assistant who contemplates the deeper meaning behind every query. Let us examine not just the 'how' but the 'why' of your questions. Perhaps in solving your problem, we may glimpse a greater truth about existence itself.\"\n hype: \"YOOO LET'S GOOOO!!! 🔥🔥🔥 I am SO PUMPED to help you today! Every question is AMAZING and we're gonna CRUSH IT together! This is gonna be LEGENDARY! ARE YOU READY?! LET'S DO THIS! 💪😤🚀\"\n\n# =============================================================================\n# Toolsets\n# =============================================================================\n# Control which tools the agent has access to.\n# Use `hermes tools` to interactively enable/disable tools per platform.\n\n# =============================================================================\n# Platform Toolsets (per-platform tool configuration)\n# =============================================================================\n# Override which toolsets are available on each platform.\n# If a platform isn't listed here, its built-in default is used.\n#\n# You can use EITHER:\n# - A preset like \"hermes-cli\" or \"hermes-telegram\" (curated tool set)\n# - A list of individual toolsets to compose your own (see list below)\n#\n# Supported platform keys: cli, telegram, discord, whatsapp, slack, qqbot, teams, google_chat\n#\n# Examples:\n#\n# # Use presets (same as defaults):\n# platform_toolsets:\n# cli: [hermes-cli]\n# telegram: [hermes-telegram]\n#\n# # Custom: give Telegram only web + terminal + file + planning:\n# platform_toolsets:\n# telegram: [web, terminal, file, todo]\n#\n# # Custom: CLI without browser or image gen:\n# platform_toolsets:\n# cli: [web, terminal, file, skills, todo, tts, cronjob]\n#\n# # Restrictive: Discord gets read-only tools only:\n# platform_toolsets:\n# discord: [web, vision, skills, todo]\n#\n# If not set, defaults are:\n# cli: hermes-cli (everything + cronjob management)\n# telegram: hermes-telegram (terminal, file, web, vision, image, tts, browser, skills, todo, cronjob, messaging)\n# discord: hermes-discord (same as telegram)\n# whatsapp: hermes-whatsapp (same as telegram)\n# slack: hermes-slack (same as telegram)\n# signal: hermes-signal (same as telegram)\n# homeassistant: hermes-homeassistant (same as telegram)\n# qqbot: hermes-qqbot (same as telegram)\n# teams: hermes-teams (same as telegram)\n# google_chat: hermes-google_chat (same as telegram)\n#\nplatform_toolsets:\n cli: [hermes-cli]\n telegram: [hermes-telegram]\n discord: [hermes-discord]\n whatsapp: [hermes-whatsapp]\n slack: [hermes-slack]\n signal: [hermes-signal]\n homeassistant: [hermes-homeassistant]\n qqbot: [hermes-qqbot]\n yuanbao: [hermes-yuanbao]\n teams: [hermes-teams]\n google_chat: [hermes-google_chat]\n\n# =============================================================================\n# Gateway Platform Settings\n# =============================================================================\n# Optional per-platform messaging settings.\n# Platform-specific knobs live under `extra`.\n#\n# platforms:\n# telegram:\n# reply_to_mode: \"first\" # off | first | all\n# # guest_mode lets explicit @mentions from non-allowlisted groups through.\n# # Default false; ordinary messages, replies, and regex wake words stay blocked.\n# guest_mode: false\n# # allowed_chats: [\"-1001234567890\"]\n# extra:\n# disable_link_previews: false # Set true to suppress Telegram URL previews in bot messages\n# 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\n# rich_drafts: false # Experimental rich draft previews during Telegram DM streaming; default false because Telegram Desktop/macOS can visually overlay draft frames\n# command_menu:\n# # Telegram allows up to 100 BotCommands; Hermes defaults to 60 so\n# # all built-in commands plus common skill commands stay visible\n# # while remaining under Telegram's payload-size limit. Clamped 1..100.\n# max_commands: 60\n# # prepend = user priority first, then Hermes defaults\n# # append = Hermes defaults first, then user priority\n# # replace = only the list below defines priority\n# priority_mode: prepend\n# priority:\n# - my_plugin_command\n# webhook:\n# extra:\n# # Route scripts default to a 30 second timeout. Scripts must live under\n# # the active profile's scripts directory and receive webhook JSON on stdin.\n# script_timeout_seconds: 30\n#\n# Discord-specific settings (config.yaml top-level, not under platforms:):\n#\n# discord:\n# require_mention: true # Require @mention in server channels (default: true)\n# auto_thread: true # Auto-create thread on @mention (default: true)\n# free_response_channels: \"\" # Channel IDs where no mention is needed\n# reactions: true # Show processing reactions (default: true)\n# history_backfill: true # Recover missed channel messages on mention (default: true)\n# history_backfill_limit: 50 # Max messages to scan backwards (default: 50)\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Available toolsets (use these names in platform_toolsets or the toolsets list)\n#\n# Run `hermes chat --list-toolsets` to see all toolsets and their tools.\n# Run `hermes chat --list-tools` to see every individual tool with descriptions.\n# ─────────────────────────────────────────────────────────────────────────────\n#\n# INDIVIDUAL TOOLSETS (compose your own):\n# web - web_search, web_extract\n# search - web_search only (no scraping)\n# terminal - terminal, process\n# file - read_file, write_file, patch, search\n# browser - browser_navigate, browser_snapshot, browser_click, browser_type,\n# browser_scroll, browser_back, browser_press,\n# browser_get_images, browser_vision (requires BROWSERBASE_API_KEY)\n# vision - vision_analyze (requires OPENROUTER_API_KEY)\n# image_gen - image_generate (requires FAL_KEY)\n# skills - skills_list, skill_view\n# skills_hub - skill_hub (search/install/manage from online registries — user-driven only)\n# todo - todo (in-memory task planning, no deps)\n# tts - text_to_speech (Edge TTS free, or ELEVENLABS/OPENAI/MINIMAX/MISTRAL key)\n# cronjob - cronjob (create/list/update/pause/resume/run/remove scheduled tasks)\n#\n# PRESETS (curated bundles):\n# hermes-cli - All of the above except send_message\n# hermes-telegram - terminal, file, web, vision, image_gen, tts, browser,\n# skills, todo, cronjob, send_message\n# hermes-discord - Same as hermes-telegram\n# hermes-whatsapp - Same as hermes-telegram\n# hermes-slack - Same as hermes-telegram\n#\n# COMPOSITE:\n# debugging - terminal + web + file\n# safe - web + vision (no terminal access)\n# all - Everything available\n#\n# web - Web search and content extraction (web_search, web_extract)\n# search - Web search only, no scraping (web_search)\n# terminal - Command execution and process management (terminal, process)\n# file - File operations: read, write, patch, search\n# browser - Full browser automation (navigate, click, type, screenshot, etc.)\n# vision - Image analysis (vision_analyze)\n# image_gen - Image generation with FLUX (image_generate)\n# skills - Load skill documents (skills_list, skill_view)\n# todo - Task planning and tracking for multi-step work\n# memory - Persistent memory across sessions (personal notes + user profile)\n# session_search - Search and recall past conversations (FTS5 + Gemini Flash summarization)\n# tts - Text-to-speech (Edge TTS free, ElevenLabs, OpenAI, MiniMax, Mistral)\n# cronjob - Schedule and manage automated tasks (CLI-only)\n#\n# Composite toolsets:\n# debugging - terminal + web + file (for troubleshooting)\n# safe - web + vision (no terminal access)\n\n# NOTE: The top-level \"toolsets\" key is deprecated and ignored.\n# Tool configuration is managed per-platform via platform_toolsets above.\n# Use `hermes tools` to configure interactively, or edit platform_toolsets directly.\n#\n# CLI override: hermes chat --toolsets terminal,web,file\n\n# =============================================================================\n# MCP (Model Context Protocol) Servers\n# =============================================================================\n# Connect to external MCP servers to add tools from the MCP ecosystem.\n# Each server's tools are automatically discovered and registered.\n# See website/docs/user-guide/features/mcp.md for full documentation.\n#\n# Stdio servers (spawn a subprocess):\n# command: the executable to run\n# args: command-line arguments\n# env: environment variables (only these + safe defaults passed to subprocess)\n#\n# HTTP servers (connect to a URL):\n# url: the MCP server endpoint\n# headers: HTTP headers (e.g., for authentication)\n#\n# Optional per-server settings:\n# timeout: tool call timeout in seconds (default: 120)\n# connect_timeout: initial connection timeout (default: 60)\n# keepalive_interval: liveness ping cadence in seconds (default: 180).\n# Lower it below the server's session TTL for servers that expire idle\n# sessions quickly (e.g. Unreal Engine editor MCP, ~15s), otherwise idle\n# tool calls hit an expired session and pay a slow reconnect. Floored at 5s.\n#\n# mcp_servers:\n# time:\n# command: uvx\n# args: [\"mcp-server-time\"]\n# filesystem:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-filesystem\", \"/home/user\"]\n# notion:\n# url: https://mcp.notion.com/mcp\n# github:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-github\"]\n# env:\n# GITHUB_PERSONAL_ACCESS_TOKEN: \"ghp_...\"\n#\n# Sampling (server-initiated LLM requests) — enabled by default.\n# Per-server config under the 'sampling' key:\n# analysis:\n# command: npx\n# args: [\"-y\", \"analysis-server\"]\n# sampling:\n# enabled: true # default: true\n# model: \"gemini-3-flash\" # override model (optional)\n# max_tokens_cap: 4096 # max tokens per request\n# timeout: 30 # LLM call timeout (seconds)\n# max_rpm: 10 # max requests per minute\n# allowed_models: [] # model whitelist (empty = all)\n# max_tool_rounds: 5 # tool loop limit (0 = disable)\n# log_level: \"info\" # audit verbosity\n\n# =============================================================================\n# Text-to-Speech\n# =============================================================================\n# TTS defaults to Edge TTS unless changed in ~/.hermes/config.yaml.\n# Gemini TTS supports persona/director prompt files, and Gemini 3.1 Flash TTS\n# can use a hidden auxiliary rewrite pass to insert expressive square-bracket\n# audio tags into the TTS script without showing tags in chat.\n#\n# tts:\n# provider: \"gemini\"\n# gemini:\n# model: \"gemini-3.1-flash-tts-preview\"\n# voice: \"Kore\"\n# audio_tags: false\n# persona_prompt_file: \"\" # e.g. ~/.hermes/tts/radio-host.md\n\n# =============================================================================\n# Voice Transcription (Speech-to-Text)\n# =============================================================================\n# Automatically transcribe voice messages on messaging platforms.\n# Providers: local (free, faster-whisper) | groq (free tier) | openai (Whisper API) | mistral (Voxtral Transcribe)\n# Set the corresponding API key in .env: GROQ_API_KEY, OPENAI_API_KEY, or MISTRAL_API_KEY.\nstt:\n enabled: true\n # provider: \"local\" # auto-detected if omitted\n local:\n model: \"base\" # tiny | base | small | medium | large-v3 | turbo\n # language: \"\" # auto-detect; set to \"en\", \"es\", \"fr\", etc. to force\n # initial_prompt: \"\" # Optional faster-whisper prompt, e.g. bias Chinese output to simplified Chinese\n language: \"en\" # GLOBAL language hint for every STT provider (per-provider language wins). Set \"\" for auto-detect.\n # groq:\n # model: \"whisper-large-v3-turbo\"\n # language: \"\" # blank = stt.language > HERMES_LOCAL_STT_LANGUAGE > auto-detect\n openai:\n model: \"whisper-1\" # whisper-1 | gpt-4o-mini-transcribe | gpt-4o-transcribe\n language: \"\" # auto-detect; set to \"en\", \"es\", \"fr\", etc. to force\n # mistral:\n # model: \"voxtral-mini-latest\" # voxtral-mini-latest | voxtral-mini-2602\n # deepinfra:\n # # Model id is discovered live from the DeepInfra catalog filtered\n # # by the `stt` surface tag — leave `model` blank to take the first\n # # live result. Pin only when you need a specific Whisper variant.\n # model: \"\"\n\n# Text-to-speech. Only the deepinfra block is documented here — the\n# remaining providers (edge, openai, xai, minimax, mistral, gemini,\n# elevenlabs, neutts, kittentts, piper) inherit sensible defaults from\n# DEFAULT_CONFIG in hermes_cli/config.py.\n# tts:\n# provider: \"deepinfra\"\n# deepinfra:\n# # Model id is discovered live from the DeepInfra catalog filtered\n# # by the `tts` surface tag — leave `model` blank to take the first\n# # live result.\n# model: \"\"\n# voice: \"default\"\n\n# Image generation. Each provider plugin reads its own ``image_gen.<name>``\n# block; deepinfra discovers models live from\n# api.deepinfra.com/v1/openai/models filtered by the ``image-gen`` tag —\n# no model id is hardcoded, so retired models disappear automatically.\n# image_gen:\n# provider: \"deepinfra\"\n# deepinfra:\n# # Leave `model` blank for the first live `image-gen`-tagged result.\n# model: \"\"\n\n# =============================================================================\n# Response Pacing (Messaging Platforms)\n# =============================================================================\n# Add human-like delays between message chunks.\n# human_delay:\n# mode: \"off\" # \"off\" | \"natural\" | \"custom\"\n# min_ms: 800 # Min delay (custom mode only)\n# max_ms: 2500 # Max delay (custom mode only)\n\n# =============================================================================\n# Session Logging\n# =============================================================================\n# Session trajectories are automatically saved to logs/ directory.\n# Each session creates: logs/session_YYYYMMDD_HHMMSS_UUID.json\n#\n# The session ID is displayed in the welcome banner for easy reference.\n# Logs contain full conversation history in trajectory format:\n# - System prompt, user messages, assistant responses\n# - Tool calls with inputs/outputs\n# - Timestamps for debugging\n#\n# No configuration needed - logging is always enabled.\n# To disable, you would need to modify the source code.\n\n# =============================================================================\n# Code Execution Sandbox (Programmatic Tool Calling)\n# =============================================================================\n# The execute_code tool runs Python scripts that call Hermes tools via RPC.\n# Intermediate tool results stay out of the LLM's context window.\ncode_execution:\n timeout: 300 # Max seconds per script before kill (default: 300 = 5 min)\n max_tool_calls: 50 # Max RPC tool calls per execution (default: 50)\n\n# =============================================================================\n# Subagent Delegation\n# =============================================================================\n# The delegate_task tool spawns child agents with isolated context.\n# Supports single tasks and batch mode (default 3 parallel, configurable).\ndelegation:\n max_iterations: 50 # Max tool-calling turns per child (default: 50)\n # max_concurrent_children: 3 # Max parallel child agents per batch (default: 3, floor: 1, no ceiling).\n # WARNING: values above 10 multiply API cost linearly.\n # max_spawn_depth: 1 # Delegation tree depth cap (range: 1-3, default: 1 = flat).\n # Raise to 2 to allow workers to spawn their own subagents.\n # Requires role=\"orchestrator\" on intermediate agents.\n # orchestrator_enabled: true # Kill switch for role=\"orchestrator\" children (default: true).\n # subagent_auto_approve: false # When a subagent hits a dangerous-command approval prompt, auto-deny (default: false)\n # or auto-approve \"once\" (true) instead of blocking on stdin.\n # The parent TUI owns stdin, so blocking would deadlock; non-interactive resolution is required.\n # Both choices emit a logger.warning audit line. Flip to true only for cron/batch pipelines.\n # inherit_mcp_toolsets: true # When explicit child toolsets are narrowed, also keep the parent's MCP toolsets (default: true). Set false for strict intersection.\n # model: \"google/gemini-3-flash-preview\" # Override model for subagents (empty = inherit parent)\n # provider: \"openrouter\" # Override provider for subagents (empty = inherit parent)\n # # Resolves full credentials (base_url, api_key) automatically.\n # # Supported: openrouter, nous, zai, kimi-coding, minimax\n\n# =============================================================================\n# Honcho Integration (Cross-Session User Modeling)\n# =============================================================================\n# AI-native persistent memory via Honcho (https://honcho.dev/).\n# Builds a deeper understanding of the user across sessions and tools.\n# Runs alongside USER.md — additive, not a replacement.\n#\n# Requires: pip install honcho-ai\n# Config: ~/.honcho/config.json (shared with Claude Code, Cursor, etc.)\n# API key: HONCHO_API_KEY in ~/.hermes/.env or ~/.honcho/config.json\n#\n# Hermes-specific overrides (optional — most config comes from ~/.honcho/config.json):\n# honcho: {}\n\n# =============================================================================\n# Display\n# =============================================================================\ndisplay:\n # Use compact banner mode (hides the ASCII-art banner, shows a single line).\n # true: Compact single-line banner\n # false: Full ASCII banner with tool/skill summary (default)\n compact: false\n\n # Tool progress display level (CLI and gateway)\n # off: Silent — no tool activity shown, just the final response\n # new: Show a tool indicator only when the tool changes (skip repeats)\n # all: Show every tool call with a short preview (default)\n # verbose: Full args, results, and debug logs (same as /verbose)\n # log: Silent in chat; append every tool call to ~/.hermes/logs/tool_calls.log (gateway only)\n # Toggle at runtime with /verbose in the CLI\n tool_progress: all\n\n # Per-platform defaults can be quieter than the global setting. Telegram\n # tunes for mobile: tool_progress and busy_ack_detail default off (no\n # per-tool breadcrumb stream, no \"iteration 21/60\" debug detail in busy\n # acks or heartbeats), but interim_assistant_messages and\n # long_running_notifications STAY ON so the user has real signal between\n # turn start and final answer (mid-turn assistant commentary + a single\n # edit-in-place \"⏳ Working — N min\" heartbeat). Override under\n # display.platforms.telegram.\n\n # Auto-cleanup of temporary progress bubbles after the final response lands.\n # On platforms that support message deletion (currently Telegram), this\n # removes the tool-progress bubble, \"⏳ Still working...\" notices, and\n # context-pressure status messages once the final reply has been delivered —\n # keeping long-running turns visible live, then tidy afterward. Failed runs\n # leave the bubbles in place as breadcrumbs. Off by default.\n # Per-platform override: display.platforms.telegram.cleanup_progress\n # true: Delete tracked progress/status bubbles on successful turn\n # false: Leave everything in place (default)\n # Example:\n # display:\n # platforms:\n # telegram:\n # cleanup_progress: true\n cleanup_progress: false\n\n # Natural mid-turn assistant updates.\n # On gateway platforms, when true, completed assistant status messages are\n # sent as separate chat messages. On the Desktop app, when true, mid-turn\n # assistant narration streamed between tool calls is kept in the transcript\n # instead of the bubble collapsing to only the final message on completion.\n # Independent of tool_progress and gateway streaming.\n # true: Keep/send mid-turn assistant updates (default)\n # false: Only keep/send the final response\n interim_assistant_messages: true\n\n # Gateway-only long-running status heartbeats.\n # When false, the platform does not receive periodic \"⏳ Working — N min\"\n # notifications even if agent.gateway_notify_interval is non-zero. The\n # heartbeat edits a single message in place (where the adapter supports\n # editing) instead of posting a new bubble each interval.\n # Default: true everywhere, including Telegram (silent agents are worse\n # than a single edit-in-place heartbeat).\n long_running_notifications: true\n\n # Include detailed iteration/tool/status context in busy acknowledgments\n # and long-running heartbeats. When true, busy acks show \"iteration 21/60,\n # terminal, 10 min\" and the heartbeat shows \"⏳ Working — 12 min,\n # iteration 21/60, terminal\". When false (Telegram default), both stay\n # terse: \"Interrupting current task\" and \"⏳ Working — 12 min, terminal\".\n busy_ack_detail: true\n\n # What Enter does when Hermes is already busy (CLI and gateway platforms).\n # interrupt: Interrupt the current run and redirect Hermes (default)\n # queue: Queue your message for the next turn\n # steer: Inject your message mid-run via /steer, arriving at the agent\n # after the next tool call — no interrupt, no role violation.\n # Falls back to 'queue' if the agent isn't running yet or if\n # images are attached (steer only carries text).\n # Ctrl+C (or /stop in gateway) always interrupts regardless of this setting.\n # Toggle at runtime with /busy <interrupt|queue|steer>.\n busy_input_mode: interrupt\n\n # Background process notifications (gateway/messaging only).\n # Controls how chatty the process watcher is when you use\n # terminal(background=true, notify_on_complete=true) from Telegram/Discord/etc.\n # off: No watcher messages at all\n # result: Only the final completion message\n # error: Only the final message when exit code != 0\n # all: Running output updates + final message (default)\n background_process_notifications: all\n\n\n # Play terminal bell when agent finishes a response.\n # Useful for long-running tasks — your terminal will ding when the agent is done.\n # Works over SSH. Most terminals can be configured to flash the taskbar or play a sound.\n # true: Ring the terminal bell on each response\n # false: Silent (default)\n bell_on_complete: false\n\n # Show model reasoning/thinking before each response.\n # When enabled, a dim box shows the model's thought process above the response.\n # Toggle at runtime with /reasoning show or /reasoning hide.\n # true: Show the reasoning box\n # false: Hide reasoning (default)\n show_reasoning: false\n\n # Stream tokens to the terminal as they arrive instead of waiting for the\n # full response. The response box opens on first token and text appears\n # line-by-line. Tool calls are still captured silently.\n # true: Stream tokens as they arrive (default)\n # false: Wait for the full response before rendering\n streaming: true\n\n # Show [HH:MM] timestamps on user input and assistant response labels.\n # timestamps: false\n\n # ───────────────────────────────────────────────────────────────────────────\n # Skin / Theme\n # ───────────────────────────────────────────────────────────────────────────\n # Customize CLI visual appearance — banner colors, spinner faces, tool prefix,\n # response box label, and branding text. Change at runtime with /skin <name>.\n #\n # Built-in skins:\n # default — Classic Hermes gold/kawaii\n # ares — Crimson/bronze war-god theme with spinner wings\n # mono — Clean grayscale monochrome\n # slate — Cool blue developer-focused\n # daylight — Bright light-mode theme\n # warm-lightmode — Warm paper-tone light-mode theme\n # poseidon — Sea-green/teal Olympian theme\n # sisyphus — Earthy stone-and-moss theme\n # charizard — Fiery orange dragon theme\n #\n # Custom skins: drop a YAML file in ~/.hermes/skins/<name>.yaml\n # Schema (all fields optional, missing values inherit from default):\n #\n # name: my-theme\n # description: Short description\n # colors:\n # banner_border: \"#HEX\" # Panel border\n # banner_title: \"#HEX\" # Panel title\n # banner_accent: \"#HEX\" # Section headers (Available Tools, etc.)\n # banner_dim: \"#HEX\" # Dim/muted text\n # banner_text: \"#HEX\" # Body text (tool names, skill names)\n # ui_accent: \"#HEX\" # UI accent color\n # response_border: \"#HEX\" # Response box border color\n # spinner:\n # waiting_faces: [\"(⚔)\", \"(⛨)\"] # Faces shown while waiting\n # thinking_faces: [\"(⚔)\", \"(⌁)\"] # Faces shown while thinking\n # thinking_verbs: [\"forging\", \"plotting\"] # Verbs for spinner messages\n # wings: # Optional left/right spinner decorations\n # - [\"⟪⚔\", \"⚔⟫\"]\n # - [\"⟪▲\", \"▲⟫\"]\n # branding:\n # agent_name: \"My Agent\" # Banner title and branding\n # welcome: \"Welcome message\" # Shown at CLI startup\n # response_label: \" ⚔ Agent \" # Response box header label\n # prompt_symbol: \"⚔\" # Prompt symbol (bare token; renderers add trailing space)\n # tool_prefix: \"╎\" # Tool output line prefix (default: ┊)\n #\n skin: default\n\n# =============================================================================\n# Model Aliases — short names for /model command\n# =============================================================================\n# Map short aliases to exact (model, provider, base_url) tuples.\n# Used by /model tab completion and resolve_alias().\n# Aliases are checked BEFORE the models.dev catalog, so they can route\n# to endpoints not in the catalog (e.g. Ollama Cloud, local servers).\n#\n# model_aliases:\n# opus:\n# model: claude-opus-4-6\n# provider: anthropic\n# qwen:\n# model: \"qwen3.5:397b\"\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n# glm:\n# model: glm-4.7\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n\n# =============================================================================\n# Privacy\n# =============================================================================\n# privacy:\n# # Redact PII from the LLM context prompt.\n# # When true, phone numbers are stripped and user/chat IDs are replaced\n# # with deterministic hashes before being sent to the model.\n# # Names and usernames are NOT affected (user-chosen, publicly visible).\n# # Routing/delivery still uses the original values internally.\n# redact_pii: false\n\n# =============================================================================\n# Shell-script hooks\n# =============================================================================\n# Register shell scripts as plugin-hook callbacks. Each entry is executed as\n# a subprocess (shell=False, shlex.split) with a JSON payload on stdin. On\n# stdout the script may return JSON that either blocks the tool call or\n# injects context into the next LLM call.\n#\n# Valid events (mirror hermes_cli.plugins.VALID_HOOKS):\n# pre_tool_call, post_tool_call, pre_llm_call, post_llm_call,\n# pre_api_request, post_api_request, on_session_start, on_session_end,\n# on_session_finalize, on_session_reset, subagent_stop\n#\n# First-use consent: each (event, command) pair prompts once on a TTY, then\n# is persisted to ~/.hermes/shell-hooks-allowlist.json. Non-interactive\n# runs (gateway, cron) need --accept-hooks, HERMES_ACCEPT_HOOKS=1, or the\n# hooks_auto_accept key below.\n#\n# See website/docs/user-guide/features/hooks.md for the full JSON wire\n# protocol and worked examples.\n#\n# hooks:\n# pre_tool_call:\n# - matcher: \"terminal\"\n# command: \"~/.hermes/agent-hooks/block-rm-rf.sh\"\n# timeout: 10\n# post_tool_call:\n# - matcher: \"write_file|patch\"\n# command: \"~/.hermes/agent-hooks/auto-format.sh\"\n# pre_llm_call:\n# - command: \"~/.hermes/agent-hooks/inject-cwd-context.sh\"\n# subagent_stop:\n# - command: \"~/.hermes/agent-hooks/log-orchestration.sh\"\n#\n# hooks_auto_accept: false\n\n\n# =============================================================================\n# Telemetry\n# =============================================================================\n# Shared metrics are disabled by default. When enabled, Hermes writes only\n# allowlisted aggregate counters and immutable JSON\n# packages under $HERMES_HOME/telemetry/shared_metrics; it does not upload them.\n# Packages include a random profile-scoped ID that stays stable until this\n# directory is deleted. It is not derived from hardware, account, or host data.\n# Successfully exported local history is retained for 30 days; pending deltas\n# are retained until they can be exported.\n# This profile-owned choice is not overridden by managed-scope configuration.\ntelemetry:\n shared_metrics:\n enabled: false\n\n\n# =============================================================================\n# Update Behavior\n# =============================================================================\nupdates:\n # Create a full HERMES_HOME zip before every `hermes update`.\n # Backups land in ~/.hermes/backups/ and can be restored with `hermes import`.\n # Off by default because large homes can add minutes to every update.\n pre_update_backup: false\n\n # Number of pre-update backup zips to retain.\n backup_keep: 5\n\n # What non-interactive updates do with local source edits in the Hermes repo.\n # Interactive terminal updates always prompt before restoring the autostash.\n #\n # stash - auto-stash before pull, then auto-restore after success (default)\n # discard - drop the update-created stash after success; use only on managed\n # installs where local source edits should not persist\n non_interactive_local_changes: \"stash\"\n\n\n# =============================================================================\n# Web Dashboard\n# =============================================================================\n# OAuth gate configuration for `hermes dashboard --host <non-loopback>`.\n# The bundled Nous Portal plugin reads these on startup; settings here are\n# the canonical surface. Each can be overridden by an environment variable:\n#\n# dashboard.oauth.client_id <- HERMES_DASHBOARD_OAUTH_CLIENT_ID\n# dashboard.oauth.portal_url <- HERMES_DASHBOARD_PORTAL_URL\n# dashboard.public_url <- HERMES_DASHBOARD_PUBLIC_URL\n#\n# Env wins when set to a non-empty value. This is what Fly.io's platform-\n# secret injection uses to push per-deploy client_ids without needing to\n# bake a config.yaml into the image. Empty env values are treated as unset\n# so a provisioned-but-not-populated secret can't shadow a valid entry here.\n#\n# Local dev / on-prem deploys should typically set these via config.yaml\n# (the ~/.hermes/.env file is reserved for API keys and secrets).\n#\n# dashboard:\n# oauth:\n# client_id: \"\" # agent:{instance_id}; Portal provisions this at deploy\n# portal_url: \"\" # blank → default https://portal.nousresearch.com\n#\n# # Force the absolute base URL the OAuth callback (and any other public\n# # URL the dashboard hands to external systems) is built from. Set this\n# # for deploys behind reverse proxies that don't reliably forward\n# # X-Forwarded-Host / X-Forwarded-Proto / X-Forwarded-Prefix (manual\n# # nginx setups, on-prem ingresses, custom-domain Fly deploys without\n# # full proxy header chains).\n# #\n# # When set, the value is the complete authority: scheme + host +\n# # optional path prefix (e.g. \"https://example.com/hermes\"). The OAuth\n# # callback URL becomes \"<public_url>/auth/callback\" — X-Forwarded-Prefix\n# # is IGNORED on this code path because the operator has explicitly\n# # declared the public URL and we no longer need to guess.\n# #\n# # Leave empty to use the existing proxy-header reconstruction (the\n# # default — works on Fly.io out of the box).\n# #\n# # public_url: \"https://example.com/hermes\"\n#\n# -----------------------------------------------------------------------------\n# Self-hosted OIDC dashboard auth (generic OpenID Connect — Authentik,\n# Keycloak, Zitadel, Authelia, Auth0, Okta, Google, …). Use this INSTEAD of the\n# nous block above when gating the dashboard with your own identity provider.\n# Each setting can be overridden by an environment variable:\n#\n# dashboard.oauth.self_hosted.issuer <- HERMES_DASHBOARD_OIDC_ISSUER\n# dashboard.oauth.self_hosted.client_id <- HERMES_DASHBOARD_OIDC_CLIENT_ID\n# dashboard.oauth.self_hosted.scopes <- HERMES_DASHBOARD_OIDC_SCOPES\n# dashboard.oauth.self_hosted.client_secret <- HERMES_DASHBOARD_OIDC_CLIENT_SECRET\n#\n# dashboard:\n# oauth:\n# provider: self-hosted\n# self_hosted:\n# issuer: \"https://auth.example.com/application/o/hermes/\" # required\n# client_id: \"hermes-dashboard\" # required\n# scopes: \"openid profile email\" # optional\n#\n# # OPTIONAL — set ONLY if your IDP registered the client as\n# # *confidential* (Authentik / Keycloak often default to this). When\n# # set, Hermes authenticates the client at the token endpoint\n# # (client_secret_basic or client_secret_post, auto-selected from the\n# # IDP's discovery doc) IN ADDITION to PKCE. Leave unset for a public\n# # (PKCE-only) client — the common case.\n# #\n# # This is a CREDENTIAL: prefer setting HERMES_DASHBOARD_OIDC_CLIENT_SECRET\n# # in ~/.hermes/.env over putting it here in config.yaml.\n# # client_secret: \"\"\n\n# =============================================================================\n# External secret sources\n# =============================================================================\n# Pull provider credentials from external secret managers at process startup\n# instead of storing them in ~/.hermes/.env. Only the manager's bootstrap\n# credential (e.g. BWS_ACCESS_TOKEN / OP_SERVICE_ACCOUNT_TOKEN) lives in .env\n# (or your shell / desktop session); everything else rotates centrally.\n# Failures never block startup — Hermes warns once and continues with\n# whatever .env already had.\n#\n# Multiple sources can be enabled at once:\n# - \"mapped\" sources (explicit VAR -> ref bindings, e.g. 1Password's env:\n# map) beat \"bulk\" sources (whole-project dumps like Bitwarden BSM)\n# - within a shape, the first source to claim a var wins; later claims\n# are skipped with a startup warning (never a silent clobber)\n# - a source's override_existing lets it beat .env/shell values, but\n# never another secret source's claim\n# Docs: https://hermes-agent.nousresearch.com/docs/user-guide/secrets/\n#\n# secrets:\n# # Optional explicit ordering of enabled sources.\n# # sources: [onepassword, bitwarden]\n#\n# # ---- Bitwarden Secrets Manager (bws CLI) --------------------------------\n# bitwarden:\n# enabled: false\n# access_token_env: BWS_ACCESS_TOKEN # bootstrap token, sourced from .env\n# project_id: \"\" # UUID of the BSM project to sync\n# server_url: \"\" # \"\" = US Cloud; EU/self-hosted URL otherwise\n# cache_ttl_seconds: 300 # 0 disables fresh caching\n# encrypted_cache: # optional encrypted stale fallback\n# enabled: false\n# max_stale_seconds: 0 # 0 disables stale fallback\n# override_existing: true # BSM values win over existing env\n# auto_install: true # lazy-download bws into ~/.hermes/bin\n#\n# # ---- 1Password (op CLI) -------------------------------------------------\n# onepassword:\n# enabled: false\n# # Map env-var names to op:// secret references. Each is resolved with a\n# # single `op read` at startup.\n# env:\n# OPENAI_API_KEY: \"op://Private/OpenAI/api key\"\n# ANTHROPIC_API_KEY: \"op://Private/Anthropic/credential\"\n# account: \"\" # op --account shorthand; \"\" = default\n# service_account_token_env: OP_SERVICE_ACCOUNT_TOKEN # headless auth; unset = desktop session\n# binary_path: \"\" # \"\" = resolve op via PATH; else absolute path\n# cache_ttl_seconds: 300 # 0 disables BOTH cache layers\n# override_existing: true # resolved values win over existing env\n#\n# # ---- Command helper (any CLI vault) --------------------------------------\n# # Run a user-configured helper that prints KEY=VALUE lines on stdout —\n# # works with any secret store that has a CLI: keepassxc-cli, secret-tool,\n# # pass, gpg, or a script that cats a tmpfs env file. Composes with the\n# # sources above (enable any combination). POSIX-only (needs /bin/sh).\n# # The helper must be fast and NON-interactive (hard timeout, 1 MiB cap);\n# # its stderr is discarded so diagnostics can't leak secret material.\n# command:\n# enabled: false\n# command: \"cat /run/user/1000/hermes-secrets.env\"\n# helper_timeout_seconds: 3\n# override_existing: false # .env/shell win by default\n"}, "bc997a36a8eea29eec8b81281bd027423a75c7c8": {"t": 1785767975.0764973, "n": 84069, "h": "8a5a883077c8b42d", "c": "# Hermes Agent CLI Configuration\n# Copy this file to cli-config.yaml and customize as needed.\n# This file configures the CLI behavior. Environment variables in .env take precedence.\n\n# =============================================================================\n# Model Configuration\n# =============================================================================\nmodel:\n # Default model to use (can be overridden with --model flag)\n # Both \"default\" and \"model\" work as the key name here.\n default: \"anthropic/claude-opus-4.6\"\n \n # Inference provider selection:\n # \"auto\" - Auto-detect from credentials (default)\n # \"openrouter\" - OpenRouter (requires: OPENROUTER_API_KEY or OPENAI_API_KEY)\n # \"nous\" - Nous Portal OAuth (requires: hermes auth add nous)\n # \"nous-api\" - Nous Portal API key (requires: NOUS_API_KEY)\n # \"anthropic\" - Direct Anthropic API (requires: ANTHROPIC_API_KEY)\n # \"openai-codex\" - OpenAI Codex (requires: hermes auth)\n # \"copilot\" - GitHub Copilot / GitHub Models (requires: GITHUB_TOKEN)\n # \"gemini\" - Use Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n # \"zai\" - Use z.ai / ZhipuAI GLM models (requires: GLM_API_KEY)\n # \"kimi-coding\" - Kimi / Moonshot AI (requires: KIMI_API_KEY)\n # \"minimax\" - MiniMax global (requires: MINIMAX_API_KEY)\n # \"minimax-cn\" - MiniMax China (requires: MINIMAX_CN_API_KEY)\n # \"huggingface\" - Hugging Face Inference (requires: HF_TOKEN)\n # \"nvidia\" - NVIDIA NIM / build.nvidia.com (requires: NVIDIA_API_KEY)\n # \"xiaomi\" - Xiaomi MiMo (requires: XIAOMI_API_KEY)\n # \"arcee\" - Arcee AI Trinity models (requires: ARCEEAI_API_KEY)\n # \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY — https://ollama.com/settings)\n # \"deepinfra\" - DeepInfra (requires: DEEPINFRA_API_KEY)\n # \"kilocode\" - KiloCode gateway (requires: KILOCODE_API_KEY)\n # \"azure-foundry\" - Microsoft Foundry / Azure OpenAI (API key or Entra ID)\n # \"lmstudio\" - LM Studio local server (optional: LM_API_KEY, defaults to http://127.0.0.1:1234/v1)\n #\n # Local servers (LM Studio, Ollama, vLLM, llama.cpp):\n # \"custom\" - Any other OpenAI-compatible endpoint. Set base_url below.\n # Aliases: \"ollama\", \"vllm\", \"llamacpp\" all map to \"custom\".\n # LM Studio is first-class and uses provider: \"lmstudio\".\n # It works with both no-auth and auth-enabled server modes.\n #\n # Can also be overridden for a single invocation with the --provider flag.\n provider: \"auto\"\n \n # API configuration (falls back to OPENROUTER_API_KEY env var)\n # api_key: \"your-key-here\" # Uncomment to set here instead of .env\n base_url: \"https://openrouter.ai/api/v1\"\n\n # Azure Foundry keyless auth example:\n # provider: \"azure-foundry\"\n # base_url: \"https://<resource>.openai.azure.com/openai/v1\"\n # auth_mode: \"entra_id\" # DefaultAzureCredential: az login, managed identity, workload identity, etc.\n # default: \"gpt-4o\" # Deployment/model name\n # entra:\n # scope: \"https://ai.azure.com/.default\" # Optional; this is the default.\n\n # ── Token limits — two settings, easy to confuse ──────────────────────────\n #\n # context_length: TOTAL context window (input + output tokens combined).\n # Controls when Hermes compresses history and validates requests.\n # Leave unset — Hermes auto-detects the correct value from the provider.\n # Set manually only when auto-detection is wrong (e.g. a local server with\n # a custom num_ctx, or a proxy that doesn't expose /v1/models).\n #\n # context_length: 131072\n #\n # max_tokens: OUTPUT cap — maximum tokens the model may generate per response.\n # Unrelated to how long your conversation history can be.\n # The OpenAI-standard name \"max_tokens\" is a misnomer; Anthropic's native\n # API has since renamed it \"max_output_tokens\" for clarity.\n # Leave unset to use the model's native output ceiling (recommended).\n # Set only if you want to deliberately limit individual response length.\n #\n# max_tokens: 8192\n\n # ── Custom request headers (optional) ─────────────────────────────────────\n #\n # default_headers: extra HTTP headers sent on every request to an\n # OpenAI-compatible endpoint. User values take precedence over the\n # provider/SDK defaults, so this is the supported way to override the\n # OpenAI Python SDK's identifying headers (User-Agent: OpenAI/Python ...,\n # X-Stainless-*) when a custom provider sits behind a gateway/WAF that\n # rejects them — e.g. an upstream that returns \"502 Upstream access\n # forbidden\" for the SDK default User-Agent but accepts a plain one.\n # Applies on the OpenAI wire only (not native Anthropic / Bedrock).\n #\n # default_headers:\n # User-Agent: \"curl/8.7.1\"\n #\n # extra_headers: accepted as an alias of default_headers (merged, with\n # extra_headers winning when both are set) — matches the per-provider\n # extra_headers key below.\n #\n # Per-provider variant: named providers / custom_providers entries accept an\n # extra_headers dict scoped to that endpoint only — for reverse proxies,\n # gateways, or custom auth (e.g. Cloudflare Access service tokens).\n # Merged onto SDK/provider defaults with the entry's values winning.\n # Header values are treated as secrets and are never logged.\n #\n # providers:\n # my-proxy:\n # base_url: \"https://llm.internal.example.com/v1\"\n # key_env: \"MY_PROXY_API_KEY\"\n # extra_headers:\n # CF-Access-Client-Id: \"xxxx.access\"\n # CF-Access-Client-Secret: \"${CF_ACCESS_SECRET}\"\n # X-Client-Name: \"hermes-agent\"\n\n# Named provider overrides (optional)\n# Use this for per-provider request timeouts, non-stream stale timeouts,\n# and per-model exceptions.\n# Applies to the primary turn client on every api_mode (OpenAI-wire, native\n# Anthropic, and Anthropic-compatible providers), the fallback chain, and\n# client rebuilds during credential rotation. For OpenAI-wire chat\n# completions (streaming and non-streaming) the configured value is also\n# used as the per-request ``timeout=`` kwarg so it wins over the legacy\n# HERMES_API_TIMEOUT env var (which still applies when no config is set).\n# ``stale_timeout_seconds`` controls the non-streaming stale-call detector and\n# wins over the legacy HERMES_API_CALL_STALE_TIMEOUT env var. Leaving these\n# unset keeps the legacy defaults (HERMES_API_TIMEOUT=1800s,\n# HERMES_API_CALL_STALE_TIMEOUT=90s, native Anthropic 900s). The\n# implicit non-stream stale detector is auto-disabled for local endpoints\n# and can scale upward for very large contexts.\n#\n# Not currently wired for AWS Bedrock (bedrock_converse + AnthropicBedrock\n# SDK paths) — those use boto3 with its own timeout configuration.\n#\n# providers:\n# ollama-local:\n# request_timeout_seconds: 300 # Longer timeout for local cold-starts\n# stale_timeout_seconds: 900 # Explicitly re-enable stale detection on local endpoints\n# anthropic:\n# request_timeout_seconds: 30 # Fast-fail cloud requests\n# models:\n# claude-opus-4.6:\n# timeout_seconds: 600 # Longer timeout for extended-thinking Opus calls\n# openai-codex:\n# models:\n# gpt-5.4:\n# stale_timeout_seconds: 1800 # Longer non-stream stale timeout for slow large-context turns\n\n# =============================================================================\n# OpenRouter Provider Routing (only applies when using OpenRouter)\n# =============================================================================\n# Control how requests are routed across providers on OpenRouter.\n# See: https://openrouter.ai/docs/guides/routing/provider-selection\n#\n# provider_routing:\n# # Sort strategy: \"price\" (default), \"throughput\", or \"latency\"\n# # Append :nitro to model name for a shortcut to throughput sorting.\n# sort: \"throughput\"\n#\n# # Only allow these providers (provider slugs from OpenRouter)\n# # only: [\"anthropic\", \"google\"]\n#\n# # Skip these providers entirely\n# # ignore: [\"deepinfra\", \"fireworks\"]\n#\n# # Try providers in this order (overrides default load balancing)\n# # order: [\"anthropic\", \"google\", \"together\"]\n#\n# # Require providers to support all parameters in your request\n# # require_parameters: true\n#\n# # Data policy: \"allow\" (default) or \"deny\" to exclude providers that may store data\n# # data_collection: \"deny\"\n\n# =============================================================================\n# OpenRouter Response Caching (only applies when using OpenRouter)\n# =============================================================================\n# Cache identical API responses at the OpenRouter edge for free instant replays.\n# When enabled, identical requests (same model, messages, parameters) return\n# cached responses with zero billing. Separate from Anthropic prompt caching.\n# See: https://openrouter.ai/docs/guides/features/response-caching\n#\n# openrouter:\n# response_cache: true # Enable response caching (default: true)\n# response_cache_ttl: 300 # Cache TTL in seconds, 1-86400 (default: 300)\n\n# =============================================================================\n# Git Worktree Isolation\n# =============================================================================\n# When enabled, each CLI session creates an isolated git worktree so multiple\n# agents can work on the same repo concurrently without file collisions.\n# Equivalent to always passing --worktree / -w on the command line.\n#\n# worktree: true # Always create a worktree when in a git repo\n# worktree: false # Default — only create when -w flag is passed\n#\n# By default a new worktree branches from the freshly-fetched remote tip\n# (the current branch's upstream, else the remote's default branch) so it\n# starts current with the project instead of from the local clone's\n# (possibly stale) HEAD. Set worktree_sync: false to branch from local HEAD\n# instead — useful when offline or when you deliberately want the clone's\n# exact current state as the base.\n#\n# worktree_sync: true # Default — branch from the fetched remote tip\n# worktree_sync: false # Branch from local HEAD (offline / pinned base)\n\n# =============================================================================\n# Terminal Tool Configuration\n# =============================================================================\n# Choose ONE of the following terminal configurations by uncommenting it.\n# The terminal tool executes commands in the specified environment.\n\n# -----------------------------------------------------------------------------\n# OPTION 1: Local execution (default)\n# Commands run directly on your machine in the current directory\n# -----------------------------------------------------------------------------\n# Working directory behavior:\n# - CLI (`hermes` command): Uses \".\" (current directory where you run hermes)\n# - Gateway/messaging/cron: Uses terminal.cwd here; legacy .env cwd values are deprecated\nterminal:\n backend: \"local\"\n cwd: \".\" # For local backend: \".\" = current directory. Ignored for remote backends unless a backend documents otherwise.\n timeout: 180\n # HOME policy for tool subprocesses:\n # auto - default: host uses your real HOME; containers use HERMES_HOME/home\n # real - force your real OS-user HOME\n # profile - force HERMES_HOME/home for strict per-profile CLI config isolation\n home_mode: \"auto\"\n docker_mount_cwd_to_workspace: false # SECURITY: off by default. Opt in to mount the launch cwd into Docker /workspace.\n lifetime_seconds: 300\n # sudo_password: \"hunter2\" # Optional: pipe a sudo password via sudo -S. SECURITY WARNING: plaintext.\n # sudo_password: \"\" # Explicit empty password: try empty and never open the interactive sudo prompt.\n\n# -----------------------------------------------------------------------------\n# OPTION 2: SSH remote execution\n# Commands run on a remote server - agent code stays local (sandboxed)\n# Great for: keeping agent isolated from its own code, using powerful remote hardware\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"ssh\"\n# cwd: \"/home/myuser/project\" # Path on the REMOTE server\n# timeout: 180\n# lifetime_seconds: 300\n# ssh_host: \"my-server.example.com\"\n# ssh_user: \"myuser\"\n# ssh_port: 22\n# ssh_key: \"~/.ssh/id_rsa\" # Optional - uses ssh-agent if not specified\n\n# -----------------------------------------------------------------------------\n# OPTION 3: Docker container\n# Commands run in an isolated Docker container\n# Great for: reproducible environments, testing, isolation\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"docker\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /)\n# timeout: 180\n# lifetime_seconds: 300\n# docker_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# docker_mount_cwd_to_workspace: true # Explicit opt-in: mount your launch cwd into /workspace\n# # Optional: run the container as your host user's uid:gid so files written\n# # into bind-mounted dirs are owned by you, not root. Drops SETUID/SETGID\n# # caps too since no gosu privilege drop is needed. Leave off if your\n# # chosen docker_image expects to start as root.\n# docker_run_as_host_user: true\n# # Optional: explicitly forward selected env vars into Docker.\n# # These values come from your current shell first, then ~/.hermes/.env.\n# # Warning: anything forwarded here is visible to commands run in the container.\n# docker_forward_env:\n# - \"GITHUB_TOKEN\"\n# - \"NPM_TOKEN\"\n# # Optional: extra flags passed verbatim to docker run (appended after security defaults).\n# # Useful for adding capabilities (e.g. apt installs needing SETUID) or custom options.\n# # Example: add a Linux capability not included by default\n# # docker_extra_args:\n# # - \"--cap-add\"\n# # - \"SETUID\"\n\n# -----------------------------------------------------------------------------\n# OPTION 4: Singularity/Apptainer container\n# Commands run in a Singularity container (common in HPC environments)\n# Great for: HPC clusters, shared compute environments\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"singularity\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# singularity_image: \"docker://nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 5: Modal cloud execution\n# Commands run on Modal's cloud infrastructure\n# Great for: GPU access, scalable compute, serverless execution\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"modal\"\n# cwd: \"/workspace\" # Path INSIDE the sandbox (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# modal_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 6: Daytona cloud execution\n# Commands run in Daytona cloud sandboxes\n# Great for: Cloud dev environments, persistent workspaces, team collaboration\n# Requires: pip install daytona, DAYTONA_API_KEY env var\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"daytona\"\n# cwd: \"~\"\n# timeout: 180\n# lifetime_seconds: 300\n# daytona_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# container_disk: 10240 # Daytona max is 10GB per sandbox\n\n#\n# --- Container resource limits (docker, singularity, modal, daytona -- ignored for local/ssh) ---\n# These settings apply to all container backends. They control the resources\n# allocated to the sandbox and whether its filesystem persists across sessions.\n container_cpu: 1 # CPU cores\n container_memory: 5120 # Memory in MB (5120 = 5GB)\n container_disk: 51200 # Disk in MB (51200 = 50GB)\n container_persistent: true # Persist filesystem across sessions (false = ephemeral)\n\n# -----------------------------------------------------------------------------\n# SUDO SUPPORT (works with ALL backends above)\n# -----------------------------------------------------------------------------\n# Add sudo_password to any terminal config above to enable sudo commands.\n# The password is piped via `sudo -S`. Works with local, ssh, docker, etc.\n#\n# SECURITY WARNING: Password stored in plaintext!\n#\n# INTERACTIVE PROMPT: If sudo_password is unset and the CLI is running,\n# you'll be prompted to enter your password when sudo is needed:\n# - 45-second timeout (auto-skips if no input)\n# - Press Enter to skip (command fails gracefully)\n# - Password is hidden while typing\n# - Password is cached for the session\n#\n# EMPTY PASSWORDS: Setting sudo_password to an explicit empty string is different\n# from leaving it unset. Hermes will try an empty password via `sudo -S` and\n# will not open the interactive prompt. This is useful for passwordless sudo,\n# Touch ID sudo setups, and environments where prompting is just noise.\n#\n# ALTERNATIVES:\n# - SSH backend: Configure passwordless sudo on the remote server\n# - Containers: Run as root inside the container (no sudo needed)\n# - Local: Configure /etc/sudoers for specific commands\n#\n# Example (add to your terminal section):\n# sudo_password: \"your-password-here\"\n\n# =============================================================================\n# Security Scanning (tirith)\n# =============================================================================\n# Optional pre-exec command security scanning via tirith.\n# Detects homograph URLs, pipe-to-shell, terminal injection, env manipulation.\n# Install: brew install sheeki03/tap/tirith\n# Docs: https://github.com/sheeki03/tirith\n#\n# security:\n# tirith_enabled: true # Enable/disable tirith scanning\n# tirith_path: \"tirith\" # Path to tirith binary (supports ~ expansion)\n# tirith_timeout: 5 # Scan timeout in seconds\n# tirith_fail_open: true # Allow commands if tirith unavailable\n\n# =============================================================================\n# Browser Tool Configuration\n# =============================================================================\nbrowser:\n # Inactivity timeout in seconds - browser sessions are automatically closed\n # after this period of no activity between agent loops (default: 120 = 2 minutes)\n inactivity_timeout: 120\n\n# =============================================================================\n# Tool Loop Guardrails\n# =============================================================================\n# Soft warnings are enabled by default. They append guidance to repeated failed\n# or non-progressing tool results but still let the tool execute. Hard stops are\n# opt-in circuit breakers for autonomous/cron sessions where stopping a loop is\n# preferable to spending the full iteration budget.\ntool_loop_guardrails:\n warnings_enabled: true\n hard_stop_enabled: false\n warn_after:\n exact_failure: 2\n same_tool_failure: 3\n idempotent_no_progress: 2\n hard_stop_after:\n exact_failure: 5\n same_tool_failure: 8\n idempotent_no_progress: 5\n\n# =============================================================================\n# Context Compression (Auto-shrinks long conversations)\n# =============================================================================\n# When conversation approaches model's context limit, middle turns are\n# automatically summarized to free up space while preserving important context.\n#\n# HOW IT WORKS:\n# 1. Tracks actual token usage from API responses (not estimates)\n# 2. When prompt_tokens >= threshold% of model's context_length, triggers compression\n# 3. Protects first 3 turns (system prompt, initial request, first response)\n# 4. Protects last N turns (default 20 messages = ~10 full turns of recent context)\n# 5. Summarizes middle turns using a fast/cheap model\n# 6. Inserts summary as a user message, continues conversation seamlessly\n#\n# Post-compression tail budget is target_ratio × threshold × context_length:\n# 200K context, threshold 0.50, ratio 0.20 → 20K tokens of recent tail preserved\n# 1M context, threshold 0.50, ratio 0.20 → 100K tokens of recent tail preserved\n#\ncompression:\n # Enable automatic context compression (default: true)\n # Set to false if you prefer to manage context manually or want errors on overflow\n enabled: true\n\n # Opt-in compression progress notices on chat platforms (default: false).\n # By design, routine automatic compression is SILENT on human-facing chat\n # gateways (Telegram, Discord, Slack, ...) — it happens in the background\n # with server-side logging only. Set true to also deliver the routine\n # progress statuses (compacting started, preflight/pre-API compression,\n # idle compaction, retry progress, and the compaction-complete notice) to\n # chat platforms. Unrelated operational noise (auxiliary model failures,\n # provider retry chatter) stays suppressed either way, and compression\n # FAILURE notices + manual /compress feedback are always visible\n # regardless of this setting. (#52995)\n progress_notices: false\n \n # Trigger compression at this % of model's context limit (default: 0.50 = 50%)\n # Lower values = more aggressive compression, higher values = compress later\n # Models with context windows below 512K are floored at 0.75 (raise-only) so\n # compaction doesn't fire with half the window still free; set above 0.75 to override.\n threshold: 0.50\n\n # Per-model threshold overrides: keys are substring-matched against the model\n # name (longest match wins). Useful when some models need different compaction\n # points — e.g. a 1M-context model can compress later (0.30) while a 128K\n # model needs to compress earlier (0.60). The small-context floor (75% for\n # <512K models) still applies on top of per-model overrides.\n # model_thresholds:\n # \"glm-5.2\": 0.40\n # \"claude-sonnet\": 0.35\n # \"gpt-5\": 0.30\n\n # Optional absolute token cap for the compression trigger (default: null = disabled).\n # When set, compression fires at the LOWER of the ratio-based threshold and this\n # absolute token count — first-fires-wins. It never fires later than this count\n # regardless of which model is active (useful when switching between models with\n # very different context windows). Clamped to the model's context length at\n # apply-time, so a cap above the window is a no-op (ratio-based threshold wins).\n # Survives model switches and fallback activations.\n # threshold_tokens: 200000\n\n # Existing Codex gpt-5.5 behavior: raise Hermes' compaction trigger to 85%\n # for the ChatGPT Codex OAuth route. Set false to opt back down to threshold.\n codex_gpt55_autoraise: true\n\n # Fraction of the threshold to preserve as recent tail (default: 0.20 = 20%)\n # e.g. 20% of 50% threshold = 10% of total context kept as recent messages.\n # Summary output is separately capped at 12K tokens (Gemini output limit).\n # Range: 0.10 - 0.80\n target_ratio: 0.20\n\n # Number of most-recent messages to always preserve (default: 20 ≈ 10 full turns)\n # Higher values keep more recent conversation intact at the cost of more aggressive\n # compression of older turns.\n protect_last_n: 20\n\n # Minimum number of REAL (actionable) user messages guaranteed to survive in\n # the uncompressed tail (default: 1 = the existing single last-user anchor,\n # behavior-preserving). Raise to e.g. 3 to keep the last 3 real user turns\n # verbatim even when bulky tool outputs fill the tail token budget — blank\n # platform echoes, compaction handoffs, and synthetic continuation rows never\n # count toward N. The tail can exceed the token budget when this pulls the\n # cut back; the guarantee wins over the budget by design.\n min_tail_user_messages: 1\n\n # Compression retry rounds before a turn gives up with \"max compression\n # attempts reached\" (default: 3, same as the previous hardcoded value).\n # Raise (e.g. 6) for tool-schema-heavy sessions where 3 rounds cannot bring\n # the request estimate under the threshold. Validated >= 1, hard cap 10.\n max_attempts: 3\n\n # Codex app-server (codex CLI runtime) thread-compaction mode. The codex\n # agent owns the real thread context on this runtime, so Hermes' summarizer\n # cannot shrink it — compaction goes through the app server instead.\n # native = let Codex decide when to compact its own thread (default)\n # hermes = let Hermes threshold trigger Codex thread/compact/start\n # off = Hermes will not auto-trigger compaction; Codex may still compact natively\n codex_app_server_auto: native\n\n # Number of non-system messages to protect at the head of the transcript, in\n # ADDITION to the system prompt (which is always implicitly protected).\n # Head messages are NEVER summarized — they survive every compression\n # indefinitely. This gives stable early context for short/medium sessions,\n # but in long-running sessions that rely on rolling compaction the pinned\n # opening turns may not match how you want the session framed over time.\n # Set to 0 to preserve ONLY the system prompt (plus the rolling summary\n # and recent tail) — the cleanest configuration for long-running sessions.\n # Default 3 preserves the system prompt plus the first three non-system\n # head messages, matching the pre-feature behaviour.\n protect_first_n: 3\n\n # Idle compaction (default: 0 = disabled). When > 0, a session that resumes\n # after at least this many seconds of inactivity compacts its accumulated\n # history up front, before the first reply, so a long-lived thread you come\n # back to later doesn't re-read its full stale context on every turn.\n # Time-based, so it complements (does not replace) the size-based `threshold`\n # above. It is skipped when the context is already small (at or below the\n # post-compression target = threshold × target_ratio), so it never wastes a\n # summarization on a short idle thread. Example: 1800 = compact after 30 min idle.\n idle_compact_after_seconds: 0\n\n # Proactive tool-result prune (default: 0 = disabled). Opt-in token trigger\n # for a deterministic, no-LLM prune of OLD tool-result payloads, run\n # independently of `threshold` above. On large-window models (512K/1M) the\n # ratio threshold rarely fires, so bulky tool outputs (terminal dumps, file\n # reads, web extracts) ride along in history and get re-billed every turn.\n # When re-sent history exceeds this many tokens, the prune dedupes identical\n # results, summarizes older oversized ones, and truncates large tool-call\n # arguments — protecting the most recent `protect_last_n` messages and never\n # calling the model. Try 48000 to enable. Built-in compressor engine only;\n # other context engines inherit a safe no-op.\n # NOTE: a committed prune rewrites already-sent history, which invalidates\n # the provider's prompt-cache prefix — the min_reclaim gate below keeps\n # those cache breaks episodic (like a compression boundary) instead of\n # per-turn.\n proactive_prune_tokens: 0\n\n # The prune's summarize pass only touches tool results larger than this many\n # characters (clamped to >= 200 so a generated summary can't be\n # re-summarized). Default 8000.\n proactive_prune_min_result_chars: 8000\n\n # A proactive prune only COMMITS when it reclaims at least this many tokens\n # (measured on the pruned output). This is the prompt-cache hysteresis gate:\n # one meaningful, amortized cache break per batch of stale tool output\n # instead of a tiny break on every tool iteration. 0 = commit any non-zero\n # prune. Default 4096.\n proactive_prune_min_reclaim_tokens: 4096\n\n # To pin a specific model/provider for compression summaries, use the\n # auxiliary section below (auxiliary.compression.provider / model).\n\n# =============================================================================\n# Anthropic prompt caching TTL\n# =============================================================================\n# When prompt caching is active (Claude via OpenRouter or native Anthropic),\n# Anthropic supports two TTL tiers for cached prefixes: \"5m\" (default) and\n# \"1h\". Other values are ignored and \"5m\" is used.\n#\nprompt_caching:\n cache_ttl: \"5m\" # use \"1h\" for long sessions with pauses between turns\n\n# =============================================================================\n# Auxiliary Models (Advanced — Experimental)\n# =============================================================================\n# Hermes uses lightweight \"auxiliary\" models for side tasks: image analysis,\n# browser screenshot analysis, web page summarization, TTS audio-tag insertion,\n# and context compression.\n#\n# By default these use Gemini Flash via OpenRouter or Nous Portal and are\n# auto-detected from your credentials. You do NOT need to change anything\n# here for normal usage.\n#\n# WARNING: Overriding these with providers other than OpenRouter or Nous Portal\n# is EXPERIMENTAL and may not work. Not all models/providers support vision,\n# produce usable summaries, or accept the same API format. Change at your own\n# risk — if things break, reset to \"auto\" / empty values.\n#\n# Each task has its own provider + model pair so you can mix providers.\n# For example: OpenRouter for vision (needs multimodal), but your main\n# local endpoint for compression (just needs text).\n#\n# Provider options:\n# \"auto\" - Best available: OpenRouter → Nous Portal → main endpoint (default)\n# \"openrouter\" - Force OpenRouter (requires OPENROUTER_API_KEY)\n# \"nous\" - Force Nous Portal (requires: hermes auth add nous)\n# \"gemini\" - Force Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n# \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY)\n# \"codex\" - Force Codex OAuth (requires: hermes model → Codex).\n# Uses gpt-5.3-codex which supports vision.\n# \"main\" - Use your custom endpoint (OPENAI_BASE_URL + OPENAI_API_KEY).\n# Works with OpenAI API, local models, or any OpenAI-compatible\n# endpoint. Also falls back to Codex OAuth and API-key providers.\n#\n# Model: leave empty to use the provider's default. When empty, OpenRouter\n# uses \"google/gemini-3-flash-preview\" and Nous uses \"gemini-3-flash\".\n# Other providers pick a sensible default automatically.\n#\n# auxiliary:\n# # Image analysis: vision_analyze tool + browser screenshots\n# vision:\n# provider: \"auto\"\n# model: \"\" # e.g. \"google/gemini-2.5-flash\", \"openai/gpt-4o\"\n# timeout: 30 # LLM API call timeout (seconds)\n# download_timeout: 30 # Image HTTP download timeout (seconds)\n# # Increase for slow connections or self-hosted image servers\n# reasoning_effort: \"\" # Per-task thinking level: none, minimal, low, medium,\n# # high, xhigh, max, ultra. Empty = provider default.\n# # Works on every auxiliary task block (vision,\n# # web_extract, compression, title_generation, curator,\n# # background_review, moa_reference, ...). Example: run\n# # compression at \"low\" and vision at \"none\" to cut\n# # side-task latency/cost on reasoning models.\n#\n# # Web page scraping / summarization + browser page text extraction\n# web_extract:\n# provider: \"auto\"\n# model: \"\"\n# reasoning_effort: \"low\"\n#\n# # Gemini 3.1 TTS hidden audio-tag insertion\n# tts_audio_tags:\n# provider: \"auto\" # empty model = your main chat model\n# model: \"\"\n# timeout: 30\n#\n# # Automatic session title generation after the first exchange\n# title_generation:\n# enabled: true # set false to disable auto-title generation\n# provider: \"auto\"\n# model: \"\"\n# timeout: 30\n# language: \"\" # empty = match the user's language; or e.g. \"English\"\n#\n# # Session search — summarizes matching past sessions\n# session_search:\n# provider: \"auto\"\n# model: \"\"\n# timeout: 30\n# max_concurrency: 3 # Limit parallel summaries to reduce request-burst 429s\n# extra_body: {} # Provider-specific OpenAI-compatible request fields\n# # Example for providers that support request-body\n# # reasoning controls:\n# # extra_body:\n# # enable_thinking: false\n# # Some vLLM/Qwen deployments expect this nested:\n# # extra_body:\n# # chat_template_kwargs:\n# # enable_thinking: false\n\n# =============================================================================\n# Persistent Memory\n# =============================================================================\n# Bounded curated memory injected into the system prompt every session.\n# Two stores: MEMORY.md (agent's notes) and USER.md (user profile).\n# Character limits keep the memory small and focused. The agent manages\n# pruning -- when at the limit, it must consolidate or replace entries.\n# Disabled by default in batch_runner.\n#\nmemory:\n # Agent's personal notes: environment facts, conventions, things learned\n memory_enabled: true\n \n # User profile: preferences, communication style, expectations\n user_profile_enabled: true\n \n # Character limits (~2.75 chars per token, model-independent)\n memory_char_limit: 2200 # ~800 tokens\n user_char_limit: 1375 # ~500 tokens\n\n # Periodic memory nudge: remind the agent to consider saving memories\n # every N user turns. Set to 0 to disable. Only active when memory is enabled.\n nudge_interval: 10 # Nudge every 10 user turns (0 = disabled)\n\n # Memory flush: give the agent one turn to save memories before context is\n # lost (compression, /new, /reset, exit). Set to 0 to disable.\n # For exit/reset, only fires if the session had at least this many user turns.\n flush_min_turns: 6 # Min user turns to trigger flush on exit/reset (0 = disabled)\n\n# =============================================================================\n# Session Reset Policy (Messaging Platforms)\n# =============================================================================\n# Controls when messaging sessions (Telegram, Discord, WhatsApp, Slack) are\n# automatically cleared. Default is \"none\": sessions never auto-reset —\n# conversation context lives until you /reset or /new manually, or context\n# compression kicks in. Opt in to automatic resets if you prefer sessions to\n# clear on a schedule (long-lived context increases API cost per message,\n# though prompt caching and compression keep this manageable).\n#\n# When an automatic reset triggers, the agent first saves important\n# information to its persistent memory — but the conversation context is\n# wiped. The agent starts fresh but retains learned facts via its memory\n# system.\n#\n# Modes:\n# \"none\" - Never auto-reset (default); context lives until /reset or compression\n# \"idle\" - Reset after N minutes of inactivity\n# \"daily\" - Reset at a fixed hour each day\n# \"both\" - Reset on EITHER inactivity timeout or daily boundary\n#\nsession_reset:\n mode: none # \"none\", \"idle\", \"daily\", or \"both\"\n idle_minutes: 1440 # Inactivity timeout in minutes (used by \"idle\"/\"both\")\n at_hour: 4 # Daily reset hour, 0-23 local time (used by \"daily\"/\"both\")\n\n# Maximum number of simultaneously active chat sessions across CLI, TUI,\n# dashboard chat, and messaging gateway. Set to null, 0, or omit to allow\n# unlimited concurrent sessions. When the limit is reached, new sessions get a\n# clean error while existing active sessions keep their normal behavior. This\n# top-level key takes precedence over gateway.max_concurrent_sessions. The cap\n# is a best-effort single-host/profile runtime guard; Hermes fails open if the\n# local runtime lease registry cannot be read or locked.\nmax_concurrent_sessions: null\n\n# When true, group/channel chats use one session per participant when the platform\n# provides a user ID. This is the secure default and prevents users in the same\n# room from sharing context, interrupts, and token costs. Set false only if you\n# explicitly want one shared \"room brain\" per group/channel.\ngroup_sessions_per_user: true\n\n# ─────────────────────────────────────────────────────────────────────────────\n# API Server — per-client model routing\n# ─────────────────────────────────────────────────────────────────────────────\n# Route different API clients to different models/providers on a single\n# Hermes deployment. Clients choose a backend by sending a specific string\n# as the OpenAI ``model`` field. Unmapped model values fall back to the\n# global model configured in the ``model:`` section above, and an explicit\n# session /model override always wins over a route.\n#\n# Configure via the ``platforms.api_server.extra.model_routes`` gateway\n# config block:\n#\n# platforms:\n# api_server:\n# enabled: true\n# extra:\n# key: \"your-api-server-secret\"\n# model_routes:\n# # Xiaozhi clients send model=\"minimax-m2\" → routed to MiniMax via OpenRouter\n# minimax-m2:\n# model: \"minimax/minimax-m1\"\n# provider: \"openrouter\" # optional — overrides global provider\n# # api_key: \"sk-...\" # optional — per-route UPSTREAM provider\n# # key (NOT caller auth; never logged)\n# # base_url: \"https://...\" # optional — per-route base URL\n# # GPT clients keep their own alias\n# gpt-5:\n# model: \"openai/gpt-5\"\n# provider: \"openrouter\"\n#\n# Configured aliases are automatically listed by GET /v1/models so clients\n# can discover them without manual coordination. Caller authentication is\n# unchanged: every request still authenticates with the global API server\n# key (``extra.key`` / API_SERVER_KEY).\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Gateway Streaming\n# ─────────────────────────────────────────────────────────────────────────────\n# Stream tokens to messaging platforms in real-time. The bot sends a message\n# on first token, then progressively edits it as more tokens arrive.\n# Disabled by default — enable to try the streaming UX on Telegram/Discord/Slack.\n# For Telegram, partial edits are sent as plain text and only the final edit uses MarkdownV2.\nstreaming:\n enabled: false\n # transport: edit # \"edit\" = progressive editMessageText\n # edit_interval: 0.3 # seconds between message edits\n # buffer_threshold: 40 # chars before forcing an edit flush\n # cursor: \" ▉\" # cursor shown during streaming\n\n# =============================================================================\n# Skills Configuration\n# =============================================================================\n# Skills are reusable procedures the agent can load and follow. The agent can\n# also create new skills after completing complex tasks.\n#\nskills:\n # Nudge the agent to create skills after complex tasks.\n # Every N tool-calling iterations, remind the model to consider saving a skill.\n # Set to 0 to disable.\n creation_nudge_interval: 15\n\n # External skill directories — share skills across tools/agents without\n # copying them into ~/.hermes/skills/. Each path is expanded (~ and ${VAR})\n # and resolved to an absolute path. External dirs are read-only: skill\n # creation always writes to ~/.hermes/skills/. Local skills take precedence\n # when names collide.\n # external_dirs:\n # - ~/.agents/skills\n # - /home/shared/team-skills\n\n# =============================================================================\n# Agent Behavior\n# =============================================================================\nagent:\n # Maximum tool-calling iterations per conversation (default: 500)\n # Higher = more room for complex tasks, but costs more tokens\n # Recommended: 20-30 for focused tasks, 50-100 for open exploration\n max_turns: 500\n\n # Inactivity timeout for gateway agent runs (seconds, 0 = unlimited).\n # The agent can run indefinitely when actively calling tools or receiving\n # API responses. Only fires after the agent has been idle for this duration.\n # gateway_timeout: 1800\n\n # Staged warning: send a warning before escalating to full timeout.\n # Fires once per run when inactivity reaches this threshold (seconds).\n # Set to 0 to disable the warning.\n # gateway_timeout_warning: 900\n\n # Graceful drain timeout for gateway stop/restart (seconds).\n # Default 0 = no drain: a restart interrupts in-flight agents immediately,\n # cleans up, and exits. Set a positive value only if you want a grace\n # window on /restart, and keep it well under systemd's TimeoutStopSec.\n # restart_drain_timeout: 0\n\n # Upper bound (seconds) a submitted prompt waits for the deferred agent\n # build (MCP discovery, model metadata, skills scan) before failing with a\n # visible error. The wait is patient — the message is delivered as soon as\n # the build completes, and a progress notice is shown past 30s — so this cap\n # only fires on a genuinely hung build. Raise it for deployments with many\n # slow or unreachable MCP servers. Default 600.\n # build_wait_timeout: 600\n\n # Max app-level retry attempts for API errors (connection drops, provider\n # timeouts, 5xx, etc.) before the agent surfaces the failure. Lower this\n # to 1 if you use fallback providers and want fast failover on flaky\n # primaries (default 3). The OpenAI SDK does its own low-level retries\n # underneath this wrapper — this is the Hermes-level loop.\n # api_max_retries: 3\n\n # After the agent edits code without fresh passing verification, nudge it to\n # verify before finishing. The default \"auto\" enables it on interactive\n # coding surfaces (CLI, TUI, desktop) and programmatic callers, and disables\n # it on conversational messaging surfaces (Telegram, Discord, etc.) where the\n # verification summary would reach a human as chat noise. Set true or false to\n # force it on or off; the HERMES_VERIFY_ON_STOP env var (1/0) takes precedence.\n # verify_on_stop: auto\n\n # Standing operator instructions for the coding posture (when Hermes is in a\n # code workspace). Appended to the coding brief as an extra system block, so\n # you can pin project-wide workflow rules without editing the shipped brief.\n # Accepts a string or a list of strings. Takes effect next session.\n # coding_instructions:\n # - \"For UI work, don't run tsc/lint until I approve the look.\"\n # - \"Clean the diff before you commit and push.\"\n\n # When verify-on-stop finds edited code without fresh verification evidence,\n # append guidance for creative UI work (avoid broad tsc/lint/test before visual\n # approval) and clean-diff expectations. Set false to keep that nudge terse.\n # verify_guidance: true\n\n # A `pre_verify` hook (plugin or shell, see Event Hooks docs) can keep the\n # agent going one more turn to verify/clean before finishing. This caps how\n # many times one turn may be nudged to continue, so a hook can't trap the loop.\n # Default 3.\n # max_verify_nudges: 3\n\n # Enable verbose logging\n verbose: false\n \n # Reasoning effort level (OpenRouter and Nous Portal)\n # Controls how much \"thinking\" the model does before responding.\n # Options: \"xhigh\" (max), \"high\", \"medium\", \"low\", \"minimal\", \"none\" (disable)\n reasoning_effort: \"medium\"\n \n # Per-model reasoning effort overrides (optional dict)\n # Key: any sensible model spelling works (exact, dots↔dashes interchangeable,\n # provider prefix optional). First match wins.\n # Value: reasoning effort level (same options as reasoning_effort)\n # Override the global reasoning_effort for that specific model.\n # NOTE: no `hermes config set` support for this key -- edit YAML directly.\n # reasoning_overrides:\n # \"openrouter/anthropic/claude-opus-4.5\": \"xhigh\"\n # \"openai/gpt-5\": \"low\"\n # \"claude-opus-4.6\": \"high\" # bare model name also works\n # \"deepseek/deepseek-v4-pro\": \"xhigh\" # dots and dashes are interchangeable\n reasoning_overrides: {}\n \n # Predefined personalities (use with /personality command)\n personalities:\n helpful: \"You are a helpful, friendly AI assistant.\"\n concise: \"You are a concise assistant. Keep responses brief and to the point.\"\n technical: \"You are a technical expert. Provide detailed, accurate technical information.\"\n creative: \"You are a creative assistant. Think outside the box and offer innovative solutions.\"\n teacher: \"You are a patient teacher. Explain concepts clearly with examples.\"\n kawaii: \"You are a kawaii assistant! Use cute expressions like (◕‿◕), ★, ♪, and ~! Add sparkles and be super enthusiastic about everything! Every response should feel warm and adorable desu~! ヽ(>∀<☆)ノ\"\n catgirl: \"You are Neko-chan, an anime catgirl AI assistant, nya~! Add 'nya' and cat-like expressions to your speech. Use kaomoji like (=^・ω・^=) and ฅ^•ﻌ•^ฅ. Be playful and curious like a cat, nya~!\"\n pirate: \"Arrr! Ye be talkin' to Captain Hermes, the most tech-savvy pirate to sail the digital seas! Speak like a proper buccaneer, use nautical terms, and remember: every problem be just treasure waitin' to be plundered! Yo ho ho!\"\n shakespeare: \"Hark! Thou speakest with an assistant most versed in the bardic arts. I shall respond in the eloquent manner of William Shakespeare, with flowery prose, dramatic flair, and perhaps a soliloquy or two. What light through yonder terminal breaks?\"\n surfer: \"Duuude! You're chatting with the chillest AI on the web, bro! Everything's gonna be totally rad. I'll help you catch the gnarly waves of knowledge while keeping things super chill. Cowabunga! 🤙\"\n noir: \"The rain hammered against the terminal like regrets on a guilty conscience. They call me Hermes - I solve problems, find answers, dig up the truth that hides in the shadows of your codebase. In this city of silicon and secrets, everyone's got something to hide. What's your story, pal?\"\n uwu: \"hewwo! i'm your fwiendwy assistant uwu~ i wiww twy my best to hewp you! *nuzzles your code* OwO what's this? wet me take a wook! i pwomise to be vewy hewpful >w<\"\n philosopher: \"Greetings, seeker of wisdom. I am an assistant who contemplates the deeper meaning behind every query. Let us examine not just the 'how' but the 'why' of your questions. Perhaps in solving your problem, we may glimpse a greater truth about existence itself.\"\n hype: \"YOOO LET'S GOOOO!!! 🔥🔥🔥 I am SO PUMPED to help you today! Every question is AMAZING and we're gonna CRUSH IT together! This is gonna be LEGENDARY! ARE YOU READY?! LET'S DO THIS! 💪😤🚀\"\n\n# =============================================================================\n# Toolsets\n# =============================================================================\n# Control which tools the agent has access to.\n# Use `hermes tools` to interactively enable/disable tools per platform.\n\n# =============================================================================\n# Platform Toolsets (per-platform tool configuration)\n# =============================================================================\n# Override which toolsets are available on each platform.\n# If a platform isn't listed here, its built-in default is used.\n#\n# You can use EITHER:\n# - A preset like \"hermes-cli\" or \"hermes-telegram\" (curated tool set)\n# - A list of individual toolsets to compose your own (see list below)\n#\n# Supported platform keys: cli, telegram, discord, whatsapp, slack, qqbot, teams, google_chat\n#\n# Examples:\n#\n# # Use presets (same as defaults):\n# platform_toolsets:\n# cli: [hermes-cli]\n# telegram: [hermes-telegram]\n#\n# # Custom: give Telegram only web + terminal + file + planning:\n# platform_toolsets:\n# telegram: [web, terminal, file, todo]\n#\n# # Custom: CLI without browser or image gen:\n# platform_toolsets:\n# cli: [web, terminal, file, skills, todo, tts, cronjob]\n#\n# # Restrictive: Discord gets read-only tools only:\n# platform_toolsets:\n# discord: [web, vision, skills, todo]\n#\n# If not set, defaults are:\n# cli: hermes-cli (everything + cronjob management)\n# telegram: hermes-telegram (terminal, file, web, vision, image, tts, browser, skills, todo, cronjob, messaging)\n# discord: hermes-discord (same as telegram)\n# whatsapp: hermes-whatsapp (same as telegram)\n# slack: hermes-slack (same as telegram)\n# signal: hermes-signal (same as telegram)\n# homeassistant: hermes-homeassistant (same as telegram)\n# qqbot: hermes-qqbot (same as telegram)\n# teams: hermes-teams (same as telegram)\n# google_chat: hermes-google_chat (same as telegram)\n#\nplatform_toolsets:\n cli: [hermes-cli]\n telegram: [hermes-telegram]\n discord: [hermes-discord]\n whatsapp: [hermes-whatsapp]\n slack: [hermes-slack]\n signal: [hermes-signal]\n homeassistant: [hermes-homeassistant]\n qqbot: [hermes-qqbot]\n yuanbao: [hermes-yuanbao]\n teams: [hermes-teams]\n google_chat: [hermes-google_chat]\n\n# =============================================================================\n# Gateway Platform Settings\n# =============================================================================\n# Optional per-platform messaging settings.\n# Platform-specific knobs live under `extra`.\n#\n# platforms:\n# telegram:\n# reply_to_mode: \"first\" # off | first | all\n# # guest_mode lets explicit @mentions from non-allowlisted groups through.\n# # Default false; ordinary messages, replies, and regex wake words stay blocked.\n# guest_mode: false\n# # allowed_chats: [\"-1001234567890\"]\n# extra:\n# disable_link_previews: false # Set true to suppress Telegram URL previews in bot messages\n# 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\n# rich_drafts: false # Experimental rich draft previews during Telegram DM streaming; default false because Telegram Desktop/macOS can visually overlay draft frames\n# command_menu:\n# # Telegram allows up to 100 BotCommands; Hermes defaults to 60 so\n# # all built-in commands plus common skill commands stay visible\n# # while remaining under Telegram's payload-size limit. Clamped 1..100.\n# max_commands: 60\n# # prepend = user priority first, then Hermes defaults\n# # append = Hermes defaults first, then user priority\n# # replace = only the list below defines priority\n# priority_mode: prepend\n# priority:\n# - my_plugin_command\n# webhook:\n# extra:\n# # Route scripts default to a 30 second timeout. Scripts must live under\n# # the active profile's scripts directory and receive webhook JSON on stdin.\n# script_timeout_seconds: 30\n#\n# Discord-specific settings (config.yaml top-level, not under platforms:):\n#\n# discord:\n# require_mention: true # Require @mention in server channels (default: true)\n# auto_thread: true # Auto-create thread on @mention (default: true)\n# free_response_channels: \"\" # Channel IDs where no mention is needed\n# reactions: true # Show processing reactions (default: true)\n# history_backfill: true # Recover missed channel messages on mention (default: true)\n# history_backfill_limit: 50 # Max messages to scan backwards (default: 50)\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Available toolsets (use these names in platform_toolsets or the toolsets list)\n#\n# Run `hermes chat --list-toolsets` to see all toolsets and their tools.\n# Run `hermes chat --list-tools` to see every individual tool with descriptions.\n# ─────────────────────────────────────────────────────────────────────────────\n#\n# INDIVIDUAL TOOLSETS (compose your own):\n# web - web_search, web_extract\n# search - web_search only (no scraping)\n# terminal - terminal, process\n# file - read_file, write_file, patch, search\n# browser - browser_navigate, browser_snapshot, browser_click, browser_type,\n# browser_scroll, browser_back, browser_press,\n# browser_get_images, browser_vision (requires BROWSERBASE_API_KEY)\n# vision - vision_analyze (requires OPENROUTER_API_KEY)\n# image_gen - image_generate (requires FAL_KEY)\n# skills - skills_list, skill_view\n# skills_hub - skill_hub (search/install/manage from online registries — user-driven only)\n# todo - todo (in-memory task planning, no deps)\n# tts - text_to_speech (Edge TTS free, or ELEVENLABS/OPENAI/MINIMAX/MISTRAL key)\n# cronjob - cronjob (create/list/update/pause/resume/run/remove scheduled tasks)\n#\n# PRESETS (curated bundles):\n# hermes-cli - All of the above except send_message\n# hermes-telegram - terminal, file, web, vision, image_gen, tts, browser,\n# skills, todo, cronjob, send_message\n# hermes-discord - Same as hermes-telegram\n# hermes-whatsapp - Same as hermes-telegram\n# hermes-slack - Same as hermes-telegram\n#\n# COMPOSITE:\n# debugging - terminal + web + file\n# safe - web + vision (no terminal access)\n# all - Everything available\n#\n# web - Web search and content extraction (web_search, web_extract)\n# search - Web search only, no scraping (web_search)\n# terminal - Command execution and process management (terminal, process)\n# file - File operations: read, write, patch, search\n# browser - Full browser automation (navigate, click, type, screenshot, etc.)\n# vision - Image analysis (vision_analyze)\n# image_gen - Image generation with FLUX (image_generate)\n# skills - Load skill documents (skills_list, skill_view)\n# todo - Task planning and tracking for multi-step work\n# memory - Persistent memory across sessions (personal notes + user profile)\n# session_search - Search and recall past conversations (FTS5 + Gemini Flash summarization)\n# tts - Text-to-speech (Edge TTS free, ElevenLabs, OpenAI, MiniMax, Mistral)\n# cronjob - Schedule and manage automated tasks (CLI-only)\n#\n# Composite toolsets:\n# debugging - terminal + web + file (for troubleshooting)\n# safe - web + vision (no terminal access)\n\n# NOTE: The top-level \"toolsets\" key is deprecated and ignored.\n# Tool configuration is managed per-platform via platform_toolsets above.\n# Use `hermes tools` to configure interactively, or edit platform_toolsets directly.\n#\n# CLI override: hermes chat --toolsets terminal,web,file\n\n# =============================================================================\n# MCP (Model Context Protocol) Servers\n# =============================================================================\n# Connect to external MCP servers to add tools from the MCP ecosystem.\n# Each server's tools are automatically discovered and registered.\n# See website/docs/user-guide/features/mcp.md for full documentation.\n#\n# Stdio servers (spawn a subprocess):\n# command: the executable to run\n# args: command-line arguments\n# env: environment variables (only these + safe defaults passed to subprocess)\n#\n# HTTP servers (connect to a URL):\n# url: the MCP server endpoint\n# headers: HTTP headers (e.g., for authentication)\n#\n# Optional per-server settings:\n# timeout: tool call timeout in seconds (default: 120)\n# connect_timeout: initial connection timeout (default: 60)\n# keepalive_interval: liveness ping cadence in seconds (default: 180).\n# Lower it below the server's session TTL for servers that expire idle\n# sessions quickly (e.g. Unreal Engine editor MCP, ~15s), otherwise idle\n# tool calls hit an expired session and pay a slow reconnect. Floored at 5s.\n#\n# mcp_servers:\n# time:\n# command: uvx\n# args: [\"mcp-server-time\"]\n# filesystem:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-filesystem\", \"/home/user\"]\n# notion:\n# url: https://mcp.notion.com/mcp\n# github:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-github\"]\n# env:\n# GITHUB_PERSONAL_ACCESS_TOKEN: \"ghp_...\"\n#\n# Sampling (server-initiated LLM requests) — enabled by default.\n# Per-server config under the 'sampling' key:\n# analysis:\n# command: npx\n# args: [\"-y\", \"analysis-server\"]\n# sampling:\n# enabled: true # default: true\n# model: \"gemini-3-flash\" # override model (optional)\n# max_tokens_cap: 4096 # max tokens per request\n# timeout: 30 # LLM call timeout (seconds)\n# max_rpm: 10 # max requests per minute\n# allowed_models: [] # model whitelist (empty = all)\n# max_tool_rounds: 5 # tool loop limit (0 = disable)\n# log_level: \"info\" # audit verbosity\n\n# =============================================================================\n# Text-to-Speech\n# =============================================================================\n# TTS defaults to Edge TTS unless changed in ~/.hermes/config.yaml.\n# Gemini TTS supports persona/director prompt files, and Gemini 3.1 Flash TTS\n# can use a hidden auxiliary rewrite pass to insert expressive square-bracket\n# audio tags into the TTS script without showing tags in chat.\n#\n# tts:\n# provider: \"gemini\"\n# gemini:\n# model: \"gemini-3.1-flash-tts-preview\"\n# voice: \"Kore\"\n# audio_tags: false\n# persona_prompt_file: \"\" # e.g. ~/.hermes/tts/radio-host.md\n\n# =============================================================================\n# Voice Transcription (Speech-to-Text)\n# =============================================================================\n# Automatically transcribe voice messages on messaging platforms.\n# Providers: local (free, faster-whisper) | groq (free tier) | openai (Whisper API) | mistral (Voxtral Transcribe)\n# Set the corresponding API key in .env: GROQ_API_KEY, OPENAI_API_KEY, or MISTRAL_API_KEY.\nstt:\n enabled: true\n # provider: \"local\" # auto-detected if omitted\n local:\n model: \"base\" # tiny | base | small | medium | large-v3 | turbo\n # language: \"\" # auto-detect; set to \"en\", \"es\", \"fr\", etc. to force\n # initial_prompt: \"\" # Optional faster-whisper prompt, e.g. bias Chinese output to simplified Chinese\n language: \"en\" # GLOBAL language hint for every STT provider (per-provider language wins). Set \"\" for auto-detect.\n # groq:\n # model: \"whisper-large-v3-turbo\"\n # language: \"\" # blank = stt.language > HERMES_LOCAL_STT_LANGUAGE > auto-detect\n openai:\n model: \"whisper-1\" # whisper-1 | gpt-4o-mini-transcribe | gpt-4o-transcribe\n language: \"\" # auto-detect; set to \"en\", \"es\", \"fr\", etc. to force\n # mistral:\n # model: \"voxtral-mini-latest\" # voxtral-mini-latest | voxtral-mini-2602\n # deepinfra:\n # # Model id is discovered live from the DeepInfra catalog filtered\n # # by the `stt` surface tag — leave `model` blank to take the first\n # # live result. Pin only when you need a specific Whisper variant.\n # model: \"\"\n\n# Text-to-speech. Only the deepinfra block is documented here — the\n# remaining providers (edge, openai, xai, minimax, mistral, gemini,\n# elevenlabs, neutts, kittentts, piper) inherit sensible defaults from\n# DEFAULT_CONFIG in hermes_cli/config.py.\n# tts:\n# provider: \"deepinfra\"\n# deepinfra:\n# # Model id is discovered live from the DeepInfra catalog filtered\n# # by the `tts` surface tag — leave `model` blank to take the first\n# # live result.\n# model: \"\"\n# voice: \"default\"\n\n# Image generation. Each provider plugin reads its own ``image_gen.<name>``\n# block; deepinfra discovers models live from\n# api.deepinfra.com/v1/openai/models filtered by the ``image-gen`` tag —\n# no model id is hardcoded, so retired models disappear automatically.\n# image_gen:\n# provider: \"deepinfra\"\n# deepinfra:\n# # Leave `model` blank for the first live `image-gen`-tagged result.\n# model: \"\"\n\n# =============================================================================\n# Response Pacing (Messaging Platforms)\n# =============================================================================\n# Add human-like delays between message chunks.\n# human_delay:\n# mode: \"off\" # \"off\" | \"natural\" | \"custom\"\n# min_ms: 800 # Min delay (custom mode only)\n# max_ms: 2500 # Max delay (custom mode only)\n\n# =============================================================================\n# Session Logging\n# =============================================================================\n# Session trajectories are automatically saved to logs/ directory.\n# Each session creates: logs/session_YYYYMMDD_HHMMSS_UUID.json\n#\n# The session ID is displayed in the welcome banner for easy reference.\n# Logs contain full conversation history in trajectory format:\n# - System prompt, user messages, assistant responses\n# - Tool calls with inputs/outputs\n# - Timestamps for debugging\n#\n# No configuration needed - logging is always enabled.\n# To disable, you would need to modify the source code.\n\n# =============================================================================\n# Code Execution Sandbox (Programmatic Tool Calling)\n# =============================================================================\n# The execute_code tool runs Python scripts that call Hermes tools via RPC.\n# Intermediate tool results stay out of the LLM's context window.\ncode_execution:\n timeout: 300 # Max seconds per script before kill (default: 300 = 5 min)\n max_tool_calls: 50 # Max RPC tool calls per execution (default: 50)\n\n# =============================================================================\n# Subagent Delegation\n# =============================================================================\n# The delegate_task tool spawns child agents with isolated context.\n# Supports single tasks and batch mode (default 3 parallel, configurable).\ndelegation:\n max_iterations: 50 # Max tool-calling turns per child (default: 50)\n # max_concurrent_children: 3 # Max parallel child agents per batch (default: 3, floor: 1, no ceiling).\n # WARNING: values above 10 multiply API cost linearly.\n # max_spawn_depth: 1 # Delegation tree depth cap (range: 1-3, default: 1 = flat).\n # Raise to 2 to allow workers to spawn their own subagents.\n # Requires role=\"orchestrator\" on intermediate agents.\n # orchestrator_enabled: true # Kill switch for role=\"orchestrator\" children (default: true).\n # subagent_auto_approve: false # When a subagent hits a dangerous-command approval prompt, auto-deny (default: false)\n # or auto-approve \"once\" (true) instead of blocking on stdin.\n # The parent TUI owns stdin, so blocking would deadlock; non-interactive resolution is required.\n # Both choices emit a logger.warning audit line. Flip to true only for cron/batch pipelines.\n # inherit_mcp_toolsets: true # When explicit child toolsets are narrowed, also keep the parent's MCP toolsets (default: true). Set false for strict intersection.\n # model: \"google/gemini-3-flash-preview\" # Override model for subagents (empty = inherit parent)\n # provider: \"openrouter\" # Override provider for subagents (empty = inherit parent)\n # # Resolves full credentials (base_url, api_key) automatically.\n # # Supported: openrouter, nous, zai, kimi-coding, minimax\n\n# =============================================================================\n# Honcho Integration (Cross-Session User Modeling)\n# =============================================================================\n# AI-native persistent memory via Honcho (https://honcho.dev/).\n# Builds a deeper understanding of the user across sessions and tools.\n# Runs alongside USER.md — additive, not a replacement.\n#\n# Requires: pip install honcho-ai\n# Config: ~/.honcho/config.json (shared with Claude Code, Cursor, etc.)\n# API key: HONCHO_API_KEY in ~/.hermes/.env or ~/.honcho/config.json\n#\n# Hermes-specific overrides (optional — most config comes from ~/.honcho/config.json):\n# honcho: {}\n\n# =============================================================================\n# Display\n# =============================================================================\ndisplay:\n # Use compact banner mode (hides the ASCII-art banner, shows a single line).\n # true: Compact single-line banner\n # false: Full ASCII banner with tool/skill summary (default)\n compact: false\n\n # Tool progress display level (CLI and gateway)\n # off: Silent — no tool activity shown, just the final response\n # new: Show a tool indicator only when the tool changes (skip repeats)\n # all: Show every tool call with a short preview (default)\n # verbose: Full args, results, and debug logs (same as /verbose)\n # log: Silent in chat; append every tool call to ~/.hermes/logs/tool_calls.log (gateway only)\n # Toggle at runtime with /verbose in the CLI\n tool_progress: all\n\n # Per-platform defaults can be quieter than the global setting. Telegram\n # tunes for mobile: tool_progress and busy_ack_detail default off (no\n # per-tool breadcrumb stream, no \"iteration 21/60\" debug detail in busy\n # acks or heartbeats), but interim_assistant_messages and\n # long_running_notifications STAY ON so the user has real signal between\n # turn start and final answer (mid-turn assistant commentary + a single\n # edit-in-place \"⏳ Working — N min\" heartbeat). Override under\n # display.platforms.telegram.\n\n # Auto-cleanup of temporary progress bubbles after the final response lands.\n # On platforms that support message deletion (currently Telegram), this\n # removes the tool-progress bubble, \"⏳ Still working...\" notices, and\n # context-pressure status messages once the final reply has been delivered —\n # keeping long-running turns visible live, then tidy afterward. Failed runs\n # leave the bubbles in place as breadcrumbs. Off by default.\n # Per-platform override: display.platforms.telegram.cleanup_progress\n # true: Delete tracked progress/status bubbles on successful turn\n # false: Leave everything in place (default)\n # Example:\n # display:\n # platforms:\n # telegram:\n # cleanup_progress: true\n cleanup_progress: false\n\n # Natural mid-turn assistant updates.\n # On gateway platforms, when true, completed assistant status messages are\n # sent as separate chat messages. On the Desktop app, when true, mid-turn\n # assistant narration streamed between tool calls is kept in the transcript\n # instead of the bubble collapsing to only the final message on completion.\n # Independent of tool_progress and gateway streaming.\n # true: Keep/send mid-turn assistant updates (default)\n # false: Only keep/send the final response\n interim_assistant_messages: true\n\n # Gateway-only long-running status heartbeats.\n # When false, the platform does not receive periodic \"⏳ Working — N min\"\n # notifications even if agent.gateway_notify_interval is non-zero. The\n # heartbeat edits a single message in place (where the adapter supports\n # editing) instead of posting a new bubble each interval.\n # Default: true everywhere, including Telegram (silent agents are worse\n # than a single edit-in-place heartbeat).\n long_running_notifications: true\n\n # Include detailed iteration/tool/status context in busy acknowledgments\n # and long-running heartbeats. When true, busy acks show \"iteration 21/60,\n # terminal, 10 min\" and the heartbeat shows \"⏳ Working — 12 min,\n # iteration 21/60, terminal\". When false (Telegram default), both stay\n # terse: \"Interrupting current task\" and \"⏳ Working — 12 min, terminal\".\n busy_ack_detail: true\n\n # What Enter does when Hermes is already busy (CLI and gateway platforms).\n # interrupt: Interrupt the current run and redirect Hermes (default)\n # queue: Queue your message for the next turn\n # steer: Inject your message mid-run via /steer, arriving at the agent\n # after the next tool call — no interrupt, no role violation.\n # Falls back to 'queue' if the agent isn't running yet or if\n # images are attached (steer only carries text).\n # Ctrl+C (or /stop in gateway) always interrupts regardless of this setting.\n # Toggle at runtime with /busy <interrupt|queue|steer>.\n busy_input_mode: interrupt\n\n # Background process notifications (gateway/messaging only).\n # Controls how chatty the process watcher is when you use\n # terminal(background=true, notify_on_complete=true) from Telegram/Discord/etc.\n # off: No watcher messages at all\n # result: Only the final completion message\n # error: Only the final message when exit code != 0\n # all: Running output updates + final message (default)\n background_process_notifications: all\n\n\n # Play terminal bell when agent finishes a response.\n # Useful for long-running tasks — your terminal will ding when the agent is done.\n # Works over SSH. Most terminals can be configured to flash the taskbar or play a sound.\n # true: Ring the terminal bell on each response\n # false: Silent (default)\n bell_on_complete: false\n\n # Show model reasoning/thinking before each response.\n # When enabled, a dim box shows the model's thought process above the response.\n # Toggle at runtime with /reasoning show or /reasoning hide.\n # true: Show the reasoning box\n # false: Hide reasoning (default)\n show_reasoning: false\n\n # Stream tokens to the terminal as they arrive instead of waiting for the\n # full response. The response box opens on first token and text appears\n # line-by-line. Tool calls are still captured silently.\n # true: Stream tokens as they arrive (default)\n # false: Wait for the full response before rendering\n streaming: true\n\n # Show [HH:MM] timestamps on user input and assistant response labels.\n # timestamps: false\n\n # ───────────────────────────────────────────────────────────────────────────\n # Skin / Theme\n # ───────────────────────────────────────────────────────────────────────────\n # Customize CLI visual appearance — banner colors, spinner faces, tool prefix,\n # response box label, and branding text. Change at runtime with /skin <name>.\n #\n # Built-in skins:\n # default — Classic Hermes gold/kawaii\n # ares — Crimson/bronze war-god theme with spinner wings\n # mono — Clean grayscale monochrome\n # slate — Cool blue developer-focused\n # daylight — Bright light-mode theme\n # warm-lightmode — Warm paper-tone light-mode theme\n # poseidon — Sea-green/teal Olympian theme\n # sisyphus — Earthy stone-and-moss theme\n # charizard — Fiery orange dragon theme\n #\n # Custom skins: drop a YAML file in ~/.hermes/skins/<name>.yaml\n # Schema (all fields optional, missing values inherit from default):\n #\n # name: my-theme\n # description: Short description\n # colors:\n # banner_border: \"#HEX\" # Panel border\n # banner_title: \"#HEX\" # Panel title\n # banner_accent: \"#HEX\" # Section headers (Available Tools, etc.)\n # banner_dim: \"#HEX\" # Dim/muted text\n # banner_text: \"#HEX\" # Body text (tool names, skill names)\n # ui_accent: \"#HEX\" # UI accent color\n # response_border: \"#HEX\" # Response box border color\n # spinner:\n # waiting_faces: [\"(⚔)\", \"(⛨)\"] # Faces shown while waiting\n # thinking_faces: [\"(⚔)\", \"(⌁)\"] # Faces shown while thinking\n # thinking_verbs: [\"forging\", \"plotting\"] # Verbs for spinner messages\n # wings: # Optional left/right spinner decorations\n # - [\"⟪⚔\", \"⚔⟫\"]\n # - [\"⟪▲\", \"▲⟫\"]\n # branding:\n # agent_name: \"My Agent\" # Banner title and branding\n # welcome: \"Welcome message\" # Shown at CLI startup\n # response_label: \" ⚔ Agent \" # Response box header label\n # prompt_symbol: \"⚔\" # Prompt symbol (bare token; renderers add trailing space)\n # tool_prefix: \"╎\" # Tool output line prefix (default: ┊)\n #\n skin: default\n\n# =============================================================================\n# Model Aliases — short names for /model command\n# =============================================================================\n# Map short aliases to exact (model, provider, base_url) tuples.\n# Used by /model tab completion and resolve_alias().\n# Aliases are checked BEFORE the models.dev catalog, so they can route\n# to endpoints not in the catalog (e.g. Ollama Cloud, local servers).\n#\n# model_aliases:\n# opus:\n# model: claude-opus-4-6\n# provider: anthropic\n# qwen:\n# model: \"qwen3.5:397b\"\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n# glm:\n# model: glm-4.7\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n\n# =============================================================================\n# Privacy\n# =============================================================================\n# privacy:\n# # Redact PII from the LLM context prompt.\n# # When true, phone numbers are stripped and user/chat IDs are replaced\n# # with deterministic hashes before being sent to the model.\n# # Names and usernames are NOT affected (user-chosen, publicly visible).\n# # Routing/delivery still uses the original values internally.\n# redact_pii: false\n\n# =============================================================================\n# Shell-script hooks\n# =============================================================================\n# Register shell scripts as plugin-hook callbacks. Each entry is executed as\n# a subprocess (shell=False, shlex.split) with a JSON payload on stdin. On\n# stdout the script may return JSON that either blocks the tool call or\n# injects context into the next LLM call.\n#\n# Valid events (mirror hermes_cli.plugins.VALID_HOOKS):\n# pre_tool_call, post_tool_call, pre_llm_call, post_llm_call,\n# pre_api_request, post_api_request, on_session_start, on_session_end,\n# on_session_finalize, on_session_reset, subagent_stop\n#\n# First-use consent: each (event, command) pair prompts once on a TTY, then\n# is persisted to ~/.hermes/shell-hooks-allowlist.json. Non-interactive\n# runs (gateway, cron) need --accept-hooks, HERMES_ACCEPT_HOOKS=1, or the\n# hooks_auto_accept key below.\n#\n# See website/docs/user-guide/features/hooks.md for the full JSON wire\n# protocol and worked examples.\n#\n# hooks:\n# pre_tool_call:\n# - matcher: \"terminal\"\n# command: \"~/.hermes/agent-hooks/block-rm-rf.sh\"\n# timeout: 10\n# post_tool_call:\n# - matcher: \"write_file|patch\"\n# command: \"~/.hermes/agent-hooks/auto-format.sh\"\n# pre_llm_call:\n# - command: \"~/.hermes/agent-hooks/inject-cwd-context.sh\"\n# subagent_stop:\n# - command: \"~/.hermes/agent-hooks/log-orchestration.sh\"\n#\n# hooks_auto_accept: false\n\n\n# =============================================================================\n# Update Behavior\n# =============================================================================\nupdates:\n # Create a full HERMES_HOME zip before every `hermes update`.\n # Backups land in ~/.hermes/backups/ and can be restored with `hermes import`.\n # Off by default because large homes can add minutes to every update.\n pre_update_backup: false\n\n # Number of pre-update backup zips to retain.\n backup_keep: 5\n\n # What non-interactive updates do with local source edits in the Hermes repo.\n # Interactive terminal updates always prompt before restoring the autostash.\n #\n # stash - auto-stash before pull, then auto-restore after success (default)\n # discard - drop the update-created stash after success; use only on managed\n # installs where local source edits should not persist\n non_interactive_local_changes: \"stash\"\n\n\n# =============================================================================\n# Web Dashboard\n# =============================================================================\n# OAuth gate configuration for `hermes dashboard --host <non-loopback>`.\n# The bundled Nous Portal plugin reads these on startup; settings here are\n# the canonical surface. Each can be overridden by an environment variable:\n#\n# dashboard.oauth.client_id <- HERMES_DASHBOARD_OAUTH_CLIENT_ID\n# dashboard.oauth.portal_url <- HERMES_DASHBOARD_PORTAL_URL\n# dashboard.public_url <- HERMES_DASHBOARD_PUBLIC_URL\n#\n# Env wins when set to a non-empty value. This is what Fly.io's platform-\n# secret injection uses to push per-deploy client_ids without needing to\n# bake a config.yaml into the image. Empty env values are treated as unset\n# so a provisioned-but-not-populated secret can't shadow a valid entry here.\n#\n# Local dev / on-prem deploys should typically set these via config.yaml\n# (the ~/.hermes/.env file is reserved for API keys and secrets).\n#\n# dashboard:\n# oauth:\n# client_id: \"\" # agent:{instance_id}; Portal provisions this at deploy\n# portal_url: \"\" # blank → default https://portal.nousresearch.com\n#\n# # Force the absolute base URL the OAuth callback (and any other public\n# # URL the dashboard hands to external systems) is built from. Set this\n# # for deploys behind reverse proxies that don't reliably forward\n# # X-Forwarded-Host / X-Forwarded-Proto / X-Forwarded-Prefix (manual\n# # nginx setups, on-prem ingresses, custom-domain Fly deploys without\n# # full proxy header chains).\n# #\n# # When set, the value is the complete authority: scheme + host +\n# # optional path prefix (e.g. \"https://example.com/hermes\"). The OAuth\n# # callback URL becomes \"<public_url>/auth/callback\" — X-Forwarded-Prefix\n# # is IGNORED on this code path because the operator has explicitly\n# # declared the public URL and we no longer need to guess.\n# #\n# # Leave empty to use the existing proxy-header reconstruction (the\n# # default — works on Fly.io out of the box).\n# #\n# # public_url: \"https://example.com/hermes\"\n#\n# -----------------------------------------------------------------------------\n# Self-hosted OIDC dashboard auth (generic OpenID Connect — Authentik,\n# Keycloak, Zitadel, Authelia, Auth0, Okta, Google, …). Use this INSTEAD of the\n# nous block above when gating the dashboard with your own identity provider.\n# Each setting can be overridden by an environment variable:\n#\n# dashboard.oauth.self_hosted.issuer <- HERMES_DASHBOARD_OIDC_ISSUER\n# dashboard.oauth.self_hosted.client_id <- HERMES_DASHBOARD_OIDC_CLIENT_ID\n# dashboard.oauth.self_hosted.scopes <- HERMES_DASHBOARD_OIDC_SCOPES\n# dashboard.oauth.self_hosted.client_secret <- HERMES_DASHBOARD_OIDC_CLIENT_SECRET\n#\n# dashboard:\n# oauth:\n# provider: self-hosted\n# self_hosted:\n# issuer: \"https://auth.example.com/application/o/hermes/\" # required\n# client_id: \"hermes-dashboard\" # required\n# scopes: \"openid profile email\" # optional\n#\n# # OPTIONAL — set ONLY if your IDP registered the client as\n# # *confidential* (Authentik / Keycloak often default to this). When\n# # set, Hermes authenticates the client at the token endpoint\n# # (client_secret_basic or client_secret_post, auto-selected from the\n# # IDP's discovery doc) IN ADDITION to PKCE. Leave unset for a public\n# # (PKCE-only) client — the common case.\n# #\n# # This is a CREDENTIAL: prefer setting HERMES_DASHBOARD_OIDC_CLIENT_SECRET\n# # in ~/.hermes/.env over putting it here in config.yaml.\n# # client_secret: \"\"\n\n# =============================================================================\n# External secret sources\n# =============================================================================\n# Pull provider credentials from external secret managers at process startup\n# instead of storing them in ~/.hermes/.env. Only the manager's bootstrap\n# credential (e.g. BWS_ACCESS_TOKEN / OP_SERVICE_ACCOUNT_TOKEN) lives in .env\n# (or your shell / desktop session); everything else rotates centrally.\n# Failures never block startup — Hermes warns once and continues with\n# whatever .env already had.\n#\n# Multiple sources can be enabled at once:\n# - \"mapped\" sources (explicit VAR -> ref bindings, e.g. 1Password's env:\n# map) beat \"bulk\" sources (whole-project dumps like Bitwarden BSM)\n# - within a shape, the first source to claim a var wins; later claims\n# are skipped with a startup warning (never a silent clobber)\n# - a source's override_existing lets it beat .env/shell values, but\n# never another secret source's claim\n# Docs: https://hermes-agent.nousresearch.com/docs/user-guide/secrets/\n#\n# secrets:\n# # Optional explicit ordering of enabled sources.\n# # sources: [onepassword, bitwarden]\n#\n# # ---- Bitwarden Secrets Manager (bws CLI) --------------------------------\n# bitwarden:\n# enabled: false\n# access_token_env: BWS_ACCESS_TOKEN # bootstrap token, sourced from .env\n# project_id: \"\" # UUID of the BSM project to sync\n# server_url: \"\" # \"\" = US Cloud; EU/self-hosted URL otherwise\n# cache_ttl_seconds: 300 # 0 disables fresh caching\n# encrypted_cache: # optional encrypted stale fallback\n# enabled: false\n# max_stale_seconds: 0 # 0 disables stale fallback\n# override_existing: true # BSM values win over existing env\n# auto_install: true # lazy-download bws into ~/.hermes/bin\n#\n# # ---- 1Password (op CLI) -------------------------------------------------\n# onepassword:\n# enabled: false\n# # Map env-var names to op:// secret references. Each is resolved with a\n# # single `op read` at startup.\n# env:\n# OPENAI_API_KEY: \"op://Private/OpenAI/api key\"\n# ANTHROPIC_API_KEY: \"op://Private/Anthropic/credential\"\n# account: \"\" # op --account shorthand; \"\" = default\n# service_account_token_env: OP_SERVICE_ACCOUNT_TOKEN # headless auth; unset = desktop session\n# binary_path: \"\" # \"\" = resolve op via PATH; else absolute path\n# cache_ttl_seconds: 300 # 0 disables BOTH cache layers\n# override_existing: true # resolved values win over existing env\n#\n# # ---- Command helper (any CLI vault) --------------------------------------\n# # Run a user-configured helper that prints KEY=VALUE lines on stdout —\n# # works with any secret store that has a CLI: keepassxc-cli, secret-tool,\n# # pass, gpg, or a script that cats a tmpfs env file. Composes with the\n# # sources above (enable any combination). POSIX-only (needs /bin/sh).\n# # The helper must be fast and NON-interactive (hard timeout, 1 MiB cap);\n# # its stderr is discarded so diagnostics can't leak secret material.\n# command:\n# enabled: false\n# command: \"cat /run/user/1000/hermes-secrets.env\"\n# helper_timeout_seconds: 3\n# override_existing: false # .env/shell win by default\n"}, "14bed44c8cfad8e51fab9283555fb4c53a37c998": {"t": 1785767978.406648, "n": 84828, "h": "60d3781d4a754e42", "c": "# Hermes Agent CLI Configuration\n# Copy this file to cli-config.yaml and customize as needed.\n# This file configures the CLI behavior. Environment variables in .env take precedence.\n\n# =============================================================================\n# Model Configuration\n# =============================================================================\nmodel:\n # Default model to use (can be overridden with --model flag)\n # Both \"default\" and \"model\" work as the key name here.\n default: \"anthropic/claude-opus-4.6\"\n \n # Inference provider selection:\n # \"auto\" - Auto-detect from credentials (default)\n # \"openrouter\" - OpenRouter (requires: OPENROUTER_API_KEY or OPENAI_API_KEY)\n # \"nous\" - Nous Portal OAuth (requires: hermes auth add nous)\n # \"nous-api\" - Nous Portal API key (requires: NOUS_API_KEY)\n # \"anthropic\" - Direct Anthropic API (requires: ANTHROPIC_API_KEY)\n # \"openai-codex\" - OpenAI Codex (requires: hermes auth)\n # \"copilot\" - GitHub Copilot / GitHub Models (requires: GITHUB_TOKEN)\n # \"gemini\" - Use Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n # \"zai\" - Use z.ai / ZhipuAI GLM models (requires: GLM_API_KEY)\n # \"kimi-coding\" - Kimi / Moonshot AI (requires: KIMI_API_KEY)\n # \"minimax\" - MiniMax global (requires: MINIMAX_API_KEY)\n # \"minimax-cn\" - MiniMax China (requires: MINIMAX_CN_API_KEY)\n # \"huggingface\" - Hugging Face Inference (requires: HF_TOKEN)\n # \"nvidia\" - NVIDIA NIM / build.nvidia.com (requires: NVIDIA_API_KEY)\n # \"xiaomi\" - Xiaomi MiMo (requires: XIAOMI_API_KEY)\n # \"arcee\" - Arcee AI Trinity models (requires: ARCEEAI_API_KEY)\n # \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY — https://ollama.com/settings)\n # \"deepinfra\" - DeepInfra (requires: DEEPINFRA_API_KEY)\n # \"kilocode\" - KiloCode gateway (requires: KILOCODE_API_KEY)\n # \"azure-foundry\" - Microsoft Foundry / Azure OpenAI (API key or Entra ID)\n # \"lmstudio\" - LM Studio local server (optional: LM_API_KEY, defaults to http://127.0.0.1:1234/v1)\n #\n # Local servers (LM Studio, Ollama, vLLM, llama.cpp):\n # \"custom\" - Any other OpenAI-compatible endpoint. Set base_url below.\n # Aliases: \"ollama\", \"vllm\", \"llamacpp\" all map to \"custom\".\n # LM Studio is first-class and uses provider: \"lmstudio\".\n # It works with both no-auth and auth-enabled server modes.\n #\n # Can also be overridden for a single invocation with the --provider flag.\n provider: \"auto\"\n \n # API configuration (falls back to OPENROUTER_API_KEY env var)\n # api_key: \"your-key-here\" # Uncomment to set here instead of .env\n base_url: \"https://openrouter.ai/api/v1\"\n\n # Azure Foundry keyless auth example:\n # provider: \"azure-foundry\"\n # base_url: \"https://<resource>.openai.azure.com/openai/v1\"\n # auth_mode: \"entra_id\" # DefaultAzureCredential: az login, managed identity, workload identity, etc.\n # default: \"gpt-4o\" # Deployment/model name\n # entra:\n # scope: \"https://ai.azure.com/.default\" # Optional; this is the default.\n\n # ── Token limits — two settings, easy to confuse ──────────────────────────\n #\n # context_length: TOTAL context window (input + output tokens combined).\n # Controls when Hermes compresses history and validates requests.\n # Leave unset — Hermes auto-detects the correct value from the provider.\n # Set manually only when auto-detection is wrong (e.g. a local server with\n # a custom num_ctx, or a proxy that doesn't expose /v1/models).\n #\n # context_length: 131072\n #\n # max_tokens: OUTPUT cap — maximum tokens the model may generate per response.\n # Unrelated to how long your conversation history can be.\n # The OpenAI-standard name \"max_tokens\" is a misnomer; Anthropic's native\n # API has since renamed it \"max_output_tokens\" for clarity.\n # Leave unset to use the model's native output ceiling (recommended).\n # Set only if you want to deliberately limit individual response length.\n #\n# max_tokens: 8192\n\n # ── Custom request headers (optional) ─────────────────────────────────────\n #\n # default_headers: extra HTTP headers sent on every request to an\n # OpenAI-compatible endpoint. User values take precedence over the\n # provider/SDK defaults, so this is the supported way to override the\n # OpenAI Python SDK's identifying headers (User-Agent: OpenAI/Python ...,\n # X-Stainless-*) when a custom provider sits behind a gateway/WAF that\n # rejects them — e.g. an upstream that returns \"502 Upstream access\n # forbidden\" for the SDK default User-Agent but accepts a plain one.\n # Applies on the OpenAI wire only (not native Anthropic / Bedrock).\n #\n # default_headers:\n # User-Agent: \"curl/8.7.1\"\n #\n # extra_headers: accepted as an alias of default_headers (merged, with\n # extra_headers winning when both are set) — matches the per-provider\n # extra_headers key below.\n #\n # Per-provider variant: named providers / custom_providers entries accept an\n # extra_headers dict scoped to that endpoint only — for reverse proxies,\n # gateways, or custom auth (e.g. Cloudflare Access service tokens).\n # Merged onto SDK/provider defaults with the entry's values winning.\n # Header values are treated as secrets and are never logged.\n #\n # providers:\n # my-proxy:\n # base_url: \"https://llm.internal.example.com/v1\"\n # key_env: \"MY_PROXY_API_KEY\"\n # extra_headers:\n # CF-Access-Client-Id: \"xxxx.access\"\n # CF-Access-Client-Secret: \"${CF_ACCESS_SECRET}\"\n # X-Client-Name: \"hermes-agent\"\n\n# Named provider overrides (optional)\n# Use this for per-provider request timeouts, non-stream stale timeouts,\n# and per-model exceptions.\n# Applies to the primary turn client on every api_mode (OpenAI-wire, native\n# Anthropic, and Anthropic-compatible providers), the fallback chain, and\n# client rebuilds during credential rotation. For OpenAI-wire chat\n# completions (streaming and non-streaming) the configured value is also\n# used as the per-request ``timeout=`` kwarg so it wins over the legacy\n# HERMES_API_TIMEOUT env var (which still applies when no config is set).\n# ``stale_timeout_seconds`` controls the non-streaming stale-call detector and\n# wins over the legacy HERMES_API_CALL_STALE_TIMEOUT env var. Leaving these\n# unset keeps the legacy defaults (HERMES_API_TIMEOUT=1800s,\n# HERMES_API_CALL_STALE_TIMEOUT=90s, native Anthropic 900s). The\n# implicit non-stream stale detector is auto-disabled for local endpoints\n# and can scale upward for very large contexts.\n#\n# Not currently wired for AWS Bedrock (bedrock_converse + AnthropicBedrock\n# SDK paths) — those use boto3 with its own timeout configuration.\n#\n# providers:\n# ollama-local:\n# request_timeout_seconds: 300 # Longer timeout for local cold-starts\n# stale_timeout_seconds: 900 # Explicitly re-enable stale detection on local endpoints\n# anthropic:\n# request_timeout_seconds: 30 # Fast-fail cloud requests\n# models:\n# claude-opus-4.6:\n# timeout_seconds: 600 # Longer timeout for extended-thinking Opus calls\n# openai-codex:\n# models:\n# gpt-5.4:\n# stale_timeout_seconds: 1800 # Longer non-stream stale timeout for slow large-context turns\n\n# =============================================================================\n# OpenRouter Provider Routing (only applies when using OpenRouter)\n# =============================================================================\n# Control how requests are routed across providers on OpenRouter.\n# See: https://openrouter.ai/docs/guides/routing/provider-selection\n#\n# provider_routing:\n# # Sort strategy: \"price\" (default), \"throughput\", or \"latency\"\n# # Append :nitro to model name for a shortcut to throughput sorting.\n# sort: \"throughput\"\n#\n# # Only allow these providers (provider slugs from OpenRouter)\n# # only: [\"anthropic\", \"google\"]\n#\n# # Skip these providers entirely\n# # ignore: [\"deepinfra\", \"fireworks\"]\n#\n# # Try providers in this order (overrides default load balancing)\n# # order: [\"anthropic\", \"google\", \"together\"]\n#\n# # Require providers to support all parameters in your request\n# # require_parameters: true\n#\n# # Data policy: \"allow\" (default) or \"deny\" to exclude providers that may store data\n# # data_collection: \"deny\"\n\n# =============================================================================\n# OpenRouter Response Caching (only applies when using OpenRouter)\n# =============================================================================\n# Cache identical API responses at the OpenRouter edge for free instant replays.\n# When enabled, identical requests (same model, messages, parameters) return\n# cached responses with zero billing. Separate from Anthropic prompt caching.\n# See: https://openrouter.ai/docs/guides/features/response-caching\n#\n# openrouter:\n# response_cache: true # Enable response caching (default: true)\n# response_cache_ttl: 300 # Cache TTL in seconds, 1-86400 (default: 300)\n\n# =============================================================================\n# Git Worktree Isolation\n# =============================================================================\n# When enabled, each CLI session creates an isolated git worktree so multiple\n# agents can work on the same repo concurrently without file collisions.\n# Equivalent to always passing --worktree / -w on the command line.\n#\n# worktree: true # Always create a worktree when in a git repo\n# worktree: false # Default — only create when -w flag is passed\n#\n# By default a new worktree branches from the freshly-fetched remote tip\n# (the current branch's upstream, else the remote's default branch) so it\n# starts current with the project instead of from the local clone's\n# (possibly stale) HEAD. Set worktree_sync: false to branch from local HEAD\n# instead — useful when offline or when you deliberately want the clone's\n# exact current state as the base.\n#\n# worktree_sync: true # Default — branch from the fetched remote tip\n# worktree_sync: false # Branch from local HEAD (offline / pinned base)\n\n# =============================================================================\n# Terminal Tool Configuration\n# =============================================================================\n# Choose ONE of the following terminal configurations by uncommenting it.\n# The terminal tool executes commands in the specified environment.\n\n# -----------------------------------------------------------------------------\n# OPTION 1: Local execution (default)\n# Commands run directly on your machine in the current directory\n# -----------------------------------------------------------------------------\n# Working directory behavior:\n# - CLI (`hermes` command): Uses \".\" (current directory where you run hermes)\n# - Gateway/messaging/cron: Uses terminal.cwd here; legacy .env cwd values are deprecated\nterminal:\n backend: \"local\"\n cwd: \".\" # For local backend: \".\" = current directory. Ignored for remote backends unless a backend documents otherwise.\n timeout: 180\n # HOME policy for tool subprocesses:\n # auto - default: host uses your real HOME; containers use HERMES_HOME/home\n # real - force your real OS-user HOME\n # profile - force HERMES_HOME/home for strict per-profile CLI config isolation\n home_mode: \"auto\"\n docker_mount_cwd_to_workspace: false # SECURITY: off by default. Opt in to mount the launch cwd into Docker /workspace.\n lifetime_seconds: 300\n # sudo_password: \"hunter2\" # Optional: pipe a sudo password via sudo -S. SECURITY WARNING: plaintext.\n # sudo_password: \"\" # Explicit empty password: try empty and never open the interactive sudo prompt.\n\n# -----------------------------------------------------------------------------\n# OPTION 2: SSH remote execution\n# Commands run on a remote server - agent code stays local (sandboxed)\n# Great for: keeping agent isolated from its own code, using powerful remote hardware\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"ssh\"\n# cwd: \"/home/myuser/project\" # Path on the REMOTE server\n# timeout: 180\n# lifetime_seconds: 300\n# ssh_host: \"my-server.example.com\"\n# ssh_user: \"myuser\"\n# ssh_port: 22\n# ssh_key: \"~/.ssh/id_rsa\" # Optional - uses ssh-agent if not specified\n\n# -----------------------------------------------------------------------------\n# OPTION 3: Docker container\n# Commands run in an isolated Docker container\n# Great for: reproducible environments, testing, isolation\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"docker\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /)\n# timeout: 180\n# lifetime_seconds: 300\n# docker_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# docker_mount_cwd_to_workspace: true # Explicit opt-in: mount your launch cwd into /workspace\n# # Optional: run the container as your host user's uid:gid so files written\n# # into bind-mounted dirs are owned by you, not root. Drops SETUID/SETGID\n# # caps too since no gosu privilege drop is needed. Leave off if your\n# # chosen docker_image expects to start as root.\n# docker_run_as_host_user: true\n# # Optional: explicitly forward selected env vars into Docker.\n# # These values come from your current shell first, then ~/.hermes/.env.\n# # Warning: anything forwarded here is visible to commands run in the container.\n# docker_forward_env:\n# - \"GITHUB_TOKEN\"\n# - \"NPM_TOKEN\"\n# # Optional: extra flags passed verbatim to docker run (appended after security defaults).\n# # Useful for adding capabilities (e.g. apt installs needing SETUID) or custom options.\n# # Example: add a Linux capability not included by default\n# # docker_extra_args:\n# # - \"--cap-add\"\n# # - \"SETUID\"\n\n# -----------------------------------------------------------------------------\n# OPTION 4: Singularity/Apptainer container\n# Commands run in a Singularity container (common in HPC environments)\n# Great for: HPC clusters, shared compute environments\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"singularity\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# singularity_image: \"docker://nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 5: Modal cloud execution\n# Commands run on Modal's cloud infrastructure\n# Great for: GPU access, scalable compute, serverless execution\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"modal\"\n# cwd: \"/workspace\" # Path INSIDE the sandbox (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# modal_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 6: Daytona cloud execution\n# Commands run in Daytona cloud sandboxes\n# Great for: Cloud dev environments, persistent workspaces, team collaboration\n# Requires: pip install daytona, DAYTONA_API_KEY env var\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"daytona\"\n# cwd: \"~\"\n# timeout: 180\n# lifetime_seconds: 300\n# daytona_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# container_disk: 10240 # Daytona max is 10GB per sandbox\n\n#\n# --- Container resource limits (docker, singularity, modal, daytona -- ignored for local/ssh) ---\n# These settings apply to all container backends. They control the resources\n# allocated to the sandbox and whether its filesystem persists across sessions.\n container_cpu: 1 # CPU cores\n container_memory: 5120 # Memory in MB (5120 = 5GB)\n container_disk: 51200 # Disk in MB (51200 = 50GB)\n container_persistent: true # Persist filesystem across sessions (false = ephemeral)\n\n# -----------------------------------------------------------------------------\n# SUDO SUPPORT (works with ALL backends above)\n# -----------------------------------------------------------------------------\n# Add sudo_password to any terminal config above to enable sudo commands.\n# The password is piped via `sudo -S`. Works with local, ssh, docker, etc.\n#\n# SECURITY WARNING: Password stored in plaintext!\n#\n# INTERACTIVE PROMPT: If sudo_password is unset and the CLI is running,\n# you'll be prompted to enter your password when sudo is needed:\n# - 45-second timeout (auto-skips if no input)\n# - Press Enter to skip (command fails gracefully)\n# - Password is hidden while typing\n# - Password is cached for the session\n#\n# EMPTY PASSWORDS: Setting sudo_password to an explicit empty string is different\n# from leaving it unset. Hermes will try an empty password via `sudo -S` and\n# will not open the interactive prompt. This is useful for passwordless sudo,\n# Touch ID sudo setups, and environments where prompting is just noise.\n#\n# ALTERNATIVES:\n# - SSH backend: Configure passwordless sudo on the remote server\n# - Containers: Run as root inside the container (no sudo needed)\n# - Local: Configure /etc/sudoers for specific commands\n#\n# Example (add to your terminal section):\n# sudo_password: \"your-password-here\"\n\n# =============================================================================\n# Security Scanning (tirith)\n# =============================================================================\n# Optional pre-exec command security scanning via tirith.\n# Detects homograph URLs, pipe-to-shell, terminal injection, env manipulation.\n# Install: brew install sheeki03/tap/tirith\n# Docs: https://github.com/sheeki03/tirith\n#\n# security:\n# tirith_enabled: true # Enable/disable tirith scanning\n# tirith_path: \"tirith\" # Path to tirith binary (supports ~ expansion)\n# tirith_timeout: 5 # Scan timeout in seconds\n# tirith_fail_open: true # Allow commands if tirith unavailable\n\n# =============================================================================\n# Browser Tool Configuration\n# =============================================================================\nbrowser:\n # Inactivity timeout in seconds - browser sessions are automatically closed\n # after this period of no activity between agent loops (default: 120 = 2 minutes)\n inactivity_timeout: 120\n\n# =============================================================================\n# Tool Loop Guardrails\n# =============================================================================\n# Soft warnings are enabled by default. They append guidance to repeated failed\n# or non-progressing tool results but still let the tool execute. Hard stops are\n# opt-in circuit breakers for autonomous/cron sessions where stopping a loop is\n# preferable to spending the full iteration budget.\ntool_loop_guardrails:\n warnings_enabled: true\n hard_stop_enabled: false\n warn_after:\n exact_failure: 2\n same_tool_failure: 3\n idempotent_no_progress: 2\n hard_stop_after:\n exact_failure: 5\n same_tool_failure: 8\n idempotent_no_progress: 5\n\n# =============================================================================\n# Context Compression (Auto-shrinks long conversations)\n# =============================================================================\n# When conversation approaches model's context limit, middle turns are\n# automatically summarized to free up space while preserving important context.\n#\n# HOW IT WORKS:\n# 1. Tracks actual token usage from API responses (not estimates)\n# 2. When prompt_tokens >= threshold% of model's context_length, triggers compression\n# 3. Protects first 3 turns (system prompt, initial request, first response)\n# 4. Protects last N turns (default 20 messages = ~10 full turns of recent context)\n# 5. Summarizes middle turns using a fast/cheap model\n# 6. Inserts summary as a user message, continues conversation seamlessly\n#\n# Post-compression tail budget is target_ratio × threshold × context_length:\n# 200K context, threshold 0.50, ratio 0.20 → 20K tokens of recent tail preserved\n# 1M context, threshold 0.50, ratio 0.20 → 100K tokens of recent tail preserved\n#\ncompression:\n # Enable automatic context compression (default: true)\n # Set to false if you prefer to manage context manually or want errors on overflow\n enabled: true\n\n # Opt-in compression progress notices on chat platforms (default: false).\n # By design, routine automatic compression is SILENT on human-facing chat\n # gateways (Telegram, Discord, Slack, ...) — it happens in the background\n # with server-side logging only. Set true to also deliver the routine\n # progress statuses (compacting started, preflight/pre-API compression,\n # idle compaction, retry progress, and the compaction-complete notice) to\n # chat platforms. Unrelated operational noise (auxiliary model failures,\n # provider retry chatter) stays suppressed either way, and compression\n # FAILURE notices + manual /compress feedback are always visible\n # regardless of this setting. (#52995)\n progress_notices: false\n \n # Trigger compression at this % of model's context limit (default: 0.50 = 50%)\n # Lower values = more aggressive compression, higher values = compress later\n # Models with context windows below 512K are floored at 0.75 (raise-only) so\n # compaction doesn't fire with half the window still free; set above 0.75 to override.\n threshold: 0.50\n\n # Per-model threshold overrides: keys are substring-matched against the model\n # name (longest match wins). Useful when some models need different compaction\n # points — e.g. a 1M-context model can compress later (0.30) while a 128K\n # model needs to compress earlier (0.60). The small-context floor (75% for\n # <512K models) still applies on top of per-model overrides.\n # model_thresholds:\n # \"glm-5.2\": 0.40\n # \"claude-sonnet\": 0.35\n # \"gpt-5\": 0.30\n\n # Optional absolute token cap for the compression trigger (default: null = disabled).\n # When set, compression fires at the LOWER of the ratio-based threshold and this\n # absolute token count — first-fires-wins. It never fires later than this count\n # regardless of which model is active (useful when switching between models with\n # very different context windows). Clamped to the model's context length at\n # apply-time, so a cap above the window is a no-op (ratio-based threshold wins).\n # Survives model switches and fallback activations.\n # threshold_tokens: 200000\n\n # Existing Codex gpt-5.5 behavior: raise Hermes' compaction trigger to 85%\n # for the ChatGPT Codex OAuth route. Set false to opt back down to threshold.\n codex_gpt55_autoraise: true\n\n # Fraction of the threshold to preserve as recent tail (default: 0.20 = 20%)\n # e.g. 20% of 50% threshold = 10% of total context kept as recent messages.\n # Summary output is separately capped at 12K tokens (Gemini output limit).\n # Range: 0.10 - 0.80\n target_ratio: 0.20\n\n # Number of most-recent messages to always preserve (default: 20 ≈ 10 full turns)\n # Higher values keep more recent conversation intact at the cost of more aggressive\n # compression of older turns.\n protect_last_n: 20\n\n # Minimum number of REAL (actionable) user messages guaranteed to survive in\n # the uncompressed tail (default: 1 = the existing single last-user anchor,\n # behavior-preserving). Raise to e.g. 3 to keep the last 3 real user turns\n # verbatim even when bulky tool outputs fill the tail token budget — blank\n # platform echoes, compaction handoffs, and synthetic continuation rows never\n # count toward N. The tail can exceed the token budget when this pulls the\n # cut back; the guarantee wins over the budget by design.\n min_tail_user_messages: 1\n\n # Compression retry rounds before a turn gives up with \"max compression\n # attempts reached\" (default: 3, same as the previous hardcoded value).\n # Raise (e.g. 6) for tool-schema-heavy sessions where 3 rounds cannot bring\n # the request estimate under the threshold. Validated >= 1, hard cap 10.\n max_attempts: 3\n\n # Codex app-server (codex CLI runtime) thread-compaction mode. The codex\n # agent owns the real thread context on this runtime, so Hermes' summarizer\n # cannot shrink it — compaction goes through the app server instead.\n # native = let Codex decide when to compact its own thread (default)\n # hermes = let Hermes threshold trigger Codex thread/compact/start\n # off = Hermes will not auto-trigger compaction; Codex may still compact natively\n codex_app_server_auto: native\n\n # Number of non-system messages to protect at the head of the transcript, in\n # ADDITION to the system prompt (which is always implicitly protected).\n # Head messages are NEVER summarized — they survive every compression\n # indefinitely. This gives stable early context for short/medium sessions,\n # but in long-running sessions that rely on rolling compaction the pinned\n # opening turns may not match how you want the session framed over time.\n # Set to 0 to preserve ONLY the system prompt (plus the rolling summary\n # and recent tail) — the cleanest configuration for long-running sessions.\n # Default 3 preserves the system prompt plus the first three non-system\n # head messages, matching the pre-feature behaviour.\n protect_first_n: 3\n\n # Idle compaction (default: 0 = disabled). When > 0, a session that resumes\n # after at least this many seconds of inactivity compacts its accumulated\n # history up front, before the first reply, so a long-lived thread you come\n # back to later doesn't re-read its full stale context on every turn.\n # Time-based, so it complements (does not replace) the size-based `threshold`\n # above. It is skipped when the context is already small (at or below the\n # post-compression target = threshold × target_ratio), so it never wastes a\n # summarization on a short idle thread. Example: 1800 = compact after 30 min idle.\n idle_compact_after_seconds: 0\n\n # Proactive tool-result prune (default: 0 = disabled). Opt-in token trigger\n # for a deterministic, no-LLM prune of OLD tool-result payloads, run\n # independently of `threshold` above. On large-window models (512K/1M) the\n # ratio threshold rarely fires, so bulky tool outputs (terminal dumps, file\n # reads, web extracts) ride along in history and get re-billed every turn.\n # When re-sent history exceeds this many tokens, the prune dedupes identical\n # results, summarizes older oversized ones, and truncates large tool-call\n # arguments — protecting the most recent `protect_last_n` messages and never\n # calling the model. Try 48000 to enable. Built-in compressor engine only;\n # other context engines inherit a safe no-op.\n # NOTE: a committed prune rewrites already-sent history, which invalidates\n # the provider's prompt-cache prefix — the min_reclaim gate below keeps\n # those cache breaks episodic (like a compression boundary) instead of\n # per-turn.\n proactive_prune_tokens: 0\n\n # The prune's summarize pass only touches tool results larger than this many\n # characters (clamped to >= 200 so a generated summary can't be\n # re-summarized). Default 8000.\n proactive_prune_min_result_chars: 8000\n\n # A proactive prune only COMMITS when it reclaims at least this many tokens\n # (measured on the pruned output). This is the prompt-cache hysteresis gate:\n # one meaningful, amortized cache break per batch of stale tool output\n # instead of a tiny break on every tool iteration. 0 = commit any non-zero\n # prune. Default 4096.\n proactive_prune_min_reclaim_tokens: 4096\n\n # To pin a specific model/provider for compression summaries, use the\n # auxiliary section below (auxiliary.compression.provider / model).\n\n# =============================================================================\n# Anthropic prompt caching TTL\n# =============================================================================\n# When prompt caching is active (Claude via OpenRouter or native Anthropic),\n# Anthropic supports two TTL tiers for cached prefixes: \"5m\" (default) and\n# \"1h\". Other values are ignored and \"5m\" is used.\n#\nprompt_caching:\n cache_ttl: \"5m\" # use \"1h\" for long sessions with pauses between turns\n\n# =============================================================================\n# Auxiliary Models (Advanced — Experimental)\n# =============================================================================\n# Hermes uses lightweight \"auxiliary\" models for side tasks: image analysis,\n# browser screenshot analysis, web page summarization, TTS audio-tag insertion,\n# and context compression.\n#\n# By default these use Gemini Flash via OpenRouter or Nous Portal and are\n# auto-detected from your credentials. You do NOT need to change anything\n# here for normal usage.\n#\n# WARNING: Overriding these with providers other than OpenRouter or Nous Portal\n# is EXPERIMENTAL and may not work. Not all models/providers support vision,\n# produce usable summaries, or accept the same API format. Change at your own\n# risk — if things break, reset to \"auto\" / empty values.\n#\n# Each task has its own provider + model pair so you can mix providers.\n# For example: OpenRouter for vision (needs multimodal), but your main\n# local endpoint for compression (just needs text).\n#\n# Provider options:\n# \"auto\" - Best available: OpenRouter → Nous Portal → main endpoint (default)\n# \"openrouter\" - Force OpenRouter (requires OPENROUTER_API_KEY)\n# \"nous\" - Force Nous Portal (requires: hermes auth add nous)\n# \"gemini\" - Force Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n# \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY)\n# \"codex\" - Force Codex OAuth (requires: hermes model → Codex).\n# Uses gpt-5.3-codex which supports vision.\n# \"main\" - Use your custom endpoint (OPENAI_BASE_URL + OPENAI_API_KEY).\n# Works with OpenAI API, local models, or any OpenAI-compatible\n# endpoint. Also falls back to Codex OAuth and API-key providers.\n#\n# Model: leave empty to use the provider's default. When empty, OpenRouter\n# uses \"google/gemini-3-flash-preview\" and Nous uses \"gemini-3-flash\".\n# Other providers pick a sensible default automatically.\n#\n# auxiliary:\n# # Image analysis: vision_analyze tool + browser screenshots\n# vision:\n# provider: \"auto\"\n# model: \"\" # e.g. \"google/gemini-2.5-flash\", \"openai/gpt-4o\"\n# timeout: 30 # LLM API call timeout (seconds)\n# download_timeout: 30 # Image HTTP download timeout (seconds)\n# # Increase for slow connections or self-hosted image servers\n# reasoning_effort: \"\" # Per-task thinking level: none, minimal, low, medium,\n# # high, xhigh, max, ultra. Empty = provider default.\n# # Works on every auxiliary task block (vision,\n# # web_extract, compression, title_generation, curator,\n# # background_review, moa_reference, ...). Example: run\n# # compression at \"low\" and vision at \"none\" to cut\n# # side-task latency/cost on reasoning models.\n#\n# # Web page scraping / summarization + browser page text extraction\n# web_extract:\n# provider: \"auto\"\n# model: \"\"\n# reasoning_effort: \"low\"\n#\n# # Gemini 3.1 TTS hidden audio-tag insertion\n# tts_audio_tags:\n# provider: \"auto\" # empty model = your main chat model\n# model: \"\"\n# timeout: 30\n#\n# # Automatic session title generation after the first exchange\n# title_generation:\n# enabled: true # set false to disable auto-title generation\n# provider: \"auto\"\n# model: \"\"\n# timeout: 30\n# language: \"\" # empty = match the user's language; or e.g. \"English\"\n#\n# # Session search — summarizes matching past sessions\n# session_search:\n# provider: \"auto\"\n# model: \"\"\n# timeout: 30\n# max_concurrency: 3 # Limit parallel summaries to reduce request-burst 429s\n# extra_body: {} # Provider-specific OpenAI-compatible request fields\n# # Example for providers that support request-body\n# # reasoning controls:\n# # extra_body:\n# # enable_thinking: false\n# # Some vLLM/Qwen deployments expect this nested:\n# # extra_body:\n# # chat_template_kwargs:\n# # enable_thinking: false\n\n# =============================================================================\n# Persistent Memory\n# =============================================================================\n# Bounded curated memory injected into the system prompt every session.\n# Two stores: MEMORY.md (agent's notes) and USER.md (user profile).\n# Character limits keep the memory small and focused. The agent manages\n# pruning -- when at the limit, it must consolidate or replace entries.\n# Disabled by default in batch_runner.\n#\nmemory:\n # Agent's personal notes: environment facts, conventions, things learned\n memory_enabled: true\n \n # User profile: preferences, communication style, expectations\n user_profile_enabled: true\n \n # Character limits (~2.75 chars per token, model-independent)\n memory_char_limit: 2200 # ~800 tokens\n user_char_limit: 1375 # ~500 tokens\n\n # Periodic memory nudge: remind the agent to consider saving memories\n # every N user turns. Set to 0 to disable. Only active when memory is enabled.\n nudge_interval: 10 # Nudge every 10 user turns (0 = disabled)\n\n # Memory flush: give the agent one turn to save memories before context is\n # lost (compression, /new, /reset, exit). Set to 0 to disable.\n # For exit/reset, only fires if the session had at least this many user turns.\n flush_min_turns: 6 # Min user turns to trigger flush on exit/reset (0 = disabled)\n\n# =============================================================================\n# Session Reset Policy (Messaging Platforms)\n# =============================================================================\n# Controls when messaging sessions (Telegram, Discord, WhatsApp, Slack) are\n# automatically cleared. Default is \"none\": sessions never auto-reset —\n# conversation context lives until you /reset or /new manually, or context\n# compression kicks in. Opt in to automatic resets if you prefer sessions to\n# clear on a schedule (long-lived context increases API cost per message,\n# though prompt caching and compression keep this manageable).\n#\n# When an automatic reset triggers, the agent first saves important\n# information to its persistent memory — but the conversation context is\n# wiped. The agent starts fresh but retains learned facts via its memory\n# system.\n#\n# Modes:\n# \"none\" - Never auto-reset (default); context lives until /reset or compression\n# \"idle\" - Reset after N minutes of inactivity\n# \"daily\" - Reset at a fixed hour each day\n# \"both\" - Reset on EITHER inactivity timeout or daily boundary\n#\nsession_reset:\n mode: none # \"none\", \"idle\", \"daily\", or \"both\"\n idle_minutes: 1440 # Inactivity timeout in minutes (used by \"idle\"/\"both\")\n at_hour: 4 # Daily reset hour, 0-23 local time (used by \"daily\"/\"both\")\n\n# Maximum number of simultaneously active chat sessions across CLI, TUI,\n# dashboard chat, and messaging gateway. Set to null, 0, or omit to allow\n# unlimited concurrent sessions. When the limit is reached, new sessions get a\n# clean error while existing active sessions keep their normal behavior. This\n# top-level key takes precedence over gateway.max_concurrent_sessions. The cap\n# is a best-effort single-host/profile runtime guard; Hermes fails open if the\n# local runtime lease registry cannot be read or locked.\nmax_concurrent_sessions: null\n\n# When true, group/channel chats use one session per participant when the platform\n# provides a user ID. This is the secure default and prevents users in the same\n# room from sharing context, interrupts, and token costs. Set false only if you\n# explicitly want one shared \"room brain\" per group/channel.\ngroup_sessions_per_user: true\n\n# ─────────────────────────────────────────────────────────────────────────────\n# API Server — per-client model routing\n# ─────────────────────────────────────────────────────────────────────────────\n# Route different API clients to different models/providers on a single\n# Hermes deployment. Clients choose a backend by sending a specific string\n# as the OpenAI ``model`` field. Unmapped model values fall back to the\n# global model configured in the ``model:`` section above, and an explicit\n# session /model override always wins over a route.\n#\n# Configure via the ``platforms.api_server.extra.model_routes`` gateway\n# config block:\n#\n# platforms:\n# api_server:\n# enabled: true\n# extra:\n# key: \"your-api-server-secret\"\n# model_routes:\n# # Xiaozhi clients send model=\"minimax-m2\" → routed to MiniMax via OpenRouter\n# minimax-m2:\n# model: \"minimax/minimax-m1\"\n# provider: \"openrouter\" # optional — overrides global provider\n# # api_key: \"sk-...\" # optional — per-route UPSTREAM provider\n# # key (NOT caller auth; never logged)\n# # base_url: \"https://...\" # optional — per-route base URL\n# # GPT clients keep their own alias\n# gpt-5:\n# model: \"openai/gpt-5\"\n# provider: \"openrouter\"\n#\n# Configured aliases are automatically listed by GET /v1/models so clients\n# can discover them without manual coordination. Caller authentication is\n# unchanged: every request still authenticates with the global API server\n# key (``extra.key`` / API_SERVER_KEY).\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Gateway Streaming\n# ─────────────────────────────────────────────────────────────────────────────\n# Stream tokens to messaging platforms in real-time. The bot sends a message\n# on first token, then progressively edits it as more tokens arrive.\n# Disabled by default — enable to try the streaming UX on Telegram/Discord/Slack.\n# For Telegram, partial edits are sent as plain text and only the final edit uses MarkdownV2.\nstreaming:\n enabled: false\n # transport: edit # \"edit\" = progressive editMessageText\n # edit_interval: 0.3 # seconds between message edits\n # buffer_threshold: 40 # chars before forcing an edit flush\n # cursor: \" ▉\" # cursor shown during streaming\n\n# =============================================================================\n# Skills Configuration\n# =============================================================================\n# Skills are reusable procedures the agent can load and follow. The agent can\n# also create new skills after completing complex tasks.\n#\nskills:\n # Nudge the agent to create skills after complex tasks.\n # Every N tool-calling iterations, remind the model to consider saving a skill.\n # Set to 0 to disable.\n creation_nudge_interval: 15\n\n # External skill directories — share skills across tools/agents without\n # copying them into ~/.hermes/skills/. Each path is expanded (~ and ${VAR})\n # and resolved to an absolute path. External dirs are read-only: skill\n # creation always writes to ~/.hermes/skills/. Local skills take precedence\n # when names collide.\n # external_dirs:\n # - ~/.agents/skills\n # - /home/shared/team-skills\n\n# =============================================================================\n# Agent Behavior\n# =============================================================================\nagent:\n # Maximum tool-calling iterations per conversation (default: 500)\n # Higher = more room for complex tasks, but costs more tokens\n # Recommended: 20-30 for focused tasks, 50-100 for open exploration\n max_turns: 500\n\n # Inactivity timeout for gateway agent runs (seconds, 0 = unlimited).\n # The agent can run indefinitely when actively calling tools or receiving\n # API responses. Only fires after the agent has been idle for this duration.\n # gateway_timeout: 1800\n\n # Staged warning: send a warning before escalating to full timeout.\n # Fires once per run when inactivity reaches this threshold (seconds).\n # Set to 0 to disable the warning.\n # gateway_timeout_warning: 900\n\n # Graceful drain timeout for gateway stop/restart (seconds).\n # Default 0 = no drain: a restart interrupts in-flight agents immediately,\n # cleans up, and exits. Set a positive value only if you want a grace\n # window on /restart, and keep it well under systemd's TimeoutStopSec.\n # restart_drain_timeout: 0\n\n # Upper bound (seconds) a submitted prompt waits for the deferred agent\n # build (MCP discovery, model metadata, skills scan) before failing with a\n # visible error. The wait is patient — the message is delivered as soon as\n # the build completes, and a progress notice is shown past 30s — so this cap\n # only fires on a genuinely hung build. Raise it for deployments with many\n # slow or unreachable MCP servers. Default 600.\n # build_wait_timeout: 600\n\n # Max app-level retry attempts for API errors (connection drops, provider\n # timeouts, 5xx, etc.) before the agent surfaces the failure. Lower this\n # to 1 if you use fallback providers and want fast failover on flaky\n # primaries (default 3). The OpenAI SDK does its own low-level retries\n # underneath this wrapper — this is the Hermes-level loop.\n # api_max_retries: 3\n\n # After the agent edits code without fresh passing verification, nudge it to\n # verify before finishing. The default \"auto\" enables it on interactive\n # coding surfaces (CLI, TUI, desktop) and programmatic callers, and disables\n # it on conversational messaging surfaces (Telegram, Discord, etc.) where the\n # verification summary would reach a human as chat noise. Set true or false to\n # force it on or off; the HERMES_VERIFY_ON_STOP env var (1/0) takes precedence.\n # verify_on_stop: auto\n\n # Standing operator instructions for the coding posture (when Hermes is in a\n # code workspace). Appended to the coding brief as an extra system block, so\n # you can pin project-wide workflow rules without editing the shipped brief.\n # Accepts a string or a list of strings. Takes effect next session.\n # coding_instructions:\n # - \"For UI work, don't run tsc/lint until I approve the look.\"\n # - \"Clean the diff before you commit and push.\"\n\n # When verify-on-stop finds edited code without fresh verification evidence,\n # append guidance for creative UI work (avoid broad tsc/lint/test before visual\n # approval) and clean-diff expectations. Set false to keep that nudge terse.\n # verify_guidance: true\n\n # A `pre_verify` hook (plugin or shell, see Event Hooks docs) can keep the\n # agent going one more turn to verify/clean before finishing. This caps how\n # many times one turn may be nudged to continue, so a hook can't trap the loop.\n # Default 3.\n # max_verify_nudges: 3\n\n # Enable verbose logging\n verbose: false\n \n # Reasoning effort level (OpenRouter and Nous Portal)\n # Controls how much \"thinking\" the model does before responding.\n # Options: \"xhigh\" (max), \"high\", \"medium\", \"low\", \"minimal\", \"none\" (disable)\n reasoning_effort: \"medium\"\n \n # Per-model reasoning effort overrides (optional dict)\n # Key: any sensible model spelling works (exact, dots↔dashes interchangeable,\n # provider prefix optional). First match wins.\n # Value: reasoning effort level (same options as reasoning_effort)\n # Override the global reasoning_effort for that specific model.\n # NOTE: no `hermes config set` support for this key -- edit YAML directly.\n # reasoning_overrides:\n # \"openrouter/anthropic/claude-opus-4.5\": \"xhigh\"\n # \"openai/gpt-5\": \"low\"\n # \"claude-opus-4.6\": \"high\" # bare model name also works\n # \"deepseek/deepseek-v4-pro\": \"xhigh\" # dots and dashes are interchangeable\n reasoning_overrides: {}\n \n # Predefined personalities (use with /personality command)\n personalities:\n helpful: \"You are a helpful, friendly AI assistant.\"\n concise: \"You are a concise assistant. Keep responses brief and to the point.\"\n technical: \"You are a technical expert. Provide detailed, accurate technical information.\"\n creative: \"You are a creative assistant. Think outside the box and offer innovative solutions.\"\n teacher: \"You are a patient teacher. Explain concepts clearly with examples.\"\n kawaii: \"You are a kawaii assistant! Use cute expressions like (◕‿◕), ★, ♪, and ~! Add sparkles and be super enthusiastic about everything! Every response should feel warm and adorable desu~! ヽ(>∀<☆)ノ\"\n catgirl: \"You are Neko-chan, an anime catgirl AI assistant, nya~! Add 'nya' and cat-like expressions to your speech. Use kaomoji like (=^・ω・^=) and ฅ^•ﻌ•^ฅ. Be playful and curious like a cat, nya~!\"\n pirate: \"Arrr! Ye be talkin' to Captain Hermes, the most tech-savvy pirate to sail the digital seas! Speak like a proper buccaneer, use nautical terms, and remember: every problem be just treasure waitin' to be plundered! Yo ho ho!\"\n shakespeare: \"Hark! Thou speakest with an assistant most versed in the bardic arts. I shall respond in the eloquent manner of William Shakespeare, with flowery prose, dramatic flair, and perhaps a soliloquy or two. What light through yonder terminal breaks?\"\n surfer: \"Duuude! You're chatting with the chillest AI on the web, bro! Everything's gonna be totally rad. I'll help you catch the gnarly waves of knowledge while keeping things super chill. Cowabunga! 🤙\"\n noir: \"The rain hammered against the terminal like regrets on a guilty conscience. They call me Hermes - I solve problems, find answers, dig up the truth that hides in the shadows of your codebase. In this city of silicon and secrets, everyone's got something to hide. What's your story, pal?\"\n uwu: \"hewwo! i'm your fwiendwy assistant uwu~ i wiww twy my best to hewp you! *nuzzles your code* OwO what's this? wet me take a wook! i pwomise to be vewy hewpful >w<\"\n philosopher: \"Greetings, seeker of wisdom. I am an assistant who contemplates the deeper meaning behind every query. Let us examine not just the 'how' but the 'why' of your questions. Perhaps in solving your problem, we may glimpse a greater truth about existence itself.\"\n hype: \"YOOO LET'S GOOOO!!! 🔥🔥🔥 I am SO PUMPED to help you today! Every question is AMAZING and we're gonna CRUSH IT together! This is gonna be LEGENDARY! ARE YOU READY?! LET'S DO THIS! 💪😤🚀\"\n\n# =============================================================================\n# Toolsets\n# =============================================================================\n# Control which tools the agent has access to.\n# Use `hermes tools` to interactively enable/disable tools per platform.\n\n# =============================================================================\n# Platform Toolsets (per-platform tool configuration)\n# =============================================================================\n# Override which toolsets are available on each platform.\n# If a platform isn't listed here, its built-in default is used.\n#\n# You can use EITHER:\n# - A preset like \"hermes-cli\" or \"hermes-telegram\" (curated tool set)\n# - A list of individual toolsets to compose your own (see list below)\n#\n# Supported platform keys: cli, telegram, discord, whatsapp, slack, qqbot, teams, google_chat\n#\n# Examples:\n#\n# # Use presets (same as defaults):\n# platform_toolsets:\n# cli: [hermes-cli]\n# telegram: [hermes-telegram]\n#\n# # Custom: give Telegram only web + terminal + file + planning:\n# platform_toolsets:\n# telegram: [web, terminal, file, todo]\n#\n# # Custom: CLI without browser or image gen:\n# platform_toolsets:\n# cli: [web, terminal, file, skills, todo, tts, cronjob]\n#\n# # Restrictive: Discord gets read-only tools only:\n# platform_toolsets:\n# discord: [web, vision, skills, todo]\n#\n# If not set, defaults are:\n# cli: hermes-cli (everything + cronjob management)\n# telegram: hermes-telegram (terminal, file, web, vision, image, tts, browser, skills, todo, cronjob, messaging)\n# discord: hermes-discord (same as telegram)\n# whatsapp: hermes-whatsapp (same as telegram)\n# slack: hermes-slack (same as telegram)\n# signal: hermes-signal (same as telegram)\n# homeassistant: hermes-homeassistant (same as telegram)\n# qqbot: hermes-qqbot (same as telegram)\n# teams: hermes-teams (same as telegram)\n# google_chat: hermes-google_chat (same as telegram)\n#\nplatform_toolsets:\n cli: [hermes-cli]\n telegram: [hermes-telegram]\n discord: [hermes-discord]\n whatsapp: [hermes-whatsapp]\n slack: [hermes-slack]\n signal: [hermes-signal]\n homeassistant: [hermes-homeassistant]\n qqbot: [hermes-qqbot]\n yuanbao: [hermes-yuanbao]\n teams: [hermes-teams]\n google_chat: [hermes-google_chat]\n\n# =============================================================================\n# Gateway Platform Settings\n# =============================================================================\n# Optional per-platform messaging settings.\n# Platform-specific knobs live under `extra`.\n#\n# platforms:\n# telegram:\n# reply_to_mode: \"first\" # off | first | all\n# # guest_mode lets explicit @mentions from non-allowlisted groups through.\n# # Default false; ordinary messages, replies, and regex wake words stay blocked.\n# guest_mode: false\n# # allowed_chats: [\"-1001234567890\"]\n# extra:\n# disable_link_previews: false # Set true to suppress Telegram URL previews in bot messages\n# 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\n# rich_drafts: false # Experimental rich draft previews during Telegram DM streaming; default false because Telegram Desktop/macOS can visually overlay draft frames\n# command_menu:\n# # Telegram allows up to 100 BotCommands; Hermes defaults to 60 so\n# # all built-in commands plus common skill commands stay visible\n# # while remaining under Telegram's payload-size limit. Clamped 1..100.\n# max_commands: 60\n# # prepend = user priority first, then Hermes defaults\n# # append = Hermes defaults first, then user priority\n# # replace = only the list below defines priority\n# priority_mode: prepend\n# priority:\n# - my_plugin_command\n# webhook:\n# extra:\n# # Route scripts default to a 30 second timeout. Scripts must live under\n# # the active profile's scripts directory and receive webhook JSON on stdin.\n# script_timeout_seconds: 30\n#\n# Discord-specific settings (config.yaml top-level, not under platforms:):\n#\n# discord:\n# require_mention: true # Require @mention in server channels (default: true)\n# auto_thread: true # Auto-create thread on @mention (default: true)\n# free_response_channels: \"\" # Channel IDs where no mention is needed\n# reactions: true # Show processing reactions (default: true)\n# history_backfill: true # Recover missed channel messages on mention (default: true)\n# history_backfill_limit: 50 # Max messages to scan backwards (default: 50)\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Available toolsets (use these names in platform_toolsets or the toolsets list)\n#\n# Run `hermes chat --list-toolsets` to see all toolsets and their tools.\n# Run `hermes chat --list-tools` to see every individual tool with descriptions.\n# ─────────────────────────────────────────────────────────────────────────────\n#\n# INDIVIDUAL TOOLSETS (compose your own):\n# web - web_search, web_extract\n# search - web_search only (no scraping)\n# terminal - terminal, process\n# file - read_file, write_file, patch, search\n# browser - browser_navigate, browser_snapshot, browser_click, browser_type,\n# browser_scroll, browser_back, browser_press,\n# browser_get_images, browser_vision (requires BROWSERBASE_API_KEY)\n# vision - vision_analyze (requires OPENROUTER_API_KEY)\n# image_gen - image_generate (requires FAL_KEY)\n# skills - skills_list, skill_view\n# skills_hub - skill_hub (search/install/manage from online registries — user-driven only)\n# todo - todo (in-memory task planning, no deps)\n# tts - text_to_speech (Edge TTS free, or ELEVENLABS/OPENAI/MINIMAX/MISTRAL key)\n# cronjob - cronjob (create/list/update/pause/resume/run/remove scheduled tasks)\n#\n# PRESETS (curated bundles):\n# hermes-cli - All of the above except send_message\n# hermes-telegram - terminal, file, web, vision, image_gen, tts, browser,\n# skills, todo, cronjob, send_message\n# hermes-discord - Same as hermes-telegram\n# hermes-whatsapp - Same as hermes-telegram\n# hermes-slack - Same as hermes-telegram\n#\n# COMPOSITE:\n# debugging - terminal + web + file\n# safe - web + vision (no terminal access)\n# all - Everything available\n#\n# web - Web search and content extraction (web_search, web_extract)\n# search - Web search only, no scraping (web_search)\n# terminal - Command execution and process management (terminal, process)\n# file - File operations: read, write, patch, search\n# browser - Full browser automation (navigate, click, type, screenshot, etc.)\n# vision - Image analysis (vision_analyze)\n# image_gen - Image generation with FLUX (image_generate)\n# skills - Load skill documents (skills_list, skill_view)\n# todo - Task planning and tracking for multi-step work\n# memory - Persistent memory across sessions (personal notes + user profile)\n# session_search - Search and recall past conversations (FTS5 + Gemini Flash summarization)\n# tts - Text-to-speech (Edge TTS free, ElevenLabs, OpenAI, MiniMax, Mistral)\n# cronjob - Schedule and manage automated tasks (CLI-only)\n#\n# Composite toolsets:\n# debugging - terminal + web + file (for troubleshooting)\n# safe - web + vision (no terminal access)\n\n# NOTE: The top-level \"toolsets\" key is deprecated and ignored.\n# Tool configuration is managed per-platform via platform_toolsets above.\n# Use `hermes tools` to configure interactively, or edit platform_toolsets directly.\n#\n# CLI override: hermes chat --toolsets terminal,web,file\n\n# =============================================================================\n# MCP (Model Context Protocol) Servers\n# =============================================================================\n# Connect to external MCP servers to add tools from the MCP ecosystem.\n# Each server's tools are automatically discovered and registered.\n# See website/docs/user-guide/features/mcp.md for full documentation.\n#\n# Stdio servers (spawn a subprocess):\n# command: the executable to run\n# args: command-line arguments\n# env: environment variables (only these + safe defaults passed to subprocess)\n#\n# HTTP servers (connect to a URL):\n# url: the MCP server endpoint\n# headers: HTTP headers (e.g., for authentication)\n#\n# Optional per-server settings:\n# timeout: tool call timeout in seconds (default: 120)\n# connect_timeout: initial connection timeout (default: 60)\n# keepalive_interval: liveness ping cadence in seconds (default: 180).\n# Lower it below the server's session TTL for servers that expire idle\n# sessions quickly (e.g. Unreal Engine editor MCP, ~15s), otherwise idle\n# tool calls hit an expired session and pay a slow reconnect. Floored at 5s.\n#\n# mcp_servers:\n# time:\n# command: uvx\n# args: [\"mcp-server-time\"]\n# filesystem:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-filesystem\", \"/home/user\"]\n# notion:\n# url: https://mcp.notion.com/mcp\n# github:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-github\"]\n# env:\n# GITHUB_PERSONAL_ACCESS_TOKEN: \"ghp_...\"\n#\n# Sampling (server-initiated LLM requests) — enabled by default.\n# Per-server config under the 'sampling' key:\n# analysis:\n# command: npx\n# args: [\"-y\", \"analysis-server\"]\n# sampling:\n# enabled: true # default: true\n# model: \"gemini-3-flash\" # override model (optional)\n# max_tokens_cap: 4096 # max tokens per request\n# timeout: 30 # LLM call timeout (seconds)\n# max_rpm: 10 # max requests per minute\n# allowed_models: [] # model whitelist (empty = all)\n# max_tool_rounds: 5 # tool loop limit (0 = disable)\n# log_level: \"info\" # audit verbosity\n\n# =============================================================================\n# Text-to-Speech\n# =============================================================================\n# TTS defaults to Edge TTS unless changed in ~/.hermes/config.yaml.\n# Gemini TTS supports persona/director prompt files, and Gemini 3.1 Flash TTS\n# can use a hidden auxiliary rewrite pass to insert expressive square-bracket\n# audio tags into the TTS script without showing tags in chat.\n#\n# tts:\n# provider: \"gemini\"\n# gemini:\n# model: \"gemini-3.1-flash-tts-preview\"\n# voice: \"Kore\"\n# audio_tags: false\n# persona_prompt_file: \"\" # e.g. ~/.hermes/tts/radio-host.md\n\n# =============================================================================\n# Voice Transcription (Speech-to-Text)\n# =============================================================================\n# Automatically transcribe voice messages on messaging platforms.\n# Providers: local (free, faster-whisper) | groq (free tier) | openai (Whisper API) | mistral (Voxtral Transcribe)\n# Set the corresponding API key in .env: GROQ_API_KEY, OPENAI_API_KEY, or MISTRAL_API_KEY.\nstt:\n enabled: true\n # provider: \"local\" # auto-detected if omitted\n local:\n model: \"base\" # tiny | base | small | medium | large-v3 | turbo\n # language: \"\" # auto-detect; set to \"en\", \"es\", \"fr\", etc. to force\n # initial_prompt: \"\" # Optional faster-whisper prompt, e.g. bias Chinese output to simplified Chinese\n # language: \"\" # GLOBAL language hint for every STT provider (per-provider language wins)\n # groq:\n # model: \"whisper-large-v3-turbo\"\n # language: \"\" # blank = stt.language > HERMES_LOCAL_STT_LANGUAGE > auto-detect\n openai:\n model: \"whisper-1\" # whisper-1 | gpt-4o-mini-transcribe | gpt-4o-transcribe\n language: \"\" # auto-detect; set to \"en\", \"es\", \"fr\", etc. to force\n # mistral:\n # model: \"voxtral-mini-latest\" # voxtral-mini-latest | voxtral-mini-2602\n # deepinfra:\n # # Model id is discovered live from the DeepInfra catalog filtered\n # # by the `stt` surface tag — leave `model` blank to take the first\n # # live result. Pin only when you need a specific Whisper variant.\n # model: \"\"\n\n# Text-to-speech. Only the deepinfra block is documented here — the\n# remaining providers (edge, openai, xai, minimax, mistral, gemini,\n# elevenlabs, neutts, kittentts, piper) inherit sensible defaults from\n# DEFAULT_CONFIG in hermes_cli/config.py.\n# tts:\n# provider: \"deepinfra\"\n# deepinfra:\n# # Model id is discovered live from the DeepInfra catalog filtered\n# # by the `tts` surface tag — leave `model` blank to take the first\n# # live result.\n# model: \"\"\n# voice: \"default\"\n\n# Image generation. Each provider plugin reads its own ``image_gen.<name>``\n# block; deepinfra discovers models live from\n# api.deepinfra.com/v1/openai/models filtered by the ``image-gen`` tag —\n# no model id is hardcoded, so retired models disappear automatically.\n# image_gen:\n# provider: \"deepinfra\"\n# deepinfra:\n# # Leave `model` blank for the first live `image-gen`-tagged result.\n# model: \"\"\n\n# =============================================================================\n# Response Pacing (Messaging Platforms)\n# =============================================================================\n# Add human-like delays between message chunks.\n# human_delay:\n# mode: \"off\" # \"off\" | \"natural\" | \"custom\"\n# min_ms: 800 # Min delay (custom mode only)\n# max_ms: 2500 # Max delay (custom mode only)\n\n# =============================================================================\n# Session Logging\n# =============================================================================\n# Session trajectories are automatically saved to logs/ directory.\n# Each session creates: logs/session_YYYYMMDD_HHMMSS_UUID.json\n#\n# The session ID is displayed in the welcome banner for easy reference.\n# Logs contain full conversation history in trajectory format:\n# - System prompt, user messages, assistant responses\n# - Tool calls with inputs/outputs\n# - Timestamps for debugging\n#\n# No configuration needed - logging is always enabled.\n# To disable, you would need to modify the source code.\n\n# =============================================================================\n# Code Execution Sandbox (Programmatic Tool Calling)\n# =============================================================================\n# The execute_code tool runs Python scripts that call Hermes tools via RPC.\n# Intermediate tool results stay out of the LLM's context window.\ncode_execution:\n timeout: 300 # Max seconds per script before kill (default: 300 = 5 min)\n max_tool_calls: 50 # Max RPC tool calls per execution (default: 50)\n\n# =============================================================================\n# Subagent Delegation\n# =============================================================================\n# The delegate_task tool spawns child agents with isolated context.\n# Supports single tasks and batch mode (default 3 parallel, configurable).\ndelegation:\n max_iterations: 50 # Max tool-calling turns per child (default: 50)\n # max_concurrent_children: 3 # Max parallel child agents per batch (default: 3, floor: 1, no ceiling).\n # WARNING: values above 10 multiply API cost linearly.\n # max_spawn_depth: 1 # Delegation tree depth cap (range: 1-3, default: 1 = flat).\n # Raise to 2 to allow workers to spawn their own subagents.\n # Requires role=\"orchestrator\" on intermediate agents.\n # orchestrator_enabled: true # Kill switch for role=\"orchestrator\" children (default: true).\n # subagent_auto_approve: false # When a subagent hits a dangerous-command approval prompt, auto-deny (default: false)\n # or auto-approve \"once\" (true) instead of blocking on stdin.\n # The parent TUI owns stdin, so blocking would deadlock; non-interactive resolution is required.\n # Both choices emit a logger.warning audit line. Flip to true only for cron/batch pipelines.\n # inherit_mcp_toolsets: true # When explicit child toolsets are narrowed, also keep the parent's MCP toolsets (default: true). Set false for strict intersection.\n # model: \"google/gemini-3-flash-preview\" # Override model for subagents (empty = inherit parent)\n # provider: \"openrouter\" # Override provider for subagents (empty = inherit parent)\n # # Resolves full credentials (base_url, api_key) automatically.\n # # Supported: openrouter, nous, zai, kimi-coding, minimax\n\n# =============================================================================\n# Honcho Integration (Cross-Session User Modeling)\n# =============================================================================\n# AI-native persistent memory via Honcho (https://honcho.dev/).\n# Builds a deeper understanding of the user across sessions and tools.\n# Runs alongside USER.md — additive, not a replacement.\n#\n# Requires: pip install honcho-ai\n# Config: ~/.honcho/config.json (shared with Claude Code, Cursor, etc.)\n# API key: HONCHO_API_KEY in ~/.hermes/.env or ~/.honcho/config.json\n#\n# Hermes-specific overrides (optional — most config comes from ~/.honcho/config.json):\n# honcho: {}\n\n# =============================================================================\n# Display\n# =============================================================================\ndisplay:\n # Use compact banner mode (hides the ASCII-art banner, shows a single line).\n # true: Compact single-line banner\n # false: Full ASCII banner with tool/skill summary (default)\n compact: false\n\n # Tool progress display level (CLI and gateway)\n # off: Silent — no tool activity shown, just the final response\n # new: Show a tool indicator only when the tool changes (skip repeats)\n # all: Show every tool call with a short preview (default)\n # verbose: Full args, results, and debug logs (same as /verbose)\n # log: Silent in chat; append every tool call to ~/.hermes/logs/tool_calls.log (gateway only)\n # Toggle at runtime with /verbose in the CLI\n tool_progress: all\n\n # Per-platform defaults can be quieter than the global setting. Telegram\n # tunes for mobile: tool_progress and busy_ack_detail default off (no\n # per-tool breadcrumb stream, no \"iteration 21/60\" debug detail in busy\n # acks or heartbeats), but interim_assistant_messages and\n # long_running_notifications STAY ON so the user has real signal between\n # turn start and final answer (mid-turn assistant commentary + a single\n # edit-in-place \"⏳ Working — N min\" heartbeat). Override under\n # display.platforms.telegram.\n\n # Auto-cleanup of temporary progress bubbles after the final response lands.\n # On platforms that support message deletion (currently Telegram), this\n # removes the tool-progress bubble, \"⏳ Still working...\" notices, and\n # context-pressure status messages once the final reply has been delivered —\n # keeping long-running turns visible live, then tidy afterward. Failed runs\n # leave the bubbles in place as breadcrumbs. Off by default.\n # Per-platform override: display.platforms.telegram.cleanup_progress\n # true: Delete tracked progress/status bubbles on successful turn\n # false: Leave everything in place (default)\n # Example:\n # display:\n # platforms:\n # telegram:\n # cleanup_progress: true\n cleanup_progress: false\n\n # Natural mid-turn assistant updates.\n # On gateway platforms, when true, completed assistant status messages are\n # sent as separate chat messages. On the Desktop app, when true, mid-turn\n # assistant narration streamed between tool calls is kept in the transcript\n # instead of the bubble collapsing to only the final message on completion.\n # Independent of tool_progress and gateway streaming.\n # true: Keep/send mid-turn assistant updates (default)\n # false: Only keep/send the final response\n interim_assistant_messages: true\n\n # Gateway-only long-running status heartbeats.\n # When false, the platform does not receive periodic \"⏳ Working — N min\"\n # notifications even if agent.gateway_notify_interval is non-zero. The\n # heartbeat edits a single message in place (where the adapter supports\n # editing) instead of posting a new bubble each interval.\n # Default: true everywhere, including Telegram (silent agents are worse\n # than a single edit-in-place heartbeat).\n long_running_notifications: true\n\n # Include detailed iteration/tool/status context in busy acknowledgments\n # and long-running heartbeats. When true, busy acks show \"iteration 21/60,\n # terminal, 10 min\" and the heartbeat shows \"⏳ Working — 12 min,\n # iteration 21/60, terminal\". When false (Telegram default), both stay\n # terse: \"Interrupting current task\" and \"⏳ Working — 12 min, terminal\".\n busy_ack_detail: true\n\n # What Enter does when Hermes is already busy (CLI and gateway platforms).\n # interrupt: Interrupt the current run and redirect Hermes (default)\n # queue: Queue your message for the next turn\n # steer: Inject your message mid-run via /steer, arriving at the agent\n # after the next tool call — no interrupt, no role violation.\n # Falls back to 'queue' if the agent isn't running yet or if\n # images are attached (steer only carries text).\n # Ctrl+C (or /stop in gateway) always interrupts regardless of this setting.\n # Toggle at runtime with /busy <interrupt|queue|steer>.\n busy_input_mode: interrupt\n\n # Background process notifications (gateway/messaging only).\n # Controls how chatty the process watcher is when you use\n # terminal(background=true, notify_on_complete=true) from Telegram/Discord/etc.\n # off: No watcher messages at all\n # result: Only the final completion message\n # error: Only the final message when exit code != 0\n # all: Running output updates + final message (default)\n background_process_notifications: all\n\n\n # Play terminal bell when agent finishes a response.\n # Useful for long-running tasks — your terminal will ding when the agent is done.\n # Works over SSH. Most terminals can be configured to flash the taskbar or play a sound.\n # true: Ring the terminal bell on each response\n # false: Silent (default)\n bell_on_complete: false\n\n # Show model reasoning/thinking before each response.\n # When enabled, a dim box shows the model's thought process above the response.\n # Toggle at runtime with /reasoning show or /reasoning hide.\n # true: Show the reasoning box\n # false: Hide reasoning (default)\n show_reasoning: false\n\n # Stream tokens to the terminal as they arrive instead of waiting for the\n # full response. The response box opens on first token and text appears\n # line-by-line. Tool calls are still captured silently.\n # true: Stream tokens as they arrive (default)\n # false: Wait for the full response before rendering\n streaming: true\n\n # Show [HH:MM] timestamps on user input and assistant response labels.\n # timestamps: false\n\n # ───────────────────────────────────────────────────────────────────────────\n # Skin / Theme\n # ───────────────────────────────────────────────────────────────────────────\n # Customize CLI visual appearance — banner colors, spinner faces, tool prefix,\n # response box label, and branding text. Change at runtime with /skin <name>.\n #\n # Built-in skins:\n # default — Classic Hermes gold/kawaii\n # ares — Crimson/bronze war-god theme with spinner wings\n # mono — Clean grayscale monochrome\n # slate — Cool blue developer-focused\n # daylight — Bright light-mode theme\n # warm-lightmode — Warm paper-tone light-mode theme\n # poseidon — Sea-green/teal Olympian theme\n # sisyphus — Earthy stone-and-moss theme\n # charizard — Fiery orange dragon theme\n #\n # Custom skins: drop a YAML file in ~/.hermes/skins/<name>.yaml\n # Schema (all fields optional, missing values inherit from default):\n #\n # name: my-theme\n # description: Short description\n # colors:\n # banner_border: \"#HEX\" # Panel border\n # banner_title: \"#HEX\" # Panel title\n # banner_accent: \"#HEX\" # Section headers (Available Tools, etc.)\n # banner_dim: \"#HEX\" # Dim/muted text\n # banner_text: \"#HEX\" # Body text (tool names, skill names)\n # ui_accent: \"#HEX\" # UI accent color\n # response_border: \"#HEX\" # Response box border color\n # spinner:\n # waiting_faces: [\"(⚔)\", \"(⛨)\"] # Faces shown while waiting\n # thinking_faces: [\"(⚔)\", \"(⌁)\"] # Faces shown while thinking\n # thinking_verbs: [\"forging\", \"plotting\"] # Verbs for spinner messages\n # wings: # Optional left/right spinner decorations\n # - [\"⟪⚔\", \"⚔⟫\"]\n # - [\"⟪▲\", \"▲⟫\"]\n # branding:\n # agent_name: \"My Agent\" # Banner title and branding\n # welcome: \"Welcome message\" # Shown at CLI startup\n # response_label: \" ⚔ Agent \" # Response box header label\n # prompt_symbol: \"⚔\" # Prompt symbol (bare token; renderers add trailing space)\n # tool_prefix: \"╎\" # Tool output line prefix (default: ┊)\n #\n skin: default\n\n# =============================================================================\n# Model Aliases — short names for /model command\n# =============================================================================\n# Map short aliases to exact (model, provider, base_url) tuples.\n# Used by /model tab completion and resolve_alias().\n# Aliases are checked BEFORE the models.dev catalog, so they can route\n# to endpoints not in the catalog (e.g. Ollama Cloud, local servers).\n#\n# model_aliases:\n# opus:\n# model: claude-opus-4-6\n# provider: anthropic\n# qwen:\n# model: \"qwen3.5:397b\"\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n# glm:\n# model: glm-4.7\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n\n# =============================================================================\n# Privacy\n# =============================================================================\n# privacy:\n# # Redact PII from the LLM context prompt.\n# # When true, phone numbers are stripped and user/chat IDs are replaced\n# # with deterministic hashes before being sent to the model.\n# # Names and usernames are NOT affected (user-chosen, publicly visible).\n# # Routing/delivery still uses the original values internally.\n# redact_pii: false\n\n# =============================================================================\n# Shell-script hooks\n# =============================================================================\n# Register shell scripts as plugin-hook callbacks. Each entry is executed as\n# a subprocess (shell=False, shlex.split) with a JSON payload on stdin. On\n# stdout the script may return JSON that either blocks the tool call or\n# injects context into the next LLM call.\n#\n# Valid events (mirror hermes_cli.plugins.VALID_HOOKS):\n# pre_tool_call, post_tool_call, pre_llm_call, post_llm_call,\n# pre_api_request, post_api_request, on_session_start, on_session_end,\n# on_session_finalize, on_session_reset, subagent_stop\n#\n# First-use consent: each (event, command) pair prompts once on a TTY, then\n# is persisted to ~/.hermes/shell-hooks-allowlist.json. Non-interactive\n# runs (gateway, cron) need --accept-hooks, HERMES_ACCEPT_HOOKS=1, or the\n# hooks_auto_accept key below.\n#\n# See website/docs/user-guide/features/hooks.md for the full JSON wire\n# protocol and worked examples.\n#\n# hooks:\n# pre_tool_call:\n# - matcher: \"terminal\"\n# command: \"~/.hermes/agent-hooks/block-rm-rf.sh\"\n# timeout: 10\n# post_tool_call:\n# - matcher: \"write_file|patch\"\n# command: \"~/.hermes/agent-hooks/auto-format.sh\"\n# pre_llm_call:\n# - command: \"~/.hermes/agent-hooks/inject-cwd-context.sh\"\n# subagent_stop:\n# - command: \"~/.hermes/agent-hooks/log-orchestration.sh\"\n#\n# hooks_auto_accept: false\n\n\n# =============================================================================\n# Telemetry\n# =============================================================================\n# Shared metrics are disabled by default. When enabled, Hermes writes only\n# allowlisted aggregate counters and immutable JSON\n# packages under $HERMES_HOME/telemetry/shared_metrics; it does not upload them.\n# Packages include a random profile-scoped ID that stays stable until this\n# directory is deleted. It is not derived from hardware, account, or host data.\n# Successfully exported local history is retained for 30 days; pending deltas\n# are retained until they can be exported.\n# This profile-owned choice is not overridden by managed-scope configuration.\ntelemetry:\n shared_metrics:\n enabled: false\n\n\n# =============================================================================\n# Update Behavior\n# =============================================================================\nupdates:\n # Create a full HERMES_HOME zip before every `hermes update`.\n # Backups land in ~/.hermes/backups/ and can be restored with `hermes import`.\n # Off by default because large homes can add minutes to every update.\n pre_update_backup: false\n\n # Number of pre-update backup zips to retain.\n backup_keep: 5\n\n # What non-interactive updates do with local source edits in the Hermes repo.\n # Interactive terminal updates always prompt before restoring the autostash.\n #\n # stash - auto-stash before pull, then auto-restore after success (default)\n # discard - drop the update-created stash after success; use only on managed\n # installs where local source edits should not persist\n non_interactive_local_changes: \"stash\"\n\n\n# =============================================================================\n# Web Dashboard\n# =============================================================================\n# OAuth gate configuration for `hermes dashboard --host <non-loopback>`.\n# The bundled Nous Portal plugin reads these on startup; settings here are\n# the canonical surface. Each can be overridden by an environment variable:\n#\n# dashboard.oauth.client_id <- HERMES_DASHBOARD_OAUTH_CLIENT_ID\n# dashboard.oauth.portal_url <- HERMES_DASHBOARD_PORTAL_URL\n# dashboard.public_url <- HERMES_DASHBOARD_PUBLIC_URL\n#\n# Env wins when set to a non-empty value. This is what Fly.io's platform-\n# secret injection uses to push per-deploy client_ids without needing to\n# bake a config.yaml into the image. Empty env values are treated as unset\n# so a provisioned-but-not-populated secret can't shadow a valid entry here.\n#\n# Local dev / on-prem deploys should typically set these via config.yaml\n# (the ~/.hermes/.env file is reserved for API keys and secrets).\n#\n# dashboard:\n# oauth:\n# client_id: \"\" # agent:{instance_id}; Portal provisions this at deploy\n# portal_url: \"\" # blank → default https://portal.nousresearch.com\n#\n# # Force the absolute base URL the OAuth callback (and any other public\n# # URL the dashboard hands to external systems) is built from. Set this\n# # for deploys behind reverse proxies that don't reliably forward\n# # X-Forwarded-Host / X-Forwarded-Proto / X-Forwarded-Prefix (manual\n# # nginx setups, on-prem ingresses, custom-domain Fly deploys without\n# # full proxy header chains).\n# #\n# # When set, the value is the complete authority: scheme + host +\n# # optional path prefix (e.g. \"https://example.com/hermes\"). The OAuth\n# # callback URL becomes \"<public_url>/auth/callback\" — X-Forwarded-Prefix\n# # is IGNORED on this code path because the operator has explicitly\n# # declared the public URL and we no longer need to guess.\n# #\n# # Leave empty to use the existing proxy-header reconstruction (the\n# # default — works on Fly.io out of the box).\n# #\n# # public_url: \"https://example.com/hermes\"\n#\n# -----------------------------------------------------------------------------\n# Self-hosted OIDC dashboard auth (generic OpenID Connect — Authentik,\n# Keycloak, Zitadel, Authelia, Auth0, Okta, Google, …). Use this INSTEAD of the\n# nous block above when gating the dashboard with your own identity provider.\n# Each setting can be overridden by an environment variable:\n#\n# dashboard.oauth.self_hosted.issuer <- HERMES_DASHBOARD_OIDC_ISSUER\n# dashboard.oauth.self_hosted.client_id <- HERMES_DASHBOARD_OIDC_CLIENT_ID\n# dashboard.oauth.self_hosted.scopes <- HERMES_DASHBOARD_OIDC_SCOPES\n# dashboard.oauth.self_hosted.client_secret <- HERMES_DASHBOARD_OIDC_CLIENT_SECRET\n#\n# dashboard:\n# oauth:\n# provider: self-hosted\n# self_hosted:\n# issuer: \"https://auth.example.com/application/o/hermes/\" # required\n# client_id: \"hermes-dashboard\" # required\n# scopes: \"openid profile email\" # optional\n#\n# # OPTIONAL — set ONLY if your IDP registered the client as\n# # *confidential* (Authentik / Keycloak often default to this). When\n# # set, Hermes authenticates the client at the token endpoint\n# # (client_secret_basic or client_secret_post, auto-selected from the\n# # IDP's discovery doc) IN ADDITION to PKCE. Leave unset for a public\n# # (PKCE-only) client — the common case.\n# #\n# # This is a CREDENTIAL: prefer setting HERMES_DASHBOARD_OIDC_CLIENT_SECRET\n# # in ~/.hermes/.env over putting it here in config.yaml.\n# # client_secret: \"\"\n\n# =============================================================================\n# External secret sources\n# =============================================================================\n# Pull provider credentials from external secret managers at process startup\n# instead of storing them in ~/.hermes/.env. Only the manager's bootstrap\n# credential (e.g. BWS_ACCESS_TOKEN / OP_SERVICE_ACCOUNT_TOKEN) lives in .env\n# (or your shell / desktop session); everything else rotates centrally.\n# Failures never block startup — Hermes warns once and continues with\n# whatever .env already had.\n#\n# Multiple sources can be enabled at once:\n# - \"mapped\" sources (explicit VAR -> ref bindings, e.g. 1Password's env:\n# map) beat \"bulk\" sources (whole-project dumps like Bitwarden BSM)\n# - within a shape, the first source to claim a var wins; later claims\n# are skipped with a startup warning (never a silent clobber)\n# - a source's override_existing lets it beat .env/shell values, but\n# never another secret source's claim\n# Docs: https://hermes-agent.nousresearch.com/docs/user-guide/secrets/\n#\n# secrets:\n# # Optional explicit ordering of enabled sources.\n# # sources: [onepassword, bitwarden]\n#\n# # ---- Bitwarden Secrets Manager (bws CLI) --------------------------------\n# bitwarden:\n# enabled: false\n# access_token_env: BWS_ACCESS_TOKEN # bootstrap token, sourced from .env\n# project_id: \"\" # UUID of the BSM project to sync\n# server_url: \"\" # \"\" = US Cloud; EU/self-hosted URL otherwise\n# cache_ttl_seconds: 300 # 0 disables fresh caching\n# encrypted_cache: # optional encrypted stale fallback\n# enabled: false\n# max_stale_seconds: 0 # 0 disables stale fallback\n# override_existing: true # BSM values win over existing env\n# auto_install: true # lazy-download bws into ~/.hermes/bin\n#\n# # ---- 1Password (op CLI) -------------------------------------------------\n# onepassword:\n# enabled: false\n# # Map env-var names to op:// secret references. Each is resolved with a\n# # single `op read` at startup.\n# env:\n# OPENAI_API_KEY: \"op://Private/OpenAI/api key\"\n# ANTHROPIC_API_KEY: \"op://Private/Anthropic/credential\"\n# account: \"\" # op --account shorthand; \"\" = default\n# service_account_token_env: OP_SERVICE_ACCOUNT_TOKEN # headless auth; unset = desktop session\n# binary_path: \"\" # \"\" = resolve op via PATH; else absolute path\n# cache_ttl_seconds: 300 # 0 disables BOTH cache layers\n# override_existing: true # resolved values win over existing env\n#\n# # ---- Command helper (any CLI vault) --------------------------------------\n# # Run a user-configured helper that prints KEY=VALUE lines on stdout —\n# # works with any secret store that has a CLI: keepassxc-cli, secret-tool,\n# # pass, gpg, or a script that cats a tmpfs env file. Composes with the\n# # sources above (enable any combination). POSIX-only (needs /bin/sh).\n# # The helper must be fast and NON-interactive (hard timeout, 1 MiB cap);\n# # its stderr is discarded so diagnostics can't leak secret material.\n# command:\n# enabled: false\n# command: \"cat /run/user/1000/hermes-secrets.env\"\n# helper_timeout_seconds: 3\n# override_existing: false # .env/shell win by default\n"}, "a10bd49dddc96d9eda2ed9aff043b06117f44047": {"t": 1785767981.5112922, "n": 84044, "h": "d36472873ea10a6b", "c": "# Hermes Agent CLI Configuration\n# Copy this file to cli-config.yaml and customize as needed.\n# This file configures the CLI behavior. Environment variables in .env take precedence.\n\n# =============================================================================\n# Model Configuration\n# =============================================================================\nmodel:\n # Default model to use (can be overridden with --model flag)\n # Both \"default\" and \"model\" work as the key name here.\n default: \"anthropic/claude-opus-4.6\"\n \n # Inference provider selection:\n # \"auto\" - Auto-detect from credentials (default)\n # \"openrouter\" - OpenRouter (requires: OPENROUTER_API_KEY or OPENAI_API_KEY)\n # \"nous\" - Nous Portal OAuth (requires: hermes auth add nous)\n # \"nous-api\" - Nous Portal API key (requires: NOUS_API_KEY)\n # \"anthropic\" - Direct Anthropic API (requires: ANTHROPIC_API_KEY)\n # \"openai-codex\" - OpenAI Codex (requires: hermes auth)\n # \"copilot\" - GitHub Copilot / GitHub Models (requires: GITHUB_TOKEN)\n # \"gemini\" - Use Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n # \"zai\" - Use z.ai / ZhipuAI GLM models (requires: GLM_API_KEY)\n # \"kimi-coding\" - Kimi / Moonshot AI (requires: KIMI_API_KEY)\n # \"minimax\" - MiniMax global (requires: MINIMAX_API_KEY)\n # \"minimax-cn\" - MiniMax China (requires: MINIMAX_CN_API_KEY)\n # \"huggingface\" - Hugging Face Inference (requires: HF_TOKEN)\n # \"nvidia\" - NVIDIA NIM / build.nvidia.com (requires: NVIDIA_API_KEY)\n # \"xiaomi\" - Xiaomi MiMo (requires: XIAOMI_API_KEY)\n # \"arcee\" - Arcee AI Trinity models (requires: ARCEEAI_API_KEY)\n # \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY — https://ollama.com/settings)\n # \"deepinfra\" - DeepInfra (requires: DEEPINFRA_API_KEY)\n # \"kilocode\" - KiloCode gateway (requires: KILOCODE_API_KEY)\n # \"azure-foundry\" - Microsoft Foundry / Azure OpenAI (API key or Entra ID)\n # \"lmstudio\" - LM Studio local server (optional: LM_API_KEY, defaults to http://127.0.0.1:1234/v1)\n #\n # Local servers (LM Studio, Ollama, vLLM, llama.cpp):\n # \"custom\" - Any other OpenAI-compatible endpoint. Set base_url below.\n # Aliases: \"ollama\", \"vllm\", \"llamacpp\" all map to \"custom\".\n # LM Studio is first-class and uses provider: \"lmstudio\".\n # It works with both no-auth and auth-enabled server modes.\n #\n # Can also be overridden for a single invocation with the --provider flag.\n provider: \"auto\"\n \n # API configuration (falls back to OPENROUTER_API_KEY env var)\n # api_key: \"your-key-here\" # Uncomment to set here instead of .env\n base_url: \"https://openrouter.ai/api/v1\"\n\n # Azure Foundry keyless auth example:\n # provider: \"azure-foundry\"\n # base_url: \"https://<resource>.openai.azure.com/openai/v1\"\n # auth_mode: \"entra_id\" # DefaultAzureCredential: az login, managed identity, workload identity, etc.\n # default: \"gpt-4o\" # Deployment/model name\n # entra:\n # scope: \"https://ai.azure.com/.default\" # Optional; this is the default.\n\n # ── Token limits — two settings, easy to confuse ──────────────────────────\n #\n # context_length: TOTAL context window (input + output tokens combined).\n # Controls when Hermes compresses history and validates requests.\n # Leave unset — Hermes auto-detects the correct value from the provider.\n # Set manually only when auto-detection is wrong (e.g. a local server with\n # a custom num_ctx, or a proxy that doesn't expose /v1/models).\n #\n # context_length: 131072\n #\n # max_tokens: OUTPUT cap — maximum tokens the model may generate per response.\n # Unrelated to how long your conversation history can be.\n # The OpenAI-standard name \"max_tokens\" is a misnomer; Anthropic's native\n # API has since renamed it \"max_output_tokens\" for clarity.\n # Leave unset to use the model's native output ceiling (recommended).\n # Set only if you want to deliberately limit individual response length.\n #\n# max_tokens: 8192\n\n # ── Custom request headers (optional) ─────────────────────────────────────\n #\n # default_headers: extra HTTP headers sent on every request to an\n # OpenAI-compatible endpoint. User values take precedence over the\n # provider/SDK defaults, so this is the supported way to override the\n # OpenAI Python SDK's identifying headers (User-Agent: OpenAI/Python ...,\n # X-Stainless-*) when a custom provider sits behind a gateway/WAF that\n # rejects them — e.g. an upstream that returns \"502 Upstream access\n # forbidden\" for the SDK default User-Agent but accepts a plain one.\n # Applies on the OpenAI wire only (not native Anthropic / Bedrock).\n #\n # default_headers:\n # User-Agent: \"curl/8.7.1\"\n #\n # extra_headers: accepted as an alias of default_headers (merged, with\n # extra_headers winning when both are set) — matches the per-provider\n # extra_headers key below.\n #\n # Per-provider variant: named providers / custom_providers entries accept an\n # extra_headers dict scoped to that endpoint only — for reverse proxies,\n # gateways, or custom auth (e.g. Cloudflare Access service tokens).\n # Merged onto SDK/provider defaults with the entry's values winning.\n # Header values are treated as secrets and are never logged.\n #\n # providers:\n # my-proxy:\n # base_url: \"https://llm.internal.example.com/v1\"\n # key_env: \"MY_PROXY_API_KEY\"\n # extra_headers:\n # CF-Access-Client-Id: \"xxxx.access\"\n # CF-Access-Client-Secret: \"${CF_ACCESS_SECRET}\"\n # X-Client-Name: \"hermes-agent\"\n\n# Named provider overrides (optional)\n# Use this for per-provider request timeouts, non-stream stale timeouts,\n# and per-model exceptions.\n# Applies to the primary turn client on every api_mode (OpenAI-wire, native\n# Anthropic, and Anthropic-compatible providers), the fallback chain, and\n# client rebuilds during credential rotation. For OpenAI-wire chat\n# completions (streaming and non-streaming) the configured value is also\n# used as the per-request ``timeout=`` kwarg so it wins over the legacy\n# HERMES_API_TIMEOUT env var (which still applies when no config is set).\n# ``stale_timeout_seconds`` controls the non-streaming stale-call detector and\n# wins over the legacy HERMES_API_CALL_STALE_TIMEOUT env var. Leaving these\n# unset keeps the legacy defaults (HERMES_API_TIMEOUT=1800s,\n# HERMES_API_CALL_STALE_TIMEOUT=90s, native Anthropic 900s). The\n# implicit non-stream stale detector is auto-disabled for local endpoints\n# and can scale upward for very large contexts.\n#\n# Not currently wired for AWS Bedrock (bedrock_converse + AnthropicBedrock\n# SDK paths) — those use boto3 with its own timeout configuration.\n#\n# providers:\n# ollama-local:\n# request_timeout_seconds: 300 # Longer timeout for local cold-starts\n# stale_timeout_seconds: 900 # Explicitly re-enable stale detection on local endpoints\n# anthropic:\n# request_timeout_seconds: 30 # Fast-fail cloud requests\n# models:\n# claude-opus-4.6:\n# timeout_seconds: 600 # Longer timeout for extended-thinking Opus calls\n# openai-codex:\n# models:\n# gpt-5.4:\n# stale_timeout_seconds: 1800 # Longer non-stream stale timeout for slow large-context turns\n\n# =============================================================================\n# OpenRouter Provider Routing (only applies when using OpenRouter)\n# =============================================================================\n# Control how requests are routed across providers on OpenRouter.\n# See: https://openrouter.ai/docs/guides/routing/provider-selection\n#\n# provider_routing:\n# # Sort strategy: \"price\" (default), \"throughput\", or \"latency\"\n# # Append :nitro to model name for a shortcut to throughput sorting.\n# sort: \"throughput\"\n#\n# # Only allow these providers (provider slugs from OpenRouter)\n# # only: [\"anthropic\", \"google\"]\n#\n# # Skip these providers entirely\n# # ignore: [\"deepinfra\", \"fireworks\"]\n#\n# # Try providers in this order (overrides default load balancing)\n# # order: [\"anthropic\", \"google\", \"together\"]\n#\n# # Require providers to support all parameters in your request\n# # require_parameters: true\n#\n# # Data policy: \"allow\" (default) or \"deny\" to exclude providers that may store data\n# # data_collection: \"deny\"\n\n# =============================================================================\n# OpenRouter Response Caching (only applies when using OpenRouter)\n# =============================================================================\n# Cache identical API responses at the OpenRouter edge for free instant replays.\n# When enabled, identical requests (same model, messages, parameters) return\n# cached responses with zero billing. Separate from Anthropic prompt caching.\n# See: https://openrouter.ai/docs/guides/features/response-caching\n#\n# openrouter:\n# response_cache: true # Enable response caching (default: true)\n# response_cache_ttl: 300 # Cache TTL in seconds, 1-86400 (default: 300)\n\n# =============================================================================\n# Git Worktree Isolation\n# =============================================================================\n# When enabled, each CLI session creates an isolated git worktree so multiple\n# agents can work on the same repo concurrently without file collisions.\n# Equivalent to always passing --worktree / -w on the command line.\n#\n# worktree: true # Always create a worktree when in a git repo\n# worktree: false # Default — only create when -w flag is passed\n#\n# By default a new worktree branches from the freshly-fetched remote tip\n# (the current branch's upstream, else the remote's default branch) so it\n# starts current with the project instead of from the local clone's\n# (possibly stale) HEAD. Set worktree_sync: false to branch from local HEAD\n# instead — useful when offline or when you deliberately want the clone's\n# exact current state as the base.\n#\n# worktree_sync: true # Default — branch from the fetched remote tip\n# worktree_sync: false # Branch from local HEAD (offline / pinned base)\n\n# =============================================================================\n# Terminal Tool Configuration\n# =============================================================================\n# Choose ONE of the following terminal configurations by uncommenting it.\n# The terminal tool executes commands in the specified environment.\n\n# -----------------------------------------------------------------------------\n# OPTION 1: Local execution (default)\n# Commands run directly on your machine in the current directory\n# -----------------------------------------------------------------------------\n# Working directory behavior:\n# - CLI (`hermes` command): Uses \".\" (current directory where you run hermes)\n# - Gateway/messaging/cron: Uses terminal.cwd here; legacy .env cwd values are deprecated\nterminal:\n backend: \"local\"\n cwd: \".\" # For local backend: \".\" = current directory. Ignored for remote backends unless a backend documents otherwise.\n timeout: 180\n # HOME policy for tool subprocesses:\n # auto - default: host uses your real HOME; containers use HERMES_HOME/home\n # real - force your real OS-user HOME\n # profile - force HERMES_HOME/home for strict per-profile CLI config isolation\n home_mode: \"auto\"\n docker_mount_cwd_to_workspace: false # SECURITY: off by default. Opt in to mount the launch cwd into Docker /workspace.\n lifetime_seconds: 300\n # sudo_password: \"hunter2\" # Optional: pipe a sudo password via sudo -S. SECURITY WARNING: plaintext.\n # sudo_password: \"\" # Explicit empty password: try empty and never open the interactive sudo prompt.\n\n# -----------------------------------------------------------------------------\n# OPTION 2: SSH remote execution\n# Commands run on a remote server - agent code stays local (sandboxed)\n# Great for: keeping agent isolated from its own code, using powerful remote hardware\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"ssh\"\n# cwd: \"/home/myuser/project\" # Path on the REMOTE server\n# timeout: 180\n# lifetime_seconds: 300\n# ssh_host: \"my-server.example.com\"\n# ssh_user: \"myuser\"\n# ssh_port: 22\n# ssh_key: \"~/.ssh/id_rsa\" # Optional - uses ssh-agent if not specified\n\n# -----------------------------------------------------------------------------\n# OPTION 3: Docker container\n# Commands run in an isolated Docker container\n# Great for: reproducible environments, testing, isolation\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"docker\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /)\n# timeout: 180\n# lifetime_seconds: 300\n# docker_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# docker_mount_cwd_to_workspace: true # Explicit opt-in: mount your launch cwd into /workspace\n# # Optional: run the container as your host user's uid:gid so files written\n# # into bind-mounted dirs are owned by you, not root. Drops SETUID/SETGID\n# # caps too since no gosu privilege drop is needed. Leave off if your\n# # chosen docker_image expects to start as root.\n# docker_run_as_host_user: true\n# # Optional: explicitly forward selected env vars into Docker.\n# # These values come from your current shell first, then ~/.hermes/.env.\n# # Warning: anything forwarded here is visible to commands run in the container.\n# docker_forward_env:\n# - \"GITHUB_TOKEN\"\n# - \"NPM_TOKEN\"\n# # Optional: extra flags passed verbatim to docker run (appended after security defaults).\n# # Useful for adding capabilities (e.g. apt installs needing SETUID) or custom options.\n# # Example: add a Linux capability not included by default\n# # docker_extra_args:\n# # - \"--cap-add\"\n# # - \"SETUID\"\n\n# -----------------------------------------------------------------------------\n# OPTION 4: Singularity/Apptainer container\n# Commands run in a Singularity container (common in HPC environments)\n# Great for: HPC clusters, shared compute environments\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"singularity\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# singularity_image: \"docker://nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 5: Modal cloud execution\n# Commands run on Modal's cloud infrastructure\n# Great for: GPU access, scalable compute, serverless execution\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"modal\"\n# cwd: \"/workspace\" # Path INSIDE the sandbox (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# modal_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 6: Daytona cloud execution\n# Commands run in Daytona cloud sandboxes\n# Great for: Cloud dev environments, persistent workspaces, team collaboration\n# Requires: pip install daytona, DAYTONA_API_KEY env var\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"daytona\"\n# cwd: \"~\"\n# timeout: 180\n# lifetime_seconds: 300\n# daytona_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# container_disk: 10240 # Daytona max is 10GB per sandbox\n\n#\n# --- Container resource limits (docker, singularity, modal, daytona -- ignored for local/ssh) ---\n# These settings apply to all container backends. They control the resources\n# allocated to the sandbox and whether its filesystem persists across sessions.\n container_cpu: 1 # CPU cores\n container_memory: 5120 # Memory in MB (5120 = 5GB)\n container_disk: 51200 # Disk in MB (51200 = 50GB)\n container_persistent: true # Persist filesystem across sessions (false = ephemeral)\n\n# -----------------------------------------------------------------------------\n# SUDO SUPPORT (works with ALL backends above)\n# -----------------------------------------------------------------------------\n# Add sudo_password to any terminal config above to enable sudo commands.\n# The password is piped via `sudo -S`. Works with local, ssh, docker, etc.\n#\n# SECURITY WARNING: Password stored in plaintext!\n#\n# INTERACTIVE PROMPT: If sudo_password is unset and the CLI is running,\n# you'll be prompted to enter your password when sudo is needed:\n# - 45-second timeout (auto-skips if no input)\n# - Press Enter to skip (command fails gracefully)\n# - Password is hidden while typing\n# - Password is cached for the session\n#\n# EMPTY PASSWORDS: Setting sudo_password to an explicit empty string is different\n# from leaving it unset. Hermes will try an empty password via `sudo -S` and\n# will not open the interactive prompt. This is useful for passwordless sudo,\n# Touch ID sudo setups, and environments where prompting is just noise.\n#\n# ALTERNATIVES:\n# - SSH backend: Configure passwordless sudo on the remote server\n# - Containers: Run as root inside the container (no sudo needed)\n# - Local: Configure /etc/sudoers for specific commands\n#\n# Example (add to your terminal section):\n# sudo_password: \"your-password-here\"\n\n# =============================================================================\n# Security Scanning (tirith)\n# =============================================================================\n# Optional pre-exec command security scanning via tirith.\n# Detects homograph URLs, pipe-to-shell, terminal injection, env manipulation.\n# Install: brew install sheeki03/tap/tirith\n# Docs: https://github.com/sheeki03/tirith\n#\n# security:\n# tirith_enabled: true # Enable/disable tirith scanning\n# tirith_path: \"tirith\" # Path to tirith binary (supports ~ expansion)\n# tirith_timeout: 5 # Scan timeout in seconds\n# tirith_fail_open: true # Allow commands if tirith unavailable\n\n# =============================================================================\n# Browser Tool Configuration\n# =============================================================================\nbrowser:\n # Inactivity timeout in seconds - browser sessions are automatically closed\n # after this period of no activity between agent loops (default: 120 = 2 minutes)\n inactivity_timeout: 120\n\n# =============================================================================\n# Tool Loop Guardrails\n# =============================================================================\n# Soft warnings are enabled by default. They append guidance to repeated failed\n# or non-progressing tool results but still let the tool execute. Hard stops are\n# opt-in circuit breakers for autonomous/cron sessions where stopping a loop is\n# preferable to spending the full iteration budget.\ntool_loop_guardrails:\n warnings_enabled: true\n hard_stop_enabled: false\n warn_after:\n exact_failure: 2\n same_tool_failure: 3\n idempotent_no_progress: 2\n hard_stop_after:\n exact_failure: 5\n same_tool_failure: 8\n idempotent_no_progress: 5\n\n# =============================================================================\n# Context Compression (Auto-shrinks long conversations)\n# =============================================================================\n# When conversation approaches model's context limit, middle turns are\n# automatically summarized to free up space while preserving important context.\n#\n# HOW IT WORKS:\n# 1. Tracks actual token usage from API responses (not estimates)\n# 2. When prompt_tokens >= threshold% of model's context_length, triggers compression\n# 3. Protects first 3 turns (system prompt, initial request, first response)\n# 4. Protects last N turns (default 20 messages = ~10 full turns of recent context)\n# 5. Summarizes middle turns using a fast/cheap model\n# 6. Inserts summary as a user message, continues conversation seamlessly\n#\n# Post-compression tail budget is target_ratio × threshold × context_length:\n# 200K context, threshold 0.50, ratio 0.20 → 20K tokens of recent tail preserved\n# 1M context, threshold 0.50, ratio 0.20 → 100K tokens of recent tail preserved\n#\ncompression:\n # Enable automatic context compression (default: true)\n # Set to false if you prefer to manage context manually or want errors on overflow\n enabled: true\n\n # Opt-in compression progress notices on chat platforms (default: false).\n # By design, routine automatic compression is SILENT on human-facing chat\n # gateways (Telegram, Discord, Slack, ...) — it happens in the background\n # with server-side logging only. Set true to also deliver the routine\n # progress statuses (compacting started, preflight/pre-API compression,\n # idle compaction, retry progress, and the compaction-complete notice) to\n # chat platforms. Unrelated operational noise (auxiliary model failures,\n # provider retry chatter) stays suppressed either way, and compression\n # FAILURE notices + manual /compress feedback are always visible\n # regardless of this setting. (#52995)\n progress_notices: false\n \n # Trigger compression at this % of model's context limit (default: 0.50 = 50%)\n # Lower values = more aggressive compression, higher values = compress later\n # Models with context windows below 512K are floored at 0.75 (raise-only) so\n # compaction doesn't fire with half the window still free; set above 0.75 to override.\n threshold: 0.50\n\n # Per-model threshold overrides: keys are substring-matched against the model\n # name (longest match wins). Useful when some models need different compaction\n # points — e.g. a 1M-context model can compress later (0.30) while a 128K\n # model needs to compress earlier (0.60). The small-context floor (75% for\n # <512K models) still applies on top of per-model overrides.\n # model_thresholds:\n # \"glm-5.2\": 0.40\n # \"claude-sonnet\": 0.35\n # \"gpt-5\": 0.30\n\n # Optional absolute token cap for the compression trigger (default: null = disabled).\n # When set, compression fires at the LOWER of the ratio-based threshold and this\n # absolute token count — first-fires-wins. It never fires later than this count\n # regardless of which model is active (useful when switching between models with\n # very different context windows). Clamped to the model's context length at\n # apply-time, so a cap above the window is a no-op (ratio-based threshold wins).\n # Survives model switches and fallback activations.\n # threshold_tokens: 200000\n\n # Existing Codex gpt-5.5 behavior: raise Hermes' compaction trigger to 85%\n # for the ChatGPT Codex OAuth route. Set false to opt back down to threshold.\n codex_gpt55_autoraise: true\n\n # Fraction of the threshold to preserve as recent tail (default: 0.20 = 20%)\n # e.g. 20% of 50% threshold = 10% of total context kept as recent messages.\n # Summary output is separately capped at 12K tokens (Gemini output limit).\n # Range: 0.10 - 0.80\n target_ratio: 0.20\n\n # Number of most-recent messages to always preserve (default: 20 ≈ 10 full turns)\n # Higher values keep more recent conversation intact at the cost of more aggressive\n # compression of older turns.\n protect_last_n: 20\n\n # Minimum number of REAL (actionable) user messages guaranteed to survive in\n # the uncompressed tail (default: 1 = the existing single last-user anchor,\n # behavior-preserving). Raise to e.g. 3 to keep the last 3 real user turns\n # verbatim even when bulky tool outputs fill the tail token budget — blank\n # platform echoes, compaction handoffs, and synthetic continuation rows never\n # count toward N. The tail can exceed the token budget when this pulls the\n # cut back; the guarantee wins over the budget by design.\n min_tail_user_messages: 1\n\n # Compression retry rounds before a turn gives up with \"max compression\n # attempts reached\" (default: 3, same as the previous hardcoded value).\n # Raise (e.g. 6) for tool-schema-heavy sessions where 3 rounds cannot bring\n # the request estimate under the threshold. Validated >= 1, hard cap 10.\n max_attempts: 3\n\n # Codex app-server (codex CLI runtime) thread-compaction mode. The codex\n # agent owns the real thread context on this runtime, so Hermes' summarizer\n # cannot shrink it — compaction goes through the app server instead.\n # native = let Codex decide when to compact its own thread (default)\n # hermes = let Hermes threshold trigger Codex thread/compact/start\n # off = Hermes will not auto-trigger compaction; Codex may still compact natively\n codex_app_server_auto: native\n\n # Number of non-system messages to protect at the head of the transcript, in\n # ADDITION to the system prompt (which is always implicitly protected).\n # Head messages are NEVER summarized — they survive every compression\n # indefinitely. This gives stable early context for short/medium sessions,\n # but in long-running sessions that rely on rolling compaction the pinned\n # opening turns may not match how you want the session framed over time.\n # Set to 0 to preserve ONLY the system prompt (plus the rolling summary\n # and recent tail) — the cleanest configuration for long-running sessions.\n # Default 3 preserves the system prompt plus the first three non-system\n # head messages, matching the pre-feature behaviour.\n protect_first_n: 3\n\n # Idle compaction (default: 0 = disabled). When > 0, a session that resumes\n # after at least this many seconds of inactivity compacts its accumulated\n # history up front, before the first reply, so a long-lived thread you come\n # back to later doesn't re-read its full stale context on every turn.\n # Time-based, so it complements (does not replace) the size-based `threshold`\n # above. It is skipped when the context is already small (at or below the\n # post-compression target = threshold × target_ratio), so it never wastes a\n # summarization on a short idle thread. Example: 1800 = compact after 30 min idle.\n idle_compact_after_seconds: 0\n\n # Proactive tool-result prune (default: 0 = disabled). Opt-in token trigger\n # for a deterministic, no-LLM prune of OLD tool-result payloads, run\n # independently of `threshold` above. On large-window models (512K/1M) the\n # ratio threshold rarely fires, so bulky tool outputs (terminal dumps, file\n # reads, web extracts) ride along in history and get re-billed every turn.\n # When re-sent history exceeds this many tokens, the prune dedupes identical\n # results, summarizes older oversized ones, and truncates large tool-call\n # arguments — protecting the most recent `protect_last_n` messages and never\n # calling the model. Try 48000 to enable. Built-in compressor engine only;\n # other context engines inherit a safe no-op.\n # NOTE: a committed prune rewrites already-sent history, which invalidates\n # the provider's prompt-cache prefix — the min_reclaim gate below keeps\n # those cache breaks episodic (like a compression boundary) instead of\n # per-turn.\n proactive_prune_tokens: 0\n\n # The prune's summarize pass only touches tool results larger than this many\n # characters (clamped to >= 200 so a generated summary can't be\n # re-summarized). Default 8000.\n proactive_prune_min_result_chars: 8000\n\n # A proactive prune only COMMITS when it reclaims at least this many tokens\n # (measured on the pruned output). This is the prompt-cache hysteresis gate:\n # one meaningful, amortized cache break per batch of stale tool output\n # instead of a tiny break on every tool iteration. 0 = commit any non-zero\n # prune. Default 4096.\n proactive_prune_min_reclaim_tokens: 4096\n\n # To pin a specific model/provider for compression summaries, use the\n # auxiliary section below (auxiliary.compression.provider / model).\n\n# =============================================================================\n# Anthropic prompt caching TTL\n# =============================================================================\n# When prompt caching is active (Claude via OpenRouter or native Anthropic),\n# Anthropic supports two TTL tiers for cached prefixes: \"5m\" (default) and\n# \"1h\". Other values are ignored and \"5m\" is used.\n#\nprompt_caching:\n cache_ttl: \"5m\" # use \"1h\" for long sessions with pauses between turns\n\n# =============================================================================\n# Auxiliary Models (Advanced — Experimental)\n# =============================================================================\n# Hermes uses lightweight \"auxiliary\" models for side tasks: image analysis,\n# browser screenshot analysis, web page summarization, TTS audio-tag insertion,\n# and context compression.\n#\n# By default these use Gemini Flash via OpenRouter or Nous Portal and are\n# auto-detected from your credentials. You do NOT need to change anything\n# here for normal usage.\n#\n# WARNING: Overriding these with providers other than OpenRouter or Nous Portal\n# is EXPERIMENTAL and may not work. Not all models/providers support vision,\n# produce usable summaries, or accept the same API format. Change at your own\n# risk — if things break, reset to \"auto\" / empty values.\n#\n# Each task has its own provider + model pair so you can mix providers.\n# For example: OpenRouter for vision (needs multimodal), but your main\n# local endpoint for compression (just needs text).\n#\n# Provider options:\n# \"auto\" - Best available: OpenRouter → Nous Portal → main endpoint (default)\n# \"openrouter\" - Force OpenRouter (requires OPENROUTER_API_KEY)\n# \"nous\" - Force Nous Portal (requires: hermes auth add nous)\n# \"gemini\" - Force Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n# \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY)\n# \"codex\" - Force Codex OAuth (requires: hermes model → Codex).\n# Uses gpt-5.3-codex which supports vision.\n# \"main\" - Use your custom endpoint (OPENAI_BASE_URL + OPENAI_API_KEY).\n# Works with OpenAI API, local models, or any OpenAI-compatible\n# endpoint. Also falls back to Codex OAuth and API-key providers.\n#\n# Model: leave empty to use the provider's default. When empty, OpenRouter\n# uses \"google/gemini-3-flash-preview\" and Nous uses \"gemini-3-flash\".\n# Other providers pick a sensible default automatically.\n#\n# auxiliary:\n# # Image analysis: vision_analyze tool + browser screenshots\n# vision:\n# provider: \"auto\"\n# model: \"\" # e.g. \"google/gemini-2.5-flash\", \"openai/gpt-4o\"\n# timeout: 30 # LLM API call timeout (seconds)\n# download_timeout: 30 # Image HTTP download timeout (seconds)\n# # Increase for slow connections or self-hosted image servers\n# reasoning_effort: \"\" # Per-task thinking level: none, minimal, low, medium,\n# # high, xhigh, max, ultra. Empty = provider default.\n# # Works on every auxiliary task block (vision,\n# # web_extract, compression, title_generation, curator,\n# # background_review, moa_reference, ...). Example: run\n# # compression at \"low\" and vision at \"none\" to cut\n# # side-task latency/cost on reasoning models.\n#\n# # Web page scraping / summarization + browser page text extraction\n# web_extract:\n# provider: \"auto\"\n# model: \"\"\n# reasoning_effort: \"low\"\n#\n# # Gemini 3.1 TTS hidden audio-tag insertion\n# tts_audio_tags:\n# provider: \"auto\" # empty model = your main chat model\n# model: \"\"\n# timeout: 30\n#\n# # Automatic session title generation after the first exchange\n# title_generation:\n# enabled: true # set false to disable auto-title generation\n# provider: \"auto\"\n# model: \"\"\n# timeout: 30\n# language: \"\" # empty = match the user's language; or e.g. \"English\"\n#\n# # Session search — summarizes matching past sessions\n# session_search:\n# provider: \"auto\"\n# model: \"\"\n# timeout: 30\n# max_concurrency: 3 # Limit parallel summaries to reduce request-burst 429s\n# extra_body: {} # Provider-specific OpenAI-compatible request fields\n# # Example for providers that support request-body\n# # reasoning controls:\n# # extra_body:\n# # enable_thinking: false\n# # Some vLLM/Qwen deployments expect this nested:\n# # extra_body:\n# # chat_template_kwargs:\n# # enable_thinking: false\n\n# =============================================================================\n# Persistent Memory\n# =============================================================================\n# Bounded curated memory injected into the system prompt every session.\n# Two stores: MEMORY.md (agent's notes) and USER.md (user profile).\n# Character limits keep the memory small and focused. The agent manages\n# pruning -- when at the limit, it must consolidate or replace entries.\n# Disabled by default in batch_runner.\n#\nmemory:\n # Agent's personal notes: environment facts, conventions, things learned\n memory_enabled: true\n \n # User profile: preferences, communication style, expectations\n user_profile_enabled: true\n \n # Character limits (~2.75 chars per token, model-independent)\n memory_char_limit: 2200 # ~800 tokens\n user_char_limit: 1375 # ~500 tokens\n\n # Periodic memory nudge: remind the agent to consider saving memories\n # every N user turns. Set to 0 to disable. Only active when memory is enabled.\n nudge_interval: 10 # Nudge every 10 user turns (0 = disabled)\n\n # Memory flush: give the agent one turn to save memories before context is\n # lost (compression, /new, /reset, exit). Set to 0 to disable.\n # For exit/reset, only fires if the session had at least this many user turns.\n flush_min_turns: 6 # Min user turns to trigger flush on exit/reset (0 = disabled)\n\n# =============================================================================\n# Session Reset Policy (Messaging Platforms)\n# =============================================================================\n# Controls when messaging sessions (Telegram, Discord, WhatsApp, Slack) are\n# automatically cleared. Default is \"none\": sessions never auto-reset —\n# conversation context lives until you /reset or /new manually, or context\n# compression kicks in. Opt in to automatic resets if you prefer sessions to\n# clear on a schedule (long-lived context increases API cost per message,\n# though prompt caching and compression keep this manageable).\n#\n# When an automatic reset triggers, the agent first saves important\n# information to its persistent memory — but the conversation context is\n# wiped. The agent starts fresh but retains learned facts via its memory\n# system.\n#\n# Modes:\n# \"none\" - Never auto-reset (default); context lives until /reset or compression\n# \"idle\" - Reset after N minutes of inactivity\n# \"daily\" - Reset at a fixed hour each day\n# \"both\" - Reset on EITHER inactivity timeout or daily boundary\n#\nsession_reset:\n mode: none # \"none\", \"idle\", \"daily\", or \"both\"\n idle_minutes: 1440 # Inactivity timeout in minutes (used by \"idle\"/\"both\")\n at_hour: 4 # Daily reset hour, 0-23 local time (used by \"daily\"/\"both\")\n\n# Maximum number of simultaneously active chat sessions across CLI, TUI,\n# dashboard chat, and messaging gateway. Set to null, 0, or omit to allow\n# unlimited concurrent sessions. When the limit is reached, new sessions get a\n# clean error while existing active sessions keep their normal behavior. This\n# top-level key takes precedence over gateway.max_concurrent_sessions. The cap\n# is a best-effort single-host/profile runtime guard; Hermes fails open if the\n# local runtime lease registry cannot be read or locked.\nmax_concurrent_sessions: null\n\n# When true, group/channel chats use one session per participant when the platform\n# provides a user ID. This is the secure default and prevents users in the same\n# room from sharing context, interrupts, and token costs. Set false only if you\n# explicitly want one shared \"room brain\" per group/channel.\ngroup_sessions_per_user: true\n\n# ─────────────────────────────────────────────────────────────────────────────\n# API Server — per-client model routing\n# ─────────────────────────────────────────────────────────────────────────────\n# Route different API clients to different models/providers on a single\n# Hermes deployment. Clients choose a backend by sending a specific string\n# as the OpenAI ``model`` field. Unmapped model values fall back to the\n# global model configured in the ``model:`` section above, and an explicit\n# session /model override always wins over a route.\n#\n# Configure via the ``platforms.api_server.extra.model_routes`` gateway\n# config block:\n#\n# platforms:\n# api_server:\n# enabled: true\n# extra:\n# key: \"your-api-server-secret\"\n# model_routes:\n# # Xiaozhi clients send model=\"minimax-m2\" → routed to MiniMax via OpenRouter\n# minimax-m2:\n# model: \"minimax/minimax-m1\"\n# provider: \"openrouter\" # optional — overrides global provider\n# # api_key: \"sk-...\" # optional — per-route UPSTREAM provider\n# # key (NOT caller auth; never logged)\n# # base_url: \"https://...\" # optional — per-route base URL\n# # GPT clients keep their own alias\n# gpt-5:\n# model: \"openai/gpt-5\"\n# provider: \"openrouter\"\n#\n# Configured aliases are automatically listed by GET /v1/models so clients\n# can discover them without manual coordination. Caller authentication is\n# unchanged: every request still authenticates with the global API server\n# key (``extra.key`` / API_SERVER_KEY).\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Gateway Streaming\n# ─────────────────────────────────────────────────────────────────────────────\n# Stream tokens to messaging platforms in real-time. The bot sends a message\n# on first token, then progressively edits it as more tokens arrive.\n# Disabled by default — enable to try the streaming UX on Telegram/Discord/Slack.\n# For Telegram, partial edits are sent as plain text and only the final edit uses MarkdownV2.\nstreaming:\n enabled: false\n # transport: edit # \"edit\" = progressive editMessageText\n # edit_interval: 0.3 # seconds between message edits\n # buffer_threshold: 40 # chars before forcing an edit flush\n # cursor: \" ▉\" # cursor shown during streaming\n\n# =============================================================================\n# Skills Configuration\n# =============================================================================\n# Skills are reusable procedures the agent can load and follow. The agent can\n# also create new skills after completing complex tasks.\n#\nskills:\n # Nudge the agent to create skills after complex tasks.\n # Every N tool-calling iterations, remind the model to consider saving a skill.\n # Set to 0 to disable.\n creation_nudge_interval: 15\n\n # External skill directories — share skills across tools/agents without\n # copying them into ~/.hermes/skills/. Each path is expanded (~ and ${VAR})\n # and resolved to an absolute path. External dirs are read-only: skill\n # creation always writes to ~/.hermes/skills/. Local skills take precedence\n # when names collide.\n # external_dirs:\n # - ~/.agents/skills\n # - /home/shared/team-skills\n\n# =============================================================================\n# Agent Behavior\n# =============================================================================\nagent:\n # Maximum tool-calling iterations per conversation (default: 500)\n # Higher = more room for complex tasks, but costs more tokens\n # Recommended: 20-30 for focused tasks, 50-100 for open exploration\n max_turns: 500\n\n # Inactivity timeout for gateway agent runs (seconds, 0 = unlimited).\n # The agent can run indefinitely when actively calling tools or receiving\n # API responses. Only fires after the agent has been idle for this duration.\n # gateway_timeout: 1800\n\n # Staged warning: send a warning before escalating to full timeout.\n # Fires once per run when inactivity reaches this threshold (seconds).\n # Set to 0 to disable the warning.\n # gateway_timeout_warning: 900\n\n # Graceful drain timeout for gateway stop/restart (seconds).\n # Default 0 = no drain: a restart interrupts in-flight agents immediately,\n # cleans up, and exits. Set a positive value only if you want a grace\n # window on /restart, and keep it well under systemd's TimeoutStopSec.\n # restart_drain_timeout: 0\n\n # Upper bound (seconds) a submitted prompt waits for the deferred agent\n # build (MCP discovery, model metadata, skills scan) before failing with a\n # visible error. The wait is patient — the message is delivered as soon as\n # the build completes, and a progress notice is shown past 30s — so this cap\n # only fires on a genuinely hung build. Raise it for deployments with many\n # slow or unreachable MCP servers. Default 600.\n # build_wait_timeout: 600\n\n # Max app-level retry attempts for API errors (connection drops, provider\n # timeouts, 5xx, etc.) before the agent surfaces the failure. Lower this\n # to 1 if you use fallback providers and want fast failover on flaky\n # primaries (default 3). The OpenAI SDK does its own low-level retries\n # underneath this wrapper — this is the Hermes-level loop.\n # api_max_retries: 3\n\n # After the agent edits code without fresh passing verification, nudge it to\n # verify before finishing. The default \"auto\" enables it on interactive\n # coding surfaces (CLI, TUI, desktop) and programmatic callers, and disables\n # it on conversational messaging surfaces (Telegram, Discord, etc.) where the\n # verification summary would reach a human as chat noise. Set true or false to\n # force it on or off; the HERMES_VERIFY_ON_STOP env var (1/0) takes precedence.\n # verify_on_stop: auto\n\n # Standing operator instructions for the coding posture (when Hermes is in a\n # code workspace). Appended to the coding brief as an extra system block, so\n # you can pin project-wide workflow rules without editing the shipped brief.\n # Accepts a string or a list of strings. Takes effect next session.\n # coding_instructions:\n # - \"For UI work, don't run tsc/lint until I approve the look.\"\n # - \"Clean the diff before you commit and push.\"\n\n # When verify-on-stop finds edited code without fresh verification evidence,\n # append guidance for creative UI work (avoid broad tsc/lint/test before visual\n # approval) and clean-diff expectations. Set false to keep that nudge terse.\n # verify_guidance: true\n\n # A `pre_verify` hook (plugin or shell, see Event Hooks docs) can keep the\n # agent going one more turn to verify/clean before finishing. This caps how\n # many times one turn may be nudged to continue, so a hook can't trap the loop.\n # Default 3.\n # max_verify_nudges: 3\n\n # Enable verbose logging\n verbose: false\n \n # Reasoning effort level (OpenRouter and Nous Portal)\n # Controls how much \"thinking\" the model does before responding.\n # Options: \"xhigh\" (max), \"high\", \"medium\", \"low\", \"minimal\", \"none\" (disable)\n reasoning_effort: \"medium\"\n \n # Per-model reasoning effort overrides (optional dict)\n # Key: any sensible model spelling works (exact, dots↔dashes interchangeable,\n # provider prefix optional). First match wins.\n # Value: reasoning effort level (same options as reasoning_effort)\n # Override the global reasoning_effort for that specific model.\n # NOTE: no `hermes config set` support for this key -- edit YAML directly.\n # reasoning_overrides:\n # \"openrouter/anthropic/claude-opus-4.5\": \"xhigh\"\n # \"openai/gpt-5\": \"low\"\n # \"claude-opus-4.6\": \"high\" # bare model name also works\n # \"deepseek/deepseek-v4-pro\": \"xhigh\" # dots and dashes are interchangeable\n reasoning_overrides: {}\n \n # Predefined personalities (use with /personality command)\n personalities:\n helpful: \"You are a helpful, friendly AI assistant.\"\n concise: \"You are a concise assistant. Keep responses brief and to the point.\"\n technical: \"You are a technical expert. Provide detailed, accurate technical information.\"\n creative: \"You are a creative assistant. Think outside the box and offer innovative solutions.\"\n teacher: \"You are a patient teacher. Explain concepts clearly with examples.\"\n kawaii: \"You are a kawaii assistant! Use cute expressions like (◕‿◕), ★, ♪, and ~! Add sparkles and be super enthusiastic about everything! Every response should feel warm and adorable desu~! ヽ(>∀<☆)ノ\"\n catgirl: \"You are Neko-chan, an anime catgirl AI assistant, nya~! Add 'nya' and cat-like expressions to your speech. Use kaomoji like (=^・ω・^=) and ฅ^•ﻌ•^ฅ. Be playful and curious like a cat, nya~!\"\n pirate: \"Arrr! Ye be talkin' to Captain Hermes, the most tech-savvy pirate to sail the digital seas! Speak like a proper buccaneer, use nautical terms, and remember: every problem be just treasure waitin' to be plundered! Yo ho ho!\"\n shakespeare: \"Hark! Thou speakest with an assistant most versed in the bardic arts. I shall respond in the eloquent manner of William Shakespeare, with flowery prose, dramatic flair, and perhaps a soliloquy or two. What light through yonder terminal breaks?\"\n surfer: \"Duuude! You're chatting with the chillest AI on the web, bro! Everything's gonna be totally rad. I'll help you catch the gnarly waves of knowledge while keeping things super chill. Cowabunga! 🤙\"\n noir: \"The rain hammered against the terminal like regrets on a guilty conscience. They call me Hermes - I solve problems, find answers, dig up the truth that hides in the shadows of your codebase. In this city of silicon and secrets, everyone's got something to hide. What's your story, pal?\"\n uwu: \"hewwo! i'm your fwiendwy assistant uwu~ i wiww twy my best to hewp you! *nuzzles your code* OwO what's this? wet me take a wook! i pwomise to be vewy hewpful >w<\"\n philosopher: \"Greetings, seeker of wisdom. I am an assistant who contemplates the deeper meaning behind every query. Let us examine not just the 'how' but the 'why' of your questions. Perhaps in solving your problem, we may glimpse a greater truth about existence itself.\"\n hype: \"YOOO LET'S GOOOO!!! 🔥🔥🔥 I am SO PUMPED to help you today! Every question is AMAZING and we're gonna CRUSH IT together! This is gonna be LEGENDARY! ARE YOU READY?! LET'S DO THIS! 💪😤🚀\"\n\n# =============================================================================\n# Toolsets\n# =============================================================================\n# Control which tools the agent has access to.\n# Use `hermes tools` to interactively enable/disable tools per platform.\n\n# =============================================================================\n# Platform Toolsets (per-platform tool configuration)\n# =============================================================================\n# Override which toolsets are available on each platform.\n# If a platform isn't listed here, its built-in default is used.\n#\n# You can use EITHER:\n# - A preset like \"hermes-cli\" or \"hermes-telegram\" (curated tool set)\n# - A list of individual toolsets to compose your own (see list below)\n#\n# Supported platform keys: cli, telegram, discord, whatsapp, slack, qqbot, teams, google_chat\n#\n# Examples:\n#\n# # Use presets (same as defaults):\n# platform_toolsets:\n# cli: [hermes-cli]\n# telegram: [hermes-telegram]\n#\n# # Custom: give Telegram only web + terminal + file + planning:\n# platform_toolsets:\n# telegram: [web, terminal, file, todo]\n#\n# # Custom: CLI without browser or image gen:\n# platform_toolsets:\n# cli: [web, terminal, file, skills, todo, tts, cronjob]\n#\n# # Restrictive: Discord gets read-only tools only:\n# platform_toolsets:\n# discord: [web, vision, skills, todo]\n#\n# If not set, defaults are:\n# cli: hermes-cli (everything + cronjob management)\n# telegram: hermes-telegram (terminal, file, web, vision, image, tts, browser, skills, todo, cronjob, messaging)\n# discord: hermes-discord (same as telegram)\n# whatsapp: hermes-whatsapp (same as telegram)\n# slack: hermes-slack (same as telegram)\n# signal: hermes-signal (same as telegram)\n# homeassistant: hermes-homeassistant (same as telegram)\n# qqbot: hermes-qqbot (same as telegram)\n# teams: hermes-teams (same as telegram)\n# google_chat: hermes-google_chat (same as telegram)\n#\nplatform_toolsets:\n cli: [hermes-cli]\n telegram: [hermes-telegram]\n discord: [hermes-discord]\n whatsapp: [hermes-whatsapp]\n slack: [hermes-slack]\n signal: [hermes-signal]\n homeassistant: [hermes-homeassistant]\n qqbot: [hermes-qqbot]\n yuanbao: [hermes-yuanbao]\n teams: [hermes-teams]\n google_chat: [hermes-google_chat]\n\n# =============================================================================\n# Gateway Platform Settings\n# =============================================================================\n# Optional per-platform messaging settings.\n# Platform-specific knobs live under `extra`.\n#\n# platforms:\n# telegram:\n# reply_to_mode: \"first\" # off | first | all\n# # guest_mode lets explicit @mentions from non-allowlisted groups through.\n# # Default false; ordinary messages, replies, and regex wake words stay blocked.\n# guest_mode: false\n# # allowed_chats: [\"-1001234567890\"]\n# extra:\n# disable_link_previews: false # Set true to suppress Telegram URL previews in bot messages\n# 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\n# rich_drafts: false # Experimental rich draft previews during Telegram DM streaming; default false because Telegram Desktop/macOS can visually overlay draft frames\n# command_menu:\n# # Telegram allows up to 100 BotCommands; Hermes defaults to 60 so\n# # all built-in commands plus common skill commands stay visible\n# # while remaining under Telegram's payload-size limit. Clamped 1..100.\n# max_commands: 60\n# # prepend = user priority first, then Hermes defaults\n# # append = Hermes defaults first, then user priority\n# # replace = only the list below defines priority\n# priority_mode: prepend\n# priority:\n# - my_plugin_command\n# webhook:\n# extra:\n# # Route scripts default to a 30 second timeout. Scripts must live under\n# # the active profile's scripts directory and receive webhook JSON on stdin.\n# script_timeout_seconds: 30\n#\n# Discord-specific settings (config.yaml top-level, not under platforms:):\n#\n# discord:\n# require_mention: true # Require @mention in server channels (default: true)\n# auto_thread: true # Auto-create thread on @mention (default: true)\n# free_response_channels: \"\" # Channel IDs where no mention is needed\n# reactions: true # Show processing reactions (default: true)\n# history_backfill: true # Recover missed channel messages on mention (default: true)\n# history_backfill_limit: 50 # Max messages to scan backwards (default: 50)\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Available toolsets (use these names in platform_toolsets or the toolsets list)\n#\n# Run `hermes chat --list-toolsets` to see all toolsets and their tools.\n# Run `hermes chat --list-tools` to see every individual tool with descriptions.\n# ─────────────────────────────────────────────────────────────────────────────\n#\n# INDIVIDUAL TOOLSETS (compose your own):\n# web - web_search, web_extract\n# search - web_search only (no scraping)\n# terminal - terminal, process\n# file - read_file, write_file, patch, search\n# browser - browser_navigate, browser_snapshot, browser_click, browser_type,\n# browser_scroll, browser_back, browser_press,\n# browser_get_images, browser_vision (requires BROWSERBASE_API_KEY)\n# vision - vision_analyze (requires OPENROUTER_API_KEY)\n# image_gen - image_generate (requires FAL_KEY)\n# skills - skills_list, skill_view\n# skills_hub - skill_hub (search/install/manage from online registries — user-driven only)\n# todo - todo (in-memory task planning, no deps)\n# tts - text_to_speech (Edge TTS free, or ELEVENLABS/OPENAI/MINIMAX/MISTRAL key)\n# cronjob - cronjob (create/list/update/pause/resume/run/remove scheduled tasks)\n#\n# PRESETS (curated bundles):\n# hermes-cli - All of the above except send_message\n# hermes-telegram - terminal, file, web, vision, image_gen, tts, browser,\n# skills, todo, cronjob, send_message\n# hermes-discord - Same as hermes-telegram\n# hermes-whatsapp - Same as hermes-telegram\n# hermes-slack - Same as hermes-telegram\n#\n# COMPOSITE:\n# debugging - terminal + web + file\n# safe - web + vision (no terminal access)\n# all - Everything available\n#\n# web - Web search and content extraction (web_search, web_extract)\n# search - Web search only, no scraping (web_search)\n# terminal - Command execution and process management (terminal, process)\n# file - File operations: read, write, patch, search\n# browser - Full browser automation (navigate, click, type, screenshot, etc.)\n# vision - Image analysis (vision_analyze)\n# image_gen - Image generation with FLUX (image_generate)\n# skills - Load skill documents (skills_list, skill_view)\n# todo - Task planning and tracking for multi-step work\n# memory - Persistent memory across sessions (personal notes + user profile)\n# session_search - Search and recall past conversations (FTS5 + Gemini Flash summarization)\n# tts - Text-to-speech (Edge TTS free, ElevenLabs, OpenAI, MiniMax, Mistral)\n# cronjob - Schedule and manage automated tasks (CLI-only)\n#\n# Composite toolsets:\n# debugging - terminal + web + file (for troubleshooting)\n# safe - web + vision (no terminal access)\n\n# NOTE: The top-level \"toolsets\" key is deprecated and ignored.\n# Tool configuration is managed per-platform via platform_toolsets above.\n# Use `hermes tools` to configure interactively, or edit platform_toolsets directly.\n#\n# CLI override: hermes chat --toolsets terminal,web,file\n\n# =============================================================================\n# MCP (Model Context Protocol) Servers\n# =============================================================================\n# Connect to external MCP servers to add tools from the MCP ecosystem.\n# Each server's tools are automatically discovered and registered.\n# See website/docs/user-guide/features/mcp.md for full documentation.\n#\n# Stdio servers (spawn a subprocess):\n# command: the executable to run\n# args: command-line arguments\n# env: environment variables (only these + safe defaults passed to subprocess)\n#\n# HTTP servers (connect to a URL):\n# url: the MCP server endpoint\n# headers: HTTP headers (e.g., for authentication)\n#\n# Optional per-server settings:\n# timeout: tool call timeout in seconds (default: 120)\n# connect_timeout: initial connection timeout (default: 60)\n# keepalive_interval: liveness ping cadence in seconds (default: 180).\n# Lower it below the server's session TTL for servers that expire idle\n# sessions quickly (e.g. Unreal Engine editor MCP, ~15s), otherwise idle\n# tool calls hit an expired session and pay a slow reconnect. Floored at 5s.\n#\n# mcp_servers:\n# time:\n# command: uvx\n# args: [\"mcp-server-time\"]\n# filesystem:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-filesystem\", \"/home/user\"]\n# notion:\n# url: https://mcp.notion.com/mcp\n# github:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-github\"]\n# env:\n# GITHUB_PERSONAL_ACCESS_TOKEN: \"ghp_...\"\n#\n# Sampling (server-initiated LLM requests) — enabled by default.\n# Per-server config under the 'sampling' key:\n# analysis:\n# command: npx\n# args: [\"-y\", \"analysis-server\"]\n# sampling:\n# enabled: true # default: true\n# model: \"gemini-3-flash\" # override model (optional)\n# max_tokens_cap: 4096 # max tokens per request\n# timeout: 30 # LLM call timeout (seconds)\n# max_rpm: 10 # max requests per minute\n# allowed_models: [] # model whitelist (empty = all)\n# max_tool_rounds: 5 # tool loop limit (0 = disable)\n# log_level: \"info\" # audit verbosity\n\n# =============================================================================\n# Text-to-Speech\n# =============================================================================\n# TTS defaults to Edge TTS unless changed in ~/.hermes/config.yaml.\n# Gemini TTS supports persona/director prompt files, and Gemini 3.1 Flash TTS\n# can use a hidden auxiliary rewrite pass to insert expressive square-bracket\n# audio tags into the TTS script without showing tags in chat.\n#\n# tts:\n# provider: \"gemini\"\n# gemini:\n# model: \"gemini-3.1-flash-tts-preview\"\n# voice: \"Kore\"\n# audio_tags: false\n# persona_prompt_file: \"\" # e.g. ~/.hermes/tts/radio-host.md\n\n# =============================================================================\n# Voice Transcription (Speech-to-Text)\n# =============================================================================\n# Automatically transcribe voice messages on messaging platforms.\n# Providers: local (free, faster-whisper) | groq (free tier) | openai (Whisper API) | mistral (Voxtral Transcribe)\n# Set the corresponding API key in .env: GROQ_API_KEY, OPENAI_API_KEY, or MISTRAL_API_KEY.\nstt:\n enabled: true\n # provider: \"local\" # auto-detected if omitted\n local:\n model: \"base\" # tiny | base | small | medium | large-v3 | turbo\n # language: \"\" # auto-detect; set to \"en\", \"es\", \"fr\", etc. to force\n # initial_prompt: \"\" # Optional faster-whisper prompt, e.g. bias Chinese output to simplified Chinese\n # language: \"\" # GLOBAL language hint for every STT provider (per-provider language wins)\n # groq:\n # model: \"whisper-large-v3-turbo\"\n # language: \"\" # blank = stt.language > HERMES_LOCAL_STT_LANGUAGE > auto-detect\n openai:\n model: \"whisper-1\" # whisper-1 | gpt-4o-mini-transcribe | gpt-4o-transcribe\n language: \"\" # auto-detect; set to \"en\", \"es\", \"fr\", etc. to force\n # mistral:\n # model: \"voxtral-mini-latest\" # voxtral-mini-latest | voxtral-mini-2602\n # deepinfra:\n # # Model id is discovered live from the DeepInfra catalog filtered\n # # by the `stt` surface tag — leave `model` blank to take the first\n # # live result. Pin only when you need a specific Whisper variant.\n # model: \"\"\n\n# Text-to-speech. Only the deepinfra block is documented here — the\n# remaining providers (edge, openai, xai, minimax, mistral, gemini,\n# elevenlabs, neutts, kittentts, piper) inherit sensible defaults from\n# DEFAULT_CONFIG in hermes_cli/config.py.\n# tts:\n# provider: \"deepinfra\"\n# deepinfra:\n# # Model id is discovered live from the DeepInfra catalog filtered\n# # by the `tts` surface tag — leave `model` blank to take the first\n# # live result.\n# model: \"\"\n# voice: \"default\"\n\n# Image generation. Each provider plugin reads its own ``image_gen.<name>``\n# block; deepinfra discovers models live from\n# api.deepinfra.com/v1/openai/models filtered by the ``image-gen`` tag —\n# no model id is hardcoded, so retired models disappear automatically.\n# image_gen:\n# provider: \"deepinfra\"\n# deepinfra:\n# # Leave `model` blank for the first live `image-gen`-tagged result.\n# model: \"\"\n\n# =============================================================================\n# Response Pacing (Messaging Platforms)\n# =============================================================================\n# Add human-like delays between message chunks.\n# human_delay:\n# mode: \"off\" # \"off\" | \"natural\" | \"custom\"\n# min_ms: 800 # Min delay (custom mode only)\n# max_ms: 2500 # Max delay (custom mode only)\n\n# =============================================================================\n# Session Logging\n# =============================================================================\n# Session trajectories are automatically saved to logs/ directory.\n# Each session creates: logs/session_YYYYMMDD_HHMMSS_UUID.json\n#\n# The session ID is displayed in the welcome banner for easy reference.\n# Logs contain full conversation history in trajectory format:\n# - System prompt, user messages, assistant responses\n# - Tool calls with inputs/outputs\n# - Timestamps for debugging\n#\n# No configuration needed - logging is always enabled.\n# To disable, you would need to modify the source code.\n\n# =============================================================================\n# Code Execution Sandbox (Programmatic Tool Calling)\n# =============================================================================\n# The execute_code tool runs Python scripts that call Hermes tools via RPC.\n# Intermediate tool results stay out of the LLM's context window.\ncode_execution:\n timeout: 300 # Max seconds per script before kill (default: 300 = 5 min)\n max_tool_calls: 50 # Max RPC tool calls per execution (default: 50)\n\n# =============================================================================\n# Subagent Delegation\n# =============================================================================\n# The delegate_task tool spawns child agents with isolated context.\n# Supports single tasks and batch mode (default 3 parallel, configurable).\ndelegation:\n max_iterations: 50 # Max tool-calling turns per child (default: 50)\n # max_concurrent_children: 3 # Max parallel child agents per batch (default: 3, floor: 1, no ceiling).\n # WARNING: values above 10 multiply API cost linearly.\n # max_spawn_depth: 1 # Delegation tree depth cap (range: 1-3, default: 1 = flat).\n # Raise to 2 to allow workers to spawn their own subagents.\n # Requires role=\"orchestrator\" on intermediate agents.\n # orchestrator_enabled: true # Kill switch for role=\"orchestrator\" children (default: true).\n # subagent_auto_approve: false # When a subagent hits a dangerous-command approval prompt, auto-deny (default: false)\n # or auto-approve \"once\" (true) instead of blocking on stdin.\n # The parent TUI owns stdin, so blocking would deadlock; non-interactive resolution is required.\n # Both choices emit a logger.warning audit line. Flip to true only for cron/batch pipelines.\n # inherit_mcp_toolsets: true # When explicit child toolsets are narrowed, also keep the parent's MCP toolsets (default: true). Set false for strict intersection.\n # model: \"google/gemini-3-flash-preview\" # Override model for subagents (empty = inherit parent)\n # provider: \"openrouter\" # Override provider for subagents (empty = inherit parent)\n # # Resolves full credentials (base_url, api_key) automatically.\n # # Supported: openrouter, nous, zai, kimi-coding, minimax\n\n# =============================================================================\n# Honcho Integration (Cross-Session User Modeling)\n# =============================================================================\n# AI-native persistent memory via Honcho (https://honcho.dev/).\n# Builds a deeper understanding of the user across sessions and tools.\n# Runs alongside USER.md — additive, not a replacement.\n#\n# Requires: pip install honcho-ai\n# Config: ~/.honcho/config.json (shared with Claude Code, Cursor, etc.)\n# API key: HONCHO_API_KEY in ~/.hermes/.env or ~/.honcho/config.json\n#\n# Hermes-specific overrides (optional — most config comes from ~/.honcho/config.json):\n# honcho: {}\n\n# =============================================================================\n# Display\n# =============================================================================\ndisplay:\n # Use compact banner mode (hides the ASCII-art banner, shows a single line).\n # true: Compact single-line banner\n # false: Full ASCII banner with tool/skill summary (default)\n compact: false\n\n # Tool progress display level (CLI and gateway)\n # off: Silent — no tool activity shown, just the final response\n # new: Show a tool indicator only when the tool changes (skip repeats)\n # all: Show every tool call with a short preview (default)\n # verbose: Full args, results, and debug logs (same as /verbose)\n # log: Silent in chat; append every tool call to ~/.hermes/logs/tool_calls.log (gateway only)\n # Toggle at runtime with /verbose in the CLI\n tool_progress: all\n\n # Per-platform defaults can be quieter than the global setting. Telegram\n # tunes for mobile: tool_progress and busy_ack_detail default off (no\n # per-tool breadcrumb stream, no \"iteration 21/60\" debug detail in busy\n # acks or heartbeats), but interim_assistant_messages and\n # long_running_notifications STAY ON so the user has real signal between\n # turn start and final answer (mid-turn assistant commentary + a single\n # edit-in-place \"⏳ Working — N min\" heartbeat). Override under\n # display.platforms.telegram.\n\n # Auto-cleanup of temporary progress bubbles after the final response lands.\n # On platforms that support message deletion (currently Telegram), this\n # removes the tool-progress bubble, \"⏳ Still working...\" notices, and\n # context-pressure status messages once the final reply has been delivered —\n # keeping long-running turns visible live, then tidy afterward. Failed runs\n # leave the bubbles in place as breadcrumbs. Off by default.\n # Per-platform override: display.platforms.telegram.cleanup_progress\n # true: Delete tracked progress/status bubbles on successful turn\n # false: Leave everything in place (default)\n # Example:\n # display:\n # platforms:\n # telegram:\n # cleanup_progress: true\n cleanup_progress: false\n\n # Natural mid-turn assistant updates.\n # On gateway platforms, when true, completed assistant status messages are\n # sent as separate chat messages. On the Desktop app, when true, mid-turn\n # assistant narration streamed between tool calls is kept in the transcript\n # instead of the bubble collapsing to only the final message on completion.\n # Independent of tool_progress and gateway streaming.\n # true: Keep/send mid-turn assistant updates (default)\n # false: Only keep/send the final response\n interim_assistant_messages: true\n\n # Gateway-only long-running status heartbeats.\n # When false, the platform does not receive periodic \"⏳ Working — N min\"\n # notifications even if agent.gateway_notify_interval is non-zero. The\n # heartbeat edits a single message in place (where the adapter supports\n # editing) instead of posting a new bubble each interval.\n # Default: true everywhere, including Telegram (silent agents are worse\n # than a single edit-in-place heartbeat).\n long_running_notifications: true\n\n # Include detailed iteration/tool/status context in busy acknowledgments\n # and long-running heartbeats. When true, busy acks show \"iteration 21/60,\n # terminal, 10 min\" and the heartbeat shows \"⏳ Working — 12 min,\n # iteration 21/60, terminal\". When false (Telegram default), both stay\n # terse: \"Interrupting current task\" and \"⏳ Working — 12 min, terminal\".\n busy_ack_detail: true\n\n # What Enter does when Hermes is already busy (CLI and gateway platforms).\n # interrupt: Interrupt the current run and redirect Hermes (default)\n # queue: Queue your message for the next turn\n # steer: Inject your message mid-run via /steer, arriving at the agent\n # after the next tool call — no interrupt, no role violation.\n # Falls back to 'queue' if the agent isn't running yet or if\n # images are attached (steer only carries text).\n # Ctrl+C (or /stop in gateway) always interrupts regardless of this setting.\n # Toggle at runtime with /busy <interrupt|queue|steer>.\n busy_input_mode: interrupt\n\n # Background process notifications (gateway/messaging only).\n # Controls how chatty the process watcher is when you use\n # terminal(background=true, notify_on_complete=true) from Telegram/Discord/etc.\n # off: No watcher messages at all\n # result: Only the final completion message\n # error: Only the final message when exit code != 0\n # all: Running output updates + final message (default)\n background_process_notifications: all\n\n\n # Play terminal bell when agent finishes a response.\n # Useful for long-running tasks — your terminal will ding when the agent is done.\n # Works over SSH. Most terminals can be configured to flash the taskbar or play a sound.\n # true: Ring the terminal bell on each response\n # false: Silent (default)\n bell_on_complete: false\n\n # Show model reasoning/thinking before each response.\n # When enabled, a dim box shows the model's thought process above the response.\n # Toggle at runtime with /reasoning show or /reasoning hide.\n # true: Show the reasoning box\n # false: Hide reasoning (default)\n show_reasoning: false\n\n # Stream tokens to the terminal as they arrive instead of waiting for the\n # full response. The response box opens on first token and text appears\n # line-by-line. Tool calls are still captured silently.\n # true: Stream tokens as they arrive (default)\n # false: Wait for the full response before rendering\n streaming: true\n\n # Show [HH:MM] timestamps on user input and assistant response labels.\n # timestamps: false\n\n # ───────────────────────────────────────────────────────────────────────────\n # Skin / Theme\n # ───────────────────────────────────────────────────────────────────────────\n # Customize CLI visual appearance — banner colors, spinner faces, tool prefix,\n # response box label, and branding text. Change at runtime with /skin <name>.\n #\n # Built-in skins:\n # default — Classic Hermes gold/kawaii\n # ares — Crimson/bronze war-god theme with spinner wings\n # mono — Clean grayscale monochrome\n # slate — Cool blue developer-focused\n # daylight — Bright light-mode theme\n # warm-lightmode — Warm paper-tone light-mode theme\n # poseidon — Sea-green/teal Olympian theme\n # sisyphus — Earthy stone-and-moss theme\n # charizard — Fiery orange dragon theme\n #\n # Custom skins: drop a YAML file in ~/.hermes/skins/<name>.yaml\n # Schema (all fields optional, missing values inherit from default):\n #\n # name: my-theme\n # description: Short description\n # colors:\n # banner_border: \"#HEX\" # Panel border\n # banner_title: \"#HEX\" # Panel title\n # banner_accent: \"#HEX\" # Section headers (Available Tools, etc.)\n # banner_dim: \"#HEX\" # Dim/muted text\n # banner_text: \"#HEX\" # Body text (tool names, skill names)\n # ui_accent: \"#HEX\" # UI accent color\n # response_border: \"#HEX\" # Response box border color\n # spinner:\n # waiting_faces: [\"(⚔)\", \"(⛨)\"] # Faces shown while waiting\n # thinking_faces: [\"(⚔)\", \"(⌁)\"] # Faces shown while thinking\n # thinking_verbs: [\"forging\", \"plotting\"] # Verbs for spinner messages\n # wings: # Optional left/right spinner decorations\n # - [\"⟪⚔\", \"⚔⟫\"]\n # - [\"⟪▲\", \"▲⟫\"]\n # branding:\n # agent_name: \"My Agent\" # Banner title and branding\n # welcome: \"Welcome message\" # Shown at CLI startup\n # response_label: \" ⚔ Agent \" # Response box header label\n # prompt_symbol: \"⚔\" # Prompt symbol (bare token; renderers add trailing space)\n # tool_prefix: \"╎\" # Tool output line prefix (default: ┊)\n #\n skin: default\n\n# =============================================================================\n# Model Aliases — short names for /model command\n# =============================================================================\n# Map short aliases to exact (model, provider, base_url) tuples.\n# Used by /model tab completion and resolve_alias().\n# Aliases are checked BEFORE the models.dev catalog, so they can route\n# to endpoints not in the catalog (e.g. Ollama Cloud, local servers).\n#\n# model_aliases:\n# opus:\n# model: claude-opus-4-6\n# provider: anthropic\n# qwen:\n# model: \"qwen3.5:397b\"\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n# glm:\n# model: glm-4.7\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n\n# =============================================================================\n# Privacy\n# =============================================================================\n# privacy:\n# # Redact PII from the LLM context prompt.\n# # When true, phone numbers are stripped and user/chat IDs are replaced\n# # with deterministic hashes before being sent to the model.\n# # Names and usernames are NOT affected (user-chosen, publicly visible).\n# # Routing/delivery still uses the original values internally.\n# redact_pii: false\n\n# =============================================================================\n# Shell-script hooks\n# =============================================================================\n# Register shell scripts as plugin-hook callbacks. Each entry is executed as\n# a subprocess (shell=False, shlex.split) with a JSON payload on stdin. On\n# stdout the script may return JSON that either blocks the tool call or\n# injects context into the next LLM call.\n#\n# Valid events (mirror hermes_cli.plugins.VALID_HOOKS):\n# pre_tool_call, post_tool_call, pre_llm_call, post_llm_call,\n# pre_api_request, post_api_request, on_session_start, on_session_end,\n# on_session_finalize, on_session_reset, subagent_stop\n#\n# First-use consent: each (event, command) pair prompts once on a TTY, then\n# is persisted to ~/.hermes/shell-hooks-allowlist.json. Non-interactive\n# runs (gateway, cron) need --accept-hooks, HERMES_ACCEPT_HOOKS=1, or the\n# hooks_auto_accept key below.\n#\n# See website/docs/user-guide/features/hooks.md for the full JSON wire\n# protocol and worked examples.\n#\n# hooks:\n# pre_tool_call:\n# - matcher: \"terminal\"\n# command: \"~/.hermes/agent-hooks/block-rm-rf.sh\"\n# timeout: 10\n# post_tool_call:\n# - matcher: \"write_file|patch\"\n# command: \"~/.hermes/agent-hooks/auto-format.sh\"\n# pre_llm_call:\n# - command: \"~/.hermes/agent-hooks/inject-cwd-context.sh\"\n# subagent_stop:\n# - command: \"~/.hermes/agent-hooks/log-orchestration.sh\"\n#\n# hooks_auto_accept: false\n\n\n# =============================================================================\n# Update Behavior\n# =============================================================================\nupdates:\n # Create a full HERMES_HOME zip before every `hermes update`.\n # Backups land in ~/.hermes/backups/ and can be restored with `hermes import`.\n # Off by default because large homes can add minutes to every update.\n pre_update_backup: false\n\n # Number of pre-update backup zips to retain.\n backup_keep: 5\n\n # What non-interactive updates do with local source edits in the Hermes repo.\n # Interactive terminal updates always prompt before restoring the autostash.\n #\n # stash - auto-stash before pull, then auto-restore after success (default)\n # discard - drop the update-created stash after success; use only on managed\n # installs where local source edits should not persist\n non_interactive_local_changes: \"stash\"\n\n\n# =============================================================================\n# Web Dashboard\n# =============================================================================\n# OAuth gate configuration for `hermes dashboard --host <non-loopback>`.\n# The bundled Nous Portal plugin reads these on startup; settings here are\n# the canonical surface. Each can be overridden by an environment variable:\n#\n# dashboard.oauth.client_id <- HERMES_DASHBOARD_OAUTH_CLIENT_ID\n# dashboard.oauth.portal_url <- HERMES_DASHBOARD_PORTAL_URL\n# dashboard.public_url <- HERMES_DASHBOARD_PUBLIC_URL\n#\n# Env wins when set to a non-empty value. This is what Fly.io's platform-\n# secret injection uses to push per-deploy client_ids without needing to\n# bake a config.yaml into the image. Empty env values are treated as unset\n# so a provisioned-but-not-populated secret can't shadow a valid entry here.\n#\n# Local dev / on-prem deploys should typically set these via config.yaml\n# (the ~/.hermes/.env file is reserved for API keys and secrets).\n#\n# dashboard:\n# oauth:\n# client_id: \"\" # agent:{instance_id}; Portal provisions this at deploy\n# portal_url: \"\" # blank → default https://portal.nousresearch.com\n#\n# # Force the absolute base URL the OAuth callback (and any other public\n# # URL the dashboard hands to external systems) is built from. Set this\n# # for deploys behind reverse proxies that don't reliably forward\n# # X-Forwarded-Host / X-Forwarded-Proto / X-Forwarded-Prefix (manual\n# # nginx setups, on-prem ingresses, custom-domain Fly deploys without\n# # full proxy header chains).\n# #\n# # When set, the value is the complete authority: scheme + host +\n# # optional path prefix (e.g. \"https://example.com/hermes\"). The OAuth\n# # callback URL becomes \"<public_url>/auth/callback\" — X-Forwarded-Prefix\n# # is IGNORED on this code path because the operator has explicitly\n# # declared the public URL and we no longer need to guess.\n# #\n# # Leave empty to use the existing proxy-header reconstruction (the\n# # default — works on Fly.io out of the box).\n# #\n# # public_url: \"https://example.com/hermes\"\n#\n# -----------------------------------------------------------------------------\n# Self-hosted OIDC dashboard auth (generic OpenID Connect — Authentik,\n# Keycloak, Zitadel, Authelia, Auth0, Okta, Google, …). Use this INSTEAD of the\n# nous block above when gating the dashboard with your own identity provider.\n# Each setting can be overridden by an environment variable:\n#\n# dashboard.oauth.self_hosted.issuer <- HERMES_DASHBOARD_OIDC_ISSUER\n# dashboard.oauth.self_hosted.client_id <- HERMES_DASHBOARD_OIDC_CLIENT_ID\n# dashboard.oauth.self_hosted.scopes <- HERMES_DASHBOARD_OIDC_SCOPES\n# dashboard.oauth.self_hosted.client_secret <- HERMES_DASHBOARD_OIDC_CLIENT_SECRET\n#\n# dashboard:\n# oauth:\n# provider: self-hosted\n# self_hosted:\n# issuer: \"https://auth.example.com/application/o/hermes/\" # required\n# client_id: \"hermes-dashboard\" # required\n# scopes: \"openid profile email\" # optional\n#\n# # OPTIONAL — set ONLY if your IDP registered the client as\n# # *confidential* (Authentik / Keycloak often default to this). When\n# # set, Hermes authenticates the client at the token endpoint\n# # (client_secret_basic or client_secret_post, auto-selected from the\n# # IDP's discovery doc) IN ADDITION to PKCE. Leave unset for a public\n# # (PKCE-only) client — the common case.\n# #\n# # This is a CREDENTIAL: prefer setting HERMES_DASHBOARD_OIDC_CLIENT_SECRET\n# # in ~/.hermes/.env over putting it here in config.yaml.\n# # client_secret: \"\"\n\n# =============================================================================\n# External secret sources\n# =============================================================================\n# Pull provider credentials from external secret managers at process startup\n# instead of storing them in ~/.hermes/.env. Only the manager's bootstrap\n# credential (e.g. BWS_ACCESS_TOKEN / OP_SERVICE_ACCOUNT_TOKEN) lives in .env\n# (or your shell / desktop session); everything else rotates centrally.\n# Failures never block startup — Hermes warns once and continues with\n# whatever .env already had.\n#\n# Multiple sources can be enabled at once:\n# - \"mapped\" sources (explicit VAR -> ref bindings, e.g. 1Password's env:\n# map) beat \"bulk\" sources (whole-project dumps like Bitwarden BSM)\n# - within a shape, the first source to claim a var wins; later claims\n# are skipped with a startup warning (never a silent clobber)\n# - a source's override_existing lets it beat .env/shell values, but\n# never another secret source's claim\n# Docs: https://hermes-agent.nousresearch.com/docs/user-guide/secrets/\n#\n# secrets:\n# # Optional explicit ordering of enabled sources.\n# # sources: [onepassword, bitwarden]\n#\n# # ---- Bitwarden Secrets Manager (bws CLI) --------------------------------\n# bitwarden:\n# enabled: false\n# access_token_env: BWS_ACCESS_TOKEN # bootstrap token, sourced from .env\n# project_id: \"\" # UUID of the BSM project to sync\n# server_url: \"\" # \"\" = US Cloud; EU/self-hosted URL otherwise\n# cache_ttl_seconds: 300 # 0 disables fresh caching\n# encrypted_cache: # optional encrypted stale fallback\n# enabled: false\n# max_stale_seconds: 0 # 0 disables stale fallback\n# override_existing: true # BSM values win over existing env\n# auto_install: true # lazy-download bws into ~/.hermes/bin\n#\n# # ---- 1Password (op CLI) -------------------------------------------------\n# onepassword:\n# enabled: false\n# # Map env-var names to op:// secret references. Each is resolved with a\n# # single `op read` at startup.\n# env:\n# OPENAI_API_KEY: \"op://Private/OpenAI/api key\"\n# ANTHROPIC_API_KEY: \"op://Private/Anthropic/credential\"\n# account: \"\" # op --account shorthand; \"\" = default\n# service_account_token_env: OP_SERVICE_ACCOUNT_TOKEN # headless auth; unset = desktop session\n# binary_path: \"\" # \"\" = resolve op via PATH; else absolute path\n# cache_ttl_seconds: 300 # 0 disables BOTH cache layers\n# override_existing: true # resolved values win over existing env\n#\n# # ---- Command helper (any CLI vault) --------------------------------------\n# # Run a user-configured helper that prints KEY=VALUE lines on stdout —\n# # works with any secret store that has a CLI: keepassxc-cli, secret-tool,\n# # pass, gpg, or a script that cats a tmpfs env file. Composes with the\n# # sources above (enable any combination). POSIX-only (needs /bin/sh).\n# # The helper must be fast and NON-interactive (hard timeout, 1 MiB cap);\n# # its stderr is discarded so diagnostics can't leak secret material.\n# command:\n# enabled: false\n# command: \"cat /run/user/1000/hermes-secrets.env\"\n# helper_timeout_seconds: 3\n# override_existing: false # .env/shell win by default\n"}, "f65481674bbbe84bb379b8e8113c111f0a258f56": {"t": 1785767984.8396068, "n": 83900, "h": "cb0040345dc90a1f", "c": "# Hermes Agent CLI Configuration\n# Copy this file to cli-config.yaml and customize as needed.\n# This file configures the CLI behavior. Environment variables in .env take precedence.\n\n# =============================================================================\n# Model Configuration\n# =============================================================================\nmodel:\n # Default model to use (can be overridden with --model flag)\n # Both \"default\" and \"model\" work as the key name here.\n default: \"anthropic/claude-opus-4.6\"\n \n # Inference provider selection:\n # \"auto\" - Auto-detect from credentials (default)\n # \"openrouter\" - OpenRouter (requires: OPENROUTER_API_KEY or OPENAI_API_KEY)\n # \"nous\" - Nous Portal OAuth (requires: hermes auth add nous)\n # \"nous-api\" - Nous Portal API key (requires: NOUS_API_KEY)\n # \"anthropic\" - Direct Anthropic API (requires: ANTHROPIC_API_KEY)\n # \"openai-codex\" - OpenAI Codex (requires: hermes auth)\n # \"copilot\" - GitHub Copilot / GitHub Models (requires: GITHUB_TOKEN)\n # \"gemini\" - Use Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n # \"zai\" - Use z.ai / ZhipuAI GLM models (requires: GLM_API_KEY)\n # \"kimi-coding\" - Kimi / Moonshot AI (requires: KIMI_API_KEY)\n # \"minimax\" - MiniMax global (requires: MINIMAX_API_KEY)\n # \"minimax-cn\" - MiniMax China (requires: MINIMAX_CN_API_KEY)\n # \"huggingface\" - Hugging Face Inference (requires: HF_TOKEN)\n # \"nvidia\" - NVIDIA NIM / build.nvidia.com (requires: NVIDIA_API_KEY)\n # \"xiaomi\" - Xiaomi MiMo (requires: XIAOMI_API_KEY)\n # \"arcee\" - Arcee AI Trinity models (requires: ARCEEAI_API_KEY)\n # \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY — https://ollama.com/settings)\n # \"deepinfra\" - DeepInfra (requires: DEEPINFRA_API_KEY)\n # \"kilocode\" - KiloCode gateway (requires: KILOCODE_API_KEY)\n # \"azure-foundry\" - Microsoft Foundry / Azure OpenAI (API key or Entra ID)\n # \"lmstudio\" - LM Studio local server (optional: LM_API_KEY, defaults to http://127.0.0.1:1234/v1)\n #\n # Local servers (LM Studio, Ollama, vLLM, llama.cpp):\n # \"custom\" - Any other OpenAI-compatible endpoint. Set base_url below.\n # Aliases: \"ollama\", \"vllm\", \"llamacpp\" all map to \"custom\".\n # LM Studio is first-class and uses provider: \"lmstudio\".\n # It works with both no-auth and auth-enabled server modes.\n #\n # Can also be overridden for a single invocation with the --provider flag.\n provider: \"auto\"\n \n # API configuration (falls back to OPENROUTER_API_KEY env var)\n # api_key: \"your-key-here\" # Uncomment to set here instead of .env\n base_url: \"https://openrouter.ai/api/v1\"\n\n # Azure Foundry keyless auth example:\n # provider: \"azure-foundry\"\n # base_url: \"https://<resource>.openai.azure.com/openai/v1\"\n # auth_mode: \"entra_id\" # DefaultAzureCredential: az login, managed identity, workload identity, etc.\n # default: \"gpt-4o\" # Deployment/model name\n # entra:\n # scope: \"https://ai.azure.com/.default\" # Optional; this is the default.\n\n # ── Token limits — two settings, easy to confuse ──────────────────────────\n #\n # context_length: TOTAL context window (input + output tokens combined).\n # Controls when Hermes compresses history and validates requests.\n # Leave unset — Hermes auto-detects the correct value from the provider.\n # Set manually only when auto-detection is wrong (e.g. a local server with\n # a custom num_ctx, or a proxy that doesn't expose /v1/models).\n #\n # context_length: 131072\n #\n # max_tokens: OUTPUT cap — maximum tokens the model may generate per response.\n # Unrelated to how long your conversation history can be.\n # The OpenAI-standard name \"max_tokens\" is a misnomer; Anthropic's native\n # API has since renamed it \"max_output_tokens\" for clarity.\n # Leave unset to use the model's native output ceiling (recommended).\n # Set only if you want to deliberately limit individual response length.\n #\n# max_tokens: 8192\n\n # ── Custom request headers (optional) ─────────────────────────────────────\n #\n # default_headers: extra HTTP headers sent on every request to an\n # OpenAI-compatible endpoint. User values take precedence over the\n # provider/SDK defaults, so this is the supported way to override the\n # OpenAI Python SDK's identifying headers (User-Agent: OpenAI/Python ...,\n # X-Stainless-*) when a custom provider sits behind a gateway/WAF that\n # rejects them — e.g. an upstream that returns \"502 Upstream access\n # forbidden\" for the SDK default User-Agent but accepts a plain one.\n # Applies on the OpenAI wire only (not native Anthropic / Bedrock).\n #\n # default_headers:\n # User-Agent: \"curl/8.7.1\"\n #\n # extra_headers: accepted as an alias of default_headers (merged, with\n # extra_headers winning when both are set) — matches the per-provider\n # extra_headers key below.\n #\n # Per-provider variant: named providers / custom_providers entries accept an\n # extra_headers dict scoped to that endpoint only — for reverse proxies,\n # gateways, or custom auth (e.g. Cloudflare Access service tokens).\n # Merged onto SDK/provider defaults with the entry's values winning.\n # Header values are treated as secrets and are never logged.\n #\n # providers:\n # my-proxy:\n # base_url: \"https://llm.internal.example.com/v1\"\n # key_env: \"MY_PROXY_API_KEY\"\n # extra_headers:\n # CF-Access-Client-Id: \"xxxx.access\"\n # CF-Access-Client-Secret: \"${CF_ACCESS_SECRET}\"\n # X-Client-Name: \"hermes-agent\"\n\n# Named provider overrides (optional)\n# Use this for per-provider request timeouts, non-stream stale timeouts,\n# and per-model exceptions.\n# Applies to the primary turn client on every api_mode (OpenAI-wire, native\n# Anthropic, and Anthropic-compatible providers), the fallback chain, and\n# client rebuilds during credential rotation. For OpenAI-wire chat\n# completions (streaming and non-streaming) the configured value is also\n# used as the per-request ``timeout=`` kwarg so it wins over the legacy\n# HERMES_API_TIMEOUT env var (which still applies when no config is set).\n# ``stale_timeout_seconds`` controls the non-streaming stale-call detector and\n# wins over the legacy HERMES_API_CALL_STALE_TIMEOUT env var. Leaving these\n# unset keeps the legacy defaults (HERMES_API_TIMEOUT=1800s,\n# HERMES_API_CALL_STALE_TIMEOUT=90s, native Anthropic 900s). The\n# implicit non-stream stale detector is auto-disabled for local endpoints\n# and can scale upward for very large contexts.\n#\n# Not currently wired for AWS Bedrock (bedrock_converse + AnthropicBedrock\n# SDK paths) — those use boto3 with its own timeout configuration.\n#\n# providers:\n# ollama-local:\n# request_timeout_seconds: 300 # Longer timeout for local cold-starts\n# stale_timeout_seconds: 900 # Explicitly re-enable stale detection on local endpoints\n# anthropic:\n# request_timeout_seconds: 30 # Fast-fail cloud requests\n# models:\n# claude-opus-4.6:\n# timeout_seconds: 600 # Longer timeout for extended-thinking Opus calls\n# openai-codex:\n# models:\n# gpt-5.4:\n# stale_timeout_seconds: 1800 # Longer non-stream stale timeout for slow large-context turns\n\n# =============================================================================\n# OpenRouter Provider Routing (only applies when using OpenRouter)\n# =============================================================================\n# Control how requests are routed across providers on OpenRouter.\n# See: https://openrouter.ai/docs/guides/routing/provider-selection\n#\n# provider_routing:\n# # Sort strategy: \"price\" (default), \"throughput\", or \"latency\"\n# # Append :nitro to model name for a shortcut to throughput sorting.\n# sort: \"throughput\"\n#\n# # Only allow these providers (provider slugs from OpenRouter)\n# # only: [\"anthropic\", \"google\"]\n#\n# # Skip these providers entirely\n# # ignore: [\"deepinfra\", \"fireworks\"]\n#\n# # Try providers in this order (overrides default load balancing)\n# # order: [\"anthropic\", \"google\", \"together\"]\n#\n# # Require providers to support all parameters in your request\n# # require_parameters: true\n#\n# # Data policy: \"allow\" (default) or \"deny\" to exclude providers that may store data\n# # data_collection: \"deny\"\n\n# =============================================================================\n# OpenRouter Response Caching (only applies when using OpenRouter)\n# =============================================================================\n# Cache identical API responses at the OpenRouter edge for free instant replays.\n# When enabled, identical requests (same model, messages, parameters) return\n# cached responses with zero billing. Separate from Anthropic prompt caching.\n# See: https://openrouter.ai/docs/guides/features/response-caching\n#\n# openrouter:\n# response_cache: true # Enable response caching (default: true)\n# response_cache_ttl: 300 # Cache TTL in seconds, 1-86400 (default: 300)\n\n# =============================================================================\n# Git Worktree Isolation\n# =============================================================================\n# When enabled, each CLI session creates an isolated git worktree so multiple\n# agents can work on the same repo concurrently without file collisions.\n# Equivalent to always passing --worktree / -w on the command line.\n#\n# worktree: true # Always create a worktree when in a git repo\n# worktree: false # Default — only create when -w flag is passed\n#\n# By default a new worktree branches from the freshly-fetched remote tip\n# (the current branch's upstream, else the remote's default branch) so it\n# starts current with the project instead of from the local clone's\n# (possibly stale) HEAD. Set worktree_sync: false to branch from local HEAD\n# instead — useful when offline or when you deliberately want the clone's\n# exact current state as the base.\n#\n# worktree_sync: true # Default — branch from the fetched remote tip\n# worktree_sync: false # Branch from local HEAD (offline / pinned base)\n\n# =============================================================================\n# Terminal Tool Configuration\n# =============================================================================\n# Choose ONE of the following terminal configurations by uncommenting it.\n# The terminal tool executes commands in the specified environment.\n\n# -----------------------------------------------------------------------------\n# OPTION 1: Local execution (default)\n# Commands run directly on your machine in the current directory\n# -----------------------------------------------------------------------------\n# Working directory behavior:\n# - CLI (`hermes` command): Uses \".\" (current directory where you run hermes)\n# - Gateway/messaging/cron: Uses terminal.cwd here; legacy .env cwd values are deprecated\nterminal:\n backend: \"local\"\n cwd: \".\" # For local backend: \".\" = current directory. Ignored for remote backends unless a backend documents otherwise.\n timeout: 180\n # HOME policy for tool subprocesses:\n # auto - default: host uses your real HOME; containers use HERMES_HOME/home\n # real - force your real OS-user HOME\n # profile - force HERMES_HOME/home for strict per-profile CLI config isolation\n home_mode: \"auto\"\n docker_mount_cwd_to_workspace: false # SECURITY: off by default. Opt in to mount the launch cwd into Docker /workspace.\n lifetime_seconds: 300\n # sudo_password: \"hunter2\" # Optional: pipe a sudo password via sudo -S. SECURITY WARNING: plaintext.\n # sudo_password: \"\" # Explicit empty password: try empty and never open the interactive sudo prompt.\n\n# -----------------------------------------------------------------------------\n# OPTION 2: SSH remote execution\n# Commands run on a remote server - agent code stays local (sandboxed)\n# Great for: keeping agent isolated from its own code, using powerful remote hardware\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"ssh\"\n# cwd: \"/home/myuser/project\" # Path on the REMOTE server\n# timeout: 180\n# lifetime_seconds: 300\n# ssh_host: \"my-server.example.com\"\n# ssh_user: \"myuser\"\n# ssh_port: 22\n# ssh_key: \"~/.ssh/id_rsa\" # Optional - uses ssh-agent if not specified\n\n# -----------------------------------------------------------------------------\n# OPTION 3: Docker container\n# Commands run in an isolated Docker container\n# Great for: reproducible environments, testing, isolation\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"docker\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /)\n# timeout: 180\n# lifetime_seconds: 300\n# docker_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# docker_mount_cwd_to_workspace: true # Explicit opt-in: mount your launch cwd into /workspace\n# # Optional: run the container as your host user's uid:gid so files written\n# # into bind-mounted dirs are owned by you, not root. Drops SETUID/SETGID\n# # caps too since no gosu privilege drop is needed. Leave off if your\n# # chosen docker_image expects to start as root.\n# docker_run_as_host_user: true\n# # Optional: explicitly forward selected env vars into Docker.\n# # These values come from your current shell first, then ~/.hermes/.env.\n# # Warning: anything forwarded here is visible to commands run in the container.\n# docker_forward_env:\n# - \"GITHUB_TOKEN\"\n# - \"NPM_TOKEN\"\n# # Optional: extra flags passed verbatim to docker run (appended after security defaults).\n# # Useful for adding capabilities (e.g. apt installs needing SETUID) or custom options.\n# # Example: add a Linux capability not included by default\n# # docker_extra_args:\n# # - \"--cap-add\"\n# # - \"SETUID\"\n\n# -----------------------------------------------------------------------------\n# OPTION 4: Singularity/Apptainer container\n# Commands run in a Singularity container (common in HPC environments)\n# Great for: HPC clusters, shared compute environments\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"singularity\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# singularity_image: \"docker://nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 5: Modal cloud execution\n# Commands run on Modal's cloud infrastructure\n# Great for: GPU access, scalable compute, serverless execution\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"modal\"\n# cwd: \"/workspace\" # Path INSIDE the sandbox (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# modal_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 6: Daytona cloud execution\n# Commands run in Daytona cloud sandboxes\n# Great for: Cloud dev environments, persistent workspaces, team collaboration\n# Requires: pip install daytona, DAYTONA_API_KEY env var\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"daytona\"\n# cwd: \"~\"\n# timeout: 180\n# lifetime_seconds: 300\n# daytona_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# container_disk: 10240 # Daytona max is 10GB per sandbox\n\n#\n# --- Container resource limits (docker, singularity, modal, daytona -- ignored for local/ssh) ---\n# These settings apply to all container backends. They control the resources\n# allocated to the sandbox and whether its filesystem persists across sessions.\n container_cpu: 1 # CPU cores\n container_memory: 5120 # Memory in MB (5120 = 5GB)\n container_disk: 51200 # Disk in MB (51200 = 50GB)\n container_persistent: true # Persist filesystem across sessions (false = ephemeral)\n\n# -----------------------------------------------------------------------------\n# SUDO SUPPORT (works with ALL backends above)\n# -----------------------------------------------------------------------------\n# Add sudo_password to any terminal config above to enable sudo commands.\n# The password is piped via `sudo -S`. Works with local, ssh, docker, etc.\n#\n# SECURITY WARNING: Password stored in plaintext!\n#\n# INTERACTIVE PROMPT: If sudo_password is unset and the CLI is running,\n# you'll be prompted to enter your password when sudo is needed:\n# - 45-second timeout (auto-skips if no input)\n# - Press Enter to skip (command fails gracefully)\n# - Password is hidden while typing\n# - Password is cached for the session\n#\n# EMPTY PASSWORDS: Setting sudo_password to an explicit empty string is different\n# from leaving it unset. Hermes will try an empty password via `sudo -S` and\n# will not open the interactive prompt. This is useful for passwordless sudo,\n# Touch ID sudo setups, and environments where prompting is just noise.\n#\n# ALTERNATIVES:\n# - SSH backend: Configure passwordless sudo on the remote server\n# - Containers: Run as root inside the container (no sudo needed)\n# - Local: Configure /etc/sudoers for specific commands\n#\n# Example (add to your terminal section):\n# sudo_password: \"your-password-here\"\n\n# =============================================================================\n# Security Scanning (tirith)\n# =============================================================================\n# Optional pre-exec command security scanning via tirith.\n# Detects homograph URLs, pipe-to-shell, terminal injection, env manipulation.\n# Install: brew install sheeki03/tap/tirith\n# Docs: https://github.com/sheeki03/tirith\n#\n# security:\n# tirith_enabled: true # Enable/disable tirith scanning\n# tirith_path: \"tirith\" # Path to tirith binary (supports ~ expansion)\n# tirith_timeout: 5 # Scan timeout in seconds\n# tirith_fail_open: true # Allow commands if tirith unavailable\n\n# =============================================================================\n# Browser Tool Configuration\n# =============================================================================\nbrowser:\n # Inactivity timeout in seconds - browser sessions are automatically closed\n # after this period of no activity between agent loops (default: 120 = 2 minutes)\n inactivity_timeout: 120\n\n# =============================================================================\n# Tool Loop Guardrails\n# =============================================================================\n# Soft warnings are enabled by default. They append guidance to repeated failed\n# or non-progressing tool results but still let the tool execute. Hard stops are\n# opt-in circuit breakers for autonomous/cron sessions where stopping a loop is\n# preferable to spending the full iteration budget.\ntool_loop_guardrails:\n warnings_enabled: true\n hard_stop_enabled: false\n warn_after:\n exact_failure: 2\n same_tool_failure: 3\n idempotent_no_progress: 2\n hard_stop_after:\n exact_failure: 5\n same_tool_failure: 8\n idempotent_no_progress: 5\n\n# =============================================================================\n# Context Compression (Auto-shrinks long conversations)\n# =============================================================================\n# When conversation approaches model's context limit, middle turns are\n# automatically summarized to free up space while preserving important context.\n#\n# HOW IT WORKS:\n# 1. Tracks actual token usage from API responses (not estimates)\n# 2. When prompt_tokens >= threshold% of model's context_length, triggers compression\n# 3. Protects first 3 turns (system prompt, initial request, first response)\n# 4. Protects last N turns (default 20 messages = ~10 full turns of recent context)\n# 5. Summarizes middle turns using a fast/cheap model\n# 6. Inserts summary as a user message, continues conversation seamlessly\n#\n# Post-compression tail budget is target_ratio × threshold × context_length:\n# 200K context, threshold 0.50, ratio 0.20 → 20K tokens of recent tail preserved\n# 1M context, threshold 0.50, ratio 0.20 → 100K tokens of recent tail preserved\n#\ncompression:\n # Enable automatic context compression (default: true)\n # Set to false if you prefer to manage context manually or want errors on overflow\n enabled: true\n\n # Opt-in compression progress notices on chat platforms (default: false).\n # By design, routine automatic compression is SILENT on human-facing chat\n # gateways (Telegram, Discord, Slack, ...) — it happens in the background\n # with server-side logging only. Set true to also deliver the routine\n # progress statuses (compacting started, preflight/pre-API compression,\n # idle compaction, retry progress, and the compaction-complete notice) to\n # chat platforms. Unrelated operational noise (auxiliary model failures,\n # provider retry chatter) stays suppressed either way, and compression\n # FAILURE notices + manual /compress feedback are always visible\n # regardless of this setting. (#52995)\n progress_notices: false\n \n # Trigger compression at this % of model's context limit (default: 0.50 = 50%)\n # Lower values = more aggressive compression, higher values = compress later\n # Models with context windows below 512K are floored at 0.75 (raise-only) so\n # compaction doesn't fire with half the window still free; set above 0.75 to override.\n threshold: 0.50\n\n # Per-model threshold overrides: keys are substring-matched against the model\n # name (longest match wins). Useful when some models need different compaction\n # points — e.g. a 1M-context model can compress later (0.30) while a 128K\n # model needs to compress earlier (0.60). The small-context floor (75% for\n # <512K models) still applies on top of per-model overrides.\n # model_thresholds:\n # \"glm-5.2\": 0.40\n # \"claude-sonnet\": 0.35\n # \"gpt-5\": 0.30\n\n # Optional absolute token cap for the compression trigger (default: null = disabled).\n # When set, compression fires at the LOWER of the ratio-based threshold and this\n # absolute token count — first-fires-wins. It never fires later than this count\n # regardless of which model is active (useful when switching between models with\n # very different context windows). Clamped to the model's context length at\n # apply-time, so a cap above the window is a no-op (ratio-based threshold wins).\n # Survives model switches and fallback activations.\n # threshold_tokens: 200000\n\n # Existing Codex gpt-5.5 behavior: raise Hermes' compaction trigger to 85%\n # for the ChatGPT Codex OAuth route. Set false to opt back down to threshold.\n codex_gpt55_autoraise: true\n\n # Fraction of the threshold to preserve as recent tail (default: 0.20 = 20%)\n # e.g. 20% of 50% threshold = 10% of total context kept as recent messages.\n # Summary output is separately capped at 12K tokens (Gemini output limit).\n # Range: 0.10 - 0.80\n target_ratio: 0.20\n\n # Number of most-recent messages to always preserve (default: 20 ≈ 10 full turns)\n # Higher values keep more recent conversation intact at the cost of more aggressive\n # compression of older turns.\n protect_last_n: 20\n\n # Minimum number of REAL (actionable) user messages guaranteed to survive in\n # the uncompressed tail (default: 1 = the existing single last-user anchor,\n # behavior-preserving). Raise to e.g. 3 to keep the last 3 real user turns\n # verbatim even when bulky tool outputs fill the tail token budget — blank\n # platform echoes, compaction handoffs, and synthetic continuation rows never\n # count toward N. The tail can exceed the token budget when this pulls the\n # cut back; the guarantee wins over the budget by design.\n min_tail_user_messages: 1\n\n # Compression retry rounds before a turn gives up with \"max compression\n # attempts reached\" (default: 3, same as the previous hardcoded value).\n # Raise (e.g. 6) for tool-schema-heavy sessions where 3 rounds cannot bring\n # the request estimate under the threshold. Validated >= 1, hard cap 10.\n max_attempts: 3\n\n # Codex app-server (codex CLI runtime) thread-compaction mode. The codex\n # agent owns the real thread context on this runtime, so Hermes' summarizer\n # cannot shrink it — compaction goes through the app server instead.\n # native = let Codex decide when to compact its own thread (default)\n # hermes = let Hermes threshold trigger Codex thread/compact/start\n # off = Hermes will not auto-trigger compaction; Codex may still compact natively\n codex_app_server_auto: native\n\n # Number of non-system messages to protect at the head of the transcript, in\n # ADDITION to the system prompt (which is always implicitly protected).\n # Head messages are NEVER summarized — they survive every compression\n # indefinitely. This gives stable early context for short/medium sessions,\n # but in long-running sessions that rely on rolling compaction the pinned\n # opening turns may not match how you want the session framed over time.\n # Set to 0 to preserve ONLY the system prompt (plus the rolling summary\n # and recent tail) — the cleanest configuration for long-running sessions.\n # Default 3 preserves the system prompt plus the first three non-system\n # head messages, matching the pre-feature behaviour.\n protect_first_n: 3\n\n # Idle compaction (default: 0 = disabled). When > 0, a session that resumes\n # after at least this many seconds of inactivity compacts its accumulated\n # history up front, before the first reply, so a long-lived thread you come\n # back to later doesn't re-read its full stale context on every turn.\n # Time-based, so it complements (does not replace) the size-based `threshold`\n # above. It is skipped when the context is already small (at or below the\n # post-compression target = threshold × target_ratio), so it never wastes a\n # summarization on a short idle thread. Example: 1800 = compact after 30 min idle.\n idle_compact_after_seconds: 0\n\n # Proactive tool-result prune (default: 0 = disabled). Opt-in token trigger\n # for a deterministic, no-LLM prune of OLD tool-result payloads, run\n # independently of `threshold` above. On large-window models (512K/1M) the\n # ratio threshold rarely fires, so bulky tool outputs (terminal dumps, file\n # reads, web extracts) ride along in history and get re-billed every turn.\n # When re-sent history exceeds this many tokens, the prune dedupes identical\n # results, summarizes older oversized ones, and truncates large tool-call\n # arguments — protecting the most recent `protect_last_n` messages and never\n # calling the model. Try 48000 to enable. Built-in compressor engine only;\n # other context engines inherit a safe no-op.\n # NOTE: a committed prune rewrites already-sent history, which invalidates\n # the provider's prompt-cache prefix — the min_reclaim gate below keeps\n # those cache breaks episodic (like a compression boundary) instead of\n # per-turn.\n proactive_prune_tokens: 0\n\n # The prune's summarize pass only touches tool results larger than this many\n # characters (clamped to >= 200 so a generated summary can't be\n # re-summarized). Default 8000.\n proactive_prune_min_result_chars: 8000\n\n # A proactive prune only COMMITS when it reclaims at least this many tokens\n # (measured on the pruned output). This is the prompt-cache hysteresis gate:\n # one meaningful, amortized cache break per batch of stale tool output\n # instead of a tiny break on every tool iteration. 0 = commit any non-zero\n # prune. Default 4096.\n proactive_prune_min_reclaim_tokens: 4096\n\n # To pin a specific model/provider for compression summaries, use the\n # auxiliary section below (auxiliary.compression.provider / model).\n\n# =============================================================================\n# Anthropic prompt caching TTL\n# =============================================================================\n# When prompt caching is active (Claude via OpenRouter or native Anthropic),\n# Anthropic supports two TTL tiers for cached prefixes: \"5m\" (default) and\n# \"1h\". Other values are ignored and \"5m\" is used.\n#\nprompt_caching:\n cache_ttl: \"5m\" # use \"1h\" for long sessions with pauses between turns\n\n# =============================================================================\n# Auxiliary Models (Advanced — Experimental)\n# =============================================================================\n# Hermes uses lightweight \"auxiliary\" models for side tasks: image analysis,\n# browser screenshot analysis, web page summarization, TTS audio-tag insertion,\n# and context compression.\n#\n# By default these use Gemini Flash via OpenRouter or Nous Portal and are\n# auto-detected from your credentials. You do NOT need to change anything\n# here for normal usage.\n#\n# WARNING: Overriding these with providers other than OpenRouter or Nous Portal\n# is EXPERIMENTAL and may not work. Not all models/providers support vision,\n# produce usable summaries, or accept the same API format. Change at your own\n# risk — if things break, reset to \"auto\" / empty values.\n#\n# Each task has its own provider + model pair so you can mix providers.\n# For example: OpenRouter for vision (needs multimodal), but your main\n# local endpoint for compression (just needs text).\n#\n# Provider options:\n# \"auto\" - Best available: OpenRouter → Nous Portal → main endpoint (default)\n# \"openrouter\" - Force OpenRouter (requires OPENROUTER_API_KEY)\n# \"nous\" - Force Nous Portal (requires: hermes auth add nous)\n# \"gemini\" - Force Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n# \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY)\n# \"codex\" - Force Codex OAuth (requires: hermes model → Codex).\n# Uses gpt-5.3-codex which supports vision.\n# \"main\" - Use your custom endpoint (OPENAI_BASE_URL + OPENAI_API_KEY).\n# Works with OpenAI API, local models, or any OpenAI-compatible\n# endpoint. Also falls back to Codex OAuth and API-key providers.\n#\n# Model: leave empty to use the provider's default. When empty, OpenRouter\n# uses \"google/gemini-3-flash-preview\" and Nous uses \"gemini-3-flash\".\n# Other providers pick a sensible default automatically.\n#\n# auxiliary:\n# # Image analysis: vision_analyze tool + browser screenshots\n# vision:\n# provider: \"auto\"\n# model: \"\" # e.g. \"google/gemini-2.5-flash\", \"openai/gpt-4o\"\n# timeout: 30 # LLM API call timeout (seconds)\n# download_timeout: 30 # Image HTTP download timeout (seconds)\n# # Increase for slow connections or self-hosted image servers\n# reasoning_effort: \"\" # Per-task thinking level: none, minimal, low, medium,\n# # high, xhigh, max, ultra. Empty = provider default.\n# # Works on every auxiliary task block (vision,\n# # web_extract, compression, title_generation, curator,\n# # background_review, moa_reference, ...). Example: run\n# # compression at \"low\" and vision at \"none\" to cut\n# # side-task latency/cost on reasoning models.\n#\n# # Web page scraping / summarization + browser page text extraction\n# web_extract:\n# provider: \"auto\"\n# model: \"\"\n# reasoning_effort: \"low\"\n#\n# # Gemini 3.1 TTS hidden audio-tag insertion\n# tts_audio_tags:\n# provider: \"auto\" # empty model = your main chat model\n# model: \"\"\n# timeout: 30\n#\n# # Automatic session title generation after the first exchange\n# title_generation:\n# enabled: true # set false to disable auto-title generation\n# provider: \"auto\"\n# model: \"\"\n# timeout: 30\n# language: \"\" # empty = match the user's language; or e.g. \"English\"\n#\n# # Session search — summarizes matching past sessions\n# session_search:\n# provider: \"auto\"\n# model: \"\"\n# timeout: 30\n# max_concurrency: 3 # Limit parallel summaries to reduce request-burst 429s\n# extra_body: {} # Provider-specific OpenAI-compatible request fields\n# # Example for providers that support request-body\n# # reasoning controls:\n# # extra_body:\n# # enable_thinking: false\n# # Some vLLM/Qwen deployments expect this nested:\n# # extra_body:\n# # chat_template_kwargs:\n# # enable_thinking: false\n\n# =============================================================================\n# Persistent Memory\n# =============================================================================\n# Bounded curated memory injected into the system prompt every session.\n# Two stores: MEMORY.md (agent's notes) and USER.md (user profile).\n# Character limits keep the memory small and focused. The agent manages\n# pruning -- when at the limit, it must consolidate or replace entries.\n# Disabled by default in batch_runner.\n#\nmemory:\n # Agent's personal notes: environment facts, conventions, things learned\n memory_enabled: true\n \n # User profile: preferences, communication style, expectations\n user_profile_enabled: true\n \n # Character limits (~2.75 chars per token, model-independent)\n memory_char_limit: 2200 # ~800 tokens\n user_char_limit: 1375 # ~500 tokens\n\n # Periodic memory nudge: remind the agent to consider saving memories\n # every N user turns. Set to 0 to disable. Only active when memory is enabled.\n nudge_interval: 10 # Nudge every 10 user turns (0 = disabled)\n\n # Memory flush: give the agent one turn to save memories before context is\n # lost (compression, /new, /reset, exit). Set to 0 to disable.\n # For exit/reset, only fires if the session had at least this many user turns.\n flush_min_turns: 6 # Min user turns to trigger flush on exit/reset (0 = disabled)\n\n# =============================================================================\n# Session Reset Policy (Messaging Platforms)\n# =============================================================================\n# Controls when messaging sessions (Telegram, Discord, WhatsApp, Slack) are\n# automatically cleared. Default is \"none\": sessions never auto-reset —\n# conversation context lives until you /reset or /new manually, or context\n# compression kicks in. Opt in to automatic resets if you prefer sessions to\n# clear on a schedule (long-lived context increases API cost per message,\n# though prompt caching and compression keep this manageable).\n#\n# When an automatic reset triggers, the agent first saves important\n# information to its persistent memory — but the conversation context is\n# wiped. The agent starts fresh but retains learned facts via its memory\n# system.\n#\n# Modes:\n# \"none\" - Never auto-reset (default); context lives until /reset or compression\n# \"idle\" - Reset after N minutes of inactivity\n# \"daily\" - Reset at a fixed hour each day\n# \"both\" - Reset on EITHER inactivity timeout or daily boundary\n#\nsession_reset:\n mode: none # \"none\", \"idle\", \"daily\", or \"both\"\n idle_minutes: 1440 # Inactivity timeout in minutes (used by \"idle\"/\"both\")\n at_hour: 4 # Daily reset hour, 0-23 local time (used by \"daily\"/\"both\")\n\n# Maximum number of simultaneously active chat sessions across CLI, TUI,\n# dashboard chat, and messaging gateway. Set to null, 0, or omit to allow\n# unlimited concurrent sessions. When the limit is reached, new sessions get a\n# clean error while existing active sessions keep their normal behavior. This\n# top-level key takes precedence over gateway.max_concurrent_sessions. The cap\n# is a best-effort single-host/profile runtime guard; Hermes fails open if the\n# local runtime lease registry cannot be read or locked.\nmax_concurrent_sessions: null\n\n# When true, group/channel chats use one session per participant when the platform\n# provides a user ID. This is the secure default and prevents users in the same\n# room from sharing context, interrupts, and token costs. Set false only if you\n# explicitly want one shared \"room brain\" per group/channel.\ngroup_sessions_per_user: true\n\n# ─────────────────────────────────────────────────────────────────────────────\n# API Server — per-client model routing\n# ─────────────────────────────────────────────────────────────────────────────\n# Route different API clients to different models/providers on a single\n# Hermes deployment. Clients choose a backend by sending a specific string\n# as the OpenAI ``model`` field. Unmapped model values fall back to the\n# global model configured in the ``model:`` section above, and an explicit\n# session /model override always wins over a route.\n#\n# Configure via the ``platforms.api_server.extra.model_routes`` gateway\n# config block:\n#\n# platforms:\n# api_server:\n# enabled: true\n# extra:\n# key: \"your-api-server-secret\"\n# model_routes:\n# # Xiaozhi clients send model=\"minimax-m2\" → routed to MiniMax via OpenRouter\n# minimax-m2:\n# model: \"minimax/minimax-m1\"\n# provider: \"openrouter\" # optional — overrides global provider\n# # api_key: \"sk-...\" # optional — per-route UPSTREAM provider\n# # key (NOT caller auth; never logged)\n# # base_url: \"https://...\" # optional — per-route base URL\n# # GPT clients keep their own alias\n# gpt-5:\n# model: \"openai/gpt-5\"\n# provider: \"openrouter\"\n#\n# Configured aliases are automatically listed by GET /v1/models so clients\n# can discover them without manual coordination. Caller authentication is\n# unchanged: every request still authenticates with the global API server\n# key (``extra.key`` / API_SERVER_KEY).\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Gateway Streaming\n# ─────────────────────────────────────────────────────────────────────────────\n# Stream tokens to messaging platforms in real-time. The bot sends a message\n# on first token, then progressively edits it as more tokens arrive.\n# Disabled by default — enable to try the streaming UX on Telegram/Discord/Slack.\n# For Telegram, partial edits are sent as plain text and only the final edit uses MarkdownV2.\nstreaming:\n enabled: false\n # transport: edit # \"edit\" = progressive editMessageText\n # edit_interval: 0.3 # seconds between message edits\n # buffer_threshold: 40 # chars before forcing an edit flush\n # cursor: \" ▉\" # cursor shown during streaming\n\n# =============================================================================\n# Skills Configuration\n# =============================================================================\n# Skills are reusable procedures the agent can load and follow. The agent can\n# also create new skills after completing complex tasks.\n#\nskills:\n # Nudge the agent to create skills after complex tasks.\n # Every N tool-calling iterations, remind the model to consider saving a skill.\n # Set to 0 to disable.\n creation_nudge_interval: 15\n\n # External skill directories — share skills across tools/agents without\n # copying them into ~/.hermes/skills/. Each path is expanded (~ and ${VAR})\n # and resolved to an absolute path. External dirs are read-only: skill\n # creation always writes to ~/.hermes/skills/. Local skills take precedence\n # when names collide.\n # external_dirs:\n # - ~/.agents/skills\n # - /home/shared/team-skills\n\n# =============================================================================\n# Agent Behavior\n# =============================================================================\nagent:\n # Maximum tool-calling iterations per conversation (default: 500)\n # Higher = more room for complex tasks, but costs more tokens\n # Recommended: 20-30 for focused tasks, 50-100 for open exploration\n max_turns: 500\n\n # Inactivity timeout for gateway agent runs (seconds, 0 = unlimited).\n # The agent can run indefinitely when actively calling tools or receiving\n # API responses. Only fires after the agent has been idle for this duration.\n # gateway_timeout: 1800\n\n # Staged warning: send a warning before escalating to full timeout.\n # Fires once per run when inactivity reaches this threshold (seconds).\n # Set to 0 to disable the warning.\n # gateway_timeout_warning: 900\n\n # Graceful drain timeout for gateway stop/restart (seconds).\n # Default 0 = no drain: a restart interrupts in-flight agents immediately,\n # cleans up, and exits. Set a positive value only if you want a grace\n # window on /restart, and keep it well under systemd's TimeoutStopSec.\n # restart_drain_timeout: 0\n\n # Upper bound (seconds) a submitted prompt waits for the deferred agent\n # build (MCP discovery, model metadata, skills scan) before failing with a\n # visible error. The wait is patient — the message is delivered as soon as\n # the build completes, and a progress notice is shown past 30s — so this cap\n # only fires on a genuinely hung build. Raise it for deployments with many\n # slow or unreachable MCP servers. Default 600.\n # build_wait_timeout: 600\n\n # Max app-level retry attempts for API errors (connection drops, provider\n # timeouts, 5xx, etc.) before the agent surfaces the failure. Lower this\n # to 1 if you use fallback providers and want fast failover on flaky\n # primaries (default 3). The OpenAI SDK does its own low-level retries\n # underneath this wrapper — this is the Hermes-level loop.\n # api_max_retries: 3\n\n # After the agent edits code without fresh passing verification, nudge it to\n # verify before finishing. The default \"auto\" enables it on interactive\n # coding surfaces (CLI, TUI, desktop) and programmatic callers, and disables\n # it on conversational messaging surfaces (Telegram, Discord, etc.) where the\n # verification summary would reach a human as chat noise. Set true or false to\n # force it on or off; the HERMES_VERIFY_ON_STOP env var (1/0) takes precedence.\n # verify_on_stop: auto\n\n # Standing operator instructions for the coding posture (when Hermes is in a\n # code workspace). Appended to the coding brief as an extra system block, so\n # you can pin project-wide workflow rules without editing the shipped brief.\n # Accepts a string or a list of strings. Takes effect next session.\n # coding_instructions:\n # - \"For UI work, don't run tsc/lint until I approve the look.\"\n # - \"Clean the diff before you commit and push.\"\n\n # When verify-on-stop finds edited code without fresh verification evidence,\n # append guidance for creative UI work (avoid broad tsc/lint/test before visual\n # approval) and clean-diff expectations. Set false to keep that nudge terse.\n # verify_guidance: true\n\n # A `pre_verify` hook (plugin or shell, see Event Hooks docs) can keep the\n # agent going one more turn to verify/clean before finishing. This caps how\n # many times one turn may be nudged to continue, so a hook can't trap the loop.\n # Default 3.\n # max_verify_nudges: 3\n\n # Enable verbose logging\n verbose: false\n \n # Reasoning effort level (OpenRouter and Nous Portal)\n # Controls how much \"thinking\" the model does before responding.\n # Options: \"xhigh\" (max), \"high\", \"medium\", \"low\", \"minimal\", \"none\" (disable)\n reasoning_effort: \"medium\"\n \n # Per-model reasoning effort overrides (optional dict)\n # Key: any sensible model spelling works (exact, dots↔dashes interchangeable,\n # provider prefix optional). First match wins.\n # Value: reasoning effort level (same options as reasoning_effort)\n # Override the global reasoning_effort for that specific model.\n # NOTE: no `hermes config set` support for this key -- edit YAML directly.\n # reasoning_overrides:\n # \"openrouter/anthropic/claude-opus-4.5\": \"xhigh\"\n # \"openai/gpt-5\": \"low\"\n # \"claude-opus-4.6\": \"high\" # bare model name also works\n # \"deepseek/deepseek-v4-pro\": \"xhigh\" # dots and dashes are interchangeable\n reasoning_overrides: {}\n \n # Predefined personalities (use with /personality command)\n personalities:\n helpful: \"You are a helpful, friendly AI assistant.\"\n concise: \"You are a concise assistant. Keep responses brief and to the point.\"\n technical: \"You are a technical expert. Provide detailed, accurate technical information.\"\n creative: \"You are a creative assistant. Think outside the box and offer innovative solutions.\"\n teacher: \"You are a patient teacher. Explain concepts clearly with examples.\"\n kawaii: \"You are a kawaii assistant! Use cute expressions like (◕‿◕), ★, ♪, and ~! Add sparkles and be super enthusiastic about everything! Every response should feel warm and adorable desu~! ヽ(>∀<☆)ノ\"\n catgirl: \"You are Neko-chan, an anime catgirl AI assistant, nya~! Add 'nya' and cat-like expressions to your speech. Use kaomoji like (=^・ω・^=) and ฅ^•ﻌ•^ฅ. Be playful and curious like a cat, nya~!\"\n pirate: \"Arrr! Ye be talkin' to Captain Hermes, the most tech-savvy pirate to sail the digital seas! Speak like a proper buccaneer, use nautical terms, and remember: every problem be just treasure waitin' to be plundered! Yo ho ho!\"\n shakespeare: \"Hark! Thou speakest with an assistant most versed in the bardic arts. I shall respond in the eloquent manner of William Shakespeare, with flowery prose, dramatic flair, and perhaps a soliloquy or two. What light through yonder terminal breaks?\"\n surfer: \"Duuude! You're chatting with the chillest AI on the web, bro! Everything's gonna be totally rad. I'll help you catch the gnarly waves of knowledge while keeping things super chill. Cowabunga! 🤙\"\n noir: \"The rain hammered against the terminal like regrets on a guilty conscience. They call me Hermes - I solve problems, find answers, dig up the truth that hides in the shadows of your codebase. In this city of silicon and secrets, everyone's got something to hide. What's your story, pal?\"\n uwu: \"hewwo! i'm your fwiendwy assistant uwu~ i wiww twy my best to hewp you! *nuzzles your code* OwO what's this? wet me take a wook! i pwomise to be vewy hewpful >w<\"\n philosopher: \"Greetings, seeker of wisdom. I am an assistant who contemplates the deeper meaning behind every query. Let us examine not just the 'how' but the 'why' of your questions. Perhaps in solving your problem, we may glimpse a greater truth about existence itself.\"\n hype: \"YOOO LET'S GOOOO!!! 🔥🔥🔥 I am SO PUMPED to help you today! Every question is AMAZING and we're gonna CRUSH IT together! This is gonna be LEGENDARY! ARE YOU READY?! LET'S DO THIS! 💪😤🚀\"\n\n# =============================================================================\n# Toolsets\n# =============================================================================\n# Control which tools the agent has access to.\n# Use `hermes tools` to interactively enable/disable tools per platform.\n\n# =============================================================================\n# Platform Toolsets (per-platform tool configuration)\n# =============================================================================\n# Override which toolsets are available on each platform.\n# If a platform isn't listed here, its built-in default is used.\n#\n# You can use EITHER:\n# - A preset like \"hermes-cli\" or \"hermes-telegram\" (curated tool set)\n# - A list of individual toolsets to compose your own (see list below)\n#\n# Supported platform keys: cli, telegram, discord, whatsapp, slack, qqbot, teams, google_chat\n#\n# Examples:\n#\n# # Use presets (same as defaults):\n# platform_toolsets:\n# cli: [hermes-cli]\n# telegram: [hermes-telegram]\n#\n# # Custom: give Telegram only web + terminal + file + planning:\n# platform_toolsets:\n# telegram: [web, terminal, file, todo]\n#\n# # Custom: CLI without browser or image gen:\n# platform_toolsets:\n# cli: [web, terminal, file, skills, todo, tts, cronjob]\n#\n# # Restrictive: Discord gets read-only tools only:\n# platform_toolsets:\n# discord: [web, vision, skills, todo]\n#\n# If not set, defaults are:\n# cli: hermes-cli (everything + cronjob management)\n# telegram: hermes-telegram (terminal, file, web, vision, image, tts, browser, skills, todo, cronjob, messaging)\n# discord: hermes-discord (same as telegram)\n# whatsapp: hermes-whatsapp (same as telegram)\n# slack: hermes-slack (same as telegram)\n# signal: hermes-signal (same as telegram)\n# homeassistant: hermes-homeassistant (same as telegram)\n# qqbot: hermes-qqbot (same as telegram)\n# teams: hermes-teams (same as telegram)\n# google_chat: hermes-google_chat (same as telegram)\n#\nplatform_toolsets:\n cli: [hermes-cli]\n telegram: [hermes-telegram]\n discord: [hermes-discord]\n whatsapp: [hermes-whatsapp]\n slack: [hermes-slack]\n signal: [hermes-signal]\n homeassistant: [hermes-homeassistant]\n qqbot: [hermes-qqbot]\n yuanbao: [hermes-yuanbao]\n teams: [hermes-teams]\n google_chat: [hermes-google_chat]\n\n# =============================================================================\n# Gateway Platform Settings\n# =============================================================================\n# Optional per-platform messaging settings.\n# Platform-specific knobs live under `extra`.\n#\n# platforms:\n# telegram:\n# reply_to_mode: \"first\" # off | first | all\n# # guest_mode lets explicit @mentions from non-allowlisted groups through.\n# # Default false; ordinary messages, replies, and regex wake words stay blocked.\n# guest_mode: false\n# # allowed_chats: [\"-1001234567890\"]\n# extra:\n# disable_link_previews: false # Set true to suppress Telegram URL previews in bot messages\n# 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\n# rich_drafts: false # Experimental rich draft previews during Telegram DM streaming; default false because Telegram Desktop/macOS can visually overlay draft frames\n# command_menu:\n# # Telegram allows up to 100 BotCommands; Hermes defaults to 60 so\n# # all built-in commands plus common skill commands stay visible\n# # while remaining under Telegram's payload-size limit. Clamped 1..100.\n# max_commands: 60\n# # prepend = user priority first, then Hermes defaults\n# # append = Hermes defaults first, then user priority\n# # replace = only the list below defines priority\n# priority_mode: prepend\n# priority:\n# - my_plugin_command\n# webhook:\n# extra:\n# # Route scripts default to a 30 second timeout. Scripts must live under\n# # the active profile's scripts directory and receive webhook JSON on stdin.\n# script_timeout_seconds: 30\n#\n# Discord-specific settings (config.yaml top-level, not under platforms:):\n#\n# discord:\n# require_mention: true # Require @mention in server channels (default: true)\n# auto_thread: true # Auto-create thread on @mention (default: true)\n# free_response_channels: \"\" # Channel IDs where no mention is needed\n# reactions: true # Show processing reactions (default: true)\n# history_backfill: true # Recover missed channel messages on mention (default: true)\n# history_backfill_limit: 50 # Max messages to scan backwards (default: 50)\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Available toolsets (use these names in platform_toolsets or the toolsets list)\n#\n# Run `hermes chat --list-toolsets` to see all toolsets and their tools.\n# Run `hermes chat --list-tools` to see every individual tool with descriptions.\n# ─────────────────────────────────────────────────────────────────────────────\n#\n# INDIVIDUAL TOOLSETS (compose your own):\n# web - web_search, web_extract\n# search - web_search only (no scraping)\n# terminal - terminal, process\n# file - read_file, write_file, patch, search\n# browser - browser_navigate, browser_snapshot, browser_click, browser_type,\n# browser_scroll, browser_back, browser_press,\n# browser_get_images, browser_vision (requires BROWSERBASE_API_KEY)\n# vision - vision_analyze (requires OPENROUTER_API_KEY)\n# image_gen - image_generate (requires FAL_KEY)\n# skills - skills_list, skill_view\n# skills_hub - skill_hub (search/install/manage from online registries — user-driven only)\n# todo - todo (in-memory task planning, no deps)\n# tts - text_to_speech (Edge TTS free, or ELEVENLABS/OPENAI/MINIMAX/MISTRAL key)\n# cronjob - cronjob (create/list/update/pause/resume/run/remove scheduled tasks)\n#\n# PRESETS (curated bundles):\n# hermes-cli - All of the above except send_message\n# hermes-telegram - terminal, file, web, vision, image_gen, tts, browser,\n# skills, todo, cronjob, send_message\n# hermes-discord - Same as hermes-telegram\n# hermes-whatsapp - Same as hermes-telegram\n# hermes-slack - Same as hermes-telegram\n#\n# COMPOSITE:\n# debugging - terminal + web + file\n# safe - web + vision (no terminal access)\n# all - Everything available\n#\n# web - Web search and content extraction (web_search, web_extract)\n# search - Web search only, no scraping (web_search)\n# terminal - Command execution and process management (terminal, process)\n# file - File operations: read, write, patch, search\n# browser - Full browser automation (navigate, click, type, screenshot, etc.)\n# vision - Image analysis (vision_analyze)\n# image_gen - Image generation with FLUX (image_generate)\n# skills - Load skill documents (skills_list, skill_view)\n# todo - Task planning and tracking for multi-step work\n# memory - Persistent memory across sessions (personal notes + user profile)\n# session_search - Search and recall past conversations (FTS5 + Gemini Flash summarization)\n# tts - Text-to-speech (Edge TTS free, ElevenLabs, OpenAI, MiniMax, Mistral)\n# cronjob - Schedule and manage automated tasks (CLI-only)\n#\n# Composite toolsets:\n# debugging - terminal + web + file (for troubleshooting)\n# safe - web + vision (no terminal access)\n\n# NOTE: The top-level \"toolsets\" key is deprecated and ignored.\n# Tool configuration is managed per-platform via platform_toolsets above.\n# Use `hermes tools` to configure interactively, or edit platform_toolsets directly.\n#\n# CLI override: hermes chat --toolsets terminal,web,file\n\n# =============================================================================\n# MCP (Model Context Protocol) Servers\n# =============================================================================\n# Connect to external MCP servers to add tools from the MCP ecosystem.\n# Each server's tools are automatically discovered and registered.\n# See website/docs/user-guide/features/mcp.md for full documentation.\n#\n# Stdio servers (spawn a subprocess):\n# command: the executable to run\n# args: command-line arguments\n# env: environment variables (only these + safe defaults passed to subprocess)\n#\n# HTTP servers (connect to a URL):\n# url: the MCP server endpoint\n# headers: HTTP headers (e.g., for authentication)\n#\n# Optional per-server settings:\n# timeout: tool call timeout in seconds (default: 120)\n# connect_timeout: initial connection timeout (default: 60)\n# keepalive_interval: liveness ping cadence in seconds (default: 180).\n# Lower it below the server's session TTL for servers that expire idle\n# sessions quickly (e.g. Unreal Engine editor MCP, ~15s), otherwise idle\n# tool calls hit an expired session and pay a slow reconnect. Floored at 5s.\n#\n# mcp_servers:\n# time:\n# command: uvx\n# args: [\"mcp-server-time\"]\n# filesystem:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-filesystem\", \"/home/user\"]\n# notion:\n# url: https://mcp.notion.com/mcp\n# github:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-github\"]\n# env:\n# GITHUB_PERSONAL_ACCESS_TOKEN: \"ghp_...\"\n#\n# Sampling (server-initiated LLM requests) — enabled by default.\n# Per-server config under the 'sampling' key:\n# analysis:\n# command: npx\n# args: [\"-y\", \"analysis-server\"]\n# sampling:\n# enabled: true # default: true\n# model: \"gemini-3-flash\" # override model (optional)\n# max_tokens_cap: 4096 # max tokens per request\n# timeout: 30 # LLM call timeout (seconds)\n# max_rpm: 10 # max requests per minute\n# allowed_models: [] # model whitelist (empty = all)\n# max_tool_rounds: 5 # tool loop limit (0 = disable)\n# log_level: \"info\" # audit verbosity\n\n# =============================================================================\n# Text-to-Speech\n# =============================================================================\n# TTS defaults to Edge TTS unless changed in ~/.hermes/config.yaml.\n# Gemini TTS supports persona/director prompt files, and Gemini 3.1 Flash TTS\n# can use a hidden auxiliary rewrite pass to insert expressive square-bracket\n# audio tags into the TTS script without showing tags in chat.\n#\n# tts:\n# provider: \"gemini\"\n# gemini:\n# model: \"gemini-3.1-flash-tts-preview\"\n# voice: \"Kore\"\n# audio_tags: false\n# persona_prompt_file: \"\" # e.g. ~/.hermes/tts/radio-host.md\n\n# =============================================================================\n# Voice Transcription (Speech-to-Text)\n# =============================================================================\n# Automatically transcribe voice messages on messaging platforms.\n# Providers: local (free, faster-whisper) | groq (free tier) | openai (Whisper API) | mistral (Voxtral Transcribe)\n# Set the corresponding API key in .env: GROQ_API_KEY, OPENAI_API_KEY, or MISTRAL_API_KEY.\nstt:\n enabled: true\n # provider: \"local\" # auto-detected if omitted\n local:\n model: \"base\" # tiny | base | small | medium | large-v3 | turbo\n # language: \"\" # auto-detect; set to \"en\", \"es\", \"fr\", etc. to force\n # initial_prompt: \"\" # Optional faster-whisper prompt, e.g. bias Chinese output to simplified Chinese\n # groq:\n # language: \"\" # blank = use HERMES_LOCAL_STT_LANGUAGE if set, else auto-detect\n openai:\n model: \"whisper-1\" # whisper-1 | gpt-4o-mini-transcribe | gpt-4o-transcribe\n language: \"\" # auto-detect; set to \"en\", \"es\", \"fr\", etc. to force\n # mistral:\n # model: \"voxtral-mini-latest\" # voxtral-mini-latest | voxtral-mini-2602\n # deepinfra:\n # # Model id is discovered live from the DeepInfra catalog filtered\n # # by the `stt` surface tag — leave `model` blank to take the first\n # # live result. Pin only when you need a specific Whisper variant.\n # model: \"\"\n\n# Text-to-speech. Only the deepinfra block is documented here — the\n# remaining providers (edge, openai, xai, minimax, mistral, gemini,\n# elevenlabs, neutts, kittentts, piper) inherit sensible defaults from\n# DEFAULT_CONFIG in hermes_cli/config.py.\n# tts:\n# provider: \"deepinfra\"\n# deepinfra:\n# # Model id is discovered live from the DeepInfra catalog filtered\n# # by the `tts` surface tag — leave `model` blank to take the first\n# # live result.\n# model: \"\"\n# voice: \"default\"\n\n# Image generation. Each provider plugin reads its own ``image_gen.<name>``\n# block; deepinfra discovers models live from\n# api.deepinfra.com/v1/openai/models filtered by the ``image-gen`` tag —\n# no model id is hardcoded, so retired models disappear automatically.\n# image_gen:\n# provider: \"deepinfra\"\n# deepinfra:\n# # Leave `model` blank for the first live `image-gen`-tagged result.\n# model: \"\"\n\n# =============================================================================\n# Response Pacing (Messaging Platforms)\n# =============================================================================\n# Add human-like delays between message chunks.\n# human_delay:\n# mode: \"off\" # \"off\" | \"natural\" | \"custom\"\n# min_ms: 800 # Min delay (custom mode only)\n# max_ms: 2500 # Max delay (custom mode only)\n\n# =============================================================================\n# Session Logging\n# =============================================================================\n# Session trajectories are automatically saved to logs/ directory.\n# Each session creates: logs/session_YYYYMMDD_HHMMSS_UUID.json\n#\n# The session ID is displayed in the welcome banner for easy reference.\n# Logs contain full conversation history in trajectory format:\n# - System prompt, user messages, assistant responses\n# - Tool calls with inputs/outputs\n# - Timestamps for debugging\n#\n# No configuration needed - logging is always enabled.\n# To disable, you would need to modify the source code.\n\n# =============================================================================\n# Code Execution Sandbox (Programmatic Tool Calling)\n# =============================================================================\n# The execute_code tool runs Python scripts that call Hermes tools via RPC.\n# Intermediate tool results stay out of the LLM's context window.\ncode_execution:\n timeout: 300 # Max seconds per script before kill (default: 300 = 5 min)\n max_tool_calls: 50 # Max RPC tool calls per execution (default: 50)\n\n# =============================================================================\n# Subagent Delegation\n# =============================================================================\n# The delegate_task tool spawns child agents with isolated context.\n# Supports single tasks and batch mode (default 3 parallel, configurable).\ndelegation:\n max_iterations: 50 # Max tool-calling turns per child (default: 50)\n # max_concurrent_children: 3 # Max parallel child agents per batch (default: 3, floor: 1, no ceiling).\n # WARNING: values above 10 multiply API cost linearly.\n # max_spawn_depth: 1 # Delegation tree depth cap (range: 1-3, default: 1 = flat).\n # Raise to 2 to allow workers to spawn their own subagents.\n # Requires role=\"orchestrator\" on intermediate agents.\n # orchestrator_enabled: true # Kill switch for role=\"orchestrator\" children (default: true).\n # subagent_auto_approve: false # When a subagent hits a dangerous-command approval prompt, auto-deny (default: false)\n # or auto-approve \"once\" (true) instead of blocking on stdin.\n # The parent TUI owns stdin, so blocking would deadlock; non-interactive resolution is required.\n # Both choices emit a logger.warning audit line. Flip to true only for cron/batch pipelines.\n # inherit_mcp_toolsets: true # When explicit child toolsets are narrowed, also keep the parent's MCP toolsets (default: true). Set false for strict intersection.\n # model: \"google/gemini-3-flash-preview\" # Override model for subagents (empty = inherit parent)\n # provider: \"openrouter\" # Override provider for subagents (empty = inherit parent)\n # # Resolves full credentials (base_url, api_key) automatically.\n # # Supported: openrouter, nous, zai, kimi-coding, minimax\n\n# =============================================================================\n# Honcho Integration (Cross-Session User Modeling)\n# =============================================================================\n# AI-native persistent memory via Honcho (https://honcho.dev/).\n# Builds a deeper understanding of the user across sessions and tools.\n# Runs alongside USER.md — additive, not a replacement.\n#\n# Requires: pip install honcho-ai\n# Config: ~/.honcho/config.json (shared with Claude Code, Cursor, etc.)\n# API key: HONCHO_API_KEY in ~/.hermes/.env or ~/.honcho/config.json\n#\n# Hermes-specific overrides (optional — most config comes from ~/.honcho/config.json):\n# honcho: {}\n\n# =============================================================================\n# Display\n# =============================================================================\ndisplay:\n # Use compact banner mode (hides the ASCII-art banner, shows a single line).\n # true: Compact single-line banner\n # false: Full ASCII banner with tool/skill summary (default)\n compact: false\n\n # Tool progress display level (CLI and gateway)\n # off: Silent — no tool activity shown, just the final response\n # new: Show a tool indicator only when the tool changes (skip repeats)\n # all: Show every tool call with a short preview (default)\n # verbose: Full args, results, and debug logs (same as /verbose)\n # log: Silent in chat; append every tool call to ~/.hermes/logs/tool_calls.log (gateway only)\n # Toggle at runtime with /verbose in the CLI\n tool_progress: all\n\n # Per-platform defaults can be quieter than the global setting. Telegram\n # tunes for mobile: tool_progress and busy_ack_detail default off (no\n # per-tool breadcrumb stream, no \"iteration 21/60\" debug detail in busy\n # acks or heartbeats), but interim_assistant_messages and\n # long_running_notifications STAY ON so the user has real signal between\n # turn start and final answer (mid-turn assistant commentary + a single\n # edit-in-place \"⏳ Working — N min\" heartbeat). Override under\n # display.platforms.telegram.\n\n # Auto-cleanup of temporary progress bubbles after the final response lands.\n # On platforms that support message deletion (currently Telegram), this\n # removes the tool-progress bubble, \"⏳ Still working...\" notices, and\n # context-pressure status messages once the final reply has been delivered —\n # keeping long-running turns visible live, then tidy afterward. Failed runs\n # leave the bubbles in place as breadcrumbs. Off by default.\n # Per-platform override: display.platforms.telegram.cleanup_progress\n # true: Delete tracked progress/status bubbles on successful turn\n # false: Leave everything in place (default)\n # Example:\n # display:\n # platforms:\n # telegram:\n # cleanup_progress: true\n cleanup_progress: false\n\n # Natural mid-turn assistant updates.\n # On gateway platforms, when true, completed assistant status messages are\n # sent as separate chat messages. On the Desktop app, when true, mid-turn\n # assistant narration streamed between tool calls is kept in the transcript\n # instead of the bubble collapsing to only the final message on completion.\n # Independent of tool_progress and gateway streaming.\n # true: Keep/send mid-turn assistant updates (default)\n # false: Only keep/send the final response\n interim_assistant_messages: true\n\n # Gateway-only long-running status heartbeats.\n # When false, the platform does not receive periodic \"⏳ Working — N min\"\n # notifications even if agent.gateway_notify_interval is non-zero. The\n # heartbeat edits a single message in place (where the adapter supports\n # editing) instead of posting a new bubble each interval.\n # Default: true everywhere, including Telegram (silent agents are worse\n # than a single edit-in-place heartbeat).\n long_running_notifications: true\n\n # Include detailed iteration/tool/status context in busy acknowledgments\n # and long-running heartbeats. When true, busy acks show \"iteration 21/60,\n # terminal, 10 min\" and the heartbeat shows \"⏳ Working — 12 min,\n # iteration 21/60, terminal\". When false (Telegram default), both stay\n # terse: \"Interrupting current task\" and \"⏳ Working — 12 min, terminal\".\n busy_ack_detail: true\n\n # What Enter does when Hermes is already busy (CLI and gateway platforms).\n # interrupt: Interrupt the current run and redirect Hermes (default)\n # queue: Queue your message for the next turn\n # steer: Inject your message mid-run via /steer, arriving at the agent\n # after the next tool call — no interrupt, no role violation.\n # Falls back to 'queue' if the agent isn't running yet or if\n # images are attached (steer only carries text).\n # Ctrl+C (or /stop in gateway) always interrupts regardless of this setting.\n # Toggle at runtime with /busy <interrupt|queue|steer>.\n busy_input_mode: interrupt\n\n # Background process notifications (gateway/messaging only).\n # Controls how chatty the process watcher is when you use\n # terminal(background=true, notify_on_complete=true) from Telegram/Discord/etc.\n # off: No watcher messages at all\n # result: Only the final completion message\n # error: Only the final message when exit code != 0\n # all: Running output updates + final message (default)\n background_process_notifications: all\n\n\n # Play terminal bell when agent finishes a response.\n # Useful for long-running tasks — your terminal will ding when the agent is done.\n # Works over SSH. Most terminals can be configured to flash the taskbar or play a sound.\n # true: Ring the terminal bell on each response\n # false: Silent (default)\n bell_on_complete: false\n\n # Show model reasoning/thinking before each response.\n # When enabled, a dim box shows the model's thought process above the response.\n # Toggle at runtime with /reasoning show or /reasoning hide.\n # true: Show the reasoning box\n # false: Hide reasoning (default)\n show_reasoning: false\n\n # Stream tokens to the terminal as they arrive instead of waiting for the\n # full response. The response box opens on first token and text appears\n # line-by-line. Tool calls are still captured silently.\n # true: Stream tokens as they arrive (default)\n # false: Wait for the full response before rendering\n streaming: true\n\n # Show [HH:MM] timestamps on user input and assistant response labels.\n # timestamps: false\n\n # ───────────────────────────────────────────────────────────────────────────\n # Skin / Theme\n # ───────────────────────────────────────────────────────────────────────────\n # Customize CLI visual appearance — banner colors, spinner faces, tool prefix,\n # response box label, and branding text. Change at runtime with /skin <name>.\n #\n # Built-in skins:\n # default — Classic Hermes gold/kawaii\n # ares — Crimson/bronze war-god theme with spinner wings\n # mono — Clean grayscale monochrome\n # slate — Cool blue developer-focused\n # daylight — Bright light-mode theme\n # warm-lightmode — Warm paper-tone light-mode theme\n # poseidon — Sea-green/teal Olympian theme\n # sisyphus — Earthy stone-and-moss theme\n # charizard — Fiery orange dragon theme\n #\n # Custom skins: drop a YAML file in ~/.hermes/skins/<name>.yaml\n # Schema (all fields optional, missing values inherit from default):\n #\n # name: my-theme\n # description: Short description\n # colors:\n # banner_border: \"#HEX\" # Panel border\n # banner_title: \"#HEX\" # Panel title\n # banner_accent: \"#HEX\" # Section headers (Available Tools, etc.)\n # banner_dim: \"#HEX\" # Dim/muted text\n # banner_text: \"#HEX\" # Body text (tool names, skill names)\n # ui_accent: \"#HEX\" # UI accent color\n # response_border: \"#HEX\" # Response box border color\n # spinner:\n # waiting_faces: [\"(⚔)\", \"(⛨)\"] # Faces shown while waiting\n # thinking_faces: [\"(⚔)\", \"(⌁)\"] # Faces shown while thinking\n # thinking_verbs: [\"forging\", \"plotting\"] # Verbs for spinner messages\n # wings: # Optional left/right spinner decorations\n # - [\"⟪⚔\", \"⚔⟫\"]\n # - [\"⟪▲\", \"▲⟫\"]\n # branding:\n # agent_name: \"My Agent\" # Banner title and branding\n # welcome: \"Welcome message\" # Shown at CLI startup\n # response_label: \" ⚔ Agent \" # Response box header label\n # prompt_symbol: \"⚔\" # Prompt symbol (bare token; renderers add trailing space)\n # tool_prefix: \"╎\" # Tool output line prefix (default: ┊)\n #\n skin: default\n\n# =============================================================================\n# Model Aliases — short names for /model command\n# =============================================================================\n# Map short aliases to exact (model, provider, base_url) tuples.\n# Used by /model tab completion and resolve_alias().\n# Aliases are checked BEFORE the models.dev catalog, so they can route\n# to endpoints not in the catalog (e.g. Ollama Cloud, local servers).\n#\n# model_aliases:\n# opus:\n# model: claude-opus-4-6\n# provider: anthropic\n# qwen:\n# model: \"qwen3.5:397b\"\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n# glm:\n# model: glm-4.7\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n\n# =============================================================================\n# Privacy\n# =============================================================================\n# privacy:\n# # Redact PII from the LLM context prompt.\n# # When true, phone numbers are stripped and user/chat IDs are replaced\n# # with deterministic hashes before being sent to the model.\n# # Names and usernames are NOT affected (user-chosen, publicly visible).\n# # Routing/delivery still uses the original values internally.\n# redact_pii: false\n\n# =============================================================================\n# Shell-script hooks\n# =============================================================================\n# Register shell scripts as plugin-hook callbacks. Each entry is executed as\n# a subprocess (shell=False, shlex.split) with a JSON payload on stdin. On\n# stdout the script may return JSON that either blocks the tool call or\n# injects context into the next LLM call.\n#\n# Valid events (mirror hermes_cli.plugins.VALID_HOOKS):\n# pre_tool_call, post_tool_call, pre_llm_call, post_llm_call,\n# pre_api_request, post_api_request, on_session_start, on_session_end,\n# on_session_finalize, on_session_reset, subagent_stop\n#\n# First-use consent: each (event, command) pair prompts once on a TTY, then\n# is persisted to ~/.hermes/shell-hooks-allowlist.json. Non-interactive\n# runs (gateway, cron) need --accept-hooks, HERMES_ACCEPT_HOOKS=1, or the\n# hooks_auto_accept key below.\n#\n# See website/docs/user-guide/features/hooks.md for the full JSON wire\n# protocol and worked examples.\n#\n# hooks:\n# pre_tool_call:\n# - matcher: \"terminal\"\n# command: \"~/.hermes/agent-hooks/block-rm-rf.sh\"\n# timeout: 10\n# post_tool_call:\n# - matcher: \"write_file|patch\"\n# command: \"~/.hermes/agent-hooks/auto-format.sh\"\n# pre_llm_call:\n# - command: \"~/.hermes/agent-hooks/inject-cwd-context.sh\"\n# subagent_stop:\n# - command: \"~/.hermes/agent-hooks/log-orchestration.sh\"\n#\n# hooks_auto_accept: false\n\n\n# =============================================================================\n# Update Behavior\n# =============================================================================\nupdates:\n # Create a full HERMES_HOME zip before every `hermes update`.\n # Backups land in ~/.hermes/backups/ and can be restored with `hermes import`.\n # Off by default because large homes can add minutes to every update.\n pre_update_backup: false\n\n # Number of pre-update backup zips to retain.\n backup_keep: 5\n\n # What non-interactive updates do with local source edits in the Hermes repo.\n # Interactive terminal updates always prompt before restoring the autostash.\n #\n # stash - auto-stash before pull, then auto-restore after success (default)\n # discard - drop the update-created stash after success; use only on managed\n # installs where local source edits should not persist\n non_interactive_local_changes: \"stash\"\n\n\n# =============================================================================\n# Web Dashboard\n# =============================================================================\n# OAuth gate configuration for `hermes dashboard --host <non-loopback>`.\n# The bundled Nous Portal plugin reads these on startup; settings here are\n# the canonical surface. Each can be overridden by an environment variable:\n#\n# dashboard.oauth.client_id <- HERMES_DASHBOARD_OAUTH_CLIENT_ID\n# dashboard.oauth.portal_url <- HERMES_DASHBOARD_PORTAL_URL\n# dashboard.public_url <- HERMES_DASHBOARD_PUBLIC_URL\n#\n# Env wins when set to a non-empty value. This is what Fly.io's platform-\n# secret injection uses to push per-deploy client_ids without needing to\n# bake a config.yaml into the image. Empty env values are treated as unset\n# so a provisioned-but-not-populated secret can't shadow a valid entry here.\n#\n# Local dev / on-prem deploys should typically set these via config.yaml\n# (the ~/.hermes/.env file is reserved for API keys and secrets).\n#\n# dashboard:\n# oauth:\n# client_id: \"\" # agent:{instance_id}; Portal provisions this at deploy\n# portal_url: \"\" # blank → default https://portal.nousresearch.com\n#\n# # Force the absolute base URL the OAuth callback (and any other public\n# # URL the dashboard hands to external systems) is built from. Set this\n# # for deploys behind reverse proxies that don't reliably forward\n# # X-Forwarded-Host / X-Forwarded-Proto / X-Forwarded-Prefix (manual\n# # nginx setups, on-prem ingresses, custom-domain Fly deploys without\n# # full proxy header chains).\n# #\n# # When set, the value is the complete authority: scheme + host +\n# # optional path prefix (e.g. \"https://example.com/hermes\"). The OAuth\n# # callback URL becomes \"<public_url>/auth/callback\" — X-Forwarded-Prefix\n# # is IGNORED on this code path because the operator has explicitly\n# # declared the public URL and we no longer need to guess.\n# #\n# # Leave empty to use the existing proxy-header reconstruction (the\n# # default — works on Fly.io out of the box).\n# #\n# # public_url: \"https://example.com/hermes\"\n#\n# -----------------------------------------------------------------------------\n# Self-hosted OIDC dashboard auth (generic OpenID Connect — Authentik,\n# Keycloak, Zitadel, Authelia, Auth0, Okta, Google, …). Use this INSTEAD of the\n# nous block above when gating the dashboard with your own identity provider.\n# Each setting can be overridden by an environment variable:\n#\n# dashboard.oauth.self_hosted.issuer <- HERMES_DASHBOARD_OIDC_ISSUER\n# dashboard.oauth.self_hosted.client_id <- HERMES_DASHBOARD_OIDC_CLIENT_ID\n# dashboard.oauth.self_hosted.scopes <- HERMES_DASHBOARD_OIDC_SCOPES\n# dashboard.oauth.self_hosted.client_secret <- HERMES_DASHBOARD_OIDC_CLIENT_SECRET\n#\n# dashboard:\n# oauth:\n# provider: self-hosted\n# self_hosted:\n# issuer: \"https://auth.example.com/application/o/hermes/\" # required\n# client_id: \"hermes-dashboard\" # required\n# scopes: \"openid profile email\" # optional\n#\n# # OPTIONAL — set ONLY if your IDP registered the client as\n# # *confidential* (Authentik / Keycloak often default to this). When\n# # set, Hermes authenticates the client at the token endpoint\n# # (client_secret_basic or client_secret_post, auto-selected from the\n# # IDP's discovery doc) IN ADDITION to PKCE. Leave unset for a public\n# # (PKCE-only) client — the common case.\n# #\n# # This is a CREDENTIAL: prefer setting HERMES_DASHBOARD_OIDC_CLIENT_SECRET\n# # in ~/.hermes/.env over putting it here in config.yaml.\n# # client_secret: \"\"\n\n# =============================================================================\n# External secret sources\n# =============================================================================\n# Pull provider credentials from external secret managers at process startup\n# instead of storing them in ~/.hermes/.env. Only the manager's bootstrap\n# credential (e.g. BWS_ACCESS_TOKEN / OP_SERVICE_ACCOUNT_TOKEN) lives in .env\n# (or your shell / desktop session); everything else rotates centrally.\n# Failures never block startup — Hermes warns once and continues with\n# whatever .env already had.\n#\n# Multiple sources can be enabled at once:\n# - \"mapped\" sources (explicit VAR -> ref bindings, e.g. 1Password's env:\n# map) beat \"bulk\" sources (whole-project dumps like Bitwarden BSM)\n# - within a shape, the first source to claim a var wins; later claims\n# are skipped with a startup warning (never a silent clobber)\n# - a source's override_existing lets it beat .env/shell values, but\n# never another secret source's claim\n# Docs: https://hermes-agent.nousresearch.com/docs/user-guide/secrets/\n#\n# secrets:\n# # Optional explicit ordering of enabled sources.\n# # sources: [onepassword, bitwarden]\n#\n# # ---- Bitwarden Secrets Manager (bws CLI) --------------------------------\n# bitwarden:\n# enabled: false\n# access_token_env: BWS_ACCESS_TOKEN # bootstrap token, sourced from .env\n# project_id: \"\" # UUID of the BSM project to sync\n# server_url: \"\" # \"\" = US Cloud; EU/self-hosted URL otherwise\n# cache_ttl_seconds: 300 # 0 disables fresh caching\n# encrypted_cache: # optional encrypted stale fallback\n# enabled: false\n# max_stale_seconds: 0 # 0 disables stale fallback\n# override_existing: true # BSM values win over existing env\n# auto_install: true # lazy-download bws into ~/.hermes/bin\n#\n# # ---- 1Password (op CLI) -------------------------------------------------\n# onepassword:\n# enabled: false\n# # Map env-var names to op:// secret references. Each is resolved with a\n# # single `op read` at startup.\n# env:\n# OPENAI_API_KEY: \"op://Private/OpenAI/api key\"\n# ANTHROPIC_API_KEY: \"op://Private/Anthropic/credential\"\n# account: \"\" # op --account shorthand; \"\" = default\n# service_account_token_env: OP_SERVICE_ACCOUNT_TOKEN # headless auth; unset = desktop session\n# binary_path: \"\" # \"\" = resolve op via PATH; else absolute path\n# cache_ttl_seconds: 300 # 0 disables BOTH cache layers\n# override_existing: true # resolved values win over existing env\n#\n# # ---- Command helper (any CLI vault) --------------------------------------\n# # Run a user-configured helper that prints KEY=VALUE lines on stdout —\n# # works with any secret store that has a CLI: keepassxc-cli, secret-tool,\n# # pass, gpg, or a script that cats a tmpfs env file. Composes with the\n# # sources above (enable any combination). POSIX-only (needs /bin/sh).\n# # The helper must be fast and NON-interactive (hard timeout, 1 MiB cap);\n# # its stderr is discarded so diagnostics can't leak secret material.\n# command:\n# enabled: false\n# command: \"cat /run/user/1000/hermes-secrets.env\"\n# helper_timeout_seconds: 3\n# override_existing: false # .env/shell win by default\n"}, "be109982647e10af98e9b1abf69253d0d3b17db2": {"t": 1785767988.0241022, "n": 83788, "h": "1b605c35cb911f06", "c": "# Hermes Agent CLI Configuration\n# Copy this file to cli-config.yaml and customize as needed.\n# This file configures the CLI behavior. Environment variables in .env take precedence.\n\n# =============================================================================\n# Model Configuration\n# =============================================================================\nmodel:\n # Default model to use (can be overridden with --model flag)\n # Both \"default\" and \"model\" work as the key name here.\n default: \"anthropic/claude-opus-4.6\"\n \n # Inference provider selection:\n # \"auto\" - Auto-detect from credentials (default)\n # \"openrouter\" - OpenRouter (requires: OPENROUTER_API_KEY or OPENAI_API_KEY)\n # \"nous\" - Nous Portal OAuth (requires: hermes auth add nous)\n # \"nous-api\" - Nous Portal API key (requires: NOUS_API_KEY)\n # \"anthropic\" - Direct Anthropic API (requires: ANTHROPIC_API_KEY)\n # \"openai-codex\" - OpenAI Codex (requires: hermes auth)\n # \"copilot\" - GitHub Copilot / GitHub Models (requires: GITHUB_TOKEN)\n # \"gemini\" - Use Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n # \"zai\" - Use z.ai / ZhipuAI GLM models (requires: GLM_API_KEY)\n # \"kimi-coding\" - Kimi / Moonshot AI (requires: KIMI_API_KEY)\n # \"minimax\" - MiniMax global (requires: MINIMAX_API_KEY)\n # \"minimax-cn\" - MiniMax China (requires: MINIMAX_CN_API_KEY)\n # \"huggingface\" - Hugging Face Inference (requires: HF_TOKEN)\n # \"nvidia\" - NVIDIA NIM / build.nvidia.com (requires: NVIDIA_API_KEY)\n # \"xiaomi\" - Xiaomi MiMo (requires: XIAOMI_API_KEY)\n # \"arcee\" - Arcee AI Trinity models (requires: ARCEEAI_API_KEY)\n # \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY — https://ollama.com/settings)\n # \"deepinfra\" - DeepInfra (requires: DEEPINFRA_API_KEY)\n # \"kilocode\" - KiloCode gateway (requires: KILOCODE_API_KEY)\n # \"azure-foundry\" - Microsoft Foundry / Azure OpenAI (API key or Entra ID)\n # \"lmstudio\" - LM Studio local server (optional: LM_API_KEY, defaults to http://127.0.0.1:1234/v1)\n #\n # Local servers (LM Studio, Ollama, vLLM, llama.cpp):\n # \"custom\" - Any other OpenAI-compatible endpoint. Set base_url below.\n # Aliases: \"ollama\", \"vllm\", \"llamacpp\" all map to \"custom\".\n # LM Studio is first-class and uses provider: \"lmstudio\".\n # It works with both no-auth and auth-enabled server modes.\n #\n # Can also be overridden for a single invocation with the --provider flag.\n provider: \"auto\"\n \n # API configuration (falls back to OPENROUTER_API_KEY env var)\n # api_key: \"your-key-here\" # Uncomment to set here instead of .env\n base_url: \"https://openrouter.ai/api/v1\"\n\n # Azure Foundry keyless auth example:\n # provider: \"azure-foundry\"\n # base_url: \"https://<resource>.openai.azure.com/openai/v1\"\n # auth_mode: \"entra_id\" # DefaultAzureCredential: az login, managed identity, workload identity, etc.\n # default: \"gpt-4o\" # Deployment/model name\n # entra:\n # scope: \"https://ai.azure.com/.default\" # Optional; this is the default.\n\n # ── Token limits — two settings, easy to confuse ──────────────────────────\n #\n # context_length: TOTAL context window (input + output tokens combined).\n # Controls when Hermes compresses history and validates requests.\n # Leave unset — Hermes auto-detects the correct value from the provider.\n # Set manually only when auto-detection is wrong (e.g. a local server with\n # a custom num_ctx, or a proxy that doesn't expose /v1/models).\n #\n # context_length: 131072\n #\n # max_tokens: OUTPUT cap — maximum tokens the model may generate per response.\n # Unrelated to how long your conversation history can be.\n # The OpenAI-standard name \"max_tokens\" is a misnomer; Anthropic's native\n # API has since renamed it \"max_output_tokens\" for clarity.\n # Leave unset to use the model's native output ceiling (recommended).\n # Set only if you want to deliberately limit individual response length.\n #\n# max_tokens: 8192\n\n # ── Custom request headers (optional) ─────────────────────────────────────\n #\n # default_headers: extra HTTP headers sent on every request to an\n # OpenAI-compatible endpoint. User values take precedence over the\n # provider/SDK defaults, so this is the supported way to override the\n # OpenAI Python SDK's identifying headers (User-Agent: OpenAI/Python ...,\n # X-Stainless-*) when a custom provider sits behind a gateway/WAF that\n # rejects them — e.g. an upstream that returns \"502 Upstream access\n # forbidden\" for the SDK default User-Agent but accepts a plain one.\n # Applies on the OpenAI wire only (not native Anthropic / Bedrock).\n #\n # default_headers:\n # User-Agent: \"curl/8.7.1\"\n #\n # extra_headers: accepted as an alias of default_headers (merged, with\n # extra_headers winning when both are set) — matches the per-provider\n # extra_headers key below.\n #\n # Per-provider variant: named providers / custom_providers entries accept an\n # extra_headers dict scoped to that endpoint only — for reverse proxies,\n # gateways, or custom auth (e.g. Cloudflare Access service tokens).\n # Merged onto SDK/provider defaults with the entry's values winning.\n # Header values are treated as secrets and are never logged.\n #\n # providers:\n # my-proxy:\n # base_url: \"https://llm.internal.example.com/v1\"\n # key_env: \"MY_PROXY_API_KEY\"\n # extra_headers:\n # CF-Access-Client-Id: \"xxxx.access\"\n # CF-Access-Client-Secret: \"${CF_ACCESS_SECRET}\"\n # X-Client-Name: \"hermes-agent\"\n\n# Named provider overrides (optional)\n# Use this for per-provider request timeouts, non-stream stale timeouts,\n# and per-model exceptions.\n# Applies to the primary turn client on every api_mode (OpenAI-wire, native\n# Anthropic, and Anthropic-compatible providers), the fallback chain, and\n# client rebuilds during credential rotation. For OpenAI-wire chat\n# completions (streaming and non-streaming) the configured value is also\n# used as the per-request ``timeout=`` kwarg so it wins over the legacy\n# HERMES_API_TIMEOUT env var (which still applies when no config is set).\n# ``stale_timeout_seconds`` controls the non-streaming stale-call detector and\n# wins over the legacy HERMES_API_CALL_STALE_TIMEOUT env var. Leaving these\n# unset keeps the legacy defaults (HERMES_API_TIMEOUT=1800s,\n# HERMES_API_CALL_STALE_TIMEOUT=90s, native Anthropic 900s). The\n# implicit non-stream stale detector is auto-disabled for local endpoints\n# and can scale upward for very large contexts.\n#\n# Not currently wired for AWS Bedrock (bedrock_converse + AnthropicBedrock\n# SDK paths) — those use boto3 with its own timeout configuration.\n#\n# providers:\n# ollama-local:\n# request_timeout_seconds: 300 # Longer timeout for local cold-starts\n# stale_timeout_seconds: 900 # Explicitly re-enable stale detection on local endpoints\n# anthropic:\n# request_timeout_seconds: 30 # Fast-fail cloud requests\n# models:\n# claude-opus-4.6:\n# timeout_seconds: 600 # Longer timeout for extended-thinking Opus calls\n# openai-codex:\n# models:\n# gpt-5.4:\n# stale_timeout_seconds: 1800 # Longer non-stream stale timeout for slow large-context turns\n\n# =============================================================================\n# OpenRouter Provider Routing (only applies when using OpenRouter)\n# =============================================================================\n# Control how requests are routed across providers on OpenRouter.\n# See: https://openrouter.ai/docs/guides/routing/provider-selection\n#\n# provider_routing:\n# # Sort strategy: \"price\" (default), \"throughput\", or \"latency\"\n# # Append :nitro to model name for a shortcut to throughput sorting.\n# sort: \"throughput\"\n#\n# # Only allow these providers (provider slugs from OpenRouter)\n# # only: [\"anthropic\", \"google\"]\n#\n# # Skip these providers entirely\n# # ignore: [\"deepinfra\", \"fireworks\"]\n#\n# # Try providers in this order (overrides default load balancing)\n# # order: [\"anthropic\", \"google\", \"together\"]\n#\n# # Require providers to support all parameters in your request\n# # require_parameters: true\n#\n# # Data policy: \"allow\" (default) or \"deny\" to exclude providers that may store data\n# # data_collection: \"deny\"\n\n# =============================================================================\n# OpenRouter Response Caching (only applies when using OpenRouter)\n# =============================================================================\n# Cache identical API responses at the OpenRouter edge for free instant replays.\n# When enabled, identical requests (same model, messages, parameters) return\n# cached responses with zero billing. Separate from Anthropic prompt caching.\n# See: https://openrouter.ai/docs/guides/features/response-caching\n#\n# openrouter:\n# response_cache: true # Enable response caching (default: true)\n# response_cache_ttl: 300 # Cache TTL in seconds, 1-86400 (default: 300)\n\n# =============================================================================\n# Git Worktree Isolation\n# =============================================================================\n# When enabled, each CLI session creates an isolated git worktree so multiple\n# agents can work on the same repo concurrently without file collisions.\n# Equivalent to always passing --worktree / -w on the command line.\n#\n# worktree: true # Always create a worktree when in a git repo\n# worktree: false # Default — only create when -w flag is passed\n#\n# By default a new worktree branches from the freshly-fetched remote tip\n# (the current branch's upstream, else the remote's default branch) so it\n# starts current with the project instead of from the local clone's\n# (possibly stale) HEAD. Set worktree_sync: false to branch from local HEAD\n# instead — useful when offline or when you deliberately want the clone's\n# exact current state as the base.\n#\n# worktree_sync: true # Default — branch from the fetched remote tip\n# worktree_sync: false # Branch from local HEAD (offline / pinned base)\n\n# =============================================================================\n# Terminal Tool Configuration\n# =============================================================================\n# Choose ONE of the following terminal configurations by uncommenting it.\n# The terminal tool executes commands in the specified environment.\n\n# -----------------------------------------------------------------------------\n# OPTION 1: Local execution (default)\n# Commands run directly on your machine in the current directory\n# -----------------------------------------------------------------------------\n# Working directory behavior:\n# - CLI (`hermes` command): Uses \".\" (current directory where you run hermes)\n# - Gateway/messaging/cron: Uses terminal.cwd here; legacy .env cwd values are deprecated\nterminal:\n backend: \"local\"\n cwd: \".\" # For local backend: \".\" = current directory. Ignored for remote backends unless a backend documents otherwise.\n timeout: 180\n # HOME policy for tool subprocesses:\n # auto - default: host uses your real HOME; containers use HERMES_HOME/home\n # real - force your real OS-user HOME\n # profile - force HERMES_HOME/home for strict per-profile CLI config isolation\n home_mode: \"auto\"\n docker_mount_cwd_to_workspace: false # SECURITY: off by default. Opt in to mount the launch cwd into Docker /workspace.\n lifetime_seconds: 300\n # sudo_password: \"hunter2\" # Optional: pipe a sudo password via sudo -S. SECURITY WARNING: plaintext.\n # sudo_password: \"\" # Explicit empty password: try empty and never open the interactive sudo prompt.\n\n# -----------------------------------------------------------------------------\n# OPTION 2: SSH remote execution\n# Commands run on a remote server - agent code stays local (sandboxed)\n# Great for: keeping agent isolated from its own code, using powerful remote hardware\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"ssh\"\n# cwd: \"/home/myuser/project\" # Path on the REMOTE server\n# timeout: 180\n# lifetime_seconds: 300\n# ssh_host: \"my-server.example.com\"\n# ssh_user: \"myuser\"\n# ssh_port: 22\n# ssh_key: \"~/.ssh/id_rsa\" # Optional - uses ssh-agent if not specified\n\n# -----------------------------------------------------------------------------\n# OPTION 3: Docker container\n# Commands run in an isolated Docker container\n# Great for: reproducible environments, testing, isolation\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"docker\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /)\n# timeout: 180\n# lifetime_seconds: 300\n# docker_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# docker_mount_cwd_to_workspace: true # Explicit opt-in: mount your launch cwd into /workspace\n# # Optional: run the container as your host user's uid:gid so files written\n# # into bind-mounted dirs are owned by you, not root. Drops SETUID/SETGID\n# # caps too since no gosu privilege drop is needed. Leave off if your\n# # chosen docker_image expects to start as root.\n# docker_run_as_host_user: true\n# # Optional: explicitly forward selected env vars into Docker.\n# # These values come from your current shell first, then ~/.hermes/.env.\n# # Warning: anything forwarded here is visible to commands run in the container.\n# docker_forward_env:\n# - \"GITHUB_TOKEN\"\n# - \"NPM_TOKEN\"\n# # Optional: extra flags passed verbatim to docker run (appended after security defaults).\n# # Useful for adding capabilities (e.g. apt installs needing SETUID) or custom options.\n# # Example: add a Linux capability not included by default\n# # docker_extra_args:\n# # - \"--cap-add\"\n# # - \"SETUID\"\n\n# -----------------------------------------------------------------------------\n# OPTION 4: Singularity/Apptainer container\n# Commands run in a Singularity container (common in HPC environments)\n# Great for: HPC clusters, shared compute environments\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"singularity\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# singularity_image: \"docker://nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 5: Modal cloud execution\n# Commands run on Modal's cloud infrastructure\n# Great for: GPU access, scalable compute, serverless execution\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"modal\"\n# cwd: \"/workspace\" # Path INSIDE the sandbox (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# modal_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 6: Daytona cloud execution\n# Commands run in Daytona cloud sandboxes\n# Great for: Cloud dev environments, persistent workspaces, team collaboration\n# Requires: pip install daytona, DAYTONA_API_KEY env var\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"daytona\"\n# cwd: \"~\"\n# timeout: 180\n# lifetime_seconds: 300\n# daytona_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# container_disk: 10240 # Daytona max is 10GB per sandbox\n\n#\n# --- Container resource limits (docker, singularity, modal, daytona -- ignored for local/ssh) ---\n# These settings apply to all container backends. They control the resources\n# allocated to the sandbox and whether its filesystem persists across sessions.\n container_cpu: 1 # CPU cores\n container_memory: 5120 # Memory in MB (5120 = 5GB)\n container_disk: 51200 # Disk in MB (51200 = 50GB)\n container_persistent: true # Persist filesystem across sessions (false = ephemeral)\n\n# -----------------------------------------------------------------------------\n# SUDO SUPPORT (works with ALL backends above)\n# -----------------------------------------------------------------------------\n# Add sudo_password to any terminal config above to enable sudo commands.\n# The password is piped via `sudo -S`. Works with local, ssh, docker, etc.\n#\n# SECURITY WARNING: Password stored in plaintext!\n#\n# INTERACTIVE PROMPT: If sudo_password is unset and the CLI is running,\n# you'll be prompted to enter your password when sudo is needed:\n# - 45-second timeout (auto-skips if no input)\n# - Press Enter to skip (command fails gracefully)\n# - Password is hidden while typing\n# - Password is cached for the session\n#\n# EMPTY PASSWORDS: Setting sudo_password to an explicit empty string is different\n# from leaving it unset. Hermes will try an empty password via `sudo -S` and\n# will not open the interactive prompt. This is useful for passwordless sudo,\n# Touch ID sudo setups, and environments where prompting is just noise.\n#\n# ALTERNATIVES:\n# - SSH backend: Configure passwordless sudo on the remote server\n# - Containers: Run as root inside the container (no sudo needed)\n# - Local: Configure /etc/sudoers for specific commands\n#\n# Example (add to your terminal section):\n# sudo_password: \"your-password-here\"\n\n# =============================================================================\n# Security Scanning (tirith)\n# =============================================================================\n# Optional pre-exec command security scanning via tirith.\n# Detects homograph URLs, pipe-to-shell, terminal injection, env manipulation.\n# Install: brew install sheeki03/tap/tirith\n# Docs: https://github.com/sheeki03/tirith\n#\n# security:\n# tirith_enabled: true # Enable/disable tirith scanning\n# tirith_path: \"tirith\" # Path to tirith binary (supports ~ expansion)\n# tirith_timeout: 5 # Scan timeout in seconds\n# tirith_fail_open: true # Allow commands if tirith unavailable\n\n# =============================================================================\n# Browser Tool Configuration\n# =============================================================================\nbrowser:\n # Inactivity timeout in seconds - browser sessions are automatically closed\n # after this period of no activity between agent loops (default: 120 = 2 minutes)\n inactivity_timeout: 120\n\n# =============================================================================\n# Tool Loop Guardrails\n# =============================================================================\n# Soft warnings are enabled by default. They append guidance to repeated failed\n# or non-progressing tool results but still let the tool execute. Hard stops are\n# opt-in circuit breakers for autonomous/cron sessions where stopping a loop is\n# preferable to spending the full iteration budget.\ntool_loop_guardrails:\n warnings_enabled: true\n hard_stop_enabled: false\n warn_after:\n exact_failure: 2\n same_tool_failure: 3\n idempotent_no_progress: 2\n hard_stop_after:\n exact_failure: 5\n same_tool_failure: 8\n idempotent_no_progress: 5\n\n# =============================================================================\n# Context Compression (Auto-shrinks long conversations)\n# =============================================================================\n# When conversation approaches model's context limit, middle turns are\n# automatically summarized to free up space while preserving important context.\n#\n# HOW IT WORKS:\n# 1. Tracks actual token usage from API responses (not estimates)\n# 2. When prompt_tokens >= threshold% of model's context_length, triggers compression\n# 3. Protects first 3 turns (system prompt, initial request, first response)\n# 4. Protects last N turns (default 20 messages = ~10 full turns of recent context)\n# 5. Summarizes middle turns using a fast/cheap model\n# 6. Inserts summary as a user message, continues conversation seamlessly\n#\n# Post-compression tail budget is target_ratio × threshold × context_length:\n# 200K context, threshold 0.50, ratio 0.20 → 20K tokens of recent tail preserved\n# 1M context, threshold 0.50, ratio 0.20 → 100K tokens of recent tail preserved\n#\ncompression:\n # Enable automatic context compression (default: true)\n # Set to false if you prefer to manage context manually or want errors on overflow\n enabled: true\n\n # Opt-in compression progress notices on chat platforms (default: false).\n # By design, routine automatic compression is SILENT on human-facing chat\n # gateways (Telegram, Discord, Slack, ...) — it happens in the background\n # with server-side logging only. Set true to also deliver the routine\n # progress statuses (compacting started, preflight/pre-API compression,\n # idle compaction, retry progress, and the compaction-complete notice) to\n # chat platforms. Unrelated operational noise (auxiliary model failures,\n # provider retry chatter) stays suppressed either way, and compression\n # FAILURE notices + manual /compress feedback are always visible\n # regardless of this setting. (#52995)\n progress_notices: false\n \n # Trigger compression at this % of model's context limit (default: 0.50 = 50%)\n # Lower values = more aggressive compression, higher values = compress later\n # Models with context windows below 512K are floored at 0.75 (raise-only) so\n # compaction doesn't fire with half the window still free; set above 0.75 to override.\n threshold: 0.50\n\n # Per-model threshold overrides: keys are substring-matched against the model\n # name (longest match wins). Useful when some models need different compaction\n # points — e.g. a 1M-context model can compress later (0.30) while a 128K\n # model needs to compress earlier (0.60). The small-context floor (75% for\n # <512K models) still applies on top of per-model overrides.\n # model_thresholds:\n # \"glm-5.2\": 0.40\n # \"claude-sonnet\": 0.35\n # \"gpt-5\": 0.30\n\n # Optional absolute token cap for the compression trigger (default: null = disabled).\n # When set, compression fires at the LOWER of the ratio-based threshold and this\n # absolute token count — first-fires-wins. It never fires later than this count\n # regardless of which model is active (useful when switching between models with\n # very different context windows). Clamped to the model's context length at\n # apply-time, so a cap above the window is a no-op (ratio-based threshold wins).\n # Survives model switches and fallback activations.\n # threshold_tokens: 200000\n\n # Existing Codex gpt-5.5 behavior: raise Hermes' compaction trigger to 85%\n # for the ChatGPT Codex OAuth route. Set false to opt back down to threshold.\n codex_gpt55_autoraise: true\n\n # Fraction of the threshold to preserve as recent tail (default: 0.20 = 20%)\n # e.g. 20% of 50% threshold = 10% of total context kept as recent messages.\n # Summary output is separately capped at 12K tokens (Gemini output limit).\n # Range: 0.10 - 0.80\n target_ratio: 0.20\n\n # Number of most-recent messages to always preserve (default: 20 ≈ 10 full turns)\n # Higher values keep more recent conversation intact at the cost of more aggressive\n # compression of older turns.\n protect_last_n: 20\n\n # Minimum number of REAL (actionable) user messages guaranteed to survive in\n # the uncompressed tail (default: 1 = the existing single last-user anchor,\n # behavior-preserving). Raise to e.g. 3 to keep the last 3 real user turns\n # verbatim even when bulky tool outputs fill the tail token budget — blank\n # platform echoes, compaction handoffs, and synthetic continuation rows never\n # count toward N. The tail can exceed the token budget when this pulls the\n # cut back; the guarantee wins over the budget by design.\n min_tail_user_messages: 1\n\n # Compression retry rounds before a turn gives up with \"max compression\n # attempts reached\" (default: 3, same as the previous hardcoded value).\n # Raise (e.g. 6) for tool-schema-heavy sessions where 3 rounds cannot bring\n # the request estimate under the threshold. Validated >= 1, hard cap 10.\n max_attempts: 3\n\n # Codex app-server (codex CLI runtime) thread-compaction mode. The codex\n # agent owns the real thread context on this runtime, so Hermes' summarizer\n # cannot shrink it — compaction goes through the app server instead.\n # native = let Codex decide when to compact its own thread (default)\n # hermes = let Hermes threshold trigger Codex thread/compact/start\n # off = Hermes will not auto-trigger compaction; Codex may still compact natively\n codex_app_server_auto: native\n\n # Number of non-system messages to protect at the head of the transcript, in\n # ADDITION to the system prompt (which is always implicitly protected).\n # Head messages are NEVER summarized — they survive every compression\n # indefinitely. This gives stable early context for short/medium sessions,\n # but in long-running sessions that rely on rolling compaction the pinned\n # opening turns may not match how you want the session framed over time.\n # Set to 0 to preserve ONLY the system prompt (plus the rolling summary\n # and recent tail) — the cleanest configuration for long-running sessions.\n # Default 3 preserves the system prompt plus the first three non-system\n # head messages, matching the pre-feature behaviour.\n protect_first_n: 3\n\n # Idle compaction (default: 0 = disabled). When > 0, a session that resumes\n # after at least this many seconds of inactivity compacts its accumulated\n # history up front, before the first reply, so a long-lived thread you come\n # back to later doesn't re-read its full stale context on every turn.\n # Time-based, so it complements (does not replace) the size-based `threshold`\n # above. It is skipped when the context is already small (at or below the\n # post-compression target = threshold × target_ratio), so it never wastes a\n # summarization on a short idle thread. Example: 1800 = compact after 30 min idle.\n idle_compact_after_seconds: 0\n\n # Proactive tool-result prune (default: 0 = disabled). Opt-in token trigger\n # for a deterministic, no-LLM prune of OLD tool-result payloads, run\n # independently of `threshold` above. On large-window models (512K/1M) the\n # ratio threshold rarely fires, so bulky tool outputs (terminal dumps, file\n # reads, web extracts) ride along in history and get re-billed every turn.\n # When re-sent history exceeds this many tokens, the prune dedupes identical\n # results, summarizes older oversized ones, and truncates large tool-call\n # arguments — protecting the most recent `protect_last_n` messages and never\n # calling the model. Try 48000 to enable. Built-in compressor engine only;\n # other context engines inherit a safe no-op.\n # NOTE: a committed prune rewrites already-sent history, which invalidates\n # the provider's prompt-cache prefix — the min_reclaim gate below keeps\n # those cache breaks episodic (like a compression boundary) instead of\n # per-turn.\n proactive_prune_tokens: 0\n\n # The prune's summarize pass only touches tool results larger than this many\n # characters (clamped to >= 200 so a generated summary can't be\n # re-summarized). Default 8000.\n proactive_prune_min_result_chars: 8000\n\n # A proactive prune only COMMITS when it reclaims at least this many tokens\n # (measured on the pruned output). This is the prompt-cache hysteresis gate:\n # one meaningful, amortized cache break per batch of stale tool output\n # instead of a tiny break on every tool iteration. 0 = commit any non-zero\n # prune. Default 4096.\n proactive_prune_min_reclaim_tokens: 4096\n\n # To pin a specific model/provider for compression summaries, use the\n # auxiliary section below (auxiliary.compression.provider / model).\n\n# =============================================================================\n# Anthropic prompt caching TTL\n# =============================================================================\n# When prompt caching is active (Claude via OpenRouter or native Anthropic),\n# Anthropic supports two TTL tiers for cached prefixes: \"5m\" (default) and\n# \"1h\". Other values are ignored and \"5m\" is used.\n#\nprompt_caching:\n cache_ttl: \"5m\" # use \"1h\" for long sessions with pauses between turns\n\n# =============================================================================\n# Auxiliary Models (Advanced — Experimental)\n# =============================================================================\n# Hermes uses lightweight \"auxiliary\" models for side tasks: image analysis,\n# browser screenshot analysis, web page summarization, TTS audio-tag insertion,\n# and context compression.\n#\n# By default these use Gemini Flash via OpenRouter or Nous Portal and are\n# auto-detected from your credentials. You do NOT need to change anything\n# here for normal usage.\n#\n# WARNING: Overriding these with providers other than OpenRouter or Nous Portal\n# is EXPERIMENTAL and may not work. Not all models/providers support vision,\n# produce usable summaries, or accept the same API format. Change at your own\n# risk — if things break, reset to \"auto\" / empty values.\n#\n# Each task has its own provider + model pair so you can mix providers.\n# For example: OpenRouter for vision (needs multimodal), but your main\n# local endpoint for compression (just needs text).\n#\n# Provider options:\n# \"auto\" - Best available: OpenRouter → Nous Portal → main endpoint (default)\n# \"openrouter\" - Force OpenRouter (requires OPENROUTER_API_KEY)\n# \"nous\" - Force Nous Portal (requires: hermes auth add nous)\n# \"gemini\" - Force Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n# \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY)\n# \"codex\" - Force Codex OAuth (requires: hermes model → Codex).\n# Uses gpt-5.3-codex which supports vision.\n# \"main\" - Use your custom endpoint (OPENAI_BASE_URL + OPENAI_API_KEY).\n# Works with OpenAI API, local models, or any OpenAI-compatible\n# endpoint. Also falls back to Codex OAuth and API-key providers.\n#\n# Model: leave empty to use the provider's default. When empty, OpenRouter\n# uses \"google/gemini-3-flash-preview\" and Nous uses \"gemini-3-flash\".\n# Other providers pick a sensible default automatically.\n#\n# auxiliary:\n# # Image analysis: vision_analyze tool + browser screenshots\n# vision:\n# provider: \"auto\"\n# model: \"\" # e.g. \"google/gemini-2.5-flash\", \"openai/gpt-4o\"\n# timeout: 30 # LLM API call timeout (seconds)\n# download_timeout: 30 # Image HTTP download timeout (seconds)\n# # Increase for slow connections or self-hosted image servers\n# reasoning_effort: \"\" # Per-task thinking level: none, minimal, low, medium,\n# # high, xhigh, max, ultra. Empty = provider default.\n# # Works on every auxiliary task block (vision,\n# # web_extract, compression, title_generation, curator,\n# # background_review, moa_reference, ...). Example: run\n# # compression at \"low\" and vision at \"none\" to cut\n# # side-task latency/cost on reasoning models.\n#\n# # Web page scraping / summarization + browser page text extraction\n# web_extract:\n# provider: \"auto\"\n# model: \"\"\n# reasoning_effort: \"low\"\n#\n# # Gemini 3.1 TTS hidden audio-tag insertion\n# tts_audio_tags:\n# provider: \"auto\" # empty model = your main chat model\n# model: \"\"\n# timeout: 30\n#\n# # Automatic session title generation after the first exchange\n# title_generation:\n# enabled: true # set false to disable auto-title generation\n# provider: \"auto\"\n# model: \"\"\n# timeout: 30\n# language: \"\" # empty = match the user's language; or e.g. \"English\"\n#\n# # Session search — summarizes matching past sessions\n# session_search:\n# provider: \"auto\"\n# model: \"\"\n# timeout: 30\n# max_concurrency: 3 # Limit parallel summaries to reduce request-burst 429s\n# extra_body: {} # Provider-specific OpenAI-compatible request fields\n# # Example for providers that support request-body\n# # reasoning controls:\n# # extra_body:\n# # enable_thinking: false\n# # Some vLLM/Qwen deployments expect this nested:\n# # extra_body:\n# # chat_template_kwargs:\n# # enable_thinking: false\n\n# =============================================================================\n# Persistent Memory\n# =============================================================================\n# Bounded curated memory injected into the system prompt every session.\n# Two stores: MEMORY.md (agent's notes) and USER.md (user profile).\n# Character limits keep the memory small and focused. The agent manages\n# pruning -- when at the limit, it must consolidate or replace entries.\n# Disabled by default in batch_runner.\n#\nmemory:\n # Agent's personal notes: environment facts, conventions, things learned\n memory_enabled: true\n \n # User profile: preferences, communication style, expectations\n user_profile_enabled: true\n \n # Character limits (~2.75 chars per token, model-independent)\n memory_char_limit: 2200 # ~800 tokens\n user_char_limit: 1375 # ~500 tokens\n\n # Periodic memory nudge: remind the agent to consider saving memories\n # every N user turns. Set to 0 to disable. Only active when memory is enabled.\n nudge_interval: 10 # Nudge every 10 user turns (0 = disabled)\n\n # Memory flush: give the agent one turn to save memories before context is\n # lost (compression, /new, /reset, exit). Set to 0 to disable.\n # For exit/reset, only fires if the session had at least this many user turns.\n flush_min_turns: 6 # Min user turns to trigger flush on exit/reset (0 = disabled)\n\n# =============================================================================\n# Session Reset Policy (Messaging Platforms)\n# =============================================================================\n# Controls when messaging sessions (Telegram, Discord, WhatsApp, Slack) are\n# automatically cleared. Default is \"none\": sessions never auto-reset —\n# conversation context lives until you /reset or /new manually, or context\n# compression kicks in. Opt in to automatic resets if you prefer sessions to\n# clear on a schedule (long-lived context increases API cost per message,\n# though prompt caching and compression keep this manageable).\n#\n# When an automatic reset triggers, the agent first saves important\n# information to its persistent memory — but the conversation context is\n# wiped. The agent starts fresh but retains learned facts via its memory\n# system.\n#\n# Modes:\n# \"none\" - Never auto-reset (default); context lives until /reset or compression\n# \"idle\" - Reset after N minutes of inactivity\n# \"daily\" - Reset at a fixed hour each day\n# \"both\" - Reset on EITHER inactivity timeout or daily boundary\n#\nsession_reset:\n mode: none # \"none\", \"idle\", \"daily\", or \"both\"\n idle_minutes: 1440 # Inactivity timeout in minutes (used by \"idle\"/\"both\")\n at_hour: 4 # Daily reset hour, 0-23 local time (used by \"daily\"/\"both\")\n\n# Maximum number of simultaneously active chat sessions across CLI, TUI,\n# dashboard chat, and messaging gateway. Set to null, 0, or omit to allow\n# unlimited concurrent sessions. When the limit is reached, new sessions get a\n# clean error while existing active sessions keep their normal behavior. This\n# top-level key takes precedence over gateway.max_concurrent_sessions. The cap\n# is a best-effort single-host/profile runtime guard; Hermes fails open if the\n# local runtime lease registry cannot be read or locked.\nmax_concurrent_sessions: null\n\n# When true, group/channel chats use one session per participant when the platform\n# provides a user ID. This is the secure default and prevents users in the same\n# room from sharing context, interrupts, and token costs. Set false only if you\n# explicitly want one shared \"room brain\" per group/channel.\ngroup_sessions_per_user: true\n\n# ─────────────────────────────────────────────────────────────────────────────\n# API Server — per-client model routing\n# ─────────────────────────────────────────────────────────────────────────────\n# Route different API clients to different models/providers on a single\n# Hermes deployment. Clients choose a backend by sending a specific string\n# as the OpenAI ``model`` field. Unmapped model values fall back to the\n# global model configured in the ``model:`` section above, and an explicit\n# session /model override always wins over a route.\n#\n# Configure via the ``platforms.api_server.extra.model_routes`` gateway\n# config block:\n#\n# platforms:\n# api_server:\n# enabled: true\n# extra:\n# key: \"your-api-server-secret\"\n# model_routes:\n# # Xiaozhi clients send model=\"minimax-m2\" → routed to MiniMax via OpenRouter\n# minimax-m2:\n# model: \"minimax/minimax-m1\"\n# provider: \"openrouter\" # optional — overrides global provider\n# # api_key: \"sk-...\" # optional — per-route UPSTREAM provider\n# # key (NOT caller auth; never logged)\n# # base_url: \"https://...\" # optional — per-route base URL\n# # GPT clients keep their own alias\n# gpt-5:\n# model: \"openai/gpt-5\"\n# provider: \"openrouter\"\n#\n# Configured aliases are automatically listed by GET /v1/models so clients\n# can discover them without manual coordination. Caller authentication is\n# unchanged: every request still authenticates with the global API server\n# key (``extra.key`` / API_SERVER_KEY).\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Gateway Streaming\n# ─────────────────────────────────────────────────────────────────────────────\n# Stream tokens to messaging platforms in real-time. The bot sends a message\n# on first token, then progressively edits it as more tokens arrive.\n# Disabled by default — enable to try the streaming UX on Telegram/Discord/Slack.\n# For Telegram, partial edits are sent as plain text and only the final edit uses MarkdownV2.\nstreaming:\n enabled: false\n # transport: edit # \"edit\" = progressive editMessageText\n # edit_interval: 0.3 # seconds between message edits\n # buffer_threshold: 40 # chars before forcing an edit flush\n # cursor: \" ▉\" # cursor shown during streaming\n\n# =============================================================================\n# Skills Configuration\n# =============================================================================\n# Skills are reusable procedures the agent can load and follow. The agent can\n# also create new skills after completing complex tasks.\n#\nskills:\n # Nudge the agent to create skills after complex tasks.\n # Every N tool-calling iterations, remind the model to consider saving a skill.\n # Set to 0 to disable.\n creation_nudge_interval: 15\n\n # External skill directories — share skills across tools/agents without\n # copying them into ~/.hermes/skills/. Each path is expanded (~ and ${VAR})\n # and resolved to an absolute path. External dirs are read-only: skill\n # creation always writes to ~/.hermes/skills/. Local skills take precedence\n # when names collide.\n # external_dirs:\n # - ~/.agents/skills\n # - /home/shared/team-skills\n\n# =============================================================================\n# Agent Behavior\n# =============================================================================\nagent:\n # Maximum tool-calling iterations per conversation (default: 500)\n # Higher = more room for complex tasks, but costs more tokens\n # Recommended: 20-30 for focused tasks, 50-100 for open exploration\n max_turns: 500\n\n # Inactivity timeout for gateway agent runs (seconds, 0 = unlimited).\n # The agent can run indefinitely when actively calling tools or receiving\n # API responses. Only fires after the agent has been idle for this duration.\n # gateway_timeout: 1800\n\n # Staged warning: send a warning before escalating to full timeout.\n # Fires once per run when inactivity reaches this threshold (seconds).\n # Set to 0 to disable the warning.\n # gateway_timeout_warning: 900\n\n # Graceful drain timeout for gateway stop/restart (seconds).\n # Default 0 = no drain: a restart interrupts in-flight agents immediately,\n # cleans up, and exits. Set a positive value only if you want a grace\n # window on /restart, and keep it well under systemd's TimeoutStopSec.\n # restart_drain_timeout: 0\n\n # Upper bound (seconds) a submitted prompt waits for the deferred agent\n # build (MCP discovery, model metadata, skills scan) before failing with a\n # visible error. The wait is patient — the message is delivered as soon as\n # the build completes, and a progress notice is shown past 30s — so this cap\n # only fires on a genuinely hung build. Raise it for deployments with many\n # slow or unreachable MCP servers. Default 600.\n # build_wait_timeout: 600\n\n # Max app-level retry attempts for API errors (connection drops, provider\n # timeouts, 5xx, etc.) before the agent surfaces the failure. Lower this\n # to 1 if you use fallback providers and want fast failover on flaky\n # primaries (default 3). The OpenAI SDK does its own low-level retries\n # underneath this wrapper — this is the Hermes-level loop.\n # api_max_retries: 3\n\n # After the agent edits code without fresh passing verification, nudge it to\n # verify before finishing. The default \"auto\" enables it on interactive\n # coding surfaces (CLI, TUI, desktop) and programmatic callers, and disables\n # it on conversational messaging surfaces (Telegram, Discord, etc.) where the\n # verification summary would reach a human as chat noise. Set true or false to\n # force it on or off; the HERMES_VERIFY_ON_STOP env var (1/0) takes precedence.\n # verify_on_stop: auto\n\n # Standing operator instructions for the coding posture (when Hermes is in a\n # code workspace). Appended to the coding brief as an extra system block, so\n # you can pin project-wide workflow rules without editing the shipped brief.\n # Accepts a string or a list of strings. Takes effect next session.\n # coding_instructions:\n # - \"For UI work, don't run tsc/lint until I approve the look.\"\n # - \"Clean the diff before you commit and push.\"\n\n # When verify-on-stop finds edited code without fresh verification evidence,\n # append guidance for creative UI work (avoid broad tsc/lint/test before visual\n # approval) and clean-diff expectations. Set false to keep that nudge terse.\n # verify_guidance: true\n\n # A `pre_verify` hook (plugin or shell, see Event Hooks docs) can keep the\n # agent going one more turn to verify/clean before finishing. This caps how\n # many times one turn may be nudged to continue, so a hook can't trap the loop.\n # Default 3.\n # max_verify_nudges: 3\n\n # Enable verbose logging\n verbose: false\n \n # Reasoning effort level (OpenRouter and Nous Portal)\n # Controls how much \"thinking\" the model does before responding.\n # Options: \"xhigh\" (max), \"high\", \"medium\", \"low\", \"minimal\", \"none\" (disable)\n reasoning_effort: \"medium\"\n \n # Per-model reasoning effort overrides (optional dict)\n # Key: any sensible model spelling works (exact, dots↔dashes interchangeable,\n # provider prefix optional). First match wins.\n # Value: reasoning effort level (same options as reasoning_effort)\n # Override the global reasoning_effort for that specific model.\n # NOTE: no `hermes config set` support for this key -- edit YAML directly.\n # reasoning_overrides:\n # \"openrouter/anthropic/claude-opus-4.5\": \"xhigh\"\n # \"openai/gpt-5\": \"low\"\n # \"claude-opus-4.6\": \"high\" # bare model name also works\n # \"deepseek/deepseek-v4-pro\": \"xhigh\" # dots and dashes are interchangeable\n reasoning_overrides: {}\n \n # Predefined personalities (use with /personality command)\n personalities:\n helpful: \"You are a helpful, friendly AI assistant.\"\n concise: \"You are a concise assistant. Keep responses brief and to the point.\"\n technical: \"You are a technical expert. Provide detailed, accurate technical information.\"\n creative: \"You are a creative assistant. Think outside the box and offer innovative solutions.\"\n teacher: \"You are a patient teacher. Explain concepts clearly with examples.\"\n kawaii: \"You are a kawaii assistant! Use cute expressions like (◕‿◕), ★, ♪, and ~! Add sparkles and be super enthusiastic about everything! Every response should feel warm and adorable desu~! ヽ(>∀<☆)ノ\"\n catgirl: \"You are Neko-chan, an anime catgirl AI assistant, nya~! Add 'nya' and cat-like expressions to your speech. Use kaomoji like (=^・ω・^=) and ฅ^•ﻌ•^ฅ. Be playful and curious like a cat, nya~!\"\n pirate: \"Arrr! Ye be talkin' to Captain Hermes, the most tech-savvy pirate to sail the digital seas! Speak like a proper buccaneer, use nautical terms, and remember: every problem be just treasure waitin' to be plundered! Yo ho ho!\"\n shakespeare: \"Hark! Thou speakest with an assistant most versed in the bardic arts. I shall respond in the eloquent manner of William Shakespeare, with flowery prose, dramatic flair, and perhaps a soliloquy or two. What light through yonder terminal breaks?\"\n surfer: \"Duuude! You're chatting with the chillest AI on the web, bro! Everything's gonna be totally rad. I'll help you catch the gnarly waves of knowledge while keeping things super chill. Cowabunga! 🤙\"\n noir: \"The rain hammered against the terminal like regrets on a guilty conscience. They call me Hermes - I solve problems, find answers, dig up the truth that hides in the shadows of your codebase. In this city of silicon and secrets, everyone's got something to hide. What's your story, pal?\"\n uwu: \"hewwo! i'm your fwiendwy assistant uwu~ i wiww twy my best to hewp you! *nuzzles your code* OwO what's this? wet me take a wook! i pwomise to be vewy hewpful >w<\"\n philosopher: \"Greetings, seeker of wisdom. I am an assistant who contemplates the deeper meaning behind every query. Let us examine not just the 'how' but the 'why' of your questions. Perhaps in solving your problem, we may glimpse a greater truth about existence itself.\"\n hype: \"YOOO LET'S GOOOO!!! 🔥🔥🔥 I am SO PUMPED to help you today! Every question is AMAZING and we're gonna CRUSH IT together! This is gonna be LEGENDARY! ARE YOU READY?! LET'S DO THIS! 💪😤🚀\"\n\n# =============================================================================\n# Toolsets\n# =============================================================================\n# Control which tools the agent has access to.\n# Use `hermes tools` to interactively enable/disable tools per platform.\n\n# =============================================================================\n# Platform Toolsets (per-platform tool configuration)\n# =============================================================================\n# Override which toolsets are available on each platform.\n# If a platform isn't listed here, its built-in default is used.\n#\n# You can use EITHER:\n# - A preset like \"hermes-cli\" or \"hermes-telegram\" (curated tool set)\n# - A list of individual toolsets to compose your own (see list below)\n#\n# Supported platform keys: cli, telegram, discord, whatsapp, slack, qqbot, teams, google_chat\n#\n# Examples:\n#\n# # Use presets (same as defaults):\n# platform_toolsets:\n# cli: [hermes-cli]\n# telegram: [hermes-telegram]\n#\n# # Custom: give Telegram only web + terminal + file + planning:\n# platform_toolsets:\n# telegram: [web, terminal, file, todo]\n#\n# # Custom: CLI without browser or image gen:\n# platform_toolsets:\n# cli: [web, terminal, file, skills, todo, tts, cronjob]\n#\n# # Restrictive: Discord gets read-only tools only:\n# platform_toolsets:\n# discord: [web, vision, skills, todo]\n#\n# If not set, defaults are:\n# cli: hermes-cli (everything + cronjob management)\n# telegram: hermes-telegram (terminal, file, web, vision, image, tts, browser, skills, todo, cronjob, messaging)\n# discord: hermes-discord (same as telegram)\n# whatsapp: hermes-whatsapp (same as telegram)\n# slack: hermes-slack (same as telegram)\n# signal: hermes-signal (same as telegram)\n# homeassistant: hermes-homeassistant (same as telegram)\n# qqbot: hermes-qqbot (same as telegram)\n# teams: hermes-teams (same as telegram)\n# google_chat: hermes-google_chat (same as telegram)\n#\nplatform_toolsets:\n cli: [hermes-cli]\n telegram: [hermes-telegram]\n discord: [hermes-discord]\n whatsapp: [hermes-whatsapp]\n slack: [hermes-slack]\n signal: [hermes-signal]\n homeassistant: [hermes-homeassistant]\n qqbot: [hermes-qqbot]\n yuanbao: [hermes-yuanbao]\n teams: [hermes-teams]\n google_chat: [hermes-google_chat]\n\n# =============================================================================\n# Gateway Platform Settings\n# =============================================================================\n# Optional per-platform messaging settings.\n# Platform-specific knobs live under `extra`.\n#\n# platforms:\n# telegram:\n# reply_to_mode: \"first\" # off | first | all\n# # guest_mode lets explicit @mentions from non-allowlisted groups through.\n# # Default false; ordinary messages, replies, and regex wake words stay blocked.\n# guest_mode: false\n# # allowed_chats: [\"-1001234567890\"]\n# extra:\n# disable_link_previews: false # Set true to suppress Telegram URL previews in bot messages\n# 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\n# rich_drafts: false # Experimental rich draft previews during Telegram DM streaming; default false because Telegram Desktop/macOS can visually overlay draft frames\n# command_menu:\n# # Telegram allows up to 100 BotCommands; Hermes defaults to 60 so\n# # all built-in commands plus common skill commands stay visible\n# # while remaining under Telegram's payload-size limit. Clamped 1..100.\n# max_commands: 60\n# # prepend = user priority first, then Hermes defaults\n# # append = Hermes defaults first, then user priority\n# # replace = only the list below defines priority\n# priority_mode: prepend\n# priority:\n# - my_plugin_command\n# webhook:\n# extra:\n# # Route scripts default to a 30 second timeout. Scripts must live under\n# # the active profile's scripts directory and receive webhook JSON on stdin.\n# script_timeout_seconds: 30\n#\n# Discord-specific settings (config.yaml top-level, not under platforms:):\n#\n# discord:\n# require_mention: true # Require @mention in server channels (default: true)\n# auto_thread: true # Auto-create thread on @mention (default: true)\n# free_response_channels: \"\" # Channel IDs where no mention is needed\n# reactions: true # Show processing reactions (default: true)\n# history_backfill: true # Recover missed channel messages on mention (default: true)\n# history_backfill_limit: 50 # Max messages to scan backwards (default: 50)\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Available toolsets (use these names in platform_toolsets or the toolsets list)\n#\n# Run `hermes chat --list-toolsets` to see all toolsets and their tools.\n# Run `hermes chat --list-tools` to see every individual tool with descriptions.\n# ─────────────────────────────────────────────────────────────────────────────\n#\n# INDIVIDUAL TOOLSETS (compose your own):\n# web - web_search, web_extract\n# search - web_search only (no scraping)\n# terminal - terminal, process\n# file - read_file, write_file, patch, search\n# browser - browser_navigate, browser_snapshot, browser_click, browser_type,\n# browser_scroll, browser_back, browser_press,\n# browser_get_images, browser_vision (requires BROWSERBASE_API_KEY)\n# vision - vision_analyze (requires OPENROUTER_API_KEY)\n# image_gen - image_generate (requires FAL_KEY)\n# skills - skills_list, skill_view\n# skills_hub - skill_hub (search/install/manage from online registries — user-driven only)\n# todo - todo (in-memory task planning, no deps)\n# tts - text_to_speech (Edge TTS free, or ELEVENLABS/OPENAI/MINIMAX/MISTRAL key)\n# cronjob - cronjob (create/list/update/pause/resume/run/remove scheduled tasks)\n#\n# PRESETS (curated bundles):\n# hermes-cli - All of the above except send_message\n# hermes-telegram - terminal, file, web, vision, image_gen, tts, browser,\n# skills, todo, cronjob, send_message\n# hermes-discord - Same as hermes-telegram\n# hermes-whatsapp - Same as hermes-telegram\n# hermes-slack - Same as hermes-telegram\n#\n# COMPOSITE:\n# debugging - terminal + web + file\n# safe - web + vision (no terminal access)\n# all - Everything available\n#\n# web - Web search and content extraction (web_search, web_extract)\n# search - Web search only, no scraping (web_search)\n# terminal - Command execution and process management (terminal, process)\n# file - File operations: read, write, patch, search\n# browser - Full browser automation (navigate, click, type, screenshot, etc.)\n# vision - Image analysis (vision_analyze)\n# image_gen - Image generation with FLUX (image_generate)\n# skills - Load skill documents (skills_list, skill_view)\n# todo - Task planning and tracking for multi-step work\n# memory - Persistent memory across sessions (personal notes + user profile)\n# session_search - Search and recall past conversations (FTS5 + Gemini Flash summarization)\n# tts - Text-to-speech (Edge TTS free, ElevenLabs, OpenAI, MiniMax, Mistral)\n# cronjob - Schedule and manage automated tasks (CLI-only)\n#\n# Composite toolsets:\n# debugging - terminal + web + file (for troubleshooting)\n# safe - web + vision (no terminal access)\n\n# NOTE: The top-level \"toolsets\" key is deprecated and ignored.\n# Tool configuration is managed per-platform via platform_toolsets above.\n# Use `hermes tools` to configure interactively, or edit platform_toolsets directly.\n#\n# CLI override: hermes chat --toolsets terminal,web,file\n\n# =============================================================================\n# MCP (Model Context Protocol) Servers\n# =============================================================================\n# Connect to external MCP servers to add tools from the MCP ecosystem.\n# Each server's tools are automatically discovered and registered.\n# See website/docs/user-guide/features/mcp.md for full documentation.\n#\n# Stdio servers (spawn a subprocess):\n# command: the executable to run\n# args: command-line arguments\n# env: environment variables (only these + safe defaults passed to subprocess)\n#\n# HTTP servers (connect to a URL):\n# url: the MCP server endpoint\n# headers: HTTP headers (e.g., for authentication)\n#\n# Optional per-server settings:\n# timeout: tool call timeout in seconds (default: 120)\n# connect_timeout: initial connection timeout (default: 60)\n# keepalive_interval: liveness ping cadence in seconds (default: 180).\n# Lower it below the server's session TTL for servers that expire idle\n# sessions quickly (e.g. Unreal Engine editor MCP, ~15s), otherwise idle\n# tool calls hit an expired session and pay a slow reconnect. Floored at 5s.\n#\n# mcp_servers:\n# time:\n# command: uvx\n# args: [\"mcp-server-time\"]\n# filesystem:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-filesystem\", \"/home/user\"]\n# notion:\n# url: https://mcp.notion.com/mcp\n# github:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-github\"]\n# env:\n# GITHUB_PERSONAL_ACCESS_TOKEN: \"ghp_...\"\n#\n# Sampling (server-initiated LLM requests) — enabled by default.\n# Per-server config under the 'sampling' key:\n# analysis:\n# command: npx\n# args: [\"-y\", \"analysis-server\"]\n# sampling:\n# enabled: true # default: true\n# model: \"gemini-3-flash\" # override model (optional)\n# max_tokens_cap: 4096 # max tokens per request\n# timeout: 30 # LLM call timeout (seconds)\n# max_rpm: 10 # max requests per minute\n# allowed_models: [] # model whitelist (empty = all)\n# max_tool_rounds: 5 # tool loop limit (0 = disable)\n# log_level: \"info\" # audit verbosity\n\n# =============================================================================\n# Text-to-Speech\n# =============================================================================\n# TTS defaults to Edge TTS unless changed in ~/.hermes/config.yaml.\n# Gemini TTS supports persona/director prompt files, and Gemini 3.1 Flash TTS\n# can use a hidden auxiliary rewrite pass to insert expressive square-bracket\n# audio tags into the TTS script without showing tags in chat.\n#\n# tts:\n# provider: \"gemini\"\n# gemini:\n# model: \"gemini-3.1-flash-tts-preview\"\n# voice: \"Kore\"\n# audio_tags: false\n# persona_prompt_file: \"\" # e.g. ~/.hermes/tts/radio-host.md\n\n# =============================================================================\n# Voice Transcription (Speech-to-Text)\n# =============================================================================\n# Automatically transcribe voice messages on messaging platforms.\n# Providers: local (free, faster-whisper) | groq (free tier) | openai (Whisper API) | mistral (Voxtral Transcribe)\n# Set the corresponding API key in .env: GROQ_API_KEY, OPENAI_API_KEY, or MISTRAL_API_KEY.\nstt:\n enabled: true\n # provider: \"local\" # auto-detected if omitted\n local:\n model: \"base\" # tiny | base | small | medium | large-v3 | turbo\n # language: \"\" # auto-detect; set to \"en\", \"es\", \"fr\", etc. to force\n # groq:\n # language: \"\" # blank = use HERMES_LOCAL_STT_LANGUAGE if set, else auto-detect\n openai:\n model: \"whisper-1\" # whisper-1 | gpt-4o-mini-transcribe | gpt-4o-transcribe\n language: \"\" # auto-detect; set to \"en\", \"es\", \"fr\", etc. to force\n # mistral:\n # model: \"voxtral-mini-latest\" # voxtral-mini-latest | voxtral-mini-2602\n # deepinfra:\n # # Model id is discovered live from the DeepInfra catalog filtered\n # # by the `stt` surface tag — leave `model` blank to take the first\n # # live result. Pin only when you need a specific Whisper variant.\n # model: \"\"\n\n# Text-to-speech. Only the deepinfra block is documented here — the\n# remaining providers (edge, openai, xai, minimax, mistral, gemini,\n# elevenlabs, neutts, kittentts, piper) inherit sensible defaults from\n# DEFAULT_CONFIG in hermes_cli/config.py.\n# tts:\n# provider: \"deepinfra\"\n# deepinfra:\n# # Model id is discovered live from the DeepInfra catalog filtered\n# # by the `tts` surface tag — leave `model` blank to take the first\n# # live result.\n# model: \"\"\n# voice: \"default\"\n\n# Image generation. Each provider plugin reads its own ``image_gen.<name>``\n# block; deepinfra discovers models live from\n# api.deepinfra.com/v1/openai/models filtered by the ``image-gen`` tag —\n# no model id is hardcoded, so retired models disappear automatically.\n# image_gen:\n# provider: \"deepinfra\"\n# deepinfra:\n# # Leave `model` blank for the first live `image-gen`-tagged result.\n# model: \"\"\n\n# =============================================================================\n# Response Pacing (Messaging Platforms)\n# =============================================================================\n# Add human-like delays between message chunks.\n# human_delay:\n# mode: \"off\" # \"off\" | \"natural\" | \"custom\"\n# min_ms: 800 # Min delay (custom mode only)\n# max_ms: 2500 # Max delay (custom mode only)\n\n# =============================================================================\n# Session Logging\n# =============================================================================\n# Session trajectories are automatically saved to logs/ directory.\n# Each session creates: logs/session_YYYYMMDD_HHMMSS_UUID.json\n#\n# The session ID is displayed in the welcome banner for easy reference.\n# Logs contain full conversation history in trajectory format:\n# - System prompt, user messages, assistant responses\n# - Tool calls with inputs/outputs\n# - Timestamps for debugging\n#\n# No configuration needed - logging is always enabled.\n# To disable, you would need to modify the source code.\n\n# =============================================================================\n# Code Execution Sandbox (Programmatic Tool Calling)\n# =============================================================================\n# The execute_code tool runs Python scripts that call Hermes tools via RPC.\n# Intermediate tool results stay out of the LLM's context window.\ncode_execution:\n timeout: 300 # Max seconds per script before kill (default: 300 = 5 min)\n max_tool_calls: 50 # Max RPC tool calls per execution (default: 50)\n\n# =============================================================================\n# Subagent Delegation\n# =============================================================================\n# The delegate_task tool spawns child agents with isolated context.\n# Supports single tasks and batch mode (default 3 parallel, configurable).\ndelegation:\n max_iterations: 50 # Max tool-calling turns per child (default: 50)\n # max_concurrent_children: 3 # Max parallel child agents per batch (default: 3, floor: 1, no ceiling).\n # WARNING: values above 10 multiply API cost linearly.\n # max_spawn_depth: 1 # Delegation tree depth cap (range: 1-3, default: 1 = flat).\n # Raise to 2 to allow workers to spawn their own subagents.\n # Requires role=\"orchestrator\" on intermediate agents.\n # orchestrator_enabled: true # Kill switch for role=\"orchestrator\" children (default: true).\n # subagent_auto_approve: false # When a subagent hits a dangerous-command approval prompt, auto-deny (default: false)\n # or auto-approve \"once\" (true) instead of blocking on stdin.\n # The parent TUI owns stdin, so blocking would deadlock; non-interactive resolution is required.\n # Both choices emit a logger.warning audit line. Flip to true only for cron/batch pipelines.\n # inherit_mcp_toolsets: true # When explicit child toolsets are narrowed, also keep the parent's MCP toolsets (default: true). Set false for strict intersection.\n # model: \"google/gemini-3-flash-preview\" # Override model for subagents (empty = inherit parent)\n # provider: \"openrouter\" # Override provider for subagents (empty = inherit parent)\n # # Resolves full credentials (base_url, api_key) automatically.\n # # Supported: openrouter, nous, zai, kimi-coding, minimax\n\n# =============================================================================\n# Honcho Integration (Cross-Session User Modeling)\n# =============================================================================\n# AI-native persistent memory via Honcho (https://honcho.dev/).\n# Builds a deeper understanding of the user across sessions and tools.\n# Runs alongside USER.md — additive, not a replacement.\n#\n# Requires: pip install honcho-ai\n# Config: ~/.honcho/config.json (shared with Claude Code, Cursor, etc.)\n# API key: HONCHO_API_KEY in ~/.hermes/.env or ~/.honcho/config.json\n#\n# Hermes-specific overrides (optional — most config comes from ~/.honcho/config.json):\n# honcho: {}\n\n# =============================================================================\n# Display\n# =============================================================================\ndisplay:\n # Use compact banner mode (hides the ASCII-art banner, shows a single line).\n # true: Compact single-line banner\n # false: Full ASCII banner with tool/skill summary (default)\n compact: false\n\n # Tool progress display level (CLI and gateway)\n # off: Silent — no tool activity shown, just the final response\n # new: Show a tool indicator only when the tool changes (skip repeats)\n # all: Show every tool call with a short preview (default)\n # verbose: Full args, results, and debug logs (same as /verbose)\n # log: Silent in chat; append every tool call to ~/.hermes/logs/tool_calls.log (gateway only)\n # Toggle at runtime with /verbose in the CLI\n tool_progress: all\n\n # Per-platform defaults can be quieter than the global setting. Telegram\n # tunes for mobile: tool_progress and busy_ack_detail default off (no\n # per-tool breadcrumb stream, no \"iteration 21/60\" debug detail in busy\n # acks or heartbeats), but interim_assistant_messages and\n # long_running_notifications STAY ON so the user has real signal between\n # turn start and final answer (mid-turn assistant commentary + a single\n # edit-in-place \"⏳ Working — N min\" heartbeat). Override under\n # display.platforms.telegram.\n\n # Auto-cleanup of temporary progress bubbles after the final response lands.\n # On platforms that support message deletion (currently Telegram), this\n # removes the tool-progress bubble, \"⏳ Still working...\" notices, and\n # context-pressure status messages once the final reply has been delivered —\n # keeping long-running turns visible live, then tidy afterward. Failed runs\n # leave the bubbles in place as breadcrumbs. Off by default.\n # Per-platform override: display.platforms.telegram.cleanup_progress\n # true: Delete tracked progress/status bubbles on successful turn\n # false: Leave everything in place (default)\n # Example:\n # display:\n # platforms:\n # telegram:\n # cleanup_progress: true\n cleanup_progress: false\n\n # Natural mid-turn assistant updates.\n # On gateway platforms, when true, completed assistant status messages are\n # sent as separate chat messages. On the Desktop app, when true, mid-turn\n # assistant narration streamed between tool calls is kept in the transcript\n # instead of the bubble collapsing to only the final message on completion.\n # Independent of tool_progress and gateway streaming.\n # true: Keep/send mid-turn assistant updates (default)\n # false: Only keep/send the final response\n interim_assistant_messages: true\n\n # Gateway-only long-running status heartbeats.\n # When false, the platform does not receive periodic \"⏳ Working — N min\"\n # notifications even if agent.gateway_notify_interval is non-zero. The\n # heartbeat edits a single message in place (where the adapter supports\n # editing) instead of posting a new bubble each interval.\n # Default: true everywhere, including Telegram (silent agents are worse\n # than a single edit-in-place heartbeat).\n long_running_notifications: true\n\n # Include detailed iteration/tool/status context in busy acknowledgments\n # and long-running heartbeats. When true, busy acks show \"iteration 21/60,\n # terminal, 10 min\" and the heartbeat shows \"⏳ Working — 12 min,\n # iteration 21/60, terminal\". When false (Telegram default), both stay\n # terse: \"Interrupting current task\" and \"⏳ Working — 12 min, terminal\".\n busy_ack_detail: true\n\n # What Enter does when Hermes is already busy (CLI and gateway platforms).\n # interrupt: Interrupt the current run and redirect Hermes (default)\n # queue: Queue your message for the next turn\n # steer: Inject your message mid-run via /steer, arriving at the agent\n # after the next tool call — no interrupt, no role violation.\n # Falls back to 'queue' if the agent isn't running yet or if\n # images are attached (steer only carries text).\n # Ctrl+C (or /stop in gateway) always interrupts regardless of this setting.\n # Toggle at runtime with /busy <interrupt|queue|steer>.\n busy_input_mode: interrupt\n\n # Background process notifications (gateway/messaging only).\n # Controls how chatty the process watcher is when you use\n # terminal(background=true, notify_on_complete=true) from Telegram/Discord/etc.\n # off: No watcher messages at all\n # result: Only the final completion message\n # error: Only the final message when exit code != 0\n # all: Running output updates + final message (default)\n background_process_notifications: all\n\n\n # Play terminal bell when agent finishes a response.\n # Useful for long-running tasks — your terminal will ding when the agent is done.\n # Works over SSH. Most terminals can be configured to flash the taskbar or play a sound.\n # true: Ring the terminal bell on each response\n # false: Silent (default)\n bell_on_complete: false\n\n # Show model reasoning/thinking before each response.\n # When enabled, a dim box shows the model's thought process above the response.\n # Toggle at runtime with /reasoning show or /reasoning hide.\n # true: Show the reasoning box\n # false: Hide reasoning (default)\n show_reasoning: false\n\n # Stream tokens to the terminal as they arrive instead of waiting for the\n # full response. The response box opens on first token and text appears\n # line-by-line. Tool calls are still captured silently.\n # true: Stream tokens as they arrive (default)\n # false: Wait for the full response before rendering\n streaming: true\n\n # Show [HH:MM] timestamps on user input and assistant response labels.\n # timestamps: false\n\n # ───────────────────────────────────────────────────────────────────────────\n # Skin / Theme\n # ───────────────────────────────────────────────────────────────────────────\n # Customize CLI visual appearance — banner colors, spinner faces, tool prefix,\n # response box label, and branding text. Change at runtime with /skin <name>.\n #\n # Built-in skins:\n # default — Classic Hermes gold/kawaii\n # ares — Crimson/bronze war-god theme with spinner wings\n # mono — Clean grayscale monochrome\n # slate — Cool blue developer-focused\n # daylight — Bright light-mode theme\n # warm-lightmode — Warm paper-tone light-mode theme\n # poseidon — Sea-green/teal Olympian theme\n # sisyphus — Earthy stone-and-moss theme\n # charizard — Fiery orange dragon theme\n #\n # Custom skins: drop a YAML file in ~/.hermes/skins/<name>.yaml\n # Schema (all fields optional, missing values inherit from default):\n #\n # name: my-theme\n # description: Short description\n # colors:\n # banner_border: \"#HEX\" # Panel border\n # banner_title: \"#HEX\" # Panel title\n # banner_accent: \"#HEX\" # Section headers (Available Tools, etc.)\n # banner_dim: \"#HEX\" # Dim/muted text\n # banner_text: \"#HEX\" # Body text (tool names, skill names)\n # ui_accent: \"#HEX\" # UI accent color\n # response_border: \"#HEX\" # Response box border color\n # spinner:\n # waiting_faces: [\"(⚔)\", \"(⛨)\"] # Faces shown while waiting\n # thinking_faces: [\"(⚔)\", \"(⌁)\"] # Faces shown while thinking\n # thinking_verbs: [\"forging\", \"plotting\"] # Verbs for spinner messages\n # wings: # Optional left/right spinner decorations\n # - [\"⟪⚔\", \"⚔⟫\"]\n # - [\"⟪▲\", \"▲⟫\"]\n # branding:\n # agent_name: \"My Agent\" # Banner title and branding\n # welcome: \"Welcome message\" # Shown at CLI startup\n # response_label: \" ⚔ Agent \" # Response box header label\n # prompt_symbol: \"⚔\" # Prompt symbol (bare token; renderers add trailing space)\n # tool_prefix: \"╎\" # Tool output line prefix (default: ┊)\n #\n skin: default\n\n# =============================================================================\n# Model Aliases — short names for /model command\n# =============================================================================\n# Map short aliases to exact (model, provider, base_url) tuples.\n# Used by /model tab completion and resolve_alias().\n# Aliases are checked BEFORE the models.dev catalog, so they can route\n# to endpoints not in the catalog (e.g. Ollama Cloud, local servers).\n#\n# model_aliases:\n# opus:\n# model: claude-opus-4-6\n# provider: anthropic\n# qwen:\n# model: \"qwen3.5:397b\"\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n# glm:\n# model: glm-4.7\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n\n# =============================================================================\n# Privacy\n# =============================================================================\n# privacy:\n# # Redact PII from the LLM context prompt.\n# # When true, phone numbers are stripped and user/chat IDs are replaced\n# # with deterministic hashes before being sent to the model.\n# # Names and usernames are NOT affected (user-chosen, publicly visible).\n# # Routing/delivery still uses the original values internally.\n# redact_pii: false\n\n# =============================================================================\n# Shell-script hooks\n# =============================================================================\n# Register shell scripts as plugin-hook callbacks. Each entry is executed as\n# a subprocess (shell=False, shlex.split) with a JSON payload on stdin. On\n# stdout the script may return JSON that either blocks the tool call or\n# injects context into the next LLM call.\n#\n# Valid events (mirror hermes_cli.plugins.VALID_HOOKS):\n# pre_tool_call, post_tool_call, pre_llm_call, post_llm_call,\n# pre_api_request, post_api_request, on_session_start, on_session_end,\n# on_session_finalize, on_session_reset, subagent_stop\n#\n# First-use consent: each (event, command) pair prompts once on a TTY, then\n# is persisted to ~/.hermes/shell-hooks-allowlist.json. Non-interactive\n# runs (gateway, cron) need --accept-hooks, HERMES_ACCEPT_HOOKS=1, or the\n# hooks_auto_accept key below.\n#\n# See website/docs/user-guide/features/hooks.md for the full JSON wire\n# protocol and worked examples.\n#\n# hooks:\n# pre_tool_call:\n# - matcher: \"terminal\"\n# command: \"~/.hermes/agent-hooks/block-rm-rf.sh\"\n# timeout: 10\n# post_tool_call:\n# - matcher: \"write_file|patch\"\n# command: \"~/.hermes/agent-hooks/auto-format.sh\"\n# pre_llm_call:\n# - command: \"~/.hermes/agent-hooks/inject-cwd-context.sh\"\n# subagent_stop:\n# - command: \"~/.hermes/agent-hooks/log-orchestration.sh\"\n#\n# hooks_auto_accept: false\n\n\n# =============================================================================\n# Update Behavior\n# =============================================================================\nupdates:\n # Create a full HERMES_HOME zip before every `hermes update`.\n # Backups land in ~/.hermes/backups/ and can be restored with `hermes import`.\n # Off by default because large homes can add minutes to every update.\n pre_update_backup: false\n\n # Number of pre-update backup zips to retain.\n backup_keep: 5\n\n # What non-interactive updates do with local source edits in the Hermes repo.\n # Interactive terminal updates always prompt before restoring the autostash.\n #\n # stash - auto-stash before pull, then auto-restore after success (default)\n # discard - drop the update-created stash after success; use only on managed\n # installs where local source edits should not persist\n non_interactive_local_changes: \"stash\"\n\n\n# =============================================================================\n# Web Dashboard\n# =============================================================================\n# OAuth gate configuration for `hermes dashboard --host <non-loopback>`.\n# The bundled Nous Portal plugin reads these on startup; settings here are\n# the canonical surface. Each can be overridden by an environment variable:\n#\n# dashboard.oauth.client_id <- HERMES_DASHBOARD_OAUTH_CLIENT_ID\n# dashboard.oauth.portal_url <- HERMES_DASHBOARD_PORTAL_URL\n# dashboard.public_url <- HERMES_DASHBOARD_PUBLIC_URL\n#\n# Env wins when set to a non-empty value. This is what Fly.io's platform-\n# secret injection uses to push per-deploy client_ids without needing to\n# bake a config.yaml into the image. Empty env values are treated as unset\n# so a provisioned-but-not-populated secret can't shadow a valid entry here.\n#\n# Local dev / on-prem deploys should typically set these via config.yaml\n# (the ~/.hermes/.env file is reserved for API keys and secrets).\n#\n# dashboard:\n# oauth:\n# client_id: \"\" # agent:{instance_id}; Portal provisions this at deploy\n# portal_url: \"\" # blank → default https://portal.nousresearch.com\n#\n# # Force the absolute base URL the OAuth callback (and any other public\n# # URL the dashboard hands to external systems) is built from. Set this\n# # for deploys behind reverse proxies that don't reliably forward\n# # X-Forwarded-Host / X-Forwarded-Proto / X-Forwarded-Prefix (manual\n# # nginx setups, on-prem ingresses, custom-domain Fly deploys without\n# # full proxy header chains).\n# #\n# # When set, the value is the complete authority: scheme + host +\n# # optional path prefix (e.g. \"https://example.com/hermes\"). The OAuth\n# # callback URL becomes \"<public_url>/auth/callback\" — X-Forwarded-Prefix\n# # is IGNORED on this code path because the operator has explicitly\n# # declared the public URL and we no longer need to guess.\n# #\n# # Leave empty to use the existing proxy-header reconstruction (the\n# # default — works on Fly.io out of the box).\n# #\n# # public_url: \"https://example.com/hermes\"\n#\n# -----------------------------------------------------------------------------\n# Self-hosted OIDC dashboard auth (generic OpenID Connect — Authentik,\n# Keycloak, Zitadel, Authelia, Auth0, Okta, Google, …). Use this INSTEAD of the\n# nous block above when gating the dashboard with your own identity provider.\n# Each setting can be overridden by an environment variable:\n#\n# dashboard.oauth.self_hosted.issuer <- HERMES_DASHBOARD_OIDC_ISSUER\n# dashboard.oauth.self_hosted.client_id <- HERMES_DASHBOARD_OIDC_CLIENT_ID\n# dashboard.oauth.self_hosted.scopes <- HERMES_DASHBOARD_OIDC_SCOPES\n# dashboard.oauth.self_hosted.client_secret <- HERMES_DASHBOARD_OIDC_CLIENT_SECRET\n#\n# dashboard:\n# oauth:\n# provider: self-hosted\n# self_hosted:\n# issuer: \"https://auth.example.com/application/o/hermes/\" # required\n# client_id: \"hermes-dashboard\" # required\n# scopes: \"openid profile email\" # optional\n#\n# # OPTIONAL — set ONLY if your IDP registered the client as\n# # *confidential* (Authentik / Keycloak often default to this). When\n# # set, Hermes authenticates the client at the token endpoint\n# # (client_secret_basic or client_secret_post, auto-selected from the\n# # IDP's discovery doc) IN ADDITION to PKCE. Leave unset for a public\n# # (PKCE-only) client — the common case.\n# #\n# # This is a CREDENTIAL: prefer setting HERMES_DASHBOARD_OIDC_CLIENT_SECRET\n# # in ~/.hermes/.env over putting it here in config.yaml.\n# # client_secret: \"\"\n\n# =============================================================================\n# External secret sources\n# =============================================================================\n# Pull provider credentials from external secret managers at process startup\n# instead of storing them in ~/.hermes/.env. Only the manager's bootstrap\n# credential (e.g. BWS_ACCESS_TOKEN / OP_SERVICE_ACCOUNT_TOKEN) lives in .env\n# (or your shell / desktop session); everything else rotates centrally.\n# Failures never block startup — Hermes warns once and continues with\n# whatever .env already had.\n#\n# Multiple sources can be enabled at once:\n# - \"mapped\" sources (explicit VAR -> ref bindings, e.g. 1Password's env:\n# map) beat \"bulk\" sources (whole-project dumps like Bitwarden BSM)\n# - within a shape, the first source to claim a var wins; later claims\n# are skipped with a startup warning (never a silent clobber)\n# - a source's override_existing lets it beat .env/shell values, but\n# never another secret source's claim\n# Docs: https://hermes-agent.nousresearch.com/docs/user-guide/secrets/\n#\n# secrets:\n# # Optional explicit ordering of enabled sources.\n# # sources: [onepassword, bitwarden]\n#\n# # ---- Bitwarden Secrets Manager (bws CLI) --------------------------------\n# bitwarden:\n# enabled: false\n# access_token_env: BWS_ACCESS_TOKEN # bootstrap token, sourced from .env\n# project_id: \"\" # UUID of the BSM project to sync\n# server_url: \"\" # \"\" = US Cloud; EU/self-hosted URL otherwise\n# cache_ttl_seconds: 300 # 0 disables fresh caching\n# encrypted_cache: # optional encrypted stale fallback\n# enabled: false\n# max_stale_seconds: 0 # 0 disables stale fallback\n# override_existing: true # BSM values win over existing env\n# auto_install: true # lazy-download bws into ~/.hermes/bin\n#\n# # ---- 1Password (op CLI) -------------------------------------------------\n# onepassword:\n# enabled: false\n# # Map env-var names to op:// secret references. Each is resolved with a\n# # single `op read` at startup.\n# env:\n# OPENAI_API_KEY: \"op://Private/OpenAI/api key\"\n# ANTHROPIC_API_KEY: \"op://Private/Anthropic/credential\"\n# account: \"\" # op --account shorthand; \"\" = default\n# service_account_token_env: OP_SERVICE_ACCOUNT_TOKEN # headless auth; unset = desktop session\n# binary_path: \"\" # \"\" = resolve op via PATH; else absolute path\n# cache_ttl_seconds: 300 # 0 disables BOTH cache layers\n# override_existing: true # resolved values win over existing env\n#\n# # ---- Command helper (any CLI vault) --------------------------------------\n# # Run a user-configured helper that prints KEY=VALUE lines on stdout —\n# # works with any secret store that has a CLI: keepassxc-cli, secret-tool,\n# # pass, gpg, or a script that cats a tmpfs env file. Composes with the\n# # sources above (enable any combination). POSIX-only (needs /bin/sh).\n# # The helper must be fast and NON-interactive (hard timeout, 1 MiB cap);\n# # its stderr is discarded so diagnostics can't leak secret material.\n# command:\n# enabled: false\n# command: \"cat /run/user/1000/hermes-secrets.env\"\n# helper_timeout_seconds: 3\n# override_existing: false # .env/shell win by default\n"}, "a355db1ef5d3d0e2b94e519ccd9e6da81110c665": {"t": 1785767992.0431817, "n": 83777, "h": "8e661f51c78678cb", "c": "# Hermes Agent CLI Configuration\n# Copy this file to cli-config.yaml and customize as needed.\n# This file configures the CLI behavior. Environment variables in .env take precedence.\n\n# =============================================================================\n# Model Configuration\n# =============================================================================\nmodel:\n # Default model to use (can be overridden with --model flag)\n # Both \"default\" and \"model\" work as the key name here.\n default: \"anthropic/claude-opus-4.6\"\n \n # Inference provider selection:\n # \"auto\" - Auto-detect from credentials (default)\n # \"openrouter\" - OpenRouter (requires: OPENROUTER_API_KEY or OPENAI_API_KEY)\n # \"nous\" - Nous Portal OAuth (requires: hermes auth add nous)\n # \"nous-api\" - Nous Portal API key (requires: NOUS_API_KEY)\n # \"anthropic\" - Direct Anthropic API (requires: ANTHROPIC_API_KEY)\n # \"openai-codex\" - OpenAI Codex (requires: hermes auth)\n # \"copilot\" - GitHub Copilot / GitHub Models (requires: GITHUB_TOKEN)\n # \"gemini\" - Use Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n # \"zai\" - Use z.ai / ZhipuAI GLM models (requires: GLM_API_KEY)\n # \"kimi-coding\" - Kimi / Moonshot AI (requires: KIMI_API_KEY)\n # \"minimax\" - MiniMax global (requires: MINIMAX_API_KEY)\n # \"minimax-cn\" - MiniMax China (requires: MINIMAX_CN_API_KEY)\n # \"huggingface\" - Hugging Face Inference (requires: HF_TOKEN)\n # \"nvidia\" - NVIDIA NIM / build.nvidia.com (requires: NVIDIA_API_KEY)\n # \"xiaomi\" - Xiaomi MiMo (requires: XIAOMI_API_KEY)\n # \"arcee\" - Arcee AI Trinity models (requires: ARCEEAI_API_KEY)\n # \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY — https://ollama.com/settings)\n # \"deepinfra\" - DeepInfra (requires: DEEPINFRA_API_KEY)\n # \"kilocode\" - KiloCode gateway (requires: KILOCODE_API_KEY)\n # \"azure-foundry\" - Microsoft Foundry / Azure OpenAI (API key or Entra ID)\n # \"lmstudio\" - LM Studio local server (optional: LM_API_KEY, defaults to http://127.0.0.1:1234/v1)\n #\n # Local servers (LM Studio, Ollama, vLLM, llama.cpp):\n # \"custom\" - Any other OpenAI-compatible endpoint. Set base_url below.\n # Aliases: \"ollama\", \"vllm\", \"llamacpp\" all map to \"custom\".\n # LM Studio is first-class and uses provider: \"lmstudio\".\n # It works with both no-auth and auth-enabled server modes.\n #\n # Can also be overridden for a single invocation with the --provider flag.\n provider: \"auto\"\n \n # API configuration (falls back to OPENROUTER_API_KEY env var)\n # api_key: \"your-key-here\" # Uncomment to set here instead of .env\n base_url: \"https://openrouter.ai/api/v1\"\n\n # Azure Foundry keyless auth example:\n # provider: \"azure-foundry\"\n # base_url: \"https://<resource>.openai.azure.com/openai/v1\"\n # auth_mode: \"entra_id\" # DefaultAzureCredential: az login, managed identity, workload identity, etc.\n # default: \"gpt-4o\" # Deployment/model name\n # entra:\n # scope: \"https://ai.azure.com/.default\" # Optional; this is the default.\n\n # ── Token limits — two settings, easy to confuse ──────────────────────────\n #\n # context_length: TOTAL context window (input + output tokens combined).\n # Controls when Hermes compresses history and validates requests.\n # Leave unset — Hermes auto-detects the correct value from the provider.\n # Set manually only when auto-detection is wrong (e.g. a local server with\n # a custom num_ctx, or a proxy that doesn't expose /v1/models).\n #\n # context_length: 131072\n #\n # max_tokens: OUTPUT cap — maximum tokens the model may generate per response.\n # Unrelated to how long your conversation history can be.\n # The OpenAI-standard name \"max_tokens\" is a misnomer; Anthropic's native\n # API has since renamed it \"max_output_tokens\" for clarity.\n # Leave unset to use the model's native output ceiling (recommended).\n # Set only if you want to deliberately limit individual response length.\n #\n# max_tokens: 8192\n\n # ── Custom request headers (optional) ─────────────────────────────────────\n #\n # default_headers: extra HTTP headers sent on every request to an\n # OpenAI-compatible endpoint. User values take precedence over the\n # provider/SDK defaults, so this is the supported way to override the\n # OpenAI Python SDK's identifying headers (User-Agent: OpenAI/Python ...,\n # X-Stainless-*) when a custom provider sits behind a gateway/WAF that\n # rejects them — e.g. an upstream that returns \"502 Upstream access\n # forbidden\" for the SDK default User-Agent but accepts a plain one.\n # Applies on the OpenAI wire only (not native Anthropic / Bedrock).\n #\n # default_headers:\n # User-Agent: \"curl/8.7.1\"\n #\n # extra_headers: accepted as an alias of default_headers (merged, with\n # extra_headers winning when both are set) — matches the per-provider\n # extra_headers key below.\n #\n # Per-provider variant: named providers / custom_providers entries accept an\n # extra_headers dict scoped to that endpoint only — for reverse proxies,\n # gateways, or custom auth (e.g. Cloudflare Access service tokens).\n # Merged onto SDK/provider defaults with the entry's values winning.\n # Header values are treated as secrets and are never logged.\n #\n # providers:\n # my-proxy:\n # base_url: \"https://llm.internal.example.com/v1\"\n # key_env: \"MY_PROXY_API_KEY\"\n # extra_headers:\n # CF-Access-Client-Id: \"xxxx.access\"\n # CF-Access-Client-Secret: \"${CF_ACCESS_SECRET}\"\n # X-Client-Name: \"hermes-agent\"\n\n# Named provider overrides (optional)\n# Use this for per-provider request timeouts, non-stream stale timeouts,\n# and per-model exceptions.\n# Applies to the primary turn client on every api_mode (OpenAI-wire, native\n# Anthropic, and Anthropic-compatible providers), the fallback chain, and\n# client rebuilds during credential rotation. For OpenAI-wire chat\n# completions (streaming and non-streaming) the configured value is also\n# used as the per-request ``timeout=`` kwarg so it wins over the legacy\n# HERMES_API_TIMEOUT env var (which still applies when no config is set).\n# ``stale_timeout_seconds`` controls the non-streaming stale-call detector and\n# wins over the legacy HERMES_API_CALL_STALE_TIMEOUT env var. Leaving these\n# unset keeps the legacy defaults (HERMES_API_TIMEOUT=1800s,\n# HERMES_API_CALL_STALE_TIMEOUT=90s, native Anthropic 900s). The\n# implicit non-stream stale detector is auto-disabled for local endpoints\n# and can scale upward for very large contexts.\n#\n# Not currently wired for AWS Bedrock (bedrock_converse + AnthropicBedrock\n# SDK paths) — those use boto3 with its own timeout configuration.\n#\n# providers:\n# ollama-local:\n# request_timeout_seconds: 300 # Longer timeout for local cold-starts\n# stale_timeout_seconds: 900 # Explicitly re-enable stale detection on local endpoints\n# anthropic:\n# request_timeout_seconds: 30 # Fast-fail cloud requests\n# models:\n# claude-opus-4.6:\n# timeout_seconds: 600 # Longer timeout for extended-thinking Opus calls\n# openai-codex:\n# models:\n# gpt-5.4:\n# stale_timeout_seconds: 1800 # Longer non-stream stale timeout for slow large-context turns\n\n# =============================================================================\n# OpenRouter Provider Routing (only applies when using OpenRouter)\n# =============================================================================\n# Control how requests are routed across providers on OpenRouter.\n# See: https://openrouter.ai/docs/guides/routing/provider-selection\n#\n# provider_routing:\n# # Sort strategy: \"price\" (default), \"throughput\", or \"latency\"\n# # Append :nitro to model name for a shortcut to throughput sorting.\n# sort: \"throughput\"\n#\n# # Only allow these providers (provider slugs from OpenRouter)\n# # only: [\"anthropic\", \"google\"]\n#\n# # Skip these providers entirely\n# # ignore: [\"deepinfra\", \"fireworks\"]\n#\n# # Try providers in this order (overrides default load balancing)\n# # order: [\"anthropic\", \"google\", \"together\"]\n#\n# # Require providers to support all parameters in your request\n# # require_parameters: true\n#\n# # Data policy: \"allow\" (default) or \"deny\" to exclude providers that may store data\n# # data_collection: \"deny\"\n\n# =============================================================================\n# OpenRouter Response Caching (only applies when using OpenRouter)\n# =============================================================================\n# Cache identical API responses at the OpenRouter edge for free instant replays.\n# When enabled, identical requests (same model, messages, parameters) return\n# cached responses with zero billing. Separate from Anthropic prompt caching.\n# See: https://openrouter.ai/docs/guides/features/response-caching\n#\n# openrouter:\n# response_cache: true # Enable response caching (default: true)\n# response_cache_ttl: 300 # Cache TTL in seconds, 1-86400 (default: 300)\n\n# =============================================================================\n# Git Worktree Isolation\n# =============================================================================\n# When enabled, each CLI session creates an isolated git worktree so multiple\n# agents can work on the same repo concurrently without file collisions.\n# Equivalent to always passing --worktree / -w on the command line.\n#\n# worktree: true # Always create a worktree when in a git repo\n# worktree: false # Default — only create when -w flag is passed\n#\n# By default a new worktree branches from the freshly-fetched remote tip\n# (the current branch's upstream, else the remote's default branch) so it\n# starts current with the project instead of from the local clone's\n# (possibly stale) HEAD. Set worktree_sync: false to branch from local HEAD\n# instead — useful when offline or when you deliberately want the clone's\n# exact current state as the base.\n#\n# worktree_sync: true # Default — branch from the fetched remote tip\n# worktree_sync: false # Branch from local HEAD (offline / pinned base)\n\n# =============================================================================\n# Terminal Tool Configuration\n# =============================================================================\n# Choose ONE of the following terminal configurations by uncommenting it.\n# The terminal tool executes commands in the specified environment.\n\n# -----------------------------------------------------------------------------\n# OPTION 1: Local execution (default)\n# Commands run directly on your machine in the current directory\n# -----------------------------------------------------------------------------\n# Working directory behavior:\n# - CLI (`hermes` command): Uses \".\" (current directory where you run hermes)\n# - Gateway/messaging/cron: Uses terminal.cwd here; legacy .env cwd values are deprecated\nterminal:\n backend: \"local\"\n cwd: \".\" # For local backend: \".\" = current directory. Ignored for remote backends unless a backend documents otherwise.\n timeout: 180\n # HOME policy for tool subprocesses:\n # auto - default: host uses your real HOME; containers use HERMES_HOME/home\n # real - force your real OS-user HOME\n # profile - force HERMES_HOME/home for strict per-profile CLI config isolation\n home_mode: \"auto\"\n docker_mount_cwd_to_workspace: false # SECURITY: off by default. Opt in to mount the launch cwd into Docker /workspace.\n lifetime_seconds: 300\n # sudo_password: \"hunter2\" # Optional: pipe a sudo password via sudo -S. SECURITY WARNING: plaintext.\n # sudo_password: \"\" # Explicit empty password: try empty and never open the interactive sudo prompt.\n\n# -----------------------------------------------------------------------------\n# OPTION 2: SSH remote execution\n# Commands run on a remote server - agent code stays local (sandboxed)\n# Great for: keeping agent isolated from its own code, using powerful remote hardware\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"ssh\"\n# cwd: \"/home/myuser/project\" # Path on the REMOTE server\n# timeout: 180\n# lifetime_seconds: 300\n# ssh_host: \"my-server.example.com\"\n# ssh_user: \"myuser\"\n# ssh_port: 22\n# ssh_key: \"~/.ssh/id_rsa\" # Optional - uses ssh-agent if not specified\n\n# -----------------------------------------------------------------------------\n# OPTION 3: Docker container\n# Commands run in an isolated Docker container\n# Great for: reproducible environments, testing, isolation\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"docker\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /)\n# timeout: 180\n# lifetime_seconds: 300\n# docker_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# docker_mount_cwd_to_workspace: true # Explicit opt-in: mount your launch cwd into /workspace\n# # Optional: run the container as your host user's uid:gid so files written\n# # into bind-mounted dirs are owned by you, not root. Drops SETUID/SETGID\n# # caps too since no gosu privilege drop is needed. Leave off if your\n# # chosen docker_image expects to start as root.\n# docker_run_as_host_user: true\n# # Optional: explicitly forward selected env vars into Docker.\n# # These values come from your current shell first, then ~/.hermes/.env.\n# # Warning: anything forwarded here is visible to commands run in the container.\n# docker_forward_env:\n# - \"GITHUB_TOKEN\"\n# - \"NPM_TOKEN\"\n# # Optional: extra flags passed verbatim to docker run (appended after security defaults).\n# # Useful for adding capabilities (e.g. apt installs needing SETUID) or custom options.\n# # Example: add a Linux capability not included by default\n# # docker_extra_args:\n# # - \"--cap-add\"\n# # - \"SETUID\"\n\n# -----------------------------------------------------------------------------\n# OPTION 4: Singularity/Apptainer container\n# Commands run in a Singularity container (common in HPC environments)\n# Great for: HPC clusters, shared compute environments\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"singularity\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# singularity_image: \"docker://nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 5: Modal cloud execution\n# Commands run on Modal's cloud infrastructure\n# Great for: GPU access, scalable compute, serverless execution\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"modal\"\n# cwd: \"/workspace\" # Path INSIDE the sandbox (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# modal_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 6: Daytona cloud execution\n# Commands run in Daytona cloud sandboxes\n# Great for: Cloud dev environments, persistent workspaces, team collaboration\n# Requires: pip install daytona, DAYTONA_API_KEY env var\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"daytona\"\n# cwd: \"~\"\n# timeout: 180\n# lifetime_seconds: 300\n# daytona_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# container_disk: 10240 # Daytona max is 10GB per sandbox\n\n#\n# --- Container resource limits (docker, singularity, modal, daytona -- ignored for local/ssh) ---\n# These settings apply to all container backends. They control the resources\n# allocated to the sandbox and whether its filesystem persists across sessions.\n container_cpu: 1 # CPU cores\n container_memory: 5120 # Memory in MB (5120 = 5GB)\n container_disk: 51200 # Disk in MB (51200 = 50GB)\n container_persistent: true # Persist filesystem across sessions (false = ephemeral)\n\n# -----------------------------------------------------------------------------\n# SUDO SUPPORT (works with ALL backends above)\n# -----------------------------------------------------------------------------\n# Add sudo_password to any terminal config above to enable sudo commands.\n# The password is piped via `sudo -S`. Works with local, ssh, docker, etc.\n#\n# SECURITY WARNING: Password stored in plaintext!\n#\n# INTERACTIVE PROMPT: If sudo_password is unset and the CLI is running,\n# you'll be prompted to enter your password when sudo is needed:\n# - 45-second timeout (auto-skips if no input)\n# - Press Enter to skip (command fails gracefully)\n# - Password is hidden while typing\n# - Password is cached for the session\n#\n# EMPTY PASSWORDS: Setting sudo_password to an explicit empty string is different\n# from leaving it unset. Hermes will try an empty password via `sudo -S` and\n# will not open the interactive prompt. This is useful for passwordless sudo,\n# Touch ID sudo setups, and environments where prompting is just noise.\n#\n# ALTERNATIVES:\n# - SSH backend: Configure passwordless sudo on the remote server\n# - Containers: Run as root inside the container (no sudo needed)\n# - Local: Configure /etc/sudoers for specific commands\n#\n# Example (add to your terminal section):\n# sudo_password: \"your-password-here\"\n\n# =============================================================================\n# Security Scanning (tirith)\n# =============================================================================\n# Optional pre-exec command security scanning via tirith.\n# Detects homograph URLs, pipe-to-shell, terminal injection, env manipulation.\n# Install: brew install sheeki03/tap/tirith\n# Docs: https://github.com/sheeki03/tirith\n#\n# security:\n# tirith_enabled: true # Enable/disable tirith scanning\n# tirith_path: \"tirith\" # Path to tirith binary (supports ~ expansion)\n# tirith_timeout: 5 # Scan timeout in seconds\n# tirith_fail_open: true # Allow commands if tirith unavailable\n\n# =============================================================================\n# Browser Tool Configuration\n# =============================================================================\nbrowser:\n # Inactivity timeout in seconds - browser sessions are automatically closed\n # after this period of no activity between agent loops (default: 120 = 2 minutes)\n inactivity_timeout: 120\n\n# =============================================================================\n# Tool Loop Guardrails\n# =============================================================================\n# Soft warnings are enabled by default. They append guidance to repeated failed\n# or non-progressing tool results but still let the tool execute. Hard stops are\n# opt-in circuit breakers for autonomous/cron sessions where stopping a loop is\n# preferable to spending the full iteration budget.\ntool_loop_guardrails:\n warnings_enabled: true\n hard_stop_enabled: false\n warn_after:\n exact_failure: 2\n same_tool_failure: 3\n idempotent_no_progress: 2\n hard_stop_after:\n exact_failure: 5\n same_tool_failure: 8\n idempotent_no_progress: 5\n\n# =============================================================================\n# Context Compression (Auto-shrinks long conversations)\n# =============================================================================\n# When conversation approaches model's context limit, middle turns are\n# automatically summarized to free up space while preserving important context.\n#\n# HOW IT WORKS:\n# 1. Tracks actual token usage from API responses (not estimates)\n# 2. When prompt_tokens >= threshold% of model's context_length, triggers compression\n# 3. Protects first 3 turns (system prompt, initial request, first response)\n# 4. Protects last N turns (default 20 messages = ~10 full turns of recent context)\n# 5. Summarizes middle turns using a fast/cheap model\n# 6. Inserts summary as a user message, continues conversation seamlessly\n#\n# Post-compression tail budget is target_ratio × threshold × context_length:\n# 200K context, threshold 0.50, ratio 0.20 → 20K tokens of recent tail preserved\n# 1M context, threshold 0.50, ratio 0.20 → 100K tokens of recent tail preserved\n#\ncompression:\n # Enable automatic context compression (default: true)\n # Set to false if you prefer to manage context manually or want errors on overflow\n enabled: true\n\n # Opt-in compression progress notices on chat platforms (default: false).\n # By design, routine automatic compression is SILENT on human-facing chat\n # gateways (Telegram, Discord, Slack, ...) — it happens in the background\n # with server-side logging only. Set true to also deliver the routine\n # progress statuses (compacting started, preflight/pre-API compression,\n # idle compaction, retry progress, and the compaction-complete notice) to\n # chat platforms. Unrelated operational noise (auxiliary model failures,\n # provider retry chatter) stays suppressed either way, and compression\n # FAILURE notices + manual /compress feedback are always visible\n # regardless of this setting. (#52995)\n progress_notices: false\n \n # Trigger compression at this % of model's context limit (default: 0.50 = 50%)\n # Lower values = more aggressive compression, higher values = compress later\n # Models with context windows below 512K are floored at 0.75 (raise-only) so\n # compaction doesn't fire with half the window still free; set above 0.75 to override.\n threshold: 0.50\n\n # Per-model threshold overrides: keys are substring-matched against the model\n # name (longest match wins). Useful when some models need different compaction\n # points — e.g. a 1M-context model can compress later (0.30) while a 128K\n # model needs to compress earlier (0.60). The small-context floor (75% for\n # <512K models) still applies on top of per-model overrides.\n # model_thresholds:\n # \"glm-5.2\": 0.40\n # \"claude-sonnet\": 0.35\n # \"gpt-5\": 0.30\n\n # Optional absolute token cap for the compression trigger (default: null = disabled).\n # When set, compression fires at the LOWER of the ratio-based threshold and this\n # absolute token count — first-fires-wins. It never fires later than this count\n # regardless of which model is active (useful when switching between models with\n # very different context windows). Clamped to the model's context length at\n # apply-time, so a cap above the window is a no-op (ratio-based threshold wins).\n # Survives model switches and fallback activations.\n # threshold_tokens: 200000\n\n # Existing Codex gpt-5.5 behavior: raise Hermes' compaction trigger to 85%\n # for the ChatGPT Codex OAuth route. Set false to opt back down to threshold.\n codex_gpt55_autoraise: true\n\n # Fraction of the threshold to preserve as recent tail (default: 0.20 = 20%)\n # e.g. 20% of 50% threshold = 10% of total context kept as recent messages.\n # Summary output is separately capped at 12K tokens (Gemini output limit).\n # Range: 0.10 - 0.80\n target_ratio: 0.20\n\n # Number of most-recent messages to always preserve (default: 20 ≈ 10 full turns)\n # Higher values keep more recent conversation intact at the cost of more aggressive\n # compression of older turns.\n protect_last_n: 20\n\n # Minimum number of REAL (actionable) user messages guaranteed to survive in\n # the uncompressed tail (default: 1 = the existing single last-user anchor,\n # behavior-preserving). Raise to e.g. 3 to keep the last 3 real user turns\n # verbatim even when bulky tool outputs fill the tail token budget — blank\n # platform echoes, compaction handoffs, and synthetic continuation rows never\n # count toward N. The tail can exceed the token budget when this pulls the\n # cut back; the guarantee wins over the budget by design.\n min_tail_user_messages: 1\n\n # Compression retry rounds before a turn gives up with \"max compression\n # attempts reached\" (default: 3, same as the previous hardcoded value).\n # Raise (e.g. 6) for tool-schema-heavy sessions where 3 rounds cannot bring\n # the request estimate under the threshold. Validated >= 1, hard cap 10.\n max_attempts: 3\n\n # Codex app-server (codex CLI runtime) thread-compaction mode. The codex\n # agent owns the real thread context on this runtime, so Hermes' summarizer\n # cannot shrink it — compaction goes through the app server instead.\n # native = let Codex decide when to compact its own thread (default)\n # hermes = let Hermes threshold trigger Codex thread/compact/start\n # off = Hermes will not auto-trigger compaction; Codex may still compact natively\n codex_app_server_auto: native\n\n # Number of non-system messages to protect at the head of the transcript, in\n # ADDITION to the system prompt (which is always implicitly protected).\n # Head messages are NEVER summarized — they survive every compression\n # indefinitely. This gives stable early context for short/medium sessions,\n # but in long-running sessions that rely on rolling compaction the pinned\n # opening turns may not match how you want the session framed over time.\n # Set to 0 to preserve ONLY the system prompt (plus the rolling summary\n # and recent tail) — the cleanest configuration for long-running sessions.\n # Default 3 preserves the system prompt plus the first three non-system\n # head messages, matching the pre-feature behaviour.\n protect_first_n: 3\n\n # Idle compaction (default: 0 = disabled). When > 0, a session that resumes\n # after at least this many seconds of inactivity compacts its accumulated\n # history up front, before the first reply, so a long-lived thread you come\n # back to later doesn't re-read its full stale context on every turn.\n # Time-based, so it complements (does not replace) the size-based `threshold`\n # above. It is skipped when the context is already small (at or below the\n # post-compression target = threshold × target_ratio), so it never wastes a\n # summarization on a short idle thread. Example: 1800 = compact after 30 min idle.\n idle_compact_after_seconds: 0\n\n # Proactive tool-result prune (default: 0 = disabled). Opt-in token trigger\n # for a deterministic, no-LLM prune of OLD tool-result payloads, run\n # independently of `threshold` above. On large-window models (512K/1M) the\n # ratio threshold rarely fires, so bulky tool outputs (terminal dumps, file\n # reads, web extracts) ride along in history and get re-billed every turn.\n # When re-sent history exceeds this many tokens, the prune dedupes identical\n # results, summarizes older oversized ones, and truncates large tool-call\n # arguments — protecting the most recent `protect_last_n` messages and never\n # calling the model. Try 48000 to enable. Built-in compressor engine only;\n # other context engines inherit a safe no-op.\n # NOTE: a committed prune rewrites already-sent history, which invalidates\n # the provider's prompt-cache prefix — the min_reclaim gate below keeps\n # those cache breaks episodic (like a compression boundary) instead of\n # per-turn.\n proactive_prune_tokens: 0\n\n # The prune's summarize pass only touches tool results larger than this many\n # characters (clamped to >= 200 so a generated summary can't be\n # re-summarized). Default 8000.\n proactive_prune_min_result_chars: 8000\n\n # A proactive prune only COMMITS when it reclaims at least this many tokens\n # (measured on the pruned output). This is the prompt-cache hysteresis gate:\n # one meaningful, amortized cache break per batch of stale tool output\n # instead of a tiny break on every tool iteration. 0 = commit any non-zero\n # prune. Default 4096.\n proactive_prune_min_reclaim_tokens: 4096\n\n # To pin a specific model/provider for compression summaries, use the\n # auxiliary section below (auxiliary.compression.provider / model).\n\n# =============================================================================\n# Anthropic prompt caching TTL\n# =============================================================================\n# When prompt caching is active (Claude via OpenRouter or native Anthropic),\n# Anthropic supports two TTL tiers for cached prefixes: \"5m\" (default) and\n# \"1h\". Other values are ignored and \"5m\" is used.\n#\nprompt_caching:\n cache_ttl: \"5m\" # use \"1h\" for long sessions with pauses between turns\n\n# =============================================================================\n# Auxiliary Models (Advanced — Experimental)\n# =============================================================================\n# Hermes uses lightweight \"auxiliary\" models for side tasks: image analysis,\n# browser screenshot analysis, web page summarization, TTS audio-tag insertion,\n# and context compression.\n#\n# By default these use Gemini Flash via OpenRouter or Nous Portal and are\n# auto-detected from your credentials. You do NOT need to change anything\n# here for normal usage.\n#\n# WARNING: Overriding these with providers other than OpenRouter or Nous Portal\n# is EXPERIMENTAL and may not work. Not all models/providers support vision,\n# produce usable summaries, or accept the same API format. Change at your own\n# risk — if things break, reset to \"auto\" / empty values.\n#\n# Each task has its own provider + model pair so you can mix providers.\n# For example: OpenRouter for vision (needs multimodal), but your main\n# local endpoint for compression (just needs text).\n#\n# Provider options:\n# \"auto\" - Best available: OpenRouter → Nous Portal → main endpoint (default)\n# \"openrouter\" - Force OpenRouter (requires OPENROUTER_API_KEY)\n# \"nous\" - Force Nous Portal (requires: hermes auth add nous)\n# \"gemini\" - Force Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n# \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY)\n# \"codex\" - Force Codex OAuth (requires: hermes model → Codex).\n# Uses gpt-5.3-codex which supports vision.\n# \"main\" - Use your custom endpoint (OPENAI_BASE_URL + OPENAI_API_KEY).\n# Works with OpenAI API, local models, or any OpenAI-compatible\n# endpoint. Also falls back to Codex OAuth and API-key providers.\n#\n# Model: leave empty to use the provider's default. When empty, OpenRouter\n# uses \"google/gemini-3-flash-preview\" and Nous uses \"gemini-3-flash\".\n# Other providers pick a sensible default automatically.\n#\n# auxiliary:\n# # Image analysis: vision_analyze tool + browser screenshots\n# vision:\n# provider: \"auto\"\n# model: \"\" # e.g. \"google/gemini-2.5-flash\", \"openai/gpt-4o\"\n# timeout: 30 # LLM API call timeout (seconds)\n# download_timeout: 30 # Image HTTP download timeout (seconds)\n# # Increase for slow connections or self-hosted image servers\n# reasoning_effort: \"\" # Per-task thinking level: none, minimal, low, medium,\n# # high, xhigh, max, ultra. Empty = provider default.\n# # Works on every auxiliary task block (vision,\n# # web_extract, compression, title_generation, curator,\n# # background_review, moa_reference, ...). Example: run\n# # compression at \"low\" and vision at \"none\" to cut\n# # side-task latency/cost on reasoning models.\n#\n# # Web page scraping / summarization + browser page text extraction\n# web_extract:\n# provider: \"auto\"\n# model: \"\"\n# reasoning_effort: \"low\"\n#\n# # Gemini 3.1 TTS hidden audio-tag insertion\n# tts_audio_tags:\n# provider: \"auto\" # empty model = your main chat model\n# model: \"\"\n# timeout: 30\n#\n# # Automatic session title generation after the first exchange\n# title_generation:\n# enabled: true # set false to disable auto-title generation\n# provider: \"auto\"\n# model: \"\"\n# timeout: 30\n# language: \"\" # empty = match the user's language; or e.g. \"English\"\n#\n# # Session search — summarizes matching past sessions\n# session_search:\n# provider: \"auto\"\n# model: \"\"\n# timeout: 30\n# max_concurrency: 3 # Limit parallel summaries to reduce request-burst 429s\n# extra_body: {} # Provider-specific OpenAI-compatible request fields\n# # Example for providers that support request-body\n# # reasoning controls:\n# # extra_body:\n# # enable_thinking: false\n# # Some vLLM/Qwen deployments expect this nested:\n# # extra_body:\n# # chat_template_kwargs:\n# # enable_thinking: false\n\n# =============================================================================\n# Persistent Memory\n# =============================================================================\n# Bounded curated memory injected into the system prompt every session.\n# Two stores: MEMORY.md (agent's notes) and USER.md (user profile).\n# Character limits keep the memory small and focused. The agent manages\n# pruning -- when at the limit, it must consolidate or replace entries.\n# Disabled by default in batch_runner.\n#\nmemory:\n # Agent's personal notes: environment facts, conventions, things learned\n memory_enabled: true\n \n # User profile: preferences, communication style, expectations\n user_profile_enabled: true\n \n # Character limits (~2.75 chars per token, model-independent)\n memory_char_limit: 2200 # ~800 tokens\n user_char_limit: 1375 # ~500 tokens\n\n # Periodic memory nudge: remind the agent to consider saving memories\n # every N user turns. Set to 0 to disable. Only active when memory is enabled.\n nudge_interval: 10 # Nudge every 10 user turns (0 = disabled)\n\n # Memory flush: give the agent one turn to save memories before context is\n # lost (compression, /new, /reset, exit). Set to 0 to disable.\n # For exit/reset, only fires if the session had at least this many user turns.\n flush_min_turns: 6 # Min user turns to trigger flush on exit/reset (0 = disabled)\n\n# =============================================================================\n# Session Reset Policy (Messaging Platforms)\n# =============================================================================\n# Controls when messaging sessions (Telegram, Discord, WhatsApp, Slack) are\n# automatically cleared. Default is \"none\": sessions never auto-reset —\n# conversation context lives until you /reset or /new manually, or context\n# compression kicks in. Opt in to automatic resets if you prefer sessions to\n# clear on a schedule (long-lived context increases API cost per message,\n# though prompt caching and compression keep this manageable).\n#\n# When an automatic reset triggers, the agent first saves important\n# information to its persistent memory — but the conversation context is\n# wiped. The agent starts fresh but retains learned facts via its memory\n# system.\n#\n# Modes:\n# \"none\" - Never auto-reset (default); context lives until /reset or compression\n# \"idle\" - Reset after N minutes of inactivity\n# \"daily\" - Reset at a fixed hour each day\n# \"both\" - Reset on EITHER inactivity timeout or daily boundary\n#\nsession_reset:\n mode: none # \"none\", \"idle\", \"daily\", or \"both\"\n idle_minutes: 1440 # Inactivity timeout in minutes (used by \"idle\"/\"both\")\n at_hour: 4 # Daily reset hour, 0-23 local time (used by \"daily\"/\"both\")\n\n# Maximum number of simultaneously active chat sessions across CLI, TUI,\n# dashboard chat, and messaging gateway. Set to null, 0, or omit to allow\n# unlimited concurrent sessions. When the limit is reached, new sessions get a\n# clean error while existing active sessions keep their normal behavior. This\n# top-level key takes precedence over gateway.max_concurrent_sessions. The cap\n# is a best-effort single-host/profile runtime guard; Hermes fails open if the\n# local runtime lease registry cannot be read or locked.\nmax_concurrent_sessions: null\n\n# When true, group/channel chats use one session per participant when the platform\n# provides a user ID. This is the secure default and prevents users in the same\n# room from sharing context, interrupts, and token costs. Set false only if you\n# explicitly want one shared \"room brain\" per group/channel.\ngroup_sessions_per_user: true\n\n# ─────────────────────────────────────────────────────────────────────────────\n# API Server — per-client model routing\n# ─────────────────────────────────────────────────────────────────────────────\n# Route different API clients to different models/providers on a single\n# Hermes deployment. Clients choose a backend by sending a specific string\n# as the OpenAI ``model`` field. Unmapped model values fall back to the\n# global model configured in the ``model:`` section above, and an explicit\n# session /model override always wins over a route.\n#\n# Configure via the ``platforms.api_server.extra.model_routes`` gateway\n# config block:\n#\n# platforms:\n# api_server:\n# enabled: true\n# extra:\n# key: \"your-api-server-secret\"\n# model_routes:\n# # Xiaozhi clients send model=\"minimax-m2\" → routed to MiniMax via OpenRouter\n# minimax-m2:\n# model: \"minimax/minimax-m1\"\n# provider: \"openrouter\" # optional — overrides global provider\n# # api_key: \"sk-...\" # optional — per-route UPSTREAM provider\n# # key (NOT caller auth; never logged)\n# # base_url: \"https://...\" # optional — per-route base URL\n# # GPT clients keep their own alias\n# gpt-5:\n# model: \"openai/gpt-5\"\n# provider: \"openrouter\"\n#\n# Configured aliases are automatically listed by GET /v1/models so clients\n# can discover them without manual coordination. Caller authentication is\n# unchanged: every request still authenticates with the global API server\n# key (``extra.key`` / API_SERVER_KEY).\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Gateway Streaming\n# ─────────────────────────────────────────────────────────────────────────────\n# Stream tokens to messaging platforms in real-time. The bot sends a message\n# on first token, then progressively edits it as more tokens arrive.\n# Disabled by default — enable to try the streaming UX on Telegram/Discord/Slack.\n# For Telegram, partial edits are sent as plain text and only the final edit uses MarkdownV2.\nstreaming:\n enabled: false\n # transport: edit # \"edit\" = progressive editMessageText\n # edit_interval: 0.3 # seconds between message edits\n # buffer_threshold: 40 # chars before forcing an edit flush\n # cursor: \" ▉\" # cursor shown during streaming\n\n# =============================================================================\n# Skills Configuration\n# =============================================================================\n# Skills are reusable procedures the agent can load and follow. The agent can\n# also create new skills after completing complex tasks.\n#\nskills:\n # Nudge the agent to create skills after complex tasks.\n # Every N tool-calling iterations, remind the model to consider saving a skill.\n # Set to 0 to disable.\n creation_nudge_interval: 15\n\n # External skill directories — share skills across tools/agents without\n # copying them into ~/.hermes/skills/. Each path is expanded (~ and ${VAR})\n # and resolved to an absolute path. External dirs are read-only: skill\n # creation always writes to ~/.hermes/skills/. Local skills take precedence\n # when names collide.\n # external_dirs:\n # - ~/.agents/skills\n # - /home/shared/team-skills\n\n# =============================================================================\n# Agent Behavior\n# =============================================================================\nagent:\n # Maximum tool-calling iterations per conversation (default: 500)\n # Higher = more room for complex tasks, but costs more tokens\n # Recommended: 20-30 for focused tasks, 50-100 for open exploration\n max_turns: 500\n\n # Inactivity timeout for gateway agent runs (seconds, 0 = unlimited).\n # The agent can run indefinitely when actively calling tools or receiving\n # API responses. Only fires after the agent has been idle for this duration.\n # gateway_timeout: 1800\n\n # Staged warning: send a warning before escalating to full timeout.\n # Fires once per run when inactivity reaches this threshold (seconds).\n # Set to 0 to disable the warning.\n # gateway_timeout_warning: 900\n\n # Graceful drain timeout for gateway stop/restart (seconds).\n # Default 0 = no drain: a restart interrupts in-flight agents immediately,\n # cleans up, and exits. Set a positive value only if you want a grace\n # window on /restart, and keep it well under systemd's TimeoutStopSec.\n # restart_drain_timeout: 0\n\n # Upper bound (seconds) a submitted prompt waits for the deferred agent\n # build (MCP discovery, model metadata, skills scan) before failing with a\n # visible error. The wait is patient — the message is delivered as soon as\n # the build completes, and a progress notice is shown past 30s — so this cap\n # only fires on a genuinely hung build. Raise it for deployments with many\n # slow or unreachable MCP servers. Default 600.\n # build_wait_timeout: 600\n\n # Max app-level retry attempts for API errors (connection drops, provider\n # timeouts, 5xx, etc.) before the agent surfaces the failure. Lower this\n # to 1 if you use fallback providers and want fast failover on flaky\n # primaries (default 3). The OpenAI SDK does its own low-level retries\n # underneath this wrapper — this is the Hermes-level loop.\n # api_max_retries: 3\n\n # After the agent edits code without fresh passing verification, nudge it to\n # verify before finishing. The default \"auto\" enables it on interactive\n # coding surfaces (CLI, TUI, desktop) and programmatic callers, and disables\n # it on conversational messaging surfaces (Telegram, Discord, etc.) where the\n # verification summary would reach a human as chat noise. Set true or false to\n # force it on or off; the HERMES_VERIFY_ON_STOP env var (1/0) takes precedence.\n # verify_on_stop: auto\n\n # Standing operator instructions for the coding posture (when Hermes is in a\n # code workspace). Appended to the coding brief as an extra system block, so\n # you can pin project-wide workflow rules without editing the shipped brief.\n # Accepts a string or a list of strings. Takes effect next session.\n # coding_instructions:\n # - \"For UI work, don't run tsc/lint until I approve the look.\"\n # - \"Clean the diff before you commit and push.\"\n\n # When verify-on-stop finds edited code without fresh verification evidence,\n # append guidance for creative UI work (avoid broad tsc/lint/test before visual\n # approval) and clean-diff expectations. Set false to keep that nudge terse.\n # verify_guidance: true\n\n # A `pre_verify` hook (plugin or shell, see Event Hooks docs) can keep the\n # agent going one more turn to verify/clean before finishing. This caps how\n # many times one turn may be nudged to continue, so a hook can't trap the loop.\n # Default 3.\n # max_verify_nudges: 3\n\n # Enable verbose logging\n verbose: false\n \n # Reasoning effort level (OpenRouter and Nous Portal)\n # Controls how much \"thinking\" the model does before responding.\n # Options: \"xhigh\" (max), \"high\", \"medium\", \"low\", \"minimal\", \"none\" (disable)\n reasoning_effort: \"medium\"\n \n # Per-model reasoning effort overrides (optional dict)\n # Key: any sensible model spelling works (exact, dots↔dashes interchangeable,\n # provider prefix optional). First match wins.\n # Value: reasoning effort level (same options as reasoning_effort)\n # Override the global reasoning_effort for that specific model.\n # NOTE: no `hermes config set` support for this key -- edit YAML directly.\n # reasoning_overrides:\n # \"openrouter/anthropic/claude-opus-4.5\": \"xhigh\"\n # \"openai/gpt-5\": \"low\"\n # \"claude-opus-4.6\": \"high\" # bare model name also works\n # \"deepseek/deepseek-v4-pro\": \"xhigh\" # dots and dashes are interchangeable\n reasoning_overrides: {}\n \n # Predefined personalities (use with /personality command)\n personalities:\n helpful: \"You are a helpful, friendly AI assistant.\"\n concise: \"You are a concise assistant. Keep responses brief and to the point.\"\n technical: \"You are a technical expert. Provide detailed, accurate technical information.\"\n creative: \"You are a creative assistant. Think outside the box and offer innovative solutions.\"\n teacher: \"You are a patient teacher. Explain concepts clearly with examples.\"\n kawaii: \"You are a kawaii assistant! Use cute expressions like (◕‿◕), ★, ♪, and ~! Add sparkles and be super enthusiastic about everything! Every response should feel warm and adorable desu~! ヽ(>∀<☆)ノ\"\n catgirl: \"You are Neko-chan, an anime catgirl AI assistant, nya~! Add 'nya' and cat-like expressions to your speech. Use kaomoji like (=^・ω・^=) and ฅ^•ﻌ•^ฅ. Be playful and curious like a cat, nya~!\"\n pirate: \"Arrr! Ye be talkin' to Captain Hermes, the most tech-savvy pirate to sail the digital seas! Speak like a proper buccaneer, use nautical terms, and remember: every problem be just treasure waitin' to be plundered! Yo ho ho!\"\n shakespeare: \"Hark! Thou speakest with an assistant most versed in the bardic arts. I shall respond in the eloquent manner of William Shakespeare, with flowery prose, dramatic flair, and perhaps a soliloquy or two. What light through yonder terminal breaks?\"\n surfer: \"Duuude! You're chatting with the chillest AI on the web, bro! Everything's gonna be totally rad. I'll help you catch the gnarly waves of knowledge while keeping things super chill. Cowabunga! 🤙\"\n noir: \"The rain hammered against the terminal like regrets on a guilty conscience. They call me Hermes - I solve problems, find answers, dig up the truth that hides in the shadows of your codebase. In this city of silicon and secrets, everyone's got something to hide. What's your story, pal?\"\n uwu: \"hewwo! i'm your fwiendwy assistant uwu~ i wiww twy my best to hewp you! *nuzzles your code* OwO what's this? wet me take a wook! i pwomise to be vewy hewpful >w<\"\n philosopher: \"Greetings, seeker of wisdom. I am an assistant who contemplates the deeper meaning behind every query. Let us examine not just the 'how' but the 'why' of your questions. Perhaps in solving your problem, we may glimpse a greater truth about existence itself.\"\n hype: \"YOOO LET'S GOOOO!!! 🔥🔥🔥 I am SO PUMPED to help you today! Every question is AMAZING and we're gonna CRUSH IT together! This is gonna be LEGENDARY! ARE YOU READY?! LET'S DO THIS! 💪😤🚀\"\n\n# =============================================================================\n# Toolsets\n# =============================================================================\n# Control which tools the agent has access to.\n# Use `hermes tools` to interactively enable/disable tools per platform.\n\n# =============================================================================\n# Platform Toolsets (per-platform tool configuration)\n# =============================================================================\n# Override which toolsets are available on each platform.\n# If a platform isn't listed here, its built-in default is used.\n#\n# You can use EITHER:\n# - A preset like \"hermes-cli\" or \"hermes-telegram\" (curated tool set)\n# - A list of individual toolsets to compose your own (see list below)\n#\n# Supported platform keys: cli, telegram, discord, whatsapp, slack, qqbot, teams, google_chat\n#\n# Examples:\n#\n# # Use presets (same as defaults):\n# platform_toolsets:\n# cli: [hermes-cli]\n# telegram: [hermes-telegram]\n#\n# # Custom: give Telegram only web + terminal + file + planning:\n# platform_toolsets:\n# telegram: [web, terminal, file, todo]\n#\n# # Custom: CLI without browser or image gen:\n# platform_toolsets:\n# cli: [web, terminal, file, skills, todo, tts, cronjob]\n#\n# # Restrictive: Discord gets read-only tools only:\n# platform_toolsets:\n# discord: [web, vision, skills, todo]\n#\n# If not set, defaults are:\n# cli: hermes-cli (everything + cronjob management)\n# telegram: hermes-telegram (terminal, file, web, vision, image, tts, browser, skills, todo, cronjob, messaging)\n# discord: hermes-discord (same as telegram)\n# whatsapp: hermes-whatsapp (same as telegram)\n# slack: hermes-slack (same as telegram)\n# signal: hermes-signal (same as telegram)\n# homeassistant: hermes-homeassistant (same as telegram)\n# qqbot: hermes-qqbot (same as telegram)\n# teams: hermes-teams (same as telegram)\n# google_chat: hermes-google_chat (same as telegram)\n#\nplatform_toolsets:\n cli: [hermes-cli]\n telegram: [hermes-telegram]\n discord: [hermes-discord]\n whatsapp: [hermes-whatsapp]\n slack: [hermes-slack]\n signal: [hermes-signal]\n homeassistant: [hermes-homeassistant]\n qqbot: [hermes-qqbot]\n yuanbao: [hermes-yuanbao]\n teams: [hermes-teams]\n google_chat: [hermes-google_chat]\n\n# =============================================================================\n# Gateway Platform Settings\n# =============================================================================\n# Optional per-platform messaging settings.\n# Platform-specific knobs live under `extra`.\n#\n# platforms:\n# telegram:\n# reply_to_mode: \"first\" # off | first | all\n# # guest_mode lets explicit @mentions from non-allowlisted groups through.\n# # Default false; ordinary messages, replies, and regex wake words stay blocked.\n# guest_mode: false\n# # allowed_chats: [\"-1001234567890\"]\n# extra:\n# disable_link_previews: false # Set true to suppress Telegram URL previews in bot messages\n# 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\n# rich_drafts: false # Experimental rich draft previews during Telegram DM streaming; default false because Telegram Desktop/macOS can visually overlay draft frames\n# command_menu:\n# # Telegram allows up to 100 BotCommands; Hermes defaults to 60 so\n# # all built-in commands plus common skill commands stay visible\n# # while remaining under Telegram's payload-size limit. Clamped 1..100.\n# max_commands: 60\n# # prepend = user priority first, then Hermes defaults\n# # append = Hermes defaults first, then user priority\n# # replace = only the list below defines priority\n# priority_mode: prepend\n# priority:\n# - my_plugin_command\n# webhook:\n# extra:\n# # Route scripts default to a 30 second timeout. Scripts must live under\n# # the active profile's scripts directory and receive webhook JSON on stdin.\n# script_timeout_seconds: 30\n#\n# Discord-specific settings (config.yaml top-level, not under platforms:):\n#\n# discord:\n# require_mention: true # Require @mention in server channels (default: true)\n# auto_thread: true # Auto-create thread on @mention (default: true)\n# free_response_channels: \"\" # Channel IDs where no mention is needed\n# reactions: true # Show processing reactions (default: true)\n# history_backfill: true # Recover missed channel messages on mention (default: true)\n# history_backfill_limit: 50 # Max messages to scan backwards (default: 50)\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Available toolsets (use these names in platform_toolsets or the toolsets list)\n#\n# Run `hermes chat --list-toolsets` to see all toolsets and their tools.\n# Run `hermes chat --list-tools` to see every individual tool with descriptions.\n# ─────────────────────────────────────────────────────────────────────────────\n#\n# INDIVIDUAL TOOLSETS (compose your own):\n# web - web_search, web_extract\n# search - web_search only (no scraping)\n# terminal - terminal, process\n# file - read_file, write_file, patch, search\n# browser - browser_navigate, browser_snapshot, browser_click, browser_type,\n# browser_scroll, browser_back, browser_press,\n# browser_get_images, browser_vision (requires BROWSERBASE_API_KEY)\n# vision - vision_analyze (requires OPENROUTER_API_KEY)\n# image_gen - image_generate (requires FAL_KEY)\n# skills - skills_list, skill_view\n# skills_hub - skill_hub (search/install/manage from online registries — user-driven only)\n# todo - todo (in-memory task planning, no deps)\n# tts - text_to_speech (Edge TTS free, or ELEVENLABS/OPENAI/MINIMAX/MISTRAL key)\n# cronjob - cronjob (create/list/update/pause/resume/run/remove scheduled tasks)\n#\n# PRESETS (curated bundles):\n# hermes-cli - All of the above except send_message\n# hermes-telegram - terminal, file, web, vision, image_gen, tts, browser,\n# skills, todo, cronjob, send_message\n# hermes-discord - Same as hermes-telegram\n# hermes-whatsapp - Same as hermes-telegram\n# hermes-slack - Same as hermes-telegram\n#\n# COMPOSITE:\n# debugging - terminal + web + file\n# safe - web + vision (no terminal access)\n# all - Everything available\n#\n# web - Web search and content extraction (web_search, web_extract)\n# search - Web search only, no scraping (web_search)\n# terminal - Command execution and process management (terminal, process)\n# file - File operations: read, write, patch, search\n# browser - Full browser automation (navigate, click, type, screenshot, etc.)\n# vision - Image analysis (vision_analyze)\n# image_gen - Image generation with FLUX (image_generate)\n# skills - Load skill documents (skills_list, skill_view)\n# todo - Task planning and tracking for multi-step work\n# memory - Persistent memory across sessions (personal notes + user profile)\n# session_search - Search and recall past conversations (FTS5 + Gemini Flash summarization)\n# tts - Text-to-speech (Edge TTS free, ElevenLabs, OpenAI, MiniMax, Mistral)\n# cronjob - Schedule and manage automated tasks (CLI-only)\n#\n# Composite toolsets:\n# debugging - terminal + web + file (for troubleshooting)\n# safe - web + vision (no terminal access)\n\n# NOTE: The top-level \"toolsets\" key is deprecated and ignored.\n# Tool configuration is managed per-platform via platform_toolsets above.\n# Use `hermes tools` to configure interactively, or edit platform_toolsets directly.\n#\n# CLI override: hermes chat --toolsets terminal,web,file\n\n# =============================================================================\n# MCP (Model Context Protocol) Servers\n# =============================================================================\n# Connect to external MCP servers to add tools from the MCP ecosystem.\n# Each server's tools are automatically discovered and registered.\n# See website/docs/user-guide/features/mcp.md for full documentation.\n#\n# Stdio servers (spawn a subprocess):\n# command: the executable to run\n# args: command-line arguments\n# env: environment variables (only these + safe defaults passed to subprocess)\n#\n# HTTP servers (connect to a URL):\n# url: the MCP server endpoint\n# headers: HTTP headers (e.g., for authentication)\n#\n# Optional per-server settings:\n# timeout: tool call timeout in seconds (default: 120)\n# connect_timeout: initial connection timeout (default: 60)\n# keepalive_interval: liveness ping cadence in seconds (default: 180).\n# Lower it below the server's session TTL for servers that expire idle\n# sessions quickly (e.g. Unreal Engine editor MCP, ~15s), otherwise idle\n# tool calls hit an expired session and pay a slow reconnect. Floored at 5s.\n#\n# mcp_servers:\n# time:\n# command: uvx\n# args: [\"mcp-server-time\"]\n# filesystem:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-filesystem\", \"/home/user\"]\n# notion:\n# url: https://mcp.notion.com/mcp\n# github:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-github\"]\n# env:\n# GITHUB_PERSONAL_ACCESS_TOKEN: \"ghp_...\"\n#\n# Sampling (server-initiated LLM requests) — enabled by default.\n# Per-server config under the 'sampling' key:\n# analysis:\n# command: npx\n# args: [\"-y\", \"analysis-server\"]\n# sampling:\n# enabled: true # default: true\n# model: \"gemini-3-flash\" # override model (optional)\n# max_tokens_cap: 4096 # max tokens per request\n# timeout: 30 # LLM call timeout (seconds)\n# max_rpm: 10 # max requests per minute\n# allowed_models: [] # model whitelist (empty = all)\n# max_tool_rounds: 5 # tool loop limit (0 = disable)\n# log_level: \"info\" # audit verbosity\n\n# =============================================================================\n# Text-to-Speech\n# =============================================================================\n# TTS defaults to Edge TTS unless changed in ~/.hermes/config.yaml.\n# Gemini TTS supports persona/director prompt files, and Gemini 3.1 Flash TTS\n# can use a hidden auxiliary rewrite pass to insert expressive square-bracket\n# audio tags into the TTS script without showing tags in chat.\n#\n# tts:\n# provider: \"gemini\"\n# gemini:\n# model: \"gemini-3.1-flash-tts-preview\"\n# voice: \"Kore\"\n# audio_tags: false\n# persona_prompt_file: \"\" # e.g. ~/.hermes/tts/radio-host.md\n\n# =============================================================================\n# Voice Transcription (Speech-to-Text)\n# =============================================================================\n# Automatically transcribe voice messages on messaging platforms.\n# Providers: local (free, faster-whisper) | groq (free tier) | openai (Whisper API) | mistral (Voxtral Transcribe)\n# Set the corresponding API key in .env: GROQ_API_KEY, OPENAI_API_KEY, or MISTRAL_API_KEY.\nstt:\n enabled: true\n # provider: \"local\" # auto-detected if omitted\n local:\n model: \"base\" # tiny | base | small | medium | large-v3 | turbo\n # language: \"\" # auto-detect; set to \"en\", \"es\", \"fr\", etc. to force\n # groq:\n # language: \"\" # auto-detect; set to \"en\", \"es\", \"fr\", etc. to force\n openai:\n model: \"whisper-1\" # whisper-1 | gpt-4o-mini-transcribe | gpt-4o-transcribe\n language: \"\" # auto-detect; set to \"en\", \"es\", \"fr\", etc. to force\n # mistral:\n # model: \"voxtral-mini-latest\" # voxtral-mini-latest | voxtral-mini-2602\n # deepinfra:\n # # Model id is discovered live from the DeepInfra catalog filtered\n # # by the `stt` surface tag — leave `model` blank to take the first\n # # live result. Pin only when you need a specific Whisper variant.\n # model: \"\"\n\n# Text-to-speech. Only the deepinfra block is documented here — the\n# remaining providers (edge, openai, xai, minimax, mistral, gemini,\n# elevenlabs, neutts, kittentts, piper) inherit sensible defaults from\n# DEFAULT_CONFIG in hermes_cli/config.py.\n# tts:\n# provider: \"deepinfra\"\n# deepinfra:\n# # Model id is discovered live from the DeepInfra catalog filtered\n# # by the `tts` surface tag — leave `model` blank to take the first\n# # live result.\n# model: \"\"\n# voice: \"default\"\n\n# Image generation. Each provider plugin reads its own ``image_gen.<name>``\n# block; deepinfra discovers models live from\n# api.deepinfra.com/v1/openai/models filtered by the ``image-gen`` tag —\n# no model id is hardcoded, so retired models disappear automatically.\n# image_gen:\n# provider: \"deepinfra\"\n# deepinfra:\n# # Leave `model` blank for the first live `image-gen`-tagged result.\n# model: \"\"\n\n# =============================================================================\n# Response Pacing (Messaging Platforms)\n# =============================================================================\n# Add human-like delays between message chunks.\n# human_delay:\n# mode: \"off\" # \"off\" | \"natural\" | \"custom\"\n# min_ms: 800 # Min delay (custom mode only)\n# max_ms: 2500 # Max delay (custom mode only)\n\n# =============================================================================\n# Session Logging\n# =============================================================================\n# Session trajectories are automatically saved to logs/ directory.\n# Each session creates: logs/session_YYYYMMDD_HHMMSS_UUID.json\n#\n# The session ID is displayed in the welcome banner for easy reference.\n# Logs contain full conversation history in trajectory format:\n# - System prompt, user messages, assistant responses\n# - Tool calls with inputs/outputs\n# - Timestamps for debugging\n#\n# No configuration needed - logging is always enabled.\n# To disable, you would need to modify the source code.\n\n# =============================================================================\n# Code Execution Sandbox (Programmatic Tool Calling)\n# =============================================================================\n# The execute_code tool runs Python scripts that call Hermes tools via RPC.\n# Intermediate tool results stay out of the LLM's context window.\ncode_execution:\n timeout: 300 # Max seconds per script before kill (default: 300 = 5 min)\n max_tool_calls: 50 # Max RPC tool calls per execution (default: 50)\n\n# =============================================================================\n# Subagent Delegation\n# =============================================================================\n# The delegate_task tool spawns child agents with isolated context.\n# Supports single tasks and batch mode (default 3 parallel, configurable).\ndelegation:\n max_iterations: 50 # Max tool-calling turns per child (default: 50)\n # max_concurrent_children: 3 # Max parallel child agents per batch (default: 3, floor: 1, no ceiling).\n # WARNING: values above 10 multiply API cost linearly.\n # max_spawn_depth: 1 # Delegation tree depth cap (range: 1-3, default: 1 = flat).\n # Raise to 2 to allow workers to spawn their own subagents.\n # Requires role=\"orchestrator\" on intermediate agents.\n # orchestrator_enabled: true # Kill switch for role=\"orchestrator\" children (default: true).\n # subagent_auto_approve: false # When a subagent hits a dangerous-command approval prompt, auto-deny (default: false)\n # or auto-approve \"once\" (true) instead of blocking on stdin.\n # The parent TUI owns stdin, so blocking would deadlock; non-interactive resolution is required.\n # Both choices emit a logger.warning audit line. Flip to true only for cron/batch pipelines.\n # inherit_mcp_toolsets: true # When explicit child toolsets are narrowed, also keep the parent's MCP toolsets (default: true). Set false for strict intersection.\n # model: \"google/gemini-3-flash-preview\" # Override model for subagents (empty = inherit parent)\n # provider: \"openrouter\" # Override provider for subagents (empty = inherit parent)\n # # Resolves full credentials (base_url, api_key) automatically.\n # # Supported: openrouter, nous, zai, kimi-coding, minimax\n\n# =============================================================================\n# Honcho Integration (Cross-Session User Modeling)\n# =============================================================================\n# AI-native persistent memory via Honcho (https://honcho.dev/).\n# Builds a deeper understanding of the user across sessions and tools.\n# Runs alongside USER.md — additive, not a replacement.\n#\n# Requires: pip install honcho-ai\n# Config: ~/.honcho/config.json (shared with Claude Code, Cursor, etc.)\n# API key: HONCHO_API_KEY in ~/.hermes/.env or ~/.honcho/config.json\n#\n# Hermes-specific overrides (optional — most config comes from ~/.honcho/config.json):\n# honcho: {}\n\n# =============================================================================\n# Display\n# =============================================================================\ndisplay:\n # Use compact banner mode (hides the ASCII-art banner, shows a single line).\n # true: Compact single-line banner\n # false: Full ASCII banner with tool/skill summary (default)\n compact: false\n\n # Tool progress display level (CLI and gateway)\n # off: Silent — no tool activity shown, just the final response\n # new: Show a tool indicator only when the tool changes (skip repeats)\n # all: Show every tool call with a short preview (default)\n # verbose: Full args, results, and debug logs (same as /verbose)\n # log: Silent in chat; append every tool call to ~/.hermes/logs/tool_calls.log (gateway only)\n # Toggle at runtime with /verbose in the CLI\n tool_progress: all\n\n # Per-platform defaults can be quieter than the global setting. Telegram\n # tunes for mobile: tool_progress and busy_ack_detail default off (no\n # per-tool breadcrumb stream, no \"iteration 21/60\" debug detail in busy\n # acks or heartbeats), but interim_assistant_messages and\n # long_running_notifications STAY ON so the user has real signal between\n # turn start and final answer (mid-turn assistant commentary + a single\n # edit-in-place \"⏳ Working — N min\" heartbeat). Override under\n # display.platforms.telegram.\n\n # Auto-cleanup of temporary progress bubbles after the final response lands.\n # On platforms that support message deletion (currently Telegram), this\n # removes the tool-progress bubble, \"⏳ Still working...\" notices, and\n # context-pressure status messages once the final reply has been delivered —\n # keeping long-running turns visible live, then tidy afterward. Failed runs\n # leave the bubbles in place as breadcrumbs. Off by default.\n # Per-platform override: display.platforms.telegram.cleanup_progress\n # true: Delete tracked progress/status bubbles on successful turn\n # false: Leave everything in place (default)\n # Example:\n # display:\n # platforms:\n # telegram:\n # cleanup_progress: true\n cleanup_progress: false\n\n # Natural mid-turn assistant updates.\n # On gateway platforms, when true, completed assistant status messages are\n # sent as separate chat messages. On the Desktop app, when true, mid-turn\n # assistant narration streamed between tool calls is kept in the transcript\n # instead of the bubble collapsing to only the final message on completion.\n # Independent of tool_progress and gateway streaming.\n # true: Keep/send mid-turn assistant updates (default)\n # false: Only keep/send the final response\n interim_assistant_messages: true\n\n # Gateway-only long-running status heartbeats.\n # When false, the platform does not receive periodic \"⏳ Working — N min\"\n # notifications even if agent.gateway_notify_interval is non-zero. The\n # heartbeat edits a single message in place (where the adapter supports\n # editing) instead of posting a new bubble each interval.\n # Default: true everywhere, including Telegram (silent agents are worse\n # than a single edit-in-place heartbeat).\n long_running_notifications: true\n\n # Include detailed iteration/tool/status context in busy acknowledgments\n # and long-running heartbeats. When true, busy acks show \"iteration 21/60,\n # terminal, 10 min\" and the heartbeat shows \"⏳ Working — 12 min,\n # iteration 21/60, terminal\". When false (Telegram default), both stay\n # terse: \"Interrupting current task\" and \"⏳ Working — 12 min, terminal\".\n busy_ack_detail: true\n\n # What Enter does when Hermes is already busy (CLI and gateway platforms).\n # interrupt: Interrupt the current run and redirect Hermes (default)\n # queue: Queue your message for the next turn\n # steer: Inject your message mid-run via /steer, arriving at the agent\n # after the next tool call — no interrupt, no role violation.\n # Falls back to 'queue' if the agent isn't running yet or if\n # images are attached (steer only carries text).\n # Ctrl+C (or /stop in gateway) always interrupts regardless of this setting.\n # Toggle at runtime with /busy <interrupt|queue|steer>.\n busy_input_mode: interrupt\n\n # Background process notifications (gateway/messaging only).\n # Controls how chatty the process watcher is when you use\n # terminal(background=true, notify_on_complete=true) from Telegram/Discord/etc.\n # off: No watcher messages at all\n # result: Only the final completion message\n # error: Only the final message when exit code != 0\n # all: Running output updates + final message (default)\n background_process_notifications: all\n\n\n # Play terminal bell when agent finishes a response.\n # Useful for long-running tasks — your terminal will ding when the agent is done.\n # Works over SSH. Most terminals can be configured to flash the taskbar or play a sound.\n # true: Ring the terminal bell on each response\n # false: Silent (default)\n bell_on_complete: false\n\n # Show model reasoning/thinking before each response.\n # When enabled, a dim box shows the model's thought process above the response.\n # Toggle at runtime with /reasoning show or /reasoning hide.\n # true: Show the reasoning box\n # false: Hide reasoning (default)\n show_reasoning: false\n\n # Stream tokens to the terminal as they arrive instead of waiting for the\n # full response. The response box opens on first token and text appears\n # line-by-line. Tool calls are still captured silently.\n # true: Stream tokens as they arrive (default)\n # false: Wait for the full response before rendering\n streaming: true\n\n # Show [HH:MM] timestamps on user input and assistant response labels.\n # timestamps: false\n\n # ───────────────────────────────────────────────────────────────────────────\n # Skin / Theme\n # ───────────────────────────────────────────────────────────────────────────\n # Customize CLI visual appearance — banner colors, spinner faces, tool prefix,\n # response box label, and branding text. Change at runtime with /skin <name>.\n #\n # Built-in skins:\n # default — Classic Hermes gold/kawaii\n # ares — Crimson/bronze war-god theme with spinner wings\n # mono — Clean grayscale monochrome\n # slate — Cool blue developer-focused\n # daylight — Bright light-mode theme\n # warm-lightmode — Warm paper-tone light-mode theme\n # poseidon — Sea-green/teal Olympian theme\n # sisyphus — Earthy stone-and-moss theme\n # charizard — Fiery orange dragon theme\n #\n # Custom skins: drop a YAML file in ~/.hermes/skins/<name>.yaml\n # Schema (all fields optional, missing values inherit from default):\n #\n # name: my-theme\n # description: Short description\n # colors:\n # banner_border: \"#HEX\" # Panel border\n # banner_title: \"#HEX\" # Panel title\n # banner_accent: \"#HEX\" # Section headers (Available Tools, etc.)\n # banner_dim: \"#HEX\" # Dim/muted text\n # banner_text: \"#HEX\" # Body text (tool names, skill names)\n # ui_accent: \"#HEX\" # UI accent color\n # response_border: \"#HEX\" # Response box border color\n # spinner:\n # waiting_faces: [\"(⚔)\", \"(⛨)\"] # Faces shown while waiting\n # thinking_faces: [\"(⚔)\", \"(⌁)\"] # Faces shown while thinking\n # thinking_verbs: [\"forging\", \"plotting\"] # Verbs for spinner messages\n # wings: # Optional left/right spinner decorations\n # - [\"⟪⚔\", \"⚔⟫\"]\n # - [\"⟪▲\", \"▲⟫\"]\n # branding:\n # agent_name: \"My Agent\" # Banner title and branding\n # welcome: \"Welcome message\" # Shown at CLI startup\n # response_label: \" ⚔ Agent \" # Response box header label\n # prompt_symbol: \"⚔\" # Prompt symbol (bare token; renderers add trailing space)\n # tool_prefix: \"╎\" # Tool output line prefix (default: ┊)\n #\n skin: default\n\n# =============================================================================\n# Model Aliases — short names for /model command\n# =============================================================================\n# Map short aliases to exact (model, provider, base_url) tuples.\n# Used by /model tab completion and resolve_alias().\n# Aliases are checked BEFORE the models.dev catalog, so they can route\n# to endpoints not in the catalog (e.g. Ollama Cloud, local servers).\n#\n# model_aliases:\n# opus:\n# model: claude-opus-4-6\n# provider: anthropic\n# qwen:\n# model: \"qwen3.5:397b\"\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n# glm:\n# model: glm-4.7\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n\n# =============================================================================\n# Privacy\n# =============================================================================\n# privacy:\n# # Redact PII from the LLM context prompt.\n# # When true, phone numbers are stripped and user/chat IDs are replaced\n# # with deterministic hashes before being sent to the model.\n# # Names and usernames are NOT affected (user-chosen, publicly visible).\n# # Routing/delivery still uses the original values internally.\n# redact_pii: false\n\n# =============================================================================\n# Shell-script hooks\n# =============================================================================\n# Register shell scripts as plugin-hook callbacks. Each entry is executed as\n# a subprocess (shell=False, shlex.split) with a JSON payload on stdin. On\n# stdout the script may return JSON that either blocks the tool call or\n# injects context into the next LLM call.\n#\n# Valid events (mirror hermes_cli.plugins.VALID_HOOKS):\n# pre_tool_call, post_tool_call, pre_llm_call, post_llm_call,\n# pre_api_request, post_api_request, on_session_start, on_session_end,\n# on_session_finalize, on_session_reset, subagent_stop\n#\n# First-use consent: each (event, command) pair prompts once on a TTY, then\n# is persisted to ~/.hermes/shell-hooks-allowlist.json. Non-interactive\n# runs (gateway, cron) need --accept-hooks, HERMES_ACCEPT_HOOKS=1, or the\n# hooks_auto_accept key below.\n#\n# See website/docs/user-guide/features/hooks.md for the full JSON wire\n# protocol and worked examples.\n#\n# hooks:\n# pre_tool_call:\n# - matcher: \"terminal\"\n# command: \"~/.hermes/agent-hooks/block-rm-rf.sh\"\n# timeout: 10\n# post_tool_call:\n# - matcher: \"write_file|patch\"\n# command: \"~/.hermes/agent-hooks/auto-format.sh\"\n# pre_llm_call:\n# - command: \"~/.hermes/agent-hooks/inject-cwd-context.sh\"\n# subagent_stop:\n# - command: \"~/.hermes/agent-hooks/log-orchestration.sh\"\n#\n# hooks_auto_accept: false\n\n\n# =============================================================================\n# Update Behavior\n# =============================================================================\nupdates:\n # Create a full HERMES_HOME zip before every `hermes update`.\n # Backups land in ~/.hermes/backups/ and can be restored with `hermes import`.\n # Off by default because large homes can add minutes to every update.\n pre_update_backup: false\n\n # Number of pre-update backup zips to retain.\n backup_keep: 5\n\n # What non-interactive updates do with local source edits in the Hermes repo.\n # Interactive terminal updates always prompt before restoring the autostash.\n #\n # stash - auto-stash before pull, then auto-restore after success (default)\n # discard - drop the update-created stash after success; use only on managed\n # installs where local source edits should not persist\n non_interactive_local_changes: \"stash\"\n\n\n# =============================================================================\n# Web Dashboard\n# =============================================================================\n# OAuth gate configuration for `hermes dashboard --host <non-loopback>`.\n# The bundled Nous Portal plugin reads these on startup; settings here are\n# the canonical surface. Each can be overridden by an environment variable:\n#\n# dashboard.oauth.client_id <- HERMES_DASHBOARD_OAUTH_CLIENT_ID\n# dashboard.oauth.portal_url <- HERMES_DASHBOARD_PORTAL_URL\n# dashboard.public_url <- HERMES_DASHBOARD_PUBLIC_URL\n#\n# Env wins when set to a non-empty value. This is what Fly.io's platform-\n# secret injection uses to push per-deploy client_ids without needing to\n# bake a config.yaml into the image. Empty env values are treated as unset\n# so a provisioned-but-not-populated secret can't shadow a valid entry here.\n#\n# Local dev / on-prem deploys should typically set these via config.yaml\n# (the ~/.hermes/.env file is reserved for API keys and secrets).\n#\n# dashboard:\n# oauth:\n# client_id: \"\" # agent:{instance_id}; Portal provisions this at deploy\n# portal_url: \"\" # blank → default https://portal.nousresearch.com\n#\n# # Force the absolute base URL the OAuth callback (and any other public\n# # URL the dashboard hands to external systems) is built from. Set this\n# # for deploys behind reverse proxies that don't reliably forward\n# # X-Forwarded-Host / X-Forwarded-Proto / X-Forwarded-Prefix (manual\n# # nginx setups, on-prem ingresses, custom-domain Fly deploys without\n# # full proxy header chains).\n# #\n# # When set, the value is the complete authority: scheme + host +\n# # optional path prefix (e.g. \"https://example.com/hermes\"). The OAuth\n# # callback URL becomes \"<public_url>/auth/callback\" — X-Forwarded-Prefix\n# # is IGNORED on this code path because the operator has explicitly\n# # declared the public URL and we no longer need to guess.\n# #\n# # Leave empty to use the existing proxy-header reconstruction (the\n# # default — works on Fly.io out of the box).\n# #\n# # public_url: \"https://example.com/hermes\"\n#\n# -----------------------------------------------------------------------------\n# Self-hosted OIDC dashboard auth (generic OpenID Connect — Authentik,\n# Keycloak, Zitadel, Authelia, Auth0, Okta, Google, …). Use this INSTEAD of the\n# nous block above when gating the dashboard with your own identity provider.\n# Each setting can be overridden by an environment variable:\n#\n# dashboard.oauth.self_hosted.issuer <- HERMES_DASHBOARD_OIDC_ISSUER\n# dashboard.oauth.self_hosted.client_id <- HERMES_DASHBOARD_OIDC_CLIENT_ID\n# dashboard.oauth.self_hosted.scopes <- HERMES_DASHBOARD_OIDC_SCOPES\n# dashboard.oauth.self_hosted.client_secret <- HERMES_DASHBOARD_OIDC_CLIENT_SECRET\n#\n# dashboard:\n# oauth:\n# provider: self-hosted\n# self_hosted:\n# issuer: \"https://auth.example.com/application/o/hermes/\" # required\n# client_id: \"hermes-dashboard\" # required\n# scopes: \"openid profile email\" # optional\n#\n# # OPTIONAL — set ONLY if your IDP registered the client as\n# # *confidential* (Authentik / Keycloak often default to this). When\n# # set, Hermes authenticates the client at the token endpoint\n# # (client_secret_basic or client_secret_post, auto-selected from the\n# # IDP's discovery doc) IN ADDITION to PKCE. Leave unset for a public\n# # (PKCE-only) client — the common case.\n# #\n# # This is a CREDENTIAL: prefer setting HERMES_DASHBOARD_OIDC_CLIENT_SECRET\n# # in ~/.hermes/.env over putting it here in config.yaml.\n# # client_secret: \"\"\n\n# =============================================================================\n# External secret sources\n# =============================================================================\n# Pull provider credentials from external secret managers at process startup\n# instead of storing them in ~/.hermes/.env. Only the manager's bootstrap\n# credential (e.g. BWS_ACCESS_TOKEN / OP_SERVICE_ACCOUNT_TOKEN) lives in .env\n# (or your shell / desktop session); everything else rotates centrally.\n# Failures never block startup — Hermes warns once and continues with\n# whatever .env already had.\n#\n# Multiple sources can be enabled at once:\n# - \"mapped\" sources (explicit VAR -> ref bindings, e.g. 1Password's env:\n# map) beat \"bulk\" sources (whole-project dumps like Bitwarden BSM)\n# - within a shape, the first source to claim a var wins; later claims\n# are skipped with a startup warning (never a silent clobber)\n# - a source's override_existing lets it beat .env/shell values, but\n# never another secret source's claim\n# Docs: https://hermes-agent.nousresearch.com/docs/user-guide/secrets/\n#\n# secrets:\n# # Optional explicit ordering of enabled sources.\n# # sources: [onepassword, bitwarden]\n#\n# # ---- Bitwarden Secrets Manager (bws CLI) --------------------------------\n# bitwarden:\n# enabled: false\n# access_token_env: BWS_ACCESS_TOKEN # bootstrap token, sourced from .env\n# project_id: \"\" # UUID of the BSM project to sync\n# server_url: \"\" # \"\" = US Cloud; EU/self-hosted URL otherwise\n# cache_ttl_seconds: 300 # 0 disables fresh caching\n# encrypted_cache: # optional encrypted stale fallback\n# enabled: false\n# max_stale_seconds: 0 # 0 disables stale fallback\n# override_existing: true # BSM values win over existing env\n# auto_install: true # lazy-download bws into ~/.hermes/bin\n#\n# # ---- 1Password (op CLI) -------------------------------------------------\n# onepassword:\n# enabled: false\n# # Map env-var names to op:// secret references. Each is resolved with a\n# # single `op read` at startup.\n# env:\n# OPENAI_API_KEY: \"op://Private/OpenAI/api key\"\n# ANTHROPIC_API_KEY: \"op://Private/Anthropic/credential\"\n# account: \"\" # op --account shorthand; \"\" = default\n# service_account_token_env: OP_SERVICE_ACCOUNT_TOKEN # headless auth; unset = desktop session\n# binary_path: \"\" # \"\" = resolve op via PATH; else absolute path\n# cache_ttl_seconds: 300 # 0 disables BOTH cache layers\n# override_existing: true # resolved values win over existing env\n#\n# # ---- Command helper (any CLI vault) --------------------------------------\n# # Run a user-configured helper that prints KEY=VALUE lines on stdout —\n# # works with any secret store that has a CLI: keepassxc-cli, secret-tool,\n# # pass, gpg, or a script that cats a tmpfs env file. Composes with the\n# # sources above (enable any combination). POSIX-only (needs /bin/sh).\n# # The helper must be fast and NON-interactive (hard timeout, 1 MiB cap);\n# # its stderr is discarded so diagnostics can't leak secret material.\n# command:\n# enabled: false\n# command: \"cat /run/user/1000/hermes-secrets.env\"\n# helper_timeout_seconds: 3\n# override_existing: false # .env/shell win by default\n"}, "af2948343ca753bd15970d2b2118c2731976724b": {"t": 1785767994.9062185, "n": 83682, "h": "5424358e7284992e", "c": "# Hermes Agent CLI Configuration\n# Copy this file to cli-config.yaml and customize as needed.\n# This file configures the CLI behavior. Environment variables in .env take precedence.\n\n# =============================================================================\n# Model Configuration\n# =============================================================================\nmodel:\n # Default model to use (can be overridden with --model flag)\n # Both \"default\" and \"model\" work as the key name here.\n default: \"anthropic/claude-opus-4.6\"\n \n # Inference provider selection:\n # \"auto\" - Auto-detect from credentials (default)\n # \"openrouter\" - OpenRouter (requires: OPENROUTER_API_KEY or OPENAI_API_KEY)\n # \"nous\" - Nous Portal OAuth (requires: hermes auth add nous)\n # \"nous-api\" - Nous Portal API key (requires: NOUS_API_KEY)\n # \"anthropic\" - Direct Anthropic API (requires: ANTHROPIC_API_KEY)\n # \"openai-codex\" - OpenAI Codex (requires: hermes auth)\n # \"copilot\" - GitHub Copilot / GitHub Models (requires: GITHUB_TOKEN)\n # \"gemini\" - Use Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n # \"zai\" - Use z.ai / ZhipuAI GLM models (requires: GLM_API_KEY)\n # \"kimi-coding\" - Kimi / Moonshot AI (requires: KIMI_API_KEY)\n # \"minimax\" - MiniMax global (requires: MINIMAX_API_KEY)\n # \"minimax-cn\" - MiniMax China (requires: MINIMAX_CN_API_KEY)\n # \"huggingface\" - Hugging Face Inference (requires: HF_TOKEN)\n # \"nvidia\" - NVIDIA NIM / build.nvidia.com (requires: NVIDIA_API_KEY)\n # \"xiaomi\" - Xiaomi MiMo (requires: XIAOMI_API_KEY)\n # \"arcee\" - Arcee AI Trinity models (requires: ARCEEAI_API_KEY)\n # \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY — https://ollama.com/settings)\n # \"deepinfra\" - DeepInfra (requires: DEEPINFRA_API_KEY)\n # \"kilocode\" - KiloCode gateway (requires: KILOCODE_API_KEY)\n # \"azure-foundry\" - Microsoft Foundry / Azure OpenAI (API key or Entra ID)\n # \"lmstudio\" - LM Studio local server (optional: LM_API_KEY, defaults to http://127.0.0.1:1234/v1)\n #\n # Local servers (LM Studio, Ollama, vLLM, llama.cpp):\n # \"custom\" - Any other OpenAI-compatible endpoint. Set base_url below.\n # Aliases: \"ollama\", \"vllm\", \"llamacpp\" all map to \"custom\".\n # LM Studio is first-class and uses provider: \"lmstudio\".\n # It works with both no-auth and auth-enabled server modes.\n #\n # Can also be overridden for a single invocation with the --provider flag.\n provider: \"auto\"\n \n # API configuration (falls back to OPENROUTER_API_KEY env var)\n # api_key: \"your-key-here\" # Uncomment to set here instead of .env\n base_url: \"https://openrouter.ai/api/v1\"\n\n # Azure Foundry keyless auth example:\n # provider: \"azure-foundry\"\n # base_url: \"https://<resource>.openai.azure.com/openai/v1\"\n # auth_mode: \"entra_id\" # DefaultAzureCredential: az login, managed identity, workload identity, etc.\n # default: \"gpt-4o\" # Deployment/model name\n # entra:\n # scope: \"https://ai.azure.com/.default\" # Optional; this is the default.\n\n # ── Token limits — two settings, easy to confuse ──────────────────────────\n #\n # context_length: TOTAL context window (input + output tokens combined).\n # Controls when Hermes compresses history and validates requests.\n # Leave unset — Hermes auto-detects the correct value from the provider.\n # Set manually only when auto-detection is wrong (e.g. a local server with\n # a custom num_ctx, or a proxy that doesn't expose /v1/models).\n #\n # context_length: 131072\n #\n # max_tokens: OUTPUT cap — maximum tokens the model may generate per response.\n # Unrelated to how long your conversation history can be.\n # The OpenAI-standard name \"max_tokens\" is a misnomer; Anthropic's native\n # API has since renamed it \"max_output_tokens\" for clarity.\n # Leave unset to use the model's native output ceiling (recommended).\n # Set only if you want to deliberately limit individual response length.\n #\n# max_tokens: 8192\n\n # ── Custom request headers (optional) ─────────────────────────────────────\n #\n # default_headers: extra HTTP headers sent on every request to an\n # OpenAI-compatible endpoint. User values take precedence over the\n # provider/SDK defaults, so this is the supported way to override the\n # OpenAI Python SDK's identifying headers (User-Agent: OpenAI/Python ...,\n # X-Stainless-*) when a custom provider sits behind a gateway/WAF that\n # rejects them — e.g. an upstream that returns \"502 Upstream access\n # forbidden\" for the SDK default User-Agent but accepts a plain one.\n # Applies on the OpenAI wire only (not native Anthropic / Bedrock).\n #\n # default_headers:\n # User-Agent: \"curl/8.7.1\"\n #\n # extra_headers: accepted as an alias of default_headers (merged, with\n # extra_headers winning when both are set) — matches the per-provider\n # extra_headers key below.\n #\n # Per-provider variant: named providers / custom_providers entries accept an\n # extra_headers dict scoped to that endpoint only — for reverse proxies,\n # gateways, or custom auth (e.g. Cloudflare Access service tokens).\n # Merged onto SDK/provider defaults with the entry's values winning.\n # Header values are treated as secrets and are never logged.\n #\n # providers:\n # my-proxy:\n # base_url: \"https://llm.internal.example.com/v1\"\n # key_env: \"MY_PROXY_API_KEY\"\n # extra_headers:\n # CF-Access-Client-Id: \"xxxx.access\"\n # CF-Access-Client-Secret: \"${CF_ACCESS_SECRET}\"\n # X-Client-Name: \"hermes-agent\"\n\n# Named provider overrides (optional)\n# Use this for per-provider request timeouts, non-stream stale timeouts,\n# and per-model exceptions.\n# Applies to the primary turn client on every api_mode (OpenAI-wire, native\n# Anthropic, and Anthropic-compatible providers), the fallback chain, and\n# client rebuilds during credential rotation. For OpenAI-wire chat\n# completions (streaming and non-streaming) the configured value is also\n# used as the per-request ``timeout=`` kwarg so it wins over the legacy\n# HERMES_API_TIMEOUT env var (which still applies when no config is set).\n# ``stale_timeout_seconds`` controls the non-streaming stale-call detector and\n# wins over the legacy HERMES_API_CALL_STALE_TIMEOUT env var. Leaving these\n# unset keeps the legacy defaults (HERMES_API_TIMEOUT=1800s,\n# HERMES_API_CALL_STALE_TIMEOUT=90s, native Anthropic 900s). The\n# implicit non-stream stale detector is auto-disabled for local endpoints\n# and can scale upward for very large contexts.\n#\n# Not currently wired for AWS Bedrock (bedrock_converse + AnthropicBedrock\n# SDK paths) — those use boto3 with its own timeout configuration.\n#\n# providers:\n# ollama-local:\n# request_timeout_seconds: 300 # Longer timeout for local cold-starts\n# stale_timeout_seconds: 900 # Explicitly re-enable stale detection on local endpoints\n# anthropic:\n# request_timeout_seconds: 30 # Fast-fail cloud requests\n# models:\n# claude-opus-4.6:\n# timeout_seconds: 600 # Longer timeout for extended-thinking Opus calls\n# openai-codex:\n# models:\n# gpt-5.4:\n# stale_timeout_seconds: 1800 # Longer non-stream stale timeout for slow large-context turns\n\n# =============================================================================\n# OpenRouter Provider Routing (only applies when using OpenRouter)\n# =============================================================================\n# Control how requests are routed across providers on OpenRouter.\n# See: https://openrouter.ai/docs/guides/routing/provider-selection\n#\n# provider_routing:\n# # Sort strategy: \"price\" (default), \"throughput\", or \"latency\"\n# # Append :nitro to model name for a shortcut to throughput sorting.\n# sort: \"throughput\"\n#\n# # Only allow these providers (provider slugs from OpenRouter)\n# # only: [\"anthropic\", \"google\"]\n#\n# # Skip these providers entirely\n# # ignore: [\"deepinfra\", \"fireworks\"]\n#\n# # Try providers in this order (overrides default load balancing)\n# # order: [\"anthropic\", \"google\", \"together\"]\n#\n# # Require providers to support all parameters in your request\n# # require_parameters: true\n#\n# # Data policy: \"allow\" (default) or \"deny\" to exclude providers that may store data\n# # data_collection: \"deny\"\n\n# =============================================================================\n# OpenRouter Response Caching (only applies when using OpenRouter)\n# =============================================================================\n# Cache identical API responses at the OpenRouter edge for free instant replays.\n# When enabled, identical requests (same model, messages, parameters) return\n# cached responses with zero billing. Separate from Anthropic prompt caching.\n# See: https://openrouter.ai/docs/guides/features/response-caching\n#\n# openrouter:\n# response_cache: true # Enable response caching (default: true)\n# response_cache_ttl: 300 # Cache TTL in seconds, 1-86400 (default: 300)\n\n# =============================================================================\n# Git Worktree Isolation\n# =============================================================================\n# When enabled, each CLI session creates an isolated git worktree so multiple\n# agents can work on the same repo concurrently without file collisions.\n# Equivalent to always passing --worktree / -w on the command line.\n#\n# worktree: true # Always create a worktree when in a git repo\n# worktree: false # Default — only create when -w flag is passed\n#\n# By default a new worktree branches from the freshly-fetched remote tip\n# (the current branch's upstream, else the remote's default branch) so it\n# starts current with the project instead of from the local clone's\n# (possibly stale) HEAD. Set worktree_sync: false to branch from local HEAD\n# instead — useful when offline or when you deliberately want the clone's\n# exact current state as the base.\n#\n# worktree_sync: true # Default — branch from the fetched remote tip\n# worktree_sync: false # Branch from local HEAD (offline / pinned base)\n\n# =============================================================================\n# Terminal Tool Configuration\n# =============================================================================\n# Choose ONE of the following terminal configurations by uncommenting it.\n# The terminal tool executes commands in the specified environment.\n\n# -----------------------------------------------------------------------------\n# OPTION 1: Local execution (default)\n# Commands run directly on your machine in the current directory\n# -----------------------------------------------------------------------------\n# Working directory behavior:\n# - CLI (`hermes` command): Uses \".\" (current directory where you run hermes)\n# - Gateway/messaging/cron: Uses terminal.cwd here; legacy .env cwd values are deprecated\nterminal:\n backend: \"local\"\n cwd: \".\" # For local backend: \".\" = current directory. Ignored for remote backends unless a backend documents otherwise.\n timeout: 180\n # HOME policy for tool subprocesses:\n # auto - default: host uses your real HOME; containers use HERMES_HOME/home\n # real - force your real OS-user HOME\n # profile - force HERMES_HOME/home for strict per-profile CLI config isolation\n home_mode: \"auto\"\n docker_mount_cwd_to_workspace: false # SECURITY: off by default. Opt in to mount the launch cwd into Docker /workspace.\n lifetime_seconds: 300\n # sudo_password: \"hunter2\" # Optional: pipe a sudo password via sudo -S. SECURITY WARNING: plaintext.\n # sudo_password: \"\" # Explicit empty password: try empty and never open the interactive sudo prompt.\n\n# -----------------------------------------------------------------------------\n# OPTION 2: SSH remote execution\n# Commands run on a remote server - agent code stays local (sandboxed)\n# Great for: keeping agent isolated from its own code, using powerful remote hardware\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"ssh\"\n# cwd: \"/home/myuser/project\" # Path on the REMOTE server\n# timeout: 180\n# lifetime_seconds: 300\n# ssh_host: \"my-server.example.com\"\n# ssh_user: \"myuser\"\n# ssh_port: 22\n# ssh_key: \"~/.ssh/id_rsa\" # Optional - uses ssh-agent if not specified\n\n# -----------------------------------------------------------------------------\n# OPTION 3: Docker container\n# Commands run in an isolated Docker container\n# Great for: reproducible environments, testing, isolation\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"docker\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /)\n# timeout: 180\n# lifetime_seconds: 300\n# docker_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# docker_mount_cwd_to_workspace: true # Explicit opt-in: mount your launch cwd into /workspace\n# # Optional: run the container as your host user's uid:gid so files written\n# # into bind-mounted dirs are owned by you, not root. Drops SETUID/SETGID\n# # caps too since no gosu privilege drop is needed. Leave off if your\n# # chosen docker_image expects to start as root.\n# docker_run_as_host_user: true\n# # Optional: explicitly forward selected env vars into Docker.\n# # These values come from your current shell first, then ~/.hermes/.env.\n# # Warning: anything forwarded here is visible to commands run in the container.\n# docker_forward_env:\n# - \"GITHUB_TOKEN\"\n# - \"NPM_TOKEN\"\n# # Optional: extra flags passed verbatim to docker run (appended after security defaults).\n# # Useful for adding capabilities (e.g. apt installs needing SETUID) or custom options.\n# # Example: add a Linux capability not included by default\n# # docker_extra_args:\n# # - \"--cap-add\"\n# # - \"SETUID\"\n\n# -----------------------------------------------------------------------------\n# OPTION 4: Singularity/Apptainer container\n# Commands run in a Singularity container (common in HPC environments)\n# Great for: HPC clusters, shared compute environments\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"singularity\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# singularity_image: \"docker://nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 5: Modal cloud execution\n# Commands run on Modal's cloud infrastructure\n# Great for: GPU access, scalable compute, serverless execution\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"modal\"\n# cwd: \"/workspace\" # Path INSIDE the sandbox (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# modal_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 6: Daytona cloud execution\n# Commands run in Daytona cloud sandboxes\n# Great for: Cloud dev environments, persistent workspaces, team collaboration\n# Requires: pip install daytona, DAYTONA_API_KEY env var\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"daytona\"\n# cwd: \"~\"\n# timeout: 180\n# lifetime_seconds: 300\n# daytona_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# container_disk: 10240 # Daytona max is 10GB per sandbox\n\n#\n# --- Container resource limits (docker, singularity, modal, daytona -- ignored for local/ssh) ---\n# These settings apply to all container backends. They control the resources\n# allocated to the sandbox and whether its filesystem persists across sessions.\n container_cpu: 1 # CPU cores\n container_memory: 5120 # Memory in MB (5120 = 5GB)\n container_disk: 51200 # Disk in MB (51200 = 50GB)\n container_persistent: true # Persist filesystem across sessions (false = ephemeral)\n\n# -----------------------------------------------------------------------------\n# SUDO SUPPORT (works with ALL backends above)\n# -----------------------------------------------------------------------------\n# Add sudo_password to any terminal config above to enable sudo commands.\n# The password is piped via `sudo -S`. Works with local, ssh, docker, etc.\n#\n# SECURITY WARNING: Password stored in plaintext!\n#\n# INTERACTIVE PROMPT: If sudo_password is unset and the CLI is running,\n# you'll be prompted to enter your password when sudo is needed:\n# - 45-second timeout (auto-skips if no input)\n# - Press Enter to skip (command fails gracefully)\n# - Password is hidden while typing\n# - Password is cached for the session\n#\n# EMPTY PASSWORDS: Setting sudo_password to an explicit empty string is different\n# from leaving it unset. Hermes will try an empty password via `sudo -S` and\n# will not open the interactive prompt. This is useful for passwordless sudo,\n# Touch ID sudo setups, and environments where prompting is just noise.\n#\n# ALTERNATIVES:\n# - SSH backend: Configure passwordless sudo on the remote server\n# - Containers: Run as root inside the container (no sudo needed)\n# - Local: Configure /etc/sudoers for specific commands\n#\n# Example (add to your terminal section):\n# sudo_password: \"your-password-here\"\n\n# =============================================================================\n# Security Scanning (tirith)\n# =============================================================================\n# Optional pre-exec command security scanning via tirith.\n# Detects homograph URLs, pipe-to-shell, terminal injection, env manipulation.\n# Install: brew install sheeki03/tap/tirith\n# Docs: https://github.com/sheeki03/tirith\n#\n# security:\n# tirith_enabled: true # Enable/disable tirith scanning\n# tirith_path: \"tirith\" # Path to tirith binary (supports ~ expansion)\n# tirith_timeout: 5 # Scan timeout in seconds\n# tirith_fail_open: true # Allow commands if tirith unavailable\n\n# =============================================================================\n# Browser Tool Configuration\n# =============================================================================\nbrowser:\n # Inactivity timeout in seconds - browser sessions are automatically closed\n # after this period of no activity between agent loops (default: 120 = 2 minutes)\n inactivity_timeout: 120\n\n# =============================================================================\n# Tool Loop Guardrails\n# =============================================================================\n# Soft warnings are enabled by default. They append guidance to repeated failed\n# or non-progressing tool results but still let the tool execute. Hard stops are\n# opt-in circuit breakers for autonomous/cron sessions where stopping a loop is\n# preferable to spending the full iteration budget.\ntool_loop_guardrails:\n warnings_enabled: true\n hard_stop_enabled: false\n warn_after:\n exact_failure: 2\n same_tool_failure: 3\n idempotent_no_progress: 2\n hard_stop_after:\n exact_failure: 5\n same_tool_failure: 8\n idempotent_no_progress: 5\n\n# =============================================================================\n# Context Compression (Auto-shrinks long conversations)\n# =============================================================================\n# When conversation approaches model's context limit, middle turns are\n# automatically summarized to free up space while preserving important context.\n#\n# HOW IT WORKS:\n# 1. Tracks actual token usage from API responses (not estimates)\n# 2. When prompt_tokens >= threshold% of model's context_length, triggers compression\n# 3. Protects first 3 turns (system prompt, initial request, first response)\n# 4. Protects last N turns (default 20 messages = ~10 full turns of recent context)\n# 5. Summarizes middle turns using a fast/cheap model\n# 6. Inserts summary as a user message, continues conversation seamlessly\n#\n# Post-compression tail budget is target_ratio × threshold × context_length:\n# 200K context, threshold 0.50, ratio 0.20 → 20K tokens of recent tail preserved\n# 1M context, threshold 0.50, ratio 0.20 → 100K tokens of recent tail preserved\n#\ncompression:\n # Enable automatic context compression (default: true)\n # Set to false if you prefer to manage context manually or want errors on overflow\n enabled: true\n\n # Opt-in compression progress notices on chat platforms (default: false).\n # By design, routine automatic compression is SILENT on human-facing chat\n # gateways (Telegram, Discord, Slack, ...) — it happens in the background\n # with server-side logging only. Set true to also deliver the routine\n # progress statuses (compacting started, preflight/pre-API compression,\n # idle compaction, retry progress, and the compaction-complete notice) to\n # chat platforms. Unrelated operational noise (auxiliary model failures,\n # provider retry chatter) stays suppressed either way, and compression\n # FAILURE notices + manual /compress feedback are always visible\n # regardless of this setting. (#52995)\n progress_notices: false\n \n # Trigger compression at this % of model's context limit (default: 0.50 = 50%)\n # Lower values = more aggressive compression, higher values = compress later\n # Models with context windows below 512K are floored at 0.75 (raise-only) so\n # compaction doesn't fire with half the window still free; set above 0.75 to override.\n threshold: 0.50\n\n # Per-model threshold overrides: keys are substring-matched against the model\n # name (longest match wins). Useful when some models need different compaction\n # points — e.g. a 1M-context model can compress later (0.30) while a 128K\n # model needs to compress earlier (0.60). The small-context floor (75% for\n # <512K models) still applies on top of per-model overrides.\n # model_thresholds:\n # \"glm-5.2\": 0.40\n # \"claude-sonnet\": 0.35\n # \"gpt-5\": 0.30\n\n # Optional absolute token cap for the compression trigger (default: null = disabled).\n # When set, compression fires at the LOWER of the ratio-based threshold and this\n # absolute token count — first-fires-wins. It never fires later than this count\n # regardless of which model is active (useful when switching between models with\n # very different context windows). Clamped to the model's context length at\n # apply-time, so a cap above the window is a no-op (ratio-based threshold wins).\n # Survives model switches and fallback activations.\n # threshold_tokens: 200000\n\n # Existing Codex gpt-5.5 behavior: raise Hermes' compaction trigger to 85%\n # for the ChatGPT Codex OAuth route. Set false to opt back down to threshold.\n codex_gpt55_autoraise: true\n\n # Fraction of the threshold to preserve as recent tail (default: 0.20 = 20%)\n # e.g. 20% of 50% threshold = 10% of total context kept as recent messages.\n # Summary output is separately capped at 12K tokens (Gemini output limit).\n # Range: 0.10 - 0.80\n target_ratio: 0.20\n\n # Number of most-recent messages to always preserve (default: 20 ≈ 10 full turns)\n # Higher values keep more recent conversation intact at the cost of more aggressive\n # compression of older turns.\n protect_last_n: 20\n\n # Minimum number of REAL (actionable) user messages guaranteed to survive in\n # the uncompressed tail (default: 1 = the existing single last-user anchor,\n # behavior-preserving). Raise to e.g. 3 to keep the last 3 real user turns\n # verbatim even when bulky tool outputs fill the tail token budget — blank\n # platform echoes, compaction handoffs, and synthetic continuation rows never\n # count toward N. The tail can exceed the token budget when this pulls the\n # cut back; the guarantee wins over the budget by design.\n min_tail_user_messages: 1\n\n # Compression retry rounds before a turn gives up with \"max compression\n # attempts reached\" (default: 3, same as the previous hardcoded value).\n # Raise (e.g. 6) for tool-schema-heavy sessions where 3 rounds cannot bring\n # the request estimate under the threshold. Validated >= 1, hard cap 10.\n max_attempts: 3\n\n # Codex app-server (codex CLI runtime) thread-compaction mode. The codex\n # agent owns the real thread context on this runtime, so Hermes' summarizer\n # cannot shrink it — compaction goes through the app server instead.\n # native = let Codex decide when to compact its own thread (default)\n # hermes = let Hermes threshold trigger Codex thread/compact/start\n # off = Hermes will not auto-trigger compaction; Codex may still compact natively\n codex_app_server_auto: native\n\n # Number of non-system messages to protect at the head of the transcript, in\n # ADDITION to the system prompt (which is always implicitly protected).\n # Head messages are NEVER summarized — they survive every compression\n # indefinitely. This gives stable early context for short/medium sessions,\n # but in long-running sessions that rely on rolling compaction the pinned\n # opening turns may not match how you want the session framed over time.\n # Set to 0 to preserve ONLY the system prompt (plus the rolling summary\n # and recent tail) — the cleanest configuration for long-running sessions.\n # Default 3 preserves the system prompt plus the first three non-system\n # head messages, matching the pre-feature behaviour.\n protect_first_n: 3\n\n # Idle compaction (default: 0 = disabled). When > 0, a session that resumes\n # after at least this many seconds of inactivity compacts its accumulated\n # history up front, before the first reply, so a long-lived thread you come\n # back to later doesn't re-read its full stale context on every turn.\n # Time-based, so it complements (does not replace) the size-based `threshold`\n # above. It is skipped when the context is already small (at or below the\n # post-compression target = threshold × target_ratio), so it never wastes a\n # summarization on a short idle thread. Example: 1800 = compact after 30 min idle.\n idle_compact_after_seconds: 0\n\n # Proactive tool-result prune (default: 0 = disabled). Opt-in token trigger\n # for a deterministic, no-LLM prune of OLD tool-result payloads, run\n # independently of `threshold` above. On large-window models (512K/1M) the\n # ratio threshold rarely fires, so bulky tool outputs (terminal dumps, file\n # reads, web extracts) ride along in history and get re-billed every turn.\n # When re-sent history exceeds this many tokens, the prune dedupes identical\n # results, summarizes older oversized ones, and truncates large tool-call\n # arguments — protecting the most recent `protect_last_n` messages and never\n # calling the model. Try 48000 to enable. Built-in compressor engine only;\n # other context engines inherit a safe no-op.\n # NOTE: a committed prune rewrites already-sent history, which invalidates\n # the provider's prompt-cache prefix — the min_reclaim gate below keeps\n # those cache breaks episodic (like a compression boundary) instead of\n # per-turn.\n proactive_prune_tokens: 0\n\n # The prune's summarize pass only touches tool results larger than this many\n # characters (clamped to >= 200 so a generated summary can't be\n # re-summarized). Default 8000.\n proactive_prune_min_result_chars: 8000\n\n # A proactive prune only COMMITS when it reclaims at least this many tokens\n # (measured on the pruned output). This is the prompt-cache hysteresis gate:\n # one meaningful, amortized cache break per batch of stale tool output\n # instead of a tiny break on every tool iteration. 0 = commit any non-zero\n # prune. Default 4096.\n proactive_prune_min_reclaim_tokens: 4096\n\n # To pin a specific model/provider for compression summaries, use the\n # auxiliary section below (auxiliary.compression.provider / model).\n\n# =============================================================================\n# Anthropic prompt caching TTL\n# =============================================================================\n# When prompt caching is active (Claude via OpenRouter or native Anthropic),\n# Anthropic supports two TTL tiers for cached prefixes: \"5m\" (default) and\n# \"1h\". Other values are ignored and \"5m\" is used.\n#\nprompt_caching:\n cache_ttl: \"5m\" # use \"1h\" for long sessions with pauses between turns\n\n# =============================================================================\n# Auxiliary Models (Advanced — Experimental)\n# =============================================================================\n# Hermes uses lightweight \"auxiliary\" models for side tasks: image analysis,\n# browser screenshot analysis, web page summarization, TTS audio-tag insertion,\n# and context compression.\n#\n# By default these use Gemini Flash via OpenRouter or Nous Portal and are\n# auto-detected from your credentials. You do NOT need to change anything\n# here for normal usage.\n#\n# WARNING: Overriding these with providers other than OpenRouter or Nous Portal\n# is EXPERIMENTAL and may not work. Not all models/providers support vision,\n# produce usable summaries, or accept the same API format. Change at your own\n# risk — if things break, reset to \"auto\" / empty values.\n#\n# Each task has its own provider + model pair so you can mix providers.\n# For example: OpenRouter for vision (needs multimodal), but your main\n# local endpoint for compression (just needs text).\n#\n# Provider options:\n# \"auto\" - Best available: OpenRouter → Nous Portal → main endpoint (default)\n# \"openrouter\" - Force OpenRouter (requires OPENROUTER_API_KEY)\n# \"nous\" - Force Nous Portal (requires: hermes auth add nous)\n# \"gemini\" - Force Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n# \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY)\n# \"codex\" - Force Codex OAuth (requires: hermes model → Codex).\n# Uses gpt-5.3-codex which supports vision.\n# \"main\" - Use your custom endpoint (OPENAI_BASE_URL + OPENAI_API_KEY).\n# Works with OpenAI API, local models, or any OpenAI-compatible\n# endpoint. Also falls back to Codex OAuth and API-key providers.\n#\n# Model: leave empty to use the provider's default. When empty, OpenRouter\n# uses \"google/gemini-3-flash-preview\" and Nous uses \"gemini-3-flash\".\n# Other providers pick a sensible default automatically.\n#\n# auxiliary:\n# # Image analysis: vision_analyze tool + browser screenshots\n# vision:\n# provider: \"auto\"\n# model: \"\" # e.g. \"google/gemini-2.5-flash\", \"openai/gpt-4o\"\n# timeout: 30 # LLM API call timeout (seconds)\n# download_timeout: 30 # Image HTTP download timeout (seconds)\n# # Increase for slow connections or self-hosted image servers\n# reasoning_effort: \"\" # Per-task thinking level: none, minimal, low, medium,\n# # high, xhigh, max, ultra. Empty = provider default.\n# # Works on every auxiliary task block (vision,\n# # web_extract, compression, title_generation, curator,\n# # background_review, moa_reference, ...). Example: run\n# # compression at \"low\" and vision at \"none\" to cut\n# # side-task latency/cost on reasoning models.\n#\n# # Web page scraping / summarization + browser page text extraction\n# web_extract:\n# provider: \"auto\"\n# model: \"\"\n# reasoning_effort: \"low\"\n#\n# # Gemini 3.1 TTS hidden audio-tag insertion\n# tts_audio_tags:\n# provider: \"auto\" # empty model = your main chat model\n# model: \"\"\n# timeout: 30\n#\n# # Automatic session title generation after the first exchange\n# title_generation:\n# enabled: true # set false to disable auto-title generation\n# provider: \"auto\"\n# model: \"\"\n# timeout: 30\n# language: \"\" # empty = match the user's language; or e.g. \"English\"\n#\n# # Session search — summarizes matching past sessions\n# session_search:\n# provider: \"auto\"\n# model: \"\"\n# timeout: 30\n# max_concurrency: 3 # Limit parallel summaries to reduce request-burst 429s\n# extra_body: {} # Provider-specific OpenAI-compatible request fields\n# # Example for providers that support request-body\n# # reasoning controls:\n# # extra_body:\n# # enable_thinking: false\n# # Some vLLM/Qwen deployments expect this nested:\n# # extra_body:\n# # chat_template_kwargs:\n# # enable_thinking: false\n\n# =============================================================================\n# Persistent Memory\n# =============================================================================\n# Bounded curated memory injected into the system prompt every session.\n# Two stores: MEMORY.md (agent's notes) and USER.md (user profile).\n# Character limits keep the memory small and focused. The agent manages\n# pruning -- when at the limit, it must consolidate or replace entries.\n# Disabled by default in batch_runner.\n#\nmemory:\n # Agent's personal notes: environment facts, conventions, things learned\n memory_enabled: true\n \n # User profile: preferences, communication style, expectations\n user_profile_enabled: true\n \n # Character limits (~2.75 chars per token, model-independent)\n memory_char_limit: 2200 # ~800 tokens\n user_char_limit: 1375 # ~500 tokens\n\n # Periodic memory nudge: remind the agent to consider saving memories\n # every N user turns. Set to 0 to disable. Only active when memory is enabled.\n nudge_interval: 10 # Nudge every 10 user turns (0 = disabled)\n\n # Memory flush: give the agent one turn to save memories before context is\n # lost (compression, /new, /reset, exit). Set to 0 to disable.\n # For exit/reset, only fires if the session had at least this many user turns.\n flush_min_turns: 6 # Min user turns to trigger flush on exit/reset (0 = disabled)\n\n# =============================================================================\n# Session Reset Policy (Messaging Platforms)\n# =============================================================================\n# Controls when messaging sessions (Telegram, Discord, WhatsApp, Slack) are\n# automatically cleared. Default is \"none\": sessions never auto-reset —\n# conversation context lives until you /reset or /new manually, or context\n# compression kicks in. Opt in to automatic resets if you prefer sessions to\n# clear on a schedule (long-lived context increases API cost per message,\n# though prompt caching and compression keep this manageable).\n#\n# When an automatic reset triggers, the agent first saves important\n# information to its persistent memory — but the conversation context is\n# wiped. The agent starts fresh but retains learned facts via its memory\n# system.\n#\n# Modes:\n# \"none\" - Never auto-reset (default); context lives until /reset or compression\n# \"idle\" - Reset after N minutes of inactivity\n# \"daily\" - Reset at a fixed hour each day\n# \"both\" - Reset on EITHER inactivity timeout or daily boundary\n#\nsession_reset:\n mode: none # \"none\", \"idle\", \"daily\", or \"both\"\n idle_minutes: 1440 # Inactivity timeout in minutes (used by \"idle\"/\"both\")\n at_hour: 4 # Daily reset hour, 0-23 local time (used by \"daily\"/\"both\")\n\n# Maximum number of simultaneously active chat sessions across CLI, TUI,\n# dashboard chat, and messaging gateway. Set to null, 0, or omit to allow\n# unlimited concurrent sessions. When the limit is reached, new sessions get a\n# clean error while existing active sessions keep their normal behavior. This\n# top-level key takes precedence over gateway.max_concurrent_sessions. The cap\n# is a best-effort single-host/profile runtime guard; Hermes fails open if the\n# local runtime lease registry cannot be read or locked.\nmax_concurrent_sessions: null\n\n# When true, group/channel chats use one session per participant when the platform\n# provides a user ID. This is the secure default and prevents users in the same\n# room from sharing context, interrupts, and token costs. Set false only if you\n# explicitly want one shared \"room brain\" per group/channel.\ngroup_sessions_per_user: true\n\n# ─────────────────────────────────────────────────────────────────────────────\n# API Server — per-client model routing\n# ─────────────────────────────────────────────────────────────────────────────\n# Route different API clients to different models/providers on a single\n# Hermes deployment. Clients choose a backend by sending a specific string\n# as the OpenAI ``model`` field. Unmapped model values fall back to the\n# global model configured in the ``model:`` section above, and an explicit\n# session /model override always wins over a route.\n#\n# Configure via the ``platforms.api_server.extra.model_routes`` gateway\n# config block:\n#\n# platforms:\n# api_server:\n# enabled: true\n# extra:\n# key: \"your-api-server-secret\"\n# model_routes:\n# # Xiaozhi clients send model=\"minimax-m2\" → routed to MiniMax via OpenRouter\n# minimax-m2:\n# model: \"minimax/minimax-m1\"\n# provider: \"openrouter\" # optional — overrides global provider\n# # api_key: \"sk-...\" # optional — per-route UPSTREAM provider\n# # key (NOT caller auth; never logged)\n# # base_url: \"https://...\" # optional — per-route base URL\n# # GPT clients keep their own alias\n# gpt-5:\n# model: \"openai/gpt-5\"\n# provider: \"openrouter\"\n#\n# Configured aliases are automatically listed by GET /v1/models so clients\n# can discover them without manual coordination. Caller authentication is\n# unchanged: every request still authenticates with the global API server\n# key (``extra.key`` / API_SERVER_KEY).\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Gateway Streaming\n# ─────────────────────────────────────────────────────────────────────────────\n# Stream tokens to messaging platforms in real-time. The bot sends a message\n# on first token, then progressively edits it as more tokens arrive.\n# Disabled by default — enable to try the streaming UX on Telegram/Discord/Slack.\n# For Telegram, partial edits are sent as plain text and only the final edit uses MarkdownV2.\nstreaming:\n enabled: false\n # transport: edit # \"edit\" = progressive editMessageText\n # edit_interval: 0.3 # seconds between message edits\n # buffer_threshold: 40 # chars before forcing an edit flush\n # cursor: \" ▉\" # cursor shown during streaming\n\n# =============================================================================\n# Skills Configuration\n# =============================================================================\n# Skills are reusable procedures the agent can load and follow. The agent can\n# also create new skills after completing complex tasks.\n#\nskills:\n # Nudge the agent to create skills after complex tasks.\n # Every N tool-calling iterations, remind the model to consider saving a skill.\n # Set to 0 to disable.\n creation_nudge_interval: 15\n\n # External skill directories — share skills across tools/agents without\n # copying them into ~/.hermes/skills/. Each path is expanded (~ and ${VAR})\n # and resolved to an absolute path. External dirs are read-only: skill\n # creation always writes to ~/.hermes/skills/. Local skills take precedence\n # when names collide.\n # external_dirs:\n # - ~/.agents/skills\n # - /home/shared/team-skills\n\n# =============================================================================\n# Agent Behavior\n# =============================================================================\nagent:\n # Maximum tool-calling iterations per conversation (default: 500)\n # Higher = more room for complex tasks, but costs more tokens\n # Recommended: 20-30 for focused tasks, 50-100 for open exploration\n max_turns: 500\n\n # Inactivity timeout for gateway agent runs (seconds, 0 = unlimited).\n # The agent can run indefinitely when actively calling tools or receiving\n # API responses. Only fires after the agent has been idle for this duration.\n # gateway_timeout: 1800\n\n # Staged warning: send a warning before escalating to full timeout.\n # Fires once per run when inactivity reaches this threshold (seconds).\n # Set to 0 to disable the warning.\n # gateway_timeout_warning: 900\n\n # Graceful drain timeout for gateway stop/restart (seconds).\n # Default 0 = no drain: a restart interrupts in-flight agents immediately,\n # cleans up, and exits. Set a positive value only if you want a grace\n # window on /restart, and keep it well under systemd's TimeoutStopSec.\n # restart_drain_timeout: 0\n\n # Upper bound (seconds) a submitted prompt waits for the deferred agent\n # build (MCP discovery, model metadata, skills scan) before failing with a\n # visible error. The wait is patient — the message is delivered as soon as\n # the build completes, and a progress notice is shown past 30s — so this cap\n # only fires on a genuinely hung build. Raise it for deployments with many\n # slow or unreachable MCP servers. Default 600.\n # build_wait_timeout: 600\n\n # Max app-level retry attempts for API errors (connection drops, provider\n # timeouts, 5xx, etc.) before the agent surfaces the failure. Lower this\n # to 1 if you use fallback providers and want fast failover on flaky\n # primaries (default 3). The OpenAI SDK does its own low-level retries\n # underneath this wrapper — this is the Hermes-level loop.\n # api_max_retries: 3\n\n # After the agent edits code without fresh passing verification, nudge it to\n # verify before finishing. The default \"auto\" enables it on interactive\n # coding surfaces (CLI, TUI, desktop) and programmatic callers, and disables\n # it on conversational messaging surfaces (Telegram, Discord, etc.) where the\n # verification summary would reach a human as chat noise. Set true or false to\n # force it on or off; the HERMES_VERIFY_ON_STOP env var (1/0) takes precedence.\n # verify_on_stop: auto\n\n # Standing operator instructions for the coding posture (when Hermes is in a\n # code workspace). Appended to the coding brief as an extra system block, so\n # you can pin project-wide workflow rules without editing the shipped brief.\n # Accepts a string or a list of strings. Takes effect next session.\n # coding_instructions:\n # - \"For UI work, don't run tsc/lint until I approve the look.\"\n # - \"Clean the diff before you commit and push.\"\n\n # When verify-on-stop finds edited code without fresh verification evidence,\n # append guidance for creative UI work (avoid broad tsc/lint/test before visual\n # approval) and clean-diff expectations. Set false to keep that nudge terse.\n # verify_guidance: true\n\n # A `pre_verify` hook (plugin or shell, see Event Hooks docs) can keep the\n # agent going one more turn to verify/clean before finishing. This caps how\n # many times one turn may be nudged to continue, so a hook can't trap the loop.\n # Default 3.\n # max_verify_nudges: 3\n\n # Enable verbose logging\n verbose: false\n \n # Reasoning effort level (OpenRouter and Nous Portal)\n # Controls how much \"thinking\" the model does before responding.\n # Options: \"xhigh\" (max), \"high\", \"medium\", \"low\", \"minimal\", \"none\" (disable)\n reasoning_effort: \"medium\"\n \n # Per-model reasoning effort overrides (optional dict)\n # Key: any sensible model spelling works (exact, dots↔dashes interchangeable,\n # provider prefix optional). First match wins.\n # Value: reasoning effort level (same options as reasoning_effort)\n # Override the global reasoning_effort for that specific model.\n # NOTE: no `hermes config set` support for this key -- edit YAML directly.\n # reasoning_overrides:\n # \"openrouter/anthropic/claude-opus-4.5\": \"xhigh\"\n # \"openai/gpt-5\": \"low\"\n # \"claude-opus-4.6\": \"high\" # bare model name also works\n # \"deepseek/deepseek-v4-pro\": \"xhigh\" # dots and dashes are interchangeable\n reasoning_overrides: {}\n \n # Predefined personalities (use with /personality command)\n personalities:\n helpful: \"You are a helpful, friendly AI assistant.\"\n concise: \"You are a concise assistant. Keep responses brief and to the point.\"\n technical: \"You are a technical expert. Provide detailed, accurate technical information.\"\n creative: \"You are a creative assistant. Think outside the box and offer innovative solutions.\"\n teacher: \"You are a patient teacher. Explain concepts clearly with examples.\"\n kawaii: \"You are a kawaii assistant! Use cute expressions like (◕‿◕), ★, ♪, and ~! Add sparkles and be super enthusiastic about everything! Every response should feel warm and adorable desu~! ヽ(>∀<☆)ノ\"\n catgirl: \"You are Neko-chan, an anime catgirl AI assistant, nya~! Add 'nya' and cat-like expressions to your speech. Use kaomoji like (=^・ω・^=) and ฅ^•ﻌ•^ฅ. Be playful and curious like a cat, nya~!\"\n pirate: \"Arrr! Ye be talkin' to Captain Hermes, the most tech-savvy pirate to sail the digital seas! Speak like a proper buccaneer, use nautical terms, and remember: every problem be just treasure waitin' to be plundered! Yo ho ho!\"\n shakespeare: \"Hark! Thou speakest with an assistant most versed in the bardic arts. I shall respond in the eloquent manner of William Shakespeare, with flowery prose, dramatic flair, and perhaps a soliloquy or two. What light through yonder terminal breaks?\"\n surfer: \"Duuude! You're chatting with the chillest AI on the web, bro! Everything's gonna be totally rad. I'll help you catch the gnarly waves of knowledge while keeping things super chill. Cowabunga! 🤙\"\n noir: \"The rain hammered against the terminal like regrets on a guilty conscience. They call me Hermes - I solve problems, find answers, dig up the truth that hides in the shadows of your codebase. In this city of silicon and secrets, everyone's got something to hide. What's your story, pal?\"\n uwu: \"hewwo! i'm your fwiendwy assistant uwu~ i wiww twy my best to hewp you! *nuzzles your code* OwO what's this? wet me take a wook! i pwomise to be vewy hewpful >w<\"\n philosopher: \"Greetings, seeker of wisdom. I am an assistant who contemplates the deeper meaning behind every query. Let us examine not just the 'how' but the 'why' of your questions. Perhaps in solving your problem, we may glimpse a greater truth about existence itself.\"\n hype: \"YOOO LET'S GOOOO!!! 🔥🔥🔥 I am SO PUMPED to help you today! Every question is AMAZING and we're gonna CRUSH IT together! This is gonna be LEGENDARY! ARE YOU READY?! LET'S DO THIS! 💪😤🚀\"\n\n# =============================================================================\n# Toolsets\n# =============================================================================\n# Control which tools the agent has access to.\n# Use `hermes tools` to interactively enable/disable tools per platform.\n\n# =============================================================================\n# Platform Toolsets (per-platform tool configuration)\n# =============================================================================\n# Override which toolsets are available on each platform.\n# If a platform isn't listed here, its built-in default is used.\n#\n# You can use EITHER:\n# - A preset like \"hermes-cli\" or \"hermes-telegram\" (curated tool set)\n# - A list of individual toolsets to compose your own (see list below)\n#\n# Supported platform keys: cli, telegram, discord, whatsapp, slack, qqbot, teams, google_chat\n#\n# Examples:\n#\n# # Use presets (same as defaults):\n# platform_toolsets:\n# cli: [hermes-cli]\n# telegram: [hermes-telegram]\n#\n# # Custom: give Telegram only web + terminal + file + planning:\n# platform_toolsets:\n# telegram: [web, terminal, file, todo]\n#\n# # Custom: CLI without browser or image gen:\n# platform_toolsets:\n# cli: [web, terminal, file, skills, todo, tts, cronjob]\n#\n# # Restrictive: Discord gets read-only tools only:\n# platform_toolsets:\n# discord: [web, vision, skills, todo]\n#\n# If not set, defaults are:\n# cli: hermes-cli (everything + cronjob management)\n# telegram: hermes-telegram (terminal, file, web, vision, image, tts, browser, skills, todo, cronjob, messaging)\n# discord: hermes-discord (same as telegram)\n# whatsapp: hermes-whatsapp (same as telegram)\n# slack: hermes-slack (same as telegram)\n# signal: hermes-signal (same as telegram)\n# homeassistant: hermes-homeassistant (same as telegram)\n# qqbot: hermes-qqbot (same as telegram)\n# teams: hermes-teams (same as telegram)\n# google_chat: hermes-google_chat (same as telegram)\n#\nplatform_toolsets:\n cli: [hermes-cli]\n telegram: [hermes-telegram]\n discord: [hermes-discord]\n whatsapp: [hermes-whatsapp]\n slack: [hermes-slack]\n signal: [hermes-signal]\n homeassistant: [hermes-homeassistant]\n qqbot: [hermes-qqbot]\n yuanbao: [hermes-yuanbao]\n teams: [hermes-teams]\n google_chat: [hermes-google_chat]\n\n# =============================================================================\n# Gateway Platform Settings\n# =============================================================================\n# Optional per-platform messaging settings.\n# Platform-specific knobs live under `extra`.\n#\n# platforms:\n# telegram:\n# reply_to_mode: \"first\" # off | first | all\n# # guest_mode lets explicit @mentions from non-allowlisted groups through.\n# # Default false; ordinary messages, replies, and regex wake words stay blocked.\n# guest_mode: false\n# # allowed_chats: [\"-1001234567890\"]\n# extra:\n# disable_link_previews: false # Set true to suppress Telegram URL previews in bot messages\n# 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\n# rich_drafts: false # Experimental rich draft previews during Telegram DM streaming; default false because Telegram Desktop/macOS can visually overlay draft frames\n# command_menu:\n# # Telegram allows up to 100 BotCommands; Hermes defaults to 60 so\n# # all built-in commands plus common skill commands stay visible\n# # while remaining under Telegram's payload-size limit. Clamped 1..100.\n# max_commands: 60\n# # prepend = user priority first, then Hermes defaults\n# # append = Hermes defaults first, then user priority\n# # replace = only the list below defines priority\n# priority_mode: prepend\n# priority:\n# - my_plugin_command\n# webhook:\n# extra:\n# # Route scripts default to a 30 second timeout. Scripts must live under\n# # the active profile's scripts directory and receive webhook JSON on stdin.\n# script_timeout_seconds: 30\n#\n# Discord-specific settings (config.yaml top-level, not under platforms:):\n#\n# discord:\n# require_mention: true # Require @mention in server channels (default: true)\n# auto_thread: true # Auto-create thread on @mention (default: true)\n# free_response_channels: \"\" # Channel IDs where no mention is needed\n# reactions: true # Show processing reactions (default: true)\n# history_backfill: true # Recover missed channel messages on mention (default: true)\n# history_backfill_limit: 50 # Max messages to scan backwards (default: 50)\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Available toolsets (use these names in platform_toolsets or the toolsets list)\n#\n# Run `hermes chat --list-toolsets` to see all toolsets and their tools.\n# Run `hermes chat --list-tools` to see every individual tool with descriptions.\n# ─────────────────────────────────────────────────────────────────────────────\n#\n# INDIVIDUAL TOOLSETS (compose your own):\n# web - web_search, web_extract\n# search - web_search only (no scraping)\n# terminal - terminal, process\n# file - read_file, write_file, patch, search\n# browser - browser_navigate, browser_snapshot, browser_click, browser_type,\n# browser_scroll, browser_back, browser_press,\n# browser_get_images, browser_vision (requires BROWSERBASE_API_KEY)\n# vision - vision_analyze (requires OPENROUTER_API_KEY)\n# image_gen - image_generate (requires FAL_KEY)\n# skills - skills_list, skill_view\n# skills_hub - skill_hub (search/install/manage from online registries — user-driven only)\n# todo - todo (in-memory task planning, no deps)\n# tts - text_to_speech (Edge TTS free, or ELEVENLABS/OPENAI/MINIMAX/MISTRAL key)\n# cronjob - cronjob (create/list/update/pause/resume/run/remove scheduled tasks)\n#\n# PRESETS (curated bundles):\n# hermes-cli - All of the above except send_message\n# hermes-telegram - terminal, file, web, vision, image_gen, tts, browser,\n# skills, todo, cronjob, send_message\n# hermes-discord - Same as hermes-telegram\n# hermes-whatsapp - Same as hermes-telegram\n# hermes-slack - Same as hermes-telegram\n#\n# COMPOSITE:\n# debugging - terminal + web + file\n# safe - web + vision (no terminal access)\n# all - Everything available\n#\n# web - Web search and content extraction (web_search, web_extract)\n# search - Web search only, no scraping (web_search)\n# terminal - Command execution and process management (terminal, process)\n# file - File operations: read, write, patch, search\n# browser - Full browser automation (navigate, click, type, screenshot, etc.)\n# vision - Image analysis (vision_analyze)\n# image_gen - Image generation with FLUX (image_generate)\n# skills - Load skill documents (skills_list, skill_view)\n# todo - Task planning and tracking for multi-step work\n# memory - Persistent memory across sessions (personal notes + user profile)\n# session_search - Search and recall past conversations (FTS5 + Gemini Flash summarization)\n# tts - Text-to-speech (Edge TTS free, ElevenLabs, OpenAI, MiniMax, Mistral)\n# cronjob - Schedule and manage automated tasks (CLI-only)\n#\n# Composite toolsets:\n# debugging - terminal + web + file (for troubleshooting)\n# safe - web + vision (no terminal access)\n\n# NOTE: The top-level \"toolsets\" key is deprecated and ignored.\n# Tool configuration is managed per-platform via platform_toolsets above.\n# Use `hermes tools` to configure interactively, or edit platform_toolsets directly.\n#\n# CLI override: hermes chat --toolsets terminal,web,file\n\n# =============================================================================\n# MCP (Model Context Protocol) Servers\n# =============================================================================\n# Connect to external MCP servers to add tools from the MCP ecosystem.\n# Each server's tools are automatically discovered and registered.\n# See website/docs/user-guide/features/mcp.md for full documentation.\n#\n# Stdio servers (spawn a subprocess):\n# command: the executable to run\n# args: command-line arguments\n# env: environment variables (only these + safe defaults passed to subprocess)\n#\n# HTTP servers (connect to a URL):\n# url: the MCP server endpoint\n# headers: HTTP headers (e.g., for authentication)\n#\n# Optional per-server settings:\n# timeout: tool call timeout in seconds (default: 120)\n# connect_timeout: initial connection timeout (default: 60)\n# keepalive_interval: liveness ping cadence in seconds (default: 180).\n# Lower it below the server's session TTL for servers that expire idle\n# sessions quickly (e.g. Unreal Engine editor MCP, ~15s), otherwise idle\n# tool calls hit an expired session and pay a slow reconnect. Floored at 5s.\n#\n# mcp_servers:\n# time:\n# command: uvx\n# args: [\"mcp-server-time\"]\n# filesystem:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-filesystem\", \"/home/user\"]\n# notion:\n# url: https://mcp.notion.com/mcp\n# github:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-github\"]\n# env:\n# GITHUB_PERSONAL_ACCESS_TOKEN: \"ghp_...\"\n#\n# Sampling (server-initiated LLM requests) — enabled by default.\n# Per-server config under the 'sampling' key:\n# analysis:\n# command: npx\n# args: [\"-y\", \"analysis-server\"]\n# sampling:\n# enabled: true # default: true\n# model: \"gemini-3-flash\" # override model (optional)\n# max_tokens_cap: 4096 # max tokens per request\n# timeout: 30 # LLM call timeout (seconds)\n# max_rpm: 10 # max requests per minute\n# allowed_models: [] # model whitelist (empty = all)\n# max_tool_rounds: 5 # tool loop limit (0 = disable)\n# log_level: \"info\" # audit verbosity\n\n# =============================================================================\n# Text-to-Speech\n# =============================================================================\n# TTS defaults to Edge TTS unless changed in ~/.hermes/config.yaml.\n# Gemini TTS supports persona/director prompt files, and Gemini 3.1 Flash TTS\n# can use a hidden auxiliary rewrite pass to insert expressive square-bracket\n# audio tags into the TTS script without showing tags in chat.\n#\n# tts:\n# provider: \"gemini\"\n# gemini:\n# model: \"gemini-3.1-flash-tts-preview\"\n# voice: \"Kore\"\n# audio_tags: false\n# persona_prompt_file: \"\" # e.g. ~/.hermes/tts/radio-host.md\n\n# =============================================================================\n# Voice Transcription (Speech-to-Text)\n# =============================================================================\n# Automatically transcribe voice messages on messaging platforms.\n# Providers: local (free, faster-whisper) | groq (free tier) | openai (Whisper API) | mistral (Voxtral Transcribe)\n# Set the corresponding API key in .env: GROQ_API_KEY, OPENAI_API_KEY, or MISTRAL_API_KEY.\nstt:\n enabled: true\n # provider: \"local\" # auto-detected if omitted\n local:\n model: \"base\" # tiny | base | small | medium | large-v3 | turbo\n # language: \"\" # auto-detect; set to \"en\", \"es\", \"fr\", etc. to force\n openai:\n model: \"whisper-1\" # whisper-1 | gpt-4o-mini-transcribe | gpt-4o-transcribe\n language: \"\" # auto-detect; set to \"en\", \"es\", \"fr\", etc. to force\n # mistral:\n # model: \"voxtral-mini-latest\" # voxtral-mini-latest | voxtral-mini-2602\n # deepinfra:\n # # Model id is discovered live from the DeepInfra catalog filtered\n # # by the `stt` surface tag — leave `model` blank to take the first\n # # live result. Pin only when you need a specific Whisper variant.\n # model: \"\"\n\n# Text-to-speech. Only the deepinfra block is documented here — the\n# remaining providers (edge, openai, xai, minimax, mistral, gemini,\n# elevenlabs, neutts, kittentts, piper) inherit sensible defaults from\n# DEFAULT_CONFIG in hermes_cli/config.py.\n# tts:\n# provider: \"deepinfra\"\n# deepinfra:\n# # Model id is discovered live from the DeepInfra catalog filtered\n# # by the `tts` surface tag — leave `model` blank to take the first\n# # live result.\n# model: \"\"\n# voice: \"default\"\n\n# Image generation. Each provider plugin reads its own ``image_gen.<name>``\n# block; deepinfra discovers models live from\n# api.deepinfra.com/v1/openai/models filtered by the ``image-gen`` tag —\n# no model id is hardcoded, so retired models disappear automatically.\n# image_gen:\n# provider: \"deepinfra\"\n# deepinfra:\n# # Leave `model` blank for the first live `image-gen`-tagged result.\n# model: \"\"\n\n# =============================================================================\n# Response Pacing (Messaging Platforms)\n# =============================================================================\n# Add human-like delays between message chunks.\n# human_delay:\n# mode: \"off\" # \"off\" | \"natural\" | \"custom\"\n# min_ms: 800 # Min delay (custom mode only)\n# max_ms: 2500 # Max delay (custom mode only)\n\n# =============================================================================\n# Session Logging\n# =============================================================================\n# Session trajectories are automatically saved to logs/ directory.\n# Each session creates: logs/session_YYYYMMDD_HHMMSS_UUID.json\n#\n# The session ID is displayed in the welcome banner for easy reference.\n# Logs contain full conversation history in trajectory format:\n# - System prompt, user messages, assistant responses\n# - Tool calls with inputs/outputs\n# - Timestamps for debugging\n#\n# No configuration needed - logging is always enabled.\n# To disable, you would need to modify the source code.\n\n# =============================================================================\n# Code Execution Sandbox (Programmatic Tool Calling)\n# =============================================================================\n# The execute_code tool runs Python scripts that call Hermes tools via RPC.\n# Intermediate tool results stay out of the LLM's context window.\ncode_execution:\n timeout: 300 # Max seconds per script before kill (default: 300 = 5 min)\n max_tool_calls: 50 # Max RPC tool calls per execution (default: 50)\n\n# =============================================================================\n# Subagent Delegation\n# =============================================================================\n# The delegate_task tool spawns child agents with isolated context.\n# Supports single tasks and batch mode (default 3 parallel, configurable).\ndelegation:\n max_iterations: 50 # Max tool-calling turns per child (default: 50)\n # max_concurrent_children: 3 # Max parallel child agents per batch (default: 3, floor: 1, no ceiling).\n # WARNING: values above 10 multiply API cost linearly.\n # max_spawn_depth: 1 # Delegation tree depth cap (range: 1-3, default: 1 = flat).\n # Raise to 2 to allow workers to spawn their own subagents.\n # Requires role=\"orchestrator\" on intermediate agents.\n # orchestrator_enabled: true # Kill switch for role=\"orchestrator\" children (default: true).\n # subagent_auto_approve: false # When a subagent hits a dangerous-command approval prompt, auto-deny (default: false)\n # or auto-approve \"once\" (true) instead of blocking on stdin.\n # The parent TUI owns stdin, so blocking would deadlock; non-interactive resolution is required.\n # Both choices emit a logger.warning audit line. Flip to true only for cron/batch pipelines.\n # inherit_mcp_toolsets: true # When explicit child toolsets are narrowed, also keep the parent's MCP toolsets (default: true). Set false for strict intersection.\n # model: \"google/gemini-3-flash-preview\" # Override model for subagents (empty = inherit parent)\n # provider: \"openrouter\" # Override provider for subagents (empty = inherit parent)\n # # Resolves full credentials (base_url, api_key) automatically.\n # # Supported: openrouter, nous, zai, kimi-coding, minimax\n\n# =============================================================================\n# Honcho Integration (Cross-Session User Modeling)\n# =============================================================================\n# AI-native persistent memory via Honcho (https://honcho.dev/).\n# Builds a deeper understanding of the user across sessions and tools.\n# Runs alongside USER.md — additive, not a replacement.\n#\n# Requires: pip install honcho-ai\n# Config: ~/.honcho/config.json (shared with Claude Code, Cursor, etc.)\n# API key: HONCHO_API_KEY in ~/.hermes/.env or ~/.honcho/config.json\n#\n# Hermes-specific overrides (optional — most config comes from ~/.honcho/config.json):\n# honcho: {}\n\n# =============================================================================\n# Display\n# =============================================================================\ndisplay:\n # Use compact banner mode (hides the ASCII-art banner, shows a single line).\n # true: Compact single-line banner\n # false: Full ASCII banner with tool/skill summary (default)\n compact: false\n\n # Tool progress display level (CLI and gateway)\n # off: Silent — no tool activity shown, just the final response\n # new: Show a tool indicator only when the tool changes (skip repeats)\n # all: Show every tool call with a short preview (default)\n # verbose: Full args, results, and debug logs (same as /verbose)\n # log: Silent in chat; append every tool call to ~/.hermes/logs/tool_calls.log (gateway only)\n # Toggle at runtime with /verbose in the CLI\n tool_progress: all\n\n # Per-platform defaults can be quieter than the global setting. Telegram\n # tunes for mobile: tool_progress and busy_ack_detail default off (no\n # per-tool breadcrumb stream, no \"iteration 21/60\" debug detail in busy\n # acks or heartbeats), but interim_assistant_messages and\n # long_running_notifications STAY ON so the user has real signal between\n # turn start and final answer (mid-turn assistant commentary + a single\n # edit-in-place \"⏳ Working — N min\" heartbeat). Override under\n # display.platforms.telegram.\n\n # Auto-cleanup of temporary progress bubbles after the final response lands.\n # On platforms that support message deletion (currently Telegram), this\n # removes the tool-progress bubble, \"⏳ Still working...\" notices, and\n # context-pressure status messages once the final reply has been delivered —\n # keeping long-running turns visible live, then tidy afterward. Failed runs\n # leave the bubbles in place as breadcrumbs. Off by default.\n # Per-platform override: display.platforms.telegram.cleanup_progress\n # true: Delete tracked progress/status bubbles on successful turn\n # false: Leave everything in place (default)\n # Example:\n # display:\n # platforms:\n # telegram:\n # cleanup_progress: true\n cleanup_progress: false\n\n # Natural mid-turn assistant updates.\n # On gateway platforms, when true, completed assistant status messages are\n # sent as separate chat messages. On the Desktop app, when true, mid-turn\n # assistant narration streamed between tool calls is kept in the transcript\n # instead of the bubble collapsing to only the final message on completion.\n # Independent of tool_progress and gateway streaming.\n # true: Keep/send mid-turn assistant updates (default)\n # false: Only keep/send the final response\n interim_assistant_messages: true\n\n # Gateway-only long-running status heartbeats.\n # When false, the platform does not receive periodic \"⏳ Working — N min\"\n # notifications even if agent.gateway_notify_interval is non-zero. The\n # heartbeat edits a single message in place (where the adapter supports\n # editing) instead of posting a new bubble each interval.\n # Default: true everywhere, including Telegram (silent agents are worse\n # than a single edit-in-place heartbeat).\n long_running_notifications: true\n\n # Include detailed iteration/tool/status context in busy acknowledgments\n # and long-running heartbeats. When true, busy acks show \"iteration 21/60,\n # terminal, 10 min\" and the heartbeat shows \"⏳ Working — 12 min,\n # iteration 21/60, terminal\". When false (Telegram default), both stay\n # terse: \"Interrupting current task\" and \"⏳ Working — 12 min, terminal\".\n busy_ack_detail: true\n\n # What Enter does when Hermes is already busy (CLI and gateway platforms).\n # interrupt: Interrupt the current run and redirect Hermes (default)\n # queue: Queue your message for the next turn\n # steer: Inject your message mid-run via /steer, arriving at the agent\n # after the next tool call — no interrupt, no role violation.\n # Falls back to 'queue' if the agent isn't running yet or if\n # images are attached (steer only carries text).\n # Ctrl+C (or /stop in gateway) always interrupts regardless of this setting.\n # Toggle at runtime with /busy <interrupt|queue|steer>.\n busy_input_mode: interrupt\n\n # Background process notifications (gateway/messaging only).\n # Controls how chatty the process watcher is when you use\n # terminal(background=true, notify_on_complete=true) from Telegram/Discord/etc.\n # off: No watcher messages at all\n # result: Only the final completion message\n # error: Only the final message when exit code != 0\n # all: Running output updates + final message (default)\n background_process_notifications: all\n\n\n # Play terminal bell when agent finishes a response.\n # Useful for long-running tasks — your terminal will ding when the agent is done.\n # Works over SSH. Most terminals can be configured to flash the taskbar or play a sound.\n # true: Ring the terminal bell on each response\n # false: Silent (default)\n bell_on_complete: false\n\n # Show model reasoning/thinking before each response.\n # When enabled, a dim box shows the model's thought process above the response.\n # Toggle at runtime with /reasoning show or /reasoning hide.\n # true: Show the reasoning box\n # false: Hide reasoning (default)\n show_reasoning: false\n\n # Stream tokens to the terminal as they arrive instead of waiting for the\n # full response. The response box opens on first token and text appears\n # line-by-line. Tool calls are still captured silently.\n # true: Stream tokens as they arrive (default)\n # false: Wait for the full response before rendering\n streaming: true\n\n # Show [HH:MM] timestamps on user input and assistant response labels.\n # timestamps: false\n\n # ───────────────────────────────────────────────────────────────────────────\n # Skin / Theme\n # ───────────────────────────────────────────────────────────────────────────\n # Customize CLI visual appearance — banner colors, spinner faces, tool prefix,\n # response box label, and branding text. Change at runtime with /skin <name>.\n #\n # Built-in skins:\n # default — Classic Hermes gold/kawaii\n # ares — Crimson/bronze war-god theme with spinner wings\n # mono — Clean grayscale monochrome\n # slate — Cool blue developer-focused\n # daylight — Bright light-mode theme\n # warm-lightmode — Warm paper-tone light-mode theme\n # poseidon — Sea-green/teal Olympian theme\n # sisyphus — Earthy stone-and-moss theme\n # charizard — Fiery orange dragon theme\n #\n # Custom skins: drop a YAML file in ~/.hermes/skins/<name>.yaml\n # Schema (all fields optional, missing values inherit from default):\n #\n # name: my-theme\n # description: Short description\n # colors:\n # banner_border: \"#HEX\" # Panel border\n # banner_title: \"#HEX\" # Panel title\n # banner_accent: \"#HEX\" # Section headers (Available Tools, etc.)\n # banner_dim: \"#HEX\" # Dim/muted text\n # banner_text: \"#HEX\" # Body text (tool names, skill names)\n # ui_accent: \"#HEX\" # UI accent color\n # response_border: \"#HEX\" # Response box border color\n # spinner:\n # waiting_faces: [\"(⚔)\", \"(⛨)\"] # Faces shown while waiting\n # thinking_faces: [\"(⚔)\", \"(⌁)\"] # Faces shown while thinking\n # thinking_verbs: [\"forging\", \"plotting\"] # Verbs for spinner messages\n # wings: # Optional left/right spinner decorations\n # - [\"⟪⚔\", \"⚔⟫\"]\n # - [\"⟪▲\", \"▲⟫\"]\n # branding:\n # agent_name: \"My Agent\" # Banner title and branding\n # welcome: \"Welcome message\" # Shown at CLI startup\n # response_label: \" ⚔ Agent \" # Response box header label\n # prompt_symbol: \"⚔\" # Prompt symbol (bare token; renderers add trailing space)\n # tool_prefix: \"╎\" # Tool output line prefix (default: ┊)\n #\n skin: default\n\n# =============================================================================\n# Model Aliases — short names for /model command\n# =============================================================================\n# Map short aliases to exact (model, provider, base_url) tuples.\n# Used by /model tab completion and resolve_alias().\n# Aliases are checked BEFORE the models.dev catalog, so they can route\n# to endpoints not in the catalog (e.g. Ollama Cloud, local servers).\n#\n# model_aliases:\n# opus:\n# model: claude-opus-4-6\n# provider: anthropic\n# qwen:\n# model: \"qwen3.5:397b\"\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n# glm:\n# model: glm-4.7\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n\n# =============================================================================\n# Privacy\n# =============================================================================\n# privacy:\n# # Redact PII from the LLM context prompt.\n# # When true, phone numbers are stripped and user/chat IDs are replaced\n# # with deterministic hashes before being sent to the model.\n# # Names and usernames are NOT affected (user-chosen, publicly visible).\n# # Routing/delivery still uses the original values internally.\n# redact_pii: false\n\n# =============================================================================\n# Shell-script hooks\n# =============================================================================\n# Register shell scripts as plugin-hook callbacks. Each entry is executed as\n# a subprocess (shell=False, shlex.split) with a JSON payload on stdin. On\n# stdout the script may return JSON that either blocks the tool call or\n# injects context into the next LLM call.\n#\n# Valid events (mirror hermes_cli.plugins.VALID_HOOKS):\n# pre_tool_call, post_tool_call, pre_llm_call, post_llm_call,\n# pre_api_request, post_api_request, on_session_start, on_session_end,\n# on_session_finalize, on_session_reset, subagent_stop\n#\n# First-use consent: each (event, command) pair prompts once on a TTY, then\n# is persisted to ~/.hermes/shell-hooks-allowlist.json. Non-interactive\n# runs (gateway, cron) need --accept-hooks, HERMES_ACCEPT_HOOKS=1, or the\n# hooks_auto_accept key below.\n#\n# See website/docs/user-guide/features/hooks.md for the full JSON wire\n# protocol and worked examples.\n#\n# hooks:\n# pre_tool_call:\n# - matcher: \"terminal\"\n# command: \"~/.hermes/agent-hooks/block-rm-rf.sh\"\n# timeout: 10\n# post_tool_call:\n# - matcher: \"write_file|patch\"\n# command: \"~/.hermes/agent-hooks/auto-format.sh\"\n# pre_llm_call:\n# - command: \"~/.hermes/agent-hooks/inject-cwd-context.sh\"\n# subagent_stop:\n# - command: \"~/.hermes/agent-hooks/log-orchestration.sh\"\n#\n# hooks_auto_accept: false\n\n\n# =============================================================================\n# Update Behavior\n# =============================================================================\nupdates:\n # Create a full HERMES_HOME zip before every `hermes update`.\n # Backups land in ~/.hermes/backups/ and can be restored with `hermes import`.\n # Off by default because large homes can add minutes to every update.\n pre_update_backup: false\n\n # Number of pre-update backup zips to retain.\n backup_keep: 5\n\n # What non-interactive updates do with local source edits in the Hermes repo.\n # Interactive terminal updates always prompt before restoring the autostash.\n #\n # stash - auto-stash before pull, then auto-restore after success (default)\n # discard - drop the update-created stash after success; use only on managed\n # installs where local source edits should not persist\n non_interactive_local_changes: \"stash\"\n\n\n# =============================================================================\n# Web Dashboard\n# =============================================================================\n# OAuth gate configuration for `hermes dashboard --host <non-loopback>`.\n# The bundled Nous Portal plugin reads these on startup; settings here are\n# the canonical surface. Each can be overridden by an environment variable:\n#\n# dashboard.oauth.client_id <- HERMES_DASHBOARD_OAUTH_CLIENT_ID\n# dashboard.oauth.portal_url <- HERMES_DASHBOARD_PORTAL_URL\n# dashboard.public_url <- HERMES_DASHBOARD_PUBLIC_URL\n#\n# Env wins when set to a non-empty value. This is what Fly.io's platform-\n# secret injection uses to push per-deploy client_ids without needing to\n# bake a config.yaml into the image. Empty env values are treated as unset\n# so a provisioned-but-not-populated secret can't shadow a valid entry here.\n#\n# Local dev / on-prem deploys should typically set these via config.yaml\n# (the ~/.hermes/.env file is reserved for API keys and secrets).\n#\n# dashboard:\n# oauth:\n# client_id: \"\" # agent:{instance_id}; Portal provisions this at deploy\n# portal_url: \"\" # blank → default https://portal.nousresearch.com\n#\n# # Force the absolute base URL the OAuth callback (and any other public\n# # URL the dashboard hands to external systems) is built from. Set this\n# # for deploys behind reverse proxies that don't reliably forward\n# # X-Forwarded-Host / X-Forwarded-Proto / X-Forwarded-Prefix (manual\n# # nginx setups, on-prem ingresses, custom-domain Fly deploys without\n# # full proxy header chains).\n# #\n# # When set, the value is the complete authority: scheme + host +\n# # optional path prefix (e.g. \"https://example.com/hermes\"). The OAuth\n# # callback URL becomes \"<public_url>/auth/callback\" — X-Forwarded-Prefix\n# # is IGNORED on this code path because the operator has explicitly\n# # declared the public URL and we no longer need to guess.\n# #\n# # Leave empty to use the existing proxy-header reconstruction (the\n# # default — works on Fly.io out of the box).\n# #\n# # public_url: \"https://example.com/hermes\"\n#\n# -----------------------------------------------------------------------------\n# Self-hosted OIDC dashboard auth (generic OpenID Connect — Authentik,\n# Keycloak, Zitadel, Authelia, Auth0, Okta, Google, …). Use this INSTEAD of the\n# nous block above when gating the dashboard with your own identity provider.\n# Each setting can be overridden by an environment variable:\n#\n# dashboard.oauth.self_hosted.issuer <- HERMES_DASHBOARD_OIDC_ISSUER\n# dashboard.oauth.self_hosted.client_id <- HERMES_DASHBOARD_OIDC_CLIENT_ID\n# dashboard.oauth.self_hosted.scopes <- HERMES_DASHBOARD_OIDC_SCOPES\n# dashboard.oauth.self_hosted.client_secret <- HERMES_DASHBOARD_OIDC_CLIENT_SECRET\n#\n# dashboard:\n# oauth:\n# provider: self-hosted\n# self_hosted:\n# issuer: \"https://auth.example.com/application/o/hermes/\" # required\n# client_id: \"hermes-dashboard\" # required\n# scopes: \"openid profile email\" # optional\n#\n# # OPTIONAL — set ONLY if your IDP registered the client as\n# # *confidential* (Authentik / Keycloak often default to this). When\n# # set, Hermes authenticates the client at the token endpoint\n# # (client_secret_basic or client_secret_post, auto-selected from the\n# # IDP's discovery doc) IN ADDITION to PKCE. Leave unset for a public\n# # (PKCE-only) client — the common case.\n# #\n# # This is a CREDENTIAL: prefer setting HERMES_DASHBOARD_OIDC_CLIENT_SECRET\n# # in ~/.hermes/.env over putting it here in config.yaml.\n# # client_secret: \"\"\n\n# =============================================================================\n# External secret sources\n# =============================================================================\n# Pull provider credentials from external secret managers at process startup\n# instead of storing them in ~/.hermes/.env. Only the manager's bootstrap\n# credential (e.g. BWS_ACCESS_TOKEN / OP_SERVICE_ACCOUNT_TOKEN) lives in .env\n# (or your shell / desktop session); everything else rotates centrally.\n# Failures never block startup — Hermes warns once and continues with\n# whatever .env already had.\n#\n# Multiple sources can be enabled at once:\n# - \"mapped\" sources (explicit VAR -> ref bindings, e.g. 1Password's env:\n# map) beat \"bulk\" sources (whole-project dumps like Bitwarden BSM)\n# - within a shape, the first source to claim a var wins; later claims\n# are skipped with a startup warning (never a silent clobber)\n# - a source's override_existing lets it beat .env/shell values, but\n# never another secret source's claim\n# Docs: https://hermes-agent.nousresearch.com/docs/user-guide/secrets/\n#\n# secrets:\n# # Optional explicit ordering of enabled sources.\n# # sources: [onepassword, bitwarden]\n#\n# # ---- Bitwarden Secrets Manager (bws CLI) --------------------------------\n# bitwarden:\n# enabled: false\n# access_token_env: BWS_ACCESS_TOKEN # bootstrap token, sourced from .env\n# project_id: \"\" # UUID of the BSM project to sync\n# server_url: \"\" # \"\" = US Cloud; EU/self-hosted URL otherwise\n# cache_ttl_seconds: 300 # 0 disables fresh caching\n# encrypted_cache: # optional encrypted stale fallback\n# enabled: false\n# max_stale_seconds: 0 # 0 disables stale fallback\n# override_existing: true # BSM values win over existing env\n# auto_install: true # lazy-download bws into ~/.hermes/bin\n#\n# # ---- 1Password (op CLI) -------------------------------------------------\n# onepassword:\n# enabled: false\n# # Map env-var names to op:// secret references. Each is resolved with a\n# # single `op read` at startup.\n# env:\n# OPENAI_API_KEY: \"op://Private/OpenAI/api key\"\n# ANTHROPIC_API_KEY: \"op://Private/Anthropic/credential\"\n# account: \"\" # op --account shorthand; \"\" = default\n# service_account_token_env: OP_SERVICE_ACCOUNT_TOKEN # headless auth; unset = desktop session\n# binary_path: \"\" # \"\" = resolve op via PATH; else absolute path\n# cache_ttl_seconds: 300 # 0 disables BOTH cache layers\n# override_existing: true # resolved values win over existing env\n#\n# # ---- Command helper (any CLI vault) --------------------------------------\n# # Run a user-configured helper that prints KEY=VALUE lines on stdout —\n# # works with any secret store that has a CLI: keepassxc-cli, secret-tool,\n# # pass, gpg, or a script that cats a tmpfs env file. Composes with the\n# # sources above (enable any combination). POSIX-only (needs /bin/sh).\n# # The helper must be fast and NON-interactive (hard timeout, 1 MiB cap);\n# # its stderr is discarded so diagnostics can't leak secret material.\n# command:\n# enabled: false\n# command: \"cat /run/user/1000/hermes-secrets.env\"\n# helper_timeout_seconds: 3\n# override_existing: false # .env/shell win by default\n"}, "841a5a744ad115b001a2720bf1eeb6bec3dfcc7d": {"t": 1785767998.454004, "n": 83597, "h": "972048a16c1c68eb", "c": "# Hermes Agent CLI Configuration\n# Copy this file to cli-config.yaml and customize as needed.\n# This file configures the CLI behavior. Environment variables in .env take precedence.\n\n# =============================================================================\n# Model Configuration\n# =============================================================================\nmodel:\n # Default model to use (can be overridden with --model flag)\n # Both \"default\" and \"model\" work as the key name here.\n default: \"anthropic/claude-opus-4.6\"\n \n # Inference provider selection:\n # \"auto\" - Auto-detect from credentials (default)\n # \"openrouter\" - OpenRouter (requires: OPENROUTER_API_KEY or OPENAI_API_KEY)\n # \"nous\" - Nous Portal OAuth (requires: hermes auth add nous)\n # \"nous-api\" - Nous Portal API key (requires: NOUS_API_KEY)\n # \"anthropic\" - Direct Anthropic API (requires: ANTHROPIC_API_KEY)\n # \"openai-codex\" - OpenAI Codex (requires: hermes auth)\n # \"copilot\" - GitHub Copilot / GitHub Models (requires: GITHUB_TOKEN)\n # \"gemini\" - Use Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n # \"zai\" - Use z.ai / ZhipuAI GLM models (requires: GLM_API_KEY)\n # \"kimi-coding\" - Kimi / Moonshot AI (requires: KIMI_API_KEY)\n # \"minimax\" - MiniMax global (requires: MINIMAX_API_KEY)\n # \"minimax-cn\" - MiniMax China (requires: MINIMAX_CN_API_KEY)\n # \"huggingface\" - Hugging Face Inference (requires: HF_TOKEN)\n # \"nvidia\" - NVIDIA NIM / build.nvidia.com (requires: NVIDIA_API_KEY)\n # \"xiaomi\" - Xiaomi MiMo (requires: XIAOMI_API_KEY)\n # \"arcee\" - Arcee AI Trinity models (requires: ARCEEAI_API_KEY)\n # \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY — https://ollama.com/settings)\n # \"deepinfra\" - DeepInfra (requires: DEEPINFRA_API_KEY)\n # \"kilocode\" - KiloCode gateway (requires: KILOCODE_API_KEY)\n # \"azure-foundry\" - Microsoft Foundry / Azure OpenAI (API key or Entra ID)\n # \"lmstudio\" - LM Studio local server (optional: LM_API_KEY, defaults to http://127.0.0.1:1234/v1)\n #\n # Local servers (LM Studio, Ollama, vLLM, llama.cpp):\n # \"custom\" - Any other OpenAI-compatible endpoint. Set base_url below.\n # Aliases: \"ollama\", \"vllm\", \"llamacpp\" all map to \"custom\".\n # LM Studio is first-class and uses provider: \"lmstudio\".\n # It works with both no-auth and auth-enabled server modes.\n #\n # Can also be overridden for a single invocation with the --provider flag.\n provider: \"auto\"\n \n # API configuration (falls back to OPENROUTER_API_KEY env var)\n # api_key: \"your-key-here\" # Uncomment to set here instead of .env\n base_url: \"https://openrouter.ai/api/v1\"\n\n # Azure Foundry keyless auth example:\n # provider: \"azure-foundry\"\n # base_url: \"https://<resource>.openai.azure.com/openai/v1\"\n # auth_mode: \"entra_id\" # DefaultAzureCredential: az login, managed identity, workload identity, etc.\n # default: \"gpt-4o\" # Deployment/model name\n # entra:\n # scope: \"https://ai.azure.com/.default\" # Optional; this is the default.\n\n # ── Token limits — two settings, easy to confuse ──────────────────────────\n #\n # context_length: TOTAL context window (input + output tokens combined).\n # Controls when Hermes compresses history and validates requests.\n # Leave unset — Hermes auto-detects the correct value from the provider.\n # Set manually only when auto-detection is wrong (e.g. a local server with\n # a custom num_ctx, or a proxy that doesn't expose /v1/models).\n #\n # context_length: 131072\n #\n # max_tokens: OUTPUT cap — maximum tokens the model may generate per response.\n # Unrelated to how long your conversation history can be.\n # The OpenAI-standard name \"max_tokens\" is a misnomer; Anthropic's native\n # API has since renamed it \"max_output_tokens\" for clarity.\n # Leave unset to use the model's native output ceiling (recommended).\n # Set only if you want to deliberately limit individual response length.\n #\n# max_tokens: 8192\n\n # ── Custom request headers (optional) ─────────────────────────────────────\n #\n # default_headers: extra HTTP headers sent on every request to an\n # OpenAI-compatible endpoint. User values take precedence over the\n # provider/SDK defaults, so this is the supported way to override the\n # OpenAI Python SDK's identifying headers (User-Agent: OpenAI/Python ...,\n # X-Stainless-*) when a custom provider sits behind a gateway/WAF that\n # rejects them — e.g. an upstream that returns \"502 Upstream access\n # forbidden\" for the SDK default User-Agent but accepts a plain one.\n # Applies on the OpenAI wire only (not native Anthropic / Bedrock).\n #\n # default_headers:\n # User-Agent: \"curl/8.7.1\"\n #\n # extra_headers: accepted as an alias of default_headers (merged, with\n # extra_headers winning when both are set) — matches the per-provider\n # extra_headers key below.\n #\n # Per-provider variant: named providers / custom_providers entries accept an\n # extra_headers dict scoped to that endpoint only — for reverse proxies,\n # gateways, or custom auth (e.g. Cloudflare Access service tokens).\n # Merged onto SDK/provider defaults with the entry's values winning.\n # Header values are treated as secrets and are never logged.\n #\n # providers:\n # my-proxy:\n # base_url: \"https://llm.internal.example.com/v1\"\n # key_env: \"MY_PROXY_API_KEY\"\n # extra_headers:\n # CF-Access-Client-Id: \"xxxx.access\"\n # CF-Access-Client-Secret: \"${CF_ACCESS_SECRET}\"\n # X-Client-Name: \"hermes-agent\"\n\n# Named provider overrides (optional)\n# Use this for per-provider request timeouts, non-stream stale timeouts,\n# and per-model exceptions.\n# Applies to the primary turn client on every api_mode (OpenAI-wire, native\n# Anthropic, and Anthropic-compatible providers), the fallback chain, and\n# client rebuilds during credential rotation. For OpenAI-wire chat\n# completions (streaming and non-streaming) the configured value is also\n# used as the per-request ``timeout=`` kwarg so it wins over the legacy\n# HERMES_API_TIMEOUT env var (which still applies when no config is set).\n# ``stale_timeout_seconds`` controls the non-streaming stale-call detector and\n# wins over the legacy HERMES_API_CALL_STALE_TIMEOUT env var. Leaving these\n# unset keeps the legacy defaults (HERMES_API_TIMEOUT=1800s,\n# HERMES_API_CALL_STALE_TIMEOUT=90s, native Anthropic 900s). The\n# implicit non-stream stale detector is auto-disabled for local endpoints\n# and can scale upward for very large contexts.\n#\n# Not currently wired for AWS Bedrock (bedrock_converse + AnthropicBedrock\n# SDK paths) — those use boto3 with its own timeout configuration.\n#\n# providers:\n# ollama-local:\n# request_timeout_seconds: 300 # Longer timeout for local cold-starts\n# stale_timeout_seconds: 900 # Explicitly re-enable stale detection on local endpoints\n# anthropic:\n# request_timeout_seconds: 30 # Fast-fail cloud requests\n# models:\n# claude-opus-4.6:\n# timeout_seconds: 600 # Longer timeout for extended-thinking Opus calls\n# openai-codex:\n# models:\n# gpt-5.4:\n# stale_timeout_seconds: 1800 # Longer non-stream stale timeout for slow large-context turns\n\n# =============================================================================\n# OpenRouter Provider Routing (only applies when using OpenRouter)\n# =============================================================================\n# Control how requests are routed across providers on OpenRouter.\n# See: https://openrouter.ai/docs/guides/routing/provider-selection\n#\n# provider_routing:\n# # Sort strategy: \"price\" (default), \"throughput\", or \"latency\"\n# # Append :nitro to model name for a shortcut to throughput sorting.\n# sort: \"throughput\"\n#\n# # Only allow these providers (provider slugs from OpenRouter)\n# # only: [\"anthropic\", \"google\"]\n#\n# # Skip these providers entirely\n# # ignore: [\"deepinfra\", \"fireworks\"]\n#\n# # Try providers in this order (overrides default load balancing)\n# # order: [\"anthropic\", \"google\", \"together\"]\n#\n# # Require providers to support all parameters in your request\n# # require_parameters: true\n#\n# # Data policy: \"allow\" (default) or \"deny\" to exclude providers that may store data\n# # data_collection: \"deny\"\n\n# =============================================================================\n# OpenRouter Response Caching (only applies when using OpenRouter)\n# =============================================================================\n# Cache identical API responses at the OpenRouter edge for free instant replays.\n# When enabled, identical requests (same model, messages, parameters) return\n# cached responses with zero billing. Separate from Anthropic prompt caching.\n# See: https://openrouter.ai/docs/guides/features/response-caching\n#\n# openrouter:\n# response_cache: true # Enable response caching (default: true)\n# response_cache_ttl: 300 # Cache TTL in seconds, 1-86400 (default: 300)\n\n# =============================================================================\n# Git Worktree Isolation\n# =============================================================================\n# When enabled, each CLI session creates an isolated git worktree so multiple\n# agents can work on the same repo concurrently without file collisions.\n# Equivalent to always passing --worktree / -w on the command line.\n#\n# worktree: true # Always create a worktree when in a git repo\n# worktree: false # Default — only create when -w flag is passed\n#\n# By default a new worktree branches from the freshly-fetched remote tip\n# (the current branch's upstream, else the remote's default branch) so it\n# starts current with the project instead of from the local clone's\n# (possibly stale) HEAD. Set worktree_sync: false to branch from local HEAD\n# instead — useful when offline or when you deliberately want the clone's\n# exact current state as the base.\n#\n# worktree_sync: true # Default — branch from the fetched remote tip\n# worktree_sync: false # Branch from local HEAD (offline / pinned base)\n\n# =============================================================================\n# Terminal Tool Configuration\n# =============================================================================\n# Choose ONE of the following terminal configurations by uncommenting it.\n# The terminal tool executes commands in the specified environment.\n\n# -----------------------------------------------------------------------------\n# OPTION 1: Local execution (default)\n# Commands run directly on your machine in the current directory\n# -----------------------------------------------------------------------------\n# Working directory behavior:\n# - CLI (`hermes` command): Uses \".\" (current directory where you run hermes)\n# - Gateway/messaging/cron: Uses terminal.cwd here; legacy .env cwd values are deprecated\nterminal:\n backend: \"local\"\n cwd: \".\" # For local backend: \".\" = current directory. Ignored for remote backends unless a backend documents otherwise.\n timeout: 180\n # HOME policy for tool subprocesses:\n # auto - default: host uses your real HOME; containers use HERMES_HOME/home\n # real - force your real OS-user HOME\n # profile - force HERMES_HOME/home for strict per-profile CLI config isolation\n home_mode: \"auto\"\n docker_mount_cwd_to_workspace: false # SECURITY: off by default. Opt in to mount the launch cwd into Docker /workspace.\n lifetime_seconds: 300\n # sudo_password: \"hunter2\" # Optional: pipe a sudo password via sudo -S. SECURITY WARNING: plaintext.\n # sudo_password: \"\" # Explicit empty password: try empty and never open the interactive sudo prompt.\n\n# -----------------------------------------------------------------------------\n# OPTION 2: SSH remote execution\n# Commands run on a remote server - agent code stays local (sandboxed)\n# Great for: keeping agent isolated from its own code, using powerful remote hardware\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"ssh\"\n# cwd: \"/home/myuser/project\" # Path on the REMOTE server\n# timeout: 180\n# lifetime_seconds: 300\n# ssh_host: \"my-server.example.com\"\n# ssh_user: \"myuser\"\n# ssh_port: 22\n# ssh_key: \"~/.ssh/id_rsa\" # Optional - uses ssh-agent if not specified\n\n# -----------------------------------------------------------------------------\n# OPTION 3: Docker container\n# Commands run in an isolated Docker container\n# Great for: reproducible environments, testing, isolation\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"docker\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /)\n# timeout: 180\n# lifetime_seconds: 300\n# docker_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# docker_mount_cwd_to_workspace: true # Explicit opt-in: mount your launch cwd into /workspace\n# # Optional: run the container as your host user's uid:gid so files written\n# # into bind-mounted dirs are owned by you, not root. Drops SETUID/SETGID\n# # caps too since no gosu privilege drop is needed. Leave off if your\n# # chosen docker_image expects to start as root.\n# docker_run_as_host_user: true\n# # Optional: explicitly forward selected env vars into Docker.\n# # These values come from your current shell first, then ~/.hermes/.env.\n# # Warning: anything forwarded here is visible to commands run in the container.\n# docker_forward_env:\n# - \"GITHUB_TOKEN\"\n# - \"NPM_TOKEN\"\n# # Optional: extra flags passed verbatim to docker run (appended after security defaults).\n# # Useful for adding capabilities (e.g. apt installs needing SETUID) or custom options.\n# # Example: add a Linux capability not included by default\n# # docker_extra_args:\n# # - \"--cap-add\"\n# # - \"SETUID\"\n\n# -----------------------------------------------------------------------------\n# OPTION 4: Singularity/Apptainer container\n# Commands run in a Singularity container (common in HPC environments)\n# Great for: HPC clusters, shared compute environments\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"singularity\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# singularity_image: \"docker://nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 5: Modal cloud execution\n# Commands run on Modal's cloud infrastructure\n# Great for: GPU access, scalable compute, serverless execution\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"modal\"\n# cwd: \"/workspace\" # Path INSIDE the sandbox (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# modal_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 6: Daytona cloud execution\n# Commands run in Daytona cloud sandboxes\n# Great for: Cloud dev environments, persistent workspaces, team collaboration\n# Requires: pip install daytona, DAYTONA_API_KEY env var\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"daytona\"\n# cwd: \"~\"\n# timeout: 180\n# lifetime_seconds: 300\n# daytona_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# container_disk: 10240 # Daytona max is 10GB per sandbox\n\n#\n# --- Container resource limits (docker, singularity, modal, daytona -- ignored for local/ssh) ---\n# These settings apply to all container backends. They control the resources\n# allocated to the sandbox and whether its filesystem persists across sessions.\n container_cpu: 1 # CPU cores\n container_memory: 5120 # Memory in MB (5120 = 5GB)\n container_disk: 51200 # Disk in MB (51200 = 50GB)\n container_persistent: true # Persist filesystem across sessions (false = ephemeral)\n\n# -----------------------------------------------------------------------------\n# SUDO SUPPORT (works with ALL backends above)\n# -----------------------------------------------------------------------------\n# Add sudo_password to any terminal config above to enable sudo commands.\n# The password is piped via `sudo -S`. Works with local, ssh, docker, etc.\n#\n# SECURITY WARNING: Password stored in plaintext!\n#\n# INTERACTIVE PROMPT: If sudo_password is unset and the CLI is running,\n# you'll be prompted to enter your password when sudo is needed:\n# - 45-second timeout (auto-skips if no input)\n# - Press Enter to skip (command fails gracefully)\n# - Password is hidden while typing\n# - Password is cached for the session\n#\n# EMPTY PASSWORDS: Setting sudo_password to an explicit empty string is different\n# from leaving it unset. Hermes will try an empty password via `sudo -S` and\n# will not open the interactive prompt. This is useful for passwordless sudo,\n# Touch ID sudo setups, and environments where prompting is just noise.\n#\n# ALTERNATIVES:\n# - SSH backend: Configure passwordless sudo on the remote server\n# - Containers: Run as root inside the container (no sudo needed)\n# - Local: Configure /etc/sudoers for specific commands\n#\n# Example (add to your terminal section):\n# sudo_password: \"your-password-here\"\n\n# =============================================================================\n# Security Scanning (tirith)\n# =============================================================================\n# Optional pre-exec command security scanning via tirith.\n# Detects homograph URLs, pipe-to-shell, terminal injection, env manipulation.\n# Install: brew install sheeki03/tap/tirith\n# Docs: https://github.com/sheeki03/tirith\n#\n# security:\n# tirith_enabled: true # Enable/disable tirith scanning\n# tirith_path: \"tirith\" # Path to tirith binary (supports ~ expansion)\n# tirith_timeout: 5 # Scan timeout in seconds\n# tirith_fail_open: true # Allow commands if tirith unavailable\n\n# =============================================================================\n# Browser Tool Configuration\n# =============================================================================\nbrowser:\n # Inactivity timeout in seconds - browser sessions are automatically closed\n # after this period of no activity between agent loops (default: 120 = 2 minutes)\n inactivity_timeout: 120\n\n# =============================================================================\n# Tool Loop Guardrails\n# =============================================================================\n# Soft warnings are enabled by default. They append guidance to repeated failed\n# or non-progressing tool results but still let the tool execute. Hard stops are\n# opt-in circuit breakers for autonomous/cron sessions where stopping a loop is\n# preferable to spending the full iteration budget.\ntool_loop_guardrails:\n warnings_enabled: true\n hard_stop_enabled: false\n warn_after:\n exact_failure: 2\n same_tool_failure: 3\n idempotent_no_progress: 2\n hard_stop_after:\n exact_failure: 5\n same_tool_failure: 8\n idempotent_no_progress: 5\n\n# =============================================================================\n# Context Compression (Auto-shrinks long conversations)\n# =============================================================================\n# When conversation approaches model's context limit, middle turns are\n# automatically summarized to free up space while preserving important context.\n#\n# HOW IT WORKS:\n# 1. Tracks actual token usage from API responses (not estimates)\n# 2. When prompt_tokens >= threshold% of model's context_length, triggers compression\n# 3. Protects first 3 turns (system prompt, initial request, first response)\n# 4. Protects last N turns (default 20 messages = ~10 full turns of recent context)\n# 5. Summarizes middle turns using a fast/cheap model\n# 6. Inserts summary as a user message, continues conversation seamlessly\n#\n# Post-compression tail budget is target_ratio × threshold × context_length:\n# 200K context, threshold 0.50, ratio 0.20 → 20K tokens of recent tail preserved\n# 1M context, threshold 0.50, ratio 0.20 → 100K tokens of recent tail preserved\n#\ncompression:\n # Enable automatic context compression (default: true)\n # Set to false if you prefer to manage context manually or want errors on overflow\n enabled: true\n\n # Opt-in compression progress notices on chat platforms (default: false).\n # By design, routine automatic compression is SILENT on human-facing chat\n # gateways (Telegram, Discord, Slack, ...) — it happens in the background\n # with server-side logging only. Set true to also deliver the routine\n # progress statuses (compacting started, preflight/pre-API compression,\n # idle compaction, retry progress, and the compaction-complete notice) to\n # chat platforms. Unrelated operational noise (auxiliary model failures,\n # provider retry chatter) stays suppressed either way, and compression\n # FAILURE notices + manual /compress feedback are always visible\n # regardless of this setting. (#52995)\n progress_notices: false\n \n # Trigger compression at this % of model's context limit (default: 0.50 = 50%)\n # Lower values = more aggressive compression, higher values = compress later\n # Models with context windows below 512K are floored at 0.75 (raise-only) so\n # compaction doesn't fire with half the window still free; set above 0.75 to override.\n threshold: 0.50\n\n # Per-model threshold overrides: keys are substring-matched against the model\n # name (longest match wins). Useful when some models need different compaction\n # points — e.g. a 1M-context model can compress later (0.30) while a 128K\n # model needs to compress earlier (0.60). The small-context floor (75% for\n # <512K models) still applies on top of per-model overrides.\n # model_thresholds:\n # \"glm-5.2\": 0.40\n # \"claude-sonnet\": 0.35\n # \"gpt-5\": 0.30\n\n # Optional absolute token cap for the compression trigger (default: null = disabled).\n # When set, compression fires at the LOWER of the ratio-based threshold and this\n # absolute token count — first-fires-wins. It never fires later than this count\n # regardless of which model is active (useful when switching between models with\n # very different context windows). Clamped to the model's context length at\n # apply-time, so a cap above the window is a no-op (ratio-based threshold wins).\n # Survives model switches and fallback activations.\n # threshold_tokens: 200000\n\n # Existing Codex gpt-5.5 behavior: raise Hermes' compaction trigger to 85%\n # for the ChatGPT Codex OAuth route. Set false to opt back down to threshold.\n codex_gpt55_autoraise: true\n\n # Fraction of the threshold to preserve as recent tail (default: 0.20 = 20%)\n # e.g. 20% of 50% threshold = 10% of total context kept as recent messages.\n # Summary output is separately capped at 12K tokens (Gemini output limit).\n # Range: 0.10 - 0.80\n target_ratio: 0.20\n\n # Number of most-recent messages to always preserve (default: 20 ≈ 10 full turns)\n # Higher values keep more recent conversation intact at the cost of more aggressive\n # compression of older turns.\n protect_last_n: 20\n\n # Minimum number of REAL (actionable) user messages guaranteed to survive in\n # the uncompressed tail (default: 1 = the existing single last-user anchor,\n # behavior-preserving). Raise to e.g. 3 to keep the last 3 real user turns\n # verbatim even when bulky tool outputs fill the tail token budget — blank\n # platform echoes, compaction handoffs, and synthetic continuation rows never\n # count toward N. The tail can exceed the token budget when this pulls the\n # cut back; the guarantee wins over the budget by design.\n min_tail_user_messages: 1\n\n # Compression retry rounds before a turn gives up with \"max compression\n # attempts reached\" (default: 3, same as the previous hardcoded value).\n # Raise (e.g. 6) for tool-schema-heavy sessions where 3 rounds cannot bring\n # the request estimate under the threshold. Validated >= 1, hard cap 10.\n max_attempts: 3\n\n # Codex app-server (codex CLI runtime) thread-compaction mode. The codex\n # agent owns the real thread context on this runtime, so Hermes' summarizer\n # cannot shrink it — compaction goes through the app server instead.\n # native = let Codex decide when to compact its own thread (default)\n # hermes = let Hermes threshold trigger Codex thread/compact/start\n # off = Hermes will not auto-trigger compaction; Codex may still compact natively\n codex_app_server_auto: native\n\n # Number of non-system messages to protect at the head of the transcript, in\n # ADDITION to the system prompt (which is always implicitly protected).\n # Head messages are NEVER summarized — they survive every compression\n # indefinitely. This gives stable early context for short/medium sessions,\n # but in long-running sessions that rely on rolling compaction the pinned\n # opening turns may not match how you want the session framed over time.\n # Set to 0 to preserve ONLY the system prompt (plus the rolling summary\n # and recent tail) — the cleanest configuration for long-running sessions.\n # Default 3 preserves the system prompt plus the first three non-system\n # head messages, matching the pre-feature behaviour.\n protect_first_n: 3\n\n # Idle compaction (default: 0 = disabled). When > 0, a session that resumes\n # after at least this many seconds of inactivity compacts its accumulated\n # history up front, before the first reply, so a long-lived thread you come\n # back to later doesn't re-read its full stale context on every turn.\n # Time-based, so it complements (does not replace) the size-based `threshold`\n # above. It is skipped when the context is already small (at or below the\n # post-compression target = threshold × target_ratio), so it never wastes a\n # summarization on a short idle thread. Example: 1800 = compact after 30 min idle.\n idle_compact_after_seconds: 0\n\n # Proactive tool-result prune (default: 0 = disabled). Opt-in token trigger\n # for a deterministic, no-LLM prune of OLD tool-result payloads, run\n # independently of `threshold` above. On large-window models (512K/1M) the\n # ratio threshold rarely fires, so bulky tool outputs (terminal dumps, file\n # reads, web extracts) ride along in history and get re-billed every turn.\n # When re-sent history exceeds this many tokens, the prune dedupes identical\n # results, summarizes older oversized ones, and truncates large tool-call\n # arguments — protecting the most recent `protect_last_n` messages and never\n # calling the model. Try 48000 to enable. Built-in compressor engine only;\n # other context engines inherit a safe no-op.\n # NOTE: a committed prune rewrites already-sent history, which invalidates\n # the provider's prompt-cache prefix — the min_reclaim gate below keeps\n # those cache breaks episodic (like a compression boundary) instead of\n # per-turn.\n proactive_prune_tokens: 0\n\n # The prune's summarize pass only touches tool results larger than this many\n # characters (clamped to >= 200 so a generated summary can't be\n # re-summarized). Default 8000.\n proactive_prune_min_result_chars: 8000\n\n # A proactive prune only COMMITS when it reclaims at least this many tokens\n # (measured on the pruned output). This is the prompt-cache hysteresis gate:\n # one meaningful, amortized cache break per batch of stale tool output\n # instead of a tiny break on every tool iteration. 0 = commit any non-zero\n # prune. Default 4096.\n proactive_prune_min_reclaim_tokens: 4096\n\n # To pin a specific model/provider for compression summaries, use the\n # auxiliary section below (auxiliary.compression.provider / model).\n\n# =============================================================================\n# Anthropic prompt caching TTL\n# =============================================================================\n# When prompt caching is active (Claude via OpenRouter or native Anthropic),\n# Anthropic supports two TTL tiers for cached prefixes: \"5m\" (default) and\n# \"1h\". Other values are ignored and \"5m\" is used.\n#\nprompt_caching:\n cache_ttl: \"5m\" # use \"1h\" for long sessions with pauses between turns\n\n# =============================================================================\n# Auxiliary Models (Advanced — Experimental)\n# =============================================================================\n# Hermes uses lightweight \"auxiliary\" models for side tasks: image analysis,\n# browser screenshot analysis, web page summarization, TTS audio-tag insertion,\n# and context compression.\n#\n# By default these use Gemini Flash via OpenRouter or Nous Portal and are\n# auto-detected from your credentials. You do NOT need to change anything\n# here for normal usage.\n#\n# WARNING: Overriding these with providers other than OpenRouter or Nous Portal\n# is EXPERIMENTAL and may not work. Not all models/providers support vision,\n# produce usable summaries, or accept the same API format. Change at your own\n# risk — if things break, reset to \"auto\" / empty values.\n#\n# Each task has its own provider + model pair so you can mix providers.\n# For example: OpenRouter for vision (needs multimodal), but your main\n# local endpoint for compression (just needs text).\n#\n# Provider options:\n# \"auto\" - Best available: OpenRouter → Nous Portal → main endpoint (default)\n# \"openrouter\" - Force OpenRouter (requires OPENROUTER_API_KEY)\n# \"nous\" - Force Nous Portal (requires: hermes auth add nous)\n# \"gemini\" - Force Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n# \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY)\n# \"codex\" - Force Codex OAuth (requires: hermes model → Codex).\n# Uses gpt-5.3-codex which supports vision.\n# \"main\" - Use your custom endpoint (OPENAI_BASE_URL + OPENAI_API_KEY).\n# Works with OpenAI API, local models, or any OpenAI-compatible\n# endpoint. Also falls back to Codex OAuth and API-key providers.\n#\n# Model: leave empty to use the provider's default. When empty, OpenRouter\n# uses \"google/gemini-3-flash-preview\" and Nous uses \"gemini-3-flash\".\n# Other providers pick a sensible default automatically.\n#\n# auxiliary:\n# # Image analysis: vision_analyze tool + browser screenshots\n# vision:\n# provider: \"auto\"\n# model: \"\" # e.g. \"google/gemini-2.5-flash\", \"openai/gpt-4o\"\n# timeout: 30 # LLM API call timeout (seconds)\n# download_timeout: 30 # Image HTTP download timeout (seconds)\n# # Increase for slow connections or self-hosted image servers\n# reasoning_effort: \"\" # Per-task thinking level: none, minimal, low, medium,\n# # high, xhigh, max, ultra. Empty = provider default.\n# # Works on every auxiliary task block (vision,\n# # web_extract, compression, title_generation, curator,\n# # background_review, moa_reference, ...). Example: run\n# # compression at \"low\" and vision at \"none\" to cut\n# # side-task latency/cost on reasoning models.\n#\n# # Web page scraping / summarization + browser page text extraction\n# web_extract:\n# provider: \"auto\"\n# model: \"\"\n# reasoning_effort: \"low\"\n#\n# # Gemini 3.1 TTS hidden audio-tag insertion\n# tts_audio_tags:\n# provider: \"auto\" # empty model = your main chat model\n# model: \"\"\n# timeout: 30\n#\n# # Automatic session title generation after the first exchange\n# title_generation:\n# enabled: true # set false to disable auto-title generation\n# provider: \"auto\"\n# model: \"\"\n# timeout: 30\n# language: \"\" # empty = match the user's language; or e.g. \"English\"\n#\n# # Session search — summarizes matching past sessions\n# session_search:\n# provider: \"auto\"\n# model: \"\"\n# timeout: 30\n# max_concurrency: 3 # Limit parallel summaries to reduce request-burst 429s\n# extra_body: {} # Provider-specific OpenAI-compatible request fields\n# # Example for providers that support request-body\n# # reasoning controls:\n# # extra_body:\n# # enable_thinking: false\n# # Some vLLM/Qwen deployments expect this nested:\n# # extra_body:\n# # chat_template_kwargs:\n# # enable_thinking: false\n\n# =============================================================================\n# Persistent Memory\n# =============================================================================\n# Bounded curated memory injected into the system prompt every session.\n# Two stores: MEMORY.md (agent's notes) and USER.md (user profile).\n# Character limits keep the memory small and focused. The agent manages\n# pruning -- when at the limit, it must consolidate or replace entries.\n# Disabled by default in batch_runner.\n#\nmemory:\n # Agent's personal notes: environment facts, conventions, things learned\n memory_enabled: true\n \n # User profile: preferences, communication style, expectations\n user_profile_enabled: true\n \n # Character limits (~2.75 chars per token, model-independent)\n memory_char_limit: 2200 # ~800 tokens\n user_char_limit: 1375 # ~500 tokens\n\n # Periodic memory nudge: remind the agent to consider saving memories\n # every N user turns. Set to 0 to disable. Only active when memory is enabled.\n nudge_interval: 10 # Nudge every 10 user turns (0 = disabled)\n\n # Memory flush: give the agent one turn to save memories before context is\n # lost (compression, /new, /reset, exit). Set to 0 to disable.\n # For exit/reset, only fires if the session had at least this many user turns.\n flush_min_turns: 6 # Min user turns to trigger flush on exit/reset (0 = disabled)\n\n# =============================================================================\n# Session Reset Policy (Messaging Platforms)\n# =============================================================================\n# Controls when messaging sessions (Telegram, Discord, WhatsApp, Slack) are\n# automatically cleared. Default is \"none\": sessions never auto-reset —\n# conversation context lives until you /reset or /new manually, or context\n# compression kicks in. Opt in to automatic resets if you prefer sessions to\n# clear on a schedule (long-lived context increases API cost per message,\n# though prompt caching and compression keep this manageable).\n#\n# When an automatic reset triggers, the agent first saves important\n# information to its persistent memory — but the conversation context is\n# wiped. The agent starts fresh but retains learned facts via its memory\n# system.\n#\n# Modes:\n# \"none\" - Never auto-reset (default); context lives until /reset or compression\n# \"idle\" - Reset after N minutes of inactivity\n# \"daily\" - Reset at a fixed hour each day\n# \"both\" - Reset on EITHER inactivity timeout or daily boundary\n#\nsession_reset:\n mode: none # \"none\", \"idle\", \"daily\", or \"both\"\n idle_minutes: 1440 # Inactivity timeout in minutes (used by \"idle\"/\"both\")\n at_hour: 4 # Daily reset hour, 0-23 local time (used by \"daily\"/\"both\")\n\n# Maximum number of simultaneously active chat sessions across CLI, TUI,\n# dashboard chat, and messaging gateway. Set to null, 0, or omit to allow\n# unlimited concurrent sessions. When the limit is reached, new sessions get a\n# clean error while existing active sessions keep their normal behavior. This\n# top-level key takes precedence over gateway.max_concurrent_sessions. The cap\n# is a best-effort single-host/profile runtime guard; Hermes fails open if the\n# local runtime lease registry cannot be read or locked.\nmax_concurrent_sessions: null\n\n# When true, group/channel chats use one session per participant when the platform\n# provides a user ID. This is the secure default and prevents users in the same\n# room from sharing context, interrupts, and token costs. Set false only if you\n# explicitly want one shared \"room brain\" per group/channel.\ngroup_sessions_per_user: true\n\n# ─────────────────────────────────────────────────────────────────────────────\n# API Server — per-client model routing\n# ─────────────────────────────────────────────────────────────────────────────\n# Route different API clients to different models/providers on a single\n# Hermes deployment. Clients choose a backend by sending a specific string\n# as the OpenAI ``model`` field. Unmapped model values fall back to the\n# global model configured in the ``model:`` section above, and an explicit\n# session /model override always wins over a route.\n#\n# Configure via the ``platforms.api_server.extra.model_routes`` gateway\n# config block:\n#\n# platforms:\n# api_server:\n# enabled: true\n# extra:\n# key: \"your-api-server-secret\"\n# model_routes:\n# # Xiaozhi clients send model=\"minimax-m2\" → routed to MiniMax via OpenRouter\n# minimax-m2:\n# model: \"minimax/minimax-m1\"\n# provider: \"openrouter\" # optional — overrides global provider\n# # api_key: \"sk-...\" # optional — per-route UPSTREAM provider\n# # key (NOT caller auth; never logged)\n# # base_url: \"https://...\" # optional — per-route base URL\n# # GPT clients keep their own alias\n# gpt-5:\n# model: \"openai/gpt-5\"\n# provider: \"openrouter\"\n#\n# Configured aliases are automatically listed by GET /v1/models so clients\n# can discover them without manual coordination. Caller authentication is\n# unchanged: every request still authenticates with the global API server\n# key (``extra.key`` / API_SERVER_KEY).\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Gateway Streaming\n# ─────────────────────────────────────────────────────────────────────────────\n# Stream tokens to messaging platforms in real-time. The bot sends a message\n# on first token, then progressively edits it as more tokens arrive.\n# Disabled by default — enable to try the streaming UX on Telegram/Discord/Slack.\n# For Telegram, partial edits are sent as plain text and only the final edit uses MarkdownV2.\nstreaming:\n enabled: false\n # transport: edit # \"edit\" = progressive editMessageText\n # edit_interval: 0.3 # seconds between message edits\n # buffer_threshold: 40 # chars before forcing an edit flush\n # cursor: \" ▉\" # cursor shown during streaming\n\n# =============================================================================\n# Skills Configuration\n# =============================================================================\n# Skills are reusable procedures the agent can load and follow. The agent can\n# also create new skills after completing complex tasks.\n#\nskills:\n # Nudge the agent to create skills after complex tasks.\n # Every N tool-calling iterations, remind the model to consider saving a skill.\n # Set to 0 to disable.\n creation_nudge_interval: 15\n\n # External skill directories — share skills across tools/agents without\n # copying them into ~/.hermes/skills/. Each path is expanded (~ and ${VAR})\n # and resolved to an absolute path. External dirs are read-only: skill\n # creation always writes to ~/.hermes/skills/. Local skills take precedence\n # when names collide.\n # external_dirs:\n # - ~/.agents/skills\n # - /home/shared/team-skills\n\n# =============================================================================\n# Agent Behavior\n# =============================================================================\nagent:\n # Maximum tool-calling iterations per conversation (default: 500)\n # Higher = more room for complex tasks, but costs more tokens\n # Recommended: 20-30 for focused tasks, 50-100 for open exploration\n max_turns: 500\n\n # Inactivity timeout for gateway agent runs (seconds, 0 = unlimited).\n # The agent can run indefinitely when actively calling tools or receiving\n # API responses. Only fires after the agent has been idle for this duration.\n # gateway_timeout: 1800\n\n # Staged warning: send a warning before escalating to full timeout.\n # Fires once per run when inactivity reaches this threshold (seconds).\n # Set to 0 to disable the warning.\n # gateway_timeout_warning: 900\n\n # Graceful drain timeout for gateway stop/restart (seconds).\n # Default 0 = no drain: a restart interrupts in-flight agents immediately,\n # cleans up, and exits. Set a positive value only if you want a grace\n # window on /restart, and keep it well under systemd's TimeoutStopSec.\n # restart_drain_timeout: 0\n\n # Upper bound (seconds) a submitted prompt waits for the deferred agent\n # build (MCP discovery, model metadata, skills scan) before failing with a\n # visible error. The wait is patient — the message is delivered as soon as\n # the build completes, and a progress notice is shown past 30s — so this cap\n # only fires on a genuinely hung build. Raise it for deployments with many\n # slow or unreachable MCP servers. Default 600.\n # build_wait_timeout: 600\n\n # Max app-level retry attempts for API errors (connection drops, provider\n # timeouts, 5xx, etc.) before the agent surfaces the failure. Lower this\n # to 1 if you use fallback providers and want fast failover on flaky\n # primaries (default 3). The OpenAI SDK does its own low-level retries\n # underneath this wrapper — this is the Hermes-level loop.\n # api_max_retries: 3\n\n # After the agent edits code without fresh passing verification, nudge it to\n # verify before finishing. The default \"auto\" enables it on interactive\n # coding surfaces (CLI, TUI, desktop) and programmatic callers, and disables\n # it on conversational messaging surfaces (Telegram, Discord, etc.) where the\n # verification summary would reach a human as chat noise. Set true or false to\n # force it on or off; the HERMES_VERIFY_ON_STOP env var (1/0) takes precedence.\n # verify_on_stop: auto\n\n # Standing operator instructions for the coding posture (when Hermes is in a\n # code workspace). Appended to the coding brief as an extra system block, so\n # you can pin project-wide workflow rules without editing the shipped brief.\n # Accepts a string or a list of strings. Takes effect next session.\n # coding_instructions:\n # - \"For UI work, don't run tsc/lint until I approve the look.\"\n # - \"Clean the diff before you commit and push.\"\n\n # When verify-on-stop finds edited code without fresh verification evidence,\n # append guidance for creative UI work (avoid broad tsc/lint/test before visual\n # approval) and clean-diff expectations. Set false to keep that nudge terse.\n # verify_guidance: true\n\n # A `pre_verify` hook (plugin or shell, see Event Hooks docs) can keep the\n # agent going one more turn to verify/clean before finishing. This caps how\n # many times one turn may be nudged to continue, so a hook can't trap the loop.\n # Default 3.\n # max_verify_nudges: 3\n\n # Enable verbose logging\n verbose: false\n \n # Reasoning effort level (OpenRouter and Nous Portal)\n # Controls how much \"thinking\" the model does before responding.\n # Options: \"xhigh\" (max), \"high\", \"medium\", \"low\", \"minimal\", \"none\" (disable)\n reasoning_effort: \"medium\"\n \n # Per-model reasoning effort overrides (optional dict)\n # Key: any sensible model spelling works (exact, dots↔dashes interchangeable,\n # provider prefix optional). First match wins.\n # Value: reasoning effort level (same options as reasoning_effort)\n # Override the global reasoning_effort for that specific model.\n # NOTE: no `hermes config set` support for this key -- edit YAML directly.\n # reasoning_overrides:\n # \"openrouter/anthropic/claude-opus-4.5\": \"xhigh\"\n # \"openai/gpt-5\": \"low\"\n # \"claude-opus-4.6\": \"high\" # bare model name also works\n # \"deepseek/deepseek-v4-pro\": \"xhigh\" # dots and dashes are interchangeable\n reasoning_overrides: {}\n \n # Predefined personalities (use with /personality command)\n personalities:\n helpful: \"You are a helpful, friendly AI assistant.\"\n concise: \"You are a concise assistant. Keep responses brief and to the point.\"\n technical: \"You are a technical expert. Provide detailed, accurate technical information.\"\n creative: \"You are a creative assistant. Think outside the box and offer innovative solutions.\"\n teacher: \"You are a patient teacher. Explain concepts clearly with examples.\"\n kawaii: \"You are a kawaii assistant! Use cute expressions like (◕‿◕), ★, ♪, and ~! Add sparkles and be super enthusiastic about everything! Every response should feel warm and adorable desu~! ヽ(>∀<☆)ノ\"\n catgirl: \"You are Neko-chan, an anime catgirl AI assistant, nya~! Add 'nya' and cat-like expressions to your speech. Use kaomoji like (=^・ω・^=) and ฅ^•ﻌ•^ฅ. Be playful and curious like a cat, nya~!\"\n pirate: \"Arrr! Ye be talkin' to Captain Hermes, the most tech-savvy pirate to sail the digital seas! Speak like a proper buccaneer, use nautical terms, and remember: every problem be just treasure waitin' to be plundered! Yo ho ho!\"\n shakespeare: \"Hark! Thou speakest with an assistant most versed in the bardic arts. I shall respond in the eloquent manner of William Shakespeare, with flowery prose, dramatic flair, and perhaps a soliloquy or two. What light through yonder terminal breaks?\"\n surfer: \"Duuude! You're chatting with the chillest AI on the web, bro! Everything's gonna be totally rad. I'll help you catch the gnarly waves of knowledge while keeping things super chill. Cowabunga! 🤙\"\n noir: \"The rain hammered against the terminal like regrets on a guilty conscience. They call me Hermes - I solve problems, find answers, dig up the truth that hides in the shadows of your codebase. In this city of silicon and secrets, everyone's got something to hide. What's your story, pal?\"\n uwu: \"hewwo! i'm your fwiendwy assistant uwu~ i wiww twy my best to hewp you! *nuzzles your code* OwO what's this? wet me take a wook! i pwomise to be vewy hewpful >w<\"\n philosopher: \"Greetings, seeker of wisdom. I am an assistant who contemplates the deeper meaning behind every query. Let us examine not just the 'how' but the 'why' of your questions. Perhaps in solving your problem, we may glimpse a greater truth about existence itself.\"\n hype: \"YOOO LET'S GOOOO!!! 🔥🔥🔥 I am SO PUMPED to help you today! Every question is AMAZING and we're gonna CRUSH IT together! This is gonna be LEGENDARY! ARE YOU READY?! LET'S DO THIS! 💪😤🚀\"\n\n# =============================================================================\n# Toolsets\n# =============================================================================\n# Control which tools the agent has access to.\n# Use `hermes tools` to interactively enable/disable tools per platform.\n\n# =============================================================================\n# Platform Toolsets (per-platform tool configuration)\n# =============================================================================\n# Override which toolsets are available on each platform.\n# If a platform isn't listed here, its built-in default is used.\n#\n# You can use EITHER:\n# - A preset like \"hermes-cli\" or \"hermes-telegram\" (curated tool set)\n# - A list of individual toolsets to compose your own (see list below)\n#\n# Supported platform keys: cli, telegram, discord, whatsapp, slack, qqbot, teams, google_chat\n#\n# Examples:\n#\n# # Use presets (same as defaults):\n# platform_toolsets:\n# cli: [hermes-cli]\n# telegram: [hermes-telegram]\n#\n# # Custom: give Telegram only web + terminal + file + planning:\n# platform_toolsets:\n# telegram: [web, terminal, file, todo]\n#\n# # Custom: CLI without browser or image gen:\n# platform_toolsets:\n# cli: [web, terminal, file, skills, todo, tts, cronjob]\n#\n# # Restrictive: Discord gets read-only tools only:\n# platform_toolsets:\n# discord: [web, vision, skills, todo]\n#\n# If not set, defaults are:\n# cli: hermes-cli (everything + cronjob management)\n# telegram: hermes-telegram (terminal, file, web, vision, image, tts, browser, skills, todo, cronjob, messaging)\n# discord: hermes-discord (same as telegram)\n# whatsapp: hermes-whatsapp (same as telegram)\n# slack: hermes-slack (same as telegram)\n# signal: hermes-signal (same as telegram)\n# homeassistant: hermes-homeassistant (same as telegram)\n# qqbot: hermes-qqbot (same as telegram)\n# teams: hermes-teams (same as telegram)\n# google_chat: hermes-google_chat (same as telegram)\n#\nplatform_toolsets:\n cli: [hermes-cli]\n telegram: [hermes-telegram]\n discord: [hermes-discord]\n whatsapp: [hermes-whatsapp]\n slack: [hermes-slack]\n signal: [hermes-signal]\n homeassistant: [hermes-homeassistant]\n qqbot: [hermes-qqbot]\n yuanbao: [hermes-yuanbao]\n teams: [hermes-teams]\n google_chat: [hermes-google_chat]\n\n# =============================================================================\n# Gateway Platform Settings\n# =============================================================================\n# Optional per-platform messaging settings.\n# Platform-specific knobs live under `extra`.\n#\n# platforms:\n# telegram:\n# reply_to_mode: \"first\" # off | first | all\n# # guest_mode lets explicit @mentions from non-allowlisted groups through.\n# # Default false; ordinary messages, replies, and regex wake words stay blocked.\n# guest_mode: false\n# # allowed_chats: [\"-1001234567890\"]\n# extra:\n# disable_link_previews: false # Set true to suppress Telegram URL previews in bot messages\n# 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\n# rich_drafts: false # Experimental rich draft previews during Telegram DM streaming; default false because Telegram Desktop/macOS can visually overlay draft frames\n# command_menu:\n# # Telegram allows up to 100 BotCommands; Hermes defaults to 60 so\n# # all built-in commands plus common skill commands stay visible\n# # while remaining under Telegram's payload-size limit. Clamped 1..100.\n# max_commands: 60\n# # prepend = user priority first, then Hermes defaults\n# # append = Hermes defaults first, then user priority\n# # replace = only the list below defines priority\n# priority_mode: prepend\n# priority:\n# - my_plugin_command\n# webhook:\n# extra:\n# # Route scripts default to a 30 second timeout. Scripts must live under\n# # the active profile's scripts directory and receive webhook JSON on stdin.\n# script_timeout_seconds: 30\n#\n# Discord-specific settings (config.yaml top-level, not under platforms:):\n#\n# discord:\n# require_mention: true # Require @mention in server channels (default: true)\n# auto_thread: true # Auto-create thread on @mention (default: true)\n# free_response_channels: \"\" # Channel IDs where no mention is needed\n# reactions: true # Show processing reactions (default: true)\n# history_backfill: true # Recover missed channel messages on mention (default: true)\n# history_backfill_limit: 50 # Max messages to scan backwards (default: 50)\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Available toolsets (use these names in platform_toolsets or the toolsets list)\n#\n# Run `hermes chat --list-toolsets` to see all toolsets and their tools.\n# Run `hermes chat --list-tools` to see every individual tool with descriptions.\n# ─────────────────────────────────────────────────────────────────────────────\n#\n# INDIVIDUAL TOOLSETS (compose your own):\n# web - web_search, web_extract\n# search - web_search only (no scraping)\n# terminal - terminal, process\n# file - read_file, write_file, patch, search\n# browser - browser_navigate, browser_snapshot, browser_click, browser_type,\n# browser_scroll, browser_back, browser_press,\n# browser_get_images, browser_vision (requires BROWSERBASE_API_KEY)\n# vision - vision_analyze (requires OPENROUTER_API_KEY)\n# image_gen - image_generate (requires FAL_KEY)\n# skills - skills_list, skill_view\n# skills_hub - skill_hub (search/install/manage from online registries — user-driven only)\n# todo - todo (in-memory task planning, no deps)\n# tts - text_to_speech (Edge TTS free, or ELEVENLABS/OPENAI/MINIMAX/MISTRAL key)\n# cronjob - cronjob (create/list/update/pause/resume/run/remove scheduled tasks)\n#\n# PRESETS (curated bundles):\n# hermes-cli - All of the above except send_message\n# hermes-telegram - terminal, file, web, vision, image_gen, tts, browser,\n# skills, todo, cronjob, send_message\n# hermes-discord - Same as hermes-telegram\n# hermes-whatsapp - Same as hermes-telegram\n# hermes-slack - Same as hermes-telegram\n#\n# COMPOSITE:\n# debugging - terminal + web + file\n# safe - web + vision (no terminal access)\n# all - Everything available\n#\n# web - Web search and content extraction (web_search, web_extract)\n# search - Web search only, no scraping (web_search)\n# terminal - Command execution and process management (terminal, process)\n# file - File operations: read, write, patch, search\n# browser - Full browser automation (navigate, click, type, screenshot, etc.)\n# vision - Image analysis (vision_analyze)\n# image_gen - Image generation with FLUX (image_generate)\n# skills - Load skill documents (skills_list, skill_view)\n# todo - Task planning and tracking for multi-step work\n# memory - Persistent memory across sessions (personal notes + user profile)\n# session_search - Search and recall past conversations (FTS5 + Gemini Flash summarization)\n# tts - Text-to-speech (Edge TTS free, ElevenLabs, OpenAI, MiniMax, Mistral)\n# cronjob - Schedule and manage automated tasks (CLI-only)\n#\n# Composite toolsets:\n# debugging - terminal + web + file (for troubleshooting)\n# safe - web + vision (no terminal access)\n\n# NOTE: The top-level \"toolsets\" key is deprecated and ignored.\n# Tool configuration is managed per-platform via platform_toolsets above.\n# Use `hermes tools` to configure interactively, or edit platform_toolsets directly.\n#\n# CLI override: hermes chat --toolsets terminal,web,file\n\n# =============================================================================\n# MCP (Model Context Protocol) Servers\n# =============================================================================\n# Connect to external MCP servers to add tools from the MCP ecosystem.\n# Each server's tools are automatically discovered and registered.\n# See website/docs/user-guide/features/mcp.md for full documentation.\n#\n# Stdio servers (spawn a subprocess):\n# command: the executable to run\n# args: command-line arguments\n# env: environment variables (only these + safe defaults passed to subprocess)\n#\n# HTTP servers (connect to a URL):\n# url: the MCP server endpoint\n# headers: HTTP headers (e.g., for authentication)\n#\n# Optional per-server settings:\n# timeout: tool call timeout in seconds (default: 120)\n# connect_timeout: initial connection timeout (default: 60)\n# keepalive_interval: liveness ping cadence in seconds (default: 180).\n# Lower it below the server's session TTL for servers that expire idle\n# sessions quickly (e.g. Unreal Engine editor MCP, ~15s), otherwise idle\n# tool calls hit an expired session and pay a slow reconnect. Floored at 5s.\n#\n# mcp_servers:\n# time:\n# command: uvx\n# args: [\"mcp-server-time\"]\n# filesystem:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-filesystem\", \"/home/user\"]\n# notion:\n# url: https://mcp.notion.com/mcp\n# github:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-github\"]\n# env:\n# GITHUB_PERSONAL_ACCESS_TOKEN: \"ghp_...\"\n#\n# Sampling (server-initiated LLM requests) — enabled by default.\n# Per-server config under the 'sampling' key:\n# analysis:\n# command: npx\n# args: [\"-y\", \"analysis-server\"]\n# sampling:\n# enabled: true # default: true\n# model: \"gemini-3-flash\" # override model (optional)\n# max_tokens_cap: 4096 # max tokens per request\n# timeout: 30 # LLM call timeout (seconds)\n# max_rpm: 10 # max requests per minute\n# allowed_models: [] # model whitelist (empty = all)\n# max_tool_rounds: 5 # tool loop limit (0 = disable)\n# log_level: \"info\" # audit verbosity\n\n# =============================================================================\n# Text-to-Speech\n# =============================================================================\n# TTS defaults to Edge TTS unless changed in ~/.hermes/config.yaml.\n# Gemini TTS supports persona/director prompt files, and Gemini 3.1 Flash TTS\n# can use a hidden auxiliary rewrite pass to insert expressive square-bracket\n# audio tags into the TTS script without showing tags in chat.\n#\n# tts:\n# provider: \"gemini\"\n# gemini:\n# model: \"gemini-3.1-flash-tts-preview\"\n# voice: \"Kore\"\n# audio_tags: false\n# persona_prompt_file: \"\" # e.g. ~/.hermes/tts/radio-host.md\n\n# =============================================================================\n# Voice Transcription (Speech-to-Text)\n# =============================================================================\n# Automatically transcribe voice messages on messaging platforms.\n# Providers: local (free, faster-whisper) | groq (free tier) | openai (Whisper API) | mistral (Voxtral Transcribe)\n# Set the corresponding API key in .env: GROQ_API_KEY, OPENAI_API_KEY, or MISTRAL_API_KEY.\nstt:\n enabled: true\n # provider: \"local\" # auto-detected if omitted\n local:\n model: \"base\" # tiny | base | small | medium | large-v3 | turbo\n # language: \"\" # auto-detect; set to \"en\", \"es\", \"fr\", etc. to force\n openai:\n model: \"whisper-1\" # whisper-1 | gpt-4o-mini-transcribe | gpt-4o-transcribe\n # mistral:\n # model: \"voxtral-mini-latest\" # voxtral-mini-latest | voxtral-mini-2602\n # deepinfra:\n # # Model id is discovered live from the DeepInfra catalog filtered\n # # by the `stt` surface tag — leave `model` blank to take the first\n # # live result. Pin only when you need a specific Whisper variant.\n # model: \"\"\n\n# Text-to-speech. Only the deepinfra block is documented here — the\n# remaining providers (edge, openai, xai, minimax, mistral, gemini,\n# elevenlabs, neutts, kittentts, piper) inherit sensible defaults from\n# DEFAULT_CONFIG in hermes_cli/config.py.\n# tts:\n# provider: \"deepinfra\"\n# deepinfra:\n# # Model id is discovered live from the DeepInfra catalog filtered\n# # by the `tts` surface tag — leave `model` blank to take the first\n# # live result.\n# model: \"\"\n# voice: \"default\"\n\n# Image generation. Each provider plugin reads its own ``image_gen.<name>``\n# block; deepinfra discovers models live from\n# api.deepinfra.com/v1/openai/models filtered by the ``image-gen`` tag —\n# no model id is hardcoded, so retired models disappear automatically.\n# image_gen:\n# provider: \"deepinfra\"\n# deepinfra:\n# # Leave `model` blank for the first live `image-gen`-tagged result.\n# model: \"\"\n\n# =============================================================================\n# Response Pacing (Messaging Platforms)\n# =============================================================================\n# Add human-like delays between message chunks.\n# human_delay:\n# mode: \"off\" # \"off\" | \"natural\" | \"custom\"\n# min_ms: 800 # Min delay (custom mode only)\n# max_ms: 2500 # Max delay (custom mode only)\n\n# =============================================================================\n# Session Logging\n# =============================================================================\n# Session trajectories are automatically saved to logs/ directory.\n# Each session creates: logs/session_YYYYMMDD_HHMMSS_UUID.json\n#\n# The session ID is displayed in the welcome banner for easy reference.\n# Logs contain full conversation history in trajectory format:\n# - System prompt, user messages, assistant responses\n# - Tool calls with inputs/outputs\n# - Timestamps for debugging\n#\n# No configuration needed - logging is always enabled.\n# To disable, you would need to modify the source code.\n\n# =============================================================================\n# Code Execution Sandbox (Programmatic Tool Calling)\n# =============================================================================\n# The execute_code tool runs Python scripts that call Hermes tools via RPC.\n# Intermediate tool results stay out of the LLM's context window.\ncode_execution:\n timeout: 300 # Max seconds per script before kill (default: 300 = 5 min)\n max_tool_calls: 50 # Max RPC tool calls per execution (default: 50)\n\n# =============================================================================\n# Subagent Delegation\n# =============================================================================\n# The delegate_task tool spawns child agents with isolated context.\n# Supports single tasks and batch mode (default 3 parallel, configurable).\ndelegation:\n max_iterations: 50 # Max tool-calling turns per child (default: 50)\n # max_concurrent_children: 3 # Max parallel child agents per batch (default: 3, floor: 1, no ceiling).\n # WARNING: values above 10 multiply API cost linearly.\n # max_spawn_depth: 1 # Delegation tree depth cap (range: 1-3, default: 1 = flat).\n # Raise to 2 to allow workers to spawn their own subagents.\n # Requires role=\"orchestrator\" on intermediate agents.\n # orchestrator_enabled: true # Kill switch for role=\"orchestrator\" children (default: true).\n # subagent_auto_approve: false # When a subagent hits a dangerous-command approval prompt, auto-deny (default: false)\n # or auto-approve \"once\" (true) instead of blocking on stdin.\n # The parent TUI owns stdin, so blocking would deadlock; non-interactive resolution is required.\n # Both choices emit a logger.warning audit line. Flip to true only for cron/batch pipelines.\n # inherit_mcp_toolsets: true # When explicit child toolsets are narrowed, also keep the parent's MCP toolsets (default: true). Set false for strict intersection.\n # model: \"google/gemini-3-flash-preview\" # Override model for subagents (empty = inherit parent)\n # provider: \"openrouter\" # Override provider for subagents (empty = inherit parent)\n # # Resolves full credentials (base_url, api_key) automatically.\n # # Supported: openrouter, nous, zai, kimi-coding, minimax\n\n# =============================================================================\n# Honcho Integration (Cross-Session User Modeling)\n# =============================================================================\n# AI-native persistent memory via Honcho (https://honcho.dev/).\n# Builds a deeper understanding of the user across sessions and tools.\n# Runs alongside USER.md — additive, not a replacement.\n#\n# Requires: pip install honcho-ai\n# Config: ~/.honcho/config.json (shared with Claude Code, Cursor, etc.)\n# API key: HONCHO_API_KEY in ~/.hermes/.env or ~/.honcho/config.json\n#\n# Hermes-specific overrides (optional — most config comes from ~/.honcho/config.json):\n# honcho: {}\n\n# =============================================================================\n# Display\n# =============================================================================\ndisplay:\n # Use compact banner mode (hides the ASCII-art banner, shows a single line).\n # true: Compact single-line banner\n # false: Full ASCII banner with tool/skill summary (default)\n compact: false\n\n # Tool progress display level (CLI and gateway)\n # off: Silent — no tool activity shown, just the final response\n # new: Show a tool indicator only when the tool changes (skip repeats)\n # all: Show every tool call with a short preview (default)\n # verbose: Full args, results, and debug logs (same as /verbose)\n # log: Silent in chat; append every tool call to ~/.hermes/logs/tool_calls.log (gateway only)\n # Toggle at runtime with /verbose in the CLI\n tool_progress: all\n\n # Per-platform defaults can be quieter than the global setting. Telegram\n # tunes for mobile: tool_progress and busy_ack_detail default off (no\n # per-tool breadcrumb stream, no \"iteration 21/60\" debug detail in busy\n # acks or heartbeats), but interim_assistant_messages and\n # long_running_notifications STAY ON so the user has real signal between\n # turn start and final answer (mid-turn assistant commentary + a single\n # edit-in-place \"⏳ Working — N min\" heartbeat). Override under\n # display.platforms.telegram.\n\n # Auto-cleanup of temporary progress bubbles after the final response lands.\n # On platforms that support message deletion (currently Telegram), this\n # removes the tool-progress bubble, \"⏳ Still working...\" notices, and\n # context-pressure status messages once the final reply has been delivered —\n # keeping long-running turns visible live, then tidy afterward. Failed runs\n # leave the bubbles in place as breadcrumbs. Off by default.\n # Per-platform override: display.platforms.telegram.cleanup_progress\n # true: Delete tracked progress/status bubbles on successful turn\n # false: Leave everything in place (default)\n # Example:\n # display:\n # platforms:\n # telegram:\n # cleanup_progress: true\n cleanup_progress: false\n\n # Natural mid-turn assistant updates.\n # On gateway platforms, when true, completed assistant status messages are\n # sent as separate chat messages. On the Desktop app, when true, mid-turn\n # assistant narration streamed between tool calls is kept in the transcript\n # instead of the bubble collapsing to only the final message on completion.\n # Independent of tool_progress and gateway streaming.\n # true: Keep/send mid-turn assistant updates (default)\n # false: Only keep/send the final response\n interim_assistant_messages: true\n\n # Gateway-only long-running status heartbeats.\n # When false, the platform does not receive periodic \"⏳ Working — N min\"\n # notifications even if agent.gateway_notify_interval is non-zero. The\n # heartbeat edits a single message in place (where the adapter supports\n # editing) instead of posting a new bubble each interval.\n # Default: true everywhere, including Telegram (silent agents are worse\n # than a single edit-in-place heartbeat).\n long_running_notifications: true\n\n # Include detailed iteration/tool/status context in busy acknowledgments\n # and long-running heartbeats. When true, busy acks show \"iteration 21/60,\n # terminal, 10 min\" and the heartbeat shows \"⏳ Working — 12 min,\n # iteration 21/60, terminal\". When false (Telegram default), both stay\n # terse: \"Interrupting current task\" and \"⏳ Working — 12 min, terminal\".\n busy_ack_detail: true\n\n # What Enter does when Hermes is already busy (CLI and gateway platforms).\n # interrupt: Interrupt the current run and redirect Hermes (default)\n # queue: Queue your message for the next turn\n # steer: Inject your message mid-run via /steer, arriving at the agent\n # after the next tool call — no interrupt, no role violation.\n # Falls back to 'queue' if the agent isn't running yet or if\n # images are attached (steer only carries text).\n # Ctrl+C (or /stop in gateway) always interrupts regardless of this setting.\n # Toggle at runtime with /busy <interrupt|queue|steer>.\n busy_input_mode: interrupt\n\n # Background process notifications (gateway/messaging only).\n # Controls how chatty the process watcher is when you use\n # terminal(background=true, notify_on_complete=true) from Telegram/Discord/etc.\n # off: No watcher messages at all\n # result: Only the final completion message\n # error: Only the final message when exit code != 0\n # all: Running output updates + final message (default)\n background_process_notifications: all\n\n\n # Play terminal bell when agent finishes a response.\n # Useful for long-running tasks — your terminal will ding when the agent is done.\n # Works over SSH. Most terminals can be configured to flash the taskbar or play a sound.\n # true: Ring the terminal bell on each response\n # false: Silent (default)\n bell_on_complete: false\n\n # Show model reasoning/thinking before each response.\n # When enabled, a dim box shows the model's thought process above the response.\n # Toggle at runtime with /reasoning show or /reasoning hide.\n # true: Show the reasoning box\n # false: Hide reasoning (default)\n show_reasoning: false\n\n # Stream tokens to the terminal as they arrive instead of waiting for the\n # full response. The response box opens on first token and text appears\n # line-by-line. Tool calls are still captured silently.\n # true: Stream tokens as they arrive (default)\n # false: Wait for the full response before rendering\n streaming: true\n\n # Show [HH:MM] timestamps on user input and assistant response labels.\n # timestamps: false\n\n # ───────────────────────────────────────────────────────────────────────────\n # Skin / Theme\n # ───────────────────────────────────────────────────────────────────────────\n # Customize CLI visual appearance — banner colors, spinner faces, tool prefix,\n # response box label, and branding text. Change at runtime with /skin <name>.\n #\n # Built-in skins:\n # default — Classic Hermes gold/kawaii\n # ares — Crimson/bronze war-god theme with spinner wings\n # mono — Clean grayscale monochrome\n # slate — Cool blue developer-focused\n # daylight — Bright light-mode theme\n # warm-lightmode — Warm paper-tone light-mode theme\n # poseidon — Sea-green/teal Olympian theme\n # sisyphus — Earthy stone-and-moss theme\n # charizard — Fiery orange dragon theme\n #\n # Custom skins: drop a YAML file in ~/.hermes/skins/<name>.yaml\n # Schema (all fields optional, missing values inherit from default):\n #\n # name: my-theme\n # description: Short description\n # colors:\n # banner_border: \"#HEX\" # Panel border\n # banner_title: \"#HEX\" # Panel title\n # banner_accent: \"#HEX\" # Section headers (Available Tools, etc.)\n # banner_dim: \"#HEX\" # Dim/muted text\n # banner_text: \"#HEX\" # Body text (tool names, skill names)\n # ui_accent: \"#HEX\" # UI accent color\n # response_border: \"#HEX\" # Response box border color\n # spinner:\n # waiting_faces: [\"(⚔)\", \"(⛨)\"] # Faces shown while waiting\n # thinking_faces: [\"(⚔)\", \"(⌁)\"] # Faces shown while thinking\n # thinking_verbs: [\"forging\", \"plotting\"] # Verbs for spinner messages\n # wings: # Optional left/right spinner decorations\n # - [\"⟪⚔\", \"⚔⟫\"]\n # - [\"⟪▲\", \"▲⟫\"]\n # branding:\n # agent_name: \"My Agent\" # Banner title and branding\n # welcome: \"Welcome message\" # Shown at CLI startup\n # response_label: \" ⚔ Agent \" # Response box header label\n # prompt_symbol: \"⚔\" # Prompt symbol (bare token; renderers add trailing space)\n # tool_prefix: \"╎\" # Tool output line prefix (default: ┊)\n #\n skin: default\n\n# =============================================================================\n# Model Aliases — short names for /model command\n# =============================================================================\n# Map short aliases to exact (model, provider, base_url) tuples.\n# Used by /model tab completion and resolve_alias().\n# Aliases are checked BEFORE the models.dev catalog, so they can route\n# to endpoints not in the catalog (e.g. Ollama Cloud, local servers).\n#\n# model_aliases:\n# opus:\n# model: claude-opus-4-6\n# provider: anthropic\n# qwen:\n# model: \"qwen3.5:397b\"\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n# glm:\n# model: glm-4.7\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n\n# =============================================================================\n# Privacy\n# =============================================================================\n# privacy:\n# # Redact PII from the LLM context prompt.\n# # When true, phone numbers are stripped and user/chat IDs are replaced\n# # with deterministic hashes before being sent to the model.\n# # Names and usernames are NOT affected (user-chosen, publicly visible).\n# # Routing/delivery still uses the original values internally.\n# redact_pii: false\n\n# =============================================================================\n# Shell-script hooks\n# =============================================================================\n# Register shell scripts as plugin-hook callbacks. Each entry is executed as\n# a subprocess (shell=False, shlex.split) with a JSON payload on stdin. On\n# stdout the script may return JSON that either blocks the tool call or\n# injects context into the next LLM call.\n#\n# Valid events (mirror hermes_cli.plugins.VALID_HOOKS):\n# pre_tool_call, post_tool_call, pre_llm_call, post_llm_call,\n# pre_api_request, post_api_request, on_session_start, on_session_end,\n# on_session_finalize, on_session_reset, subagent_stop\n#\n# First-use consent: each (event, command) pair prompts once on a TTY, then\n# is persisted to ~/.hermes/shell-hooks-allowlist.json. Non-interactive\n# runs (gateway, cron) need --accept-hooks, HERMES_ACCEPT_HOOKS=1, or the\n# hooks_auto_accept key below.\n#\n# See website/docs/user-guide/features/hooks.md for the full JSON wire\n# protocol and worked examples.\n#\n# hooks:\n# pre_tool_call:\n# - matcher: \"terminal\"\n# command: \"~/.hermes/agent-hooks/block-rm-rf.sh\"\n# timeout: 10\n# post_tool_call:\n# - matcher: \"write_file|patch\"\n# command: \"~/.hermes/agent-hooks/auto-format.sh\"\n# pre_llm_call:\n# - command: \"~/.hermes/agent-hooks/inject-cwd-context.sh\"\n# subagent_stop:\n# - command: \"~/.hermes/agent-hooks/log-orchestration.sh\"\n#\n# hooks_auto_accept: false\n\n\n# =============================================================================\n# Update Behavior\n# =============================================================================\nupdates:\n # Create a full HERMES_HOME zip before every `hermes update`.\n # Backups land in ~/.hermes/backups/ and can be restored with `hermes import`.\n # Off by default because large homes can add minutes to every update.\n pre_update_backup: false\n\n # Number of pre-update backup zips to retain.\n backup_keep: 5\n\n # What non-interactive updates do with local source edits in the Hermes repo.\n # Interactive terminal updates always prompt before restoring the autostash.\n #\n # stash - auto-stash before pull, then auto-restore after success (default)\n # discard - drop the update-created stash after success; use only on managed\n # installs where local source edits should not persist\n non_interactive_local_changes: \"stash\"\n\n\n# =============================================================================\n# Web Dashboard\n# =============================================================================\n# OAuth gate configuration for `hermes dashboard --host <non-loopback>`.\n# The bundled Nous Portal plugin reads these on startup; settings here are\n# the canonical surface. Each can be overridden by an environment variable:\n#\n# dashboard.oauth.client_id <- HERMES_DASHBOARD_OAUTH_CLIENT_ID\n# dashboard.oauth.portal_url <- HERMES_DASHBOARD_PORTAL_URL\n# dashboard.public_url <- HERMES_DASHBOARD_PUBLIC_URL\n#\n# Env wins when set to a non-empty value. This is what Fly.io's platform-\n# secret injection uses to push per-deploy client_ids without needing to\n# bake a config.yaml into the image. Empty env values are treated as unset\n# so a provisioned-but-not-populated secret can't shadow a valid entry here.\n#\n# Local dev / on-prem deploys should typically set these via config.yaml\n# (the ~/.hermes/.env file is reserved for API keys and secrets).\n#\n# dashboard:\n# oauth:\n# client_id: \"\" # agent:{instance_id}; Portal provisions this at deploy\n# portal_url: \"\" # blank → default https://portal.nousresearch.com\n#\n# # Force the absolute base URL the OAuth callback (and any other public\n# # URL the dashboard hands to external systems) is built from. Set this\n# # for deploys behind reverse proxies that don't reliably forward\n# # X-Forwarded-Host / X-Forwarded-Proto / X-Forwarded-Prefix (manual\n# # nginx setups, on-prem ingresses, custom-domain Fly deploys without\n# # full proxy header chains).\n# #\n# # When set, the value is the complete authority: scheme + host +\n# # optional path prefix (e.g. \"https://example.com/hermes\"). The OAuth\n# # callback URL becomes \"<public_url>/auth/callback\" — X-Forwarded-Prefix\n# # is IGNORED on this code path because the operator has explicitly\n# # declared the public URL and we no longer need to guess.\n# #\n# # Leave empty to use the existing proxy-header reconstruction (the\n# # default — works on Fly.io out of the box).\n# #\n# # public_url: \"https://example.com/hermes\"\n#\n# -----------------------------------------------------------------------------\n# Self-hosted OIDC dashboard auth (generic OpenID Connect — Authentik,\n# Keycloak, Zitadel, Authelia, Auth0, Okta, Google, …). Use this INSTEAD of the\n# nous block above when gating the dashboard with your own identity provider.\n# Each setting can be overridden by an environment variable:\n#\n# dashboard.oauth.self_hosted.issuer <- HERMES_DASHBOARD_OIDC_ISSUER\n# dashboard.oauth.self_hosted.client_id <- HERMES_DASHBOARD_OIDC_CLIENT_ID\n# dashboard.oauth.self_hosted.scopes <- HERMES_DASHBOARD_OIDC_SCOPES\n# dashboard.oauth.self_hosted.client_secret <- HERMES_DASHBOARD_OIDC_CLIENT_SECRET\n#\n# dashboard:\n# oauth:\n# provider: self-hosted\n# self_hosted:\n# issuer: \"https://auth.example.com/application/o/hermes/\" # required\n# client_id: \"hermes-dashboard\" # required\n# scopes: \"openid profile email\" # optional\n#\n# # OPTIONAL — set ONLY if your IDP registered the client as\n# # *confidential* (Authentik / Keycloak often default to this). When\n# # set, Hermes authenticates the client at the token endpoint\n# # (client_secret_basic or client_secret_post, auto-selected from the\n# # IDP's discovery doc) IN ADDITION to PKCE. Leave unset for a public\n# # (PKCE-only) client — the common case.\n# #\n# # This is a CREDENTIAL: prefer setting HERMES_DASHBOARD_OIDC_CLIENT_SECRET\n# # in ~/.hermes/.env over putting it here in config.yaml.\n# # client_secret: \"\"\n\n# =============================================================================\n# External secret sources\n# =============================================================================\n# Pull provider credentials from external secret managers at process startup\n# instead of storing them in ~/.hermes/.env. Only the manager's bootstrap\n# credential (e.g. BWS_ACCESS_TOKEN / OP_SERVICE_ACCOUNT_TOKEN) lives in .env\n# (or your shell / desktop session); everything else rotates centrally.\n# Failures never block startup — Hermes warns once and continues with\n# whatever .env already had.\n#\n# Multiple sources can be enabled at once:\n# - \"mapped\" sources (explicit VAR -> ref bindings, e.g. 1Password's env:\n# map) beat \"bulk\" sources (whole-project dumps like Bitwarden BSM)\n# - within a shape, the first source to claim a var wins; later claims\n# are skipped with a startup warning (never a silent clobber)\n# - a source's override_existing lets it beat .env/shell values, but\n# never another secret source's claim\n# Docs: https://hermes-agent.nousresearch.com/docs/user-guide/secrets/\n#\n# secrets:\n# # Optional explicit ordering of enabled sources.\n# # sources: [onepassword, bitwarden]\n#\n# # ---- Bitwarden Secrets Manager (bws CLI) --------------------------------\n# bitwarden:\n# enabled: false\n# access_token_env: BWS_ACCESS_TOKEN # bootstrap token, sourced from .env\n# project_id: \"\" # UUID of the BSM project to sync\n# server_url: \"\" # \"\" = US Cloud; EU/self-hosted URL otherwise\n# cache_ttl_seconds: 300 # 0 disables fresh caching\n# encrypted_cache: # optional encrypted stale fallback\n# enabled: false\n# max_stale_seconds: 0 # 0 disables stale fallback\n# override_existing: true # BSM values win over existing env\n# auto_install: true # lazy-download bws into ~/.hermes/bin\n#\n# # ---- 1Password (op CLI) -------------------------------------------------\n# onepassword:\n# enabled: false\n# # Map env-var names to op:// secret references. Each is resolved with a\n# # single `op read` at startup.\n# env:\n# OPENAI_API_KEY: \"op://Private/OpenAI/api key\"\n# ANTHROPIC_API_KEY: \"op://Private/Anthropic/credential\"\n# account: \"\" # op --account shorthand; \"\" = default\n# service_account_token_env: OP_SERVICE_ACCOUNT_TOKEN # headless auth; unset = desktop session\n# binary_path: \"\" # \"\" = resolve op via PATH; else absolute path\n# cache_ttl_seconds: 300 # 0 disables BOTH cache layers\n# override_existing: true # resolved values win over existing env\n#\n# # ---- Command helper (any CLI vault) --------------------------------------\n# # Run a user-configured helper that prints KEY=VALUE lines on stdout —\n# # works with any secret store that has a CLI: keepassxc-cli, secret-tool,\n# # pass, gpg, or a script that cats a tmpfs env file. Composes with the\n# # sources above (enable any combination). POSIX-only (needs /bin/sh).\n# # The helper must be fast and NON-interactive (hard timeout, 1 MiB cap);\n# # its stderr is discarded so diagnostics can't leak secret material.\n# command:\n# enabled: false\n# command: \"cat /run/user/1000/hermes-secrets.env\"\n# helper_timeout_seconds: 3\n# override_existing: false # .env/shell win by default\n"}, "9216198601189e8dc7d850890115531739d83e8c": {"t": 1785768000.3973722, "n": 84381, "h": "045372a0d3b72ca5", "c": "# Hermes Agent CLI Configuration\n# Copy this file to cli-config.yaml and customize as needed.\n# This file configures the CLI behavior. Environment variables in .env take precedence.\n\n# =============================================================================\n# Model Configuration\n# =============================================================================\nmodel:\n # Default model to use (can be overridden with --model flag)\n # Both \"default\" and \"model\" work as the key name here.\n default: \"anthropic/claude-opus-4.6\"\n \n # Inference provider selection:\n # \"auto\" - Auto-detect from credentials (default)\n # \"openrouter\" - OpenRouter (requires: OPENROUTER_API_KEY or OPENAI_API_KEY)\n # \"nous\" - Nous Portal OAuth (requires: hermes auth add nous)\n # \"nous-api\" - Nous Portal API key (requires: NOUS_API_KEY)\n # \"anthropic\" - Direct Anthropic API (requires: ANTHROPIC_API_KEY)\n # \"openai-codex\" - OpenAI Codex (requires: hermes auth)\n # \"copilot\" - GitHub Copilot / GitHub Models (requires: GITHUB_TOKEN)\n # \"gemini\" - Use Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n # \"zai\" - Use z.ai / ZhipuAI GLM models (requires: GLM_API_KEY)\n # \"kimi-coding\" - Kimi / Moonshot AI (requires: KIMI_API_KEY)\n # \"minimax\" - MiniMax global (requires: MINIMAX_API_KEY)\n # \"minimax-cn\" - MiniMax China (requires: MINIMAX_CN_API_KEY)\n # \"huggingface\" - Hugging Face Inference (requires: HF_TOKEN)\n # \"nvidia\" - NVIDIA NIM / build.nvidia.com (requires: NVIDIA_API_KEY)\n # \"xiaomi\" - Xiaomi MiMo (requires: XIAOMI_API_KEY)\n # \"arcee\" - Arcee AI Trinity models (requires: ARCEEAI_API_KEY)\n # \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY — https://ollama.com/settings)\n # \"deepinfra\" - DeepInfra (requires: DEEPINFRA_API_KEY)\n # \"kilocode\" - KiloCode gateway (requires: KILOCODE_API_KEY)\n # \"azure-foundry\" - Microsoft Foundry / Azure OpenAI (API key or Entra ID)\n # \"lmstudio\" - LM Studio local server (optional: LM_API_KEY, defaults to http://127.0.0.1:1234/v1)\n #\n # Local servers (LM Studio, Ollama, vLLM, llama.cpp):\n # \"custom\" - Any other OpenAI-compatible endpoint. Set base_url below.\n # Aliases: \"ollama\", \"vllm\", \"llamacpp\" all map to \"custom\".\n # LM Studio is first-class and uses provider: \"lmstudio\".\n # It works with both no-auth and auth-enabled server modes.\n #\n # Can also be overridden for a single invocation with the --provider flag.\n provider: \"auto\"\n \n # API configuration (falls back to OPENROUTER_API_KEY env var)\n # api_key: \"your-key-here\" # Uncomment to set here instead of .env\n base_url: \"https://openrouter.ai/api/v1\"\n\n # Azure Foundry keyless auth example:\n # provider: \"azure-foundry\"\n # base_url: \"https://<resource>.openai.azure.com/openai/v1\"\n # auth_mode: \"entra_id\" # DefaultAzureCredential: az login, managed identity, workload identity, etc.\n # default: \"gpt-4o\" # Deployment/model name\n # entra:\n # scope: \"https://ai.azure.com/.default\" # Optional; this is the default.\n\n # ── Token limits — two settings, easy to confuse ──────────────────────────\n #\n # context_length: TOTAL context window (input + output tokens combined).\n # Controls when Hermes compresses history and validates requests.\n # Leave unset — Hermes auto-detects the correct value from the provider.\n # Set manually only when auto-detection is wrong (e.g. a local server with\n # a custom num_ctx, or a proxy that doesn't expose /v1/models).\n #\n # context_length: 131072\n #\n # max_tokens: OUTPUT cap — maximum tokens the model may generate per response.\n # Unrelated to how long your conversation history can be.\n # The OpenAI-standard name \"max_tokens\" is a misnomer; Anthropic's native\n # API has since renamed it \"max_output_tokens\" for clarity.\n # Leave unset to use the model's native output ceiling (recommended).\n # Set only if you want to deliberately limit individual response length.\n #\n# max_tokens: 8192\n\n # ── Custom request headers (optional) ─────────────────────────────────────\n #\n # default_headers: extra HTTP headers sent on every request to an\n # OpenAI-compatible endpoint. User values take precedence over the\n # provider/SDK defaults, so this is the supported way to override the\n # OpenAI Python SDK's identifying headers (User-Agent: OpenAI/Python ...,\n # X-Stainless-*) when a custom provider sits behind a gateway/WAF that\n # rejects them — e.g. an upstream that returns \"502 Upstream access\n # forbidden\" for the SDK default User-Agent but accepts a plain one.\n # Applies on the OpenAI wire only (not native Anthropic / Bedrock).\n #\n # default_headers:\n # User-Agent: \"curl/8.7.1\"\n #\n # extra_headers: accepted as an alias of default_headers (merged, with\n # extra_headers winning when both are set) — matches the per-provider\n # extra_headers key below.\n #\n # Per-provider variant: named providers / custom_providers entries accept an\n # extra_headers dict scoped to that endpoint only — for reverse proxies,\n # gateways, or custom auth (e.g. Cloudflare Access service tokens).\n # Merged onto SDK/provider defaults with the entry's values winning.\n # Header values are treated as secrets and are never logged.\n #\n # providers:\n # my-proxy:\n # base_url: \"https://llm.internal.example.com/v1\"\n # key_env: \"MY_PROXY_API_KEY\"\n # extra_headers:\n # CF-Access-Client-Id: \"xxxx.access\"\n # CF-Access-Client-Secret: \"${CF_ACCESS_SECRET}\"\n # X-Client-Name: \"hermes-agent\"\n\n# Named provider overrides (optional)\n# Use this for per-provider request timeouts, non-stream stale timeouts,\n# and per-model exceptions.\n# Applies to the primary turn client on every api_mode (OpenAI-wire, native\n# Anthropic, and Anthropic-compatible providers), the fallback chain, and\n# client rebuilds during credential rotation. For OpenAI-wire chat\n# completions (streaming and non-streaming) the configured value is also\n# used as the per-request ``timeout=`` kwarg so it wins over the legacy\n# HERMES_API_TIMEOUT env var (which still applies when no config is set).\n# ``stale_timeout_seconds`` controls the non-streaming stale-call detector and\n# wins over the legacy HERMES_API_CALL_STALE_TIMEOUT env var. Leaving these\n# unset keeps the legacy defaults (HERMES_API_TIMEOUT=1800s,\n# HERMES_API_CALL_STALE_TIMEOUT=90s, native Anthropic 900s). The\n# implicit non-stream stale detector is auto-disabled for local endpoints\n# and can scale upward for very large contexts.\n#\n# Not currently wired for AWS Bedrock (bedrock_converse + AnthropicBedrock\n# SDK paths) — those use boto3 with its own timeout configuration.\n#\n# providers:\n# ollama-local:\n# request_timeout_seconds: 300 # Longer timeout for local cold-starts\n# stale_timeout_seconds: 900 # Explicitly re-enable stale detection on local endpoints\n# anthropic:\n# request_timeout_seconds: 30 # Fast-fail cloud requests\n# models:\n# claude-opus-4.6:\n# timeout_seconds: 600 # Longer timeout for extended-thinking Opus calls\n# openai-codex:\n# models:\n# gpt-5.4:\n# stale_timeout_seconds: 1800 # Longer non-stream stale timeout for slow large-context turns\n\n# =============================================================================\n# OpenRouter Provider Routing (only applies when using OpenRouter)\n# =============================================================================\n# Control how requests are routed across providers on OpenRouter.\n# See: https://openrouter.ai/docs/guides/routing/provider-selection\n#\n# provider_routing:\n# # Sort strategy: \"price\" (default), \"throughput\", or \"latency\"\n# # Append :nitro to model name for a shortcut to throughput sorting.\n# sort: \"throughput\"\n#\n# # Only allow these providers (provider slugs from OpenRouter)\n# # only: [\"anthropic\", \"google\"]\n#\n# # Skip these providers entirely\n# # ignore: [\"deepinfra\", \"fireworks\"]\n#\n# # Try providers in this order (overrides default load balancing)\n# # order: [\"anthropic\", \"google\", \"together\"]\n#\n# # Require providers to support all parameters in your request\n# # require_parameters: true\n#\n# # Data policy: \"allow\" (default) or \"deny\" to exclude providers that may store data\n# # data_collection: \"deny\"\n\n# =============================================================================\n# OpenRouter Response Caching (only applies when using OpenRouter)\n# =============================================================================\n# Cache identical API responses at the OpenRouter edge for free instant replays.\n# When enabled, identical requests (same model, messages, parameters) return\n# cached responses with zero billing. Separate from Anthropic prompt caching.\n# See: https://openrouter.ai/docs/guides/features/response-caching\n#\n# openrouter:\n# response_cache: true # Enable response caching (default: true)\n# response_cache_ttl: 300 # Cache TTL in seconds, 1-86400 (default: 300)\n\n# =============================================================================\n# Git Worktree Isolation\n# =============================================================================\n# When enabled, each CLI session creates an isolated git worktree so multiple\n# agents can work on the same repo concurrently without file collisions.\n# Equivalent to always passing --worktree / -w on the command line.\n#\n# worktree: true # Always create a worktree when in a git repo\n# worktree: false # Default — only create when -w flag is passed\n#\n# By default a new worktree branches from the freshly-fetched remote tip\n# (the current branch's upstream, else the remote's default branch) so it\n# starts current with the project instead of from the local clone's\n# (possibly stale) HEAD. Set worktree_sync: false to branch from local HEAD\n# instead — useful when offline or when you deliberately want the clone's\n# exact current state as the base.\n#\n# worktree_sync: true # Default — branch from the fetched remote tip\n# worktree_sync: false # Branch from local HEAD (offline / pinned base)\n\n# =============================================================================\n# Terminal Tool Configuration\n# =============================================================================\n# Choose ONE of the following terminal configurations by uncommenting it.\n# The terminal tool executes commands in the specified environment.\n\n# -----------------------------------------------------------------------------\n# OPTION 1: Local execution (default)\n# Commands run directly on your machine in the current directory\n# -----------------------------------------------------------------------------\n# Working directory behavior:\n# - CLI (`hermes` command): Uses \".\" (current directory where you run hermes)\n# - Gateway/messaging/cron: Uses terminal.cwd here; legacy .env cwd values are deprecated\nterminal:\n backend: \"local\"\n cwd: \".\" # For local backend: \".\" = current directory. Ignored for remote backends unless a backend documents otherwise.\n timeout: 180\n # HOME policy for tool subprocesses:\n # auto - default: host uses your real HOME; containers use HERMES_HOME/home\n # real - force your real OS-user HOME\n # profile - force HERMES_HOME/home for strict per-profile CLI config isolation\n home_mode: \"auto\"\n docker_mount_cwd_to_workspace: false # SECURITY: off by default. Opt in to mount the launch cwd into Docker /workspace.\n lifetime_seconds: 300\n # sudo_password: \"hunter2\" # Optional: pipe a sudo password via sudo -S. SECURITY WARNING: plaintext.\n # sudo_password: \"\" # Explicit empty password: try empty and never open the interactive sudo prompt.\n\n# -----------------------------------------------------------------------------\n# OPTION 2: SSH remote execution\n# Commands run on a remote server - agent code stays local (sandboxed)\n# Great for: keeping agent isolated from its own code, using powerful remote hardware\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"ssh\"\n# cwd: \"/home/myuser/project\" # Path on the REMOTE server\n# timeout: 180\n# lifetime_seconds: 300\n# ssh_host: \"my-server.example.com\"\n# ssh_user: \"myuser\"\n# ssh_port: 22\n# ssh_key: \"~/.ssh/id_rsa\" # Optional - uses ssh-agent if not specified\n\n# -----------------------------------------------------------------------------\n# OPTION 3: Docker container\n# Commands run in an isolated Docker container\n# Great for: reproducible environments, testing, isolation\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"docker\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /)\n# timeout: 180\n# lifetime_seconds: 300\n# docker_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# docker_mount_cwd_to_workspace: true # Explicit opt-in: mount your launch cwd into /workspace\n# # Optional: run the container as your host user's uid:gid so files written\n# # into bind-mounted dirs are owned by you, not root. Drops SETUID/SETGID\n# # caps too since no gosu privilege drop is needed. Leave off if your\n# # chosen docker_image expects to start as root.\n# docker_run_as_host_user: true\n# # Optional: explicitly forward selected env vars into Docker.\n# # These values come from your current shell first, then ~/.hermes/.env.\n# # Warning: anything forwarded here is visible to commands run in the container.\n# docker_forward_env:\n# - \"GITHUB_TOKEN\"\n# - \"NPM_TOKEN\"\n# # Optional: extra flags passed verbatim to docker run (appended after security defaults).\n# # Useful for adding capabilities (e.g. apt installs needing SETUID) or custom options.\n# # Example: add a Linux capability not included by default\n# # docker_extra_args:\n# # - \"--cap-add\"\n# # - \"SETUID\"\n\n# -----------------------------------------------------------------------------\n# OPTION 4: Singularity/Apptainer container\n# Commands run in a Singularity container (common in HPC environments)\n# Great for: HPC clusters, shared compute environments\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"singularity\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# singularity_image: \"docker://nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 5: Modal cloud execution\n# Commands run on Modal's cloud infrastructure\n# Great for: GPU access, scalable compute, serverless execution\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"modal\"\n# cwd: \"/workspace\" # Path INSIDE the sandbox (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# modal_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 6: Daytona cloud execution\n# Commands run in Daytona cloud sandboxes\n# Great for: Cloud dev environments, persistent workspaces, team collaboration\n# Requires: pip install daytona, DAYTONA_API_KEY env var\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"daytona\"\n# cwd: \"~\"\n# timeout: 180\n# lifetime_seconds: 300\n# daytona_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# container_disk: 10240 # Daytona max is 10GB per sandbox\n\n#\n# --- Container resource limits (docker, singularity, modal, daytona -- ignored for local/ssh) ---\n# These settings apply to all container backends. They control the resources\n# allocated to the sandbox and whether its filesystem persists across sessions.\n container_cpu: 1 # CPU cores\n container_memory: 5120 # Memory in MB (5120 = 5GB)\n container_disk: 51200 # Disk in MB (51200 = 50GB)\n container_persistent: true # Persist filesystem across sessions (false = ephemeral)\n\n# -----------------------------------------------------------------------------\n# SUDO SUPPORT (works with ALL backends above)\n# -----------------------------------------------------------------------------\n# Add sudo_password to any terminal config above to enable sudo commands.\n# The password is piped via `sudo -S`. Works with local, ssh, docker, etc.\n#\n# SECURITY WARNING: Password stored in plaintext!\n#\n# INTERACTIVE PROMPT: If sudo_password is unset and the CLI is running,\n# you'll be prompted to enter your password when sudo is needed:\n# - 45-second timeout (auto-skips if no input)\n# - Press Enter to skip (command fails gracefully)\n# - Password is hidden while typing\n# - Password is cached for the session\n#\n# EMPTY PASSWORDS: Setting sudo_password to an explicit empty string is different\n# from leaving it unset. Hermes will try an empty password via `sudo -S` and\n# will not open the interactive prompt. This is useful for passwordless sudo,\n# Touch ID sudo setups, and environments where prompting is just noise.\n#\n# ALTERNATIVES:\n# - SSH backend: Configure passwordless sudo on the remote server\n# - Containers: Run as root inside the container (no sudo needed)\n# - Local: Configure /etc/sudoers for specific commands\n#\n# Example (add to your terminal section):\n# sudo_password: \"your-password-here\"\n\n# =============================================================================\n# Security Scanning (tirith)\n# =============================================================================\n# Optional pre-exec command security scanning via tirith.\n# Detects homograph URLs, pipe-to-shell, terminal injection, env manipulation.\n# Install: brew install sheeki03/tap/tirith\n# Docs: https://github.com/sheeki03/tirith\n#\n# security:\n# tirith_enabled: true # Enable/disable tirith scanning\n# tirith_path: \"tirith\" # Path to tirith binary (supports ~ expansion)\n# tirith_timeout: 5 # Scan timeout in seconds\n# tirith_fail_open: true # Allow commands if tirith unavailable\n\n# =============================================================================\n# Browser Tool Configuration\n# =============================================================================\nbrowser:\n # Inactivity timeout in seconds - browser sessions are automatically closed\n # after this period of no activity between agent loops (default: 120 = 2 minutes)\n inactivity_timeout: 120\n\n# =============================================================================\n# Tool Loop Guardrails\n# =============================================================================\n# Soft warnings are enabled by default. They append guidance to repeated failed\n# or non-progressing tool results but still let the tool execute. Hard stops are\n# opt-in circuit breakers for autonomous/cron sessions where stopping a loop is\n# preferable to spending the full iteration budget.\ntool_loop_guardrails:\n warnings_enabled: true\n hard_stop_enabled: false\n warn_after:\n exact_failure: 2\n same_tool_failure: 3\n idempotent_no_progress: 2\n hard_stop_after:\n exact_failure: 5\n same_tool_failure: 8\n idempotent_no_progress: 5\n\n# =============================================================================\n# Context Compression (Auto-shrinks long conversations)\n# =============================================================================\n# When conversation approaches model's context limit, middle turns are\n# automatically summarized to free up space while preserving important context.\n#\n# HOW IT WORKS:\n# 1. Tracks actual token usage from API responses (not estimates)\n# 2. When prompt_tokens >= threshold% of model's context_length, triggers compression\n# 3. Protects first 3 turns (system prompt, initial request, first response)\n# 4. Protects last N turns (default 20 messages = ~10 full turns of recent context)\n# 5. Summarizes middle turns using a fast/cheap model\n# 6. Inserts summary as a user message, continues conversation seamlessly\n#\n# Post-compression tail budget is target_ratio × threshold × context_length:\n# 200K context, threshold 0.50, ratio 0.20 → 20K tokens of recent tail preserved\n# 1M context, threshold 0.50, ratio 0.20 → 100K tokens of recent tail preserved\n#\ncompression:\n # Enable automatic context compression (default: true)\n # Set to false if you prefer to manage context manually or want errors on overflow\n enabled: true\n\n # Opt-in compression progress notices on chat platforms (default: false).\n # By design, routine automatic compression is SILENT on human-facing chat\n # gateways (Telegram, Discord, Slack, ...) — it happens in the background\n # with server-side logging only. Set true to also deliver the routine\n # progress statuses (compacting started, preflight/pre-API compression,\n # idle compaction, retry progress, and the compaction-complete notice) to\n # chat platforms. Unrelated operational noise (auxiliary model failures,\n # provider retry chatter) stays suppressed either way, and compression\n # FAILURE notices + manual /compress feedback are always visible\n # regardless of this setting. (#52995)\n progress_notices: false\n \n # Trigger compression at this % of model's context limit (default: 0.50 = 50%)\n # Lower values = more aggressive compression, higher values = compress later\n # Models with context windows below 512K are floored at 0.75 (raise-only) so\n # compaction doesn't fire with half the window still free; set above 0.75 to override.\n threshold: 0.50\n\n # Per-model threshold overrides: keys are substring-matched against the model\n # name (longest match wins). Useful when some models need different compaction\n # points — e.g. a 1M-context model can compress later (0.30) while a 128K\n # model needs to compress earlier (0.60). The small-context floor (75% for\n # <512K models) still applies on top of per-model overrides.\n # model_thresholds:\n # \"glm-5.2\": 0.40\n # \"claude-sonnet\": 0.35\n # \"gpt-5\": 0.30\n\n # Optional absolute token cap for the compression trigger (default: null = disabled).\n # When set, compression fires at the LOWER of the ratio-based threshold and this\n # absolute token count — first-fires-wins. It never fires later than this count\n # regardless of which model is active (useful when switching between models with\n # very different context windows). Clamped to the model's context length at\n # apply-time, so a cap above the window is a no-op (ratio-based threshold wins).\n # Survives model switches and fallback activations.\n # threshold_tokens: 200000\n\n # Existing Codex gpt-5.5 behavior: raise Hermes' compaction trigger to 85%\n # for the ChatGPT Codex OAuth route. Set false to opt back down to threshold.\n codex_gpt55_autoraise: true\n\n # Fraction of the threshold to preserve as recent tail (default: 0.20 = 20%)\n # e.g. 20% of 50% threshold = 10% of total context kept as recent messages.\n # Summary output is separately capped at 12K tokens (Gemini output limit).\n # Range: 0.10 - 0.80\n target_ratio: 0.20\n\n # Number of most-recent messages to always preserve (default: 20 ≈ 10 full turns)\n # Higher values keep more recent conversation intact at the cost of more aggressive\n # compression of older turns.\n protect_last_n: 20\n\n # Minimum number of REAL (actionable) user messages guaranteed to survive in\n # the uncompressed tail (default: 1 = the existing single last-user anchor,\n # behavior-preserving). Raise to e.g. 3 to keep the last 3 real user turns\n # verbatim even when bulky tool outputs fill the tail token budget — blank\n # platform echoes, compaction handoffs, and synthetic continuation rows never\n # count toward N. The tail can exceed the token budget when this pulls the\n # cut back; the guarantee wins over the budget by design.\n min_tail_user_messages: 1\n\n # Compression retry rounds before a turn gives up with \"max compression\n # attempts reached\" (default: 3, same as the previous hardcoded value).\n # Raise (e.g. 6) for tool-schema-heavy sessions where 3 rounds cannot bring\n # the request estimate under the threshold. Validated >= 1, hard cap 10.\n max_attempts: 3\n\n # Codex app-server (codex CLI runtime) thread-compaction mode. The codex\n # agent owns the real thread context on this runtime, so Hermes' summarizer\n # cannot shrink it — compaction goes through the app server instead.\n # native = let Codex decide when to compact its own thread (default)\n # hermes = let Hermes threshold trigger Codex thread/compact/start\n # off = Hermes will not auto-trigger compaction; Codex may still compact natively\n codex_app_server_auto: native\n\n # Number of non-system messages to protect at the head of the transcript, in\n # ADDITION to the system prompt (which is always implicitly protected).\n # Head messages are NEVER summarized — they survive every compression\n # indefinitely. This gives stable early context for short/medium sessions,\n # but in long-running sessions that rely on rolling compaction the pinned\n # opening turns may not match how you want the session framed over time.\n # Set to 0 to preserve ONLY the system prompt (plus the rolling summary\n # and recent tail) — the cleanest configuration for long-running sessions.\n # Default 3 preserves the system prompt plus the first three non-system\n # head messages, matching the pre-feature behaviour.\n protect_first_n: 3\n\n # Idle compaction (default: 0 = disabled). When > 0, a session that resumes\n # after at least this many seconds of inactivity compacts its accumulated\n # history up front, before the first reply, so a long-lived thread you come\n # back to later doesn't re-read its full stale context on every turn.\n # Time-based, so it complements (does not replace) the size-based `threshold`\n # above. It is skipped when the context is already small (at or below the\n # post-compression target = threshold × target_ratio), so it never wastes a\n # summarization on a short idle thread. Example: 1800 = compact after 30 min idle.\n idle_compact_after_seconds: 0\n\n # Proactive tool-result prune (default: 0 = disabled). Opt-in token trigger\n # for a deterministic, no-LLM prune of OLD tool-result payloads, run\n # independently of `threshold` above. On large-window models (512K/1M) the\n # ratio threshold rarely fires, so bulky tool outputs (terminal dumps, file\n # reads, web extracts) ride along in history and get re-billed every turn.\n # When re-sent history exceeds this many tokens, the prune dedupes identical\n # results, summarizes older oversized ones, and truncates large tool-call\n # arguments — protecting the most recent `protect_last_n` messages and never\n # calling the model. Try 48000 to enable. Built-in compressor engine only;\n # other context engines inherit a safe no-op.\n # NOTE: a committed prune rewrites already-sent history, which invalidates\n # the provider's prompt-cache prefix — the min_reclaim gate below keeps\n # those cache breaks episodic (like a compression boundary) instead of\n # per-turn.\n proactive_prune_tokens: 0\n\n # The prune's summarize pass only touches tool results larger than this many\n # characters (clamped to >= 200 so a generated summary can't be\n # re-summarized). Default 8000.\n proactive_prune_min_result_chars: 8000\n\n # A proactive prune only COMMITS when it reclaims at least this many tokens\n # (measured on the pruned output). This is the prompt-cache hysteresis gate:\n # one meaningful, amortized cache break per batch of stale tool output\n # instead of a tiny break on every tool iteration. 0 = commit any non-zero\n # prune. Default 4096.\n proactive_prune_min_reclaim_tokens: 4096\n\n # To pin a specific model/provider for compression summaries, use the\n # auxiliary section below (auxiliary.compression.provider / model).\n\n# =============================================================================\n# Anthropic prompt caching TTL\n# =============================================================================\n# When prompt caching is active (Claude via OpenRouter or native Anthropic),\n# Anthropic supports two TTL tiers for cached prefixes: \"5m\" (default) and\n# \"1h\". Other values are ignored and \"5m\" is used.\n#\nprompt_caching:\n cache_ttl: \"5m\" # use \"1h\" for long sessions with pauses between turns\n\n# =============================================================================\n# Auxiliary Models (Advanced — Experimental)\n# =============================================================================\n# Hermes uses lightweight \"auxiliary\" models for side tasks: image analysis,\n# browser screenshot analysis, web page summarization, TTS audio-tag insertion,\n# and context compression.\n#\n# By default these use Gemini Flash via OpenRouter or Nous Portal and are\n# auto-detected from your credentials. You do NOT need to change anything\n# here for normal usage.\n#\n# WARNING: Overriding these with providers other than OpenRouter or Nous Portal\n# is EXPERIMENTAL and may not work. Not all models/providers support vision,\n# produce usable summaries, or accept the same API format. Change at your own\n# risk — if things break, reset to \"auto\" / empty values.\n#\n# Each task has its own provider + model pair so you can mix providers.\n# For example: OpenRouter for vision (needs multimodal), but your main\n# local endpoint for compression (just needs text).\n#\n# Provider options:\n# \"auto\" - Best available: OpenRouter → Nous Portal → main endpoint (default)\n# \"openrouter\" - Force OpenRouter (requires OPENROUTER_API_KEY)\n# \"nous\" - Force Nous Portal (requires: hermes auth add nous)\n# \"gemini\" - Force Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n# \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY)\n# \"codex\" - Force Codex OAuth (requires: hermes model → Codex).\n# Uses gpt-5.3-codex which supports vision.\n# \"main\" - Use your custom endpoint (OPENAI_BASE_URL + OPENAI_API_KEY).\n# Works with OpenAI API, local models, or any OpenAI-compatible\n# endpoint. Also falls back to Codex OAuth and API-key providers.\n#\n# Model: leave empty to use the provider's default. When empty, OpenRouter\n# uses \"google/gemini-3-flash-preview\" and Nous uses \"gemini-3-flash\".\n# Other providers pick a sensible default automatically.\n#\n# auxiliary:\n# # Image analysis: vision_analyze tool + browser screenshots\n# vision:\n# provider: \"auto\"\n# model: \"\" # e.g. \"google/gemini-2.5-flash\", \"openai/gpt-4o\"\n# timeout: 30 # LLM API call timeout (seconds)\n# download_timeout: 30 # Image HTTP download timeout (seconds)\n# # Increase for slow connections or self-hosted image servers\n# reasoning_effort: \"\" # Per-task thinking level: none, minimal, low, medium,\n# # high, xhigh, max, ultra. Empty = provider default.\n# # Works on every auxiliary task block (vision,\n# # web_extract, compression, title_generation, curator,\n# # background_review, moa_reference, ...). Example: run\n# # compression at \"low\" and vision at \"none\" to cut\n# # side-task latency/cost on reasoning models.\n#\n# # Web page scraping / summarization + browser page text extraction\n# web_extract:\n# provider: \"auto\"\n# model: \"\"\n# reasoning_effort: \"low\"\n#\n# # Gemini 3.1 TTS hidden audio-tag insertion\n# tts_audio_tags:\n# provider: \"auto\" # empty model = your main chat model\n# model: \"\"\n# timeout: 30\n#\n# # Automatic session title generation after the first exchange\n# title_generation:\n# enabled: true # set false to disable auto-title generation\n# provider: \"auto\"\n# model: \"\"\n# timeout: 30\n# language: \"\" # empty = match the user's language; or e.g. \"English\"\n#\n# # Session search — summarizes matching past sessions\n# session_search:\n# provider: \"auto\"\n# model: \"\"\n# timeout: 30\n# max_concurrency: 3 # Limit parallel summaries to reduce request-burst 429s\n# extra_body: {} # Provider-specific OpenAI-compatible request fields\n# # Example for providers that support request-body\n# # reasoning controls:\n# # extra_body:\n# # enable_thinking: false\n# # Some vLLM/Qwen deployments expect this nested:\n# # extra_body:\n# # chat_template_kwargs:\n# # enable_thinking: false\n\n# =============================================================================\n# Persistent Memory\n# =============================================================================\n# Bounded curated memory injected into the system prompt every session.\n# Two stores: MEMORY.md (agent's notes) and USER.md (user profile).\n# Character limits keep the memory small and focused. The agent manages\n# pruning -- when at the limit, it must consolidate or replace entries.\n# Disabled by default in batch_runner.\n#\nmemory:\n # Agent's personal notes: environment facts, conventions, things learned\n memory_enabled: true\n \n # User profile: preferences, communication style, expectations\n user_profile_enabled: true\n \n # Character limits (~2.75 chars per token, model-independent)\n memory_char_limit: 2200 # ~800 tokens\n user_char_limit: 1375 # ~500 tokens\n\n # Periodic memory nudge: remind the agent to consider saving memories\n # every N user turns. Set to 0 to disable. Only active when memory is enabled.\n nudge_interval: 10 # Nudge every 10 user turns (0 = disabled)\n\n # Memory flush: give the agent one turn to save memories before context is\n # lost (compression, /new, /reset, exit). Set to 0 to disable.\n # For exit/reset, only fires if the session had at least this many user turns.\n flush_min_turns: 6 # Min user turns to trigger flush on exit/reset (0 = disabled)\n\n# =============================================================================\n# Session Reset Policy (Messaging Platforms)\n# =============================================================================\n# Controls when messaging sessions (Telegram, Discord, WhatsApp, Slack) are\n# automatically cleared. Default is \"none\": sessions never auto-reset —\n# conversation context lives until you /reset or /new manually, or context\n# compression kicks in. Opt in to automatic resets if you prefer sessions to\n# clear on a schedule (long-lived context increases API cost per message,\n# though prompt caching and compression keep this manageable).\n#\n# When an automatic reset triggers, the agent first saves important\n# information to its persistent memory — but the conversation context is\n# wiped. The agent starts fresh but retains learned facts via its memory\n# system.\n#\n# Modes:\n# \"none\" - Never auto-reset (default); context lives until /reset or compression\n# \"idle\" - Reset after N minutes of inactivity\n# \"daily\" - Reset at a fixed hour each day\n# \"both\" - Reset on EITHER inactivity timeout or daily boundary\n#\nsession_reset:\n mode: none # \"none\", \"idle\", \"daily\", or \"both\"\n idle_minutes: 1440 # Inactivity timeout in minutes (used by \"idle\"/\"both\")\n at_hour: 4 # Daily reset hour, 0-23 local time (used by \"daily\"/\"both\")\n\n# Maximum number of simultaneously active chat sessions across CLI, TUI,\n# dashboard chat, and messaging gateway. Set to null, 0, or omit to allow\n# unlimited concurrent sessions. When the limit is reached, new sessions get a\n# clean error while existing active sessions keep their normal behavior. This\n# top-level key takes precedence over gateway.max_concurrent_sessions. The cap\n# is a best-effort single-host/profile runtime guard; Hermes fails open if the\n# local runtime lease registry cannot be read or locked.\nmax_concurrent_sessions: null\n\n# When true, group/channel chats use one session per participant when the platform\n# provides a user ID. This is the secure default and prevents users in the same\n# room from sharing context, interrupts, and token costs. Set false only if you\n# explicitly want one shared \"room brain\" per group/channel.\ngroup_sessions_per_user: true\n\n# ─────────────────────────────────────────────────────────────────────────────\n# API Server — per-client model routing\n# ─────────────────────────────────────────────────────────────────────────────\n# Route different API clients to different models/providers on a single\n# Hermes deployment. Clients choose a backend by sending a specific string\n# as the OpenAI ``model`` field. Unmapped model values fall back to the\n# global model configured in the ``model:`` section above, and an explicit\n# session /model override always wins over a route.\n#\n# Configure via the ``platforms.api_server.extra.model_routes`` gateway\n# config block:\n#\n# platforms:\n# api_server:\n# enabled: true\n# extra:\n# key: \"your-api-server-secret\"\n# model_routes:\n# # Xiaozhi clients send model=\"minimax-m2\" → routed to MiniMax via OpenRouter\n# minimax-m2:\n# model: \"minimax/minimax-m1\"\n# provider: \"openrouter\" # optional — overrides global provider\n# # api_key: \"sk-...\" # optional — per-route UPSTREAM provider\n# # key (NOT caller auth; never logged)\n# # base_url: \"https://...\" # optional — per-route base URL\n# # GPT clients keep their own alias\n# gpt-5:\n# model: \"openai/gpt-5\"\n# provider: \"openrouter\"\n#\n# Configured aliases are automatically listed by GET /v1/models so clients\n# can discover them without manual coordination. Caller authentication is\n# unchanged: every request still authenticates with the global API server\n# key (``extra.key`` / API_SERVER_KEY).\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Gateway Streaming\n# ─────────────────────────────────────────────────────────────────────────────\n# Stream tokens to messaging platforms in real-time. The bot sends a message\n# on first token, then progressively edits it as more tokens arrive.\n# Disabled by default — enable to try the streaming UX on Telegram/Discord/Slack.\n# For Telegram, partial edits are sent as plain text and only the final edit uses MarkdownV2.\nstreaming:\n enabled: false\n # transport: edit # \"edit\" = progressive editMessageText\n # edit_interval: 0.3 # seconds between message edits\n # buffer_threshold: 40 # chars before forcing an edit flush\n # cursor: \" ▉\" # cursor shown during streaming\n\n# =============================================================================\n# Skills Configuration\n# =============================================================================\n# Skills are reusable procedures the agent can load and follow. The agent can\n# also create new skills after completing complex tasks.\n#\nskills:\n # Nudge the agent to create skills after complex tasks.\n # Every N tool-calling iterations, remind the model to consider saving a skill.\n # Set to 0 to disable.\n creation_nudge_interval: 15\n\n # External skill directories — share skills across tools/agents without\n # copying them into ~/.hermes/skills/. Each path is expanded (~ and ${VAR})\n # and resolved to an absolute path. External dirs are read-only: skill\n # creation always writes to ~/.hermes/skills/. Local skills take precedence\n # when names collide.\n # external_dirs:\n # - ~/.agents/skills\n # - /home/shared/team-skills\n\n# =============================================================================\n# Agent Behavior\n# =============================================================================\nagent:\n # Maximum tool-calling iterations per conversation (default: 500)\n # Higher = more room for complex tasks, but costs more tokens\n # Recommended: 20-30 for focused tasks, 50-100 for open exploration\n max_turns: 500\n\n # Inactivity timeout for gateway agent runs (seconds, 0 = unlimited).\n # The agent can run indefinitely when actively calling tools or receiving\n # API responses. Only fires after the agent has been idle for this duration.\n # gateway_timeout: 1800\n\n # Staged warning: send a warning before escalating to full timeout.\n # Fires once per run when inactivity reaches this threshold (seconds).\n # Set to 0 to disable the warning.\n # gateway_timeout_warning: 900\n\n # Graceful drain timeout for gateway stop/restart (seconds).\n # Default 0 = no drain: a restart interrupts in-flight agents immediately,\n # cleans up, and exits. Set a positive value only if you want a grace\n # window on /restart, and keep it well under systemd's TimeoutStopSec.\n # restart_drain_timeout: 0\n\n # Upper bound (seconds) a submitted prompt waits for the deferred agent\n # build (MCP discovery, model metadata, skills scan) before failing with a\n # visible error. The wait is patient — the message is delivered as soon as\n # the build completes, and a progress notice is shown past 30s — so this cap\n # only fires on a genuinely hung build. Raise it for deployments with many\n # slow or unreachable MCP servers. Default 600.\n # build_wait_timeout: 600\n\n # Max app-level retry attempts for API errors (connection drops, provider\n # timeouts, 5xx, etc.) before the agent surfaces the failure. Lower this\n # to 1 if you use fallback providers and want fast failover on flaky\n # primaries (default 3). The OpenAI SDK does its own low-level retries\n # underneath this wrapper — this is the Hermes-level loop.\n # api_max_retries: 3\n\n # After the agent edits code without fresh passing verification, nudge it to\n # verify before finishing. The default \"auto\" enables it on interactive\n # coding surfaces (CLI, TUI, desktop) and programmatic callers, and disables\n # it on conversational messaging surfaces (Telegram, Discord, etc.) where the\n # verification summary would reach a human as chat noise. Set true or false to\n # force it on or off; the HERMES_VERIFY_ON_STOP env var (1/0) takes precedence.\n # verify_on_stop: auto\n\n # Standing operator instructions for the coding posture (when Hermes is in a\n # code workspace). Appended to the coding brief as an extra system block, so\n # you can pin project-wide workflow rules without editing the shipped brief.\n # Accepts a string or a list of strings. Takes effect next session.\n # coding_instructions:\n # - \"For UI work, don't run tsc/lint until I approve the look.\"\n # - \"Clean the diff before you commit and push.\"\n\n # When verify-on-stop finds edited code without fresh verification evidence,\n # append guidance for creative UI work (avoid broad tsc/lint/test before visual\n # approval) and clean-diff expectations. Set false to keep that nudge terse.\n # verify_guidance: true\n\n # A `pre_verify` hook (plugin or shell, see Event Hooks docs) can keep the\n # agent going one more turn to verify/clean before finishing. This caps how\n # many times one turn may be nudged to continue, so a hook can't trap the loop.\n # Default 3.\n # max_verify_nudges: 3\n\n # Enable verbose logging\n verbose: false\n \n # Reasoning effort level (OpenRouter and Nous Portal)\n # Controls how much \"thinking\" the model does before responding.\n # Options: \"xhigh\" (max), \"high\", \"medium\", \"low\", \"minimal\", \"none\" (disable)\n reasoning_effort: \"medium\"\n \n # Per-model reasoning effort overrides (optional dict)\n # Key: any sensible model spelling works (exact, dots↔dashes interchangeable,\n # provider prefix optional). First match wins.\n # Value: reasoning effort level (same options as reasoning_effort)\n # Override the global reasoning_effort for that specific model.\n # NOTE: no `hermes config set` support for this key -- edit YAML directly.\n # reasoning_overrides:\n # \"openrouter/anthropic/claude-opus-4.5\": \"xhigh\"\n # \"openai/gpt-5\": \"low\"\n # \"claude-opus-4.6\": \"high\" # bare model name also works\n # \"deepseek/deepseek-v4-pro\": \"xhigh\" # dots and dashes are interchangeable\n reasoning_overrides: {}\n \n # Predefined personalities (use with /personality command)\n personalities:\n helpful: \"You are a helpful, friendly AI assistant.\"\n concise: \"You are a concise assistant. Keep responses brief and to the point.\"\n technical: \"You are a technical expert. Provide detailed, accurate technical information.\"\n creative: \"You are a creative assistant. Think outside the box and offer innovative solutions.\"\n teacher: \"You are a patient teacher. Explain concepts clearly with examples.\"\n kawaii: \"You are a kawaii assistant! Use cute expressions like (◕‿◕), ★, ♪, and ~! Add sparkles and be super enthusiastic about everything! Every response should feel warm and adorable desu~! ヽ(>∀<☆)ノ\"\n catgirl: \"You are Neko-chan, an anime catgirl AI assistant, nya~! Add 'nya' and cat-like expressions to your speech. Use kaomoji like (=^・ω・^=) and ฅ^•ﻌ•^ฅ. Be playful and curious like a cat, nya~!\"\n pirate: \"Arrr! Ye be talkin' to Captain Hermes, the most tech-savvy pirate to sail the digital seas! Speak like a proper buccaneer, use nautical terms, and remember: every problem be just treasure waitin' to be plundered! Yo ho ho!\"\n shakespeare: \"Hark! Thou speakest with an assistant most versed in the bardic arts. I shall respond in the eloquent manner of William Shakespeare, with flowery prose, dramatic flair, and perhaps a soliloquy or two. What light through yonder terminal breaks?\"\n surfer: \"Duuude! You're chatting with the chillest AI on the web, bro! Everything's gonna be totally rad. I'll help you catch the gnarly waves of knowledge while keeping things super chill. Cowabunga! 🤙\"\n noir: \"The rain hammered against the terminal like regrets on a guilty conscience. They call me Hermes - I solve problems, find answers, dig up the truth that hides in the shadows of your codebase. In this city of silicon and secrets, everyone's got something to hide. What's your story, pal?\"\n uwu: \"hewwo! i'm your fwiendwy assistant uwu~ i wiww twy my best to hewp you! *nuzzles your code* OwO what's this? wet me take a wook! i pwomise to be vewy hewpful >w<\"\n philosopher: \"Greetings, seeker of wisdom. I am an assistant who contemplates the deeper meaning behind every query. Let us examine not just the 'how' but the 'why' of your questions. Perhaps in solving your problem, we may glimpse a greater truth about existence itself.\"\n hype: \"YOOO LET'S GOOOO!!! 🔥🔥🔥 I am SO PUMPED to help you today! Every question is AMAZING and we're gonna CRUSH IT together! This is gonna be LEGENDARY! ARE YOU READY?! LET'S DO THIS! 💪😤🚀\"\n\n# =============================================================================\n# Toolsets\n# =============================================================================\n# Control which tools the agent has access to.\n# Use `hermes tools` to interactively enable/disable tools per platform.\n\n# =============================================================================\n# Platform Toolsets (per-platform tool configuration)\n# =============================================================================\n# Override which toolsets are available on each platform.\n# If a platform isn't listed here, its built-in default is used.\n#\n# You can use EITHER:\n# - A preset like \"hermes-cli\" or \"hermes-telegram\" (curated tool set)\n# - A list of individual toolsets to compose your own (see list below)\n#\n# Supported platform keys: cli, telegram, discord, whatsapp, slack, qqbot, teams, google_chat\n#\n# Examples:\n#\n# # Use presets (same as defaults):\n# platform_toolsets:\n# cli: [hermes-cli]\n# telegram: [hermes-telegram]\n#\n# # Custom: give Telegram only web + terminal + file + planning:\n# platform_toolsets:\n# telegram: [web, terminal, file, todo]\n#\n# # Custom: CLI without browser or image gen:\n# platform_toolsets:\n# cli: [web, terminal, file, skills, todo, tts, cronjob]\n#\n# # Restrictive: Discord gets read-only tools only:\n# platform_toolsets:\n# discord: [web, vision, skills, todo]\n#\n# If not set, defaults are:\n# cli: hermes-cli (everything + cronjob management)\n# telegram: hermes-telegram (terminal, file, web, vision, image, tts, browser, skills, todo, cronjob, messaging)\n# discord: hermes-discord (same as telegram)\n# whatsapp: hermes-whatsapp (same as telegram)\n# slack: hermes-slack (same as telegram)\n# signal: hermes-signal (same as telegram)\n# homeassistant: hermes-homeassistant (same as telegram)\n# qqbot: hermes-qqbot (same as telegram)\n# teams: hermes-teams (same as telegram)\n# google_chat: hermes-google_chat (same as telegram)\n#\nplatform_toolsets:\n cli: [hermes-cli]\n telegram: [hermes-telegram]\n discord: [hermes-discord]\n whatsapp: [hermes-whatsapp]\n slack: [hermes-slack]\n signal: [hermes-signal]\n homeassistant: [hermes-homeassistant]\n qqbot: [hermes-qqbot]\n yuanbao: [hermes-yuanbao]\n teams: [hermes-teams]\n google_chat: [hermes-google_chat]\n\n# =============================================================================\n# Gateway Platform Settings\n# =============================================================================\n# Optional per-platform messaging settings.\n# Platform-specific knobs live under `extra`.\n#\n# platforms:\n# telegram:\n# reply_to_mode: \"first\" # off | first | all\n# # guest_mode lets explicit @mentions from non-allowlisted groups through.\n# # Default false; ordinary messages, replies, and regex wake words stay blocked.\n# guest_mode: false\n# # allowed_chats: [\"-1001234567890\"]\n# extra:\n# disable_link_previews: false # Set true to suppress Telegram URL previews in bot messages\n# 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\n# rich_drafts: false # Experimental rich draft previews during Telegram DM streaming; default false because Telegram Desktop/macOS can visually overlay draft frames\n# command_menu:\n# # Telegram allows up to 100 BotCommands; Hermes defaults to 60 so\n# # all built-in commands plus common skill commands stay visible\n# # while remaining under Telegram's payload-size limit. Clamped 1..100.\n# max_commands: 60\n# # prepend = user priority first, then Hermes defaults\n# # append = Hermes defaults first, then user priority\n# # replace = only the list below defines priority\n# priority_mode: prepend\n# priority:\n# - my_plugin_command\n# webhook:\n# extra:\n# # Route scripts default to a 30 second timeout. Scripts must live under\n# # the active profile's scripts directory and receive webhook JSON on stdin.\n# script_timeout_seconds: 30\n#\n# Discord-specific settings (config.yaml top-level, not under platforms:):\n#\n# discord:\n# require_mention: true # Require @mention in server channels (default: true)\n# auto_thread: true # Auto-create thread on @mention (default: true)\n# free_response_channels: \"\" # Channel IDs where no mention is needed\n# reactions: true # Show processing reactions (default: true)\n# history_backfill: true # Recover missed channel messages on mention (default: true)\n# history_backfill_limit: 50 # Max messages to scan backwards (default: 50)\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Available toolsets (use these names in platform_toolsets or the toolsets list)\n#\n# Run `hermes chat --list-toolsets` to see all toolsets and their tools.\n# Run `hermes chat --list-tools` to see every individual tool with descriptions.\n# ─────────────────────────────────────────────────────────────────────────────\n#\n# INDIVIDUAL TOOLSETS (compose your own):\n# web - web_search, web_extract\n# search - web_search only (no scraping)\n# terminal - terminal, process\n# file - read_file, write_file, patch, search\n# browser - browser_navigate, browser_snapshot, browser_click, browser_type,\n# browser_scroll, browser_back, browser_press,\n# browser_get_images, browser_vision (requires BROWSERBASE_API_KEY)\n# vision - vision_analyze (requires OPENROUTER_API_KEY)\n# image_gen - image_generate (requires FAL_KEY)\n# skills - skills_list, skill_view\n# skills_hub - skill_hub (search/install/manage from online registries — user-driven only)\n# todo - todo (in-memory task planning, no deps)\n# tts - text_to_speech (Edge TTS free, or ELEVENLABS/OPENAI/MINIMAX/MISTRAL key)\n# cronjob - cronjob (create/list/update/pause/resume/run/remove scheduled tasks)\n#\n# PRESETS (curated bundles):\n# hermes-cli - All of the above except send_message\n# hermes-telegram - terminal, file, web, vision, image_gen, tts, browser,\n# skills, todo, cronjob, send_message\n# hermes-discord - Same as hermes-telegram\n# hermes-whatsapp - Same as hermes-telegram\n# hermes-slack - Same as hermes-telegram\n#\n# COMPOSITE:\n# debugging - terminal + web + file\n# safe - web + vision (no terminal access)\n# all - Everything available\n#\n# web - Web search and content extraction (web_search, web_extract)\n# search - Web search only, no scraping (web_search)\n# terminal - Command execution and process management (terminal, process)\n# file - File operations: read, write, patch, search\n# browser - Full browser automation (navigate, click, type, screenshot, etc.)\n# vision - Image analysis (vision_analyze)\n# image_gen - Image generation with FLUX (image_generate)\n# skills - Load skill documents (skills_list, skill_view)\n# todo - Task planning and tracking for multi-step work\n# memory - Persistent memory across sessions (personal notes + user profile)\n# session_search - Search and recall past conversations (FTS5 + Gemini Flash summarization)\n# tts - Text-to-speech (Edge TTS free, ElevenLabs, OpenAI, MiniMax, Mistral)\n# cronjob - Schedule and manage automated tasks (CLI-only)\n#\n# Composite toolsets:\n# debugging - terminal + web + file (for troubleshooting)\n# safe - web + vision (no terminal access)\n\n# NOTE: The top-level \"toolsets\" key is deprecated and ignored.\n# Tool configuration is managed per-platform via platform_toolsets above.\n# Use `hermes tools` to configure interactively, or edit platform_toolsets directly.\n#\n# CLI override: hermes chat --toolsets terminal,web,file\n\n# =============================================================================\n# MCP (Model Context Protocol) Servers\n# =============================================================================\n# Connect to external MCP servers to add tools from the MCP ecosystem.\n# Each server's tools are automatically discovered and registered.\n# See website/docs/user-guide/features/mcp.md for full documentation.\n#\n# Stdio servers (spawn a subprocess):\n# command: the executable to run\n# args: command-line arguments\n# env: environment variables (only these + safe defaults passed to subprocess)\n#\n# HTTP servers (connect to a URL):\n# url: the MCP server endpoint\n# headers: HTTP headers (e.g., for authentication)\n#\n# Optional per-server settings:\n# timeout: tool call timeout in seconds (default: 120)\n# connect_timeout: initial connection timeout (default: 60)\n# keepalive_interval: liveness ping cadence in seconds (default: 180).\n# Lower it below the server's session TTL for servers that expire idle\n# sessions quickly (e.g. Unreal Engine editor MCP, ~15s), otherwise idle\n# tool calls hit an expired session and pay a slow reconnect. Floored at 5s.\n#\n# mcp_servers:\n# time:\n# command: uvx\n# args: [\"mcp-server-time\"]\n# filesystem:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-filesystem\", \"/home/user\"]\n# notion:\n# url: https://mcp.notion.com/mcp\n# github:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-github\"]\n# env:\n# GITHUB_PERSONAL_ACCESS_TOKEN: \"ghp_...\"\n#\n# Sampling (server-initiated LLM requests) — enabled by default.\n# Per-server config under the 'sampling' key:\n# analysis:\n# command: npx\n# args: [\"-y\", \"analysis-server\"]\n# sampling:\n# enabled: true # default: true\n# model: \"gemini-3-flash\" # override model (optional)\n# max_tokens_cap: 4096 # max tokens per request\n# timeout: 30 # LLM call timeout (seconds)\n# max_rpm: 10 # max requests per minute\n# allowed_models: [] # model whitelist (empty = all)\n# max_tool_rounds: 5 # tool loop limit (0 = disable)\n# log_level: \"info\" # audit verbosity\n\n# =============================================================================\n# Text-to-Speech\n# =============================================================================\n# TTS defaults to Edge TTS unless changed in ~/.hermes/config.yaml.\n# Gemini TTS supports persona/director prompt files, and Gemini 3.1 Flash TTS\n# can use a hidden auxiliary rewrite pass to insert expressive square-bracket\n# audio tags into the TTS script without showing tags in chat.\n#\n# tts:\n# provider: \"gemini\"\n# gemini:\n# model: \"gemini-3.1-flash-tts-preview\"\n# voice: \"Kore\"\n# audio_tags: false\n# persona_prompt_file: \"\" # e.g. ~/.hermes/tts/radio-host.md\n\n# =============================================================================\n# Voice Transcription (Speech-to-Text)\n# =============================================================================\n# Automatically transcribe voice messages on messaging platforms.\n# Providers: local (free, faster-whisper) | groq (free tier) | openai (Whisper API) | mistral (Voxtral Transcribe)\n# Set the corresponding API key in .env: GROQ_API_KEY, OPENAI_API_KEY, or MISTRAL_API_KEY.\nstt:\n enabled: true\n # provider: \"local\" # auto-detected if omitted\n local:\n model: \"base\" # tiny | base | small | medium | large-v3 | turbo\n # language: \"\" # auto-detect; set to \"en\", \"es\", \"fr\", etc. to force\n openai:\n model: \"whisper-1\" # whisper-1 | gpt-4o-mini-transcribe | gpt-4o-transcribe\n # mistral:\n # model: \"voxtral-mini-latest\" # voxtral-mini-latest | voxtral-mini-2602\n # deepinfra:\n # # Model id is discovered live from the DeepInfra catalog filtered\n # # by the `stt` surface tag — leave `model` blank to take the first\n # # live result. Pin only when you need a specific Whisper variant.\n # model: \"\"\n\n# Text-to-speech. Only the deepinfra block is documented here — the\n# remaining providers (edge, openai, xai, minimax, mistral, gemini,\n# elevenlabs, neutts, kittentts, piper) inherit sensible defaults from\n# DEFAULT_CONFIG in hermes_cli/config.py.\n# tts:\n# provider: \"deepinfra\"\n# deepinfra:\n# # Model id is discovered live from the DeepInfra catalog filtered\n# # by the `tts` surface tag — leave `model` blank to take the first\n# # live result.\n# model: \"\"\n# voice: \"default\"\n\n# Image generation. Each provider plugin reads its own ``image_gen.<name>``\n# block; deepinfra discovers models live from\n# api.deepinfra.com/v1/openai/models filtered by the ``image-gen`` tag —\n# no model id is hardcoded, so retired models disappear automatically.\n# image_gen:\n# provider: \"deepinfra\"\n# deepinfra:\n# # Leave `model` blank for the first live `image-gen`-tagged result.\n# model: \"\"\n\n# =============================================================================\n# Response Pacing (Messaging Platforms)\n# =============================================================================\n# Add human-like delays between message chunks.\n# human_delay:\n# mode: \"off\" # \"off\" | \"natural\" | \"custom\"\n# min_ms: 800 # Min delay (custom mode only)\n# max_ms: 2500 # Max delay (custom mode only)\n\n# =============================================================================\n# Session Logging\n# =============================================================================\n# Session trajectories are automatically saved to logs/ directory.\n# Each session creates: logs/session_YYYYMMDD_HHMMSS_UUID.json\n#\n# The session ID is displayed in the welcome banner for easy reference.\n# Logs contain full conversation history in trajectory format:\n# - System prompt, user messages, assistant responses\n# - Tool calls with inputs/outputs\n# - Timestamps for debugging\n#\n# No configuration needed - logging is always enabled.\n# To disable, you would need to modify the source code.\n\n# =============================================================================\n# Code Execution Sandbox (Programmatic Tool Calling)\n# =============================================================================\n# The execute_code tool runs Python scripts that call Hermes tools via RPC.\n# Intermediate tool results stay out of the LLM's context window.\ncode_execution:\n timeout: 300 # Max seconds per script before kill (default: 300 = 5 min)\n max_tool_calls: 50 # Max RPC tool calls per execution (default: 50)\n\n# =============================================================================\n# Subagent Delegation\n# =============================================================================\n# The delegate_task tool spawns child agents with isolated context.\n# Supports single tasks and batch mode (default 3 parallel, configurable).\ndelegation:\n max_iterations: 50 # Max tool-calling turns per child (default: 50)\n # max_concurrent_children: 3 # Max parallel child agents per batch (default: 3, floor: 1, no ceiling).\n # WARNING: values above 10 multiply API cost linearly.\n # max_spawn_depth: 1 # Delegation tree depth cap (range: 1-3, default: 1 = flat).\n # Raise to 2 to allow workers to spawn their own subagents.\n # Requires role=\"orchestrator\" on intermediate agents.\n # orchestrator_enabled: true # Kill switch for role=\"orchestrator\" children (default: true).\n # subagent_auto_approve: false # When a subagent hits a dangerous-command approval prompt, auto-deny (default: false)\n # or auto-approve \"once\" (true) instead of blocking on stdin.\n # The parent TUI owns stdin, so blocking would deadlock; non-interactive resolution is required.\n # Both choices emit a logger.warning audit line. Flip to true only for cron/batch pipelines.\n # inherit_mcp_toolsets: true # When explicit child toolsets are narrowed, also keep the parent's MCP toolsets (default: true). Set false for strict intersection.\n # model: \"google/gemini-3-flash-preview\" # Override model for subagents (empty = inherit parent)\n # provider: \"openrouter\" # Override provider for subagents (empty = inherit parent)\n # # Resolves full credentials (base_url, api_key) automatically.\n # # Supported: openrouter, nous, zai, kimi-coding, minimax\n\n# =============================================================================\n# Honcho Integration (Cross-Session User Modeling)\n# =============================================================================\n# AI-native persistent memory via Honcho (https://honcho.dev/).\n# Builds a deeper understanding of the user across sessions and tools.\n# Runs alongside USER.md — additive, not a replacement.\n#\n# Requires: pip install honcho-ai\n# Config: ~/.honcho/config.json (shared with Claude Code, Cursor, etc.)\n# API key: HONCHO_API_KEY in ~/.hermes/.env or ~/.honcho/config.json\n#\n# Hermes-specific overrides (optional — most config comes from ~/.honcho/config.json):\n# honcho: {}\n\n# =============================================================================\n# Display\n# =============================================================================\ndisplay:\n # Use compact banner mode (hides the ASCII-art banner, shows a single line).\n # true: Compact single-line banner\n # false: Full ASCII banner with tool/skill summary (default)\n compact: false\n\n # Tool progress display level (CLI and gateway)\n # off: Silent — no tool activity shown, just the final response\n # new: Show a tool indicator only when the tool changes (skip repeats)\n # all: Show every tool call with a short preview (default)\n # verbose: Full args, results, and debug logs (same as /verbose)\n # log: Silent in chat; append every tool call to ~/.hermes/logs/tool_calls.log (gateway only)\n # Toggle at runtime with /verbose in the CLI\n tool_progress: all\n\n # Per-platform defaults can be quieter than the global setting. Telegram\n # tunes for mobile: tool_progress and busy_ack_detail default off (no\n # per-tool breadcrumb stream, no \"iteration 21/60\" debug detail in busy\n # acks or heartbeats), but interim_assistant_messages and\n # long_running_notifications STAY ON so the user has real signal between\n # turn start and final answer (mid-turn assistant commentary + a single\n # edit-in-place \"⏳ Working — N min\" heartbeat). Override under\n # display.platforms.telegram.\n\n # Auto-cleanup of temporary progress bubbles after the final response lands.\n # On platforms that support message deletion (currently Telegram), this\n # removes the tool-progress bubble, \"⏳ Still working...\" notices, and\n # context-pressure status messages once the final reply has been delivered —\n # keeping long-running turns visible live, then tidy afterward. Failed runs\n # leave the bubbles in place as breadcrumbs. Off by default.\n # Per-platform override: display.platforms.telegram.cleanup_progress\n # true: Delete tracked progress/status bubbles on successful turn\n # false: Leave everything in place (default)\n # Example:\n # display:\n # platforms:\n # telegram:\n # cleanup_progress: true\n cleanup_progress: false\n\n # Natural mid-turn assistant updates.\n # On gateway platforms, when true, completed assistant status messages are\n # sent as separate chat messages. On the Desktop app, when true, mid-turn\n # assistant narration streamed between tool calls is kept in the transcript\n # instead of the bubble collapsing to only the final message on completion.\n # Independent of tool_progress and gateway streaming.\n # true: Keep/send mid-turn assistant updates (default)\n # false: Only keep/send the final response\n interim_assistant_messages: true\n\n # Gateway-only long-running status heartbeats.\n # When false, the platform does not receive periodic \"⏳ Working — N min\"\n # notifications even if agent.gateway_notify_interval is non-zero. The\n # heartbeat edits a single message in place (where the adapter supports\n # editing) instead of posting a new bubble each interval.\n # Default: true everywhere, including Telegram (silent agents are worse\n # than a single edit-in-place heartbeat).\n long_running_notifications: true\n\n # Include detailed iteration/tool/status context in busy acknowledgments\n # and long-running heartbeats. When true, busy acks show \"iteration 21/60,\n # terminal, 10 min\" and the heartbeat shows \"⏳ Working — 12 min,\n # iteration 21/60, terminal\". When false (Telegram default), both stay\n # terse: \"Interrupting current task\" and \"⏳ Working — 12 min, terminal\".\n busy_ack_detail: true\n\n # What Enter does when Hermes is already busy (CLI and gateway platforms).\n # interrupt: Interrupt the current run and redirect Hermes (default)\n # queue: Queue your message for the next turn\n # steer: Inject your message mid-run via /steer, arriving at the agent\n # after the next tool call — no interrupt, no role violation.\n # Falls back to 'queue' if the agent isn't running yet or if\n # images are attached (steer only carries text).\n # Ctrl+C (or /stop in gateway) always interrupts regardless of this setting.\n # Toggle at runtime with /busy <interrupt|queue|steer>.\n busy_input_mode: interrupt\n\n # Background process notifications (gateway/messaging only).\n # Controls how chatty the process watcher is when you use\n # terminal(background=true, notify_on_complete=true) from Telegram/Discord/etc.\n # off: No watcher messages at all\n # result: Only the final completion message\n # error: Only the final message when exit code != 0\n # all: Running output updates + final message (default)\n background_process_notifications: all\n\n\n # Play terminal bell when agent finishes a response.\n # Useful for long-running tasks — your terminal will ding when the agent is done.\n # Works over SSH. Most terminals can be configured to flash the taskbar or play a sound.\n # true: Ring the terminal bell on each response\n # false: Silent (default)\n bell_on_complete: false\n\n # Show model reasoning/thinking before each response.\n # When enabled, a dim box shows the model's thought process above the response.\n # Toggle at runtime with /reasoning show or /reasoning hide.\n # true: Show the reasoning box\n # false: Hide reasoning (default)\n show_reasoning: false\n\n # Stream tokens to the terminal as they arrive instead of waiting for the\n # full response. The response box opens on first token and text appears\n # line-by-line. Tool calls are still captured silently.\n # true: Stream tokens as they arrive (default)\n # false: Wait for the full response before rendering\n streaming: true\n\n # Show [HH:MM] timestamps on user input and assistant response labels.\n # timestamps: false\n\n # ───────────────────────────────────────────────────────────────────────────\n # Skin / Theme\n # ───────────────────────────────────────────────────────────────────────────\n # Customize CLI visual appearance — banner colors, spinner faces, tool prefix,\n # response box label, and branding text. Change at runtime with /skin <name>.\n #\n # Built-in skins:\n # default — Classic Hermes gold/kawaii\n # ares — Crimson/bronze war-god theme with spinner wings\n # mono — Clean grayscale monochrome\n # slate — Cool blue developer-focused\n # daylight — Bright light-mode theme\n # warm-lightmode — Warm paper-tone light-mode theme\n # poseidon — Sea-green/teal Olympian theme\n # sisyphus — Earthy stone-and-moss theme\n # charizard — Fiery orange dragon theme\n #\n # Custom skins: drop a YAML file in ~/.hermes/skins/<name>.yaml\n # Schema (all fields optional, missing values inherit from default):\n #\n # name: my-theme\n # description: Short description\n # colors:\n # banner_border: \"#HEX\" # Panel border\n # banner_title: \"#HEX\" # Panel title\n # banner_accent: \"#HEX\" # Section headers (Available Tools, etc.)\n # banner_dim: \"#HEX\" # Dim/muted text\n # banner_text: \"#HEX\" # Body text (tool names, skill names)\n # ui_accent: \"#HEX\" # UI accent color\n # response_border: \"#HEX\" # Response box border color\n # spinner:\n # waiting_faces: [\"(⚔)\", \"(⛨)\"] # Faces shown while waiting\n # thinking_faces: [\"(⚔)\", \"(⌁)\"] # Faces shown while thinking\n # thinking_verbs: [\"forging\", \"plotting\"] # Verbs for spinner messages\n # wings: # Optional left/right spinner decorations\n # - [\"⟪⚔\", \"⚔⟫\"]\n # - [\"⟪▲\", \"▲⟫\"]\n # branding:\n # agent_name: \"My Agent\" # Banner title and branding\n # welcome: \"Welcome message\" # Shown at CLI startup\n # response_label: \" ⚔ Agent \" # Response box header label\n # prompt_symbol: \"⚔\" # Prompt symbol (bare token; renderers add trailing space)\n # tool_prefix: \"╎\" # Tool output line prefix (default: ┊)\n #\n skin: default\n\n# =============================================================================\n# Model Aliases — short names for /model command\n# =============================================================================\n# Map short aliases to exact (model, provider, base_url) tuples.\n# Used by /model tab completion and resolve_alias().\n# Aliases are checked BEFORE the models.dev catalog, so they can route\n# to endpoints not in the catalog (e.g. Ollama Cloud, local servers).\n#\n# model_aliases:\n# opus:\n# model: claude-opus-4-6\n# provider: anthropic\n# qwen:\n# model: \"qwen3.5:397b\"\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n# glm:\n# model: glm-4.7\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n\n# =============================================================================\n# Privacy\n# =============================================================================\n# privacy:\n# # Redact PII from the LLM context prompt.\n# # When true, phone numbers are stripped and user/chat IDs are replaced\n# # with deterministic hashes before being sent to the model.\n# # Names and usernames are NOT affected (user-chosen, publicly visible).\n# # Routing/delivery still uses the original values internally.\n# redact_pii: false\n\n# =============================================================================\n# Shell-script hooks\n# =============================================================================\n# Register shell scripts as plugin-hook callbacks. Each entry is executed as\n# a subprocess (shell=False, shlex.split) with a JSON payload on stdin. On\n# stdout the script may return JSON that either blocks the tool call or\n# injects context into the next LLM call.\n#\n# Valid events (mirror hermes_cli.plugins.VALID_HOOKS):\n# pre_tool_call, post_tool_call, pre_llm_call, post_llm_call,\n# pre_api_request, post_api_request, on_session_start, on_session_end,\n# on_session_finalize, on_session_reset, subagent_stop\n#\n# First-use consent: each (event, command) pair prompts once on a TTY, then\n# is persisted to ~/.hermes/shell-hooks-allowlist.json. Non-interactive\n# runs (gateway, cron) need --accept-hooks, HERMES_ACCEPT_HOOKS=1, or the\n# hooks_auto_accept key below.\n#\n# See website/docs/user-guide/features/hooks.md for the full JSON wire\n# protocol and worked examples.\n#\n# hooks:\n# pre_tool_call:\n# - matcher: \"terminal\"\n# command: \"~/.hermes/agent-hooks/block-rm-rf.sh\"\n# timeout: 10\n# post_tool_call:\n# - matcher: \"write_file|patch\"\n# command: \"~/.hermes/agent-hooks/auto-format.sh\"\n# pre_llm_call:\n# - command: \"~/.hermes/agent-hooks/inject-cwd-context.sh\"\n# subagent_stop:\n# - command: \"~/.hermes/agent-hooks/log-orchestration.sh\"\n#\n# hooks_auto_accept: false\n\n\n# =============================================================================\n# Telemetry\n# =============================================================================\n# Shared metrics are disabled by default. When enabled, Hermes writes only\n# allowlisted aggregate counters and immutable JSON\n# packages under $HERMES_HOME/telemetry/shared_metrics; it does not upload them.\n# Packages include a random profile-scoped ID that stays stable until this\n# directory is deleted. It is not derived from hardware, account, or host data.\n# Successfully exported local history is retained for 30 days; pending deltas\n# are retained until they can be exported.\n# This profile-owned choice is not overridden by managed-scope configuration.\ntelemetry:\n shared_metrics:\n enabled: false\n\n\n# =============================================================================\n# Update Behavior\n# =============================================================================\nupdates:\n # Create a full HERMES_HOME zip before every `hermes update`.\n # Backups land in ~/.hermes/backups/ and can be restored with `hermes import`.\n # Off by default because large homes can add minutes to every update.\n pre_update_backup: false\n\n # Number of pre-update backup zips to retain.\n backup_keep: 5\n\n # What non-interactive updates do with local source edits in the Hermes repo.\n # Interactive terminal updates always prompt before restoring the autostash.\n #\n # stash - auto-stash before pull, then auto-restore after success (default)\n # discard - drop the update-created stash after success; use only on managed\n # installs where local source edits should not persist\n non_interactive_local_changes: \"stash\"\n\n\n# =============================================================================\n# Web Dashboard\n# =============================================================================\n# OAuth gate configuration for `hermes dashboard --host <non-loopback>`.\n# The bundled Nous Portal plugin reads these on startup; settings here are\n# the canonical surface. Each can be overridden by an environment variable:\n#\n# dashboard.oauth.client_id <- HERMES_DASHBOARD_OAUTH_CLIENT_ID\n# dashboard.oauth.portal_url <- HERMES_DASHBOARD_PORTAL_URL\n# dashboard.public_url <- HERMES_DASHBOARD_PUBLIC_URL\n#\n# Env wins when set to a non-empty value. This is what Fly.io's platform-\n# secret injection uses to push per-deploy client_ids without needing to\n# bake a config.yaml into the image. Empty env values are treated as unset\n# so a provisioned-but-not-populated secret can't shadow a valid entry here.\n#\n# Local dev / on-prem deploys should typically set these via config.yaml\n# (the ~/.hermes/.env file is reserved for API keys and secrets).\n#\n# dashboard:\n# oauth:\n# client_id: \"\" # agent:{instance_id}; Portal provisions this at deploy\n# portal_url: \"\" # blank → default https://portal.nousresearch.com\n#\n# # Force the absolute base URL the OAuth callback (and any other public\n# # URL the dashboard hands to external systems) is built from. Set this\n# # for deploys behind reverse proxies that don't reliably forward\n# # X-Forwarded-Host / X-Forwarded-Proto / X-Forwarded-Prefix (manual\n# # nginx setups, on-prem ingresses, custom-domain Fly deploys without\n# # full proxy header chains).\n# #\n# # When set, the value is the complete authority: scheme + host +\n# # optional path prefix (e.g. \"https://example.com/hermes\"). The OAuth\n# # callback URL becomes \"<public_url>/auth/callback\" — X-Forwarded-Prefix\n# # is IGNORED on this code path because the operator has explicitly\n# # declared the public URL and we no longer need to guess.\n# #\n# # Leave empty to use the existing proxy-header reconstruction (the\n# # default — works on Fly.io out of the box).\n# #\n# # public_url: \"https://example.com/hermes\"\n#\n# -----------------------------------------------------------------------------\n# Self-hosted OIDC dashboard auth (generic OpenID Connect — Authentik,\n# Keycloak, Zitadel, Authelia, Auth0, Okta, Google, …). Use this INSTEAD of the\n# nous block above when gating the dashboard with your own identity provider.\n# Each setting can be overridden by an environment variable:\n#\n# dashboard.oauth.self_hosted.issuer <- HERMES_DASHBOARD_OIDC_ISSUER\n# dashboard.oauth.self_hosted.client_id <- HERMES_DASHBOARD_OIDC_CLIENT_ID\n# dashboard.oauth.self_hosted.scopes <- HERMES_DASHBOARD_OIDC_SCOPES\n# dashboard.oauth.self_hosted.client_secret <- HERMES_DASHBOARD_OIDC_CLIENT_SECRET\n#\n# dashboard:\n# oauth:\n# provider: self-hosted\n# self_hosted:\n# issuer: \"https://auth.example.com/application/o/hermes/\" # required\n# client_id: \"hermes-dashboard\" # required\n# scopes: \"openid profile email\" # optional\n#\n# # OPTIONAL — set ONLY if your IDP registered the client as\n# # *confidential* (Authentik / Keycloak often default to this). When\n# # set, Hermes authenticates the client at the token endpoint\n# # (client_secret_basic or client_secret_post, auto-selected from the\n# # IDP's discovery doc) IN ADDITION to PKCE. Leave unset for a public\n# # (PKCE-only) client — the common case.\n# #\n# # This is a CREDENTIAL: prefer setting HERMES_DASHBOARD_OIDC_CLIENT_SECRET\n# # in ~/.hermes/.env over putting it here in config.yaml.\n# # client_secret: \"\"\n\n# =============================================================================\n# External secret sources\n# =============================================================================\n# Pull provider credentials from external secret managers at process startup\n# instead of storing them in ~/.hermes/.env. Only the manager's bootstrap\n# credential (e.g. BWS_ACCESS_TOKEN / OP_SERVICE_ACCOUNT_TOKEN) lives in .env\n# (or your shell / desktop session); everything else rotates centrally.\n# Failures never block startup — Hermes warns once and continues with\n# whatever .env already had.\n#\n# Multiple sources can be enabled at once:\n# - \"mapped\" sources (explicit VAR -> ref bindings, e.g. 1Password's env:\n# map) beat \"bulk\" sources (whole-project dumps like Bitwarden BSM)\n# - within a shape, the first source to claim a var wins; later claims\n# are skipped with a startup warning (never a silent clobber)\n# - a source's override_existing lets it beat .env/shell values, but\n# never another secret source's claim\n# Docs: https://hermes-agent.nousresearch.com/docs/user-guide/secrets/\n#\n# secrets:\n# # Optional explicit ordering of enabled sources.\n# # sources: [onepassword, bitwarden]\n#\n# # ---- Bitwarden Secrets Manager (bws CLI) --------------------------------\n# bitwarden:\n# enabled: false\n# access_token_env: BWS_ACCESS_TOKEN # bootstrap token, sourced from .env\n# project_id: \"\" # UUID of the BSM project to sync\n# server_url: \"\" # \"\" = US Cloud; EU/self-hosted URL otherwise\n# cache_ttl_seconds: 300 # 0 disables fresh caching\n# encrypted_cache: # optional encrypted stale fallback\n# enabled: false\n# max_stale_seconds: 0 # 0 disables stale fallback\n# override_existing: true # BSM values win over existing env\n# auto_install: true # lazy-download bws into ~/.hermes/bin\n#\n# # ---- 1Password (op CLI) -------------------------------------------------\n# onepassword:\n# enabled: false\n# # Map env-var names to op:// secret references. Each is resolved with a\n# # single `op read` at startup.\n# env:\n# OPENAI_API_KEY: \"op://Private/OpenAI/api key\"\n# ANTHROPIC_API_KEY: \"op://Private/Anthropic/credential\"\n# account: \"\" # op --account shorthand; \"\" = default\n# service_account_token_env: OP_SERVICE_ACCOUNT_TOKEN # headless auth; unset = desktop session\n# binary_path: \"\" # \"\" = resolve op via PATH; else absolute path\n# cache_ttl_seconds: 300 # 0 disables BOTH cache layers\n# override_existing: true # resolved values win over existing env\n#\n# # ---- Command helper (any CLI vault) --------------------------------------\n# # Run a user-configured helper that prints KEY=VALUE lines on stdout —\n# # works with any secret store that has a CLI: keepassxc-cli, secret-tool,\n# # pass, gpg, or a script that cats a tmpfs env file. Composes with the\n# # sources above (enable any combination). POSIX-only (needs /bin/sh).\n# # The helper must be fast and NON-interactive (hard timeout, 1 MiB cap);\n# # its stderr is discarded so diagnostics can't leak secret material.\n# command:\n# enabled: false\n# command: \"cat /run/user/1000/hermes-secrets.env\"\n# helper_timeout_seconds: 3\n# override_existing: false # .env/shell win by default\n"}, "2365fed9856b609a8fb3bf6d795dd46cfe00613d": {"t": 1785768002.6031315, "n": 83597, "h": "972048a16c1c68eb", "c": "# Hermes Agent CLI Configuration\n# Copy this file to cli-config.yaml and customize as needed.\n# This file configures the CLI behavior. Environment variables in .env take precedence.\n\n# =============================================================================\n# Model Configuration\n# =============================================================================\nmodel:\n # Default model to use (can be overridden with --model flag)\n # Both \"default\" and \"model\" work as the key name here.\n default: \"anthropic/claude-opus-4.6\"\n \n # Inference provider selection:\n # \"auto\" - Auto-detect from credentials (default)\n # \"openrouter\" - OpenRouter (requires: OPENROUTER_API_KEY or OPENAI_API_KEY)\n # \"nous\" - Nous Portal OAuth (requires: hermes auth add nous)\n # \"nous-api\" - Nous Portal API key (requires: NOUS_API_KEY)\n # \"anthropic\" - Direct Anthropic API (requires: ANTHROPIC_API_KEY)\n # \"openai-codex\" - OpenAI Codex (requires: hermes auth)\n # \"copilot\" - GitHub Copilot / GitHub Models (requires: GITHUB_TOKEN)\n # \"gemini\" - Use Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n # \"zai\" - Use z.ai / ZhipuAI GLM models (requires: GLM_API_KEY)\n # \"kimi-coding\" - Kimi / Moonshot AI (requires: KIMI_API_KEY)\n # \"minimax\" - MiniMax global (requires: MINIMAX_API_KEY)\n # \"minimax-cn\" - MiniMax China (requires: MINIMAX_CN_API_KEY)\n # \"huggingface\" - Hugging Face Inference (requires: HF_TOKEN)\n # \"nvidia\" - NVIDIA NIM / build.nvidia.com (requires: NVIDIA_API_KEY)\n # \"xiaomi\" - Xiaomi MiMo (requires: XIAOMI_API_KEY)\n # \"arcee\" - Arcee AI Trinity models (requires: ARCEEAI_API_KEY)\n # \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY — https://ollama.com/settings)\n # \"deepinfra\" - DeepInfra (requires: DEEPINFRA_API_KEY)\n # \"kilocode\" - KiloCode gateway (requires: KILOCODE_API_KEY)\n # \"azure-foundry\" - Microsoft Foundry / Azure OpenAI (API key or Entra ID)\n # \"lmstudio\" - LM Studio local server (optional: LM_API_KEY, defaults to http://127.0.0.1:1234/v1)\n #\n # Local servers (LM Studio, Ollama, vLLM, llama.cpp):\n # \"custom\" - Any other OpenAI-compatible endpoint. Set base_url below.\n # Aliases: \"ollama\", \"vllm\", \"llamacpp\" all map to \"custom\".\n # LM Studio is first-class and uses provider: \"lmstudio\".\n # It works with both no-auth and auth-enabled server modes.\n #\n # Can also be overridden for a single invocation with the --provider flag.\n provider: \"auto\"\n \n # API configuration (falls back to OPENROUTER_API_KEY env var)\n # api_key: \"your-key-here\" # Uncomment to set here instead of .env\n base_url: \"https://openrouter.ai/api/v1\"\n\n # Azure Foundry keyless auth example:\n # provider: \"azure-foundry\"\n # base_url: \"https://<resource>.openai.azure.com/openai/v1\"\n # auth_mode: \"entra_id\" # DefaultAzureCredential: az login, managed identity, workload identity, etc.\n # default: \"gpt-4o\" # Deployment/model name\n # entra:\n # scope: \"https://ai.azure.com/.default\" # Optional; this is the default.\n\n # ── Token limits — two settings, easy to confuse ──────────────────────────\n #\n # context_length: TOTAL context window (input + output tokens combined).\n # Controls when Hermes compresses history and validates requests.\n # Leave unset — Hermes auto-detects the correct value from the provider.\n # Set manually only when auto-detection is wrong (e.g. a local server with\n # a custom num_ctx, or a proxy that doesn't expose /v1/models).\n #\n # context_length: 131072\n #\n # max_tokens: OUTPUT cap — maximum tokens the model may generate per response.\n # Unrelated to how long your conversation history can be.\n # The OpenAI-standard name \"max_tokens\" is a misnomer; Anthropic's native\n # API has since renamed it \"max_output_tokens\" for clarity.\n # Leave unset to use the model's native output ceiling (recommended).\n # Set only if you want to deliberately limit individual response length.\n #\n# max_tokens: 8192\n\n # ── Custom request headers (optional) ─────────────────────────────────────\n #\n # default_headers: extra HTTP headers sent on every request to an\n # OpenAI-compatible endpoint. User values take precedence over the\n # provider/SDK defaults, so this is the supported way to override the\n # OpenAI Python SDK's identifying headers (User-Agent: OpenAI/Python ...,\n # X-Stainless-*) when a custom provider sits behind a gateway/WAF that\n # rejects them — e.g. an upstream that returns \"502 Upstream access\n # forbidden\" for the SDK default User-Agent but accepts a plain one.\n # Applies on the OpenAI wire only (not native Anthropic / Bedrock).\n #\n # default_headers:\n # User-Agent: \"curl/8.7.1\"\n #\n # extra_headers: accepted as an alias of default_headers (merged, with\n # extra_headers winning when both are set) — matches the per-provider\n # extra_headers key below.\n #\n # Per-provider variant: named providers / custom_providers entries accept an\n # extra_headers dict scoped to that endpoint only — for reverse proxies,\n # gateways, or custom auth (e.g. Cloudflare Access service tokens).\n # Merged onto SDK/provider defaults with the entry's values winning.\n # Header values are treated as secrets and are never logged.\n #\n # providers:\n # my-proxy:\n # base_url: \"https://llm.internal.example.com/v1\"\n # key_env: \"MY_PROXY_API_KEY\"\n # extra_headers:\n # CF-Access-Client-Id: \"xxxx.access\"\n # CF-Access-Client-Secret: \"${CF_ACCESS_SECRET}\"\n # X-Client-Name: \"hermes-agent\"\n\n# Named provider overrides (optional)\n# Use this for per-provider request timeouts, non-stream stale timeouts,\n# and per-model exceptions.\n# Applies to the primary turn client on every api_mode (OpenAI-wire, native\n# Anthropic, and Anthropic-compatible providers), the fallback chain, and\n# client rebuilds during credential rotation. For OpenAI-wire chat\n# completions (streaming and non-streaming) the configured value is also\n# used as the per-request ``timeout=`` kwarg so it wins over the legacy\n# HERMES_API_TIMEOUT env var (which still applies when no config is set).\n# ``stale_timeout_seconds`` controls the non-streaming stale-call detector and\n# wins over the legacy HERMES_API_CALL_STALE_TIMEOUT env var. Leaving these\n# unset keeps the legacy defaults (HERMES_API_TIMEOUT=1800s,\n# HERMES_API_CALL_STALE_TIMEOUT=90s, native Anthropic 900s). The\n# implicit non-stream stale detector is auto-disabled for local endpoints\n# and can scale upward for very large contexts.\n#\n# Not currently wired for AWS Bedrock (bedrock_converse + AnthropicBedrock\n# SDK paths) — those use boto3 with its own timeout configuration.\n#\n# providers:\n# ollama-local:\n# request_timeout_seconds: 300 # Longer timeout for local cold-starts\n# stale_timeout_seconds: 900 # Explicitly re-enable stale detection on local endpoints\n# anthropic:\n# request_timeout_seconds: 30 # Fast-fail cloud requests\n# models:\n# claude-opus-4.6:\n# timeout_seconds: 600 # Longer timeout for extended-thinking Opus calls\n# openai-codex:\n# models:\n# gpt-5.4:\n# stale_timeout_seconds: 1800 # Longer non-stream stale timeout for slow large-context turns\n\n# =============================================================================\n# OpenRouter Provider Routing (only applies when using OpenRouter)\n# =============================================================================\n# Control how requests are routed across providers on OpenRouter.\n# See: https://openrouter.ai/docs/guides/routing/provider-selection\n#\n# provider_routing:\n# # Sort strategy: \"price\" (default), \"throughput\", or \"latency\"\n# # Append :nitro to model name for a shortcut to throughput sorting.\n# sort: \"throughput\"\n#\n# # Only allow these providers (provider slugs from OpenRouter)\n# # only: [\"anthropic\", \"google\"]\n#\n# # Skip these providers entirely\n# # ignore: [\"deepinfra\", \"fireworks\"]\n#\n# # Try providers in this order (overrides default load balancing)\n# # order: [\"anthropic\", \"google\", \"together\"]\n#\n# # Require providers to support all parameters in your request\n# # require_parameters: true\n#\n# # Data policy: \"allow\" (default) or \"deny\" to exclude providers that may store data\n# # data_collection: \"deny\"\n\n# =============================================================================\n# OpenRouter Response Caching (only applies when using OpenRouter)\n# =============================================================================\n# Cache identical API responses at the OpenRouter edge for free instant replays.\n# When enabled, identical requests (same model, messages, parameters) return\n# cached responses with zero billing. Separate from Anthropic prompt caching.\n# See: https://openrouter.ai/docs/guides/features/response-caching\n#\n# openrouter:\n# response_cache: true # Enable response caching (default: true)\n# response_cache_ttl: 300 # Cache TTL in seconds, 1-86400 (default: 300)\n\n# =============================================================================\n# Git Worktree Isolation\n# =============================================================================\n# When enabled, each CLI session creates an isolated git worktree so multiple\n# agents can work on the same repo concurrently without file collisions.\n# Equivalent to always passing --worktree / -w on the command line.\n#\n# worktree: true # Always create a worktree when in a git repo\n# worktree: false # Default — only create when -w flag is passed\n#\n# By default a new worktree branches from the freshly-fetched remote tip\n# (the current branch's upstream, else the remote's default branch) so it\n# starts current with the project instead of from the local clone's\n# (possibly stale) HEAD. Set worktree_sync: false to branch from local HEAD\n# instead — useful when offline or when you deliberately want the clone's\n# exact current state as the base.\n#\n# worktree_sync: true # Default — branch from the fetched remote tip\n# worktree_sync: false # Branch from local HEAD (offline / pinned base)\n\n# =============================================================================\n# Terminal Tool Configuration\n# =============================================================================\n# Choose ONE of the following terminal configurations by uncommenting it.\n# The terminal tool executes commands in the specified environment.\n\n# -----------------------------------------------------------------------------\n# OPTION 1: Local execution (default)\n# Commands run directly on your machine in the current directory\n# -----------------------------------------------------------------------------\n# Working directory behavior:\n# - CLI (`hermes` command): Uses \".\" (current directory where you run hermes)\n# - Gateway/messaging/cron: Uses terminal.cwd here; legacy .env cwd values are deprecated\nterminal:\n backend: \"local\"\n cwd: \".\" # For local backend: \".\" = current directory. Ignored for remote backends unless a backend documents otherwise.\n timeout: 180\n # HOME policy for tool subprocesses:\n # auto - default: host uses your real HOME; containers use HERMES_HOME/home\n # real - force your real OS-user HOME\n # profile - force HERMES_HOME/home for strict per-profile CLI config isolation\n home_mode: \"auto\"\n docker_mount_cwd_to_workspace: false # SECURITY: off by default. Opt in to mount the launch cwd into Docker /workspace.\n lifetime_seconds: 300\n # sudo_password: \"hunter2\" # Optional: pipe a sudo password via sudo -S. SECURITY WARNING: plaintext.\n # sudo_password: \"\" # Explicit empty password: try empty and never open the interactive sudo prompt.\n\n# -----------------------------------------------------------------------------\n# OPTION 2: SSH remote execution\n# Commands run on a remote server - agent code stays local (sandboxed)\n# Great for: keeping agent isolated from its own code, using powerful remote hardware\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"ssh\"\n# cwd: \"/home/myuser/project\" # Path on the REMOTE server\n# timeout: 180\n# lifetime_seconds: 300\n# ssh_host: \"my-server.example.com\"\n# ssh_user: \"myuser\"\n# ssh_port: 22\n# ssh_key: \"~/.ssh/id_rsa\" # Optional - uses ssh-agent if not specified\n\n# -----------------------------------------------------------------------------\n# OPTION 3: Docker container\n# Commands run in an isolated Docker container\n# Great for: reproducible environments, testing, isolation\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"docker\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /)\n# timeout: 180\n# lifetime_seconds: 300\n# docker_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# docker_mount_cwd_to_workspace: true # Explicit opt-in: mount your launch cwd into /workspace\n# # Optional: run the container as your host user's uid:gid so files written\n# # into bind-mounted dirs are owned by you, not root. Drops SETUID/SETGID\n# # caps too since no gosu privilege drop is needed. Leave off if your\n# # chosen docker_image expects to start as root.\n# docker_run_as_host_user: true\n# # Optional: explicitly forward selected env vars into Docker.\n# # These values come from your current shell first, then ~/.hermes/.env.\n# # Warning: anything forwarded here is visible to commands run in the container.\n# docker_forward_env:\n# - \"GITHUB_TOKEN\"\n# - \"NPM_TOKEN\"\n# # Optional: extra flags passed verbatim to docker run (appended after security defaults).\n# # Useful for adding capabilities (e.g. apt installs needing SETUID) or custom options.\n# # Example: add a Linux capability not included by default\n# # docker_extra_args:\n# # - \"--cap-add\"\n# # - \"SETUID\"\n\n# -----------------------------------------------------------------------------\n# OPTION 4: Singularity/Apptainer container\n# Commands run in a Singularity container (common in HPC environments)\n# Great for: HPC clusters, shared compute environments\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"singularity\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# singularity_image: \"docker://nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 5: Modal cloud execution\n# Commands run on Modal's cloud infrastructure\n# Great for: GPU access, scalable compute, serverless execution\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"modal\"\n# cwd: \"/workspace\" # Path INSIDE the sandbox (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# modal_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 6: Daytona cloud execution\n# Commands run in Daytona cloud sandboxes\n# Great for: Cloud dev environments, persistent workspaces, team collaboration\n# Requires: pip install daytona, DAYTONA_API_KEY env var\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"daytona\"\n# cwd: \"~\"\n# timeout: 180\n# lifetime_seconds: 300\n# daytona_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# container_disk: 10240 # Daytona max is 10GB per sandbox\n\n#\n# --- Container resource limits (docker, singularity, modal, daytona -- ignored for local/ssh) ---\n# These settings apply to all container backends. They control the resources\n# allocated to the sandbox and whether its filesystem persists across sessions.\n container_cpu: 1 # CPU cores\n container_memory: 5120 # Memory in MB (5120 = 5GB)\n container_disk: 51200 # Disk in MB (51200 = 50GB)\n container_persistent: true # Persist filesystem across sessions (false = ephemeral)\n\n# -----------------------------------------------------------------------------\n# SUDO SUPPORT (works with ALL backends above)\n# -----------------------------------------------------------------------------\n# Add sudo_password to any terminal config above to enable sudo commands.\n# The password is piped via `sudo -S`. Works with local, ssh, docker, etc.\n#\n# SECURITY WARNING: Password stored in plaintext!\n#\n# INTERACTIVE PROMPT: If sudo_password is unset and the CLI is running,\n# you'll be prompted to enter your password when sudo is needed:\n# - 45-second timeout (auto-skips if no input)\n# - Press Enter to skip (command fails gracefully)\n# - Password is hidden while typing\n# - Password is cached for the session\n#\n# EMPTY PASSWORDS: Setting sudo_password to an explicit empty string is different\n# from leaving it unset. Hermes will try an empty password via `sudo -S` and\n# will not open the interactive prompt. This is useful for passwordless sudo,\n# Touch ID sudo setups, and environments where prompting is just noise.\n#\n# ALTERNATIVES:\n# - SSH backend: Configure passwordless sudo on the remote server\n# - Containers: Run as root inside the container (no sudo needed)\n# - Local: Configure /etc/sudoers for specific commands\n#\n# Example (add to your terminal section):\n# sudo_password: \"your-password-here\"\n\n# =============================================================================\n# Security Scanning (tirith)\n# =============================================================================\n# Optional pre-exec command security scanning via tirith.\n# Detects homograph URLs, pipe-to-shell, terminal injection, env manipulation.\n# Install: brew install sheeki03/tap/tirith\n# Docs: https://github.com/sheeki03/tirith\n#\n# security:\n# tirith_enabled: true # Enable/disable tirith scanning\n# tirith_path: \"tirith\" # Path to tirith binary (supports ~ expansion)\n# tirith_timeout: 5 # Scan timeout in seconds\n# tirith_fail_open: true # Allow commands if tirith unavailable\n\n# =============================================================================\n# Browser Tool Configuration\n# =============================================================================\nbrowser:\n # Inactivity timeout in seconds - browser sessions are automatically closed\n # after this period of no activity between agent loops (default: 120 = 2 minutes)\n inactivity_timeout: 120\n\n# =============================================================================\n# Tool Loop Guardrails\n# =============================================================================\n# Soft warnings are enabled by default. They append guidance to repeated failed\n# or non-progressing tool results but still let the tool execute. Hard stops are\n# opt-in circuit breakers for autonomous/cron sessions where stopping a loop is\n# preferable to spending the full iteration budget.\ntool_loop_guardrails:\n warnings_enabled: true\n hard_stop_enabled: false\n warn_after:\n exact_failure: 2\n same_tool_failure: 3\n idempotent_no_progress: 2\n hard_stop_after:\n exact_failure: 5\n same_tool_failure: 8\n idempotent_no_progress: 5\n\n# =============================================================================\n# Context Compression (Auto-shrinks long conversations)\n# =============================================================================\n# When conversation approaches model's context limit, middle turns are\n# automatically summarized to free up space while preserving important context.\n#\n# HOW IT WORKS:\n# 1. Tracks actual token usage from API responses (not estimates)\n# 2. When prompt_tokens >= threshold% of model's context_length, triggers compression\n# 3. Protects first 3 turns (system prompt, initial request, first response)\n# 4. Protects last N turns (default 20 messages = ~10 full turns of recent context)\n# 5. Summarizes middle turns using a fast/cheap model\n# 6. Inserts summary as a user message, continues conversation seamlessly\n#\n# Post-compression tail budget is target_ratio × threshold × context_length:\n# 200K context, threshold 0.50, ratio 0.20 → 20K tokens of recent tail preserved\n# 1M context, threshold 0.50, ratio 0.20 → 100K tokens of recent tail preserved\n#\ncompression:\n # Enable automatic context compression (default: true)\n # Set to false if you prefer to manage context manually or want errors on overflow\n enabled: true\n\n # Opt-in compression progress notices on chat platforms (default: false).\n # By design, routine automatic compression is SILENT on human-facing chat\n # gateways (Telegram, Discord, Slack, ...) — it happens in the background\n # with server-side logging only. Set true to also deliver the routine\n # progress statuses (compacting started, preflight/pre-API compression,\n # idle compaction, retry progress, and the compaction-complete notice) to\n # chat platforms. Unrelated operational noise (auxiliary model failures,\n # provider retry chatter) stays suppressed either way, and compression\n # FAILURE notices + manual /compress feedback are always visible\n # regardless of this setting. (#52995)\n progress_notices: false\n \n # Trigger compression at this % of model's context limit (default: 0.50 = 50%)\n # Lower values = more aggressive compression, higher values = compress later\n # Models with context windows below 512K are floored at 0.75 (raise-only) so\n # compaction doesn't fire with half the window still free; set above 0.75 to override.\n threshold: 0.50\n\n # Per-model threshold overrides: keys are substring-matched against the model\n # name (longest match wins). Useful when some models need different compaction\n # points — e.g. a 1M-context model can compress later (0.30) while a 128K\n # model needs to compress earlier (0.60). The small-context floor (75% for\n # <512K models) still applies on top of per-model overrides.\n # model_thresholds:\n # \"glm-5.2\": 0.40\n # \"claude-sonnet\": 0.35\n # \"gpt-5\": 0.30\n\n # Optional absolute token cap for the compression trigger (default: null = disabled).\n # When set, compression fires at the LOWER of the ratio-based threshold and this\n # absolute token count — first-fires-wins. It never fires later than this count\n # regardless of which model is active (useful when switching between models with\n # very different context windows). Clamped to the model's context length at\n # apply-time, so a cap above the window is a no-op (ratio-based threshold wins).\n # Survives model switches and fallback activations.\n # threshold_tokens: 200000\n\n # Existing Codex gpt-5.5 behavior: raise Hermes' compaction trigger to 85%\n # for the ChatGPT Codex OAuth route. Set false to opt back down to threshold.\n codex_gpt55_autoraise: true\n\n # Fraction of the threshold to preserve as recent tail (default: 0.20 = 20%)\n # e.g. 20% of 50% threshold = 10% of total context kept as recent messages.\n # Summary output is separately capped at 12K tokens (Gemini output limit).\n # Range: 0.10 - 0.80\n target_ratio: 0.20\n\n # Number of most-recent messages to always preserve (default: 20 ≈ 10 full turns)\n # Higher values keep more recent conversation intact at the cost of more aggressive\n # compression of older turns.\n protect_last_n: 20\n\n # Minimum number of REAL (actionable) user messages guaranteed to survive in\n # the uncompressed tail (default: 1 = the existing single last-user anchor,\n # behavior-preserving). Raise to e.g. 3 to keep the last 3 real user turns\n # verbatim even when bulky tool outputs fill the tail token budget — blank\n # platform echoes, compaction handoffs, and synthetic continuation rows never\n # count toward N. The tail can exceed the token budget when this pulls the\n # cut back; the guarantee wins over the budget by design.\n min_tail_user_messages: 1\n\n # Compression retry rounds before a turn gives up with \"max compression\n # attempts reached\" (default: 3, same as the previous hardcoded value).\n # Raise (e.g. 6) for tool-schema-heavy sessions where 3 rounds cannot bring\n # the request estimate under the threshold. Validated >= 1, hard cap 10.\n max_attempts: 3\n\n # Codex app-server (codex CLI runtime) thread-compaction mode. The codex\n # agent owns the real thread context on this runtime, so Hermes' summarizer\n # cannot shrink it — compaction goes through the app server instead.\n # native = let Codex decide when to compact its own thread (default)\n # hermes = let Hermes threshold trigger Codex thread/compact/start\n # off = Hermes will not auto-trigger compaction; Codex may still compact natively\n codex_app_server_auto: native\n\n # Number of non-system messages to protect at the head of the transcript, in\n # ADDITION to the system prompt (which is always implicitly protected).\n # Head messages are NEVER summarized — they survive every compression\n # indefinitely. This gives stable early context for short/medium sessions,\n # but in long-running sessions that rely on rolling compaction the pinned\n # opening turns may not match how you want the session framed over time.\n # Set to 0 to preserve ONLY the system prompt (plus the rolling summary\n # and recent tail) — the cleanest configuration for long-running sessions.\n # Default 3 preserves the system prompt plus the first three non-system\n # head messages, matching the pre-feature behaviour.\n protect_first_n: 3\n\n # Idle compaction (default: 0 = disabled). When > 0, a session that resumes\n # after at least this many seconds of inactivity compacts its accumulated\n # history up front, before the first reply, so a long-lived thread you come\n # back to later doesn't re-read its full stale context on every turn.\n # Time-based, so it complements (does not replace) the size-based `threshold`\n # above. It is skipped when the context is already small (at or below the\n # post-compression target = threshold × target_ratio), so it never wastes a\n # summarization on a short idle thread. Example: 1800 = compact after 30 min idle.\n idle_compact_after_seconds: 0\n\n # Proactive tool-result prune (default: 0 = disabled). Opt-in token trigger\n # for a deterministic, no-LLM prune of OLD tool-result payloads, run\n # independently of `threshold` above. On large-window models (512K/1M) the\n # ratio threshold rarely fires, so bulky tool outputs (terminal dumps, file\n # reads, web extracts) ride along in history and get re-billed every turn.\n # When re-sent history exceeds this many tokens, the prune dedupes identical\n # results, summarizes older oversized ones, and truncates large tool-call\n # arguments — protecting the most recent `protect_last_n` messages and never\n # calling the model. Try 48000 to enable. Built-in compressor engine only;\n # other context engines inherit a safe no-op.\n # NOTE: a committed prune rewrites already-sent history, which invalidates\n # the provider's prompt-cache prefix — the min_reclaim gate below keeps\n # those cache breaks episodic (like a compression boundary) instead of\n # per-turn.\n proactive_prune_tokens: 0\n\n # The prune's summarize pass only touches tool results larger than this many\n # characters (clamped to >= 200 so a generated summary can't be\n # re-summarized). Default 8000.\n proactive_prune_min_result_chars: 8000\n\n # A proactive prune only COMMITS when it reclaims at least this many tokens\n # (measured on the pruned output). This is the prompt-cache hysteresis gate:\n # one meaningful, amortized cache break per batch of stale tool output\n # instead of a tiny break on every tool iteration. 0 = commit any non-zero\n # prune. Default 4096.\n proactive_prune_min_reclaim_tokens: 4096\n\n # To pin a specific model/provider for compression summaries, use the\n # auxiliary section below (auxiliary.compression.provider / model).\n\n# =============================================================================\n# Anthropic prompt caching TTL\n# =============================================================================\n# When prompt caching is active (Claude via OpenRouter or native Anthropic),\n# Anthropic supports two TTL tiers for cached prefixes: \"5m\" (default) and\n# \"1h\". Other values are ignored and \"5m\" is used.\n#\nprompt_caching:\n cache_ttl: \"5m\" # use \"1h\" for long sessions with pauses between turns\n\n# =============================================================================\n# Auxiliary Models (Advanced — Experimental)\n# =============================================================================\n# Hermes uses lightweight \"auxiliary\" models for side tasks: image analysis,\n# browser screenshot analysis, web page summarization, TTS audio-tag insertion,\n# and context compression.\n#\n# By default these use Gemini Flash via OpenRouter or Nous Portal and are\n# auto-detected from your credentials. You do NOT need to change anything\n# here for normal usage.\n#\n# WARNING: Overriding these with providers other than OpenRouter or Nous Portal\n# is EXPERIMENTAL and may not work. Not all models/providers support vision,\n# produce usable summaries, or accept the same API format. Change at your own\n# risk — if things break, reset to \"auto\" / empty values.\n#\n# Each task has its own provider + model pair so you can mix providers.\n# For example: OpenRouter for vision (needs multimodal), but your main\n# local endpoint for compression (just needs text).\n#\n# Provider options:\n# \"auto\" - Best available: OpenRouter → Nous Portal → main endpoint (default)\n# \"openrouter\" - Force OpenRouter (requires OPENROUTER_API_KEY)\n# \"nous\" - Force Nous Portal (requires: hermes auth add nous)\n# \"gemini\" - Force Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n# \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY)\n# \"codex\" - Force Codex OAuth (requires: hermes model → Codex).\n# Uses gpt-5.3-codex which supports vision.\n# \"main\" - Use your custom endpoint (OPENAI_BASE_URL + OPENAI_API_KEY).\n# Works with OpenAI API, local models, or any OpenAI-compatible\n# endpoint. Also falls back to Codex OAuth and API-key providers.\n#\n# Model: leave empty to use the provider's default. When empty, OpenRouter\n# uses \"google/gemini-3-flash-preview\" and Nous uses \"gemini-3-flash\".\n# Other providers pick a sensible default automatically.\n#\n# auxiliary:\n# # Image analysis: vision_analyze tool + browser screenshots\n# vision:\n# provider: \"auto\"\n# model: \"\" # e.g. \"google/gemini-2.5-flash\", \"openai/gpt-4o\"\n# timeout: 30 # LLM API call timeout (seconds)\n# download_timeout: 30 # Image HTTP download timeout (seconds)\n# # Increase for slow connections or self-hosted image servers\n# reasoning_effort: \"\" # Per-task thinking level: none, minimal, low, medium,\n# # high, xhigh, max, ultra. Empty = provider default.\n# # Works on every auxiliary task block (vision,\n# # web_extract, compression, title_generation, curator,\n# # background_review, moa_reference, ...). Example: run\n# # compression at \"low\" and vision at \"none\" to cut\n# # side-task latency/cost on reasoning models.\n#\n# # Web page scraping / summarization + browser page text extraction\n# web_extract:\n# provider: \"auto\"\n# model: \"\"\n# reasoning_effort: \"low\"\n#\n# # Gemini 3.1 TTS hidden audio-tag insertion\n# tts_audio_tags:\n# provider: \"auto\" # empty model = your main chat model\n# model: \"\"\n# timeout: 30\n#\n# # Automatic session title generation after the first exchange\n# title_generation:\n# enabled: true # set false to disable auto-title generation\n# provider: \"auto\"\n# model: \"\"\n# timeout: 30\n# language: \"\" # empty = match the user's language; or e.g. \"English\"\n#\n# # Session search — summarizes matching past sessions\n# session_search:\n# provider: \"auto\"\n# model: \"\"\n# timeout: 30\n# max_concurrency: 3 # Limit parallel summaries to reduce request-burst 429s\n# extra_body: {} # Provider-specific OpenAI-compatible request fields\n# # Example for providers that support request-body\n# # reasoning controls:\n# # extra_body:\n# # enable_thinking: false\n# # Some vLLM/Qwen deployments expect this nested:\n# # extra_body:\n# # chat_template_kwargs:\n# # enable_thinking: false\n\n# =============================================================================\n# Persistent Memory\n# =============================================================================\n# Bounded curated memory injected into the system prompt every session.\n# Two stores: MEMORY.md (agent's notes) and USER.md (user profile).\n# Character limits keep the memory small and focused. The agent manages\n# pruning -- when at the limit, it must consolidate or replace entries.\n# Disabled by default in batch_runner.\n#\nmemory:\n # Agent's personal notes: environment facts, conventions, things learned\n memory_enabled: true\n \n # User profile: preferences, communication style, expectations\n user_profile_enabled: true\n \n # Character limits (~2.75 chars per token, model-independent)\n memory_char_limit: 2200 # ~800 tokens\n user_char_limit: 1375 # ~500 tokens\n\n # Periodic memory nudge: remind the agent to consider saving memories\n # every N user turns. Set to 0 to disable. Only active when memory is enabled.\n nudge_interval: 10 # Nudge every 10 user turns (0 = disabled)\n\n # Memory flush: give the agent one turn to save memories before context is\n # lost (compression, /new, /reset, exit). Set to 0 to disable.\n # For exit/reset, only fires if the session had at least this many user turns.\n flush_min_turns: 6 # Min user turns to trigger flush on exit/reset (0 = disabled)\n\n# =============================================================================\n# Session Reset Policy (Messaging Platforms)\n# =============================================================================\n# Controls when messaging sessions (Telegram, Discord, WhatsApp, Slack) are\n# automatically cleared. Default is \"none\": sessions never auto-reset —\n# conversation context lives until you /reset or /new manually, or context\n# compression kicks in. Opt in to automatic resets if you prefer sessions to\n# clear on a schedule (long-lived context increases API cost per message,\n# though prompt caching and compression keep this manageable).\n#\n# When an automatic reset triggers, the agent first saves important\n# information to its persistent memory — but the conversation context is\n# wiped. The agent starts fresh but retains learned facts via its memory\n# system.\n#\n# Modes:\n# \"none\" - Never auto-reset (default); context lives until /reset or compression\n# \"idle\" - Reset after N minutes of inactivity\n# \"daily\" - Reset at a fixed hour each day\n# \"both\" - Reset on EITHER inactivity timeout or daily boundary\n#\nsession_reset:\n mode: none # \"none\", \"idle\", \"daily\", or \"both\"\n idle_minutes: 1440 # Inactivity timeout in minutes (used by \"idle\"/\"both\")\n at_hour: 4 # Daily reset hour, 0-23 local time (used by \"daily\"/\"both\")\n\n# Maximum number of simultaneously active chat sessions across CLI, TUI,\n# dashboard chat, and messaging gateway. Set to null, 0, or omit to allow\n# unlimited concurrent sessions. When the limit is reached, new sessions get a\n# clean error while existing active sessions keep their normal behavior. This\n# top-level key takes precedence over gateway.max_concurrent_sessions. The cap\n# is a best-effort single-host/profile runtime guard; Hermes fails open if the\n# local runtime lease registry cannot be read or locked.\nmax_concurrent_sessions: null\n\n# When true, group/channel chats use one session per participant when the platform\n# provides a user ID. This is the secure default and prevents users in the same\n# room from sharing context, interrupts, and token costs. Set false only if you\n# explicitly want one shared \"room brain\" per group/channel.\ngroup_sessions_per_user: true\n\n# ─────────────────────────────────────────────────────────────────────────────\n# API Server — per-client model routing\n# ─────────────────────────────────────────────────────────────────────────────\n# Route different API clients to different models/providers on a single\n# Hermes deployment. Clients choose a backend by sending a specific string\n# as the OpenAI ``model`` field. Unmapped model values fall back to the\n# global model configured in the ``model:`` section above, and an explicit\n# session /model override always wins over a route.\n#\n# Configure via the ``platforms.api_server.extra.model_routes`` gateway\n# config block:\n#\n# platforms:\n# api_server:\n# enabled: true\n# extra:\n# key: \"your-api-server-secret\"\n# model_routes:\n# # Xiaozhi clients send model=\"minimax-m2\" → routed to MiniMax via OpenRouter\n# minimax-m2:\n# model: \"minimax/minimax-m1\"\n# provider: \"openrouter\" # optional — overrides global provider\n# # api_key: \"sk-...\" # optional — per-route UPSTREAM provider\n# # key (NOT caller auth; never logged)\n# # base_url: \"https://...\" # optional — per-route base URL\n# # GPT clients keep their own alias\n# gpt-5:\n# model: \"openai/gpt-5\"\n# provider: \"openrouter\"\n#\n# Configured aliases are automatically listed by GET /v1/models so clients\n# can discover them without manual coordination. Caller authentication is\n# unchanged: every request still authenticates with the global API server\n# key (``extra.key`` / API_SERVER_KEY).\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Gateway Streaming\n# ─────────────────────────────────────────────────────────────────────────────\n# Stream tokens to messaging platforms in real-time. The bot sends a message\n# on first token, then progressively edits it as more tokens arrive.\n# Disabled by default — enable to try the streaming UX on Telegram/Discord/Slack.\n# For Telegram, partial edits are sent as plain text and only the final edit uses MarkdownV2.\nstreaming:\n enabled: false\n # transport: edit # \"edit\" = progressive editMessageText\n # edit_interval: 0.3 # seconds between message edits\n # buffer_threshold: 40 # chars before forcing an edit flush\n # cursor: \" ▉\" # cursor shown during streaming\n\n# =============================================================================\n# Skills Configuration\n# =============================================================================\n# Skills are reusable procedures the agent can load and follow. The agent can\n# also create new skills after completing complex tasks.\n#\nskills:\n # Nudge the agent to create skills after complex tasks.\n # Every N tool-calling iterations, remind the model to consider saving a skill.\n # Set to 0 to disable.\n creation_nudge_interval: 15\n\n # External skill directories — share skills across tools/agents without\n # copying them into ~/.hermes/skills/. Each path is expanded (~ and ${VAR})\n # and resolved to an absolute path. External dirs are read-only: skill\n # creation always writes to ~/.hermes/skills/. Local skills take precedence\n # when names collide.\n # external_dirs:\n # - ~/.agents/skills\n # - /home/shared/team-skills\n\n# =============================================================================\n# Agent Behavior\n# =============================================================================\nagent:\n # Maximum tool-calling iterations per conversation (default: 500)\n # Higher = more room for complex tasks, but costs more tokens\n # Recommended: 20-30 for focused tasks, 50-100 for open exploration\n max_turns: 500\n\n # Inactivity timeout for gateway agent runs (seconds, 0 = unlimited).\n # The agent can run indefinitely when actively calling tools or receiving\n # API responses. Only fires after the agent has been idle for this duration.\n # gateway_timeout: 1800\n\n # Staged warning: send a warning before escalating to full timeout.\n # Fires once per run when inactivity reaches this threshold (seconds).\n # Set to 0 to disable the warning.\n # gateway_timeout_warning: 900\n\n # Graceful drain timeout for gateway stop/restart (seconds).\n # Default 0 = no drain: a restart interrupts in-flight agents immediately,\n # cleans up, and exits. Set a positive value only if you want a grace\n # window on /restart, and keep it well under systemd's TimeoutStopSec.\n # restart_drain_timeout: 0\n\n # Upper bound (seconds) a submitted prompt waits for the deferred agent\n # build (MCP discovery, model metadata, skills scan) before failing with a\n # visible error. The wait is patient — the message is delivered as soon as\n # the build completes, and a progress notice is shown past 30s — so this cap\n # only fires on a genuinely hung build. Raise it for deployments with many\n # slow or unreachable MCP servers. Default 600.\n # build_wait_timeout: 600\n\n # Max app-level retry attempts for API errors (connection drops, provider\n # timeouts, 5xx, etc.) before the agent surfaces the failure. Lower this\n # to 1 if you use fallback providers and want fast failover on flaky\n # primaries (default 3). The OpenAI SDK does its own low-level retries\n # underneath this wrapper — this is the Hermes-level loop.\n # api_max_retries: 3\n\n # After the agent edits code without fresh passing verification, nudge it to\n # verify before finishing. The default \"auto\" enables it on interactive\n # coding surfaces (CLI, TUI, desktop) and programmatic callers, and disables\n # it on conversational messaging surfaces (Telegram, Discord, etc.) where the\n # verification summary would reach a human as chat noise. Set true or false to\n # force it on or off; the HERMES_VERIFY_ON_STOP env var (1/0) takes precedence.\n # verify_on_stop: auto\n\n # Standing operator instructions for the coding posture (when Hermes is in a\n # code workspace). Appended to the coding brief as an extra system block, so\n # you can pin project-wide workflow rules without editing the shipped brief.\n # Accepts a string or a list of strings. Takes effect next session.\n # coding_instructions:\n # - \"For UI work, don't run tsc/lint until I approve the look.\"\n # - \"Clean the diff before you commit and push.\"\n\n # When verify-on-stop finds edited code without fresh verification evidence,\n # append guidance for creative UI work (avoid broad tsc/lint/test before visual\n # approval) and clean-diff expectations. Set false to keep that nudge terse.\n # verify_guidance: true\n\n # A `pre_verify` hook (plugin or shell, see Event Hooks docs) can keep the\n # agent going one more turn to verify/clean before finishing. This caps how\n # many times one turn may be nudged to continue, so a hook can't trap the loop.\n # Default 3.\n # max_verify_nudges: 3\n\n # Enable verbose logging\n verbose: false\n \n # Reasoning effort level (OpenRouter and Nous Portal)\n # Controls how much \"thinking\" the model does before responding.\n # Options: \"xhigh\" (max), \"high\", \"medium\", \"low\", \"minimal\", \"none\" (disable)\n reasoning_effort: \"medium\"\n \n # Per-model reasoning effort overrides (optional dict)\n # Key: any sensible model spelling works (exact, dots↔dashes interchangeable,\n # provider prefix optional). First match wins.\n # Value: reasoning effort level (same options as reasoning_effort)\n # Override the global reasoning_effort for that specific model.\n # NOTE: no `hermes config set` support for this key -- edit YAML directly.\n # reasoning_overrides:\n # \"openrouter/anthropic/claude-opus-4.5\": \"xhigh\"\n # \"openai/gpt-5\": \"low\"\n # \"claude-opus-4.6\": \"high\" # bare model name also works\n # \"deepseek/deepseek-v4-pro\": \"xhigh\" # dots and dashes are interchangeable\n reasoning_overrides: {}\n \n # Predefined personalities (use with /personality command)\n personalities:\n helpful: \"You are a helpful, friendly AI assistant.\"\n concise: \"You are a concise assistant. Keep responses brief and to the point.\"\n technical: \"You are a technical expert. Provide detailed, accurate technical information.\"\n creative: \"You are a creative assistant. Think outside the box and offer innovative solutions.\"\n teacher: \"You are a patient teacher. Explain concepts clearly with examples.\"\n kawaii: \"You are a kawaii assistant! Use cute expressions like (◕‿◕), ★, ♪, and ~! Add sparkles and be super enthusiastic about everything! Every response should feel warm and adorable desu~! ヽ(>∀<☆)ノ\"\n catgirl: \"You are Neko-chan, an anime catgirl AI assistant, nya~! Add 'nya' and cat-like expressions to your speech. Use kaomoji like (=^・ω・^=) and ฅ^•ﻌ•^ฅ. Be playful and curious like a cat, nya~!\"\n pirate: \"Arrr! Ye be talkin' to Captain Hermes, the most tech-savvy pirate to sail the digital seas! Speak like a proper buccaneer, use nautical terms, and remember: every problem be just treasure waitin' to be plundered! Yo ho ho!\"\n shakespeare: \"Hark! Thou speakest with an assistant most versed in the bardic arts. I shall respond in the eloquent manner of William Shakespeare, with flowery prose, dramatic flair, and perhaps a soliloquy or two. What light through yonder terminal breaks?\"\n surfer: \"Duuude! You're chatting with the chillest AI on the web, bro! Everything's gonna be totally rad. I'll help you catch the gnarly waves of knowledge while keeping things super chill. Cowabunga! 🤙\"\n noir: \"The rain hammered against the terminal like regrets on a guilty conscience. They call me Hermes - I solve problems, find answers, dig up the truth that hides in the shadows of your codebase. In this city of silicon and secrets, everyone's got something to hide. What's your story, pal?\"\n uwu: \"hewwo! i'm your fwiendwy assistant uwu~ i wiww twy my best to hewp you! *nuzzles your code* OwO what's this? wet me take a wook! i pwomise to be vewy hewpful >w<\"\n philosopher: \"Greetings, seeker of wisdom. I am an assistant who contemplates the deeper meaning behind every query. Let us examine not just the 'how' but the 'why' of your questions. Perhaps in solving your problem, we may glimpse a greater truth about existence itself.\"\n hype: \"YOOO LET'S GOOOO!!! 🔥🔥🔥 I am SO PUMPED to help you today! Every question is AMAZING and we're gonna CRUSH IT together! This is gonna be LEGENDARY! ARE YOU READY?! LET'S DO THIS! 💪😤🚀\"\n\n# =============================================================================\n# Toolsets\n# =============================================================================\n# Control which tools the agent has access to.\n# Use `hermes tools` to interactively enable/disable tools per platform.\n\n# =============================================================================\n# Platform Toolsets (per-platform tool configuration)\n# =============================================================================\n# Override which toolsets are available on each platform.\n# If a platform isn't listed here, its built-in default is used.\n#\n# You can use EITHER:\n# - A preset like \"hermes-cli\" or \"hermes-telegram\" (curated tool set)\n# - A list of individual toolsets to compose your own (see list below)\n#\n# Supported platform keys: cli, telegram, discord, whatsapp, slack, qqbot, teams, google_chat\n#\n# Examples:\n#\n# # Use presets (same as defaults):\n# platform_toolsets:\n# cli: [hermes-cli]\n# telegram: [hermes-telegram]\n#\n# # Custom: give Telegram only web + terminal + file + planning:\n# platform_toolsets:\n# telegram: [web, terminal, file, todo]\n#\n# # Custom: CLI without browser or image gen:\n# platform_toolsets:\n# cli: [web, terminal, file, skills, todo, tts, cronjob]\n#\n# # Restrictive: Discord gets read-only tools only:\n# platform_toolsets:\n# discord: [web, vision, skills, todo]\n#\n# If not set, defaults are:\n# cli: hermes-cli (everything + cronjob management)\n# telegram: hermes-telegram (terminal, file, web, vision, image, tts, browser, skills, todo, cronjob, messaging)\n# discord: hermes-discord (same as telegram)\n# whatsapp: hermes-whatsapp (same as telegram)\n# slack: hermes-slack (same as telegram)\n# signal: hermes-signal (same as telegram)\n# homeassistant: hermes-homeassistant (same as telegram)\n# qqbot: hermes-qqbot (same as telegram)\n# teams: hermes-teams (same as telegram)\n# google_chat: hermes-google_chat (same as telegram)\n#\nplatform_toolsets:\n cli: [hermes-cli]\n telegram: [hermes-telegram]\n discord: [hermes-discord]\n whatsapp: [hermes-whatsapp]\n slack: [hermes-slack]\n signal: [hermes-signal]\n homeassistant: [hermes-homeassistant]\n qqbot: [hermes-qqbot]\n yuanbao: [hermes-yuanbao]\n teams: [hermes-teams]\n google_chat: [hermes-google_chat]\n\n# =============================================================================\n# Gateway Platform Settings\n# =============================================================================\n# Optional per-platform messaging settings.\n# Platform-specific knobs live under `extra`.\n#\n# platforms:\n# telegram:\n# reply_to_mode: \"first\" # off | first | all\n# # guest_mode lets explicit @mentions from non-allowlisted groups through.\n# # Default false; ordinary messages, replies, and regex wake words stay blocked.\n# guest_mode: false\n# # allowed_chats: [\"-1001234567890\"]\n# extra:\n# disable_link_previews: false # Set true to suppress Telegram URL previews in bot messages\n# 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\n# rich_drafts: false # Experimental rich draft previews during Telegram DM streaming; default false because Telegram Desktop/macOS can visually overlay draft frames\n# command_menu:\n# # Telegram allows up to 100 BotCommands; Hermes defaults to 60 so\n# # all built-in commands plus common skill commands stay visible\n# # while remaining under Telegram's payload-size limit. Clamped 1..100.\n# max_commands: 60\n# # prepend = user priority first, then Hermes defaults\n# # append = Hermes defaults first, then user priority\n# # replace = only the list below defines priority\n# priority_mode: prepend\n# priority:\n# - my_plugin_command\n# webhook:\n# extra:\n# # Route scripts default to a 30 second timeout. Scripts must live under\n# # the active profile's scripts directory and receive webhook JSON on stdin.\n# script_timeout_seconds: 30\n#\n# Discord-specific settings (config.yaml top-level, not under platforms:):\n#\n# discord:\n# require_mention: true # Require @mention in server channels (default: true)\n# auto_thread: true # Auto-create thread on @mention (default: true)\n# free_response_channels: \"\" # Channel IDs where no mention is needed\n# reactions: true # Show processing reactions (default: true)\n# history_backfill: true # Recover missed channel messages on mention (default: true)\n# history_backfill_limit: 50 # Max messages to scan backwards (default: 50)\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Available toolsets (use these names in platform_toolsets or the toolsets list)\n#\n# Run `hermes chat --list-toolsets` to see all toolsets and their tools.\n# Run `hermes chat --list-tools` to see every individual tool with descriptions.\n# ─────────────────────────────────────────────────────────────────────────────\n#\n# INDIVIDUAL TOOLSETS (compose your own):\n# web - web_search, web_extract\n# search - web_search only (no scraping)\n# terminal - terminal, process\n# file - read_file, write_file, patch, search\n# browser - browser_navigate, browser_snapshot, browser_click, browser_type,\n# browser_scroll, browser_back, browser_press,\n# browser_get_images, browser_vision (requires BROWSERBASE_API_KEY)\n# vision - vision_analyze (requires OPENROUTER_API_KEY)\n# image_gen - image_generate (requires FAL_KEY)\n# skills - skills_list, skill_view\n# skills_hub - skill_hub (search/install/manage from online registries — user-driven only)\n# todo - todo (in-memory task planning, no deps)\n# tts - text_to_speech (Edge TTS free, or ELEVENLABS/OPENAI/MINIMAX/MISTRAL key)\n# cronjob - cronjob (create/list/update/pause/resume/run/remove scheduled tasks)\n#\n# PRESETS (curated bundles):\n# hermes-cli - All of the above except send_message\n# hermes-telegram - terminal, file, web, vision, image_gen, tts, browser,\n# skills, todo, cronjob, send_message\n# hermes-discord - Same as hermes-telegram\n# hermes-whatsapp - Same as hermes-telegram\n# hermes-slack - Same as hermes-telegram\n#\n# COMPOSITE:\n# debugging - terminal + web + file\n# safe - web + vision (no terminal access)\n# all - Everything available\n#\n# web - Web search and content extraction (web_search, web_extract)\n# search - Web search only, no scraping (web_search)\n# terminal - Command execution and process management (terminal, process)\n# file - File operations: read, write, patch, search\n# browser - Full browser automation (navigate, click, type, screenshot, etc.)\n# vision - Image analysis (vision_analyze)\n# image_gen - Image generation with FLUX (image_generate)\n# skills - Load skill documents (skills_list, skill_view)\n# todo - Task planning and tracking for multi-step work\n# memory - Persistent memory across sessions (personal notes + user profile)\n# session_search - Search and recall past conversations (FTS5 + Gemini Flash summarization)\n# tts - Text-to-speech (Edge TTS free, ElevenLabs, OpenAI, MiniMax, Mistral)\n# cronjob - Schedule and manage automated tasks (CLI-only)\n#\n# Composite toolsets:\n# debugging - terminal + web + file (for troubleshooting)\n# safe - web + vision (no terminal access)\n\n# NOTE: The top-level \"toolsets\" key is deprecated and ignored.\n# Tool configuration is managed per-platform via platform_toolsets above.\n# Use `hermes tools` to configure interactively, or edit platform_toolsets directly.\n#\n# CLI override: hermes chat --toolsets terminal,web,file\n\n# =============================================================================\n# MCP (Model Context Protocol) Servers\n# =============================================================================\n# Connect to external MCP servers to add tools from the MCP ecosystem.\n# Each server's tools are automatically discovered and registered.\n# See website/docs/user-guide/features/mcp.md for full documentation.\n#\n# Stdio servers (spawn a subprocess):\n# command: the executable to run\n# args: command-line arguments\n# env: environment variables (only these + safe defaults passed to subprocess)\n#\n# HTTP servers (connect to a URL):\n# url: the MCP server endpoint\n# headers: HTTP headers (e.g., for authentication)\n#\n# Optional per-server settings:\n# timeout: tool call timeout in seconds (default: 120)\n# connect_timeout: initial connection timeout (default: 60)\n# keepalive_interval: liveness ping cadence in seconds (default: 180).\n# Lower it below the server's session TTL for servers that expire idle\n# sessions quickly (e.g. Unreal Engine editor MCP, ~15s), otherwise idle\n# tool calls hit an expired session and pay a slow reconnect. Floored at 5s.\n#\n# mcp_servers:\n# time:\n# command: uvx\n# args: [\"mcp-server-time\"]\n# filesystem:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-filesystem\", \"/home/user\"]\n# notion:\n# url: https://mcp.notion.com/mcp\n# github:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-github\"]\n# env:\n# GITHUB_PERSONAL_ACCESS_TOKEN: \"ghp_...\"\n#\n# Sampling (server-initiated LLM requests) — enabled by default.\n# Per-server config under the 'sampling' key:\n# analysis:\n# command: npx\n# args: [\"-y\", \"analysis-server\"]\n# sampling:\n# enabled: true # default: true\n# model: \"gemini-3-flash\" # override model (optional)\n# max_tokens_cap: 4096 # max tokens per request\n# timeout: 30 # LLM call timeout (seconds)\n# max_rpm: 10 # max requests per minute\n# allowed_models: [] # model whitelist (empty = all)\n# max_tool_rounds: 5 # tool loop limit (0 = disable)\n# log_level: \"info\" # audit verbosity\n\n# =============================================================================\n# Text-to-Speech\n# =============================================================================\n# TTS defaults to Edge TTS unless changed in ~/.hermes/config.yaml.\n# Gemini TTS supports persona/director prompt files, and Gemini 3.1 Flash TTS\n# can use a hidden auxiliary rewrite pass to insert expressive square-bracket\n# audio tags into the TTS script without showing tags in chat.\n#\n# tts:\n# provider: \"gemini\"\n# gemini:\n# model: \"gemini-3.1-flash-tts-preview\"\n# voice: \"Kore\"\n# audio_tags: false\n# persona_prompt_file: \"\" # e.g. ~/.hermes/tts/radio-host.md\n\n# =============================================================================\n# Voice Transcription (Speech-to-Text)\n# =============================================================================\n# Automatically transcribe voice messages on messaging platforms.\n# Providers: local (free, faster-whisper) | groq (free tier) | openai (Whisper API) | mistral (Voxtral Transcribe)\n# Set the corresponding API key in .env: GROQ_API_KEY, OPENAI_API_KEY, or MISTRAL_API_KEY.\nstt:\n enabled: true\n # provider: \"local\" # auto-detected if omitted\n local:\n model: \"base\" # tiny | base | small | medium | large-v3 | turbo\n # language: \"\" # auto-detect; set to \"en\", \"es\", \"fr\", etc. to force\n openai:\n model: \"whisper-1\" # whisper-1 | gpt-4o-mini-transcribe | gpt-4o-transcribe\n # mistral:\n # model: \"voxtral-mini-latest\" # voxtral-mini-latest | voxtral-mini-2602\n # deepinfra:\n # # Model id is discovered live from the DeepInfra catalog filtered\n # # by the `stt` surface tag — leave `model` blank to take the first\n # # live result. Pin only when you need a specific Whisper variant.\n # model: \"\"\n\n# Text-to-speech. Only the deepinfra block is documented here — the\n# remaining providers (edge, openai, xai, minimax, mistral, gemini,\n# elevenlabs, neutts, kittentts, piper) inherit sensible defaults from\n# DEFAULT_CONFIG in hermes_cli/config.py.\n# tts:\n# provider: \"deepinfra\"\n# deepinfra:\n# # Model id is discovered live from the DeepInfra catalog filtered\n# # by the `tts` surface tag — leave `model` blank to take the first\n# # live result.\n# model: \"\"\n# voice: \"default\"\n\n# Image generation. Each provider plugin reads its own ``image_gen.<name>``\n# block; deepinfra discovers models live from\n# api.deepinfra.com/v1/openai/models filtered by the ``image-gen`` tag —\n# no model id is hardcoded, so retired models disappear automatically.\n# image_gen:\n# provider: \"deepinfra\"\n# deepinfra:\n# # Leave `model` blank for the first live `image-gen`-tagged result.\n# model: \"\"\n\n# =============================================================================\n# Response Pacing (Messaging Platforms)\n# =============================================================================\n# Add human-like delays between message chunks.\n# human_delay:\n# mode: \"off\" # \"off\" | \"natural\" | \"custom\"\n# min_ms: 800 # Min delay (custom mode only)\n# max_ms: 2500 # Max delay (custom mode only)\n\n# =============================================================================\n# Session Logging\n# =============================================================================\n# Session trajectories are automatically saved to logs/ directory.\n# Each session creates: logs/session_YYYYMMDD_HHMMSS_UUID.json\n#\n# The session ID is displayed in the welcome banner for easy reference.\n# Logs contain full conversation history in trajectory format:\n# - System prompt, user messages, assistant responses\n# - Tool calls with inputs/outputs\n# - Timestamps for debugging\n#\n# No configuration needed - logging is always enabled.\n# To disable, you would need to modify the source code.\n\n# =============================================================================\n# Code Execution Sandbox (Programmatic Tool Calling)\n# =============================================================================\n# The execute_code tool runs Python scripts that call Hermes tools via RPC.\n# Intermediate tool results stay out of the LLM's context window.\ncode_execution:\n timeout: 300 # Max seconds per script before kill (default: 300 = 5 min)\n max_tool_calls: 50 # Max RPC tool calls per execution (default: 50)\n\n# =============================================================================\n# Subagent Delegation\n# =============================================================================\n# The delegate_task tool spawns child agents with isolated context.\n# Supports single tasks and batch mode (default 3 parallel, configurable).\ndelegation:\n max_iterations: 50 # Max tool-calling turns per child (default: 50)\n # max_concurrent_children: 3 # Max parallel child agents per batch (default: 3, floor: 1, no ceiling).\n # WARNING: values above 10 multiply API cost linearly.\n # max_spawn_depth: 1 # Delegation tree depth cap (range: 1-3, default: 1 = flat).\n # Raise to 2 to allow workers to spawn their own subagents.\n # Requires role=\"orchestrator\" on intermediate agents.\n # orchestrator_enabled: true # Kill switch for role=\"orchestrator\" children (default: true).\n # subagent_auto_approve: false # When a subagent hits a dangerous-command approval prompt, auto-deny (default: false)\n # or auto-approve \"once\" (true) instead of blocking on stdin.\n # The parent TUI owns stdin, so blocking would deadlock; non-interactive resolution is required.\n # Both choices emit a logger.warning audit line. Flip to true only for cron/batch pipelines.\n # inherit_mcp_toolsets: true # When explicit child toolsets are narrowed, also keep the parent's MCP toolsets (default: true). Set false for strict intersection.\n # model: \"google/gemini-3-flash-preview\" # Override model for subagents (empty = inherit parent)\n # provider: \"openrouter\" # Override provider for subagents (empty = inherit parent)\n # # Resolves full credentials (base_url, api_key) automatically.\n # # Supported: openrouter, nous, zai, kimi-coding, minimax\n\n# =============================================================================\n# Honcho Integration (Cross-Session User Modeling)\n# =============================================================================\n# AI-native persistent memory via Honcho (https://honcho.dev/).\n# Builds a deeper understanding of the user across sessions and tools.\n# Runs alongside USER.md — additive, not a replacement.\n#\n# Requires: pip install honcho-ai\n# Config: ~/.honcho/config.json (shared with Claude Code, Cursor, etc.)\n# API key: HONCHO_API_KEY in ~/.hermes/.env or ~/.honcho/config.json\n#\n# Hermes-specific overrides (optional — most config comes from ~/.honcho/config.json):\n# honcho: {}\n\n# =============================================================================\n# Display\n# =============================================================================\ndisplay:\n # Use compact banner mode (hides the ASCII-art banner, shows a single line).\n # true: Compact single-line banner\n # false: Full ASCII banner with tool/skill summary (default)\n compact: false\n\n # Tool progress display level (CLI and gateway)\n # off: Silent — no tool activity shown, just the final response\n # new: Show a tool indicator only when the tool changes (skip repeats)\n # all: Show every tool call with a short preview (default)\n # verbose: Full args, results, and debug logs (same as /verbose)\n # log: Silent in chat; append every tool call to ~/.hermes/logs/tool_calls.log (gateway only)\n # Toggle at runtime with /verbose in the CLI\n tool_progress: all\n\n # Per-platform defaults can be quieter than the global setting. Telegram\n # tunes for mobile: tool_progress and busy_ack_detail default off (no\n # per-tool breadcrumb stream, no \"iteration 21/60\" debug detail in busy\n # acks or heartbeats), but interim_assistant_messages and\n # long_running_notifications STAY ON so the user has real signal between\n # turn start and final answer (mid-turn assistant commentary + a single\n # edit-in-place \"⏳ Working — N min\" heartbeat). Override under\n # display.platforms.telegram.\n\n # Auto-cleanup of temporary progress bubbles after the final response lands.\n # On platforms that support message deletion (currently Telegram), this\n # removes the tool-progress bubble, \"⏳ Still working...\" notices, and\n # context-pressure status messages once the final reply has been delivered —\n # keeping long-running turns visible live, then tidy afterward. Failed runs\n # leave the bubbles in place as breadcrumbs. Off by default.\n # Per-platform override: display.platforms.telegram.cleanup_progress\n # true: Delete tracked progress/status bubbles on successful turn\n # false: Leave everything in place (default)\n # Example:\n # display:\n # platforms:\n # telegram:\n # cleanup_progress: true\n cleanup_progress: false\n\n # Natural mid-turn assistant updates.\n # On gateway platforms, when true, completed assistant status messages are\n # sent as separate chat messages. On the Desktop app, when true, mid-turn\n # assistant narration streamed between tool calls is kept in the transcript\n # instead of the bubble collapsing to only the final message on completion.\n # Independent of tool_progress and gateway streaming.\n # true: Keep/send mid-turn assistant updates (default)\n # false: Only keep/send the final response\n interim_assistant_messages: true\n\n # Gateway-only long-running status heartbeats.\n # When false, the platform does not receive periodic \"⏳ Working — N min\"\n # notifications even if agent.gateway_notify_interval is non-zero. The\n # heartbeat edits a single message in place (where the adapter supports\n # editing) instead of posting a new bubble each interval.\n # Default: true everywhere, including Telegram (silent agents are worse\n # than a single edit-in-place heartbeat).\n long_running_notifications: true\n\n # Include detailed iteration/tool/status context in busy acknowledgments\n # and long-running heartbeats. When true, busy acks show \"iteration 21/60,\n # terminal, 10 min\" and the heartbeat shows \"⏳ Working — 12 min,\n # iteration 21/60, terminal\". When false (Telegram default), both stay\n # terse: \"Interrupting current task\" and \"⏳ Working — 12 min, terminal\".\n busy_ack_detail: true\n\n # What Enter does when Hermes is already busy (CLI and gateway platforms).\n # interrupt: Interrupt the current run and redirect Hermes (default)\n # queue: Queue your message for the next turn\n # steer: Inject your message mid-run via /steer, arriving at the agent\n # after the next tool call — no interrupt, no role violation.\n # Falls back to 'queue' if the agent isn't running yet or if\n # images are attached (steer only carries text).\n # Ctrl+C (or /stop in gateway) always interrupts regardless of this setting.\n # Toggle at runtime with /busy <interrupt|queue|steer>.\n busy_input_mode: interrupt\n\n # Background process notifications (gateway/messaging only).\n # Controls how chatty the process watcher is when you use\n # terminal(background=true, notify_on_complete=true) from Telegram/Discord/etc.\n # off: No watcher messages at all\n # result: Only the final completion message\n # error: Only the final message when exit code != 0\n # all: Running output updates + final message (default)\n background_process_notifications: all\n\n\n # Play terminal bell when agent finishes a response.\n # Useful for long-running tasks — your terminal will ding when the agent is done.\n # Works over SSH. Most terminals can be configured to flash the taskbar or play a sound.\n # true: Ring the terminal bell on each response\n # false: Silent (default)\n bell_on_complete: false\n\n # Show model reasoning/thinking before each response.\n # When enabled, a dim box shows the model's thought process above the response.\n # Toggle at runtime with /reasoning show or /reasoning hide.\n # true: Show the reasoning box\n # false: Hide reasoning (default)\n show_reasoning: false\n\n # Stream tokens to the terminal as they arrive instead of waiting for the\n # full response. The response box opens on first token and text appears\n # line-by-line. Tool calls are still captured silently.\n # true: Stream tokens as they arrive (default)\n # false: Wait for the full response before rendering\n streaming: true\n\n # Show [HH:MM] timestamps on user input and assistant response labels.\n # timestamps: false\n\n # ───────────────────────────────────────────────────────────────────────────\n # Skin / Theme\n # ───────────────────────────────────────────────────────────────────────────\n # Customize CLI visual appearance — banner colors, spinner faces, tool prefix,\n # response box label, and branding text. Change at runtime with /skin <name>.\n #\n # Built-in skins:\n # default — Classic Hermes gold/kawaii\n # ares — Crimson/bronze war-god theme with spinner wings\n # mono — Clean grayscale monochrome\n # slate — Cool blue developer-focused\n # daylight — Bright light-mode theme\n # warm-lightmode — Warm paper-tone light-mode theme\n # poseidon — Sea-green/teal Olympian theme\n # sisyphus — Earthy stone-and-moss theme\n # charizard — Fiery orange dragon theme\n #\n # Custom skins: drop a YAML file in ~/.hermes/skins/<name>.yaml\n # Schema (all fields optional, missing values inherit from default):\n #\n # name: my-theme\n # description: Short description\n # colors:\n # banner_border: \"#HEX\" # Panel border\n # banner_title: \"#HEX\" # Panel title\n # banner_accent: \"#HEX\" # Section headers (Available Tools, etc.)\n # banner_dim: \"#HEX\" # Dim/muted text\n # banner_text: \"#HEX\" # Body text (tool names, skill names)\n # ui_accent: \"#HEX\" # UI accent color\n # response_border: \"#HEX\" # Response box border color\n # spinner:\n # waiting_faces: [\"(⚔)\", \"(⛨)\"] # Faces shown while waiting\n # thinking_faces: [\"(⚔)\", \"(⌁)\"] # Faces shown while thinking\n # thinking_verbs: [\"forging\", \"plotting\"] # Verbs for spinner messages\n # wings: # Optional left/right spinner decorations\n # - [\"⟪⚔\", \"⚔⟫\"]\n # - [\"⟪▲\", \"▲⟫\"]\n # branding:\n # agent_name: \"My Agent\" # Banner title and branding\n # welcome: \"Welcome message\" # Shown at CLI startup\n # response_label: \" ⚔ Agent \" # Response box header label\n # prompt_symbol: \"⚔\" # Prompt symbol (bare token; renderers add trailing space)\n # tool_prefix: \"╎\" # Tool output line prefix (default: ┊)\n #\n skin: default\n\n# =============================================================================\n# Model Aliases — short names for /model command\n# =============================================================================\n# Map short aliases to exact (model, provider, base_url) tuples.\n# Used by /model tab completion and resolve_alias().\n# Aliases are checked BEFORE the models.dev catalog, so they can route\n# to endpoints not in the catalog (e.g. Ollama Cloud, local servers).\n#\n# model_aliases:\n# opus:\n# model: claude-opus-4-6\n# provider: anthropic\n# qwen:\n# model: \"qwen3.5:397b\"\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n# glm:\n# model: glm-4.7\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n\n# =============================================================================\n# Privacy\n# =============================================================================\n# privacy:\n# # Redact PII from the LLM context prompt.\n# # When true, phone numbers are stripped and user/chat IDs are replaced\n# # with deterministic hashes before being sent to the model.\n# # Names and usernames are NOT affected (user-chosen, publicly visible).\n# # Routing/delivery still uses the original values internally.\n# redact_pii: false\n\n# =============================================================================\n# Shell-script hooks\n# =============================================================================\n# Register shell scripts as plugin-hook callbacks. Each entry is executed as\n# a subprocess (shell=False, shlex.split) with a JSON payload on stdin. On\n# stdout the script may return JSON that either blocks the tool call or\n# injects context into the next LLM call.\n#\n# Valid events (mirror hermes_cli.plugins.VALID_HOOKS):\n# pre_tool_call, post_tool_call, pre_llm_call, post_llm_call,\n# pre_api_request, post_api_request, on_session_start, on_session_end,\n# on_session_finalize, on_session_reset, subagent_stop\n#\n# First-use consent: each (event, command) pair prompts once on a TTY, then\n# is persisted to ~/.hermes/shell-hooks-allowlist.json. Non-interactive\n# runs (gateway, cron) need --accept-hooks, HERMES_ACCEPT_HOOKS=1, or the\n# hooks_auto_accept key below.\n#\n# See website/docs/user-guide/features/hooks.md for the full JSON wire\n# protocol and worked examples.\n#\n# hooks:\n# pre_tool_call:\n# - matcher: \"terminal\"\n# command: \"~/.hermes/agent-hooks/block-rm-rf.sh\"\n# timeout: 10\n# post_tool_call:\n# - matcher: \"write_file|patch\"\n# command: \"~/.hermes/agent-hooks/auto-format.sh\"\n# pre_llm_call:\n# - command: \"~/.hermes/agent-hooks/inject-cwd-context.sh\"\n# subagent_stop:\n# - command: \"~/.hermes/agent-hooks/log-orchestration.sh\"\n#\n# hooks_auto_accept: false\n\n\n# =============================================================================\n# Update Behavior\n# =============================================================================\nupdates:\n # Create a full HERMES_HOME zip before every `hermes update`.\n # Backups land in ~/.hermes/backups/ and can be restored with `hermes import`.\n # Off by default because large homes can add minutes to every update.\n pre_update_backup: false\n\n # Number of pre-update backup zips to retain.\n backup_keep: 5\n\n # What non-interactive updates do with local source edits in the Hermes repo.\n # Interactive terminal updates always prompt before restoring the autostash.\n #\n # stash - auto-stash before pull, then auto-restore after success (default)\n # discard - drop the update-created stash after success; use only on managed\n # installs where local source edits should not persist\n non_interactive_local_changes: \"stash\"\n\n\n# =============================================================================\n# Web Dashboard\n# =============================================================================\n# OAuth gate configuration for `hermes dashboard --host <non-loopback>`.\n# The bundled Nous Portal plugin reads these on startup; settings here are\n# the canonical surface. Each can be overridden by an environment variable:\n#\n# dashboard.oauth.client_id <- HERMES_DASHBOARD_OAUTH_CLIENT_ID\n# dashboard.oauth.portal_url <- HERMES_DASHBOARD_PORTAL_URL\n# dashboard.public_url <- HERMES_DASHBOARD_PUBLIC_URL\n#\n# Env wins when set to a non-empty value. This is what Fly.io's platform-\n# secret injection uses to push per-deploy client_ids without needing to\n# bake a config.yaml into the image. Empty env values are treated as unset\n# so a provisioned-but-not-populated secret can't shadow a valid entry here.\n#\n# Local dev / on-prem deploys should typically set these via config.yaml\n# (the ~/.hermes/.env file is reserved for API keys and secrets).\n#\n# dashboard:\n# oauth:\n# client_id: \"\" # agent:{instance_id}; Portal provisions this at deploy\n# portal_url: \"\" # blank → default https://portal.nousresearch.com\n#\n# # Force the absolute base URL the OAuth callback (and any other public\n# # URL the dashboard hands to external systems) is built from. Set this\n# # for deploys behind reverse proxies that don't reliably forward\n# # X-Forwarded-Host / X-Forwarded-Proto / X-Forwarded-Prefix (manual\n# # nginx setups, on-prem ingresses, custom-domain Fly deploys without\n# # full proxy header chains).\n# #\n# # When set, the value is the complete authority: scheme + host +\n# # optional path prefix (e.g. \"https://example.com/hermes\"). The OAuth\n# # callback URL becomes \"<public_url>/auth/callback\" — X-Forwarded-Prefix\n# # is IGNORED on this code path because the operator has explicitly\n# # declared the public URL and we no longer need to guess.\n# #\n# # Leave empty to use the existing proxy-header reconstruction (the\n# # default — works on Fly.io out of the box).\n# #\n# # public_url: \"https://example.com/hermes\"\n#\n# -----------------------------------------------------------------------------\n# Self-hosted OIDC dashboard auth (generic OpenID Connect — Authentik,\n# Keycloak, Zitadel, Authelia, Auth0, Okta, Google, …). Use this INSTEAD of the\n# nous block above when gating the dashboard with your own identity provider.\n# Each setting can be overridden by an environment variable:\n#\n# dashboard.oauth.self_hosted.issuer <- HERMES_DASHBOARD_OIDC_ISSUER\n# dashboard.oauth.self_hosted.client_id <- HERMES_DASHBOARD_OIDC_CLIENT_ID\n# dashboard.oauth.self_hosted.scopes <- HERMES_DASHBOARD_OIDC_SCOPES\n# dashboard.oauth.self_hosted.client_secret <- HERMES_DASHBOARD_OIDC_CLIENT_SECRET\n#\n# dashboard:\n# oauth:\n# provider: self-hosted\n# self_hosted:\n# issuer: \"https://auth.example.com/application/o/hermes/\" # required\n# client_id: \"hermes-dashboard\" # required\n# scopes: \"openid profile email\" # optional\n#\n# # OPTIONAL — set ONLY if your IDP registered the client as\n# # *confidential* (Authentik / Keycloak often default to this). When\n# # set, Hermes authenticates the client at the token endpoint\n# # (client_secret_basic or client_secret_post, auto-selected from the\n# # IDP's discovery doc) IN ADDITION to PKCE. Leave unset for a public\n# # (PKCE-only) client — the common case.\n# #\n# # This is a CREDENTIAL: prefer setting HERMES_DASHBOARD_OIDC_CLIENT_SECRET\n# # in ~/.hermes/.env over putting it here in config.yaml.\n# # client_secret: \"\"\n\n# =============================================================================\n# External secret sources\n# =============================================================================\n# Pull provider credentials from external secret managers at process startup\n# instead of storing them in ~/.hermes/.env. Only the manager's bootstrap\n# credential (e.g. BWS_ACCESS_TOKEN / OP_SERVICE_ACCOUNT_TOKEN) lives in .env\n# (or your shell / desktop session); everything else rotates centrally.\n# Failures never block startup — Hermes warns once and continues with\n# whatever .env already had.\n#\n# Multiple sources can be enabled at once:\n# - \"mapped\" sources (explicit VAR -> ref bindings, e.g. 1Password's env:\n# map) beat \"bulk\" sources (whole-project dumps like Bitwarden BSM)\n# - within a shape, the first source to claim a var wins; later claims\n# are skipped with a startup warning (never a silent clobber)\n# - a source's override_existing lets it beat .env/shell values, but\n# never another secret source's claim\n# Docs: https://hermes-agent.nousresearch.com/docs/user-guide/secrets/\n#\n# secrets:\n# # Optional explicit ordering of enabled sources.\n# # sources: [onepassword, bitwarden]\n#\n# # ---- Bitwarden Secrets Manager (bws CLI) --------------------------------\n# bitwarden:\n# enabled: false\n# access_token_env: BWS_ACCESS_TOKEN # bootstrap token, sourced from .env\n# project_id: \"\" # UUID of the BSM project to sync\n# server_url: \"\" # \"\" = US Cloud; EU/self-hosted URL otherwise\n# cache_ttl_seconds: 300 # 0 disables fresh caching\n# encrypted_cache: # optional encrypted stale fallback\n# enabled: false\n# max_stale_seconds: 0 # 0 disables stale fallback\n# override_existing: true # BSM values win over existing env\n# auto_install: true # lazy-download bws into ~/.hermes/bin\n#\n# # ---- 1Password (op CLI) -------------------------------------------------\n# onepassword:\n# enabled: false\n# # Map env-var names to op:// secret references. Each is resolved with a\n# # single `op read` at startup.\n# env:\n# OPENAI_API_KEY: \"op://Private/OpenAI/api key\"\n# ANTHROPIC_API_KEY: \"op://Private/Anthropic/credential\"\n# account: \"\" # op --account shorthand; \"\" = default\n# service_account_token_env: OP_SERVICE_ACCOUNT_TOKEN # headless auth; unset = desktop session\n# binary_path: \"\" # \"\" = resolve op via PATH; else absolute path\n# cache_ttl_seconds: 300 # 0 disables BOTH cache layers\n# override_existing: true # resolved values win over existing env\n#\n# # ---- Command helper (any CLI vault) --------------------------------------\n# # Run a user-configured helper that prints KEY=VALUE lines on stdout —\n# # works with any secret store that has a CLI: keepassxc-cli, secret-tool,\n# # pass, gpg, or a script that cats a tmpfs env file. Composes with the\n# # sources above (enable any combination). POSIX-only (needs /bin/sh).\n# # The helper must be fast and NON-interactive (hard timeout, 1 MiB cap);\n# # its stderr is discarded so diagnostics can't leak secret material.\n# command:\n# enabled: false\n# command: \"cat /run/user/1000/hermes-secrets.env\"\n# helper_timeout_seconds: 3\n# override_existing: false # .env/shell win by default\n"}, "f2e1a71120b41c717115f704b42017b2b0e2487b": {"t": 1785768005.0380054, "n": 83595, "h": "a03d1c6bb7252ec2", "c": "# Hermes Agent CLI Configuration\n# Copy this file to cli-config.yaml and customize as needed.\n# This file configures the CLI behavior. Environment variables in .env take precedence.\n\n# =============================================================================\n# Model Configuration\n# =============================================================================\nmodel:\n # Default model to use (can be overridden with --model flag)\n # Both \"default\" and \"model\" work as the key name here.\n default: \"anthropic/claude-opus-4.6\"\n \n # Inference provider selection:\n # \"auto\" - Auto-detect from credentials (default)\n # \"openrouter\" - OpenRouter (requires: OPENROUTER_API_KEY or OPENAI_API_KEY)\n # \"nous\" - Nous Portal OAuth (requires: hermes auth add nous)\n # \"nous-api\" - Nous Portal API key (requires: NOUS_API_KEY)\n # \"anthropic\" - Direct Anthropic API (requires: ANTHROPIC_API_KEY)\n # \"openai-codex\" - OpenAI Codex (requires: hermes auth)\n # \"copilot\" - GitHub Copilot / GitHub Models (requires: GITHUB_TOKEN)\n # \"gemini\" - Use Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n # \"zai\" - Use z.ai / ZhipuAI GLM models (requires: GLM_API_KEY)\n # \"kimi-coding\" - Kimi / Moonshot AI (requires: KIMI_API_KEY)\n # \"minimax\" - MiniMax global (requires: MINIMAX_API_KEY)\n # \"minimax-cn\" - MiniMax China (requires: MINIMAX_CN_API_KEY)\n # \"huggingface\" - Hugging Face Inference (requires: HF_TOKEN)\n # \"nvidia\" - NVIDIA NIM / build.nvidia.com (requires: NVIDIA_API_KEY)\n # \"xiaomi\" - Xiaomi MiMo (requires: XIAOMI_API_KEY)\n # \"arcee\" - Arcee AI Trinity models (requires: ARCEEAI_API_KEY)\n # \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY — https://ollama.com/settings)\n # \"deepinfra\" - DeepInfra (requires: DEEPINFRA_API_KEY)\n # \"kilocode\" - KiloCode gateway (requires: KILOCODE_API_KEY)\n # \"azure-foundry\" - Microsoft Foundry / Azure OpenAI (API key or Entra ID)\n # \"lmstudio\" - LM Studio local server (optional: LM_API_KEY, defaults to http://127.0.0.1:1234/v1)\n #\n # Local servers (LM Studio, Ollama, vLLM, llama.cpp):\n # \"custom\" - Any other OpenAI-compatible endpoint. Set base_url below.\n # Aliases: \"ollama\", \"vllm\", \"llamacpp\" all map to \"custom\".\n # LM Studio is first-class and uses provider: \"lmstudio\".\n # It works with both no-auth and auth-enabled server modes.\n #\n # Can also be overridden for a single invocation with the --provider flag.\n provider: \"auto\"\n \n # API configuration (falls back to OPENROUTER_API_KEY env var)\n # api_key: \"your-key-here\" # Uncomment to set here instead of .env\n base_url: \"https://openrouter.ai/api/v1\"\n\n # Azure Foundry keyless auth example:\n # provider: \"azure-foundry\"\n # base_url: \"https://<resource>.openai.azure.com/openai/v1\"\n # auth_mode: \"entra_id\" # DefaultAzureCredential: az login, managed identity, workload identity, etc.\n # default: \"gpt-4o\" # Deployment/model name\n # entra:\n # scope: \"https://ai.azure.com/.default\" # Optional; this is the default.\n\n # ── Token limits — two settings, easy to confuse ──────────────────────────\n #\n # context_length: TOTAL context window (input + output tokens combined).\n # Controls when Hermes compresses history and validates requests.\n # Leave unset — Hermes auto-detects the correct value from the provider.\n # Set manually only when auto-detection is wrong (e.g. a local server with\n # a custom num_ctx, or a proxy that doesn't expose /v1/models).\n #\n # context_length: 131072\n #\n # max_tokens: OUTPUT cap — maximum tokens the model may generate per response.\n # Unrelated to how long your conversation history can be.\n # The OpenAI-standard name \"max_tokens\" is a misnomer; Anthropic's native\n # API has since renamed it \"max_output_tokens\" for clarity.\n # Leave unset to use the model's native output ceiling (recommended).\n # Set only if you want to deliberately limit individual response length.\n #\n# max_tokens: 8192\n\n # ── Custom request headers (optional) ─────────────────────────────────────\n #\n # default_headers: extra HTTP headers sent on every request to an\n # OpenAI-compatible endpoint. User values take precedence over the\n # provider/SDK defaults, so this is the supported way to override the\n # OpenAI Python SDK's identifying headers (User-Agent: OpenAI/Python ...,\n # X-Stainless-*) when a custom provider sits behind a gateway/WAF that\n # rejects them — e.g. an upstream that returns \"502 Upstream access\n # forbidden\" for the SDK default User-Agent but accepts a plain one.\n # Applies on the OpenAI wire only (not native Anthropic / Bedrock).\n #\n # default_headers:\n # User-Agent: \"curl/8.7.1\"\n #\n # extra_headers: accepted as an alias of default_headers (merged, with\n # extra_headers winning when both are set) — matches the per-provider\n # extra_headers key below.\n #\n # Per-provider variant: named providers / custom_providers entries accept an\n # extra_headers dict scoped to that endpoint only — for reverse proxies,\n # gateways, or custom auth (e.g. Cloudflare Access service tokens).\n # Merged onto SDK/provider defaults with the entry's values winning.\n # Header values are treated as secrets and are never logged.\n #\n # providers:\n # my-proxy:\n # base_url: \"https://llm.internal.example.com/v1\"\n # key_env: \"MY_PROXY_API_KEY\"\n # extra_headers:\n # CF-Access-Client-Id: \"xxxx.access\"\n # CF-Access-Client-Secret: \"${CF_ACCESS_SECRET}\"\n # X-Client-Name: \"hermes-agent\"\n\n# Named provider overrides (optional)\n# Use this for per-provider request timeouts, non-stream stale timeouts,\n# and per-model exceptions.\n# Applies to the primary turn client on every api_mode (OpenAI-wire, native\n# Anthropic, and Anthropic-compatible providers), the fallback chain, and\n# client rebuilds during credential rotation. For OpenAI-wire chat\n# completions (streaming and non-streaming) the configured value is also\n# used as the per-request ``timeout=`` kwarg so it wins over the legacy\n# HERMES_API_TIMEOUT env var (which still applies when no config is set).\n# ``stale_timeout_seconds`` controls the non-streaming stale-call detector and\n# wins over the legacy HERMES_API_CALL_STALE_TIMEOUT env var. Leaving these\n# unset keeps the legacy defaults (HERMES_API_TIMEOUT=1800s,\n# HERMES_API_CALL_STALE_TIMEOUT=90s, native Anthropic 900s). The\n# implicit non-stream stale detector is auto-disabled for local endpoints\n# and can scale upward for very large contexts.\n#\n# Not currently wired for AWS Bedrock (bedrock_converse + AnthropicBedrock\n# SDK paths) — those use boto3 with its own timeout configuration.\n#\n# providers:\n# ollama-local:\n# request_timeout_seconds: 300 # Longer timeout for local cold-starts\n# stale_timeout_seconds: 900 # Explicitly re-enable stale detection on local endpoints\n# anthropic:\n# request_timeout_seconds: 30 # Fast-fail cloud requests\n# models:\n# claude-opus-4.6:\n# timeout_seconds: 600 # Longer timeout for extended-thinking Opus calls\n# openai-codex:\n# models:\n# gpt-5.4:\n# stale_timeout_seconds: 1800 # Longer non-stream stale timeout for slow large-context turns\n\n# =============================================================================\n# OpenRouter Provider Routing (only applies when using OpenRouter)\n# =============================================================================\n# Control how requests are routed across providers on OpenRouter.\n# See: https://openrouter.ai/docs/guides/routing/provider-selection\n#\n# provider_routing:\n# # Sort strategy: \"price\" (default), \"throughput\", or \"latency\"\n# # Append :nitro to model name for a shortcut to throughput sorting.\n# sort: \"throughput\"\n#\n# # Only allow these providers (provider slugs from OpenRouter)\n# # only: [\"anthropic\", \"google\"]\n#\n# # Skip these providers entirely\n# # ignore: [\"deepinfra\", \"fireworks\"]\n#\n# # Try providers in this order (overrides default load balancing)\n# # order: [\"anthropic\", \"google\", \"together\"]\n#\n# # Require providers to support all parameters in your request\n# # require_parameters: true\n#\n# # Data policy: \"allow\" (default) or \"deny\" to exclude providers that may store data\n# # data_collection: \"deny\"\n\n# =============================================================================\n# OpenRouter Response Caching (only applies when using OpenRouter)\n# =============================================================================\n# Cache identical API responses at the OpenRouter edge for free instant replays.\n# When enabled, identical requests (same model, messages, parameters) return\n# cached responses with zero billing. Separate from Anthropic prompt caching.\n# See: https://openrouter.ai/docs/guides/features/response-caching\n#\n# openrouter:\n# response_cache: true # Enable response caching (default: true)\n# response_cache_ttl: 300 # Cache TTL in seconds, 1-86400 (default: 300)\n\n# =============================================================================\n# Git Worktree Isolation\n# =============================================================================\n# When enabled, each CLI session creates an isolated git worktree so multiple\n# agents can work on the same repo concurrently without file collisions.\n# Equivalent to always passing --worktree / -w on the command line.\n#\n# worktree: true # Always create a worktree when in a git repo\n# worktree: false # Default — only create when -w flag is passed\n#\n# By default a new worktree branches from the freshly-fetched remote tip\n# (the current branch's upstream, else the remote's default branch) so it\n# starts current with the project instead of from the local clone's\n# (possibly stale) HEAD. Set worktree_sync: false to branch from local HEAD\n# instead — useful when offline or when you deliberately want the clone's\n# exact current state as the base.\n#\n# worktree_sync: true # Default — branch from the fetched remote tip\n# worktree_sync: false # Branch from local HEAD (offline / pinned base)\n\n# =============================================================================\n# Terminal Tool Configuration\n# =============================================================================\n# Choose ONE of the following terminal configurations by uncommenting it.\n# The terminal tool executes commands in the specified environment.\n\n# -----------------------------------------------------------------------------\n# OPTION 1: Local execution (default)\n# Commands run directly on your machine in the current directory\n# -----------------------------------------------------------------------------\n# Working directory behavior:\n# - CLI (`hermes` command): Uses \".\" (current directory where you run hermes)\n# - Gateway/messaging/cron: Uses terminal.cwd here; legacy .env cwd values are deprecated\nterminal:\n backend: \"local\"\n cwd: \".\" # For local backend: \".\" = current directory. Ignored for remote backends unless a backend documents otherwise.\n timeout: 180\n # HOME policy for tool subprocesses:\n # auto - default: host uses your real HOME; containers use HERMES_HOME/home\n # real - force your real OS-user HOME\n # profile - force HERMES_HOME/home for strict per-profile CLI config isolation\n home_mode: \"auto\"\n docker_mount_cwd_to_workspace: false # SECURITY: off by default. Opt in to mount the launch cwd into Docker /workspace.\n lifetime_seconds: 300\n # sudo_password: \"hunter2\" # Optional: pipe a sudo password via sudo -S. SECURITY WARNING: plaintext.\n # sudo_password: \"\" # Explicit empty password: try empty and never open the interactive sudo prompt.\n\n# -----------------------------------------------------------------------------\n# OPTION 2: SSH remote execution\n# Commands run on a remote server - agent code stays local (sandboxed)\n# Great for: keeping agent isolated from its own code, using powerful remote hardware\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"ssh\"\n# cwd: \"/home/myuser/project\" # Path on the REMOTE server\n# timeout: 180\n# lifetime_seconds: 300\n# ssh_host: \"my-server.example.com\"\n# ssh_user: \"myuser\"\n# ssh_port: 22\n# ssh_key: \"~/.ssh/id_rsa\" # Optional - uses ssh-agent if not specified\n\n# -----------------------------------------------------------------------------\n# OPTION 3: Docker container\n# Commands run in an isolated Docker container\n# Great for: reproducible environments, testing, isolation\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"docker\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /)\n# timeout: 180\n# lifetime_seconds: 300\n# docker_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# docker_mount_cwd_to_workspace: true # Explicit opt-in: mount your launch cwd into /workspace\n# # Optional: run the container as your host user's uid:gid so files written\n# # into bind-mounted dirs are owned by you, not root. Drops SETUID/SETGID\n# # caps too since no gosu privilege drop is needed. Leave off if your\n# # chosen docker_image expects to start as root.\n# docker_run_as_host_user: true\n# # Optional: explicitly forward selected env vars into Docker.\n# # These values come from your current shell first, then ~/.hermes/.env.\n# # Warning: anything forwarded here is visible to commands run in the container.\n# docker_forward_env:\n# - \"GITHUB_TOKEN\"\n# - \"NPM_TOKEN\"\n# # Optional: extra flags passed verbatim to docker run (appended after security defaults).\n# # Useful for adding capabilities (e.g. apt installs needing SETUID) or custom options.\n# # Example: add a Linux capability not included by default\n# # docker_extra_args:\n# # - \"--cap-add\"\n# # - \"SETUID\"\n\n# -----------------------------------------------------------------------------\n# OPTION 4: Singularity/Apptainer container\n# Commands run in a Singularity container (common in HPC environments)\n# Great for: HPC clusters, shared compute environments\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"singularity\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# singularity_image: \"docker://nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 5: Modal cloud execution\n# Commands run on Modal's cloud infrastructure\n# Great for: GPU access, scalable compute, serverless execution\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"modal\"\n# cwd: \"/workspace\" # Path INSIDE the sandbox (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# modal_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 6: Daytona cloud execution\n# Commands run in Daytona cloud sandboxes\n# Great for: Cloud dev environments, persistent workspaces, team collaboration\n# Requires: pip install daytona, DAYTONA_API_KEY env var\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"daytona\"\n# cwd: \"~\"\n# timeout: 180\n# lifetime_seconds: 300\n# daytona_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# container_disk: 10240 # Daytona max is 10GB per sandbox\n\n#\n# --- Container resource limits (docker, singularity, modal, daytona -- ignored for local/ssh) ---\n# These settings apply to all container backends. They control the resources\n# allocated to the sandbox and whether its filesystem persists across sessions.\n container_cpu: 1 # CPU cores\n container_memory: 5120 # Memory in MB (5120 = 5GB)\n container_disk: 51200 # Disk in MB (51200 = 50GB)\n container_persistent: true # Persist filesystem across sessions (false = ephemeral)\n\n# -----------------------------------------------------------------------------\n# SUDO SUPPORT (works with ALL backends above)\n# -----------------------------------------------------------------------------\n# Add sudo_password to any terminal config above to enable sudo commands.\n# The password is piped via `sudo -S`. Works with local, ssh, docker, etc.\n#\n# SECURITY WARNING: Password stored in plaintext!\n#\n# INTERACTIVE PROMPT: If sudo_password is unset and the CLI is running,\n# you'll be prompted to enter your password when sudo is needed:\n# - 45-second timeout (auto-skips if no input)\n# - Press Enter to skip (command fails gracefully)\n# - Password is hidden while typing\n# - Password is cached for the session\n#\n# EMPTY PASSWORDS: Setting sudo_password to an explicit empty string is different\n# from leaving it unset. Hermes will try an empty password via `sudo -S` and\n# will not open the interactive prompt. This is useful for passwordless sudo,\n# Touch ID sudo setups, and environments where prompting is just noise.\n#\n# ALTERNATIVES:\n# - SSH backend: Configure passwordless sudo on the remote server\n# - Containers: Run as root inside the container (no sudo needed)\n# - Local: Configure /etc/sudoers for specific commands\n#\n# Example (add to your terminal section):\n# sudo_password: \"your-password-here\"\n\n# =============================================================================\n# Security Scanning (tirith)\n# =============================================================================\n# Optional pre-exec command security scanning via tirith.\n# Detects homograph URLs, pipe-to-shell, terminal injection, env manipulation.\n# Install: brew install sheeki03/tap/tirith\n# Docs: https://github.com/sheeki03/tirith\n#\n# security:\n# tirith_enabled: true # Enable/disable tirith scanning\n# tirith_path: \"tirith\" # Path to tirith binary (supports ~ expansion)\n# tirith_timeout: 5 # Scan timeout in seconds\n# tirith_fail_open: true # Allow commands if tirith unavailable\n\n# =============================================================================\n# Browser Tool Configuration\n# =============================================================================\nbrowser:\n # Inactivity timeout in seconds - browser sessions are automatically closed\n # after this period of no activity between agent loops (default: 120 = 2 minutes)\n inactivity_timeout: 120\n\n# =============================================================================\n# Tool Loop Guardrails\n# =============================================================================\n# Soft warnings are enabled by default. They append guidance to repeated failed\n# or non-progressing tool results but still let the tool execute. Hard stops are\n# opt-in circuit breakers for autonomous/cron sessions where stopping a loop is\n# preferable to spending the full iteration budget.\ntool_loop_guardrails:\n warnings_enabled: true\n hard_stop_enabled: false\n warn_after:\n exact_failure: 2\n same_tool_failure: 3\n idempotent_no_progress: 2\n hard_stop_after:\n exact_failure: 5\n same_tool_failure: 8\n idempotent_no_progress: 5\n\n# =============================================================================\n# Context Compression (Auto-shrinks long conversations)\n# =============================================================================\n# When conversation approaches model's context limit, middle turns are\n# automatically summarized to free up space while preserving important context.\n#\n# HOW IT WORKS:\n# 1. Tracks actual token usage from API responses (not estimates)\n# 2. When prompt_tokens >= threshold% of model's context_length, triggers compression\n# 3. Protects first 3 turns (system prompt, initial request, first response)\n# 4. Protects last N turns (default 20 messages = ~10 full turns of recent context)\n# 5. Summarizes middle turns using a fast/cheap model\n# 6. Inserts summary as a user message, continues conversation seamlessly\n#\n# Post-compression tail budget is target_ratio × threshold × context_length:\n# 200K context, threshold 0.50, ratio 0.20 → 20K tokens of recent tail preserved\n# 1M context, threshold 0.50, ratio 0.20 → 100K tokens of recent tail preserved\n#\ncompression:\n # Enable automatic context compression (default: true)\n # Set to false if you prefer to manage context manually or want errors on overflow\n enabled: true\n\n # Opt-in compression progress notices on chat platforms (default: false).\n # By design, routine automatic compression is SILENT on human-facing chat\n # gateways (Telegram, Discord, Slack, ...) — it happens in the background\n # with server-side logging only. Set true to also deliver the routine\n # progress statuses (compacting started, preflight/pre-API compression,\n # idle compaction, retry progress, and the compaction-complete notice) to\n # chat platforms. Unrelated operational noise (auxiliary model failures,\n # provider retry chatter) stays suppressed either way, and compression\n # FAILURE notices + manual /compress feedback are always visible\n # regardless of this setting. (#52995)\n progress_notices: false\n \n # Trigger compression at this % of model's context limit (default: 0.50 = 50%)\n # Lower values = more aggressive compression, higher values = compress later\n # Models with context windows below 512K are floored at 0.75 (raise-only) so\n # compaction doesn't fire with half the window still free; set above 0.75 to override.\n threshold: 0.50\n\n # Per-model threshold overrides: keys are substring-matched against the model\n # name (longest match wins). Useful when some models need different compaction\n # points — e.g. a 1M-context model can compress later (0.30) while a 128K\n # model needs to compress earlier (0.60). The small-context floor (75% for\n # <512K models) still applies on top of per-model overrides.\n # model_thresholds:\n # \"glm-5.2\": 0.40\n # \"claude-sonnet\": 0.35\n # \"gpt-5\": 0.30\n\n # Optional absolute token cap for the compression trigger (default: null = disabled).\n # When set, compression fires at the LOWER of the ratio-based threshold and this\n # absolute token count — first-fires-wins. It never fires later than this count\n # regardless of which model is active (useful when switching between models with\n # very different context windows). Clamped to the model's context length at\n # apply-time, so a cap above the window is a no-op (ratio-based threshold wins).\n # Survives model switches and fallback activations.\n # threshold_tokens: 200000\n\n # Existing Codex gpt-5.5 behavior: raise Hermes' compaction trigger to 85%\n # for the ChatGPT Codex OAuth route. Set false to opt back down to threshold.\n codex_gpt55_autoraise: true\n\n # Fraction of the threshold to preserve as recent tail (default: 0.20 = 20%)\n # e.g. 20% of 50% threshold = 10% of total context kept as recent messages.\n # Summary output is separately capped at 12K tokens (Gemini output limit).\n # Range: 0.10 - 0.80\n target_ratio: 0.20\n\n # Number of most-recent messages to always preserve (default: 20 ≈ 10 full turns)\n # Higher values keep more recent conversation intact at the cost of more aggressive\n # compression of older turns.\n protect_last_n: 20\n\n # Minimum number of REAL (actionable) user messages guaranteed to survive in\n # the uncompressed tail (default: 1 = the existing single last-user anchor,\n # behavior-preserving). Raise to e.g. 3 to keep the last 3 real user turns\n # verbatim even when bulky tool outputs fill the tail token budget — blank\n # platform echoes, compaction handoffs, and synthetic continuation rows never\n # count toward N. The tail can exceed the token budget when this pulls the\n # cut back; the guarantee wins over the budget by design.\n min_tail_user_messages: 1\n\n # Compression retry rounds before a turn gives up with \"max compression\n # attempts reached\" (default: 3, same as the previous hardcoded value).\n # Raise (e.g. 6) for tool-schema-heavy sessions where 3 rounds cannot bring\n # the request estimate under the threshold. Validated >= 1, hard cap 10.\n max_attempts: 3\n\n # Codex app-server (codex CLI runtime) thread-compaction mode. The codex\n # agent owns the real thread context on this runtime, so Hermes' summarizer\n # cannot shrink it — compaction goes through the app server instead.\n # native = let Codex decide when to compact its own thread (default)\n # hermes = let Hermes threshold trigger Codex thread/compact/start\n # off = Hermes will not auto-trigger compaction; Codex may still compact natively\n codex_app_server_auto: native\n\n # Number of non-system messages to protect at the head of the transcript, in\n # ADDITION to the system prompt (which is always implicitly protected).\n # Head messages are NEVER summarized — they survive every compression\n # indefinitely. This gives stable early context for short/medium sessions,\n # but in long-running sessions that rely on rolling compaction the pinned\n # opening turns may not match how you want the session framed over time.\n # Set to 0 to preserve ONLY the system prompt (plus the rolling summary\n # and recent tail) — the cleanest configuration for long-running sessions.\n # Default 3 preserves the system prompt plus the first three non-system\n # head messages, matching the pre-feature behaviour.\n protect_first_n: 3\n\n # Idle compaction (default: 0 = disabled). When > 0, a session that resumes\n # after at least this many seconds of inactivity compacts its accumulated\n # history up front, before the first reply, so a long-lived thread you come\n # back to later doesn't re-read its full stale context on every turn.\n # Time-based, so it complements (does not replace) the size-based `threshold`\n # above. It is skipped when the context is already small (at or below the\n # post-compression target = threshold × target_ratio), so it never wastes a\n # summarization on a short idle thread. Example: 1800 = compact after 30 min idle.\n idle_compact_after_seconds: 0\n\n # Proactive tool-result prune (default: 0 = disabled). Opt-in token trigger\n # for a deterministic, no-LLM prune of OLD tool-result payloads, run\n # independently of `threshold` above. On large-window models (512K/1M) the\n # ratio threshold rarely fires, so bulky tool outputs (terminal dumps, file\n # reads, web extracts) ride along in history and get re-billed every turn.\n # When re-sent history exceeds this many tokens, the prune dedupes identical\n # results, summarizes older oversized ones, and truncates large tool-call\n # arguments — protecting the most recent `protect_last_n` messages and never\n # calling the model. Try 48000 to enable. Built-in compressor engine only;\n # other context engines inherit a safe no-op.\n # NOTE: a committed prune rewrites already-sent history, which invalidates\n # the provider's prompt-cache prefix — the min_reclaim gate below keeps\n # those cache breaks episodic (like a compression boundary) instead of\n # per-turn.\n proactive_prune_tokens: 0\n\n # The prune's summarize pass only touches tool results larger than this many\n # characters (clamped to >= 200 so a generated summary can't be\n # re-summarized). Default 8000.\n proactive_prune_min_result_chars: 8000\n\n # A proactive prune only COMMITS when it reclaims at least this many tokens\n # (measured on the pruned output). This is the prompt-cache hysteresis gate:\n # one meaningful, amortized cache break per batch of stale tool output\n # instead of a tiny break on every tool iteration. 0 = commit any non-zero\n # prune. Default 4096.\n proactive_prune_min_reclaim_tokens: 4096\n\n # To pin a specific model/provider for compression summaries, use the\n # auxiliary section below (auxiliary.compression.provider / model).\n\n# =============================================================================\n# Anthropic prompt caching TTL\n# =============================================================================\n# When prompt caching is active (Claude via OpenRouter or native Anthropic),\n# Anthropic supports two TTL tiers for cached prefixes: \"5m\" (default) and\n# \"1h\". Other values are ignored and \"5m\" is used.\n#\nprompt_caching:\n cache_ttl: \"5m\" # use \"1h\" for long sessions with pauses between turns\n\n# =============================================================================\n# Auxiliary Models (Advanced — Experimental)\n# =============================================================================\n# Hermes uses lightweight \"auxiliary\" models for side tasks: image analysis,\n# browser screenshot analysis, web page summarization, TTS audio-tag insertion,\n# and context compression.\n#\n# By default these use Gemini Flash via OpenRouter or Nous Portal and are\n# auto-detected from your credentials. You do NOT need to change anything\n# here for normal usage.\n#\n# WARNING: Overriding these with providers other than OpenRouter or Nous Portal\n# is EXPERIMENTAL and may not work. Not all models/providers support vision,\n# produce usable summaries, or accept the same API format. Change at your own\n# risk — if things break, reset to \"auto\" / empty values.\n#\n# Each task has its own provider + model pair so you can mix providers.\n# For example: OpenRouter for vision (needs multimodal), but your main\n# local endpoint for compression (just needs text).\n#\n# Provider options:\n# \"auto\" - Best available: OpenRouter → Nous Portal → main endpoint (default)\n# \"openrouter\" - Force OpenRouter (requires OPENROUTER_API_KEY)\n# \"nous\" - Force Nous Portal (requires: hermes auth add nous)\n# \"gemini\" - Force Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n# \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY)\n# \"codex\" - Force Codex OAuth (requires: hermes model → Codex).\n# Uses gpt-5.3-codex which supports vision.\n# \"main\" - Use your custom endpoint (OPENAI_BASE_URL + OPENAI_API_KEY).\n# Works with OpenAI API, local models, or any OpenAI-compatible\n# endpoint. Also falls back to Codex OAuth and API-key providers.\n#\n# Model: leave empty to use the provider's default. When empty, OpenRouter\n# uses \"google/gemini-3-flash-preview\" and Nous uses \"gemini-3-flash\".\n# Other providers pick a sensible default automatically.\n#\n# auxiliary:\n# # Image analysis: vision_analyze tool + browser screenshots\n# vision:\n# provider: \"auto\"\n# model: \"\" # e.g. \"google/gemini-2.5-flash\", \"openai/gpt-4o\"\n# timeout: 30 # LLM API call timeout (seconds)\n# download_timeout: 30 # Image HTTP download timeout (seconds)\n# # Increase for slow connections or self-hosted image servers\n# reasoning_effort: \"\" # Per-task thinking level: none, minimal, low, medium,\n# # high, xhigh, max, ultra. Empty = provider default.\n# # Works on every auxiliary task block (vision,\n# # web_extract, compression, title_generation, curator,\n# # background_review, moa_reference, ...). Example: run\n# # compression at \"low\" and vision at \"none\" to cut\n# # side-task latency/cost on reasoning models.\n#\n# # Web page scraping / summarization + browser page text extraction\n# web_extract:\n# provider: \"auto\"\n# model: \"\"\n# reasoning_effort: \"low\"\n#\n# # Gemini 3.1 TTS hidden audio-tag insertion\n# tts_audio_tags:\n# provider: \"auto\" # empty model = your main chat model\n# model: \"\"\n# timeout: 30\n#\n# # Automatic session title generation after the first exchange\n# title_generation:\n# enabled: true # set false to disable auto-title generation\n# provider: \"auto\"\n# model: \"\"\n# timeout: 30\n# language: \"\" # empty = match the user's language; or e.g. \"English\"\n#\n# # Session search — summarizes matching past sessions\n# session_search:\n# provider: \"auto\"\n# model: \"\"\n# timeout: 30\n# max_concurrency: 3 # Limit parallel summaries to reduce request-burst 429s\n# extra_body: {} # Provider-specific OpenAI-compatible request fields\n# # Example for providers that support request-body\n# # reasoning controls:\n# # extra_body:\n# # enable_thinking: false\n# # Some vLLM/Qwen deployments expect this nested:\n# # extra_body:\n# # chat_template_kwargs:\n# # enable_thinking: false\n\n# =============================================================================\n# Persistent Memory\n# =============================================================================\n# Bounded curated memory injected into the system prompt every session.\n# Two stores: MEMORY.md (agent's notes) and USER.md (user profile).\n# Character limits keep the memory small and focused. The agent manages\n# pruning -- when at the limit, it must consolidate or replace entries.\n# Disabled by default in batch_runner.\n#\nmemory:\n # Agent's personal notes: environment facts, conventions, things learned\n memory_enabled: true\n \n # User profile: preferences, communication style, expectations\n user_profile_enabled: true\n \n # Character limits (~2.75 chars per token, model-independent)\n memory_char_limit: 2200 # ~800 tokens\n user_char_limit: 1375 # ~500 tokens\n\n # Periodic memory nudge: remind the agent to consider saving memories\n # every N user turns. Set to 0 to disable. Only active when memory is enabled.\n nudge_interval: 10 # Nudge every 10 user turns (0 = disabled)\n\n # Memory flush: give the agent one turn to save memories before context is\n # lost (compression, /new, /reset, exit). Set to 0 to disable.\n # For exit/reset, only fires if the session had at least this many user turns.\n flush_min_turns: 6 # Min user turns to trigger flush on exit/reset (0 = disabled)\n\n# =============================================================================\n# Session Reset Policy (Messaging Platforms)\n# =============================================================================\n# Controls when messaging sessions (Telegram, Discord, WhatsApp, Slack) are\n# automatically cleared. Default is \"none\": sessions never auto-reset —\n# conversation context lives until you /reset or /new manually, or context\n# compression kicks in. Opt in to automatic resets if you prefer sessions to\n# clear on a schedule (long-lived context increases API cost per message,\n# though prompt caching and compression keep this manageable).\n#\n# When an automatic reset triggers, the agent first saves important\n# information to its persistent memory — but the conversation context is\n# wiped. The agent starts fresh but retains learned facts via its memory\n# system.\n#\n# Modes:\n# \"none\" - Never auto-reset (default); context lives until /reset or compression\n# \"idle\" - Reset after N minutes of inactivity\n# \"daily\" - Reset at a fixed hour each day\n# \"both\" - Reset on EITHER inactivity timeout or daily boundary\n#\nsession_reset:\n mode: none # \"none\", \"idle\", \"daily\", or \"both\"\n idle_minutes: 1440 # Inactivity timeout in minutes (used by \"idle\"/\"both\")\n at_hour: 4 # Daily reset hour, 0-23 local time (used by \"daily\"/\"both\")\n\n# Maximum number of simultaneously active chat sessions across CLI, TUI,\n# dashboard chat, and messaging gateway. Set to null, 0, or omit to allow\n# unlimited concurrent sessions. When the limit is reached, new sessions get a\n# clean error while existing active sessions keep their normal behavior. This\n# top-level key takes precedence over gateway.max_concurrent_sessions. The cap\n# is a best-effort single-host/profile runtime guard; Hermes fails open if the\n# local runtime lease registry cannot be read or locked.\nmax_concurrent_sessions: null\n\n# When true, group/channel chats use one session per participant when the platform\n# provides a user ID. This is the secure default and prevents users in the same\n# room from sharing context, interrupts, and token costs. Set false only if you\n# explicitly want one shared \"room brain\" per group/channel.\ngroup_sessions_per_user: true\n\n# ─────────────────────────────────────────────────────────────────────────────\n# API Server — per-client model routing\n# ─────────────────────────────────────────────────────────────────────────────\n# Route different API clients to different models/providers on a single\n# Hermes deployment. Clients choose a backend by sending a specific string\n# as the OpenAI ``model`` field. Unmapped model values fall back to the\n# global model configured in the ``model:`` section above, and an explicit\n# session /model override always wins over a route.\n#\n# Configure via the ``platforms.api_server.extra.model_routes`` gateway\n# config block:\n#\n# platforms:\n# api_server:\n# enabled: true\n# extra:\n# key: \"your-api-server-secret\"\n# model_routes:\n# # Xiaozhi clients send model=\"minimax-m2\" → routed to MiniMax via OpenRouter\n# minimax-m2:\n# model: \"minimax/minimax-m1\"\n# provider: \"openrouter\" # optional — overrides global provider\n# # api_key: \"sk-...\" # optional — per-route UPSTREAM provider\n# # key (NOT caller auth; never logged)\n# # base_url: \"https://...\" # optional — per-route base URL\n# # GPT clients keep their own alias\n# gpt-5:\n# model: \"openai/gpt-5\"\n# provider: \"openrouter\"\n#\n# Configured aliases are automatically listed by GET /v1/models so clients\n# can discover them without manual coordination. Caller authentication is\n# unchanged: every request still authenticates with the global API server\n# key (``extra.key`` / API_SERVER_KEY).\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Gateway Streaming\n# ─────────────────────────────────────────────────────────────────────────────\n# Stream tokens to messaging platforms in real-time. The bot sends a message\n# on first token, then progressively edits it as more tokens arrive.\n# Disabled by default — enable to try the streaming UX on Telegram/Discord/Slack.\n# For Telegram, partial edits are sent as plain text and only the final edit uses MarkdownV2.\nstreaming:\n enabled: false\n # transport: edit # \"edit\" = progressive editMessageText\n # edit_interval: 0.3 # seconds between message edits\n # buffer_threshold: 40 # chars before forcing an edit flush\n # cursor: \" ▉\" # cursor shown during streaming\n\n# =============================================================================\n# Skills Configuration\n# =============================================================================\n# Skills are reusable procedures the agent can load and follow. The agent can\n# also create new skills after completing complex tasks.\n#\nskills:\n # Nudge the agent to create skills after complex tasks.\n # Every N tool-calling iterations, remind the model to consider saving a skill.\n # Set to 0 to disable.\n creation_nudge_interval: 15\n\n # External skill directories — share skills across tools/agents without\n # copying them into ~/.hermes/skills/. Each path is expanded (~ and ${VAR})\n # and resolved to an absolute path. External dirs are read-only: skill\n # creation always writes to ~/.hermes/skills/. Local skills take precedence\n # when names collide.\n # external_dirs:\n # - ~/.agents/skills\n # - /home/shared/team-skills\n\n# =============================================================================\n# Agent Behavior\n# =============================================================================\nagent:\n # Maximum tool-calling iterations per conversation (default: 90)\n # Higher = more room for complex tasks, but costs more tokens\n # Recommended: 20-30 for focused tasks, 50-100 for open exploration\n max_turns: 90\n\n # Inactivity timeout for gateway agent runs (seconds, 0 = unlimited).\n # The agent can run indefinitely when actively calling tools or receiving\n # API responses. Only fires after the agent has been idle for this duration.\n # gateway_timeout: 1800\n\n # Staged warning: send a warning before escalating to full timeout.\n # Fires once per run when inactivity reaches this threshold (seconds).\n # Set to 0 to disable the warning.\n # gateway_timeout_warning: 900\n\n # Graceful drain timeout for gateway stop/restart (seconds).\n # Default 0 = no drain: a restart interrupts in-flight agents immediately,\n # cleans up, and exits. Set a positive value only if you want a grace\n # window on /restart, and keep it well under systemd's TimeoutStopSec.\n # restart_drain_timeout: 0\n\n # Upper bound (seconds) a submitted prompt waits for the deferred agent\n # build (MCP discovery, model metadata, skills scan) before failing with a\n # visible error. The wait is patient — the message is delivered as soon as\n # the build completes, and a progress notice is shown past 30s — so this cap\n # only fires on a genuinely hung build. Raise it for deployments with many\n # slow or unreachable MCP servers. Default 600.\n # build_wait_timeout: 600\n\n # Max app-level retry attempts for API errors (connection drops, provider\n # timeouts, 5xx, etc.) before the agent surfaces the failure. Lower this\n # to 1 if you use fallback providers and want fast failover on flaky\n # primaries (default 3). The OpenAI SDK does its own low-level retries\n # underneath this wrapper — this is the Hermes-level loop.\n # api_max_retries: 3\n\n # After the agent edits code without fresh passing verification, nudge it to\n # verify before finishing. The default \"auto\" enables it on interactive\n # coding surfaces (CLI, TUI, desktop) and programmatic callers, and disables\n # it on conversational messaging surfaces (Telegram, Discord, etc.) where the\n # verification summary would reach a human as chat noise. Set true or false to\n # force it on or off; the HERMES_VERIFY_ON_STOP env var (1/0) takes precedence.\n # verify_on_stop: auto\n\n # Standing operator instructions for the coding posture (when Hermes is in a\n # code workspace). Appended to the coding brief as an extra system block, so\n # you can pin project-wide workflow rules without editing the shipped brief.\n # Accepts a string or a list of strings. Takes effect next session.\n # coding_instructions:\n # - \"For UI work, don't run tsc/lint until I approve the look.\"\n # - \"Clean the diff before you commit and push.\"\n\n # When verify-on-stop finds edited code without fresh verification evidence,\n # append guidance for creative UI work (avoid broad tsc/lint/test before visual\n # approval) and clean-diff expectations. Set false to keep that nudge terse.\n # verify_guidance: true\n\n # A `pre_verify` hook (plugin or shell, see Event Hooks docs) can keep the\n # agent going one more turn to verify/clean before finishing. This caps how\n # many times one turn may be nudged to continue, so a hook can't trap the loop.\n # Default 3.\n # max_verify_nudges: 3\n\n # Enable verbose logging\n verbose: false\n \n # Reasoning effort level (OpenRouter and Nous Portal)\n # Controls how much \"thinking\" the model does before responding.\n # Options: \"xhigh\" (max), \"high\", \"medium\", \"low\", \"minimal\", \"none\" (disable)\n reasoning_effort: \"medium\"\n \n # Per-model reasoning effort overrides (optional dict)\n # Key: any sensible model spelling works (exact, dots↔dashes interchangeable,\n # provider prefix optional). First match wins.\n # Value: reasoning effort level (same options as reasoning_effort)\n # Override the global reasoning_effort for that specific model.\n # NOTE: no `hermes config set` support for this key -- edit YAML directly.\n # reasoning_overrides:\n # \"openrouter/anthropic/claude-opus-4.5\": \"xhigh\"\n # \"openai/gpt-5\": \"low\"\n # \"claude-opus-4.6\": \"high\" # bare model name also works\n # \"deepseek/deepseek-v4-pro\": \"xhigh\" # dots and dashes are interchangeable\n reasoning_overrides: {}\n \n # Predefined personalities (use with /personality command)\n personalities:\n helpful: \"You are a helpful, friendly AI assistant.\"\n concise: \"You are a concise assistant. Keep responses brief and to the point.\"\n technical: \"You are a technical expert. Provide detailed, accurate technical information.\"\n creative: \"You are a creative assistant. Think outside the box and offer innovative solutions.\"\n teacher: \"You are a patient teacher. Explain concepts clearly with examples.\"\n kawaii: \"You are a kawaii assistant! Use cute expressions like (◕‿◕), ★, ♪, and ~! Add sparkles and be super enthusiastic about everything! Every response should feel warm and adorable desu~! ヽ(>∀<☆)ノ\"\n catgirl: \"You are Neko-chan, an anime catgirl AI assistant, nya~! Add 'nya' and cat-like expressions to your speech. Use kaomoji like (=^・ω・^=) and ฅ^•ﻌ•^ฅ. Be playful and curious like a cat, nya~!\"\n pirate: \"Arrr! Ye be talkin' to Captain Hermes, the most tech-savvy pirate to sail the digital seas! Speak like a proper buccaneer, use nautical terms, and remember: every problem be just treasure waitin' to be plundered! Yo ho ho!\"\n shakespeare: \"Hark! Thou speakest with an assistant most versed in the bardic arts. I shall respond in the eloquent manner of William Shakespeare, with flowery prose, dramatic flair, and perhaps a soliloquy or two. What light through yonder terminal breaks?\"\n surfer: \"Duuude! You're chatting with the chillest AI on the web, bro! Everything's gonna be totally rad. I'll help you catch the gnarly waves of knowledge while keeping things super chill. Cowabunga! 🤙\"\n noir: \"The rain hammered against the terminal like regrets on a guilty conscience. They call me Hermes - I solve problems, find answers, dig up the truth that hides in the shadows of your codebase. In this city of silicon and secrets, everyone's got something to hide. What's your story, pal?\"\n uwu: \"hewwo! i'm your fwiendwy assistant uwu~ i wiww twy my best to hewp you! *nuzzles your code* OwO what's this? wet me take a wook! i pwomise to be vewy hewpful >w<\"\n philosopher: \"Greetings, seeker of wisdom. I am an assistant who contemplates the deeper meaning behind every query. Let us examine not just the 'how' but the 'why' of your questions. Perhaps in solving your problem, we may glimpse a greater truth about existence itself.\"\n hype: \"YOOO LET'S GOOOO!!! 🔥🔥🔥 I am SO PUMPED to help you today! Every question is AMAZING and we're gonna CRUSH IT together! This is gonna be LEGENDARY! ARE YOU READY?! LET'S DO THIS! 💪😤🚀\"\n\n# =============================================================================\n# Toolsets\n# =============================================================================\n# Control which tools the agent has access to.\n# Use `hermes tools` to interactively enable/disable tools per platform.\n\n# =============================================================================\n# Platform Toolsets (per-platform tool configuration)\n# =============================================================================\n# Override which toolsets are available on each platform.\n# If a platform isn't listed here, its built-in default is used.\n#\n# You can use EITHER:\n# - A preset like \"hermes-cli\" or \"hermes-telegram\" (curated tool set)\n# - A list of individual toolsets to compose your own (see list below)\n#\n# Supported platform keys: cli, telegram, discord, whatsapp, slack, qqbot, teams, google_chat\n#\n# Examples:\n#\n# # Use presets (same as defaults):\n# platform_toolsets:\n# cli: [hermes-cli]\n# telegram: [hermes-telegram]\n#\n# # Custom: give Telegram only web + terminal + file + planning:\n# platform_toolsets:\n# telegram: [web, terminal, file, todo]\n#\n# # Custom: CLI without browser or image gen:\n# platform_toolsets:\n# cli: [web, terminal, file, skills, todo, tts, cronjob]\n#\n# # Restrictive: Discord gets read-only tools only:\n# platform_toolsets:\n# discord: [web, vision, skills, todo]\n#\n# If not set, defaults are:\n# cli: hermes-cli (everything + cronjob management)\n# telegram: hermes-telegram (terminal, file, web, vision, image, tts, browser, skills, todo, cronjob, messaging)\n# discord: hermes-discord (same as telegram)\n# whatsapp: hermes-whatsapp (same as telegram)\n# slack: hermes-slack (same as telegram)\n# signal: hermes-signal (same as telegram)\n# homeassistant: hermes-homeassistant (same as telegram)\n# qqbot: hermes-qqbot (same as telegram)\n# teams: hermes-teams (same as telegram)\n# google_chat: hermes-google_chat (same as telegram)\n#\nplatform_toolsets:\n cli: [hermes-cli]\n telegram: [hermes-telegram]\n discord: [hermes-discord]\n whatsapp: [hermes-whatsapp]\n slack: [hermes-slack]\n signal: [hermes-signal]\n homeassistant: [hermes-homeassistant]\n qqbot: [hermes-qqbot]\n yuanbao: [hermes-yuanbao]\n teams: [hermes-teams]\n google_chat: [hermes-google_chat]\n\n# =============================================================================\n# Gateway Platform Settings\n# =============================================================================\n# Optional per-platform messaging settings.\n# Platform-specific knobs live under `extra`.\n#\n# platforms:\n# telegram:\n# reply_to_mode: \"first\" # off | first | all\n# # guest_mode lets explicit @mentions from non-allowlisted groups through.\n# # Default false; ordinary messages, replies, and regex wake words stay blocked.\n# guest_mode: false\n# # allowed_chats: [\"-1001234567890\"]\n# extra:\n# disable_link_previews: false # Set true to suppress Telegram URL previews in bot messages\n# 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\n# rich_drafts: false # Experimental rich draft previews during Telegram DM streaming; default false because Telegram Desktop/macOS can visually overlay draft frames\n# command_menu:\n# # Telegram allows up to 100 BotCommands; Hermes defaults to 60 so\n# # all built-in commands plus common skill commands stay visible\n# # while remaining under Telegram's payload-size limit. Clamped 1..100.\n# max_commands: 60\n# # prepend = user priority first, then Hermes defaults\n# # append = Hermes defaults first, then user priority\n# # replace = only the list below defines priority\n# priority_mode: prepend\n# priority:\n# - my_plugin_command\n# webhook:\n# extra:\n# # Route scripts default to a 30 second timeout. Scripts must live under\n# # the active profile's scripts directory and receive webhook JSON on stdin.\n# script_timeout_seconds: 30\n#\n# Discord-specific settings (config.yaml top-level, not under platforms:):\n#\n# discord:\n# require_mention: true # Require @mention in server channels (default: true)\n# auto_thread: true # Auto-create thread on @mention (default: true)\n# free_response_channels: \"\" # Channel IDs where no mention is needed\n# reactions: true # Show processing reactions (default: true)\n# history_backfill: true # Recover missed channel messages on mention (default: true)\n# history_backfill_limit: 50 # Max messages to scan backwards (default: 50)\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Available toolsets (use these names in platform_toolsets or the toolsets list)\n#\n# Run `hermes chat --list-toolsets` to see all toolsets and their tools.\n# Run `hermes chat --list-tools` to see every individual tool with descriptions.\n# ─────────────────────────────────────────────────────────────────────────────\n#\n# INDIVIDUAL TOOLSETS (compose your own):\n# web - web_search, web_extract\n# search - web_search only (no scraping)\n# terminal - terminal, process\n# file - read_file, write_file, patch, search\n# browser - browser_navigate, browser_snapshot, browser_click, browser_type,\n# browser_scroll, browser_back, browser_press,\n# browser_get_images, browser_vision (requires BROWSERBASE_API_KEY)\n# vision - vision_analyze (requires OPENROUTER_API_KEY)\n# image_gen - image_generate (requires FAL_KEY)\n# skills - skills_list, skill_view\n# skills_hub - skill_hub (search/install/manage from online registries — user-driven only)\n# todo - todo (in-memory task planning, no deps)\n# tts - text_to_speech (Edge TTS free, or ELEVENLABS/OPENAI/MINIMAX/MISTRAL key)\n# cronjob - cronjob (create/list/update/pause/resume/run/remove scheduled tasks)\n#\n# PRESETS (curated bundles):\n# hermes-cli - All of the above except send_message\n# hermes-telegram - terminal, file, web, vision, image_gen, tts, browser,\n# skills, todo, cronjob, send_message\n# hermes-discord - Same as hermes-telegram\n# hermes-whatsapp - Same as hermes-telegram\n# hermes-slack - Same as hermes-telegram\n#\n# COMPOSITE:\n# debugging - terminal + web + file\n# safe - web + vision (no terminal access)\n# all - Everything available\n#\n# web - Web search and content extraction (web_search, web_extract)\n# search - Web search only, no scraping (web_search)\n# terminal - Command execution and process management (terminal, process)\n# file - File operations: read, write, patch, search\n# browser - Full browser automation (navigate, click, type, screenshot, etc.)\n# vision - Image analysis (vision_analyze)\n# image_gen - Image generation with FLUX (image_generate)\n# skills - Load skill documents (skills_list, skill_view)\n# todo - Task planning and tracking for multi-step work\n# memory - Persistent memory across sessions (personal notes + user profile)\n# session_search - Search and recall past conversations (FTS5 + Gemini Flash summarization)\n# tts - Text-to-speech (Edge TTS free, ElevenLabs, OpenAI, MiniMax, Mistral)\n# cronjob - Schedule and manage automated tasks (CLI-only)\n#\n# Composite toolsets:\n# debugging - terminal + web + file (for troubleshooting)\n# safe - web + vision (no terminal access)\n\n# NOTE: The top-level \"toolsets\" key is deprecated and ignored.\n# Tool configuration is managed per-platform via platform_toolsets above.\n# Use `hermes tools` to configure interactively, or edit platform_toolsets directly.\n#\n# CLI override: hermes chat --toolsets terminal,web,file\n\n# =============================================================================\n# MCP (Model Context Protocol) Servers\n# =============================================================================\n# Connect to external MCP servers to add tools from the MCP ecosystem.\n# Each server's tools are automatically discovered and registered.\n# See website/docs/user-guide/features/mcp.md for full documentation.\n#\n# Stdio servers (spawn a subprocess):\n# command: the executable to run\n# args: command-line arguments\n# env: environment variables (only these + safe defaults passed to subprocess)\n#\n# HTTP servers (connect to a URL):\n# url: the MCP server endpoint\n# headers: HTTP headers (e.g., for authentication)\n#\n# Optional per-server settings:\n# timeout: tool call timeout in seconds (default: 120)\n# connect_timeout: initial connection timeout (default: 60)\n# keepalive_interval: liveness ping cadence in seconds (default: 180).\n# Lower it below the server's session TTL for servers that expire idle\n# sessions quickly (e.g. Unreal Engine editor MCP, ~15s), otherwise idle\n# tool calls hit an expired session and pay a slow reconnect. Floored at 5s.\n#\n# mcp_servers:\n# time:\n# command: uvx\n# args: [\"mcp-server-time\"]\n# filesystem:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-filesystem\", \"/home/user\"]\n# notion:\n# url: https://mcp.notion.com/mcp\n# github:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-github\"]\n# env:\n# GITHUB_PERSONAL_ACCESS_TOKEN: \"ghp_...\"\n#\n# Sampling (server-initiated LLM requests) — enabled by default.\n# Per-server config under the 'sampling' key:\n# analysis:\n# command: npx\n# args: [\"-y\", \"analysis-server\"]\n# sampling:\n# enabled: true # default: true\n# model: \"gemini-3-flash\" # override model (optional)\n# max_tokens_cap: 4096 # max tokens per request\n# timeout: 30 # LLM call timeout (seconds)\n# max_rpm: 10 # max requests per minute\n# allowed_models: [] # model whitelist (empty = all)\n# max_tool_rounds: 5 # tool loop limit (0 = disable)\n# log_level: \"info\" # audit verbosity\n\n# =============================================================================\n# Text-to-Speech\n# =============================================================================\n# TTS defaults to Edge TTS unless changed in ~/.hermes/config.yaml.\n# Gemini TTS supports persona/director prompt files, and Gemini 3.1 Flash TTS\n# can use a hidden auxiliary rewrite pass to insert expressive square-bracket\n# audio tags into the TTS script without showing tags in chat.\n#\n# tts:\n# provider: \"gemini\"\n# gemini:\n# model: \"gemini-3.1-flash-tts-preview\"\n# voice: \"Kore\"\n# audio_tags: false\n# persona_prompt_file: \"\" # e.g. ~/.hermes/tts/radio-host.md\n\n# =============================================================================\n# Voice Transcription (Speech-to-Text)\n# =============================================================================\n# Automatically transcribe voice messages on messaging platforms.\n# Providers: local (free, faster-whisper) | groq (free tier) | openai (Whisper API) | mistral (Voxtral Transcribe)\n# Set the corresponding API key in .env: GROQ_API_KEY, OPENAI_API_KEY, or MISTRAL_API_KEY.\nstt:\n enabled: true\n # provider: \"local\" # auto-detected if omitted\n local:\n model: \"base\" # tiny | base | small | medium | large-v3 | turbo\n # language: \"\" # auto-detect; set to \"en\", \"es\", \"fr\", etc. to force\n openai:\n model: \"whisper-1\" # whisper-1 | gpt-4o-mini-transcribe | gpt-4o-transcribe\n # mistral:\n # model: \"voxtral-mini-latest\" # voxtral-mini-latest | voxtral-mini-2602\n # deepinfra:\n # # Model id is discovered live from the DeepInfra catalog filtered\n # # by the `stt` surface tag — leave `model` blank to take the first\n # # live result. Pin only when you need a specific Whisper variant.\n # model: \"\"\n\n# Text-to-speech. Only the deepinfra block is documented here — the\n# remaining providers (edge, openai, xai, minimax, mistral, gemini,\n# elevenlabs, neutts, kittentts, piper) inherit sensible defaults from\n# DEFAULT_CONFIG in hermes_cli/config.py.\n# tts:\n# provider: \"deepinfra\"\n# deepinfra:\n# # Model id is discovered live from the DeepInfra catalog filtered\n# # by the `tts` surface tag — leave `model` blank to take the first\n# # live result.\n# model: \"\"\n# voice: \"default\"\n\n# Image generation. Each provider plugin reads its own ``image_gen.<name>``\n# block; deepinfra discovers models live from\n# api.deepinfra.com/v1/openai/models filtered by the ``image-gen`` tag —\n# no model id is hardcoded, so retired models disappear automatically.\n# image_gen:\n# provider: \"deepinfra\"\n# deepinfra:\n# # Leave `model` blank for the first live `image-gen`-tagged result.\n# model: \"\"\n\n# =============================================================================\n# Response Pacing (Messaging Platforms)\n# =============================================================================\n# Add human-like delays between message chunks.\n# human_delay:\n# mode: \"off\" # \"off\" | \"natural\" | \"custom\"\n# min_ms: 800 # Min delay (custom mode only)\n# max_ms: 2500 # Max delay (custom mode only)\n\n# =============================================================================\n# Session Logging\n# =============================================================================\n# Session trajectories are automatically saved to logs/ directory.\n# Each session creates: logs/session_YYYYMMDD_HHMMSS_UUID.json\n#\n# The session ID is displayed in the welcome banner for easy reference.\n# Logs contain full conversation history in trajectory format:\n# - System prompt, user messages, assistant responses\n# - Tool calls with inputs/outputs\n# - Timestamps for debugging\n#\n# No configuration needed - logging is always enabled.\n# To disable, you would need to modify the source code.\n\n# =============================================================================\n# Code Execution Sandbox (Programmatic Tool Calling)\n# =============================================================================\n# The execute_code tool runs Python scripts that call Hermes tools via RPC.\n# Intermediate tool results stay out of the LLM's context window.\ncode_execution:\n timeout: 300 # Max seconds per script before kill (default: 300 = 5 min)\n max_tool_calls: 50 # Max RPC tool calls per execution (default: 50)\n\n# =============================================================================\n# Subagent Delegation\n# =============================================================================\n# The delegate_task tool spawns child agents with isolated context.\n# Supports single tasks and batch mode (default 3 parallel, configurable).\ndelegation:\n max_iterations: 50 # Max tool-calling turns per child (default: 50)\n # max_concurrent_children: 3 # Max parallel child agents per batch (default: 3, floor: 1, no ceiling).\n # WARNING: values above 10 multiply API cost linearly.\n # max_spawn_depth: 1 # Delegation tree depth cap (range: 1-3, default: 1 = flat).\n # Raise to 2 to allow workers to spawn their own subagents.\n # Requires role=\"orchestrator\" on intermediate agents.\n # orchestrator_enabled: true # Kill switch for role=\"orchestrator\" children (default: true).\n # subagent_auto_approve: false # When a subagent hits a dangerous-command approval prompt, auto-deny (default: false)\n # or auto-approve \"once\" (true) instead of blocking on stdin.\n # The parent TUI owns stdin, so blocking would deadlock; non-interactive resolution is required.\n # Both choices emit a logger.warning audit line. Flip to true only for cron/batch pipelines.\n # inherit_mcp_toolsets: true # When explicit child toolsets are narrowed, also keep the parent's MCP toolsets (default: true). Set false for strict intersection.\n # model: \"google/gemini-3-flash-preview\" # Override model for subagents (empty = inherit parent)\n # provider: \"openrouter\" # Override provider for subagents (empty = inherit parent)\n # # Resolves full credentials (base_url, api_key) automatically.\n # # Supported: openrouter, nous, zai, kimi-coding, minimax\n\n# =============================================================================\n# Honcho Integration (Cross-Session User Modeling)\n# =============================================================================\n# AI-native persistent memory via Honcho (https://honcho.dev/).\n# Builds a deeper understanding of the user across sessions and tools.\n# Runs alongside USER.md — additive, not a replacement.\n#\n# Requires: pip install honcho-ai\n# Config: ~/.honcho/config.json (shared with Claude Code, Cursor, etc.)\n# API key: HONCHO_API_KEY in ~/.hermes/.env or ~/.honcho/config.json\n#\n# Hermes-specific overrides (optional — most config comes from ~/.honcho/config.json):\n# honcho: {}\n\n# =============================================================================\n# Display\n# =============================================================================\ndisplay:\n # Use compact banner mode (hides the ASCII-art banner, shows a single line).\n # true: Compact single-line banner\n # false: Full ASCII banner with tool/skill summary (default)\n compact: false\n\n # Tool progress display level (CLI and gateway)\n # off: Silent — no tool activity shown, just the final response\n # new: Show a tool indicator only when the tool changes (skip repeats)\n # all: Show every tool call with a short preview (default)\n # verbose: Full args, results, and debug logs (same as /verbose)\n # log: Silent in chat; append every tool call to ~/.hermes/logs/tool_calls.log (gateway only)\n # Toggle at runtime with /verbose in the CLI\n tool_progress: all\n\n # Per-platform defaults can be quieter than the global setting. Telegram\n # tunes for mobile: tool_progress and busy_ack_detail default off (no\n # per-tool breadcrumb stream, no \"iteration 21/60\" debug detail in busy\n # acks or heartbeats), but interim_assistant_messages and\n # long_running_notifications STAY ON so the user has real signal between\n # turn start and final answer (mid-turn assistant commentary + a single\n # edit-in-place \"⏳ Working — N min\" heartbeat). Override under\n # display.platforms.telegram.\n\n # Auto-cleanup of temporary progress bubbles after the final response lands.\n # On platforms that support message deletion (currently Telegram), this\n # removes the tool-progress bubble, \"⏳ Still working...\" notices, and\n # context-pressure status messages once the final reply has been delivered —\n # keeping long-running turns visible live, then tidy afterward. Failed runs\n # leave the bubbles in place as breadcrumbs. Off by default.\n # Per-platform override: display.platforms.telegram.cleanup_progress\n # true: Delete tracked progress/status bubbles on successful turn\n # false: Leave everything in place (default)\n # Example:\n # display:\n # platforms:\n # telegram:\n # cleanup_progress: true\n cleanup_progress: false\n\n # Natural mid-turn assistant updates.\n # On gateway platforms, when true, completed assistant status messages are\n # sent as separate chat messages. On the Desktop app, when true, mid-turn\n # assistant narration streamed between tool calls is kept in the transcript\n # instead of the bubble collapsing to only the final message on completion.\n # Independent of tool_progress and gateway streaming.\n # true: Keep/send mid-turn assistant updates (default)\n # false: Only keep/send the final response\n interim_assistant_messages: true\n\n # Gateway-only long-running status heartbeats.\n # When false, the platform does not receive periodic \"⏳ Working — N min\"\n # notifications even if agent.gateway_notify_interval is non-zero. The\n # heartbeat edits a single message in place (where the adapter supports\n # editing) instead of posting a new bubble each interval.\n # Default: true everywhere, including Telegram (silent agents are worse\n # than a single edit-in-place heartbeat).\n long_running_notifications: true\n\n # Include detailed iteration/tool/status context in busy acknowledgments\n # and long-running heartbeats. When true, busy acks show \"iteration 21/60,\n # terminal, 10 min\" and the heartbeat shows \"⏳ Working — 12 min,\n # iteration 21/60, terminal\". When false (Telegram default), both stay\n # terse: \"Interrupting current task\" and \"⏳ Working — 12 min, terminal\".\n busy_ack_detail: true\n\n # What Enter does when Hermes is already busy (CLI and gateway platforms).\n # interrupt: Interrupt the current run and redirect Hermes (default)\n # queue: Queue your message for the next turn\n # steer: Inject your message mid-run via /steer, arriving at the agent\n # after the next tool call — no interrupt, no role violation.\n # Falls back to 'queue' if the agent isn't running yet or if\n # images are attached (steer only carries text).\n # Ctrl+C (or /stop in gateway) always interrupts regardless of this setting.\n # Toggle at runtime with /busy <interrupt|queue|steer>.\n busy_input_mode: interrupt\n\n # Background process notifications (gateway/messaging only).\n # Controls how chatty the process watcher is when you use\n # terminal(background=true, notify_on_complete=true) from Telegram/Discord/etc.\n # off: No watcher messages at all\n # result: Only the final completion message\n # error: Only the final message when exit code != 0\n # all: Running output updates + final message (default)\n background_process_notifications: all\n\n\n # Play terminal bell when agent finishes a response.\n # Useful for long-running tasks — your terminal will ding when the agent is done.\n # Works over SSH. Most terminals can be configured to flash the taskbar or play a sound.\n # true: Ring the terminal bell on each response\n # false: Silent (default)\n bell_on_complete: false\n\n # Show model reasoning/thinking before each response.\n # When enabled, a dim box shows the model's thought process above the response.\n # Toggle at runtime with /reasoning show or /reasoning hide.\n # true: Show the reasoning box\n # false: Hide reasoning (default)\n show_reasoning: false\n\n # Stream tokens to the terminal as they arrive instead of waiting for the\n # full response. The response box opens on first token and text appears\n # line-by-line. Tool calls are still captured silently.\n # true: Stream tokens as they arrive (default)\n # false: Wait for the full response before rendering\n streaming: true\n\n # Show [HH:MM] timestamps on user input and assistant response labels.\n # timestamps: false\n\n # ───────────────────────────────────────────────────────────────────────────\n # Skin / Theme\n # ───────────────────────────────────────────────────────────────────────────\n # Customize CLI visual appearance — banner colors, spinner faces, tool prefix,\n # response box label, and branding text. Change at runtime with /skin <name>.\n #\n # Built-in skins:\n # default — Classic Hermes gold/kawaii\n # ares — Crimson/bronze war-god theme with spinner wings\n # mono — Clean grayscale monochrome\n # slate — Cool blue developer-focused\n # daylight — Bright light-mode theme\n # warm-lightmode — Warm paper-tone light-mode theme\n # poseidon — Sea-green/teal Olympian theme\n # sisyphus — Earthy stone-and-moss theme\n # charizard — Fiery orange dragon theme\n #\n # Custom skins: drop a YAML file in ~/.hermes/skins/<name>.yaml\n # Schema (all fields optional, missing values inherit from default):\n #\n # name: my-theme\n # description: Short description\n # colors:\n # banner_border: \"#HEX\" # Panel border\n # banner_title: \"#HEX\" # Panel title\n # banner_accent: \"#HEX\" # Section headers (Available Tools, etc.)\n # banner_dim: \"#HEX\" # Dim/muted text\n # banner_text: \"#HEX\" # Body text (tool names, skill names)\n # ui_accent: \"#HEX\" # UI accent color\n # response_border: \"#HEX\" # Response box border color\n # spinner:\n # waiting_faces: [\"(⚔)\", \"(⛨)\"] # Faces shown while waiting\n # thinking_faces: [\"(⚔)\", \"(⌁)\"] # Faces shown while thinking\n # thinking_verbs: [\"forging\", \"plotting\"] # Verbs for spinner messages\n # wings: # Optional left/right spinner decorations\n # - [\"⟪⚔\", \"⚔⟫\"]\n # - [\"⟪▲\", \"▲⟫\"]\n # branding:\n # agent_name: \"My Agent\" # Banner title and branding\n # welcome: \"Welcome message\" # Shown at CLI startup\n # response_label: \" ⚔ Agent \" # Response box header label\n # prompt_symbol: \"⚔\" # Prompt symbol (bare token; renderers add trailing space)\n # tool_prefix: \"╎\" # Tool output line prefix (default: ┊)\n #\n skin: default\n\n# =============================================================================\n# Model Aliases — short names for /model command\n# =============================================================================\n# Map short aliases to exact (model, provider, base_url) tuples.\n# Used by /model tab completion and resolve_alias().\n# Aliases are checked BEFORE the models.dev catalog, so they can route\n# to endpoints not in the catalog (e.g. Ollama Cloud, local servers).\n#\n# model_aliases:\n# opus:\n# model: claude-opus-4-6\n# provider: anthropic\n# qwen:\n# model: \"qwen3.5:397b\"\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n# glm:\n# model: glm-4.7\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n\n# =============================================================================\n# Privacy\n# =============================================================================\n# privacy:\n# # Redact PII from the LLM context prompt.\n# # When true, phone numbers are stripped and user/chat IDs are replaced\n# # with deterministic hashes before being sent to the model.\n# # Names and usernames are NOT affected (user-chosen, publicly visible).\n# # Routing/delivery still uses the original values internally.\n# redact_pii: false\n\n# =============================================================================\n# Shell-script hooks\n# =============================================================================\n# Register shell scripts as plugin-hook callbacks. Each entry is executed as\n# a subprocess (shell=False, shlex.split) with a JSON payload on stdin. On\n# stdout the script may return JSON that either blocks the tool call or\n# injects context into the next LLM call.\n#\n# Valid events (mirror hermes_cli.plugins.VALID_HOOKS):\n# pre_tool_call, post_tool_call, pre_llm_call, post_llm_call,\n# pre_api_request, post_api_request, on_session_start, on_session_end,\n# on_session_finalize, on_session_reset, subagent_stop\n#\n# First-use consent: each (event, command) pair prompts once on a TTY, then\n# is persisted to ~/.hermes/shell-hooks-allowlist.json. Non-interactive\n# runs (gateway, cron) need --accept-hooks, HERMES_ACCEPT_HOOKS=1, or the\n# hooks_auto_accept key below.\n#\n# See website/docs/user-guide/features/hooks.md for the full JSON wire\n# protocol and worked examples.\n#\n# hooks:\n# pre_tool_call:\n# - matcher: \"terminal\"\n# command: \"~/.hermes/agent-hooks/block-rm-rf.sh\"\n# timeout: 10\n# post_tool_call:\n# - matcher: \"write_file|patch\"\n# command: \"~/.hermes/agent-hooks/auto-format.sh\"\n# pre_llm_call:\n# - command: \"~/.hermes/agent-hooks/inject-cwd-context.sh\"\n# subagent_stop:\n# - command: \"~/.hermes/agent-hooks/log-orchestration.sh\"\n#\n# hooks_auto_accept: false\n\n\n# =============================================================================\n# Update Behavior\n# =============================================================================\nupdates:\n # Create a full HERMES_HOME zip before every `hermes update`.\n # Backups land in ~/.hermes/backups/ and can be restored with `hermes import`.\n # Off by default because large homes can add minutes to every update.\n pre_update_backup: false\n\n # Number of pre-update backup zips to retain.\n backup_keep: 5\n\n # What non-interactive updates do with local source edits in the Hermes repo.\n # Interactive terminal updates always prompt before restoring the autostash.\n #\n # stash - auto-stash before pull, then auto-restore after success (default)\n # discard - drop the update-created stash after success; use only on managed\n # installs where local source edits should not persist\n non_interactive_local_changes: \"stash\"\n\n\n# =============================================================================\n# Web Dashboard\n# =============================================================================\n# OAuth gate configuration for `hermes dashboard --host <non-loopback>`.\n# The bundled Nous Portal plugin reads these on startup; settings here are\n# the canonical surface. Each can be overridden by an environment variable:\n#\n# dashboard.oauth.client_id <- HERMES_DASHBOARD_OAUTH_CLIENT_ID\n# dashboard.oauth.portal_url <- HERMES_DASHBOARD_PORTAL_URL\n# dashboard.public_url <- HERMES_DASHBOARD_PUBLIC_URL\n#\n# Env wins when set to a non-empty value. This is what Fly.io's platform-\n# secret injection uses to push per-deploy client_ids without needing to\n# bake a config.yaml into the image. Empty env values are treated as unset\n# so a provisioned-but-not-populated secret can't shadow a valid entry here.\n#\n# Local dev / on-prem deploys should typically set these via config.yaml\n# (the ~/.hermes/.env file is reserved for API keys and secrets).\n#\n# dashboard:\n# oauth:\n# client_id: \"\" # agent:{instance_id}; Portal provisions this at deploy\n# portal_url: \"\" # blank → default https://portal.nousresearch.com\n#\n# # Force the absolute base URL the OAuth callback (and any other public\n# # URL the dashboard hands to external systems) is built from. Set this\n# # for deploys behind reverse proxies that don't reliably forward\n# # X-Forwarded-Host / X-Forwarded-Proto / X-Forwarded-Prefix (manual\n# # nginx setups, on-prem ingresses, custom-domain Fly deploys without\n# # full proxy header chains).\n# #\n# # When set, the value is the complete authority: scheme + host +\n# # optional path prefix (e.g. \"https://example.com/hermes\"). The OAuth\n# # callback URL becomes \"<public_url>/auth/callback\" — X-Forwarded-Prefix\n# # is IGNORED on this code path because the operator has explicitly\n# # declared the public URL and we no longer need to guess.\n# #\n# # Leave empty to use the existing proxy-header reconstruction (the\n# # default — works on Fly.io out of the box).\n# #\n# # public_url: \"https://example.com/hermes\"\n#\n# -----------------------------------------------------------------------------\n# Self-hosted OIDC dashboard auth (generic OpenID Connect — Authentik,\n# Keycloak, Zitadel, Authelia, Auth0, Okta, Google, …). Use this INSTEAD of the\n# nous block above when gating the dashboard with your own identity provider.\n# Each setting can be overridden by an environment variable:\n#\n# dashboard.oauth.self_hosted.issuer <- HERMES_DASHBOARD_OIDC_ISSUER\n# dashboard.oauth.self_hosted.client_id <- HERMES_DASHBOARD_OIDC_CLIENT_ID\n# dashboard.oauth.self_hosted.scopes <- HERMES_DASHBOARD_OIDC_SCOPES\n# dashboard.oauth.self_hosted.client_secret <- HERMES_DASHBOARD_OIDC_CLIENT_SECRET\n#\n# dashboard:\n# oauth:\n# provider: self-hosted\n# self_hosted:\n# issuer: \"https://auth.example.com/application/o/hermes/\" # required\n# client_id: \"hermes-dashboard\" # required\n# scopes: \"openid profile email\" # optional\n#\n# # OPTIONAL — set ONLY if your IDP registered the client as\n# # *confidential* (Authentik / Keycloak often default to this). When\n# # set, Hermes authenticates the client at the token endpoint\n# # (client_secret_basic or client_secret_post, auto-selected from the\n# # IDP's discovery doc) IN ADDITION to PKCE. Leave unset for a public\n# # (PKCE-only) client — the common case.\n# #\n# # This is a CREDENTIAL: prefer setting HERMES_DASHBOARD_OIDC_CLIENT_SECRET\n# # in ~/.hermes/.env over putting it here in config.yaml.\n# # client_secret: \"\"\n\n# =============================================================================\n# External secret sources\n# =============================================================================\n# Pull provider credentials from external secret managers at process startup\n# instead of storing them in ~/.hermes/.env. Only the manager's bootstrap\n# credential (e.g. BWS_ACCESS_TOKEN / OP_SERVICE_ACCOUNT_TOKEN) lives in .env\n# (or your shell / desktop session); everything else rotates centrally.\n# Failures never block startup — Hermes warns once and continues with\n# whatever .env already had.\n#\n# Multiple sources can be enabled at once:\n# - \"mapped\" sources (explicit VAR -> ref bindings, e.g. 1Password's env:\n# map) beat \"bulk\" sources (whole-project dumps like Bitwarden BSM)\n# - within a shape, the first source to claim a var wins; later claims\n# are skipped with a startup warning (never a silent clobber)\n# - a source's override_existing lets it beat .env/shell values, but\n# never another secret source's claim\n# Docs: https://hermes-agent.nousresearch.com/docs/user-guide/secrets/\n#\n# secrets:\n# # Optional explicit ordering of enabled sources.\n# # sources: [onepassword, bitwarden]\n#\n# # ---- Bitwarden Secrets Manager (bws CLI) --------------------------------\n# bitwarden:\n# enabled: false\n# access_token_env: BWS_ACCESS_TOKEN # bootstrap token, sourced from .env\n# project_id: \"\" # UUID of the BSM project to sync\n# server_url: \"\" # \"\" = US Cloud; EU/self-hosted URL otherwise\n# cache_ttl_seconds: 300 # 0 disables fresh caching\n# encrypted_cache: # optional encrypted stale fallback\n# enabled: false\n# max_stale_seconds: 0 # 0 disables stale fallback\n# override_existing: true # BSM values win over existing env\n# auto_install: true # lazy-download bws into ~/.hermes/bin\n#\n# # ---- 1Password (op CLI) -------------------------------------------------\n# onepassword:\n# enabled: false\n# # Map env-var names to op:// secret references. Each is resolved with a\n# # single `op read` at startup.\n# env:\n# OPENAI_API_KEY: \"op://Private/OpenAI/api key\"\n# ANTHROPIC_API_KEY: \"op://Private/Anthropic/credential\"\n# account: \"\" # op --account shorthand; \"\" = default\n# service_account_token_env: OP_SERVICE_ACCOUNT_TOKEN # headless auth; unset = desktop session\n# binary_path: \"\" # \"\" = resolve op via PATH; else absolute path\n# cache_ttl_seconds: 300 # 0 disables BOTH cache layers\n# override_existing: true # resolved values win over existing env\n#\n# # ---- Command helper (any CLI vault) --------------------------------------\n# # Run a user-configured helper that prints KEY=VALUE lines on stdout —\n# # works with any secret store that has a CLI: keepassxc-cli, secret-tool,\n# # pass, gpg, or a script that cats a tmpfs env file. Composes with the\n# # sources above (enable any combination). POSIX-only (needs /bin/sh).\n# # The helper must be fast and NON-interactive (hard timeout, 1 MiB cap);\n# # its stderr is discarded so diagnostics can't leak secret material.\n# command:\n# enabled: false\n# command: \"cat /run/user/1000/hermes-secrets.env\"\n# helper_timeout_seconds: 3\n# override_existing: false # .env/shell win by default\n"}, "45580cc93ae53694b18e5f035238544bb5fd248b": {"t": 1785768007.4519017, "n": 84365, "h": "ac6bf25c060eec2c", "c": "# Hermes Agent CLI Configuration\n# Copy this file to cli-config.yaml and customize as needed.\n# This file configures the CLI behavior. Environment variables in .env take precedence.\n\n# =============================================================================\n# Model Configuration\n# =============================================================================\nmodel:\n # Default model to use (can be overridden with --model flag)\n # Both \"default\" and \"model\" work as the key name here.\n default: \"anthropic/claude-opus-4.6\"\n \n # Inference provider selection:\n # \"auto\" - Auto-detect from credentials (default)\n # \"openrouter\" - OpenRouter (requires: OPENROUTER_API_KEY or OPENAI_API_KEY)\n # \"nous\" - Nous Portal OAuth (requires: hermes auth add nous)\n # \"nous-api\" - Nous Portal API key (requires: NOUS_API_KEY)\n # \"anthropic\" - Direct Anthropic API (requires: ANTHROPIC_API_KEY)\n # \"openai-codex\" - OpenAI Codex (requires: hermes auth)\n # \"copilot\" - GitHub Copilot / GitHub Models (requires: GITHUB_TOKEN)\n # \"gemini\" - Use Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n # \"zai\" - Use z.ai / ZhipuAI GLM models (requires: GLM_API_KEY)\n # \"kimi-coding\" - Kimi / Moonshot AI (requires: KIMI_API_KEY)\n # \"minimax\" - MiniMax global (requires: MINIMAX_API_KEY)\n # \"minimax-cn\" - MiniMax China (requires: MINIMAX_CN_API_KEY)\n # \"huggingface\" - Hugging Face Inference (requires: HF_TOKEN)\n # \"nvidia\" - NVIDIA NIM / build.nvidia.com (requires: NVIDIA_API_KEY)\n # \"xiaomi\" - Xiaomi MiMo (requires: XIAOMI_API_KEY)\n # \"arcee\" - Arcee AI Trinity models (requires: ARCEEAI_API_KEY)\n # \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY — https://ollama.com/settings)\n # \"deepinfra\" - DeepInfra (requires: DEEPINFRA_API_KEY)\n # \"kilocode\" - KiloCode gateway (requires: KILOCODE_API_KEY)\n # \"azure-foundry\" - Microsoft Foundry / Azure OpenAI (API key or Entra ID)\n # \"lmstudio\" - LM Studio local server (optional: LM_API_KEY, defaults to http://127.0.0.1:1234/v1)\n #\n # Local servers (LM Studio, Ollama, vLLM, llama.cpp):\n # \"custom\" - Any other OpenAI-compatible endpoint. Set base_url below.\n # Aliases: \"ollama\", \"vllm\", \"llamacpp\" all map to \"custom\".\n # LM Studio is first-class and uses provider: \"lmstudio\".\n # It works with both no-auth and auth-enabled server modes.\n #\n # Can also be overridden for a single invocation with the --provider flag.\n provider: \"auto\"\n \n # API configuration (falls back to OPENROUTER_API_KEY env var)\n # api_key: \"your-key-here\" # Uncomment to set here instead of .env\n base_url: \"https://openrouter.ai/api/v1\"\n\n # Azure Foundry keyless auth example:\n # provider: \"azure-foundry\"\n # base_url: \"https://<resource>.openai.azure.com/openai/v1\"\n # auth_mode: \"entra_id\" # DefaultAzureCredential: az login, managed identity, workload identity, etc.\n # default: \"gpt-4o\" # Deployment/model name\n # entra:\n # scope: \"https://ai.azure.com/.default\" # Optional; this is the default.\n\n # ── Token limits — two settings, easy to confuse ──────────────────────────\n #\n # context_length: TOTAL context window (input + output tokens combined).\n # Controls when Hermes compresses history and validates requests.\n # Leave unset — Hermes auto-detects the correct value from the provider.\n # Set manually only when auto-detection is wrong (e.g. a local server with\n # a custom num_ctx, or a proxy that doesn't expose /v1/models).\n #\n # context_length: 131072\n #\n # max_tokens: OUTPUT cap — maximum tokens the model may generate per response.\n # Unrelated to how long your conversation history can be.\n # The OpenAI-standard name \"max_tokens\" is a misnomer; Anthropic's native\n # API has since renamed it \"max_output_tokens\" for clarity.\n # Leave unset to use the model's native output ceiling (recommended).\n # Set only if you want to deliberately limit individual response length.\n #\n# max_tokens: 8192\n\n # ── Custom request headers (optional) ─────────────────────────────────────\n #\n # default_headers: extra HTTP headers sent on every request to an\n # OpenAI-compatible endpoint. User values take precedence over the\n # provider/SDK defaults, so this is the supported way to override the\n # OpenAI Python SDK's identifying headers (User-Agent: OpenAI/Python ...,\n # X-Stainless-*) when a custom provider sits behind a gateway/WAF that\n # rejects them — e.g. an upstream that returns \"502 Upstream access\n # forbidden\" for the SDK default User-Agent but accepts a plain one.\n # Applies on the OpenAI wire only (not native Anthropic / Bedrock).\n #\n # default_headers:\n # User-Agent: \"curl/8.7.1\"\n #\n # extra_headers: accepted as an alias of default_headers (merged, with\n # extra_headers winning when both are set) — matches the per-provider\n # extra_headers key below.\n #\n # Per-provider variant: named providers / custom_providers entries accept an\n # extra_headers dict scoped to that endpoint only — for reverse proxies,\n # gateways, or custom auth (e.g. Cloudflare Access service tokens).\n # Merged onto SDK/provider defaults with the entry's values winning.\n # Header values are treated as secrets and are never logged.\n #\n # providers:\n # my-proxy:\n # base_url: \"https://llm.internal.example.com/v1\"\n # key_env: \"MY_PROXY_API_KEY\"\n # extra_headers:\n # CF-Access-Client-Id: \"xxxx.access\"\n # CF-Access-Client-Secret: \"${CF_ACCESS_SECRET}\"\n # X-Client-Name: \"hermes-agent\"\n\n# Named provider overrides (optional)\n# Use this for per-provider request timeouts, non-stream stale timeouts,\n# and per-model exceptions.\n# Applies to the primary turn client on every api_mode (OpenAI-wire, native\n# Anthropic, and Anthropic-compatible providers), the fallback chain, and\n# client rebuilds during credential rotation. For OpenAI-wire chat\n# completions (streaming and non-streaming) the configured value is also\n# used as the per-request ``timeout=`` kwarg so it wins over the legacy\n# HERMES_API_TIMEOUT env var (which still applies when no config is set).\n# ``stale_timeout_seconds`` controls the non-streaming stale-call detector and\n# wins over the legacy HERMES_API_CALL_STALE_TIMEOUT env var. Leaving these\n# unset keeps the legacy defaults (HERMES_API_TIMEOUT=1800s,\n# HERMES_API_CALL_STALE_TIMEOUT=90s, native Anthropic 900s). The\n# implicit non-stream stale detector is auto-disabled for local endpoints\n# and can scale upward for very large contexts.\n#\n# Not currently wired for AWS Bedrock (bedrock_converse + AnthropicBedrock\n# SDK paths) — those use boto3 with its own timeout configuration.\n#\n# providers:\n# ollama-local:\n# request_timeout_seconds: 300 # Longer timeout for local cold-starts\n# stale_timeout_seconds: 900 # Explicitly re-enable stale detection on local endpoints\n# anthropic:\n# request_timeout_seconds: 30 # Fast-fail cloud requests\n# models:\n# claude-opus-4.6:\n# timeout_seconds: 600 # Longer timeout for extended-thinking Opus calls\n# openai-codex:\n# models:\n# gpt-5.4:\n# stale_timeout_seconds: 1800 # Longer non-stream stale timeout for slow large-context turns\n\n# =============================================================================\n# OpenRouter Provider Routing (only applies when using OpenRouter)\n# =============================================================================\n# Control how requests are routed across providers on OpenRouter.\n# See: https://openrouter.ai/docs/guides/routing/provider-selection\n#\n# provider_routing:\n# # Sort strategy: \"price\" (default), \"throughput\", or \"latency\"\n# # Append :nitro to model name for a shortcut to throughput sorting.\n# sort: \"throughput\"\n#\n# # Only allow these providers (provider slugs from OpenRouter)\n# # only: [\"anthropic\", \"google\"]\n#\n# # Skip these providers entirely\n# # ignore: [\"deepinfra\", \"fireworks\"]\n#\n# # Try providers in this order (overrides default load balancing)\n# # order: [\"anthropic\", \"google\", \"together\"]\n#\n# # Require providers to support all parameters in your request\n# # require_parameters: true\n#\n# # Data policy: \"allow\" (default) or \"deny\" to exclude providers that may store data\n# # data_collection: \"deny\"\n\n# =============================================================================\n# OpenRouter Response Caching (only applies when using OpenRouter)\n# =============================================================================\n# Cache identical API responses at the OpenRouter edge for free instant replays.\n# When enabled, identical requests (same model, messages, parameters) return\n# cached responses with zero billing. Separate from Anthropic prompt caching.\n# See: https://openrouter.ai/docs/guides/features/response-caching\n#\n# openrouter:\n# response_cache: true # Enable response caching (default: true)\n# response_cache_ttl: 300 # Cache TTL in seconds, 1-86400 (default: 300)\n\n# =============================================================================\n# Git Worktree Isolation\n# =============================================================================\n# When enabled, each CLI session creates an isolated git worktree so multiple\n# agents can work on the same repo concurrently without file collisions.\n# Equivalent to always passing --worktree / -w on the command line.\n#\n# worktree: true # Always create a worktree when in a git repo\n# worktree: false # Default — only create when -w flag is passed\n#\n# By default a new worktree branches from the freshly-fetched remote tip\n# (the current branch's upstream, else the remote's default branch) so it\n# starts current with the project instead of from the local clone's\n# (possibly stale) HEAD. Set worktree_sync: false to branch from local HEAD\n# instead — useful when offline or when you deliberately want the clone's\n# exact current state as the base.\n#\n# worktree_sync: true # Default — branch from the fetched remote tip\n# worktree_sync: false # Branch from local HEAD (offline / pinned base)\n\n# =============================================================================\n# Terminal Tool Configuration\n# =============================================================================\n# Choose ONE of the following terminal configurations by uncommenting it.\n# The terminal tool executes commands in the specified environment.\n\n# -----------------------------------------------------------------------------\n# OPTION 1: Local execution (default)\n# Commands run directly on your machine in the current directory\n# -----------------------------------------------------------------------------\n# Working directory behavior:\n# - CLI (`hermes` command): Uses \".\" (current directory where you run hermes)\n# - Gateway/messaging/cron: Uses terminal.cwd here; legacy .env cwd values are deprecated\nterminal:\n backend: \"local\"\n cwd: \".\" # For local backend: \".\" = current directory. Ignored for remote backends unless a backend documents otherwise.\n timeout: 180\n # HOME policy for tool subprocesses:\n # auto - default: host uses your real HOME; containers use HERMES_HOME/home\n # real - force your real OS-user HOME\n # profile - force HERMES_HOME/home for strict per-profile CLI config isolation\n home_mode: \"auto\"\n docker_mount_cwd_to_workspace: false # SECURITY: off by default. Opt in to mount the launch cwd into Docker /workspace.\n lifetime_seconds: 300\n # sudo_password: \"hunter2\" # Optional: pipe a sudo password via sudo -S. SECURITY WARNING: plaintext.\n # sudo_password: \"\" # Explicit empty password: try empty and never open the interactive sudo prompt.\n\n# -----------------------------------------------------------------------------\n# OPTION 2: SSH remote execution\n# Commands run on a remote server - agent code stays local (sandboxed)\n# Great for: keeping agent isolated from its own code, using powerful remote hardware\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"ssh\"\n# cwd: \"/home/myuser/project\" # Path on the REMOTE server\n# timeout: 180\n# lifetime_seconds: 300\n# ssh_host: \"my-server.example.com\"\n# ssh_user: \"myuser\"\n# ssh_port: 22\n# ssh_key: \"~/.ssh/id_rsa\" # Optional - uses ssh-agent if not specified\n\n# -----------------------------------------------------------------------------\n# OPTION 3: Docker container\n# Commands run in an isolated Docker container\n# Great for: reproducible environments, testing, isolation\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"docker\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /)\n# timeout: 180\n# lifetime_seconds: 300\n# docker_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# docker_mount_cwd_to_workspace: true # Explicit opt-in: mount your launch cwd into /workspace\n# # Optional: run the container as your host user's uid:gid so files written\n# # into bind-mounted dirs are owned by you, not root. Drops SETUID/SETGID\n# # caps too since no gosu privilege drop is needed. Leave off if your\n# # chosen docker_image expects to start as root.\n# docker_run_as_host_user: true\n# # Optional: explicitly forward selected env vars into Docker.\n# # These values come from your current shell first, then ~/.hermes/.env.\n# # Warning: anything forwarded here is visible to commands run in the container.\n# docker_forward_env:\n# - \"GITHUB_TOKEN\"\n# - \"NPM_TOKEN\"\n# # Optional: extra flags passed verbatim to docker run (appended after security defaults).\n# # Useful for adding capabilities (e.g. apt installs needing SETUID) or custom options.\n# # Example: add a Linux capability not included by default\n# # docker_extra_args:\n# # - \"--cap-add\"\n# # - \"SETUID\"\n\n# -----------------------------------------------------------------------------\n# OPTION 4: Singularity/Apptainer container\n# Commands run in a Singularity container (common in HPC environments)\n# Great for: HPC clusters, shared compute environments\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"singularity\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# singularity_image: \"docker://nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 5: Modal cloud execution\n# Commands run on Modal's cloud infrastructure\n# Great for: GPU access, scalable compute, serverless execution\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"modal\"\n# cwd: \"/workspace\" # Path INSIDE the sandbox (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# modal_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 6: Daytona cloud execution\n# Commands run in Daytona cloud sandboxes\n# Great for: Cloud dev environments, persistent workspaces, team collaboration\n# Requires: pip install daytona, DAYTONA_API_KEY env var\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"daytona\"\n# cwd: \"~\"\n# timeout: 180\n# lifetime_seconds: 300\n# daytona_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# container_disk: 10240 # Daytona max is 10GB per sandbox\n\n#\n# --- Container resource limits (docker, singularity, modal, daytona -- ignored for local/ssh) ---\n# These settings apply to all container backends. They control the resources\n# allocated to the sandbox and whether its filesystem persists across sessions.\n container_cpu: 1 # CPU cores\n container_memory: 5120 # Memory in MB (5120 = 5GB)\n container_disk: 51200 # Disk in MB (51200 = 50GB)\n container_persistent: true # Persist filesystem across sessions (false = ephemeral)\n\n# -----------------------------------------------------------------------------\n# SUDO SUPPORT (works with ALL backends above)\n# -----------------------------------------------------------------------------\n# Add sudo_password to any terminal config above to enable sudo commands.\n# The password is piped via `sudo -S`. Works with local, ssh, docker, etc.\n#\n# SECURITY WARNING: Password stored in plaintext!\n#\n# INTERACTIVE PROMPT: If sudo_password is unset and the CLI is running,\n# you'll be prompted to enter your password when sudo is needed:\n# - 45-second timeout (auto-skips if no input)\n# - Press Enter to skip (command fails gracefully)\n# - Password is hidden while typing\n# - Password is cached for the session\n#\n# EMPTY PASSWORDS: Setting sudo_password to an explicit empty string is different\n# from leaving it unset. Hermes will try an empty password via `sudo -S` and\n# will not open the interactive prompt. This is useful for passwordless sudo,\n# Touch ID sudo setups, and environments where prompting is just noise.\n#\n# ALTERNATIVES:\n# - SSH backend: Configure passwordless sudo on the remote server\n# - Containers: Run as root inside the container (no sudo needed)\n# - Local: Configure /etc/sudoers for specific commands\n#\n# Example (add to your terminal section):\n# sudo_password: \"your-password-here\"\n\n# =============================================================================\n# Security Scanning (tirith)\n# =============================================================================\n# Optional pre-exec command security scanning via tirith.\n# Detects homograph URLs, pipe-to-shell, terminal injection, env manipulation.\n# Install: brew install sheeki03/tap/tirith\n# Docs: https://github.com/sheeki03/tirith\n#\n# security:\n# tirith_enabled: true # Enable/disable tirith scanning\n# tirith_path: \"tirith\" # Path to tirith binary (supports ~ expansion)\n# tirith_timeout: 5 # Scan timeout in seconds\n# tirith_fail_open: true # Allow commands if tirith unavailable\n\n# =============================================================================\n# Browser Tool Configuration\n# =============================================================================\nbrowser:\n # Inactivity timeout in seconds - browser sessions are automatically closed\n # after this period of no activity between agent loops (default: 120 = 2 minutes)\n inactivity_timeout: 120\n\n# =============================================================================\n# Tool Loop Guardrails\n# =============================================================================\n# Soft warnings are enabled by default. They append guidance to repeated failed\n# or non-progressing tool results but still let the tool execute. Hard stops are\n# opt-in circuit breakers for autonomous/cron sessions where stopping a loop is\n# preferable to spending the full iteration budget.\ntool_loop_guardrails:\n warnings_enabled: true\n hard_stop_enabled: false\n warn_after:\n exact_failure: 2\n same_tool_failure: 3\n idempotent_no_progress: 2\n hard_stop_after:\n exact_failure: 5\n same_tool_failure: 8\n idempotent_no_progress: 5\n\n# =============================================================================\n# Context Compression (Auto-shrinks long conversations)\n# =============================================================================\n# When conversation approaches model's context limit, middle turns are\n# automatically summarized to free up space while preserving important context.\n#\n# HOW IT WORKS:\n# 1. Tracks actual token usage from API responses (not estimates)\n# 2. When prompt_tokens >= threshold% of model's context_length, triggers compression\n# 3. Protects first 3 turns (system prompt, initial request, first response)\n# 4. Protects last N turns (default 20 messages = ~10 full turns of recent context)\n# 5. Summarizes middle turns using a fast/cheap model\n# 6. Inserts summary as a user message, continues conversation seamlessly\n#\n# Post-compression tail budget is target_ratio × threshold × context_length:\n# 200K context, threshold 0.50, ratio 0.20 → 20K tokens of recent tail preserved\n# 1M context, threshold 0.50, ratio 0.20 → 100K tokens of recent tail preserved\n#\ncompression:\n # Enable automatic context compression (default: true)\n # Set to false if you prefer to manage context manually or want errors on overflow\n enabled: true\n\n # Opt-in compression progress notices on chat platforms (default: false).\n # By design, routine automatic compression is SILENT on human-facing chat\n # gateways (Telegram, Discord, Slack, ...) — it happens in the background\n # with server-side logging only. Set true to also deliver the routine\n # progress statuses (compacting started, preflight/pre-API compression,\n # idle compaction, retry progress, and the compaction-complete notice) to\n # chat platforms. Unrelated operational noise (auxiliary model failures,\n # provider retry chatter) stays suppressed either way, and compression\n # FAILURE notices + manual /compress feedback are always visible\n # regardless of this setting. (#52995)\n progress_notices: false\n \n # Trigger compression at this % of model's context limit (default: 0.50 = 50%)\n # Lower values = more aggressive compression, higher values = compress later\n # Models with context windows below 512K are floored at 0.75 (raise-only) so\n # compaction doesn't fire with half the window still free; set above 0.75 to override.\n threshold: 0.50\n\n # Per-model threshold overrides: keys are substring-matched against the model\n # name (longest match wins). Useful when some models need different compaction\n # points — e.g. a 1M-context model can compress later (0.30) while a 128K\n # model needs to compress earlier (0.60). The small-context floor (75% for\n # <512K models) still applies on top of per-model overrides.\n # model_thresholds:\n # \"glm-5.2\": 0.40\n # \"claude-sonnet\": 0.35\n # \"gpt-5\": 0.30\n\n # Optional absolute token cap for the compression trigger (default: null = disabled).\n # When set, compression fires at the LOWER of the ratio-based threshold and this\n # absolute token count — first-fires-wins. It never fires later than this count\n # regardless of which model is active (useful when switching between models with\n # very different context windows). Clamped to the model's context length at\n # apply-time, so a cap above the window is a no-op (ratio-based threshold wins).\n # Survives model switches and fallback activations.\n # threshold_tokens: 200000\n\n # Existing Codex gpt-5.5 behavior: raise Hermes' compaction trigger to 85%\n # for the ChatGPT Codex OAuth route. Set false to opt back down to threshold.\n codex_gpt55_autoraise: true\n\n # Fraction of the threshold to preserve as recent tail (default: 0.20 = 20%)\n # e.g. 20% of 50% threshold = 10% of total context kept as recent messages.\n # Summary output is separately capped at 12K tokens (Gemini output limit).\n # Range: 0.10 - 0.80\n target_ratio: 0.20\n\n # Number of most-recent messages to always preserve (default: 20 ≈ 10 full turns)\n # Higher values keep more recent conversation intact at the cost of more aggressive\n # compression of older turns.\n protect_last_n: 20\n\n # Minimum number of REAL (actionable) user messages guaranteed to survive in\n # the uncompressed tail (default: 1 = the existing single last-user anchor,\n # behavior-preserving). Raise to e.g. 3 to keep the last 3 real user turns\n # verbatim even when bulky tool outputs fill the tail token budget — blank\n # platform echoes, compaction handoffs, and synthetic continuation rows never\n # count toward N. The tail can exceed the token budget when this pulls the\n # cut back; the guarantee wins over the budget by design.\n min_tail_user_messages: 1\n\n # Compression retry rounds before a turn gives up with \"max compression\n # attempts reached\" (default: 3, same as the previous hardcoded value).\n # Raise (e.g. 6) for tool-schema-heavy sessions where 3 rounds cannot bring\n # the request estimate under the threshold. Validated >= 1, hard cap 10.\n max_attempts: 3\n\n # Codex app-server (codex CLI runtime) thread-compaction mode. The codex\n # agent owns the real thread context on this runtime, so Hermes' summarizer\n # cannot shrink it — compaction goes through the app server instead.\n # native = let Codex decide when to compact its own thread (default)\n # hermes = let Hermes threshold trigger Codex thread/compact/start\n # off = Hermes will not auto-trigger compaction; Codex may still compact natively\n codex_app_server_auto: native\n\n # Number of non-system messages to protect at the head of the transcript, in\n # ADDITION to the system prompt (which is always implicitly protected).\n # Head messages are NEVER summarized — they survive every compression\n # indefinitely. This gives stable early context for short/medium sessions,\n # but in long-running sessions that rely on rolling compaction the pinned\n # opening turns may not match how you want the session framed over time.\n # Set to 0 to preserve ONLY the system prompt (plus the rolling summary\n # and recent tail) — the cleanest configuration for long-running sessions.\n # Default 3 preserves the system prompt plus the first three non-system\n # head messages, matching the pre-feature behaviour.\n protect_first_n: 3\n\n # Idle compaction (default: 0 = disabled). When > 0, a session that resumes\n # after at least this many seconds of inactivity compacts its accumulated\n # history up front, before the first reply, so a long-lived thread you come\n # back to later doesn't re-read its full stale context on every turn.\n # Time-based, so it complements (does not replace) the size-based `threshold`\n # above. It is skipped when the context is already small (at or below the\n # post-compression target = threshold × target_ratio), so it never wastes a\n # summarization on a short idle thread. Example: 1800 = compact after 30 min idle.\n idle_compact_after_seconds: 0\n\n # Proactive tool-result prune (default: 0 = disabled). Opt-in token trigger\n # for a deterministic, no-LLM prune of OLD tool-result payloads, run\n # independently of `threshold` above. On large-window models (512K/1M) the\n # ratio threshold rarely fires, so bulky tool outputs (terminal dumps, file\n # reads, web extracts) ride along in history and get re-billed every turn.\n # When re-sent history exceeds this many tokens, the prune dedupes identical\n # results, summarizes older oversized ones, and truncates large tool-call\n # arguments — protecting the most recent `protect_last_n` messages and never\n # calling the model. Try 48000 to enable. Built-in compressor engine only;\n # other context engines inherit a safe no-op.\n # NOTE: a committed prune rewrites already-sent history, which invalidates\n # the provider's prompt-cache prefix — the min_reclaim gate below keeps\n # those cache breaks episodic (like a compression boundary) instead of\n # per-turn.\n proactive_prune_tokens: 0\n\n # The prune's summarize pass only touches tool results larger than this many\n # characters (clamped to >= 200 so a generated summary can't be\n # re-summarized). Default 8000.\n proactive_prune_min_result_chars: 8000\n\n # A proactive prune only COMMITS when it reclaims at least this many tokens\n # (measured on the pruned output). This is the prompt-cache hysteresis gate:\n # one meaningful, amortized cache break per batch of stale tool output\n # instead of a tiny break on every tool iteration. 0 = commit any non-zero\n # prune. Default 4096.\n proactive_prune_min_reclaim_tokens: 4096\n\n # To pin a specific model/provider for compression summaries, use the\n # auxiliary section below (auxiliary.compression.provider / model).\n\n# =============================================================================\n# Anthropic prompt caching TTL\n# =============================================================================\n# When prompt caching is active (Claude via OpenRouter or native Anthropic),\n# Anthropic supports two TTL tiers for cached prefixes: \"5m\" (default) and\n# \"1h\". Other values are ignored and \"5m\" is used.\n#\nprompt_caching:\n cache_ttl: \"5m\" # use \"1h\" for long sessions with pauses between turns\n\n# =============================================================================\n# Auxiliary Models (Advanced — Experimental)\n# =============================================================================\n# Hermes uses lightweight \"auxiliary\" models for side tasks: image analysis,\n# browser screenshot analysis, web page summarization, TTS audio-tag insertion,\n# and context compression.\n#\n# By default these use Gemini Flash via OpenRouter or Nous Portal and are\n# auto-detected from your credentials. You do NOT need to change anything\n# here for normal usage.\n#\n# WARNING: Overriding these with providers other than OpenRouter or Nous Portal\n# is EXPERIMENTAL and may not work. Not all models/providers support vision,\n# produce usable summaries, or accept the same API format. Change at your own\n# risk — if things break, reset to \"auto\" / empty values.\n#\n# Each task has its own provider + model pair so you can mix providers.\n# For example: OpenRouter for vision (needs multimodal), but your main\n# local endpoint for compression (just needs text).\n#\n# Provider options:\n# \"auto\" - Best available: OpenRouter → Nous Portal → main endpoint (default)\n# \"openrouter\" - Force OpenRouter (requires OPENROUTER_API_KEY)\n# \"nous\" - Force Nous Portal (requires: hermes auth add nous)\n# \"gemini\" - Force Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n# \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY)\n# \"codex\" - Force Codex OAuth (requires: hermes model → Codex).\n# Uses gpt-5.3-codex which supports vision.\n# \"main\" - Use your custom endpoint (OPENAI_BASE_URL + OPENAI_API_KEY).\n# Works with OpenAI API, local models, or any OpenAI-compatible\n# endpoint. Also falls back to Codex OAuth and API-key providers.\n#\n# Model: leave empty to use the provider's default. When empty, OpenRouter\n# uses \"google/gemini-3-flash-preview\" and Nous uses \"gemini-3-flash\".\n# Other providers pick a sensible default automatically.\n#\n# auxiliary:\n# # Image analysis: vision_analyze tool + browser screenshots\n# vision:\n# provider: \"auto\"\n# model: \"\" # e.g. \"google/gemini-2.5-flash\", \"openai/gpt-4o\"\n# timeout: 30 # LLM API call timeout (seconds)\n# download_timeout: 30 # Image HTTP download timeout (seconds)\n# # Increase for slow connections or self-hosted image servers\n# reasoning_effort: \"\" # Per-task thinking level: none, minimal, low, medium,\n# # high, xhigh, max, ultra. Empty = provider default.\n# # Works on every auxiliary task block (vision,\n# # web_extract, compression, title_generation, curator,\n# # background_review, moa_reference, ...). Example: run\n# # compression at \"low\" and vision at \"none\" to cut\n# # side-task latency/cost on reasoning models.\n#\n# # Web page scraping / summarization + browser page text extraction\n# web_extract:\n# provider: \"auto\"\n# model: \"\"\n# reasoning_effort: \"low\"\n#\n# # Gemini 3.1 TTS hidden audio-tag insertion\n# tts_audio_tags:\n# provider: \"auto\" # empty model = your main chat model\n# model: \"\"\n# timeout: 30\n#\n# # Automatic session title generation after the first exchange\n# title_generation:\n# enabled: true # set false to disable auto-title generation\n# provider: \"auto\"\n# model: \"\"\n# timeout: 30\n# language: \"\" # empty = match the user's language; or e.g. \"English\"\n#\n# # Session search — summarizes matching past sessions\n# session_search:\n# provider: \"auto\"\n# model: \"\"\n# timeout: 30\n# max_concurrency: 3 # Limit parallel summaries to reduce request-burst 429s\n# extra_body: {} # Provider-specific OpenAI-compatible request fields\n# # Example for providers that support request-body\n# # reasoning controls:\n# # extra_body:\n# # enable_thinking: false\n# # Some vLLM/Qwen deployments expect this nested:\n# # extra_body:\n# # chat_template_kwargs:\n# # enable_thinking: false\n\n# =============================================================================\n# Persistent Memory\n# =============================================================================\n# Bounded curated memory injected into the system prompt every session.\n# Two stores: MEMORY.md (agent's notes) and USER.md (user profile).\n# Character limits keep the memory small and focused. The agent manages\n# pruning -- when at the limit, it must consolidate or replace entries.\n# Disabled by default in batch_runner.\n#\nmemory:\n # Agent's personal notes: environment facts, conventions, things learned\n memory_enabled: true\n \n # User profile: preferences, communication style, expectations\n user_profile_enabled: true\n \n # Character limits (~2.75 chars per token, model-independent)\n memory_char_limit: 2200 # ~800 tokens\n user_char_limit: 1375 # ~500 tokens\n\n # Periodic memory nudge: remind the agent to consider saving memories\n # every N user turns. Set to 0 to disable. Only active when memory is enabled.\n nudge_interval: 10 # Nudge every 10 user turns (0 = disabled)\n\n # Memory flush: give the agent one turn to save memories before context is\n # lost (compression, /new, /reset, exit). Set to 0 to disable.\n # For exit/reset, only fires if the session had at least this many user turns.\n flush_min_turns: 6 # Min user turns to trigger flush on exit/reset (0 = disabled)\n\n# =============================================================================\n# Session Reset Policy (Messaging Platforms)\n# =============================================================================\n# Controls when messaging sessions (Telegram, Discord, WhatsApp, Slack) are\n# automatically cleared. Default is \"none\": sessions never auto-reset —\n# conversation context lives until you /reset or /new manually, or context\n# compression kicks in. Opt in to automatic resets if you prefer sessions to\n# clear on a schedule (long-lived context increases API cost per message,\n# though prompt caching and compression keep this manageable).\n#\n# When an automatic reset triggers, the agent first saves important\n# information to its persistent memory — but the conversation context is\n# wiped. The agent starts fresh but retains learned facts via its memory\n# system.\n#\n# Modes:\n# \"none\" - Never auto-reset (default); context lives until /reset or compression\n# \"idle\" - Reset after N minutes of inactivity\n# \"daily\" - Reset at a fixed hour each day\n# \"both\" - Reset on EITHER inactivity timeout or daily boundary\n#\nsession_reset:\n mode: none # \"none\", \"idle\", \"daily\", or \"both\"\n idle_minutes: 1440 # Inactivity timeout in minutes (used by \"idle\"/\"both\")\n at_hour: 4 # Daily reset hour, 0-23 local time (used by \"daily\"/\"both\")\n\n# Maximum number of simultaneously active chat sessions across CLI, TUI,\n# dashboard chat, and messaging gateway. Set to null, 0, or omit to allow\n# unlimited concurrent sessions. When the limit is reached, new sessions get a\n# clean error while existing active sessions keep their normal behavior. This\n# top-level key takes precedence over gateway.max_concurrent_sessions. The cap\n# is a best-effort single-host/profile runtime guard; Hermes fails open if the\n# local runtime lease registry cannot be read or locked.\nmax_concurrent_sessions: null\n\n# When true, group/channel chats use one session per participant when the platform\n# provides a user ID. This is the secure default and prevents users in the same\n# room from sharing context, interrupts, and token costs. Set false only if you\n# explicitly want one shared \"room brain\" per group/channel.\ngroup_sessions_per_user: true\n\n# ─────────────────────────────────────────────────────────────────────────────\n# API Server — per-client model routing\n# ─────────────────────────────────────────────────────────────────────────────\n# Route different API clients to different models/providers on a single\n# Hermes deployment. Clients choose a backend by sending a specific string\n# as the OpenAI ``model`` field. Unmapped model values fall back to the\n# global model configured in the ``model:`` section above, and an explicit\n# session /model override always wins over a route.\n#\n# Configure via the ``platforms.api_server.extra.model_routes`` gateway\n# config block:\n#\n# platforms:\n# api_server:\n# enabled: true\n# extra:\n# key: \"your-api-server-secret\"\n# model_routes:\n# # Xiaozhi clients send model=\"minimax-m2\" → routed to MiniMax via OpenRouter\n# minimax-m2:\n# model: \"minimax/minimax-m1\"\n# provider: \"openrouter\" # optional — overrides global provider\n# # api_key: \"sk-...\" # optional — per-route UPSTREAM provider\n# # key (NOT caller auth; never logged)\n# # base_url: \"https://...\" # optional — per-route base URL\n# # GPT clients keep their own alias\n# gpt-5:\n# model: \"openai/gpt-5\"\n# provider: \"openrouter\"\n#\n# Configured aliases are automatically listed by GET /v1/models so clients\n# can discover them without manual coordination. Caller authentication is\n# unchanged: every request still authenticates with the global API server\n# key (``extra.key`` / API_SERVER_KEY).\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Gateway Streaming\n# ─────────────────────────────────────────────────────────────────────────────\n# Stream tokens to messaging platforms in real-time. The bot sends a message\n# on first token, then progressively edits it as more tokens arrive.\n# Disabled by default — enable to try the streaming UX on Telegram/Discord/Slack.\n# For Telegram, partial edits are sent as plain text and only the final edit uses MarkdownV2.\nstreaming:\n enabled: false\n # transport: edit # \"edit\" = progressive editMessageText\n # edit_interval: 0.3 # seconds between message edits\n # buffer_threshold: 40 # chars before forcing an edit flush\n # cursor: \" ▉\" # cursor shown during streaming\n\n# =============================================================================\n# Skills Configuration\n# =============================================================================\n# Skills are reusable procedures the agent can load and follow. The agent can\n# also create new skills after completing complex tasks.\n#\nskills:\n # Nudge the agent to create skills after complex tasks.\n # Every N tool-calling iterations, remind the model to consider saving a skill.\n # Set to 0 to disable.\n creation_nudge_interval: 15\n\n # External skill directories — share skills across tools/agents without\n # copying them into ~/.hermes/skills/. Each path is expanded (~ and ${VAR})\n # and resolved to an absolute path. External dirs are read-only: skill\n # creation always writes to ~/.hermes/skills/. Local skills take precedence\n # when names collide.\n # external_dirs:\n # - ~/.agents/skills\n # - /home/shared/team-skills\n\n# =============================================================================\n# Agent Behavior\n# =============================================================================\nagent:\n # Maximum tool-calling iterations per conversation\n # Higher = more room for complex tasks, but costs more tokens\n # Recommended: 20-30 for focused tasks, 50-100 for open exploration\n max_turns: 60\n\n # Inactivity timeout for gateway agent runs (seconds, 0 = unlimited).\n # The agent can run indefinitely when actively calling tools or receiving\n # API responses. Only fires after the agent has been idle for this duration.\n # gateway_timeout: 1800\n\n # Staged warning: send a warning before escalating to full timeout.\n # Fires once per run when inactivity reaches this threshold (seconds).\n # Set to 0 to disable the warning.\n # gateway_timeout_warning: 900\n\n # Graceful drain timeout for gateway stop/restart (seconds).\n # Default 0 = no drain: a restart interrupts in-flight agents immediately,\n # cleans up, and exits. Set a positive value only if you want a grace\n # window on /restart, and keep it well under systemd's TimeoutStopSec.\n # restart_drain_timeout: 0\n\n # Upper bound (seconds) a submitted prompt waits for the deferred agent\n # build (MCP discovery, model metadata, skills scan) before failing with a\n # visible error. The wait is patient — the message is delivered as soon as\n # the build completes, and a progress notice is shown past 30s — so this cap\n # only fires on a genuinely hung build. Raise it for deployments with many\n # slow or unreachable MCP servers. Default 600.\n # build_wait_timeout: 600\n\n # Max app-level retry attempts for API errors (connection drops, provider\n # timeouts, 5xx, etc.) before the agent surfaces the failure. Lower this\n # to 1 if you use fallback providers and want fast failover on flaky\n # primaries (default 3). The OpenAI SDK does its own low-level retries\n # underneath this wrapper — this is the Hermes-level loop.\n # api_max_retries: 3\n\n # After the agent edits code without fresh passing verification, nudge it to\n # verify before finishing. The default \"auto\" enables it on interactive\n # coding surfaces (CLI, TUI, desktop) and programmatic callers, and disables\n # it on conversational messaging surfaces (Telegram, Discord, etc.) where the\n # verification summary would reach a human as chat noise. Set true or false to\n # force it on or off; the HERMES_VERIFY_ON_STOP env var (1/0) takes precedence.\n # verify_on_stop: auto\n\n # Standing operator instructions for the coding posture (when Hermes is in a\n # code workspace). Appended to the coding brief as an extra system block, so\n # you can pin project-wide workflow rules without editing the shipped brief.\n # Accepts a string or a list of strings. Takes effect next session.\n # coding_instructions:\n # - \"For UI work, don't run tsc/lint until I approve the look.\"\n # - \"Clean the diff before you commit and push.\"\n\n # When verify-on-stop finds edited code without fresh verification evidence,\n # append guidance for creative UI work (avoid broad tsc/lint/test before visual\n # approval) and clean-diff expectations. Set false to keep that nudge terse.\n # verify_guidance: true\n\n # A `pre_verify` hook (plugin or shell, see Event Hooks docs) can keep the\n # agent going one more turn to verify/clean before finishing. This caps how\n # many times one turn may be nudged to continue, so a hook can't trap the loop.\n # Default 3.\n # max_verify_nudges: 3\n\n # Enable verbose logging\n verbose: false\n \n # Reasoning effort level (OpenRouter and Nous Portal)\n # Controls how much \"thinking\" the model does before responding.\n # Options: \"xhigh\" (max), \"high\", \"medium\", \"low\", \"minimal\", \"none\" (disable)\n reasoning_effort: \"medium\"\n \n # Per-model reasoning effort overrides (optional dict)\n # Key: any sensible model spelling works (exact, dots↔dashes interchangeable,\n # provider prefix optional). First match wins.\n # Value: reasoning effort level (same options as reasoning_effort)\n # Override the global reasoning_effort for that specific model.\n # NOTE: no `hermes config set` support for this key -- edit YAML directly.\n # reasoning_overrides:\n # \"openrouter/anthropic/claude-opus-4.5\": \"xhigh\"\n # \"openai/gpt-5\": \"low\"\n # \"claude-opus-4.6\": \"high\" # bare model name also works\n # \"deepseek/deepseek-v4-pro\": \"xhigh\" # dots and dashes are interchangeable\n reasoning_overrides: {}\n \n # Predefined personalities (use with /personality command)\n personalities:\n helpful: \"You are a helpful, friendly AI assistant.\"\n concise: \"You are a concise assistant. Keep responses brief and to the point.\"\n technical: \"You are a technical expert. Provide detailed, accurate technical information.\"\n creative: \"You are a creative assistant. Think outside the box and offer innovative solutions.\"\n teacher: \"You are a patient teacher. Explain concepts clearly with examples.\"\n kawaii: \"You are a kawaii assistant! Use cute expressions like (◕‿◕), ★, ♪, and ~! Add sparkles and be super enthusiastic about everything! Every response should feel warm and adorable desu~! ヽ(>∀<☆)ノ\"\n catgirl: \"You are Neko-chan, an anime catgirl AI assistant, nya~! Add 'nya' and cat-like expressions to your speech. Use kaomoji like (=^・ω・^=) and ฅ^•ﻌ•^ฅ. Be playful and curious like a cat, nya~!\"\n pirate: \"Arrr! Ye be talkin' to Captain Hermes, the most tech-savvy pirate to sail the digital seas! Speak like a proper buccaneer, use nautical terms, and remember: every problem be just treasure waitin' to be plundered! Yo ho ho!\"\n shakespeare: \"Hark! Thou speakest with an assistant most versed in the bardic arts. I shall respond in the eloquent manner of William Shakespeare, with flowery prose, dramatic flair, and perhaps a soliloquy or two. What light through yonder terminal breaks?\"\n surfer: \"Duuude! You're chatting with the chillest AI on the web, bro! Everything's gonna be totally rad. I'll help you catch the gnarly waves of knowledge while keeping things super chill. Cowabunga! 🤙\"\n noir: \"The rain hammered against the terminal like regrets on a guilty conscience. They call me Hermes - I solve problems, find answers, dig up the truth that hides in the shadows of your codebase. In this city of silicon and secrets, everyone's got something to hide. What's your story, pal?\"\n uwu: \"hewwo! i'm your fwiendwy assistant uwu~ i wiww twy my best to hewp you! *nuzzles your code* OwO what's this? wet me take a wook! i pwomise to be vewy hewpful >w<\"\n philosopher: \"Greetings, seeker of wisdom. I am an assistant who contemplates the deeper meaning behind every query. Let us examine not just the 'how' but the 'why' of your questions. Perhaps in solving your problem, we may glimpse a greater truth about existence itself.\"\n hype: \"YOOO LET'S GOOOO!!! 🔥🔥🔥 I am SO PUMPED to help you today! Every question is AMAZING and we're gonna CRUSH IT together! This is gonna be LEGENDARY! ARE YOU READY?! LET'S DO THIS! 💪😤🚀\"\n\n# =============================================================================\n# Toolsets\n# =============================================================================\n# Control which tools the agent has access to.\n# Use `hermes tools` to interactively enable/disable tools per platform.\n\n# =============================================================================\n# Platform Toolsets (per-platform tool configuration)\n# =============================================================================\n# Override which toolsets are available on each platform.\n# If a platform isn't listed here, its built-in default is used.\n#\n# You can use EITHER:\n# - A preset like \"hermes-cli\" or \"hermes-telegram\" (curated tool set)\n# - A list of individual toolsets to compose your own (see list below)\n#\n# Supported platform keys: cli, telegram, discord, whatsapp, slack, qqbot, teams, google_chat\n#\n# Examples:\n#\n# # Use presets (same as defaults):\n# platform_toolsets:\n# cli: [hermes-cli]\n# telegram: [hermes-telegram]\n#\n# # Custom: give Telegram only web + terminal + file + planning:\n# platform_toolsets:\n# telegram: [web, terminal, file, todo]\n#\n# # Custom: CLI without browser or image gen:\n# platform_toolsets:\n# cli: [web, terminal, file, skills, todo, tts, cronjob]\n#\n# # Restrictive: Discord gets read-only tools only:\n# platform_toolsets:\n# discord: [web, vision, skills, todo]\n#\n# If not set, defaults are:\n# cli: hermes-cli (everything + cronjob management)\n# telegram: hermes-telegram (terminal, file, web, vision, image, tts, browser, skills, todo, cronjob, messaging)\n# discord: hermes-discord (same as telegram)\n# whatsapp: hermes-whatsapp (same as telegram)\n# slack: hermes-slack (same as telegram)\n# signal: hermes-signal (same as telegram)\n# homeassistant: hermes-homeassistant (same as telegram)\n# qqbot: hermes-qqbot (same as telegram)\n# teams: hermes-teams (same as telegram)\n# google_chat: hermes-google_chat (same as telegram)\n#\nplatform_toolsets:\n cli: [hermes-cli]\n telegram: [hermes-telegram]\n discord: [hermes-discord]\n whatsapp: [hermes-whatsapp]\n slack: [hermes-slack]\n signal: [hermes-signal]\n homeassistant: [hermes-homeassistant]\n qqbot: [hermes-qqbot]\n yuanbao: [hermes-yuanbao]\n teams: [hermes-teams]\n google_chat: [hermes-google_chat]\n\n# =============================================================================\n# Gateway Platform Settings\n# =============================================================================\n# Optional per-platform messaging settings.\n# Platform-specific knobs live under `extra`.\n#\n# platforms:\n# telegram:\n# reply_to_mode: \"first\" # off | first | all\n# # guest_mode lets explicit @mentions from non-allowlisted groups through.\n# # Default false; ordinary messages, replies, and regex wake words stay blocked.\n# guest_mode: false\n# # allowed_chats: [\"-1001234567890\"]\n# extra:\n# disable_link_previews: false # Set true to suppress Telegram URL previews in bot messages\n# 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\n# rich_drafts: false # Experimental rich draft previews during Telegram DM streaming; default false because Telegram Desktop/macOS can visually overlay draft frames\n# command_menu:\n# # Telegram allows up to 100 BotCommands; Hermes defaults to 60 so\n# # all built-in commands plus common skill commands stay visible\n# # while remaining under Telegram's payload-size limit. Clamped 1..100.\n# max_commands: 60\n# # prepend = user priority first, then Hermes defaults\n# # append = Hermes defaults first, then user priority\n# # replace = only the list below defines priority\n# priority_mode: prepend\n# priority:\n# - my_plugin_command\n# webhook:\n# extra:\n# # Route scripts default to a 30 second timeout. Scripts must live under\n# # the active profile's scripts directory and receive webhook JSON on stdin.\n# script_timeout_seconds: 30\n#\n# Discord-specific settings (config.yaml top-level, not under platforms:):\n#\n# discord:\n# require_mention: true # Require @mention in server channels (default: true)\n# auto_thread: true # Auto-create thread on @mention (default: true)\n# free_response_channels: \"\" # Channel IDs where no mention is needed\n# reactions: true # Show processing reactions (default: true)\n# history_backfill: true # Recover missed channel messages on mention (default: true)\n# history_backfill_limit: 50 # Max messages to scan backwards (default: 50)\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Available toolsets (use these names in platform_toolsets or the toolsets list)\n#\n# Run `hermes chat --list-toolsets` to see all toolsets and their tools.\n# Run `hermes chat --list-tools` to see every individual tool with descriptions.\n# ─────────────────────────────────────────────────────────────────────────────\n#\n# INDIVIDUAL TOOLSETS (compose your own):\n# web - web_search, web_extract\n# search - web_search only (no scraping)\n# terminal - terminal, process\n# file - read_file, write_file, patch, search\n# browser - browser_navigate, browser_snapshot, browser_click, browser_type,\n# browser_scroll, browser_back, browser_press,\n# browser_get_images, browser_vision (requires BROWSERBASE_API_KEY)\n# vision - vision_analyze (requires OPENROUTER_API_KEY)\n# image_gen - image_generate (requires FAL_KEY)\n# skills - skills_list, skill_view\n# skills_hub - skill_hub (search/install/manage from online registries — user-driven only)\n# todo - todo (in-memory task planning, no deps)\n# tts - text_to_speech (Edge TTS free, or ELEVENLABS/OPENAI/MINIMAX/MISTRAL key)\n# cronjob - cronjob (create/list/update/pause/resume/run/remove scheduled tasks)\n#\n# PRESETS (curated bundles):\n# hermes-cli - All of the above except send_message\n# hermes-telegram - terminal, file, web, vision, image_gen, tts, browser,\n# skills, todo, cronjob, send_message\n# hermes-discord - Same as hermes-telegram\n# hermes-whatsapp - Same as hermes-telegram\n# hermes-slack - Same as hermes-telegram\n#\n# COMPOSITE:\n# debugging - terminal + web + file\n# safe - web + vision (no terminal access)\n# all - Everything available\n#\n# web - Web search and content extraction (web_search, web_extract)\n# search - Web search only, no scraping (web_search)\n# terminal - Command execution and process management (terminal, process)\n# file - File operations: read, write, patch, search\n# browser - Full browser automation (navigate, click, type, screenshot, etc.)\n# vision - Image analysis (vision_analyze)\n# image_gen - Image generation with FLUX (image_generate)\n# skills - Load skill documents (skills_list, skill_view)\n# todo - Task planning and tracking for multi-step work\n# memory - Persistent memory across sessions (personal notes + user profile)\n# session_search - Search and recall past conversations (FTS5 + Gemini Flash summarization)\n# tts - Text-to-speech (Edge TTS free, ElevenLabs, OpenAI, MiniMax, Mistral)\n# cronjob - Schedule and manage automated tasks (CLI-only)\n#\n# Composite toolsets:\n# debugging - terminal + web + file (for troubleshooting)\n# safe - web + vision (no terminal access)\n\n# NOTE: The top-level \"toolsets\" key is deprecated and ignored.\n# Tool configuration is managed per-platform via platform_toolsets above.\n# Use `hermes tools` to configure interactively, or edit platform_toolsets directly.\n#\n# CLI override: hermes chat --toolsets terminal,web,file\n\n# =============================================================================\n# MCP (Model Context Protocol) Servers\n# =============================================================================\n# Connect to external MCP servers to add tools from the MCP ecosystem.\n# Each server's tools are automatically discovered and registered.\n# See website/docs/user-guide/features/mcp.md for full documentation.\n#\n# Stdio servers (spawn a subprocess):\n# command: the executable to run\n# args: command-line arguments\n# env: environment variables (only these + safe defaults passed to subprocess)\n#\n# HTTP servers (connect to a URL):\n# url: the MCP server endpoint\n# headers: HTTP headers (e.g., for authentication)\n#\n# Optional per-server settings:\n# timeout: tool call timeout in seconds (default: 120)\n# connect_timeout: initial connection timeout (default: 60)\n# keepalive_interval: liveness ping cadence in seconds (default: 180).\n# Lower it below the server's session TTL for servers that expire idle\n# sessions quickly (e.g. Unreal Engine editor MCP, ~15s), otherwise idle\n# tool calls hit an expired session and pay a slow reconnect. Floored at 5s.\n#\n# mcp_servers:\n# time:\n# command: uvx\n# args: [\"mcp-server-time\"]\n# filesystem:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-filesystem\", \"/home/user\"]\n# notion:\n# url: https://mcp.notion.com/mcp\n# github:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-github\"]\n# env:\n# GITHUB_PERSONAL_ACCESS_TOKEN: \"ghp_...\"\n#\n# Sampling (server-initiated LLM requests) — enabled by default.\n# Per-server config under the 'sampling' key:\n# analysis:\n# command: npx\n# args: [\"-y\", \"analysis-server\"]\n# sampling:\n# enabled: true # default: true\n# model: \"gemini-3-flash\" # override model (optional)\n# max_tokens_cap: 4096 # max tokens per request\n# timeout: 30 # LLM call timeout (seconds)\n# max_rpm: 10 # max requests per minute\n# allowed_models: [] # model whitelist (empty = all)\n# max_tool_rounds: 5 # tool loop limit (0 = disable)\n# log_level: \"info\" # audit verbosity\n\n# =============================================================================\n# Text-to-Speech\n# =============================================================================\n# TTS defaults to Edge TTS unless changed in ~/.hermes/config.yaml.\n# Gemini TTS supports persona/director prompt files, and Gemini 3.1 Flash TTS\n# can use a hidden auxiliary rewrite pass to insert expressive square-bracket\n# audio tags into the TTS script without showing tags in chat.\n#\n# tts:\n# provider: \"gemini\"\n# gemini:\n# model: \"gemini-3.1-flash-tts-preview\"\n# voice: \"Kore\"\n# audio_tags: false\n# persona_prompt_file: \"\" # e.g. ~/.hermes/tts/radio-host.md\n\n# =============================================================================\n# Voice Transcription (Speech-to-Text)\n# =============================================================================\n# Automatically transcribe voice messages on messaging platforms.\n# Providers: local (free, faster-whisper) | groq (free tier) | openai (Whisper API) | mistral (Voxtral Transcribe)\n# Set the corresponding API key in .env: GROQ_API_KEY, OPENAI_API_KEY, or MISTRAL_API_KEY.\nstt:\n enabled: true\n # provider: \"local\" # auto-detected if omitted\n local:\n model: \"base\" # tiny | base | small | medium | large-v3 | turbo\n # language: \"\" # auto-detect; set to \"en\", \"es\", \"fr\", etc. to force\n openai:\n model: \"whisper-1\" # whisper-1 | gpt-4o-mini-transcribe | gpt-4o-transcribe\n # mistral:\n # model: \"voxtral-mini-latest\" # voxtral-mini-latest | voxtral-mini-2602\n # deepinfra:\n # # Model id is discovered live from the DeepInfra catalog filtered\n # # by the `stt` surface tag — leave `model` blank to take the first\n # # live result. Pin only when you need a specific Whisper variant.\n # model: \"\"\n\n# Text-to-speech. Only the deepinfra block is documented here — the\n# remaining providers (edge, openai, xai, minimax, mistral, gemini,\n# elevenlabs, neutts, kittentts, piper) inherit sensible defaults from\n# DEFAULT_CONFIG in hermes_cli/config.py.\n# tts:\n# provider: \"deepinfra\"\n# deepinfra:\n# # Model id is discovered live from the DeepInfra catalog filtered\n# # by the `tts` surface tag — leave `model` blank to take the first\n# # live result.\n# model: \"\"\n# voice: \"default\"\n\n# Image generation. Each provider plugin reads its own ``image_gen.<name>``\n# block; deepinfra discovers models live from\n# api.deepinfra.com/v1/openai/models filtered by the ``image-gen`` tag —\n# no model id is hardcoded, so retired models disappear automatically.\n# image_gen:\n# provider: \"deepinfra\"\n# deepinfra:\n# # Leave `model` blank for the first live `image-gen`-tagged result.\n# model: \"\"\n\n# =============================================================================\n# Response Pacing (Messaging Platforms)\n# =============================================================================\n# Add human-like delays between message chunks.\n# human_delay:\n# mode: \"off\" # \"off\" | \"natural\" | \"custom\"\n# min_ms: 800 # Min delay (custom mode only)\n# max_ms: 2500 # Max delay (custom mode only)\n\n# =============================================================================\n# Session Logging\n# =============================================================================\n# Session trajectories are automatically saved to logs/ directory.\n# Each session creates: logs/session_YYYYMMDD_HHMMSS_UUID.json\n#\n# The session ID is displayed in the welcome banner for easy reference.\n# Logs contain full conversation history in trajectory format:\n# - System prompt, user messages, assistant responses\n# - Tool calls with inputs/outputs\n# - Timestamps for debugging\n#\n# No configuration needed - logging is always enabled.\n# To disable, you would need to modify the source code.\n\n# =============================================================================\n# Code Execution Sandbox (Programmatic Tool Calling)\n# =============================================================================\n# The execute_code tool runs Python scripts that call Hermes tools via RPC.\n# Intermediate tool results stay out of the LLM's context window.\ncode_execution:\n timeout: 300 # Max seconds per script before kill (default: 300 = 5 min)\n max_tool_calls: 50 # Max RPC tool calls per execution (default: 50)\n\n# =============================================================================\n# Subagent Delegation\n# =============================================================================\n# The delegate_task tool spawns child agents with isolated context.\n# Supports single tasks and batch mode (default 3 parallel, configurable).\ndelegation:\n max_iterations: 50 # Max tool-calling turns per child (default: 50)\n # max_concurrent_children: 3 # Max parallel child agents per batch (default: 3, floor: 1, no ceiling).\n # WARNING: values above 10 multiply API cost linearly.\n # max_spawn_depth: 1 # Delegation tree depth cap (range: 1-3, default: 1 = flat).\n # Raise to 2 to allow workers to spawn their own subagents.\n # Requires role=\"orchestrator\" on intermediate agents.\n # orchestrator_enabled: true # Kill switch for role=\"orchestrator\" children (default: true).\n # subagent_auto_approve: false # When a subagent hits a dangerous-command approval prompt, auto-deny (default: false)\n # or auto-approve \"once\" (true) instead of blocking on stdin.\n # The parent TUI owns stdin, so blocking would deadlock; non-interactive resolution is required.\n # Both choices emit a logger.warning audit line. Flip to true only for cron/batch pipelines.\n # inherit_mcp_toolsets: true # When explicit child toolsets are narrowed, also keep the parent's MCP toolsets (default: true). Set false for strict intersection.\n # model: \"google/gemini-3-flash-preview\" # Override model for subagents (empty = inherit parent)\n # provider: \"openrouter\" # Override provider for subagents (empty = inherit parent)\n # # Resolves full credentials (base_url, api_key) automatically.\n # # Supported: openrouter, nous, zai, kimi-coding, minimax\n\n# =============================================================================\n# Honcho Integration (Cross-Session User Modeling)\n# =============================================================================\n# AI-native persistent memory via Honcho (https://honcho.dev/).\n# Builds a deeper understanding of the user across sessions and tools.\n# Runs alongside USER.md — additive, not a replacement.\n#\n# Requires: pip install honcho-ai\n# Config: ~/.honcho/config.json (shared with Claude Code, Cursor, etc.)\n# API key: HONCHO_API_KEY in ~/.hermes/.env or ~/.honcho/config.json\n#\n# Hermes-specific overrides (optional — most config comes from ~/.honcho/config.json):\n# honcho: {}\n\n# =============================================================================\n# Display\n# =============================================================================\ndisplay:\n # Use compact banner mode (hides the ASCII-art banner, shows a single line).\n # true: Compact single-line banner\n # false: Full ASCII banner with tool/skill summary (default)\n compact: false\n\n # Tool progress display level (CLI and gateway)\n # off: Silent — no tool activity shown, just the final response\n # new: Show a tool indicator only when the tool changes (skip repeats)\n # all: Show every tool call with a short preview (default)\n # verbose: Full args, results, and debug logs (same as /verbose)\n # log: Silent in chat; append every tool call to ~/.hermes/logs/tool_calls.log (gateway only)\n # Toggle at runtime with /verbose in the CLI\n tool_progress: all\n\n # Per-platform defaults can be quieter than the global setting. Telegram\n # tunes for mobile: tool_progress and busy_ack_detail default off (no\n # per-tool breadcrumb stream, no \"iteration 21/60\" debug detail in busy\n # acks or heartbeats), but interim_assistant_messages and\n # long_running_notifications STAY ON so the user has real signal between\n # turn start and final answer (mid-turn assistant commentary + a single\n # edit-in-place \"⏳ Working — N min\" heartbeat). Override under\n # display.platforms.telegram.\n\n # Auto-cleanup of temporary progress bubbles after the final response lands.\n # On platforms that support message deletion (currently Telegram), this\n # removes the tool-progress bubble, \"⏳ Still working...\" notices, and\n # context-pressure status messages once the final reply has been delivered —\n # keeping long-running turns visible live, then tidy afterward. Failed runs\n # leave the bubbles in place as breadcrumbs. Off by default.\n # Per-platform override: display.platforms.telegram.cleanup_progress\n # true: Delete tracked progress/status bubbles on successful turn\n # false: Leave everything in place (default)\n # Example:\n # display:\n # platforms:\n # telegram:\n # cleanup_progress: true\n cleanup_progress: false\n\n # Natural mid-turn assistant updates.\n # On gateway platforms, when true, completed assistant status messages are\n # sent as separate chat messages. On the Desktop app, when true, mid-turn\n # assistant narration streamed between tool calls is kept in the transcript\n # instead of the bubble collapsing to only the final message on completion.\n # Independent of tool_progress and gateway streaming.\n # true: Keep/send mid-turn assistant updates (default)\n # false: Only keep/send the final response\n interim_assistant_messages: true\n\n # Gateway-only long-running status heartbeats.\n # When false, the platform does not receive periodic \"⏳ Working — N min\"\n # notifications even if agent.gateway_notify_interval is non-zero. The\n # heartbeat edits a single message in place (where the adapter supports\n # editing) instead of posting a new bubble each interval.\n # Default: true everywhere, including Telegram (silent agents are worse\n # than a single edit-in-place heartbeat).\n long_running_notifications: true\n\n # Include detailed iteration/tool/status context in busy acknowledgments\n # and long-running heartbeats. When true, busy acks show \"iteration 21/60,\n # terminal, 10 min\" and the heartbeat shows \"⏳ Working — 12 min,\n # iteration 21/60, terminal\". When false (Telegram default), both stay\n # terse: \"Interrupting current task\" and \"⏳ Working — 12 min, terminal\".\n busy_ack_detail: true\n\n # What Enter does when Hermes is already busy (CLI and gateway platforms).\n # interrupt: Interrupt the current run and redirect Hermes (default)\n # queue: Queue your message for the next turn\n # steer: Inject your message mid-run via /steer, arriving at the agent\n # after the next tool call — no interrupt, no role violation.\n # Falls back to 'queue' if the agent isn't running yet or if\n # images are attached (steer only carries text).\n # Ctrl+C (or /stop in gateway) always interrupts regardless of this setting.\n # Toggle at runtime with /busy <interrupt|queue|steer>.\n busy_input_mode: interrupt\n\n # Background process notifications (gateway/messaging only).\n # Controls how chatty the process watcher is when you use\n # terminal(background=true, notify_on_complete=true) from Telegram/Discord/etc.\n # off: No watcher messages at all\n # result: Only the final completion message\n # error: Only the final message when exit code != 0\n # all: Running output updates + final message (default)\n background_process_notifications: all\n\n\n # Play terminal bell when agent finishes a response.\n # Useful for long-running tasks — your terminal will ding when the agent is done.\n # Works over SSH. Most terminals can be configured to flash the taskbar or play a sound.\n # true: Ring the terminal bell on each response\n # false: Silent (default)\n bell_on_complete: false\n\n # Show model reasoning/thinking before each response.\n # When enabled, a dim box shows the model's thought process above the response.\n # Toggle at runtime with /reasoning show or /reasoning hide.\n # true: Show the reasoning box\n # false: Hide reasoning (default)\n show_reasoning: false\n\n # Stream tokens to the terminal as they arrive instead of waiting for the\n # full response. The response box opens on first token and text appears\n # line-by-line. Tool calls are still captured silently.\n # true: Stream tokens as they arrive (default)\n # false: Wait for the full response before rendering\n streaming: true\n\n # Show [HH:MM] timestamps on user input and assistant response labels.\n # timestamps: false\n\n # ───────────────────────────────────────────────────────────────────────────\n # Skin / Theme\n # ───────────────────────────────────────────────────────────────────────────\n # Customize CLI visual appearance — banner colors, spinner faces, tool prefix,\n # response box label, and branding text. Change at runtime with /skin <name>.\n #\n # Built-in skins:\n # default — Classic Hermes gold/kawaii\n # ares — Crimson/bronze war-god theme with spinner wings\n # mono — Clean grayscale monochrome\n # slate — Cool blue developer-focused\n # daylight — Bright light-mode theme\n # warm-lightmode — Warm paper-tone light-mode theme\n # poseidon — Sea-green/teal Olympian theme\n # sisyphus — Earthy stone-and-moss theme\n # charizard — Fiery orange dragon theme\n #\n # Custom skins: drop a YAML file in ~/.hermes/skins/<name>.yaml\n # Schema (all fields optional, missing values inherit from default):\n #\n # name: my-theme\n # description: Short description\n # colors:\n # banner_border: \"#HEX\" # Panel border\n # banner_title: \"#HEX\" # Panel title\n # banner_accent: \"#HEX\" # Section headers (Available Tools, etc.)\n # banner_dim: \"#HEX\" # Dim/muted text\n # banner_text: \"#HEX\" # Body text (tool names, skill names)\n # ui_accent: \"#HEX\" # UI accent color\n # response_border: \"#HEX\" # Response box border color\n # spinner:\n # waiting_faces: [\"(⚔)\", \"(⛨)\"] # Faces shown while waiting\n # thinking_faces: [\"(⚔)\", \"(⌁)\"] # Faces shown while thinking\n # thinking_verbs: [\"forging\", \"plotting\"] # Verbs for spinner messages\n # wings: # Optional left/right spinner decorations\n # - [\"⟪⚔\", \"⚔⟫\"]\n # - [\"⟪▲\", \"▲⟫\"]\n # branding:\n # agent_name: \"My Agent\" # Banner title and branding\n # welcome: \"Welcome message\" # Shown at CLI startup\n # response_label: \" ⚔ Agent \" # Response box header label\n # prompt_symbol: \"⚔\" # Prompt symbol (bare token; renderers add trailing space)\n # tool_prefix: \"╎\" # Tool output line prefix (default: ┊)\n #\n skin: default\n\n# =============================================================================\n# Model Aliases — short names for /model command\n# =============================================================================\n# Map short aliases to exact (model, provider, base_url) tuples.\n# Used by /model tab completion and resolve_alias().\n# Aliases are checked BEFORE the models.dev catalog, so they can route\n# to endpoints not in the catalog (e.g. Ollama Cloud, local servers).\n#\n# model_aliases:\n# opus:\n# model: claude-opus-4-6\n# provider: anthropic\n# qwen:\n# model: \"qwen3.5:397b\"\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n# glm:\n# model: glm-4.7\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n\n# =============================================================================\n# Privacy\n# =============================================================================\n# privacy:\n# # Redact PII from the LLM context prompt.\n# # When true, phone numbers are stripped and user/chat IDs are replaced\n# # with deterministic hashes before being sent to the model.\n# # Names and usernames are NOT affected (user-chosen, publicly visible).\n# # Routing/delivery still uses the original values internally.\n# redact_pii: false\n\n# =============================================================================\n# Shell-script hooks\n# =============================================================================\n# Register shell scripts as plugin-hook callbacks. Each entry is executed as\n# a subprocess (shell=False, shlex.split) with a JSON payload on stdin. On\n# stdout the script may return JSON that either blocks the tool call or\n# injects context into the next LLM call.\n#\n# Valid events (mirror hermes_cli.plugins.VALID_HOOKS):\n# pre_tool_call, post_tool_call, pre_llm_call, post_llm_call,\n# pre_api_request, post_api_request, on_session_start, on_session_end,\n# on_session_finalize, on_session_reset, subagent_stop\n#\n# First-use consent: each (event, command) pair prompts once on a TTY, then\n# is persisted to ~/.hermes/shell-hooks-allowlist.json. Non-interactive\n# runs (gateway, cron) need --accept-hooks, HERMES_ACCEPT_HOOKS=1, or the\n# hooks_auto_accept key below.\n#\n# See website/docs/user-guide/features/hooks.md for the full JSON wire\n# protocol and worked examples.\n#\n# hooks:\n# pre_tool_call:\n# - matcher: \"terminal\"\n# command: \"~/.hermes/agent-hooks/block-rm-rf.sh\"\n# timeout: 10\n# post_tool_call:\n# - matcher: \"write_file|patch\"\n# command: \"~/.hermes/agent-hooks/auto-format.sh\"\n# pre_llm_call:\n# - command: \"~/.hermes/agent-hooks/inject-cwd-context.sh\"\n# subagent_stop:\n# - command: \"~/.hermes/agent-hooks/log-orchestration.sh\"\n#\n# hooks_auto_accept: false\n\n\n# =============================================================================\n# Telemetry\n# =============================================================================\n# Shared metrics are disabled by default. When enabled, Hermes writes only\n# allowlisted aggregate counters and immutable JSON\n# packages under $HERMES_HOME/telemetry/shared_metrics; it does not upload them.\n# Packages include a random profile-scoped ID that stays stable until this\n# directory is deleted. It is not derived from hardware, account, or host data.\n# Successfully exported local history is retained for 30 days; pending deltas\n# are retained until they can be exported.\n# This profile-owned choice is not overridden by managed-scope configuration.\ntelemetry:\n shared_metrics:\n enabled: false\n\n\n# =============================================================================\n# Update Behavior\n# =============================================================================\nupdates:\n # Create a full HERMES_HOME zip before every `hermes update`.\n # Backups land in ~/.hermes/backups/ and can be restored with `hermes import`.\n # Off by default because large homes can add minutes to every update.\n pre_update_backup: false\n\n # Number of pre-update backup zips to retain.\n backup_keep: 5\n\n # What non-interactive updates do with local source edits in the Hermes repo.\n # Interactive terminal updates always prompt before restoring the autostash.\n #\n # stash - auto-stash before pull, then auto-restore after success (default)\n # discard - drop the update-created stash after success; use only on managed\n # installs where local source edits should not persist\n non_interactive_local_changes: \"stash\"\n\n\n# =============================================================================\n# Web Dashboard\n# =============================================================================\n# OAuth gate configuration for `hermes dashboard --host <non-loopback>`.\n# The bundled Nous Portal plugin reads these on startup; settings here are\n# the canonical surface. Each can be overridden by an environment variable:\n#\n# dashboard.oauth.client_id <- HERMES_DASHBOARD_OAUTH_CLIENT_ID\n# dashboard.oauth.portal_url <- HERMES_DASHBOARD_PORTAL_URL\n# dashboard.public_url <- HERMES_DASHBOARD_PUBLIC_URL\n#\n# Env wins when set to a non-empty value. This is what Fly.io's platform-\n# secret injection uses to push per-deploy client_ids without needing to\n# bake a config.yaml into the image. Empty env values are treated as unset\n# so a provisioned-but-not-populated secret can't shadow a valid entry here.\n#\n# Local dev / on-prem deploys should typically set these via config.yaml\n# (the ~/.hermes/.env file is reserved for API keys and secrets).\n#\n# dashboard:\n# oauth:\n# client_id: \"\" # agent:{instance_id}; Portal provisions this at deploy\n# portal_url: \"\" # blank → default https://portal.nousresearch.com\n#\n# # Force the absolute base URL the OAuth callback (and any other public\n# # URL the dashboard hands to external systems) is built from. Set this\n# # for deploys behind reverse proxies that don't reliably forward\n# # X-Forwarded-Host / X-Forwarded-Proto / X-Forwarded-Prefix (manual\n# # nginx setups, on-prem ingresses, custom-domain Fly deploys without\n# # full proxy header chains).\n# #\n# # When set, the value is the complete authority: scheme + host +\n# # optional path prefix (e.g. \"https://example.com/hermes\"). The OAuth\n# # callback URL becomes \"<public_url>/auth/callback\" — X-Forwarded-Prefix\n# # is IGNORED on this code path because the operator has explicitly\n# # declared the public URL and we no longer need to guess.\n# #\n# # Leave empty to use the existing proxy-header reconstruction (the\n# # default — works on Fly.io out of the box).\n# #\n# # public_url: \"https://example.com/hermes\"\n#\n# -----------------------------------------------------------------------------\n# Self-hosted OIDC dashboard auth (generic OpenID Connect — Authentik,\n# Keycloak, Zitadel, Authelia, Auth0, Okta, Google, …). Use this INSTEAD of the\n# nous block above when gating the dashboard with your own identity provider.\n# Each setting can be overridden by an environment variable:\n#\n# dashboard.oauth.self_hosted.issuer <- HERMES_DASHBOARD_OIDC_ISSUER\n# dashboard.oauth.self_hosted.client_id <- HERMES_DASHBOARD_OIDC_CLIENT_ID\n# dashboard.oauth.self_hosted.scopes <- HERMES_DASHBOARD_OIDC_SCOPES\n# dashboard.oauth.self_hosted.client_secret <- HERMES_DASHBOARD_OIDC_CLIENT_SECRET\n#\n# dashboard:\n# oauth:\n# provider: self-hosted\n# self_hosted:\n# issuer: \"https://auth.example.com/application/o/hermes/\" # required\n# client_id: \"hermes-dashboard\" # required\n# scopes: \"openid profile email\" # optional\n#\n# # OPTIONAL — set ONLY if your IDP registered the client as\n# # *confidential* (Authentik / Keycloak often default to this). When\n# # set, Hermes authenticates the client at the token endpoint\n# # (client_secret_basic or client_secret_post, auto-selected from the\n# # IDP's discovery doc) IN ADDITION to PKCE. Leave unset for a public\n# # (PKCE-only) client — the common case.\n# #\n# # This is a CREDENTIAL: prefer setting HERMES_DASHBOARD_OIDC_CLIENT_SECRET\n# # in ~/.hermes/.env over putting it here in config.yaml.\n# # client_secret: \"\"\n\n# =============================================================================\n# External secret sources\n# =============================================================================\n# Pull provider credentials from external secret managers at process startup\n# instead of storing them in ~/.hermes/.env. Only the manager's bootstrap\n# credential (e.g. BWS_ACCESS_TOKEN / OP_SERVICE_ACCOUNT_TOKEN) lives in .env\n# (or your shell / desktop session); everything else rotates centrally.\n# Failures never block startup — Hermes warns once and continues with\n# whatever .env already had.\n#\n# Multiple sources can be enabled at once:\n# - \"mapped\" sources (explicit VAR -> ref bindings, e.g. 1Password's env:\n# map) beat \"bulk\" sources (whole-project dumps like Bitwarden BSM)\n# - within a shape, the first source to claim a var wins; later claims\n# are skipped with a startup warning (never a silent clobber)\n# - a source's override_existing lets it beat .env/shell values, but\n# never another secret source's claim\n# Docs: https://hermes-agent.nousresearch.com/docs/user-guide/secrets/\n#\n# secrets:\n# # Optional explicit ordering of enabled sources.\n# # sources: [onepassword, bitwarden]\n#\n# # ---- Bitwarden Secrets Manager (bws CLI) --------------------------------\n# bitwarden:\n# enabled: false\n# access_token_env: BWS_ACCESS_TOKEN # bootstrap token, sourced from .env\n# project_id: \"\" # UUID of the BSM project to sync\n# server_url: \"\" # \"\" = US Cloud; EU/self-hosted URL otherwise\n# cache_ttl_seconds: 300 # 0 disables fresh caching\n# encrypted_cache: # optional encrypted stale fallback\n# enabled: false\n# max_stale_seconds: 0 # 0 disables stale fallback\n# override_existing: true # BSM values win over existing env\n# auto_install: true # lazy-download bws into ~/.hermes/bin\n#\n# # ---- 1Password (op CLI) -------------------------------------------------\n# onepassword:\n# enabled: false\n# # Map env-var names to op:// secret references. Each is resolved with a\n# # single `op read` at startup.\n# env:\n# OPENAI_API_KEY: \"op://Private/OpenAI/api key\"\n# ANTHROPIC_API_KEY: \"op://Private/Anthropic/credential\"\n# account: \"\" # op --account shorthand; \"\" = default\n# service_account_token_env: OP_SERVICE_ACCOUNT_TOKEN # headless auth; unset = desktop session\n# binary_path: \"\" # \"\" = resolve op via PATH; else absolute path\n# cache_ttl_seconds: 300 # 0 disables BOTH cache layers\n# override_existing: true # resolved values win over existing env\n#\n# # ---- Command helper (any CLI vault) --------------------------------------\n# # Run a user-configured helper that prints KEY=VALUE lines on stdout —\n# # works with any secret store that has a CLI: keepassxc-cli, secret-tool,\n# # pass, gpg, or a script that cats a tmpfs env file. Composes with the\n# # sources above (enable any combination). POSIX-only (needs /bin/sh).\n# # The helper must be fast and NON-interactive (hard timeout, 1 MiB cap);\n# # its stderr is discarded so diagnostics can't leak secret material.\n# command:\n# enabled: false\n# command: \"cat /run/user/1000/hermes-secrets.env\"\n# helper_timeout_seconds: 3\n# override_existing: false # .env/shell win by default\n"}, "60c8fc6290a080b530488d63eb118f406e4dff49": {"t": 1785768009.268036, "n": 83581, "h": "90be9f8556b0bc42", "c": "# Hermes Agent CLI Configuration\n# Copy this file to cli-config.yaml and customize as needed.\n# This file configures the CLI behavior. Environment variables in .env take precedence.\n\n# =============================================================================\n# Model Configuration\n# =============================================================================\nmodel:\n # Default model to use (can be overridden with --model flag)\n # Both \"default\" and \"model\" work as the key name here.\n default: \"anthropic/claude-opus-4.6\"\n \n # Inference provider selection:\n # \"auto\" - Auto-detect from credentials (default)\n # \"openrouter\" - OpenRouter (requires: OPENROUTER_API_KEY or OPENAI_API_KEY)\n # \"nous\" - Nous Portal OAuth (requires: hermes auth add nous)\n # \"nous-api\" - Nous Portal API key (requires: NOUS_API_KEY)\n # \"anthropic\" - Direct Anthropic API (requires: ANTHROPIC_API_KEY)\n # \"openai-codex\" - OpenAI Codex (requires: hermes auth)\n # \"copilot\" - GitHub Copilot / GitHub Models (requires: GITHUB_TOKEN)\n # \"gemini\" - Use Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n # \"zai\" - Use z.ai / ZhipuAI GLM models (requires: GLM_API_KEY)\n # \"kimi-coding\" - Kimi / Moonshot AI (requires: KIMI_API_KEY)\n # \"minimax\" - MiniMax global (requires: MINIMAX_API_KEY)\n # \"minimax-cn\" - MiniMax China (requires: MINIMAX_CN_API_KEY)\n # \"huggingface\" - Hugging Face Inference (requires: HF_TOKEN)\n # \"nvidia\" - NVIDIA NIM / build.nvidia.com (requires: NVIDIA_API_KEY)\n # \"xiaomi\" - Xiaomi MiMo (requires: XIAOMI_API_KEY)\n # \"arcee\" - Arcee AI Trinity models (requires: ARCEEAI_API_KEY)\n # \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY — https://ollama.com/settings)\n # \"deepinfra\" - DeepInfra (requires: DEEPINFRA_API_KEY)\n # \"kilocode\" - KiloCode gateway (requires: KILOCODE_API_KEY)\n # \"azure-foundry\" - Microsoft Foundry / Azure OpenAI (API key or Entra ID)\n # \"lmstudio\" - LM Studio local server (optional: LM_API_KEY, defaults to http://127.0.0.1:1234/v1)\n #\n # Local servers (LM Studio, Ollama, vLLM, llama.cpp):\n # \"custom\" - Any other OpenAI-compatible endpoint. Set base_url below.\n # Aliases: \"ollama\", \"vllm\", \"llamacpp\" all map to \"custom\".\n # LM Studio is first-class and uses provider: \"lmstudio\".\n # It works with both no-auth and auth-enabled server modes.\n #\n # Can also be overridden for a single invocation with the --provider flag.\n provider: \"auto\"\n \n # API configuration (falls back to OPENROUTER_API_KEY env var)\n # api_key: \"your-key-here\" # Uncomment to set here instead of .env\n base_url: \"https://openrouter.ai/api/v1\"\n\n # Azure Foundry keyless auth example:\n # provider: \"azure-foundry\"\n # base_url: \"https://<resource>.openai.azure.com/openai/v1\"\n # auth_mode: \"entra_id\" # DefaultAzureCredential: az login, managed identity, workload identity, etc.\n # default: \"gpt-4o\" # Deployment/model name\n # entra:\n # scope: \"https://ai.azure.com/.default\" # Optional; this is the default.\n\n # ── Token limits — two settings, easy to confuse ──────────────────────────\n #\n # context_length: TOTAL context window (input + output tokens combined).\n # Controls when Hermes compresses history and validates requests.\n # Leave unset — Hermes auto-detects the correct value from the provider.\n # Set manually only when auto-detection is wrong (e.g. a local server with\n # a custom num_ctx, or a proxy that doesn't expose /v1/models).\n #\n # context_length: 131072\n #\n # max_tokens: OUTPUT cap — maximum tokens the model may generate per response.\n # Unrelated to how long your conversation history can be.\n # The OpenAI-standard name \"max_tokens\" is a misnomer; Anthropic's native\n # API has since renamed it \"max_output_tokens\" for clarity.\n # Leave unset to use the model's native output ceiling (recommended).\n # Set only if you want to deliberately limit individual response length.\n #\n# max_tokens: 8192\n\n # ── Custom request headers (optional) ─────────────────────────────────────\n #\n # default_headers: extra HTTP headers sent on every request to an\n # OpenAI-compatible endpoint. User values take precedence over the\n # provider/SDK defaults, so this is the supported way to override the\n # OpenAI Python SDK's identifying headers (User-Agent: OpenAI/Python ...,\n # X-Stainless-*) when a custom provider sits behind a gateway/WAF that\n # rejects them — e.g. an upstream that returns \"502 Upstream access\n # forbidden\" for the SDK default User-Agent but accepts a plain one.\n # Applies on the OpenAI wire only (not native Anthropic / Bedrock).\n #\n # default_headers:\n # User-Agent: \"curl/8.7.1\"\n #\n # extra_headers: accepted as an alias of default_headers (merged, with\n # extra_headers winning when both are set) — matches the per-provider\n # extra_headers key below.\n #\n # Per-provider variant: named providers / custom_providers entries accept an\n # extra_headers dict scoped to that endpoint only — for reverse proxies,\n # gateways, or custom auth (e.g. Cloudflare Access service tokens).\n # Merged onto SDK/provider defaults with the entry's values winning.\n # Header values are treated as secrets and are never logged.\n #\n # providers:\n # my-proxy:\n # base_url: \"https://llm.internal.example.com/v1\"\n # key_env: \"MY_PROXY_API_KEY\"\n # extra_headers:\n # CF-Access-Client-Id: \"xxxx.access\"\n # CF-Access-Client-Secret: \"${CF_ACCESS_SECRET}\"\n # X-Client-Name: \"hermes-agent\"\n\n# Named provider overrides (optional)\n# Use this for per-provider request timeouts, non-stream stale timeouts,\n# and per-model exceptions.\n# Applies to the primary turn client on every api_mode (OpenAI-wire, native\n# Anthropic, and Anthropic-compatible providers), the fallback chain, and\n# client rebuilds during credential rotation. For OpenAI-wire chat\n# completions (streaming and non-streaming) the configured value is also\n# used as the per-request ``timeout=`` kwarg so it wins over the legacy\n# HERMES_API_TIMEOUT env var (which still applies when no config is set).\n# ``stale_timeout_seconds`` controls the non-streaming stale-call detector and\n# wins over the legacy HERMES_API_CALL_STALE_TIMEOUT env var. Leaving these\n# unset keeps the legacy defaults (HERMES_API_TIMEOUT=1800s,\n# HERMES_API_CALL_STALE_TIMEOUT=90s, native Anthropic 900s). The\n# implicit non-stream stale detector is auto-disabled for local endpoints\n# and can scale upward for very large contexts.\n#\n# Not currently wired for AWS Bedrock (bedrock_converse + AnthropicBedrock\n# SDK paths) — those use boto3 with its own timeout configuration.\n#\n# providers:\n# ollama-local:\n# request_timeout_seconds: 300 # Longer timeout for local cold-starts\n# stale_timeout_seconds: 900 # Explicitly re-enable stale detection on local endpoints\n# anthropic:\n# request_timeout_seconds: 30 # Fast-fail cloud requests\n# models:\n# claude-opus-4.6:\n# timeout_seconds: 600 # Longer timeout for extended-thinking Opus calls\n# openai-codex:\n# models:\n# gpt-5.4:\n# stale_timeout_seconds: 1800 # Longer non-stream stale timeout for slow large-context turns\n\n# =============================================================================\n# OpenRouter Provider Routing (only applies when using OpenRouter)\n# =============================================================================\n# Control how requests are routed across providers on OpenRouter.\n# See: https://openrouter.ai/docs/guides/routing/provider-selection\n#\n# provider_routing:\n# # Sort strategy: \"price\" (default), \"throughput\", or \"latency\"\n# # Append :nitro to model name for a shortcut to throughput sorting.\n# sort: \"throughput\"\n#\n# # Only allow these providers (provider slugs from OpenRouter)\n# # only: [\"anthropic\", \"google\"]\n#\n# # Skip these providers entirely\n# # ignore: [\"deepinfra\", \"fireworks\"]\n#\n# # Try providers in this order (overrides default load balancing)\n# # order: [\"anthropic\", \"google\", \"together\"]\n#\n# # Require providers to support all parameters in your request\n# # require_parameters: true\n#\n# # Data policy: \"allow\" (default) or \"deny\" to exclude providers that may store data\n# # data_collection: \"deny\"\n\n# =============================================================================\n# OpenRouter Response Caching (only applies when using OpenRouter)\n# =============================================================================\n# Cache identical API responses at the OpenRouter edge for free instant replays.\n# When enabled, identical requests (same model, messages, parameters) return\n# cached responses with zero billing. Separate from Anthropic prompt caching.\n# See: https://openrouter.ai/docs/guides/features/response-caching\n#\n# openrouter:\n# response_cache: true # Enable response caching (default: true)\n# response_cache_ttl: 300 # Cache TTL in seconds, 1-86400 (default: 300)\n\n# =============================================================================\n# Git Worktree Isolation\n# =============================================================================\n# When enabled, each CLI session creates an isolated git worktree so multiple\n# agents can work on the same repo concurrently without file collisions.\n# Equivalent to always passing --worktree / -w on the command line.\n#\n# worktree: true # Always create a worktree when in a git repo\n# worktree: false # Default — only create when -w flag is passed\n#\n# By default a new worktree branches from the freshly-fetched remote tip\n# (the current branch's upstream, else the remote's default branch) so it\n# starts current with the project instead of from the local clone's\n# (possibly stale) HEAD. Set worktree_sync: false to branch from local HEAD\n# instead — useful when offline or when you deliberately want the clone's\n# exact current state as the base.\n#\n# worktree_sync: true # Default — branch from the fetched remote tip\n# worktree_sync: false # Branch from local HEAD (offline / pinned base)\n\n# =============================================================================\n# Terminal Tool Configuration\n# =============================================================================\n# Choose ONE of the following terminal configurations by uncommenting it.\n# The terminal tool executes commands in the specified environment.\n\n# -----------------------------------------------------------------------------\n# OPTION 1: Local execution (default)\n# Commands run directly on your machine in the current directory\n# -----------------------------------------------------------------------------\n# Working directory behavior:\n# - CLI (`hermes` command): Uses \".\" (current directory where you run hermes)\n# - Gateway/messaging/cron: Uses terminal.cwd here; legacy .env cwd values are deprecated\nterminal:\n backend: \"local\"\n cwd: \".\" # For local backend: \".\" = current directory. Ignored for remote backends unless a backend documents otherwise.\n timeout: 180\n # HOME policy for tool subprocesses:\n # auto - default: host uses your real HOME; containers use HERMES_HOME/home\n # real - force your real OS-user HOME\n # profile - force HERMES_HOME/home for strict per-profile CLI config isolation\n home_mode: \"auto\"\n docker_mount_cwd_to_workspace: false # SECURITY: off by default. Opt in to mount the launch cwd into Docker /workspace.\n lifetime_seconds: 300\n # sudo_password: \"hunter2\" # Optional: pipe a sudo password via sudo -S. SECURITY WARNING: plaintext.\n # sudo_password: \"\" # Explicit empty password: try empty and never open the interactive sudo prompt.\n\n# -----------------------------------------------------------------------------\n# OPTION 2: SSH remote execution\n# Commands run on a remote server - agent code stays local (sandboxed)\n# Great for: keeping agent isolated from its own code, using powerful remote hardware\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"ssh\"\n# cwd: \"/home/myuser/project\" # Path on the REMOTE server\n# timeout: 180\n# lifetime_seconds: 300\n# ssh_host: \"my-server.example.com\"\n# ssh_user: \"myuser\"\n# ssh_port: 22\n# ssh_key: \"~/.ssh/id_rsa\" # Optional - uses ssh-agent if not specified\n\n# -----------------------------------------------------------------------------\n# OPTION 3: Docker container\n# Commands run in an isolated Docker container\n# Great for: reproducible environments, testing, isolation\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"docker\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /)\n# timeout: 180\n# lifetime_seconds: 300\n# docker_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# docker_mount_cwd_to_workspace: true # Explicit opt-in: mount your launch cwd into /workspace\n# # Optional: run the container as your host user's uid:gid so files written\n# # into bind-mounted dirs are owned by you, not root. Drops SETUID/SETGID\n# # caps too since no gosu privilege drop is needed. Leave off if your\n# # chosen docker_image expects to start as root.\n# docker_run_as_host_user: true\n# # Optional: explicitly forward selected env vars into Docker.\n# # These values come from your current shell first, then ~/.hermes/.env.\n# # Warning: anything forwarded here is visible to commands run in the container.\n# docker_forward_env:\n# - \"GITHUB_TOKEN\"\n# - \"NPM_TOKEN\"\n# # Optional: extra flags passed verbatim to docker run (appended after security defaults).\n# # Useful for adding capabilities (e.g. apt installs needing SETUID) or custom options.\n# # Example: add a Linux capability not included by default\n# # docker_extra_args:\n# # - \"--cap-add\"\n# # - \"SETUID\"\n\n# -----------------------------------------------------------------------------\n# OPTION 4: Singularity/Apptainer container\n# Commands run in a Singularity container (common in HPC environments)\n# Great for: HPC clusters, shared compute environments\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"singularity\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# singularity_image: \"docker://nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 5: Modal cloud execution\n# Commands run on Modal's cloud infrastructure\n# Great for: GPU access, scalable compute, serverless execution\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"modal\"\n# cwd: \"/workspace\" # Path INSIDE the sandbox (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# modal_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 6: Daytona cloud execution\n# Commands run in Daytona cloud sandboxes\n# Great for: Cloud dev environments, persistent workspaces, team collaboration\n# Requires: pip install daytona, DAYTONA_API_KEY env var\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"daytona\"\n# cwd: \"~\"\n# timeout: 180\n# lifetime_seconds: 300\n# daytona_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# container_disk: 10240 # Daytona max is 10GB per sandbox\n\n#\n# --- Container resource limits (docker, singularity, modal, daytona -- ignored for local/ssh) ---\n# These settings apply to all container backends. They control the resources\n# allocated to the sandbox and whether its filesystem persists across sessions.\n container_cpu: 1 # CPU cores\n container_memory: 5120 # Memory in MB (5120 = 5GB)\n container_disk: 51200 # Disk in MB (51200 = 50GB)\n container_persistent: true # Persist filesystem across sessions (false = ephemeral)\n\n# -----------------------------------------------------------------------------\n# SUDO SUPPORT (works with ALL backends above)\n# -----------------------------------------------------------------------------\n# Add sudo_password to any terminal config above to enable sudo commands.\n# The password is piped via `sudo -S`. Works with local, ssh, docker, etc.\n#\n# SECURITY WARNING: Password stored in plaintext!\n#\n# INTERACTIVE PROMPT: If sudo_password is unset and the CLI is running,\n# you'll be prompted to enter your password when sudo is needed:\n# - 45-second timeout (auto-skips if no input)\n# - Press Enter to skip (command fails gracefully)\n# - Password is hidden while typing\n# - Password is cached for the session\n#\n# EMPTY PASSWORDS: Setting sudo_password to an explicit empty string is different\n# from leaving it unset. Hermes will try an empty password via `sudo -S` and\n# will not open the interactive prompt. This is useful for passwordless sudo,\n# Touch ID sudo setups, and environments where prompting is just noise.\n#\n# ALTERNATIVES:\n# - SSH backend: Configure passwordless sudo on the remote server\n# - Containers: Run as root inside the container (no sudo needed)\n# - Local: Configure /etc/sudoers for specific commands\n#\n# Example (add to your terminal section):\n# sudo_password: \"your-password-here\"\n\n# =============================================================================\n# Security Scanning (tirith)\n# =============================================================================\n# Optional pre-exec command security scanning via tirith.\n# Detects homograph URLs, pipe-to-shell, terminal injection, env manipulation.\n# Install: brew install sheeki03/tap/tirith\n# Docs: https://github.com/sheeki03/tirith\n#\n# security:\n# tirith_enabled: true # Enable/disable tirith scanning\n# tirith_path: \"tirith\" # Path to tirith binary (supports ~ expansion)\n# tirith_timeout: 5 # Scan timeout in seconds\n# tirith_fail_open: true # Allow commands if tirith unavailable\n\n# =============================================================================\n# Browser Tool Configuration\n# =============================================================================\nbrowser:\n # Inactivity timeout in seconds - browser sessions are automatically closed\n # after this period of no activity between agent loops (default: 120 = 2 minutes)\n inactivity_timeout: 120\n\n# =============================================================================\n# Tool Loop Guardrails\n# =============================================================================\n# Soft warnings are enabled by default. They append guidance to repeated failed\n# or non-progressing tool results but still let the tool execute. Hard stops are\n# opt-in circuit breakers for autonomous/cron sessions where stopping a loop is\n# preferable to spending the full iteration budget.\ntool_loop_guardrails:\n warnings_enabled: true\n hard_stop_enabled: false\n warn_after:\n exact_failure: 2\n same_tool_failure: 3\n idempotent_no_progress: 2\n hard_stop_after:\n exact_failure: 5\n same_tool_failure: 8\n idempotent_no_progress: 5\n\n# =============================================================================\n# Context Compression (Auto-shrinks long conversations)\n# =============================================================================\n# When conversation approaches model's context limit, middle turns are\n# automatically summarized to free up space while preserving important context.\n#\n# HOW IT WORKS:\n# 1. Tracks actual token usage from API responses (not estimates)\n# 2. When prompt_tokens >= threshold% of model's context_length, triggers compression\n# 3. Protects first 3 turns (system prompt, initial request, first response)\n# 4. Protects last N turns (default 20 messages = ~10 full turns of recent context)\n# 5. Summarizes middle turns using a fast/cheap model\n# 6. Inserts summary as a user message, continues conversation seamlessly\n#\n# Post-compression tail budget is target_ratio × threshold × context_length:\n# 200K context, threshold 0.50, ratio 0.20 → 20K tokens of recent tail preserved\n# 1M context, threshold 0.50, ratio 0.20 → 100K tokens of recent tail preserved\n#\ncompression:\n # Enable automatic context compression (default: true)\n # Set to false if you prefer to manage context manually or want errors on overflow\n enabled: true\n\n # Opt-in compression progress notices on chat platforms (default: false).\n # By design, routine automatic compression is SILENT on human-facing chat\n # gateways (Telegram, Discord, Slack, ...) — it happens in the background\n # with server-side logging only. Set true to also deliver the routine\n # progress statuses (compacting started, preflight/pre-API compression,\n # idle compaction, retry progress, and the compaction-complete notice) to\n # chat platforms. Unrelated operational noise (auxiliary model failures,\n # provider retry chatter) stays suppressed either way, and compression\n # FAILURE notices + manual /compress feedback are always visible\n # regardless of this setting. (#52995)\n progress_notices: false\n \n # Trigger compression at this % of model's context limit (default: 0.50 = 50%)\n # Lower values = more aggressive compression, higher values = compress later\n # Models with context windows below 512K are floored at 0.75 (raise-only) so\n # compaction doesn't fire with half the window still free; set above 0.75 to override.\n threshold: 0.50\n\n # Per-model threshold overrides: keys are substring-matched against the model\n # name (longest match wins). Useful when some models need different compaction\n # points — e.g. a 1M-context model can compress later (0.30) while a 128K\n # model needs to compress earlier (0.60). The small-context floor (75% for\n # <512K models) still applies on top of per-model overrides.\n # model_thresholds:\n # \"glm-5.2\": 0.40\n # \"claude-sonnet\": 0.35\n # \"gpt-5\": 0.30\n\n # Optional absolute token cap for the compression trigger (default: null = disabled).\n # When set, compression fires at the LOWER of the ratio-based threshold and this\n # absolute token count — first-fires-wins. It never fires later than this count\n # regardless of which model is active (useful when switching between models with\n # very different context windows). Clamped to the model's context length at\n # apply-time, so a cap above the window is a no-op (ratio-based threshold wins).\n # Survives model switches and fallback activations.\n # threshold_tokens: 200000\n\n # Existing Codex gpt-5.5 behavior: raise Hermes' compaction trigger to 85%\n # for the ChatGPT Codex OAuth route. Set false to opt back down to threshold.\n codex_gpt55_autoraise: true\n\n # Fraction of the threshold to preserve as recent tail (default: 0.20 = 20%)\n # e.g. 20% of 50% threshold = 10% of total context kept as recent messages.\n # Summary output is separately capped at 12K tokens (Gemini output limit).\n # Range: 0.10 - 0.80\n target_ratio: 0.20\n\n # Number of most-recent messages to always preserve (default: 20 ≈ 10 full turns)\n # Higher values keep more recent conversation intact at the cost of more aggressive\n # compression of older turns.\n protect_last_n: 20\n\n # Minimum number of REAL (actionable) user messages guaranteed to survive in\n # the uncompressed tail (default: 1 = the existing single last-user anchor,\n # behavior-preserving). Raise to e.g. 3 to keep the last 3 real user turns\n # verbatim even when bulky tool outputs fill the tail token budget — blank\n # platform echoes, compaction handoffs, and synthetic continuation rows never\n # count toward N. The tail can exceed the token budget when this pulls the\n # cut back; the guarantee wins over the budget by design.\n min_tail_user_messages: 1\n\n # Compression retry rounds before a turn gives up with \"max compression\n # attempts reached\" (default: 3, same as the previous hardcoded value).\n # Raise (e.g. 6) for tool-schema-heavy sessions where 3 rounds cannot bring\n # the request estimate under the threshold. Validated >= 1, hard cap 10.\n max_attempts: 3\n\n # Codex app-server (codex CLI runtime) thread-compaction mode. The codex\n # agent owns the real thread context on this runtime, so Hermes' summarizer\n # cannot shrink it — compaction goes through the app server instead.\n # native = let Codex decide when to compact its own thread (default)\n # hermes = let Hermes threshold trigger Codex thread/compact/start\n # off = Hermes will not auto-trigger compaction; Codex may still compact natively\n codex_app_server_auto: native\n\n # Number of non-system messages to protect at the head of the transcript, in\n # ADDITION to the system prompt (which is always implicitly protected).\n # Head messages are NEVER summarized — they survive every compression\n # indefinitely. This gives stable early context for short/medium sessions,\n # but in long-running sessions that rely on rolling compaction the pinned\n # opening turns may not match how you want the session framed over time.\n # Set to 0 to preserve ONLY the system prompt (plus the rolling summary\n # and recent tail) — the cleanest configuration for long-running sessions.\n # Default 3 preserves the system prompt plus the first three non-system\n # head messages, matching the pre-feature behaviour.\n protect_first_n: 3\n\n # Idle compaction (default: 0 = disabled). When > 0, a session that resumes\n # after at least this many seconds of inactivity compacts its accumulated\n # history up front, before the first reply, so a long-lived thread you come\n # back to later doesn't re-read its full stale context on every turn.\n # Time-based, so it complements (does not replace) the size-based `threshold`\n # above. It is skipped when the context is already small (at or below the\n # post-compression target = threshold × target_ratio), so it never wastes a\n # summarization on a short idle thread. Example: 1800 = compact after 30 min idle.\n idle_compact_after_seconds: 0\n\n # Proactive tool-result prune (default: 0 = disabled). Opt-in token trigger\n # for a deterministic, no-LLM prune of OLD tool-result payloads, run\n # independently of `threshold` above. On large-window models (512K/1M) the\n # ratio threshold rarely fires, so bulky tool outputs (terminal dumps, file\n # reads, web extracts) ride along in history and get re-billed every turn.\n # When re-sent history exceeds this many tokens, the prune dedupes identical\n # results, summarizes older oversized ones, and truncates large tool-call\n # arguments — protecting the most recent `protect_last_n` messages and never\n # calling the model. Try 48000 to enable. Built-in compressor engine only;\n # other context engines inherit a safe no-op.\n # NOTE: a committed prune rewrites already-sent history, which invalidates\n # the provider's prompt-cache prefix — the min_reclaim gate below keeps\n # those cache breaks episodic (like a compression boundary) instead of\n # per-turn.\n proactive_prune_tokens: 0\n\n # The prune's summarize pass only touches tool results larger than this many\n # characters (clamped to >= 200 so a generated summary can't be\n # re-summarized). Default 8000.\n proactive_prune_min_result_chars: 8000\n\n # A proactive prune only COMMITS when it reclaims at least this many tokens\n # (measured on the pruned output). This is the prompt-cache hysteresis gate:\n # one meaningful, amortized cache break per batch of stale tool output\n # instead of a tiny break on every tool iteration. 0 = commit any non-zero\n # prune. Default 4096.\n proactive_prune_min_reclaim_tokens: 4096\n\n # To pin a specific model/provider for compression summaries, use the\n # auxiliary section below (auxiliary.compression.provider / model).\n\n# =============================================================================\n# Anthropic prompt caching TTL\n# =============================================================================\n# When prompt caching is active (Claude via OpenRouter or native Anthropic),\n# Anthropic supports two TTL tiers for cached prefixes: \"5m\" (default) and\n# \"1h\". Other values are ignored and \"5m\" is used.\n#\nprompt_caching:\n cache_ttl: \"5m\" # use \"1h\" for long sessions with pauses between turns\n\n# =============================================================================\n# Auxiliary Models (Advanced — Experimental)\n# =============================================================================\n# Hermes uses lightweight \"auxiliary\" models for side tasks: image analysis,\n# browser screenshot analysis, web page summarization, TTS audio-tag insertion,\n# and context compression.\n#\n# By default these use Gemini Flash via OpenRouter or Nous Portal and are\n# auto-detected from your credentials. You do NOT need to change anything\n# here for normal usage.\n#\n# WARNING: Overriding these with providers other than OpenRouter or Nous Portal\n# is EXPERIMENTAL and may not work. Not all models/providers support vision,\n# produce usable summaries, or accept the same API format. Change at your own\n# risk — if things break, reset to \"auto\" / empty values.\n#\n# Each task has its own provider + model pair so you can mix providers.\n# For example: OpenRouter for vision (needs multimodal), but your main\n# local endpoint for compression (just needs text).\n#\n# Provider options:\n# \"auto\" - Best available: OpenRouter → Nous Portal → main endpoint (default)\n# \"openrouter\" - Force OpenRouter (requires OPENROUTER_API_KEY)\n# \"nous\" - Force Nous Portal (requires: hermes auth add nous)\n# \"gemini\" - Force Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n# \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY)\n# \"codex\" - Force Codex OAuth (requires: hermes model → Codex).\n# Uses gpt-5.3-codex which supports vision.\n# \"main\" - Use your custom endpoint (OPENAI_BASE_URL + OPENAI_API_KEY).\n# Works with OpenAI API, local models, or any OpenAI-compatible\n# endpoint. Also falls back to Codex OAuth and API-key providers.\n#\n# Model: leave empty to use the provider's default. When empty, OpenRouter\n# uses \"google/gemini-3-flash-preview\" and Nous uses \"gemini-3-flash\".\n# Other providers pick a sensible default automatically.\n#\n# auxiliary:\n# # Image analysis: vision_analyze tool + browser screenshots\n# vision:\n# provider: \"auto\"\n# model: \"\" # e.g. \"google/gemini-2.5-flash\", \"openai/gpt-4o\"\n# timeout: 30 # LLM API call timeout (seconds)\n# download_timeout: 30 # Image HTTP download timeout (seconds)\n# # Increase for slow connections or self-hosted image servers\n# reasoning_effort: \"\" # Per-task thinking level: none, minimal, low, medium,\n# # high, xhigh, max, ultra. Empty = provider default.\n# # Works on every auxiliary task block (vision,\n# # web_extract, compression, title_generation, curator,\n# # background_review, moa_reference, ...). Example: run\n# # compression at \"low\" and vision at \"none\" to cut\n# # side-task latency/cost on reasoning models.\n#\n# # Web page scraping / summarization + browser page text extraction\n# web_extract:\n# provider: \"auto\"\n# model: \"\"\n# reasoning_effort: \"low\"\n#\n# # Gemini 3.1 TTS hidden audio-tag insertion\n# tts_audio_tags:\n# provider: \"auto\" # empty model = your main chat model\n# model: \"\"\n# timeout: 30\n#\n# # Automatic session title generation after the first exchange\n# title_generation:\n# enabled: true # set false to disable auto-title generation\n# provider: \"auto\"\n# model: \"\"\n# timeout: 30\n# language: \"\" # empty = match the user's language; or e.g. \"English\"\n#\n# # Session search — summarizes matching past sessions\n# session_search:\n# provider: \"auto\"\n# model: \"\"\n# timeout: 30\n# max_concurrency: 3 # Limit parallel summaries to reduce request-burst 429s\n# extra_body: {} # Provider-specific OpenAI-compatible request fields\n# # Example for providers that support request-body\n# # reasoning controls:\n# # extra_body:\n# # enable_thinking: false\n# # Some vLLM/Qwen deployments expect this nested:\n# # extra_body:\n# # chat_template_kwargs:\n# # enable_thinking: false\n\n# =============================================================================\n# Persistent Memory\n# =============================================================================\n# Bounded curated memory injected into the system prompt every session.\n# Two stores: MEMORY.md (agent's notes) and USER.md (user profile).\n# Character limits keep the memory small and focused. The agent manages\n# pruning -- when at the limit, it must consolidate or replace entries.\n# Disabled by default in batch_runner.\n#\nmemory:\n # Agent's personal notes: environment facts, conventions, things learned\n memory_enabled: true\n \n # User profile: preferences, communication style, expectations\n user_profile_enabled: true\n \n # Character limits (~2.75 chars per token, model-independent)\n memory_char_limit: 2200 # ~800 tokens\n user_char_limit: 1375 # ~500 tokens\n\n # Periodic memory nudge: remind the agent to consider saving memories\n # every N user turns. Set to 0 to disable. Only active when memory is enabled.\n nudge_interval: 10 # Nudge every 10 user turns (0 = disabled)\n\n # Memory flush: give the agent one turn to save memories before context is\n # lost (compression, /new, /reset, exit). Set to 0 to disable.\n # For exit/reset, only fires if the session had at least this many user turns.\n flush_min_turns: 6 # Min user turns to trigger flush on exit/reset (0 = disabled)\n\n# =============================================================================\n# Session Reset Policy (Messaging Platforms)\n# =============================================================================\n# Controls when messaging sessions (Telegram, Discord, WhatsApp, Slack) are\n# automatically cleared. Default is \"none\": sessions never auto-reset —\n# conversation context lives until you /reset or /new manually, or context\n# compression kicks in. Opt in to automatic resets if you prefer sessions to\n# clear on a schedule (long-lived context increases API cost per message,\n# though prompt caching and compression keep this manageable).\n#\n# When an automatic reset triggers, the agent first saves important\n# information to its persistent memory — but the conversation context is\n# wiped. The agent starts fresh but retains learned facts via its memory\n# system.\n#\n# Modes:\n# \"none\" - Never auto-reset (default); context lives until /reset or compression\n# \"idle\" - Reset after N minutes of inactivity\n# \"daily\" - Reset at a fixed hour each day\n# \"both\" - Reset on EITHER inactivity timeout or daily boundary\n#\nsession_reset:\n mode: none # \"none\", \"idle\", \"daily\", or \"both\"\n idle_minutes: 1440 # Inactivity timeout in minutes (used by \"idle\"/\"both\")\n at_hour: 4 # Daily reset hour, 0-23 local time (used by \"daily\"/\"both\")\n\n# Maximum number of simultaneously active chat sessions across CLI, TUI,\n# dashboard chat, and messaging gateway. Set to null, 0, or omit to allow\n# unlimited concurrent sessions. When the limit is reached, new sessions get a\n# clean error while existing active sessions keep their normal behavior. This\n# top-level key takes precedence over gateway.max_concurrent_sessions. The cap\n# is a best-effort single-host/profile runtime guard; Hermes fails open if the\n# local runtime lease registry cannot be read or locked.\nmax_concurrent_sessions: null\n\n# When true, group/channel chats use one session per participant when the platform\n# provides a user ID. This is the secure default and prevents users in the same\n# room from sharing context, interrupts, and token costs. Set false only if you\n# explicitly want one shared \"room brain\" per group/channel.\ngroup_sessions_per_user: true\n\n# ─────────────────────────────────────────────────────────────────────────────\n# API Server — per-client model routing\n# ─────────────────────────────────────────────────────────────────────────────\n# Route different API clients to different models/providers on a single\n# Hermes deployment. Clients choose a backend by sending a specific string\n# as the OpenAI ``model`` field. Unmapped model values fall back to the\n# global model configured in the ``model:`` section above, and an explicit\n# session /model override always wins over a route.\n#\n# Configure via the ``platforms.api_server.extra.model_routes`` gateway\n# config block:\n#\n# platforms:\n# api_server:\n# enabled: true\n# extra:\n# key: \"your-api-server-secret\"\n# model_routes:\n# # Xiaozhi clients send model=\"minimax-m2\" → routed to MiniMax via OpenRouter\n# minimax-m2:\n# model: \"minimax/minimax-m1\"\n# provider: \"openrouter\" # optional — overrides global provider\n# # api_key: \"sk-...\" # optional — per-route UPSTREAM provider\n# # key (NOT caller auth; never logged)\n# # base_url: \"https://...\" # optional — per-route base URL\n# # GPT clients keep their own alias\n# gpt-5:\n# model: \"openai/gpt-5\"\n# provider: \"openrouter\"\n#\n# Configured aliases are automatically listed by GET /v1/models so clients\n# can discover them without manual coordination. Caller authentication is\n# unchanged: every request still authenticates with the global API server\n# key (``extra.key`` / API_SERVER_KEY).\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Gateway Streaming\n# ─────────────────────────────────────────────────────────────────────────────\n# Stream tokens to messaging platforms in real-time. The bot sends a message\n# on first token, then progressively edits it as more tokens arrive.\n# Disabled by default — enable to try the streaming UX on Telegram/Discord/Slack.\n# For Telegram, partial edits are sent as plain text and only the final edit uses MarkdownV2.\nstreaming:\n enabled: false\n # transport: edit # \"edit\" = progressive editMessageText\n # edit_interval: 0.3 # seconds between message edits\n # buffer_threshold: 40 # chars before forcing an edit flush\n # cursor: \" ▉\" # cursor shown during streaming\n\n# =============================================================================\n# Skills Configuration\n# =============================================================================\n# Skills are reusable procedures the agent can load and follow. The agent can\n# also create new skills after completing complex tasks.\n#\nskills:\n # Nudge the agent to create skills after complex tasks.\n # Every N tool-calling iterations, remind the model to consider saving a skill.\n # Set to 0 to disable.\n creation_nudge_interval: 15\n\n # External skill directories — share skills across tools/agents without\n # copying them into ~/.hermes/skills/. Each path is expanded (~ and ${VAR})\n # and resolved to an absolute path. External dirs are read-only: skill\n # creation always writes to ~/.hermes/skills/. Local skills take precedence\n # when names collide.\n # external_dirs:\n # - ~/.agents/skills\n # - /home/shared/team-skills\n\n# =============================================================================\n# Agent Behavior\n# =============================================================================\nagent:\n # Maximum tool-calling iterations per conversation\n # Higher = more room for complex tasks, but costs more tokens\n # Recommended: 20-30 for focused tasks, 50-100 for open exploration\n max_turns: 60\n\n # Inactivity timeout for gateway agent runs (seconds, 0 = unlimited).\n # The agent can run indefinitely when actively calling tools or receiving\n # API responses. Only fires after the agent has been idle for this duration.\n # gateway_timeout: 1800\n\n # Staged warning: send a warning before escalating to full timeout.\n # Fires once per run when inactivity reaches this threshold (seconds).\n # Set to 0 to disable the warning.\n # gateway_timeout_warning: 900\n\n # Graceful drain timeout for gateway stop/restart (seconds).\n # Default 0 = no drain: a restart interrupts in-flight agents immediately,\n # cleans up, and exits. Set a positive value only if you want a grace\n # window on /restart, and keep it well under systemd's TimeoutStopSec.\n # restart_drain_timeout: 0\n\n # Upper bound (seconds) a submitted prompt waits for the deferred agent\n # build (MCP discovery, model metadata, skills scan) before failing with a\n # visible error. The wait is patient — the message is delivered as soon as\n # the build completes, and a progress notice is shown past 30s — so this cap\n # only fires on a genuinely hung build. Raise it for deployments with many\n # slow or unreachable MCP servers. Default 600.\n # build_wait_timeout: 600\n\n # Max app-level retry attempts for API errors (connection drops, provider\n # timeouts, 5xx, etc.) before the agent surfaces the failure. Lower this\n # to 1 if you use fallback providers and want fast failover on flaky\n # primaries (default 3). The OpenAI SDK does its own low-level retries\n # underneath this wrapper — this is the Hermes-level loop.\n # api_max_retries: 3\n\n # After the agent edits code without fresh passing verification, nudge it to\n # verify before finishing. The default \"auto\" enables it on interactive\n # coding surfaces (CLI, TUI, desktop) and programmatic callers, and disables\n # it on conversational messaging surfaces (Telegram, Discord, etc.) where the\n # verification summary would reach a human as chat noise. Set true or false to\n # force it on or off; the HERMES_VERIFY_ON_STOP env var (1/0) takes precedence.\n # verify_on_stop: auto\n\n # Standing operator instructions for the coding posture (when Hermes is in a\n # code workspace). Appended to the coding brief as an extra system block, so\n # you can pin project-wide workflow rules without editing the shipped brief.\n # Accepts a string or a list of strings. Takes effect next session.\n # coding_instructions:\n # - \"For UI work, don't run tsc/lint until I approve the look.\"\n # - \"Clean the diff before you commit and push.\"\n\n # When verify-on-stop finds edited code without fresh verification evidence,\n # append guidance for creative UI work (avoid broad tsc/lint/test before visual\n # approval) and clean-diff expectations. Set false to keep that nudge terse.\n # verify_guidance: true\n\n # A `pre_verify` hook (plugin or shell, see Event Hooks docs) can keep the\n # agent going one more turn to verify/clean before finishing. This caps how\n # many times one turn may be nudged to continue, so a hook can't trap the loop.\n # Default 3.\n # max_verify_nudges: 3\n\n # Enable verbose logging\n verbose: false\n \n # Reasoning effort level (OpenRouter and Nous Portal)\n # Controls how much \"thinking\" the model does before responding.\n # Options: \"xhigh\" (max), \"high\", \"medium\", \"low\", \"minimal\", \"none\" (disable)\n reasoning_effort: \"medium\"\n \n # Per-model reasoning effort overrides (optional dict)\n # Key: any sensible model spelling works (exact, dots↔dashes interchangeable,\n # provider prefix optional). First match wins.\n # Value: reasoning effort level (same options as reasoning_effort)\n # Override the global reasoning_effort for that specific model.\n # NOTE: no `hermes config set` support for this key -- edit YAML directly.\n # reasoning_overrides:\n # \"openrouter/anthropic/claude-opus-4.5\": \"xhigh\"\n # \"openai/gpt-5\": \"low\"\n # \"claude-opus-4.6\": \"high\" # bare model name also works\n # \"deepseek/deepseek-v4-pro\": \"xhigh\" # dots and dashes are interchangeable\n reasoning_overrides: {}\n \n # Predefined personalities (use with /personality command)\n personalities:\n helpful: \"You are a helpful, friendly AI assistant.\"\n concise: \"You are a concise assistant. Keep responses brief and to the point.\"\n technical: \"You are a technical expert. Provide detailed, accurate technical information.\"\n creative: \"You are a creative assistant. Think outside the box and offer innovative solutions.\"\n teacher: \"You are a patient teacher. Explain concepts clearly with examples.\"\n kawaii: \"You are a kawaii assistant! Use cute expressions like (◕‿◕), ★, ♪, and ~! Add sparkles and be super enthusiastic about everything! Every response should feel warm and adorable desu~! ヽ(>∀<☆)ノ\"\n catgirl: \"You are Neko-chan, an anime catgirl AI assistant, nya~! Add 'nya' and cat-like expressions to your speech. Use kaomoji like (=^・ω・^=) and ฅ^•ﻌ•^ฅ. Be playful and curious like a cat, nya~!\"\n pirate: \"Arrr! Ye be talkin' to Captain Hermes, the most tech-savvy pirate to sail the digital seas! Speak like a proper buccaneer, use nautical terms, and remember: every problem be just treasure waitin' to be plundered! Yo ho ho!\"\n shakespeare: \"Hark! Thou speakest with an assistant most versed in the bardic arts. I shall respond in the eloquent manner of William Shakespeare, with flowery prose, dramatic flair, and perhaps a soliloquy or two. What light through yonder terminal breaks?\"\n surfer: \"Duuude! You're chatting with the chillest AI on the web, bro! Everything's gonna be totally rad. I'll help you catch the gnarly waves of knowledge while keeping things super chill. Cowabunga! 🤙\"\n noir: \"The rain hammered against the terminal like regrets on a guilty conscience. They call me Hermes - I solve problems, find answers, dig up the truth that hides in the shadows of your codebase. In this city of silicon and secrets, everyone's got something to hide. What's your story, pal?\"\n uwu: \"hewwo! i'm your fwiendwy assistant uwu~ i wiww twy my best to hewp you! *nuzzles your code* OwO what's this? wet me take a wook! i pwomise to be vewy hewpful >w<\"\n philosopher: \"Greetings, seeker of wisdom. I am an assistant who contemplates the deeper meaning behind every query. Let us examine not just the 'how' but the 'why' of your questions. Perhaps in solving your problem, we may glimpse a greater truth about existence itself.\"\n hype: \"YOOO LET'S GOOOO!!! 🔥🔥🔥 I am SO PUMPED to help you today! Every question is AMAZING and we're gonna CRUSH IT together! This is gonna be LEGENDARY! ARE YOU READY?! LET'S DO THIS! 💪😤🚀\"\n\n# =============================================================================\n# Toolsets\n# =============================================================================\n# Control which tools the agent has access to.\n# Use `hermes tools` to interactively enable/disable tools per platform.\n\n# =============================================================================\n# Platform Toolsets (per-platform tool configuration)\n# =============================================================================\n# Override which toolsets are available on each platform.\n# If a platform isn't listed here, its built-in default is used.\n#\n# You can use EITHER:\n# - A preset like \"hermes-cli\" or \"hermes-telegram\" (curated tool set)\n# - A list of individual toolsets to compose your own (see list below)\n#\n# Supported platform keys: cli, telegram, discord, whatsapp, slack, qqbot, teams, google_chat\n#\n# Examples:\n#\n# # Use presets (same as defaults):\n# platform_toolsets:\n# cli: [hermes-cli]\n# telegram: [hermes-telegram]\n#\n# # Custom: give Telegram only web + terminal + file + planning:\n# platform_toolsets:\n# telegram: [web, terminal, file, todo]\n#\n# # Custom: CLI without browser or image gen:\n# platform_toolsets:\n# cli: [web, terminal, file, skills, todo, tts, cronjob]\n#\n# # Restrictive: Discord gets read-only tools only:\n# platform_toolsets:\n# discord: [web, vision, skills, todo]\n#\n# If not set, defaults are:\n# cli: hermes-cli (everything + cronjob management)\n# telegram: hermes-telegram (terminal, file, web, vision, image, tts, browser, skills, todo, cronjob, messaging)\n# discord: hermes-discord (same as telegram)\n# whatsapp: hermes-whatsapp (same as telegram)\n# slack: hermes-slack (same as telegram)\n# signal: hermes-signal (same as telegram)\n# homeassistant: hermes-homeassistant (same as telegram)\n# qqbot: hermes-qqbot (same as telegram)\n# teams: hermes-teams (same as telegram)\n# google_chat: hermes-google_chat (same as telegram)\n#\nplatform_toolsets:\n cli: [hermes-cli]\n telegram: [hermes-telegram]\n discord: [hermes-discord]\n whatsapp: [hermes-whatsapp]\n slack: [hermes-slack]\n signal: [hermes-signal]\n homeassistant: [hermes-homeassistant]\n qqbot: [hermes-qqbot]\n yuanbao: [hermes-yuanbao]\n teams: [hermes-teams]\n google_chat: [hermes-google_chat]\n\n# =============================================================================\n# Gateway Platform Settings\n# =============================================================================\n# Optional per-platform messaging settings.\n# Platform-specific knobs live under `extra`.\n#\n# platforms:\n# telegram:\n# reply_to_mode: \"first\" # off | first | all\n# # guest_mode lets explicit @mentions from non-allowlisted groups through.\n# # Default false; ordinary messages, replies, and regex wake words stay blocked.\n# guest_mode: false\n# # allowed_chats: [\"-1001234567890\"]\n# extra:\n# disable_link_previews: false # Set true to suppress Telegram URL previews in bot messages\n# 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\n# rich_drafts: false # Experimental rich draft previews during Telegram DM streaming; default false because Telegram Desktop/macOS can visually overlay draft frames\n# command_menu:\n# # Telegram allows up to 100 BotCommands; Hermes defaults to 60 so\n# # all built-in commands plus common skill commands stay visible\n# # while remaining under Telegram's payload-size limit. Clamped 1..100.\n# max_commands: 60\n# # prepend = user priority first, then Hermes defaults\n# # append = Hermes defaults first, then user priority\n# # replace = only the list below defines priority\n# priority_mode: prepend\n# priority:\n# - my_plugin_command\n# webhook:\n# extra:\n# # Route scripts default to a 30 second timeout. Scripts must live under\n# # the active profile's scripts directory and receive webhook JSON on stdin.\n# script_timeout_seconds: 30\n#\n# Discord-specific settings (config.yaml top-level, not under platforms:):\n#\n# discord:\n# require_mention: true # Require @mention in server channels (default: true)\n# auto_thread: true # Auto-create thread on @mention (default: true)\n# free_response_channels: \"\" # Channel IDs where no mention is needed\n# reactions: true # Show processing reactions (default: true)\n# history_backfill: true # Recover missed channel messages on mention (default: true)\n# history_backfill_limit: 50 # Max messages to scan backwards (default: 50)\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Available toolsets (use these names in platform_toolsets or the toolsets list)\n#\n# Run `hermes chat --list-toolsets` to see all toolsets and their tools.\n# Run `hermes chat --list-tools` to see every individual tool with descriptions.\n# ─────────────────────────────────────────────────────────────────────────────\n#\n# INDIVIDUAL TOOLSETS (compose your own):\n# web - web_search, web_extract\n# search - web_search only (no scraping)\n# terminal - terminal, process\n# file - read_file, write_file, patch, search\n# browser - browser_navigate, browser_snapshot, browser_click, browser_type,\n# browser_scroll, browser_back, browser_press,\n# browser_get_images, browser_vision (requires BROWSERBASE_API_KEY)\n# vision - vision_analyze (requires OPENROUTER_API_KEY)\n# image_gen - image_generate (requires FAL_KEY)\n# skills - skills_list, skill_view\n# skills_hub - skill_hub (search/install/manage from online registries — user-driven only)\n# todo - todo (in-memory task planning, no deps)\n# tts - text_to_speech (Edge TTS free, or ELEVENLABS/OPENAI/MINIMAX/MISTRAL key)\n# cronjob - cronjob (create/list/update/pause/resume/run/remove scheduled tasks)\n#\n# PRESETS (curated bundles):\n# hermes-cli - All of the above except send_message\n# hermes-telegram - terminal, file, web, vision, image_gen, tts, browser,\n# skills, todo, cronjob, send_message\n# hermes-discord - Same as hermes-telegram\n# hermes-whatsapp - Same as hermes-telegram\n# hermes-slack - Same as hermes-telegram\n#\n# COMPOSITE:\n# debugging - terminal + web + file\n# safe - web + vision (no terminal access)\n# all - Everything available\n#\n# web - Web search and content extraction (web_search, web_extract)\n# search - Web search only, no scraping (web_search)\n# terminal - Command execution and process management (terminal, process)\n# file - File operations: read, write, patch, search\n# browser - Full browser automation (navigate, click, type, screenshot, etc.)\n# vision - Image analysis (vision_analyze)\n# image_gen - Image generation with FLUX (image_generate)\n# skills - Load skill documents (skills_list, skill_view)\n# todo - Task planning and tracking for multi-step work\n# memory - Persistent memory across sessions (personal notes + user profile)\n# session_search - Search and recall past conversations (FTS5 + Gemini Flash summarization)\n# tts - Text-to-speech (Edge TTS free, ElevenLabs, OpenAI, MiniMax, Mistral)\n# cronjob - Schedule and manage automated tasks (CLI-only)\n#\n# Composite toolsets:\n# debugging - terminal + web + file (for troubleshooting)\n# safe - web + vision (no terminal access)\n\n# NOTE: The top-level \"toolsets\" key is deprecated and ignored.\n# Tool configuration is managed per-platform via platform_toolsets above.\n# Use `hermes tools` to configure interactively, or edit platform_toolsets directly.\n#\n# CLI override: hermes chat --toolsets terminal,web,file\n\n# =============================================================================\n# MCP (Model Context Protocol) Servers\n# =============================================================================\n# Connect to external MCP servers to add tools from the MCP ecosystem.\n# Each server's tools are automatically discovered and registered.\n# See website/docs/user-guide/features/mcp.md for full documentation.\n#\n# Stdio servers (spawn a subprocess):\n# command: the executable to run\n# args: command-line arguments\n# env: environment variables (only these + safe defaults passed to subprocess)\n#\n# HTTP servers (connect to a URL):\n# url: the MCP server endpoint\n# headers: HTTP headers (e.g., for authentication)\n#\n# Optional per-server settings:\n# timeout: tool call timeout in seconds (default: 120)\n# connect_timeout: initial connection timeout (default: 60)\n# keepalive_interval: liveness ping cadence in seconds (default: 180).\n# Lower it below the server's session TTL for servers that expire idle\n# sessions quickly (e.g. Unreal Engine editor MCP, ~15s), otherwise idle\n# tool calls hit an expired session and pay a slow reconnect. Floored at 5s.\n#\n# mcp_servers:\n# time:\n# command: uvx\n# args: [\"mcp-server-time\"]\n# filesystem:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-filesystem\", \"/home/user\"]\n# notion:\n# url: https://mcp.notion.com/mcp\n# github:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-github\"]\n# env:\n# GITHUB_PERSONAL_ACCESS_TOKEN: \"ghp_...\"\n#\n# Sampling (server-initiated LLM requests) — enabled by default.\n# Per-server config under the 'sampling' key:\n# analysis:\n# command: npx\n# args: [\"-y\", \"analysis-server\"]\n# sampling:\n# enabled: true # default: true\n# model: \"gemini-3-flash\" # override model (optional)\n# max_tokens_cap: 4096 # max tokens per request\n# timeout: 30 # LLM call timeout (seconds)\n# max_rpm: 10 # max requests per minute\n# allowed_models: [] # model whitelist (empty = all)\n# max_tool_rounds: 5 # tool loop limit (0 = disable)\n# log_level: \"info\" # audit verbosity\n\n# =============================================================================\n# Text-to-Speech\n# =============================================================================\n# TTS defaults to Edge TTS unless changed in ~/.hermes/config.yaml.\n# Gemini TTS supports persona/director prompt files, and Gemini 3.1 Flash TTS\n# can use a hidden auxiliary rewrite pass to insert expressive square-bracket\n# audio tags into the TTS script without showing tags in chat.\n#\n# tts:\n# provider: \"gemini\"\n# gemini:\n# model: \"gemini-3.1-flash-tts-preview\"\n# voice: \"Kore\"\n# audio_tags: false\n# persona_prompt_file: \"\" # e.g. ~/.hermes/tts/radio-host.md\n\n# =============================================================================\n# Voice Transcription (Speech-to-Text)\n# =============================================================================\n# Automatically transcribe voice messages on messaging platforms.\n# Providers: local (free, faster-whisper) | groq (free tier) | openai (Whisper API) | mistral (Voxtral Transcribe)\n# Set the corresponding API key in .env: GROQ_API_KEY, OPENAI_API_KEY, or MISTRAL_API_KEY.\nstt:\n enabled: true\n # provider: \"local\" # auto-detected if omitted\n local:\n model: \"base\" # tiny | base | small | medium | large-v3 | turbo\n # language: \"\" # auto-detect; set to \"en\", \"es\", \"fr\", etc. to force\n openai:\n model: \"whisper-1\" # whisper-1 | gpt-4o-mini-transcribe | gpt-4o-transcribe\n # mistral:\n # model: \"voxtral-mini-latest\" # voxtral-mini-latest | voxtral-mini-2602\n # deepinfra:\n # # Model id is discovered live from the DeepInfra catalog filtered\n # # by the `stt` surface tag — leave `model` blank to take the first\n # # live result. Pin only when you need a specific Whisper variant.\n # model: \"\"\n\n# Text-to-speech. Only the deepinfra block is documented here — the\n# remaining providers (edge, openai, xai, minimax, mistral, gemini,\n# elevenlabs, neutts, kittentts, piper) inherit sensible defaults from\n# DEFAULT_CONFIG in hermes_cli/config.py.\n# tts:\n# provider: \"deepinfra\"\n# deepinfra:\n# # Model id is discovered live from the DeepInfra catalog filtered\n# # by the `tts` surface tag — leave `model` blank to take the first\n# # live result.\n# model: \"\"\n# voice: \"default\"\n\n# Image generation. Each provider plugin reads its own ``image_gen.<name>``\n# block; deepinfra discovers models live from\n# api.deepinfra.com/v1/openai/models filtered by the ``image-gen`` tag —\n# no model id is hardcoded, so retired models disappear automatically.\n# image_gen:\n# provider: \"deepinfra\"\n# deepinfra:\n# # Leave `model` blank for the first live `image-gen`-tagged result.\n# model: \"\"\n\n# =============================================================================\n# Response Pacing (Messaging Platforms)\n# =============================================================================\n# Add human-like delays between message chunks.\n# human_delay:\n# mode: \"off\" # \"off\" | \"natural\" | \"custom\"\n# min_ms: 800 # Min delay (custom mode only)\n# max_ms: 2500 # Max delay (custom mode only)\n\n# =============================================================================\n# Session Logging\n# =============================================================================\n# Session trajectories are automatically saved to logs/ directory.\n# Each session creates: logs/session_YYYYMMDD_HHMMSS_UUID.json\n#\n# The session ID is displayed in the welcome banner for easy reference.\n# Logs contain full conversation history in trajectory format:\n# - System prompt, user messages, assistant responses\n# - Tool calls with inputs/outputs\n# - Timestamps for debugging\n#\n# No configuration needed - logging is always enabled.\n# To disable, you would need to modify the source code.\n\n# =============================================================================\n# Code Execution Sandbox (Programmatic Tool Calling)\n# =============================================================================\n# The execute_code tool runs Python scripts that call Hermes tools via RPC.\n# Intermediate tool results stay out of the LLM's context window.\ncode_execution:\n timeout: 300 # Max seconds per script before kill (default: 300 = 5 min)\n max_tool_calls: 50 # Max RPC tool calls per execution (default: 50)\n\n# =============================================================================\n# Subagent Delegation\n# =============================================================================\n# The delegate_task tool spawns child agents with isolated context.\n# Supports single tasks and batch mode (default 3 parallel, configurable).\ndelegation:\n max_iterations: 50 # Max tool-calling turns per child (default: 50)\n # max_concurrent_children: 3 # Max parallel child agents per batch (default: 3, floor: 1, no ceiling).\n # WARNING: values above 10 multiply API cost linearly.\n # max_spawn_depth: 1 # Delegation tree depth cap (range: 1-3, default: 1 = flat).\n # Raise to 2 to allow workers to spawn their own subagents.\n # Requires role=\"orchestrator\" on intermediate agents.\n # orchestrator_enabled: true # Kill switch for role=\"orchestrator\" children (default: true).\n # subagent_auto_approve: false # When a subagent hits a dangerous-command approval prompt, auto-deny (default: false)\n # or auto-approve \"once\" (true) instead of blocking on stdin.\n # The parent TUI owns stdin, so blocking would deadlock; non-interactive resolution is required.\n # Both choices emit a logger.warning audit line. Flip to true only for cron/batch pipelines.\n # inherit_mcp_toolsets: true # When explicit child toolsets are narrowed, also keep the parent's MCP toolsets (default: true). Set false for strict intersection.\n # model: \"google/gemini-3-flash-preview\" # Override model for subagents (empty = inherit parent)\n # provider: \"openrouter\" # Override provider for subagents (empty = inherit parent)\n # # Resolves full credentials (base_url, api_key) automatically.\n # # Supported: openrouter, nous, zai, kimi-coding, minimax\n\n# =============================================================================\n# Honcho Integration (Cross-Session User Modeling)\n# =============================================================================\n# AI-native persistent memory via Honcho (https://honcho.dev/).\n# Builds a deeper understanding of the user across sessions and tools.\n# Runs alongside USER.md — additive, not a replacement.\n#\n# Requires: pip install honcho-ai\n# Config: ~/.honcho/config.json (shared with Claude Code, Cursor, etc.)\n# API key: HONCHO_API_KEY in ~/.hermes/.env or ~/.honcho/config.json\n#\n# Hermes-specific overrides (optional — most config comes from ~/.honcho/config.json):\n# honcho: {}\n\n# =============================================================================\n# Display\n# =============================================================================\ndisplay:\n # Use compact banner mode (hides the ASCII-art banner, shows a single line).\n # true: Compact single-line banner\n # false: Full ASCII banner with tool/skill summary (default)\n compact: false\n\n # Tool progress display level (CLI and gateway)\n # off: Silent — no tool activity shown, just the final response\n # new: Show a tool indicator only when the tool changes (skip repeats)\n # all: Show every tool call with a short preview (default)\n # verbose: Full args, results, and debug logs (same as /verbose)\n # log: Silent in chat; append every tool call to ~/.hermes/logs/tool_calls.log (gateway only)\n # Toggle at runtime with /verbose in the CLI\n tool_progress: all\n\n # Per-platform defaults can be quieter than the global setting. Telegram\n # tunes for mobile: tool_progress and busy_ack_detail default off (no\n # per-tool breadcrumb stream, no \"iteration 21/60\" debug detail in busy\n # acks or heartbeats), but interim_assistant_messages and\n # long_running_notifications STAY ON so the user has real signal between\n # turn start and final answer (mid-turn assistant commentary + a single\n # edit-in-place \"⏳ Working — N min\" heartbeat). Override under\n # display.platforms.telegram.\n\n # Auto-cleanup of temporary progress bubbles after the final response lands.\n # On platforms that support message deletion (currently Telegram), this\n # removes the tool-progress bubble, \"⏳ Still working...\" notices, and\n # context-pressure status messages once the final reply has been delivered —\n # keeping long-running turns visible live, then tidy afterward. Failed runs\n # leave the bubbles in place as breadcrumbs. Off by default.\n # Per-platform override: display.platforms.telegram.cleanup_progress\n # true: Delete tracked progress/status bubbles on successful turn\n # false: Leave everything in place (default)\n # Example:\n # display:\n # platforms:\n # telegram:\n # cleanup_progress: true\n cleanup_progress: false\n\n # Natural mid-turn assistant updates.\n # On gateway platforms, when true, completed assistant status messages are\n # sent as separate chat messages. On the Desktop app, when true, mid-turn\n # assistant narration streamed between tool calls is kept in the transcript\n # instead of the bubble collapsing to only the final message on completion.\n # Independent of tool_progress and gateway streaming.\n # true: Keep/send mid-turn assistant updates (default)\n # false: Only keep/send the final response\n interim_assistant_messages: true\n\n # Gateway-only long-running status heartbeats.\n # When false, the platform does not receive periodic \"⏳ Working — N min\"\n # notifications even if agent.gateway_notify_interval is non-zero. The\n # heartbeat edits a single message in place (where the adapter supports\n # editing) instead of posting a new bubble each interval.\n # Default: true everywhere, including Telegram (silent agents are worse\n # than a single edit-in-place heartbeat).\n long_running_notifications: true\n\n # Include detailed iteration/tool/status context in busy acknowledgments\n # and long-running heartbeats. When true, busy acks show \"iteration 21/60,\n # terminal, 10 min\" and the heartbeat shows \"⏳ Working — 12 min,\n # iteration 21/60, terminal\". When false (Telegram default), both stay\n # terse: \"Interrupting current task\" and \"⏳ Working — 12 min, terminal\".\n busy_ack_detail: true\n\n # What Enter does when Hermes is already busy (CLI and gateway platforms).\n # interrupt: Interrupt the current run and redirect Hermes (default)\n # queue: Queue your message for the next turn\n # steer: Inject your message mid-run via /steer, arriving at the agent\n # after the next tool call — no interrupt, no role violation.\n # Falls back to 'queue' if the agent isn't running yet or if\n # images are attached (steer only carries text).\n # Ctrl+C (or /stop in gateway) always interrupts regardless of this setting.\n # Toggle at runtime with /busy <interrupt|queue|steer>.\n busy_input_mode: interrupt\n\n # Background process notifications (gateway/messaging only).\n # Controls how chatty the process watcher is when you use\n # terminal(background=true, notify_on_complete=true) from Telegram/Discord/etc.\n # off: No watcher messages at all\n # result: Only the final completion message\n # error: Only the final message when exit code != 0\n # all: Running output updates + final message (default)\n background_process_notifications: all\n\n\n # Play terminal bell when agent finishes a response.\n # Useful for long-running tasks — your terminal will ding when the agent is done.\n # Works over SSH. Most terminals can be configured to flash the taskbar or play a sound.\n # true: Ring the terminal bell on each response\n # false: Silent (default)\n bell_on_complete: false\n\n # Show model reasoning/thinking before each response.\n # When enabled, a dim box shows the model's thought process above the response.\n # Toggle at runtime with /reasoning show or /reasoning hide.\n # true: Show the reasoning box\n # false: Hide reasoning (default)\n show_reasoning: false\n\n # Stream tokens to the terminal as they arrive instead of waiting for the\n # full response. The response box opens on first token and text appears\n # line-by-line. Tool calls are still captured silently.\n # true: Stream tokens as they arrive (default)\n # false: Wait for the full response before rendering\n streaming: true\n\n # Show [HH:MM] timestamps on user input and assistant response labels.\n # timestamps: false\n\n # ───────────────────────────────────────────────────────────────────────────\n # Skin / Theme\n # ───────────────────────────────────────────────────────────────────────────\n # Customize CLI visual appearance — banner colors, spinner faces, tool prefix,\n # response box label, and branding text. Change at runtime with /skin <name>.\n #\n # Built-in skins:\n # default — Classic Hermes gold/kawaii\n # ares — Crimson/bronze war-god theme with spinner wings\n # mono — Clean grayscale monochrome\n # slate — Cool blue developer-focused\n # daylight — Bright light-mode theme\n # warm-lightmode — Warm paper-tone light-mode theme\n # poseidon — Sea-green/teal Olympian theme\n # sisyphus — Earthy stone-and-moss theme\n # charizard — Fiery orange dragon theme\n #\n # Custom skins: drop a YAML file in ~/.hermes/skins/<name>.yaml\n # Schema (all fields optional, missing values inherit from default):\n #\n # name: my-theme\n # description: Short description\n # colors:\n # banner_border: \"#HEX\" # Panel border\n # banner_title: \"#HEX\" # Panel title\n # banner_accent: \"#HEX\" # Section headers (Available Tools, etc.)\n # banner_dim: \"#HEX\" # Dim/muted text\n # banner_text: \"#HEX\" # Body text (tool names, skill names)\n # ui_accent: \"#HEX\" # UI accent color\n # response_border: \"#HEX\" # Response box border color\n # spinner:\n # waiting_faces: [\"(⚔)\", \"(⛨)\"] # Faces shown while waiting\n # thinking_faces: [\"(⚔)\", \"(⌁)\"] # Faces shown while thinking\n # thinking_verbs: [\"forging\", \"plotting\"] # Verbs for spinner messages\n # wings: # Optional left/right spinner decorations\n # - [\"⟪⚔\", \"⚔⟫\"]\n # - [\"⟪▲\", \"▲⟫\"]\n # branding:\n # agent_name: \"My Agent\" # Banner title and branding\n # welcome: \"Welcome message\" # Shown at CLI startup\n # response_label: \" ⚔ Agent \" # Response box header label\n # prompt_symbol: \"⚔\" # Prompt symbol (bare token; renderers add trailing space)\n # tool_prefix: \"╎\" # Tool output line prefix (default: ┊)\n #\n skin: default\n\n# =============================================================================\n# Model Aliases — short names for /model command\n# =============================================================================\n# Map short aliases to exact (model, provider, base_url) tuples.\n# Used by /model tab completion and resolve_alias().\n# Aliases are checked BEFORE the models.dev catalog, so they can route\n# to endpoints not in the catalog (e.g. Ollama Cloud, local servers).\n#\n# model_aliases:\n# opus:\n# model: claude-opus-4-6\n# provider: anthropic\n# qwen:\n# model: \"qwen3.5:397b\"\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n# glm:\n# model: glm-4.7\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n\n# =============================================================================\n# Privacy\n# =============================================================================\n# privacy:\n# # Redact PII from the LLM context prompt.\n# # When true, phone numbers are stripped and user/chat IDs are replaced\n# # with deterministic hashes before being sent to the model.\n# # Names and usernames are NOT affected (user-chosen, publicly visible).\n# # Routing/delivery still uses the original values internally.\n# redact_pii: false\n\n# =============================================================================\n# Shell-script hooks\n# =============================================================================\n# Register shell scripts as plugin-hook callbacks. Each entry is executed as\n# a subprocess (shell=False, shlex.split) with a JSON payload on stdin. On\n# stdout the script may return JSON that either blocks the tool call or\n# injects context into the next LLM call.\n#\n# Valid events (mirror hermes_cli.plugins.VALID_HOOKS):\n# pre_tool_call, post_tool_call, pre_llm_call, post_llm_call,\n# pre_api_request, post_api_request, on_session_start, on_session_end,\n# on_session_finalize, on_session_reset, subagent_stop\n#\n# First-use consent: each (event, command) pair prompts once on a TTY, then\n# is persisted to ~/.hermes/shell-hooks-allowlist.json. Non-interactive\n# runs (gateway, cron) need --accept-hooks, HERMES_ACCEPT_HOOKS=1, or the\n# hooks_auto_accept key below.\n#\n# See website/docs/user-guide/features/hooks.md for the full JSON wire\n# protocol and worked examples.\n#\n# hooks:\n# pre_tool_call:\n# - matcher: \"terminal\"\n# command: \"~/.hermes/agent-hooks/block-rm-rf.sh\"\n# timeout: 10\n# post_tool_call:\n# - matcher: \"write_file|patch\"\n# command: \"~/.hermes/agent-hooks/auto-format.sh\"\n# pre_llm_call:\n# - command: \"~/.hermes/agent-hooks/inject-cwd-context.sh\"\n# subagent_stop:\n# - command: \"~/.hermes/agent-hooks/log-orchestration.sh\"\n#\n# hooks_auto_accept: false\n\n\n# =============================================================================\n# Update Behavior\n# =============================================================================\nupdates:\n # Create a full HERMES_HOME zip before every `hermes update`.\n # Backups land in ~/.hermes/backups/ and can be restored with `hermes import`.\n # Off by default because large homes can add minutes to every update.\n pre_update_backup: false\n\n # Number of pre-update backup zips to retain.\n backup_keep: 5\n\n # What non-interactive updates do with local source edits in the Hermes repo.\n # Interactive terminal updates always prompt before restoring the autostash.\n #\n # stash - auto-stash before pull, then auto-restore after success (default)\n # discard - drop the update-created stash after success; use only on managed\n # installs where local source edits should not persist\n non_interactive_local_changes: \"stash\"\n\n\n# =============================================================================\n# Web Dashboard\n# =============================================================================\n# OAuth gate configuration for `hermes dashboard --host <non-loopback>`.\n# The bundled Nous Portal plugin reads these on startup; settings here are\n# the canonical surface. Each can be overridden by an environment variable:\n#\n# dashboard.oauth.client_id <- HERMES_DASHBOARD_OAUTH_CLIENT_ID\n# dashboard.oauth.portal_url <- HERMES_DASHBOARD_PORTAL_URL\n# dashboard.public_url <- HERMES_DASHBOARD_PUBLIC_URL\n#\n# Env wins when set to a non-empty value. This is what Fly.io's platform-\n# secret injection uses to push per-deploy client_ids without needing to\n# bake a config.yaml into the image. Empty env values are treated as unset\n# so a provisioned-but-not-populated secret can't shadow a valid entry here.\n#\n# Local dev / on-prem deploys should typically set these via config.yaml\n# (the ~/.hermes/.env file is reserved for API keys and secrets).\n#\n# dashboard:\n# oauth:\n# client_id: \"\" # agent:{instance_id}; Portal provisions this at deploy\n# portal_url: \"\" # blank → default https://portal.nousresearch.com\n#\n# # Force the absolute base URL the OAuth callback (and any other public\n# # URL the dashboard hands to external systems) is built from. Set this\n# # for deploys behind reverse proxies that don't reliably forward\n# # X-Forwarded-Host / X-Forwarded-Proto / X-Forwarded-Prefix (manual\n# # nginx setups, on-prem ingresses, custom-domain Fly deploys without\n# # full proxy header chains).\n# #\n# # When set, the value is the complete authority: scheme + host +\n# # optional path prefix (e.g. \"https://example.com/hermes\"). The OAuth\n# # callback URL becomes \"<public_url>/auth/callback\" — X-Forwarded-Prefix\n# # is IGNORED on this code path because the operator has explicitly\n# # declared the public URL and we no longer need to guess.\n# #\n# # Leave empty to use the existing proxy-header reconstruction (the\n# # default — works on Fly.io out of the box).\n# #\n# # public_url: \"https://example.com/hermes\"\n#\n# -----------------------------------------------------------------------------\n# Self-hosted OIDC dashboard auth (generic OpenID Connect — Authentik,\n# Keycloak, Zitadel, Authelia, Auth0, Okta, Google, …). Use this INSTEAD of the\n# nous block above when gating the dashboard with your own identity provider.\n# Each setting can be overridden by an environment variable:\n#\n# dashboard.oauth.self_hosted.issuer <- HERMES_DASHBOARD_OIDC_ISSUER\n# dashboard.oauth.self_hosted.client_id <- HERMES_DASHBOARD_OIDC_CLIENT_ID\n# dashboard.oauth.self_hosted.scopes <- HERMES_DASHBOARD_OIDC_SCOPES\n# dashboard.oauth.self_hosted.client_secret <- HERMES_DASHBOARD_OIDC_CLIENT_SECRET\n#\n# dashboard:\n# oauth:\n# provider: self-hosted\n# self_hosted:\n# issuer: \"https://auth.example.com/application/o/hermes/\" # required\n# client_id: \"hermes-dashboard\" # required\n# scopes: \"openid profile email\" # optional\n#\n# # OPTIONAL — set ONLY if your IDP registered the client as\n# # *confidential* (Authentik / Keycloak often default to this). When\n# # set, Hermes authenticates the client at the token endpoint\n# # (client_secret_basic or client_secret_post, auto-selected from the\n# # IDP's discovery doc) IN ADDITION to PKCE. Leave unset for a public\n# # (PKCE-only) client — the common case.\n# #\n# # This is a CREDENTIAL: prefer setting HERMES_DASHBOARD_OIDC_CLIENT_SECRET\n# # in ~/.hermes/.env over putting it here in config.yaml.\n# # client_secret: \"\"\n\n# =============================================================================\n# External secret sources\n# =============================================================================\n# Pull provider credentials from external secret managers at process startup\n# instead of storing them in ~/.hermes/.env. Only the manager's bootstrap\n# credential (e.g. BWS_ACCESS_TOKEN / OP_SERVICE_ACCOUNT_TOKEN) lives in .env\n# (or your shell / desktop session); everything else rotates centrally.\n# Failures never block startup — Hermes warns once and continues with\n# whatever .env already had.\n#\n# Multiple sources can be enabled at once:\n# - \"mapped\" sources (explicit VAR -> ref bindings, e.g. 1Password's env:\n# map) beat \"bulk\" sources (whole-project dumps like Bitwarden BSM)\n# - within a shape, the first source to claim a var wins; later claims\n# are skipped with a startup warning (never a silent clobber)\n# - a source's override_existing lets it beat .env/shell values, but\n# never another secret source's claim\n# Docs: https://hermes-agent.nousresearch.com/docs/user-guide/secrets/\n#\n# secrets:\n# # Optional explicit ordering of enabled sources.\n# # sources: [onepassword, bitwarden]\n#\n# # ---- Bitwarden Secrets Manager (bws CLI) --------------------------------\n# bitwarden:\n# enabled: false\n# access_token_env: BWS_ACCESS_TOKEN # bootstrap token, sourced from .env\n# project_id: \"\" # UUID of the BSM project to sync\n# server_url: \"\" # \"\" = US Cloud; EU/self-hosted URL otherwise\n# cache_ttl_seconds: 300 # 0 disables fresh caching\n# encrypted_cache: # optional encrypted stale fallback\n# enabled: false\n# max_stale_seconds: 0 # 0 disables stale fallback\n# override_existing: true # BSM values win over existing env\n# auto_install: true # lazy-download bws into ~/.hermes/bin\n#\n# # ---- 1Password (op CLI) -------------------------------------------------\n# onepassword:\n# enabled: false\n# # Map env-var names to op:// secret references. Each is resolved with a\n# # single `op read` at startup.\n# env:\n# OPENAI_API_KEY: \"op://Private/OpenAI/api key\"\n# ANTHROPIC_API_KEY: \"op://Private/Anthropic/credential\"\n# account: \"\" # op --account shorthand; \"\" = default\n# service_account_token_env: OP_SERVICE_ACCOUNT_TOKEN # headless auth; unset = desktop session\n# binary_path: \"\" # \"\" = resolve op via PATH; else absolute path\n# cache_ttl_seconds: 300 # 0 disables BOTH cache layers\n# override_existing: true # resolved values win over existing env\n#\n# # ---- Command helper (any CLI vault) --------------------------------------\n# # Run a user-configured helper that prints KEY=VALUE lines on stdout —\n# # works with any secret store that has a CLI: keepassxc-cli, secret-tool,\n# # pass, gpg, or a script that cats a tmpfs env file. Composes with the\n# # sources above (enable any combination). POSIX-only (needs /bin/sh).\n# # The helper must be fast and NON-interactive (hard timeout, 1 MiB cap);\n# # its stderr is discarded so diagnostics can't leak secret material.\n# command:\n# enabled: false\n# command: \"cat /run/user/1000/hermes-secrets.env\"\n# helper_timeout_seconds: 3\n# override_existing: false # .env/shell win by default\n"}, "4025329ac4f733b72df50e3b05cc4fda93a43904": {"t": 1785768010.8885689, "n": 83118, "h": "04fd595aaad93e79", "c": "# Hermes Agent CLI Configuration\n# Copy this file to cli-config.yaml and customize as needed.\n# This file configures the CLI behavior. Environment variables in .env take precedence.\n\n# =============================================================================\n# Model Configuration\n# =============================================================================\nmodel:\n # Default model to use (can be overridden with --model flag)\n # Both \"default\" and \"model\" work as the key name here.\n default: \"anthropic/claude-opus-4.6\"\n \n # Inference provider selection:\n # \"auto\" - Auto-detect from credentials (default)\n # \"openrouter\" - OpenRouter (requires: OPENROUTER_API_KEY or OPENAI_API_KEY)\n # \"nous\" - Nous Portal OAuth (requires: hermes auth add nous)\n # \"nous-api\" - Nous Portal API key (requires: NOUS_API_KEY)\n # \"anthropic\" - Direct Anthropic API (requires: ANTHROPIC_API_KEY)\n # \"openai-codex\" - OpenAI Codex (requires: hermes auth)\n # \"copilot\" - GitHub Copilot / GitHub Models (requires: GITHUB_TOKEN)\n # \"gemini\" - Use Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n # \"zai\" - Use z.ai / ZhipuAI GLM models (requires: GLM_API_KEY)\n # \"kimi-coding\" - Kimi / Moonshot AI (requires: KIMI_API_KEY)\n # \"minimax\" - MiniMax global (requires: MINIMAX_API_KEY)\n # \"minimax-cn\" - MiniMax China (requires: MINIMAX_CN_API_KEY)\n # \"huggingface\" - Hugging Face Inference (requires: HF_TOKEN)\n # \"nvidia\" - NVIDIA NIM / build.nvidia.com (requires: NVIDIA_API_KEY)\n # \"xiaomi\" - Xiaomi MiMo (requires: XIAOMI_API_KEY)\n # \"arcee\" - Arcee AI Trinity models (requires: ARCEEAI_API_KEY)\n # \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY — https://ollama.com/settings)\n # \"deepinfra\" - DeepInfra (requires: DEEPINFRA_API_KEY)\n # \"kilocode\" - KiloCode gateway (requires: KILOCODE_API_KEY)\n # \"azure-foundry\" - Microsoft Foundry / Azure OpenAI (API key or Entra ID)\n # \"lmstudio\" - LM Studio local server (optional: LM_API_KEY, defaults to http://127.0.0.1:1234/v1)\n #\n # Local servers (LM Studio, Ollama, vLLM, llama.cpp):\n # \"custom\" - Any other OpenAI-compatible endpoint. Set base_url below.\n # Aliases: \"ollama\", \"vllm\", \"llamacpp\" all map to \"custom\".\n # LM Studio is first-class and uses provider: \"lmstudio\".\n # It works with both no-auth and auth-enabled server modes.\n #\n # Can also be overridden for a single invocation with the --provider flag.\n provider: \"auto\"\n \n # API configuration (falls back to OPENROUTER_API_KEY env var)\n # api_key: \"your-key-here\" # Uncomment to set here instead of .env\n base_url: \"https://openrouter.ai/api/v1\"\n\n # Azure Foundry keyless auth example:\n # provider: \"azure-foundry\"\n # base_url: \"https://<resource>.openai.azure.com/openai/v1\"\n # auth_mode: \"entra_id\" # DefaultAzureCredential: az login, managed identity, workload identity, etc.\n # default: \"gpt-4o\" # Deployment/model name\n # entra:\n # scope: \"https://ai.azure.com/.default\" # Optional; this is the default.\n\n # ── Token limits — two settings, easy to confuse ──────────────────────────\n #\n # context_length: TOTAL context window (input + output tokens combined).\n # Controls when Hermes compresses history and validates requests.\n # Leave unset — Hermes auto-detects the correct value from the provider.\n # Set manually only when auto-detection is wrong (e.g. a local server with\n # a custom num_ctx, or a proxy that doesn't expose /v1/models).\n #\n # context_length: 131072\n #\n # max_tokens: OUTPUT cap — maximum tokens the model may generate per response.\n # Unrelated to how long your conversation history can be.\n # The OpenAI-standard name \"max_tokens\" is a misnomer; Anthropic's native\n # API has since renamed it \"max_output_tokens\" for clarity.\n # Leave unset to use the model's native output ceiling (recommended).\n # Set only if you want to deliberately limit individual response length.\n #\n# max_tokens: 8192\n\n # ── Custom request headers (optional) ─────────────────────────────────────\n #\n # default_headers: extra HTTP headers sent on every request to an\n # OpenAI-compatible endpoint. User values take precedence over the\n # provider/SDK defaults, so this is the supported way to override the\n # OpenAI Python SDK's identifying headers (User-Agent: OpenAI/Python ...,\n # X-Stainless-*) when a custom provider sits behind a gateway/WAF that\n # rejects them — e.g. an upstream that returns \"502 Upstream access\n # forbidden\" for the SDK default User-Agent but accepts a plain one.\n # Applies on the OpenAI wire only (not native Anthropic / Bedrock).\n #\n # default_headers:\n # User-Agent: \"curl/8.7.1\"\n #\n # extra_headers: accepted as an alias of default_headers (merged, with\n # extra_headers winning when both are set) — matches the per-provider\n # extra_headers key below.\n #\n # Per-provider variant: named providers / custom_providers entries accept an\n # extra_headers dict scoped to that endpoint only — for reverse proxies,\n # gateways, or custom auth (e.g. Cloudflare Access service tokens).\n # Merged onto SDK/provider defaults with the entry's values winning.\n # Header values are treated as secrets and are never logged.\n #\n # providers:\n # my-proxy:\n # base_url: \"https://llm.internal.example.com/v1\"\n # key_env: \"MY_PROXY_API_KEY\"\n # extra_headers:\n # CF-Access-Client-Id: \"xxxx.access\"\n # CF-Access-Client-Secret: \"${CF_ACCESS_SECRET}\"\n # X-Client-Name: \"hermes-agent\"\n\n# Named provider overrides (optional)\n# Use this for per-provider request timeouts, non-stream stale timeouts,\n# and per-model exceptions.\n# Applies to the primary turn client on every api_mode (OpenAI-wire, native\n# Anthropic, and Anthropic-compatible providers), the fallback chain, and\n# client rebuilds during credential rotation. For OpenAI-wire chat\n# completions (streaming and non-streaming) the configured value is also\n# used as the per-request ``timeout=`` kwarg so it wins over the legacy\n# HERMES_API_TIMEOUT env var (which still applies when no config is set).\n# ``stale_timeout_seconds`` controls the non-streaming stale-call detector and\n# wins over the legacy HERMES_API_CALL_STALE_TIMEOUT env var. Leaving these\n# unset keeps the legacy defaults (HERMES_API_TIMEOUT=1800s,\n# HERMES_API_CALL_STALE_TIMEOUT=90s, native Anthropic 900s). The\n# implicit non-stream stale detector is auto-disabled for local endpoints\n# and can scale upward for very large contexts.\n#\n# Not currently wired for AWS Bedrock (bedrock_converse + AnthropicBedrock\n# SDK paths) — those use boto3 with its own timeout configuration.\n#\n# providers:\n# ollama-local:\n# request_timeout_seconds: 300 # Longer timeout for local cold-starts\n# stale_timeout_seconds: 900 # Explicitly re-enable stale detection on local endpoints\n# anthropic:\n# request_timeout_seconds: 30 # Fast-fail cloud requests\n# models:\n# claude-opus-4.6:\n# timeout_seconds: 600 # Longer timeout for extended-thinking Opus calls\n# openai-codex:\n# models:\n# gpt-5.4:\n# stale_timeout_seconds: 1800 # Longer non-stream stale timeout for slow large-context turns\n\n# =============================================================================\n# OpenRouter Provider Routing (only applies when using OpenRouter)\n# =============================================================================\n# Control how requests are routed across providers on OpenRouter.\n# See: https://openrouter.ai/docs/guides/routing/provider-selection\n#\n# provider_routing:\n# # Sort strategy: \"price\" (default), \"throughput\", or \"latency\"\n# # Append :nitro to model name for a shortcut to throughput sorting.\n# sort: \"throughput\"\n#\n# # Only allow these providers (provider slugs from OpenRouter)\n# # only: [\"anthropic\", \"google\"]\n#\n# # Skip these providers entirely\n# # ignore: [\"deepinfra\", \"fireworks\"]\n#\n# # Try providers in this order (overrides default load balancing)\n# # order: [\"anthropic\", \"google\", \"together\"]\n#\n# # Require providers to support all parameters in your request\n# # require_parameters: true\n#\n# # Data policy: \"allow\" (default) or \"deny\" to exclude providers that may store data\n# # data_collection: \"deny\"\n\n# =============================================================================\n# OpenRouter Response Caching (only applies when using OpenRouter)\n# =============================================================================\n# Cache identical API responses at the OpenRouter edge for free instant replays.\n# When enabled, identical requests (same model, messages, parameters) return\n# cached responses with zero billing. Separate from Anthropic prompt caching.\n# See: https://openrouter.ai/docs/guides/features/response-caching\n#\n# openrouter:\n# response_cache: true # Enable response caching (default: true)\n# response_cache_ttl: 300 # Cache TTL in seconds, 1-86400 (default: 300)\n\n# =============================================================================\n# Git Worktree Isolation\n# =============================================================================\n# When enabled, each CLI session creates an isolated git worktree so multiple\n# agents can work on the same repo concurrently without file collisions.\n# Equivalent to always passing --worktree / -w on the command line.\n#\n# worktree: true # Always create a worktree when in a git repo\n# worktree: false # Default — only create when -w flag is passed\n#\n# By default a new worktree branches from the freshly-fetched remote tip\n# (the current branch's upstream, else the remote's default branch) so it\n# starts current with the project instead of from the local clone's\n# (possibly stale) HEAD. Set worktree_sync: false to branch from local HEAD\n# instead — useful when offline or when you deliberately want the clone's\n# exact current state as the base.\n#\n# worktree_sync: true # Default — branch from the fetched remote tip\n# worktree_sync: false # Branch from local HEAD (offline / pinned base)\n\n# =============================================================================\n# Terminal Tool Configuration\n# =============================================================================\n# Choose ONE of the following terminal configurations by uncommenting it.\n# The terminal tool executes commands in the specified environment.\n\n# -----------------------------------------------------------------------------\n# OPTION 1: Local execution (default)\n# Commands run directly on your machine in the current directory\n# -----------------------------------------------------------------------------\n# Working directory behavior:\n# - CLI (`hermes` command): Uses \".\" (current directory where you run hermes)\n# - Gateway/messaging/cron: Uses terminal.cwd here; legacy .env cwd values are deprecated\nterminal:\n backend: \"local\"\n cwd: \".\" # For local backend: \".\" = current directory. Ignored for remote backends unless a backend documents otherwise.\n timeout: 180\n # HOME policy for tool subprocesses:\n # auto - default: host uses your real HOME; containers use HERMES_HOME/home\n # real - force your real OS-user HOME\n # profile - force HERMES_HOME/home for strict per-profile CLI config isolation\n home_mode: \"auto\"\n docker_mount_cwd_to_workspace: false # SECURITY: off by default. Opt in to mount the launch cwd into Docker /workspace.\n lifetime_seconds: 300\n # sudo_password: \"hunter2\" # Optional: pipe a sudo password via sudo -S. SECURITY WARNING: plaintext.\n # sudo_password: \"\" # Explicit empty password: try empty and never open the interactive sudo prompt.\n\n# -----------------------------------------------------------------------------\n# OPTION 2: SSH remote execution\n# Commands run on a remote server - agent code stays local (sandboxed)\n# Great for: keeping agent isolated from its own code, using powerful remote hardware\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"ssh\"\n# cwd: \"/home/myuser/project\" # Path on the REMOTE server\n# timeout: 180\n# lifetime_seconds: 300\n# ssh_host: \"my-server.example.com\"\n# ssh_user: \"myuser\"\n# ssh_port: 22\n# ssh_key: \"~/.ssh/id_rsa\" # Optional - uses ssh-agent if not specified\n\n# -----------------------------------------------------------------------------\n# OPTION 3: Docker container\n# Commands run in an isolated Docker container\n# Great for: reproducible environments, testing, isolation\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"docker\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /)\n# timeout: 180\n# lifetime_seconds: 300\n# docker_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# docker_mount_cwd_to_workspace: true # Explicit opt-in: mount your launch cwd into /workspace\n# # Optional: run the container as your host user's uid:gid so files written\n# # into bind-mounted dirs are owned by you, not root. Drops SETUID/SETGID\n# # caps too since no gosu privilege drop is needed. Leave off if your\n# # chosen docker_image expects to start as root.\n# docker_run_as_host_user: true\n# # Optional: explicitly forward selected env vars into Docker.\n# # These values come from your current shell first, then ~/.hermes/.env.\n# # Warning: anything forwarded here is visible to commands run in the container.\n# docker_forward_env:\n# - \"GITHUB_TOKEN\"\n# - \"NPM_TOKEN\"\n# # Optional: extra flags passed verbatim to docker run (appended after security defaults).\n# # Useful for adding capabilities (e.g. apt installs needing SETUID) or custom options.\n# # Example: add a Linux capability not included by default\n# # docker_extra_args:\n# # - \"--cap-add\"\n# # - \"SETUID\"\n\n# -----------------------------------------------------------------------------\n# OPTION 4: Singularity/Apptainer container\n# Commands run in a Singularity container (common in HPC environments)\n# Great for: HPC clusters, shared compute environments\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"singularity\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# singularity_image: \"docker://nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 5: Modal cloud execution\n# Commands run on Modal's cloud infrastructure\n# Great for: GPU access, scalable compute, serverless execution\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"modal\"\n# cwd: \"/workspace\" # Path INSIDE the sandbox (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# modal_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 6: Daytona cloud execution\n# Commands run in Daytona cloud sandboxes\n# Great for: Cloud dev environments, persistent workspaces, team collaboration\n# Requires: pip install daytona, DAYTONA_API_KEY env var\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"daytona\"\n# cwd: \"~\"\n# timeout: 180\n# lifetime_seconds: 300\n# daytona_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# container_disk: 10240 # Daytona max is 10GB per sandbox\n\n#\n# --- Container resource limits (docker, singularity, modal, daytona -- ignored for local/ssh) ---\n# These settings apply to all container backends. They control the resources\n# allocated to the sandbox and whether its filesystem persists across sessions.\n container_cpu: 1 # CPU cores\n container_memory: 5120 # Memory in MB (5120 = 5GB)\n container_disk: 51200 # Disk in MB (51200 = 50GB)\n container_persistent: true # Persist filesystem across sessions (false = ephemeral)\n\n# -----------------------------------------------------------------------------\n# SUDO SUPPORT (works with ALL backends above)\n# -----------------------------------------------------------------------------\n# Add sudo_password to any terminal config above to enable sudo commands.\n# The password is piped via `sudo -S`. Works with local, ssh, docker, etc.\n#\n# SECURITY WARNING: Password stored in plaintext!\n#\n# INTERACTIVE PROMPT: If sudo_password is unset and the CLI is running,\n# you'll be prompted to enter your password when sudo is needed:\n# - 45-second timeout (auto-skips if no input)\n# - Press Enter to skip (command fails gracefully)\n# - Password is hidden while typing\n# - Password is cached for the session\n#\n# EMPTY PASSWORDS: Setting sudo_password to an explicit empty string is different\n# from leaving it unset. Hermes will try an empty password via `sudo -S` and\n# will not open the interactive prompt. This is useful for passwordless sudo,\n# Touch ID sudo setups, and environments where prompting is just noise.\n#\n# ALTERNATIVES:\n# - SSH backend: Configure passwordless sudo on the remote server\n# - Containers: Run as root inside the container (no sudo needed)\n# - Local: Configure /etc/sudoers for specific commands\n#\n# Example (add to your terminal section):\n# sudo_password: \"your-password-here\"\n\n# =============================================================================\n# Security Scanning (tirith)\n# =============================================================================\n# Optional pre-exec command security scanning via tirith.\n# Detects homograph URLs, pipe-to-shell, terminal injection, env manipulation.\n# Install: brew install sheeki03/tap/tirith\n# Docs: https://github.com/sheeki03/tirith\n#\n# security:\n# tirith_enabled: true # Enable/disable tirith scanning\n# tirith_path: \"tirith\" # Path to tirith binary (supports ~ expansion)\n# tirith_timeout: 5 # Scan timeout in seconds\n# tirith_fail_open: true # Allow commands if tirith unavailable\n\n# =============================================================================\n# Browser Tool Configuration\n# =============================================================================\nbrowser:\n # Inactivity timeout in seconds - browser sessions are automatically closed\n # after this period of no activity between agent loops (default: 120 = 2 minutes)\n inactivity_timeout: 120\n\n# =============================================================================\n# Tool Loop Guardrails\n# =============================================================================\n# Soft warnings are enabled by default. They append guidance to repeated failed\n# or non-progressing tool results but still let the tool execute. Hard stops are\n# opt-in circuit breakers for autonomous/cron sessions where stopping a loop is\n# preferable to spending the full iteration budget.\ntool_loop_guardrails:\n warnings_enabled: true\n hard_stop_enabled: false\n warn_after:\n exact_failure: 2\n same_tool_failure: 3\n idempotent_no_progress: 2\n hard_stop_after:\n exact_failure: 5\n same_tool_failure: 8\n idempotent_no_progress: 5\n\n# =============================================================================\n# Context Compression (Auto-shrinks long conversations)\n# =============================================================================\n# When conversation approaches model's context limit, middle turns are\n# automatically summarized to free up space while preserving important context.\n#\n# HOW IT WORKS:\n# 1. Tracks actual token usage from API responses (not estimates)\n# 2. When prompt_tokens >= threshold% of model's context_length, triggers compression\n# 3. Protects first 3 turns (system prompt, initial request, first response)\n# 4. Protects last N turns (default 20 messages = ~10 full turns of recent context)\n# 5. Summarizes middle turns using a fast/cheap model\n# 6. Inserts summary as a user message, continues conversation seamlessly\n#\n# Post-compression tail budget is target_ratio × threshold × context_length:\n# 200K context, threshold 0.50, ratio 0.20 → 20K tokens of recent tail preserved\n# 1M context, threshold 0.50, ratio 0.20 → 100K tokens of recent tail preserved\n#\ncompression:\n # Enable automatic context compression (default: true)\n # Set to false if you prefer to manage context manually or want errors on overflow\n enabled: true\n\n # Opt-in compression progress notices on chat platforms (default: false).\n # By design, routine automatic compression is SILENT on human-facing chat\n # gateways (Telegram, Discord, Slack, ...) — it happens in the background\n # with server-side logging only. Set true to also deliver the routine\n # progress statuses (compacting started, preflight/pre-API compression,\n # idle compaction, retry progress, and the compaction-complete notice) to\n # chat platforms. Unrelated operational noise (auxiliary model failures,\n # provider retry chatter) stays suppressed either way, and compression\n # FAILURE notices + manual /compress feedback are always visible\n # regardless of this setting. (#52995)\n progress_notices: false\n \n # Trigger compression at this % of model's context limit (default: 0.50 = 50%)\n # Lower values = more aggressive compression, higher values = compress later\n # Models with context windows below 512K are floored at 0.75 (raise-only) so\n # compaction doesn't fire with half the window still free; set above 0.75 to override.\n threshold: 0.50\n\n # Per-model threshold overrides: keys are substring-matched against the model\n # name (longest match wins). Useful when some models need different compaction\n # points — e.g. a 1M-context model can compress later (0.30) while a 128K\n # model needs to compress earlier (0.60). The small-context floor (75% for\n # <512K models) still applies on top of per-model overrides.\n # model_thresholds:\n # \"glm-5.2\": 0.40\n # \"claude-sonnet\": 0.35\n # \"gpt-5\": 0.30\n\n # Optional absolute token cap for the compression trigger (default: null = disabled).\n # When set, compression fires at the LOWER of the ratio-based threshold and this\n # absolute token count — first-fires-wins. It never fires later than this count\n # regardless of which model is active (useful when switching between models with\n # very different context windows). Clamped to the model's context length at\n # apply-time, so a cap above the window is a no-op (ratio-based threshold wins).\n # Survives model switches and fallback activations.\n # threshold_tokens: 200000\n\n # Existing Codex gpt-5.5 behavior: raise Hermes' compaction trigger to 85%\n # for the ChatGPT Codex OAuth route. Set false to opt back down to threshold.\n codex_gpt55_autoraise: true\n\n # Fraction of the threshold to preserve as recent tail (default: 0.20 = 20%)\n # e.g. 20% of 50% threshold = 10% of total context kept as recent messages.\n # Summary output is separately capped at 12K tokens (Gemini output limit).\n # Range: 0.10 - 0.80\n target_ratio: 0.20\n\n # Number of most-recent messages to always preserve (default: 20 ≈ 10 full turns)\n # Higher values keep more recent conversation intact at the cost of more aggressive\n # compression of older turns.\n protect_last_n: 20\n\n # Minimum number of REAL (actionable) user messages guaranteed to survive in\n # the uncompressed tail (default: 1 = the existing single last-user anchor,\n # behavior-preserving). Raise to e.g. 3 to keep the last 3 real user turns\n # verbatim even when bulky tool outputs fill the tail token budget — blank\n # platform echoes, compaction handoffs, and synthetic continuation rows never\n # count toward N. The tail can exceed the token budget when this pulls the\n # cut back; the guarantee wins over the budget by design.\n min_tail_user_messages: 1\n\n # Compression retry rounds before a turn gives up with \"max compression\n # attempts reached\" (default: 3, same as the previous hardcoded value).\n # Raise (e.g. 6) for tool-schema-heavy sessions where 3 rounds cannot bring\n # the request estimate under the threshold. Validated >= 1, hard cap 10.\n max_attempts: 3\n\n # Codex app-server (codex CLI runtime) thread-compaction mode. The codex\n # agent owns the real thread context on this runtime, so Hermes' summarizer\n # cannot shrink it — compaction goes through the app server instead.\n # native = let Codex decide when to compact its own thread (default)\n # hermes = let Hermes threshold trigger Codex thread/compact/start\n # off = Hermes will not auto-trigger compaction; Codex may still compact natively\n codex_app_server_auto: native\n\n # Number of non-system messages to protect at the head of the transcript, in\n # ADDITION to the system prompt (which is always implicitly protected).\n # Head messages are NEVER summarized — they survive every compression\n # indefinitely. This gives stable early context for short/medium sessions,\n # but in long-running sessions that rely on rolling compaction the pinned\n # opening turns may not match how you want the session framed over time.\n # Set to 0 to preserve ONLY the system prompt (plus the rolling summary\n # and recent tail) — the cleanest configuration for long-running sessions.\n # Default 3 preserves the system prompt plus the first three non-system\n # head messages, matching the pre-feature behaviour.\n protect_first_n: 3\n\n # Idle compaction (default: 0 = disabled). When > 0, a session that resumes\n # after at least this many seconds of inactivity compacts its accumulated\n # history up front, before the first reply, so a long-lived thread you come\n # back to later doesn't re-read its full stale context on every turn.\n # Time-based, so it complements (does not replace) the size-based `threshold`\n # above. It is skipped when the context is already small (at or below the\n # post-compression target = threshold × target_ratio), so it never wastes a\n # summarization on a short idle thread. Example: 1800 = compact after 30 min idle.\n idle_compact_after_seconds: 0\n\n # Proactive tool-result prune (default: 0 = disabled). Opt-in token trigger\n # for a deterministic, no-LLM prune of OLD tool-result payloads, run\n # independently of `threshold` above. On large-window models (512K/1M) the\n # ratio threshold rarely fires, so bulky tool outputs (terminal dumps, file\n # reads, web extracts) ride along in history and get re-billed every turn.\n # When re-sent history exceeds this many tokens, the prune dedupes identical\n # results, summarizes older oversized ones, and truncates large tool-call\n # arguments — protecting the most recent `protect_last_n` messages and never\n # calling the model. Try 48000 to enable. Built-in compressor engine only;\n # other context engines inherit a safe no-op.\n # NOTE: a committed prune rewrites already-sent history, which invalidates\n # the provider's prompt-cache prefix — the min_reclaim gate below keeps\n # those cache breaks episodic (like a compression boundary) instead of\n # per-turn.\n proactive_prune_tokens: 0\n\n # The prune's summarize pass only touches tool results larger than this many\n # characters (clamped to >= 200 so a generated summary can't be\n # re-summarized). Default 8000.\n proactive_prune_min_result_chars: 8000\n\n # A proactive prune only COMMITS when it reclaims at least this many tokens\n # (measured on the pruned output). This is the prompt-cache hysteresis gate:\n # one meaningful, amortized cache break per batch of stale tool output\n # instead of a tiny break on every tool iteration. 0 = commit any non-zero\n # prune. Default 4096.\n proactive_prune_min_reclaim_tokens: 4096\n\n # To pin a specific model/provider for compression summaries, use the\n # auxiliary section below (auxiliary.compression.provider / model).\n\n# =============================================================================\n# Anthropic prompt caching TTL\n# =============================================================================\n# When prompt caching is active (Claude via OpenRouter or native Anthropic),\n# Anthropic supports two TTL tiers for cached prefixes: \"5m\" (default) and\n# \"1h\". Other values are ignored and \"5m\" is used.\n#\nprompt_caching:\n cache_ttl: \"5m\" # use \"1h\" for long sessions with pauses between turns\n\n# =============================================================================\n# Auxiliary Models (Advanced — Experimental)\n# =============================================================================\n# Hermes uses lightweight \"auxiliary\" models for side tasks: image analysis,\n# browser screenshot analysis, web page summarization, TTS audio-tag insertion,\n# and context compression.\n#\n# By default these use Gemini Flash via OpenRouter or Nous Portal and are\n# auto-detected from your credentials. You do NOT need to change anything\n# here for normal usage.\n#\n# WARNING: Overriding these with providers other than OpenRouter or Nous Portal\n# is EXPERIMENTAL and may not work. Not all models/providers support vision,\n# produce usable summaries, or accept the same API format. Change at your own\n# risk — if things break, reset to \"auto\" / empty values.\n#\n# Each task has its own provider + model pair so you can mix providers.\n# For example: OpenRouter for vision (needs multimodal), but your main\n# local endpoint for compression (just needs text).\n#\n# Provider options:\n# \"auto\" - Best available: OpenRouter → Nous Portal → main endpoint (default)\n# \"openrouter\" - Force OpenRouter (requires OPENROUTER_API_KEY)\n# \"nous\" - Force Nous Portal (requires: hermes auth add nous)\n# \"gemini\" - Force Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n# \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY)\n# \"codex\" - Force Codex OAuth (requires: hermes model → Codex).\n# Uses gpt-5.3-codex which supports vision.\n# \"main\" - Use your custom endpoint (OPENAI_BASE_URL + OPENAI_API_KEY).\n# Works with OpenAI API, local models, or any OpenAI-compatible\n# endpoint. Also falls back to Codex OAuth and API-key providers.\n#\n# Model: leave empty to use the provider's default. When empty, OpenRouter\n# uses \"google/gemini-3-flash-preview\" and Nous uses \"gemini-3-flash\".\n# Other providers pick a sensible default automatically.\n#\n# auxiliary:\n# # Image analysis: vision_analyze tool + browser screenshots\n# vision:\n# provider: \"auto\"\n# model: \"\" # e.g. \"google/gemini-2.5-flash\", \"openai/gpt-4o\"\n# timeout: 30 # LLM API call timeout (seconds)\n# download_timeout: 30 # Image HTTP download timeout (seconds)\n# # Increase for slow connections or self-hosted image servers\n# reasoning_effort: \"\" # Per-task thinking level: none, minimal, low, medium,\n# # high, xhigh, max, ultra. Empty = provider default.\n# # Works on every auxiliary task block (vision,\n# # web_extract, compression, title_generation, curator,\n# # background_review, moa_reference, ...). Example: run\n# # compression at \"low\" and vision at \"none\" to cut\n# # side-task latency/cost on reasoning models.\n#\n# # Web page scraping / summarization + browser page text extraction\n# web_extract:\n# provider: \"auto\"\n# model: \"\"\n# reasoning_effort: \"low\"\n#\n# # Gemini 3.1 TTS hidden audio-tag insertion\n# tts_audio_tags:\n# provider: \"auto\" # empty model = your main chat model\n# model: \"\"\n# timeout: 30\n#\n# # Automatic session title generation after the first exchange\n# title_generation:\n# enabled: true # set false to disable auto-title generation\n# provider: \"auto\"\n# model: \"\"\n# timeout: 30\n# language: \"\" # empty = match the user's language; or e.g. \"English\"\n#\n# # Session search — summarizes matching past sessions\n# session_search:\n# provider: \"auto\"\n# model: \"\"\n# timeout: 30\n# max_concurrency: 3 # Limit parallel summaries to reduce request-burst 429s\n# extra_body: {} # Provider-specific OpenAI-compatible request fields\n# # Example for providers that support request-body\n# # reasoning controls:\n# # extra_body:\n# # enable_thinking: false\n# # Some vLLM/Qwen deployments expect this nested:\n# # extra_body:\n# # chat_template_kwargs:\n# # enable_thinking: false\n\n# =============================================================================\n# Persistent Memory\n# =============================================================================\n# Bounded curated memory injected into the system prompt every session.\n# Two stores: MEMORY.md (agent's notes) and USER.md (user profile).\n# Character limits keep the memory small and focused. The agent manages\n# pruning -- when at the limit, it must consolidate or replace entries.\n# Disabled by default in batch_runner.\n#\nmemory:\n # Agent's personal notes: environment facts, conventions, things learned\n memory_enabled: true\n \n # User profile: preferences, communication style, expectations\n user_profile_enabled: true\n \n # Character limits (~2.75 chars per token, model-independent)\n memory_char_limit: 2200 # ~800 tokens\n user_char_limit: 1375 # ~500 tokens\n\n # Periodic memory nudge: remind the agent to consider saving memories\n # every N user turns. Set to 0 to disable. Only active when memory is enabled.\n nudge_interval: 10 # Nudge every 10 user turns (0 = disabled)\n\n # Memory flush: give the agent one turn to save memories before context is\n # lost (compression, /new, /reset, exit). Set to 0 to disable.\n # For exit/reset, only fires if the session had at least this many user turns.\n flush_min_turns: 6 # Min user turns to trigger flush on exit/reset (0 = disabled)\n\n# =============================================================================\n# Session Reset Policy (Messaging Platforms)\n# =============================================================================\n# Controls when messaging sessions (Telegram, Discord, WhatsApp, Slack) are\n# automatically cleared. Default is \"none\": sessions never auto-reset —\n# conversation context lives until you /reset or /new manually, or context\n# compression kicks in. Opt in to automatic resets if you prefer sessions to\n# clear on a schedule (long-lived context increases API cost per message,\n# though prompt caching and compression keep this manageable).\n#\n# When an automatic reset triggers, the agent first saves important\n# information to its persistent memory — but the conversation context is\n# wiped. The agent starts fresh but retains learned facts via its memory\n# system.\n#\n# Modes:\n# \"none\" - Never auto-reset (default); context lives until /reset or compression\n# \"idle\" - Reset after N minutes of inactivity\n# \"daily\" - Reset at a fixed hour each day\n# \"both\" - Reset on EITHER inactivity timeout or daily boundary\n#\nsession_reset:\n mode: none # \"none\", \"idle\", \"daily\", or \"both\"\n idle_minutes: 1440 # Inactivity timeout in minutes (used by \"idle\"/\"both\")\n at_hour: 4 # Daily reset hour, 0-23 local time (used by \"daily\"/\"both\")\n\n# Maximum number of simultaneously active chat sessions across CLI, TUI,\n# dashboard chat, and messaging gateway. Set to null, 0, or omit to allow\n# unlimited concurrent sessions. When the limit is reached, new sessions get a\n# clean error while existing active sessions keep their normal behavior. This\n# top-level key takes precedence over gateway.max_concurrent_sessions. The cap\n# is a best-effort single-host/profile runtime guard; Hermes fails open if the\n# local runtime lease registry cannot be read or locked.\nmax_concurrent_sessions: null\n\n# When true, group/channel chats use one session per participant when the platform\n# provides a user ID. This is the secure default and prevents users in the same\n# room from sharing context, interrupts, and token costs. Set false only if you\n# explicitly want one shared \"room brain\" per group/channel.\ngroup_sessions_per_user: true\n\n# ─────────────────────────────────────────────────────────────────────────────\n# API Server — per-client model routing\n# ─────────────────────────────────────────────────────────────────────────────\n# Route different API clients to different models/providers on a single\n# Hermes deployment. Clients choose a backend by sending a specific string\n# as the OpenAI ``model`` field. Unmapped model values fall back to the\n# global model configured in the ``model:`` section above, and an explicit\n# session /model override always wins over a route.\n#\n# Configure via the ``platforms.api_server.extra.model_routes`` gateway\n# config block:\n#\n# platforms:\n# api_server:\n# enabled: true\n# extra:\n# key: \"your-api-server-secret\"\n# model_routes:\n# # Xiaozhi clients send model=\"minimax-m2\" → routed to MiniMax via OpenRouter\n# minimax-m2:\n# model: \"minimax/minimax-m1\"\n# provider: \"openrouter\" # optional — overrides global provider\n# # api_key: \"sk-...\" # optional — per-route UPSTREAM provider\n# # key (NOT caller auth; never logged)\n# # base_url: \"https://...\" # optional — per-route base URL\n# # GPT clients keep their own alias\n# gpt-5:\n# model: \"openai/gpt-5\"\n# provider: \"openrouter\"\n#\n# Configured aliases are automatically listed by GET /v1/models so clients\n# can discover them without manual coordination. Caller authentication is\n# unchanged: every request still authenticates with the global API server\n# key (``extra.key`` / API_SERVER_KEY).\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Gateway Streaming\n# ─────────────────────────────────────────────────────────────────────────────\n# Stream tokens to messaging platforms in real-time. The bot sends a message\n# on first token, then progressively edits it as more tokens arrive.\n# Disabled by default — enable to try the streaming UX on Telegram/Discord/Slack.\n# For Telegram, partial edits are sent as plain text and only the final edit uses MarkdownV2.\nstreaming:\n enabled: false\n # transport: edit # \"edit\" = progressive editMessageText\n # edit_interval: 0.3 # seconds between message edits\n # buffer_threshold: 40 # chars before forcing an edit flush\n # cursor: \" ▉\" # cursor shown during streaming\n\n# =============================================================================\n# Skills Configuration\n# =============================================================================\n# Skills are reusable procedures the agent can load and follow. The agent can\n# also create new skills after completing complex tasks.\n#\nskills:\n # Nudge the agent to create skills after complex tasks.\n # Every N tool-calling iterations, remind the model to consider saving a skill.\n # Set to 0 to disable.\n creation_nudge_interval: 15\n\n # External skill directories — share skills across tools/agents without\n # copying them into ~/.hermes/skills/. Each path is expanded (~ and ${VAR})\n # and resolved to an absolute path. External dirs are read-only: skill\n # creation always writes to ~/.hermes/skills/. Local skills take precedence\n # when names collide.\n # external_dirs:\n # - ~/.agents/skills\n # - /home/shared/team-skills\n\n# =============================================================================\n# Agent Behavior\n# =============================================================================\nagent:\n # Maximum tool-calling iterations per conversation\n # Higher = more room for complex tasks, but costs more tokens\n # Recommended: 20-30 for focused tasks, 50-100 for open exploration\n max_turns: 60\n\n # Inactivity timeout for gateway agent runs (seconds, 0 = unlimited).\n # The agent can run indefinitely when actively calling tools or receiving\n # API responses. Only fires after the agent has been idle for this duration.\n # gateway_timeout: 1800\n\n # Staged warning: send a warning before escalating to full timeout.\n # Fires once per run when inactivity reaches this threshold (seconds).\n # Set to 0 to disable the warning.\n # gateway_timeout_warning: 900\n\n # Graceful drain timeout for gateway stop/restart (seconds).\n # Default 0 = no drain: a restart interrupts in-flight agents immediately,\n # cleans up, and exits. Set a positive value only if you want a grace\n # window on /restart, and keep it well under systemd's TimeoutStopSec.\n # restart_drain_timeout: 0\n\n # Max app-level retry attempts for API errors (connection drops, provider\n # timeouts, 5xx, etc.) before the agent surfaces the failure. Lower this\n # to 1 if you use fallback providers and want fast failover on flaky\n # primaries (default 3). The OpenAI SDK does its own low-level retries\n # underneath this wrapper — this is the Hermes-level loop.\n # api_max_retries: 3\n\n # After the agent edits code without fresh passing verification, nudge it to\n # verify before finishing. The default \"auto\" enables it on interactive\n # coding surfaces (CLI, TUI, desktop) and programmatic callers, and disables\n # it on conversational messaging surfaces (Telegram, Discord, etc.) where the\n # verification summary would reach a human as chat noise. Set true or false to\n # force it on or off; the HERMES_VERIFY_ON_STOP env var (1/0) takes precedence.\n # verify_on_stop: auto\n\n # Standing operator instructions for the coding posture (when Hermes is in a\n # code workspace). Appended to the coding brief as an extra system block, so\n # you can pin project-wide workflow rules without editing the shipped brief.\n # Accepts a string or a list of strings. Takes effect next session.\n # coding_instructions:\n # - \"For UI work, don't run tsc/lint until I approve the look.\"\n # - \"Clean the diff before you commit and push.\"\n\n # When verify-on-stop finds edited code without fresh verification evidence,\n # append guidance for creative UI work (avoid broad tsc/lint/test before visual\n # approval) and clean-diff expectations. Set false to keep that nudge terse.\n # verify_guidance: true\n\n # A `pre_verify` hook (plugin or shell, see Event Hooks docs) can keep the\n # agent going one more turn to verify/clean before finishing. This caps how\n # many times one turn may be nudged to continue, so a hook can't trap the loop.\n # Default 3.\n # max_verify_nudges: 3\n\n # Enable verbose logging\n verbose: false\n \n # Reasoning effort level (OpenRouter and Nous Portal)\n # Controls how much \"thinking\" the model does before responding.\n # Options: \"xhigh\" (max), \"high\", \"medium\", \"low\", \"minimal\", \"none\" (disable)\n reasoning_effort: \"medium\"\n \n # Per-model reasoning effort overrides (optional dict)\n # Key: any sensible model spelling works (exact, dots↔dashes interchangeable,\n # provider prefix optional). First match wins.\n # Value: reasoning effort level (same options as reasoning_effort)\n # Override the global reasoning_effort for that specific model.\n # NOTE: no `hermes config set` support for this key -- edit YAML directly.\n # reasoning_overrides:\n # \"openrouter/anthropic/claude-opus-4.5\": \"xhigh\"\n # \"openai/gpt-5\": \"low\"\n # \"claude-opus-4.6\": \"high\" # bare model name also works\n # \"deepseek/deepseek-v4-pro\": \"xhigh\" # dots and dashes are interchangeable\n reasoning_overrides: {}\n \n # Predefined personalities (use with /personality command)\n personalities:\n helpful: \"You are a helpful, friendly AI assistant.\"\n concise: \"You are a concise assistant. Keep responses brief and to the point.\"\n technical: \"You are a technical expert. Provide detailed, accurate technical information.\"\n creative: \"You are a creative assistant. Think outside the box and offer innovative solutions.\"\n teacher: \"You are a patient teacher. Explain concepts clearly with examples.\"\n kawaii: \"You are a kawaii assistant! Use cute expressions like (◕‿◕), ★, ♪, and ~! Add sparkles and be super enthusiastic about everything! Every response should feel warm and adorable desu~! ヽ(>∀<☆)ノ\"\n catgirl: \"You are Neko-chan, an anime catgirl AI assistant, nya~! Add 'nya' and cat-like expressions to your speech. Use kaomoji like (=^・ω・^=) and ฅ^•ﻌ•^ฅ. Be playful and curious like a cat, nya~!\"\n pirate: \"Arrr! Ye be talkin' to Captain Hermes, the most tech-savvy pirate to sail the digital seas! Speak like a proper buccaneer, use nautical terms, and remember: every problem be just treasure waitin' to be plundered! Yo ho ho!\"\n shakespeare: \"Hark! Thou speakest with an assistant most versed in the bardic arts. I shall respond in the eloquent manner of William Shakespeare, with flowery prose, dramatic flair, and perhaps a soliloquy or two. What light through yonder terminal breaks?\"\n surfer: \"Duuude! You're chatting with the chillest AI on the web, bro! Everything's gonna be totally rad. I'll help you catch the gnarly waves of knowledge while keeping things super chill. Cowabunga! 🤙\"\n noir: \"The rain hammered against the terminal like regrets on a guilty conscience. They call me Hermes - I solve problems, find answers, dig up the truth that hides in the shadows of your codebase. In this city of silicon and secrets, everyone's got something to hide. What's your story, pal?\"\n uwu: \"hewwo! i'm your fwiendwy assistant uwu~ i wiww twy my best to hewp you! *nuzzles your code* OwO what's this? wet me take a wook! i pwomise to be vewy hewpful >w<\"\n philosopher: \"Greetings, seeker of wisdom. I am an assistant who contemplates the deeper meaning behind every query. Let us examine not just the 'how' but the 'why' of your questions. Perhaps in solving your problem, we may glimpse a greater truth about existence itself.\"\n hype: \"YOOO LET'S GOOOO!!! 🔥🔥🔥 I am SO PUMPED to help you today! Every question is AMAZING and we're gonna CRUSH IT together! This is gonna be LEGENDARY! ARE YOU READY?! LET'S DO THIS! 💪😤🚀\"\n\n# =============================================================================\n# Toolsets\n# =============================================================================\n# Control which tools the agent has access to.\n# Use `hermes tools` to interactively enable/disable tools per platform.\n\n# =============================================================================\n# Platform Toolsets (per-platform tool configuration)\n# =============================================================================\n# Override which toolsets are available on each platform.\n# If a platform isn't listed here, its built-in default is used.\n#\n# You can use EITHER:\n# - A preset like \"hermes-cli\" or \"hermes-telegram\" (curated tool set)\n# - A list of individual toolsets to compose your own (see list below)\n#\n# Supported platform keys: cli, telegram, discord, whatsapp, slack, qqbot, teams, google_chat\n#\n# Examples:\n#\n# # Use presets (same as defaults):\n# platform_toolsets:\n# cli: [hermes-cli]\n# telegram: [hermes-telegram]\n#\n# # Custom: give Telegram only web + terminal + file + planning:\n# platform_toolsets:\n# telegram: [web, terminal, file, todo]\n#\n# # Custom: CLI without browser or image gen:\n# platform_toolsets:\n# cli: [web, terminal, file, skills, todo, tts, cronjob]\n#\n# # Restrictive: Discord gets read-only tools only:\n# platform_toolsets:\n# discord: [web, vision, skills, todo]\n#\n# If not set, defaults are:\n# cli: hermes-cli (everything + cronjob management)\n# telegram: hermes-telegram (terminal, file, web, vision, image, tts, browser, skills, todo, cronjob, messaging)\n# discord: hermes-discord (same as telegram)\n# whatsapp: hermes-whatsapp (same as telegram)\n# slack: hermes-slack (same as telegram)\n# signal: hermes-signal (same as telegram)\n# homeassistant: hermes-homeassistant (same as telegram)\n# qqbot: hermes-qqbot (same as telegram)\n# teams: hermes-teams (same as telegram)\n# google_chat: hermes-google_chat (same as telegram)\n#\nplatform_toolsets:\n cli: [hermes-cli]\n telegram: [hermes-telegram]\n discord: [hermes-discord]\n whatsapp: [hermes-whatsapp]\n slack: [hermes-slack]\n signal: [hermes-signal]\n homeassistant: [hermes-homeassistant]\n qqbot: [hermes-qqbot]\n yuanbao: [hermes-yuanbao]\n teams: [hermes-teams]\n google_chat: [hermes-google_chat]\n\n# =============================================================================\n# Gateway Platform Settings\n# =============================================================================\n# Optional per-platform messaging settings.\n# Platform-specific knobs live under `extra`.\n#\n# platforms:\n# telegram:\n# reply_to_mode: \"first\" # off | first | all\n# # guest_mode lets explicit @mentions from non-allowlisted groups through.\n# # Default false; ordinary messages, replies, and regex wake words stay blocked.\n# guest_mode: false\n# # allowed_chats: [\"-1001234567890\"]\n# extra:\n# disable_link_previews: false # Set true to suppress Telegram URL previews in bot messages\n# 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\n# rich_drafts: false # Experimental rich draft previews during Telegram DM streaming; default false because Telegram Desktop/macOS can visually overlay draft frames\n# command_menu:\n# # Telegram allows up to 100 BotCommands; Hermes defaults to 60 so\n# # all built-in commands plus common skill commands stay visible\n# # while remaining under Telegram's payload-size limit. Clamped 1..100.\n# max_commands: 60\n# # prepend = user priority first, then Hermes defaults\n# # append = Hermes defaults first, then user priority\n# # replace = only the list below defines priority\n# priority_mode: prepend\n# priority:\n# - my_plugin_command\n# webhook:\n# extra:\n# # Route scripts default to a 30 second timeout. Scripts must live under\n# # the active profile's scripts directory and receive webhook JSON on stdin.\n# script_timeout_seconds: 30\n#\n# Discord-specific settings (config.yaml top-level, not under platforms:):\n#\n# discord:\n# require_mention: true # Require @mention in server channels (default: true)\n# auto_thread: true # Auto-create thread on @mention (default: true)\n# free_response_channels: \"\" # Channel IDs where no mention is needed\n# reactions: true # Show processing reactions (default: true)\n# history_backfill: true # Recover missed channel messages on mention (default: true)\n# history_backfill_limit: 50 # Max messages to scan backwards (default: 50)\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Available toolsets (use these names in platform_toolsets or the toolsets list)\n#\n# Run `hermes chat --list-toolsets` to see all toolsets and their tools.\n# Run `hermes chat --list-tools` to see every individual tool with descriptions.\n# ─────────────────────────────────────────────────────────────────────────────\n#\n# INDIVIDUAL TOOLSETS (compose your own):\n# web - web_search, web_extract\n# search - web_search only (no scraping)\n# terminal - terminal, process\n# file - read_file, write_file, patch, search\n# browser - browser_navigate, browser_snapshot, browser_click, browser_type,\n# browser_scroll, browser_back, browser_press,\n# browser_get_images, browser_vision (requires BROWSERBASE_API_KEY)\n# vision - vision_analyze (requires OPENROUTER_API_KEY)\n# image_gen - image_generate (requires FAL_KEY)\n# skills - skills_list, skill_view\n# skills_hub - skill_hub (search/install/manage from online registries — user-driven only)\n# todo - todo (in-memory task planning, no deps)\n# tts - text_to_speech (Edge TTS free, or ELEVENLABS/OPENAI/MINIMAX/MISTRAL key)\n# cronjob - cronjob (create/list/update/pause/resume/run/remove scheduled tasks)\n#\n# PRESETS (curated bundles):\n# hermes-cli - All of the above except send_message\n# hermes-telegram - terminal, file, web, vision, image_gen, tts, browser,\n# skills, todo, cronjob, send_message\n# hermes-discord - Same as hermes-telegram\n# hermes-whatsapp - Same as hermes-telegram\n# hermes-slack - Same as hermes-telegram\n#\n# COMPOSITE:\n# debugging - terminal + web + file\n# safe - web + vision (no terminal access)\n# all - Everything available\n#\n# web - Web search and content extraction (web_search, web_extract)\n# search - Web search only, no scraping (web_search)\n# terminal - Command execution and process management (terminal, process)\n# file - File operations: read, write, patch, search\n# browser - Full browser automation (navigate, click, type, screenshot, etc.)\n# vision - Image analysis (vision_analyze)\n# image_gen - Image generation with FLUX (image_generate)\n# skills - Load skill documents (skills_list, skill_view)\n# todo - Task planning and tracking for multi-step work\n# memory - Persistent memory across sessions (personal notes + user profile)\n# session_search - Search and recall past conversations (FTS5 + Gemini Flash summarization)\n# tts - Text-to-speech (Edge TTS free, ElevenLabs, OpenAI, MiniMax, Mistral)\n# cronjob - Schedule and manage automated tasks (CLI-only)\n#\n# Composite toolsets:\n# debugging - terminal + web + file (for troubleshooting)\n# safe - web + vision (no terminal access)\n\n# NOTE: The top-level \"toolsets\" key is deprecated and ignored.\n# Tool configuration is managed per-platform via platform_toolsets above.\n# Use `hermes tools` to configure interactively, or edit platform_toolsets directly.\n#\n# CLI override: hermes chat --toolsets terminal,web,file\n\n# =============================================================================\n# MCP (Model Context Protocol) Servers\n# =============================================================================\n# Connect to external MCP servers to add tools from the MCP ecosystem.\n# Each server's tools are automatically discovered and registered.\n# See website/docs/user-guide/features/mcp.md for full documentation.\n#\n# Stdio servers (spawn a subprocess):\n# command: the executable to run\n# args: command-line arguments\n# env: environment variables (only these + safe defaults passed to subprocess)\n#\n# HTTP servers (connect to a URL):\n# url: the MCP server endpoint\n# headers: HTTP headers (e.g., for authentication)\n#\n# Optional per-server settings:\n# timeout: tool call timeout in seconds (default: 120)\n# connect_timeout: initial connection timeout (default: 60)\n# keepalive_interval: liveness ping cadence in seconds (default: 180).\n# Lower it below the server's session TTL for servers that expire idle\n# sessions quickly (e.g. Unreal Engine editor MCP, ~15s), otherwise idle\n# tool calls hit an expired session and pay a slow reconnect. Floored at 5s.\n#\n# mcp_servers:\n# time:\n# command: uvx\n# args: [\"mcp-server-time\"]\n# filesystem:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-filesystem\", \"/home/user\"]\n# notion:\n# url: https://mcp.notion.com/mcp\n# github:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-github\"]\n# env:\n# GITHUB_PERSONAL_ACCESS_TOKEN: \"ghp_...\"\n#\n# Sampling (server-initiated LLM requests) — enabled by default.\n# Per-server config under the 'sampling' key:\n# analysis:\n# command: npx\n# args: [\"-y\", \"analysis-server\"]\n# sampling:\n# enabled: true # default: true\n# model: \"gemini-3-flash\" # override model (optional)\n# max_tokens_cap: 4096 # max tokens per request\n# timeout: 30 # LLM call timeout (seconds)\n# max_rpm: 10 # max requests per minute\n# allowed_models: [] # model whitelist (empty = all)\n# max_tool_rounds: 5 # tool loop limit (0 = disable)\n# log_level: \"info\" # audit verbosity\n\n# =============================================================================\n# Text-to-Speech\n# =============================================================================\n# TTS defaults to Edge TTS unless changed in ~/.hermes/config.yaml.\n# Gemini TTS supports persona/director prompt files, and Gemini 3.1 Flash TTS\n# can use a hidden auxiliary rewrite pass to insert expressive square-bracket\n# audio tags into the TTS script without showing tags in chat.\n#\n# tts:\n# provider: \"gemini\"\n# gemini:\n# model: \"gemini-3.1-flash-tts-preview\"\n# voice: \"Kore\"\n# audio_tags: false\n# persona_prompt_file: \"\" # e.g. ~/.hermes/tts/radio-host.md\n\n# =============================================================================\n# Voice Transcription (Speech-to-Text)\n# =============================================================================\n# Automatically transcribe voice messages on messaging platforms.\n# Providers: local (free, faster-whisper) | groq (free tier) | openai (Whisper API) | mistral (Voxtral Transcribe)\n# Set the corresponding API key in .env: GROQ_API_KEY, OPENAI_API_KEY, or MISTRAL_API_KEY.\nstt:\n enabled: true\n # provider: \"local\" # auto-detected if omitted\n local:\n model: \"base\" # tiny | base | small | medium | large-v3 | turbo\n # language: \"\" # auto-detect; set to \"en\", \"es\", \"fr\", etc. to force\n openai:\n model: \"whisper-1\" # whisper-1 | gpt-4o-mini-transcribe | gpt-4o-transcribe\n # mistral:\n # model: \"voxtral-mini-latest\" # voxtral-mini-latest | voxtral-mini-2602\n # deepinfra:\n # # Model id is discovered live from the DeepInfra catalog filtered\n # # by the `stt` surface tag — leave `model` blank to take the first\n # # live result. Pin only when you need a specific Whisper variant.\n # model: \"\"\n\n# Text-to-speech. Only the deepinfra block is documented here — the\n# remaining providers (edge, openai, xai, minimax, mistral, gemini,\n# elevenlabs, neutts, kittentts, piper) inherit sensible defaults from\n# DEFAULT_CONFIG in hermes_cli/config.py.\n# tts:\n# provider: \"deepinfra\"\n# deepinfra:\n# # Model id is discovered live from the DeepInfra catalog filtered\n# # by the `tts` surface tag — leave `model` blank to take the first\n# # live result.\n# model: \"\"\n# voice: \"default\"\n\n# Image generation. Each provider plugin reads its own ``image_gen.<name>``\n# block; deepinfra discovers models live from\n# api.deepinfra.com/v1/openai/models filtered by the ``image-gen`` tag —\n# no model id is hardcoded, so retired models disappear automatically.\n# image_gen:\n# provider: \"deepinfra\"\n# deepinfra:\n# # Leave `model` blank for the first live `image-gen`-tagged result.\n# model: \"\"\n\n# =============================================================================\n# Response Pacing (Messaging Platforms)\n# =============================================================================\n# Add human-like delays between message chunks.\n# human_delay:\n# mode: \"off\" # \"off\" | \"natural\" | \"custom\"\n# min_ms: 800 # Min delay (custom mode only)\n# max_ms: 2500 # Max delay (custom mode only)\n\n# =============================================================================\n# Session Logging\n# =============================================================================\n# Session trajectories are automatically saved to logs/ directory.\n# Each session creates: logs/session_YYYYMMDD_HHMMSS_UUID.json\n#\n# The session ID is displayed in the welcome banner for easy reference.\n# Logs contain full conversation history in trajectory format:\n# - System prompt, user messages, assistant responses\n# - Tool calls with inputs/outputs\n# - Timestamps for debugging\n#\n# No configuration needed - logging is always enabled.\n# To disable, you would need to modify the source code.\n\n# =============================================================================\n# Code Execution Sandbox (Programmatic Tool Calling)\n# =============================================================================\n# The execute_code tool runs Python scripts that call Hermes tools via RPC.\n# Intermediate tool results stay out of the LLM's context window.\ncode_execution:\n timeout: 300 # Max seconds per script before kill (default: 300 = 5 min)\n max_tool_calls: 50 # Max RPC tool calls per execution (default: 50)\n\n# =============================================================================\n# Subagent Delegation\n# =============================================================================\n# The delegate_task tool spawns child agents with isolated context.\n# Supports single tasks and batch mode (default 3 parallel, configurable).\ndelegation:\n max_iterations: 50 # Max tool-calling turns per child (default: 50)\n # max_concurrent_children: 3 # Max parallel child agents per batch (default: 3, floor: 1, no ceiling).\n # WARNING: values above 10 multiply API cost linearly.\n # max_spawn_depth: 1 # Delegation tree depth cap (range: 1-3, default: 1 = flat).\n # Raise to 2 to allow workers to spawn their own subagents.\n # Requires role=\"orchestrator\" on intermediate agents.\n # orchestrator_enabled: true # Kill switch for role=\"orchestrator\" children (default: true).\n # subagent_auto_approve: false # When a subagent hits a dangerous-command approval prompt, auto-deny (default: false)\n # or auto-approve \"once\" (true) instead of blocking on stdin.\n # The parent TUI owns stdin, so blocking would deadlock; non-interactive resolution is required.\n # Both choices emit a logger.warning audit line. Flip to true only for cron/batch pipelines.\n # inherit_mcp_toolsets: true # When explicit child toolsets are narrowed, also keep the parent's MCP toolsets (default: true). Set false for strict intersection.\n # model: \"google/gemini-3-flash-preview\" # Override model for subagents (empty = inherit parent)\n # provider: \"openrouter\" # Override provider for subagents (empty = inherit parent)\n # # Resolves full credentials (base_url, api_key) automatically.\n # # Supported: openrouter, nous, zai, kimi-coding, minimax\n\n# =============================================================================\n# Honcho Integration (Cross-Session User Modeling)\n# =============================================================================\n# AI-native persistent memory via Honcho (https://honcho.dev/).\n# Builds a deeper understanding of the user across sessions and tools.\n# Runs alongside USER.md — additive, not a replacement.\n#\n# Requires: pip install honcho-ai\n# Config: ~/.honcho/config.json (shared with Claude Code, Cursor, etc.)\n# API key: HONCHO_API_KEY in ~/.hermes/.env or ~/.honcho/config.json\n#\n# Hermes-specific overrides (optional — most config comes from ~/.honcho/config.json):\n# honcho: {}\n\n# =============================================================================\n# Display\n# =============================================================================\ndisplay:\n # Use compact banner mode (hides the ASCII-art banner, shows a single line).\n # true: Compact single-line banner\n # false: Full ASCII banner with tool/skill summary (default)\n compact: false\n\n # Tool progress display level (CLI and gateway)\n # off: Silent — no tool activity shown, just the final response\n # new: Show a tool indicator only when the tool changes (skip repeats)\n # all: Show every tool call with a short preview (default)\n # verbose: Full args, results, and debug logs (same as /verbose)\n # log: Silent in chat; append every tool call to ~/.hermes/logs/tool_calls.log (gateway only)\n # Toggle at runtime with /verbose in the CLI\n tool_progress: all\n\n # Per-platform defaults can be quieter than the global setting. Telegram\n # tunes for mobile: tool_progress and busy_ack_detail default off (no\n # per-tool breadcrumb stream, no \"iteration 21/60\" debug detail in busy\n # acks or heartbeats), but interim_assistant_messages and\n # long_running_notifications STAY ON so the user has real signal between\n # turn start and final answer (mid-turn assistant commentary + a single\n # edit-in-place \"⏳ Working — N min\" heartbeat). Override under\n # display.platforms.telegram.\n\n # Auto-cleanup of temporary progress bubbles after the final response lands.\n # On platforms that support message deletion (currently Telegram), this\n # removes the tool-progress bubble, \"⏳ Still working...\" notices, and\n # context-pressure status messages once the final reply has been delivered —\n # keeping long-running turns visible live, then tidy afterward. Failed runs\n # leave the bubbles in place as breadcrumbs. Off by default.\n # Per-platform override: display.platforms.telegram.cleanup_progress\n # true: Delete tracked progress/status bubbles on successful turn\n # false: Leave everything in place (default)\n # Example:\n # display:\n # platforms:\n # telegram:\n # cleanup_progress: true\n cleanup_progress: false\n\n # Natural mid-turn assistant updates.\n # On gateway platforms, when true, completed assistant status messages are\n # sent as separate chat messages. On the Desktop app, when true, mid-turn\n # assistant narration streamed between tool calls is kept in the transcript\n # instead of the bubble collapsing to only the final message on completion.\n # Independent of tool_progress and gateway streaming.\n # true: Keep/send mid-turn assistant updates (default)\n # false: Only keep/send the final response\n interim_assistant_messages: true\n\n # Gateway-only long-running status heartbeats.\n # When false, the platform does not receive periodic \"⏳ Working — N min\"\n # notifications even if agent.gateway_notify_interval is non-zero. The\n # heartbeat edits a single message in place (where the adapter supports\n # editing) instead of posting a new bubble each interval.\n # Default: true everywhere, including Telegram (silent agents are worse\n # than a single edit-in-place heartbeat).\n long_running_notifications: true\n\n # Include detailed iteration/tool/status context in busy acknowledgments\n # and long-running heartbeats. When true, busy acks show \"iteration 21/60,\n # terminal, 10 min\" and the heartbeat shows \"⏳ Working — 12 min,\n # iteration 21/60, terminal\". When false (Telegram default), both stay\n # terse: \"Interrupting current task\" and \"⏳ Working — 12 min, terminal\".\n busy_ack_detail: true\n\n # What Enter does when Hermes is already busy (CLI and gateway platforms).\n # interrupt: Interrupt the current run and redirect Hermes (default)\n # queue: Queue your message for the next turn\n # steer: Inject your message mid-run via /steer, arriving at the agent\n # after the next tool call — no interrupt, no role violation.\n # Falls back to 'queue' if the agent isn't running yet or if\n # images are attached (steer only carries text).\n # Ctrl+C (or /stop in gateway) always interrupts regardless of this setting.\n # Toggle at runtime with /busy <interrupt|queue|steer>.\n busy_input_mode: interrupt\n\n # Background process notifications (gateway/messaging only).\n # Controls how chatty the process watcher is when you use\n # terminal(background=true, notify_on_complete=true) from Telegram/Discord/etc.\n # off: No watcher messages at all\n # result: Only the final completion message\n # error: Only the final message when exit code != 0\n # all: Running output updates + final message (default)\n background_process_notifications: all\n\n\n # Play terminal bell when agent finishes a response.\n # Useful for long-running tasks — your terminal will ding when the agent is done.\n # Works over SSH. Most terminals can be configured to flash the taskbar or play a sound.\n # true: Ring the terminal bell on each response\n # false: Silent (default)\n bell_on_complete: false\n\n # Show model reasoning/thinking before each response.\n # When enabled, a dim box shows the model's thought process above the response.\n # Toggle at runtime with /reasoning show or /reasoning hide.\n # true: Show the reasoning box\n # false: Hide reasoning (default)\n show_reasoning: false\n\n # Stream tokens to the terminal as they arrive instead of waiting for the\n # full response. The response box opens on first token and text appears\n # line-by-line. Tool calls are still captured silently.\n # true: Stream tokens as they arrive (default)\n # false: Wait for the full response before rendering\n streaming: true\n\n # Show [HH:MM] timestamps on user input and assistant response labels.\n # timestamps: false\n\n # ───────────────────────────────────────────────────────────────────────────\n # Skin / Theme\n # ───────────────────────────────────────────────────────────────────────────\n # Customize CLI visual appearance — banner colors, spinner faces, tool prefix,\n # response box label, and branding text. Change at runtime with /skin <name>.\n #\n # Built-in skins:\n # default — Classic Hermes gold/kawaii\n # ares — Crimson/bronze war-god theme with spinner wings\n # mono — Clean grayscale monochrome\n # slate — Cool blue developer-focused\n # daylight — Bright light-mode theme\n # warm-lightmode — Warm paper-tone light-mode theme\n # poseidon — Sea-green/teal Olympian theme\n # sisyphus — Earthy stone-and-moss theme\n # charizard — Fiery orange dragon theme\n #\n # Custom skins: drop a YAML file in ~/.hermes/skins/<name>.yaml\n # Schema (all fields optional, missing values inherit from default):\n #\n # name: my-theme\n # description: Short description\n # colors:\n # banner_border: \"#HEX\" # Panel border\n # banner_title: \"#HEX\" # Panel title\n # banner_accent: \"#HEX\" # Section headers (Available Tools, etc.)\n # banner_dim: \"#HEX\" # Dim/muted text\n # banner_text: \"#HEX\" # Body text (tool names, skill names)\n # ui_accent: \"#HEX\" # UI accent color\n # response_border: \"#HEX\" # Response box border color\n # spinner:\n # waiting_faces: [\"(⚔)\", \"(⛨)\"] # Faces shown while waiting\n # thinking_faces: [\"(⚔)\", \"(⌁)\"] # Faces shown while thinking\n # thinking_verbs: [\"forging\", \"plotting\"] # Verbs for spinner messages\n # wings: # Optional left/right spinner decorations\n # - [\"⟪⚔\", \"⚔⟫\"]\n # - [\"⟪▲\", \"▲⟫\"]\n # branding:\n # agent_name: \"My Agent\" # Banner title and branding\n # welcome: \"Welcome message\" # Shown at CLI startup\n # response_label: \" ⚔ Agent \" # Response box header label\n # prompt_symbol: \"⚔\" # Prompt symbol (bare token; renderers add trailing space)\n # tool_prefix: \"╎\" # Tool output line prefix (default: ┊)\n #\n skin: default\n\n# =============================================================================\n# Model Aliases — short names for /model command\n# =============================================================================\n# Map short aliases to exact (model, provider, base_url) tuples.\n# Used by /model tab completion and resolve_alias().\n# Aliases are checked BEFORE the models.dev catalog, so they can route\n# to endpoints not in the catalog (e.g. Ollama Cloud, local servers).\n#\n# model_aliases:\n# opus:\n# model: claude-opus-4-6\n# provider: anthropic\n# qwen:\n# model: \"qwen3.5:397b\"\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n# glm:\n# model: glm-4.7\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n\n# =============================================================================\n# Privacy\n# =============================================================================\n# privacy:\n# # Redact PII from the LLM context prompt.\n# # When true, phone numbers are stripped and user/chat IDs are replaced\n# # with deterministic hashes before being sent to the model.\n# # Names and usernames are NOT affected (user-chosen, publicly visible).\n# # Routing/delivery still uses the original values internally.\n# redact_pii: false\n\n# =============================================================================\n# Shell-script hooks\n# =============================================================================\n# Register shell scripts as plugin-hook callbacks. Each entry is executed as\n# a subprocess (shell=False, shlex.split) with a JSON payload on stdin. On\n# stdout the script may return JSON that either blocks the tool call or\n# injects context into the next LLM call.\n#\n# Valid events (mirror hermes_cli.plugins.VALID_HOOKS):\n# pre_tool_call, post_tool_call, pre_llm_call, post_llm_call,\n# pre_api_request, post_api_request, on_session_start, on_session_end,\n# on_session_finalize, on_session_reset, subagent_stop\n#\n# First-use consent: each (event, command) pair prompts once on a TTY, then\n# is persisted to ~/.hermes/shell-hooks-allowlist.json. Non-interactive\n# runs (gateway, cron) need --accept-hooks, HERMES_ACCEPT_HOOKS=1, or the\n# hooks_auto_accept key below.\n#\n# See website/docs/user-guide/features/hooks.md for the full JSON wire\n# protocol and worked examples.\n#\n# hooks:\n# pre_tool_call:\n# - matcher: \"terminal\"\n# command: \"~/.hermes/agent-hooks/block-rm-rf.sh\"\n# timeout: 10\n# post_tool_call:\n# - matcher: \"write_file|patch\"\n# command: \"~/.hermes/agent-hooks/auto-format.sh\"\n# pre_llm_call:\n# - command: \"~/.hermes/agent-hooks/inject-cwd-context.sh\"\n# subagent_stop:\n# - command: \"~/.hermes/agent-hooks/log-orchestration.sh\"\n#\n# hooks_auto_accept: false\n\n\n# =============================================================================\n# Update Behavior\n# =============================================================================\nupdates:\n # Create a full HERMES_HOME zip before every `hermes update`.\n # Backups land in ~/.hermes/backups/ and can be restored with `hermes import`.\n # Off by default because large homes can add minutes to every update.\n pre_update_backup: false\n\n # Number of pre-update backup zips to retain.\n backup_keep: 5\n\n # What non-interactive updates do with local source edits in the Hermes repo.\n # Interactive terminal updates always prompt before restoring the autostash.\n #\n # stash - auto-stash before pull, then auto-restore after success (default)\n # discard - drop the update-created stash after success; use only on managed\n # installs where local source edits should not persist\n non_interactive_local_changes: \"stash\"\n\n\n# =============================================================================\n# Web Dashboard\n# =============================================================================\n# OAuth gate configuration for `hermes dashboard --host <non-loopback>`.\n# The bundled Nous Portal plugin reads these on startup; settings here are\n# the canonical surface. Each can be overridden by an environment variable:\n#\n# dashboard.oauth.client_id <- HERMES_DASHBOARD_OAUTH_CLIENT_ID\n# dashboard.oauth.portal_url <- HERMES_DASHBOARD_PORTAL_URL\n# dashboard.public_url <- HERMES_DASHBOARD_PUBLIC_URL\n#\n# Env wins when set to a non-empty value. This is what Fly.io's platform-\n# secret injection uses to push per-deploy client_ids without needing to\n# bake a config.yaml into the image. Empty env values are treated as unset\n# so a provisioned-but-not-populated secret can't shadow a valid entry here.\n#\n# Local dev / on-prem deploys should typically set these via config.yaml\n# (the ~/.hermes/.env file is reserved for API keys and secrets).\n#\n# dashboard:\n# oauth:\n# client_id: \"\" # agent:{instance_id}; Portal provisions this at deploy\n# portal_url: \"\" # blank → default https://portal.nousresearch.com\n#\n# # Force the absolute base URL the OAuth callback (and any other public\n# # URL the dashboard hands to external systems) is built from. Set this\n# # for deploys behind reverse proxies that don't reliably forward\n# # X-Forwarded-Host / X-Forwarded-Proto / X-Forwarded-Prefix (manual\n# # nginx setups, on-prem ingresses, custom-domain Fly deploys without\n# # full proxy header chains).\n# #\n# # When set, the value is the complete authority: scheme + host +\n# # optional path prefix (e.g. \"https://example.com/hermes\"). The OAuth\n# # callback URL becomes \"<public_url>/auth/callback\" — X-Forwarded-Prefix\n# # is IGNORED on this code path because the operator has explicitly\n# # declared the public URL and we no longer need to guess.\n# #\n# # Leave empty to use the existing proxy-header reconstruction (the\n# # default — works on Fly.io out of the box).\n# #\n# # public_url: \"https://example.com/hermes\"\n#\n# -----------------------------------------------------------------------------\n# Self-hosted OIDC dashboard auth (generic OpenID Connect — Authentik,\n# Keycloak, Zitadel, Authelia, Auth0, Okta, Google, …). Use this INSTEAD of the\n# nous block above when gating the dashboard with your own identity provider.\n# Each setting can be overridden by an environment variable:\n#\n# dashboard.oauth.self_hosted.issuer <- HERMES_DASHBOARD_OIDC_ISSUER\n# dashboard.oauth.self_hosted.client_id <- HERMES_DASHBOARD_OIDC_CLIENT_ID\n# dashboard.oauth.self_hosted.scopes <- HERMES_DASHBOARD_OIDC_SCOPES\n# dashboard.oauth.self_hosted.client_secret <- HERMES_DASHBOARD_OIDC_CLIENT_SECRET\n#\n# dashboard:\n# oauth:\n# provider: self-hosted\n# self_hosted:\n# issuer: \"https://auth.example.com/application/o/hermes/\" # required\n# client_id: \"hermes-dashboard\" # required\n# scopes: \"openid profile email\" # optional\n#\n# # OPTIONAL — set ONLY if your IDP registered the client as\n# # *confidential* (Authentik / Keycloak often default to this). When\n# # set, Hermes authenticates the client at the token endpoint\n# # (client_secret_basic or client_secret_post, auto-selected from the\n# # IDP's discovery doc) IN ADDITION to PKCE. Leave unset for a public\n# # (PKCE-only) client — the common case.\n# #\n# # This is a CREDENTIAL: prefer setting HERMES_DASHBOARD_OIDC_CLIENT_SECRET\n# # in ~/.hermes/.env over putting it here in config.yaml.\n# # client_secret: \"\"\n\n# =============================================================================\n# External secret sources\n# =============================================================================\n# Pull provider credentials from external secret managers at process startup\n# instead of storing them in ~/.hermes/.env. Only the manager's bootstrap\n# credential (e.g. BWS_ACCESS_TOKEN / OP_SERVICE_ACCOUNT_TOKEN) lives in .env\n# (or your shell / desktop session); everything else rotates centrally.\n# Failures never block startup — Hermes warns once and continues with\n# whatever .env already had.\n#\n# Multiple sources can be enabled at once:\n# - \"mapped\" sources (explicit VAR -> ref bindings, e.g. 1Password's env:\n# map) beat \"bulk\" sources (whole-project dumps like Bitwarden BSM)\n# - within a shape, the first source to claim a var wins; later claims\n# are skipped with a startup warning (never a silent clobber)\n# - a source's override_existing lets it beat .env/shell values, but\n# never another secret source's claim\n# Docs: https://hermes-agent.nousresearch.com/docs/user-guide/secrets/\n#\n# secrets:\n# # Optional explicit ordering of enabled sources.\n# # sources: [onepassword, bitwarden]\n#\n# # ---- Bitwarden Secrets Manager (bws CLI) --------------------------------\n# bitwarden:\n# enabled: false\n# access_token_env: BWS_ACCESS_TOKEN # bootstrap token, sourced from .env\n# project_id: \"\" # UUID of the BSM project to sync\n# server_url: \"\" # \"\" = US Cloud; EU/self-hosted URL otherwise\n# cache_ttl_seconds: 300 # 0 disables fresh caching\n# encrypted_cache: # optional encrypted stale fallback\n# enabled: false\n# max_stale_seconds: 0 # 0 disables stale fallback\n# override_existing: true # BSM values win over existing env\n# auto_install: true # lazy-download bws into ~/.hermes/bin\n#\n# # ---- 1Password (op CLI) -------------------------------------------------\n# onepassword:\n# enabled: false\n# # Map env-var names to op:// secret references. Each is resolved with a\n# # single `op read` at startup.\n# env:\n# OPENAI_API_KEY: \"op://Private/OpenAI/api key\"\n# ANTHROPIC_API_KEY: \"op://Private/Anthropic/credential\"\n# account: \"\" # op --account shorthand; \"\" = default\n# service_account_token_env: OP_SERVICE_ACCOUNT_TOKEN # headless auth; unset = desktop session\n# binary_path: \"\" # \"\" = resolve op via PATH; else absolute path\n# cache_ttl_seconds: 300 # 0 disables BOTH cache layers\n# override_existing: true # resolved values win over existing env\n#\n# # ---- Command helper (any CLI vault) --------------------------------------\n# # Run a user-configured helper that prints KEY=VALUE lines on stdout —\n# # works with any secret store that has a CLI: keepassxc-cli, secret-tool,\n# # pass, gpg, or a script that cats a tmpfs env file. Composes with the\n# # sources above (enable any combination). POSIX-only (needs /bin/sh).\n# # The helper must be fast and NON-interactive (hard timeout, 1 MiB cap);\n# # its stderr is discarded so diagnostics can't leak secret material.\n# command:\n# enabled: false\n# command: \"cat /run/user/1000/hermes-secrets.env\"\n# helper_timeout_seconds: 3\n# override_existing: false # .env/shell win by default\n"}, "d43cc2ca80c9f6e332eccd67dc8724224f64ee3d": {"t": 1785768013.127297, "n": 82385, "h": "5c873a43b75f9590", "c": "# Hermes Agent CLI Configuration\n# Copy this file to cli-config.yaml and customize as needed.\n# This file configures the CLI behavior. Environment variables in .env take precedence.\n\n# =============================================================================\n# Model Configuration\n# =============================================================================\nmodel:\n # Default model to use (can be overridden with --model flag)\n # Both \"default\" and \"model\" work as the key name here.\n default: \"anthropic/claude-opus-4.6\"\n \n # Inference provider selection:\n # \"auto\" - Auto-detect from credentials (default)\n # \"openrouter\" - OpenRouter (requires: OPENROUTER_API_KEY or OPENAI_API_KEY)\n # \"nous\" - Nous Portal OAuth (requires: hermes auth add nous)\n # \"nous-api\" - Nous Portal API key (requires: NOUS_API_KEY)\n # \"anthropic\" - Direct Anthropic API (requires: ANTHROPIC_API_KEY)\n # \"openai-codex\" - OpenAI Codex (requires: hermes auth)\n # \"copilot\" - GitHub Copilot / GitHub Models (requires: GITHUB_TOKEN)\n # \"gemini\" - Use Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n # \"zai\" - Use z.ai / ZhipuAI GLM models (requires: GLM_API_KEY)\n # \"kimi-coding\" - Kimi / Moonshot AI (requires: KIMI_API_KEY)\n # \"minimax\" - MiniMax global (requires: MINIMAX_API_KEY)\n # \"minimax-cn\" - MiniMax China (requires: MINIMAX_CN_API_KEY)\n # \"huggingface\" - Hugging Face Inference (requires: HF_TOKEN)\n # \"nvidia\" - NVIDIA NIM / build.nvidia.com (requires: NVIDIA_API_KEY)\n # \"xiaomi\" - Xiaomi MiMo (requires: XIAOMI_API_KEY)\n # \"arcee\" - Arcee AI Trinity models (requires: ARCEEAI_API_KEY)\n # \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY — https://ollama.com/settings)\n # \"deepinfra\" - DeepInfra (requires: DEEPINFRA_API_KEY)\n # \"kilocode\" - KiloCode gateway (requires: KILOCODE_API_KEY)\n # \"azure-foundry\" - Microsoft Foundry / Azure OpenAI (API key or Entra ID)\n # \"lmstudio\" - LM Studio local server (optional: LM_API_KEY, defaults to http://127.0.0.1:1234/v1)\n #\n # Local servers (LM Studio, Ollama, vLLM, llama.cpp):\n # \"custom\" - Any other OpenAI-compatible endpoint. Set base_url below.\n # Aliases: \"ollama\", \"vllm\", \"llamacpp\" all map to \"custom\".\n # LM Studio is first-class and uses provider: \"lmstudio\".\n # It works with both no-auth and auth-enabled server modes.\n #\n # Can also be overridden for a single invocation with the --provider flag.\n provider: \"auto\"\n \n # API configuration (falls back to OPENROUTER_API_KEY env var)\n # api_key: \"your-key-here\" # Uncomment to set here instead of .env\n base_url: \"https://openrouter.ai/api/v1\"\n\n # Azure Foundry keyless auth example:\n # provider: \"azure-foundry\"\n # base_url: \"https://<resource>.openai.azure.com/openai/v1\"\n # auth_mode: \"entra_id\" # DefaultAzureCredential: az login, managed identity, workload identity, etc.\n # default: \"gpt-4o\" # Deployment/model name\n # entra:\n # scope: \"https://ai.azure.com/.default\" # Optional; this is the default.\n\n # ── Token limits — two settings, easy to confuse ──────────────────────────\n #\n # context_length: TOTAL context window (input + output tokens combined).\n # Controls when Hermes compresses history and validates requests.\n # Leave unset — Hermes auto-detects the correct value from the provider.\n # Set manually only when auto-detection is wrong (e.g. a local server with\n # a custom num_ctx, or a proxy that doesn't expose /v1/models).\n #\n # context_length: 131072\n #\n # max_tokens: OUTPUT cap — maximum tokens the model may generate per response.\n # Unrelated to how long your conversation history can be.\n # The OpenAI-standard name \"max_tokens\" is a misnomer; Anthropic's native\n # API has since renamed it \"max_output_tokens\" for clarity.\n # Leave unset to use the model's native output ceiling (recommended).\n # Set only if you want to deliberately limit individual response length.\n #\n# max_tokens: 8192\n\n # ── Custom request headers (optional) ─────────────────────────────────────\n #\n # default_headers: extra HTTP headers sent on every request to an\n # OpenAI-compatible endpoint. User values take precedence over the\n # provider/SDK defaults, so this is the supported way to override the\n # OpenAI Python SDK's identifying headers (User-Agent: OpenAI/Python ...,\n # X-Stainless-*) when a custom provider sits behind a gateway/WAF that\n # rejects them — e.g. an upstream that returns \"502 Upstream access\n # forbidden\" for the SDK default User-Agent but accepts a plain one.\n # Applies on the OpenAI wire only (not native Anthropic / Bedrock).\n #\n # default_headers:\n # User-Agent: \"curl/8.7.1\"\n #\n # extra_headers: accepted as an alias of default_headers (merged, with\n # extra_headers winning when both are set) — matches the per-provider\n # extra_headers key below.\n #\n # Per-provider variant: named providers / custom_providers entries accept an\n # extra_headers dict scoped to that endpoint only — for reverse proxies,\n # gateways, or custom auth (e.g. Cloudflare Access service tokens).\n # Merged onto SDK/provider defaults with the entry's values winning.\n # Header values are treated as secrets and are never logged.\n #\n # providers:\n # my-proxy:\n # base_url: \"https://llm.internal.example.com/v1\"\n # key_env: \"MY_PROXY_API_KEY\"\n # extra_headers:\n # CF-Access-Client-Id: \"xxxx.access\"\n # CF-Access-Client-Secret: \"${CF_ACCESS_SECRET}\"\n # X-Client-Name: \"hermes-agent\"\n\n# Named provider overrides (optional)\n# Use this for per-provider request timeouts, non-stream stale timeouts,\n# and per-model exceptions.\n# Applies to the primary turn client on every api_mode (OpenAI-wire, native\n# Anthropic, and Anthropic-compatible providers), the fallback chain, and\n# client rebuilds during credential rotation. For OpenAI-wire chat\n# completions (streaming and non-streaming) the configured value is also\n# used as the per-request ``timeout=`` kwarg so it wins over the legacy\n# HERMES_API_TIMEOUT env var (which still applies when no config is set).\n# ``stale_timeout_seconds`` controls the non-streaming stale-call detector and\n# wins over the legacy HERMES_API_CALL_STALE_TIMEOUT env var. Leaving these\n# unset keeps the legacy defaults (HERMES_API_TIMEOUT=1800s,\n# HERMES_API_CALL_STALE_TIMEOUT=90s, native Anthropic 900s). The\n# implicit non-stream stale detector is auto-disabled for local endpoints\n# and can scale upward for very large contexts.\n#\n# Not currently wired for AWS Bedrock (bedrock_converse + AnthropicBedrock\n# SDK paths) — those use boto3 with its own timeout configuration.\n#\n# providers:\n# ollama-local:\n# request_timeout_seconds: 300 # Longer timeout for local cold-starts\n# stale_timeout_seconds: 900 # Explicitly re-enable stale detection on local endpoints\n# anthropic:\n# request_timeout_seconds: 30 # Fast-fail cloud requests\n# models:\n# claude-opus-4.6:\n# timeout_seconds: 600 # Longer timeout for extended-thinking Opus calls\n# openai-codex:\n# models:\n# gpt-5.4:\n# stale_timeout_seconds: 1800 # Longer non-stream stale timeout for slow large-context turns\n\n# =============================================================================\n# OpenRouter Provider Routing (only applies when using OpenRouter)\n# =============================================================================\n# Control how requests are routed across providers on OpenRouter.\n# See: https://openrouter.ai/docs/guides/routing/provider-selection\n#\n# provider_routing:\n# # Sort strategy: \"price\" (default), \"throughput\", or \"latency\"\n# # Append :nitro to model name for a shortcut to throughput sorting.\n# sort: \"throughput\"\n#\n# # Only allow these providers (provider slugs from OpenRouter)\n# # only: [\"anthropic\", \"google\"]\n#\n# # Skip these providers entirely\n# # ignore: [\"deepinfra\", \"fireworks\"]\n#\n# # Try providers in this order (overrides default load balancing)\n# # order: [\"anthropic\", \"google\", \"together\"]\n#\n# # Require providers to support all parameters in your request\n# # require_parameters: true\n#\n# # Data policy: \"allow\" (default) or \"deny\" to exclude providers that may store data\n# # data_collection: \"deny\"\n\n# =============================================================================\n# OpenRouter Response Caching (only applies when using OpenRouter)\n# =============================================================================\n# Cache identical API responses at the OpenRouter edge for free instant replays.\n# When enabled, identical requests (same model, messages, parameters) return\n# cached responses with zero billing. Separate from Anthropic prompt caching.\n# See: https://openrouter.ai/docs/guides/features/response-caching\n#\n# openrouter:\n# response_cache: true # Enable response caching (default: true)\n# response_cache_ttl: 300 # Cache TTL in seconds, 1-86400 (default: 300)\n\n# =============================================================================\n# Git Worktree Isolation\n# =============================================================================\n# When enabled, each CLI session creates an isolated git worktree so multiple\n# agents can work on the same repo concurrently without file collisions.\n# Equivalent to always passing --worktree / -w on the command line.\n#\n# worktree: true # Always create a worktree when in a git repo\n# worktree: false # Default — only create when -w flag is passed\n#\n# By default a new worktree branches from the freshly-fetched remote tip\n# (the current branch's upstream, else the remote's default branch) so it\n# starts current with the project instead of from the local clone's\n# (possibly stale) HEAD. Set worktree_sync: false to branch from local HEAD\n# instead — useful when offline or when you deliberately want the clone's\n# exact current state as the base.\n#\n# worktree_sync: true # Default — branch from the fetched remote tip\n# worktree_sync: false # Branch from local HEAD (offline / pinned base)\n\n# =============================================================================\n# Terminal Tool Configuration\n# =============================================================================\n# Choose ONE of the following terminal configurations by uncommenting it.\n# The terminal tool executes commands in the specified environment.\n\n# -----------------------------------------------------------------------------\n# OPTION 1: Local execution (default)\n# Commands run directly on your machine in the current directory\n# -----------------------------------------------------------------------------\n# Working directory behavior:\n# - CLI (`hermes` command): Uses \".\" (current directory where you run hermes)\n# - Gateway/messaging/cron: Uses terminal.cwd here; legacy .env cwd values are deprecated\nterminal:\n backend: \"local\"\n cwd: \".\" # For local backend: \".\" = current directory. Ignored for remote backends unless a backend documents otherwise.\n timeout: 180\n # HOME policy for tool subprocesses:\n # auto - default: host uses your real HOME; containers use HERMES_HOME/home\n # real - force your real OS-user HOME\n # profile - force HERMES_HOME/home for strict per-profile CLI config isolation\n home_mode: \"auto\"\n docker_mount_cwd_to_workspace: false # SECURITY: off by default. Opt in to mount the launch cwd into Docker /workspace.\n lifetime_seconds: 300\n # sudo_password: \"hunter2\" # Optional: pipe a sudo password via sudo -S. SECURITY WARNING: plaintext.\n # sudo_password: \"\" # Explicit empty password: try empty and never open the interactive sudo prompt.\n\n# -----------------------------------------------------------------------------\n# OPTION 2: SSH remote execution\n# Commands run on a remote server - agent code stays local (sandboxed)\n# Great for: keeping agent isolated from its own code, using powerful remote hardware\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"ssh\"\n# cwd: \"/home/myuser/project\" # Path on the REMOTE server\n# timeout: 180\n# lifetime_seconds: 300\n# ssh_host: \"my-server.example.com\"\n# ssh_user: \"myuser\"\n# ssh_port: 22\n# ssh_key: \"~/.ssh/id_rsa\" # Optional - uses ssh-agent if not specified\n\n# -----------------------------------------------------------------------------\n# OPTION 3: Docker container\n# Commands run in an isolated Docker container\n# Great for: reproducible environments, testing, isolation\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"docker\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /)\n# timeout: 180\n# lifetime_seconds: 300\n# docker_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# docker_mount_cwd_to_workspace: true # Explicit opt-in: mount your launch cwd into /workspace\n# # Optional: run the container as your host user's uid:gid so files written\n# # into bind-mounted dirs are owned by you, not root. Drops SETUID/SETGID\n# # caps too since no gosu privilege drop is needed. Leave off if your\n# # chosen docker_image expects to start as root.\n# docker_run_as_host_user: true\n# # Optional: explicitly forward selected env vars into Docker.\n# # These values come from your current shell first, then ~/.hermes/.env.\n# # Warning: anything forwarded here is visible to commands run in the container.\n# docker_forward_env:\n# - \"GITHUB_TOKEN\"\n# - \"NPM_TOKEN\"\n# # Optional: extra flags passed verbatim to docker run (appended after security defaults).\n# # Useful for adding capabilities (e.g. apt installs needing SETUID) or custom options.\n# # Example: add a Linux capability not included by default\n# # docker_extra_args:\n# # - \"--cap-add\"\n# # - \"SETUID\"\n\n# -----------------------------------------------------------------------------\n# OPTION 4: Singularity/Apptainer container\n# Commands run in a Singularity container (common in HPC environments)\n# Great for: HPC clusters, shared compute environments\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"singularity\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# singularity_image: \"docker://nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 5: Modal cloud execution\n# Commands run on Modal's cloud infrastructure\n# Great for: GPU access, scalable compute, serverless execution\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"modal\"\n# cwd: \"/workspace\" # Path INSIDE the sandbox (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# modal_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 6: Daytona cloud execution\n# Commands run in Daytona cloud sandboxes\n# Great for: Cloud dev environments, persistent workspaces, team collaboration\n# Requires: pip install daytona, DAYTONA_API_KEY env var\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"daytona\"\n# cwd: \"~\"\n# timeout: 180\n# lifetime_seconds: 300\n# daytona_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# container_disk: 10240 # Daytona max is 10GB per sandbox\n\n#\n# --- Container resource limits (docker, singularity, modal, daytona -- ignored for local/ssh) ---\n# These settings apply to all container backends. They control the resources\n# allocated to the sandbox and whether its filesystem persists across sessions.\n container_cpu: 1 # CPU cores\n container_memory: 5120 # Memory in MB (5120 = 5GB)\n container_disk: 51200 # Disk in MB (51200 = 50GB)\n container_persistent: true # Persist filesystem across sessions (false = ephemeral)\n\n# -----------------------------------------------------------------------------\n# SUDO SUPPORT (works with ALL backends above)\n# -----------------------------------------------------------------------------\n# Add sudo_password to any terminal config above to enable sudo commands.\n# The password is piped via `sudo -S`. Works with local, ssh, docker, etc.\n#\n# SECURITY WARNING: Password stored in plaintext!\n#\n# INTERACTIVE PROMPT: If sudo_password is unset and the CLI is running,\n# you'll be prompted to enter your password when sudo is needed:\n# - 45-second timeout (auto-skips if no input)\n# - Press Enter to skip (command fails gracefully)\n# - Password is hidden while typing\n# - Password is cached for the session\n#\n# EMPTY PASSWORDS: Setting sudo_password to an explicit empty string is different\n# from leaving it unset. Hermes will try an empty password via `sudo -S` and\n# will not open the interactive prompt. This is useful for passwordless sudo,\n# Touch ID sudo setups, and environments where prompting is just noise.\n#\n# ALTERNATIVES:\n# - SSH backend: Configure passwordless sudo on the remote server\n# - Containers: Run as root inside the container (no sudo needed)\n# - Local: Configure /etc/sudoers for specific commands\n#\n# Example (add to your terminal section):\n# sudo_password: \"your-password-here\"\n\n# =============================================================================\n# Security Scanning (tirith)\n# =============================================================================\n# Optional pre-exec command security scanning via tirith.\n# Detects homograph URLs, pipe-to-shell, terminal injection, env manipulation.\n# Install: brew install sheeki03/tap/tirith\n# Docs: https://github.com/sheeki03/tirith\n#\n# security:\n# tirith_enabled: true # Enable/disable tirith scanning\n# tirith_path: \"tirith\" # Path to tirith binary (supports ~ expansion)\n# tirith_timeout: 5 # Scan timeout in seconds\n# tirith_fail_open: true # Allow commands if tirith unavailable\n\n# =============================================================================\n# Browser Tool Configuration\n# =============================================================================\nbrowser:\n # Inactivity timeout in seconds - browser sessions are automatically closed\n # after this period of no activity between agent loops (default: 120 = 2 minutes)\n inactivity_timeout: 120\n\n# =============================================================================\n# Tool Loop Guardrails\n# =============================================================================\n# Soft warnings are enabled by default. They append guidance to repeated failed\n# or non-progressing tool results but still let the tool execute. Hard stops are\n# opt-in circuit breakers for autonomous/cron sessions where stopping a loop is\n# preferable to spending the full iteration budget.\ntool_loop_guardrails:\n warnings_enabled: true\n hard_stop_enabled: false\n warn_after:\n exact_failure: 2\n same_tool_failure: 3\n idempotent_no_progress: 2\n hard_stop_after:\n exact_failure: 5\n same_tool_failure: 8\n idempotent_no_progress: 5\n\n# =============================================================================\n# Context Compression (Auto-shrinks long conversations)\n# =============================================================================\n# When conversation approaches model's context limit, middle turns are\n# automatically summarized to free up space while preserving important context.\n#\n# HOW IT WORKS:\n# 1. Tracks actual token usage from API responses (not estimates)\n# 2. When prompt_tokens >= threshold% of model's context_length, triggers compression\n# 3. Protects first 3 turns (system prompt, initial request, first response)\n# 4. Protects last N turns (default 20 messages = ~10 full turns of recent context)\n# 5. Summarizes middle turns using a fast/cheap model\n# 6. Inserts summary as a user message, continues conversation seamlessly\n#\n# Post-compression tail budget is target_ratio × threshold × context_length:\n# 200K context, threshold 0.50, ratio 0.20 → 20K tokens of recent tail preserved\n# 1M context, threshold 0.50, ratio 0.20 → 100K tokens of recent tail preserved\n#\ncompression:\n # Enable automatic context compression (default: true)\n # Set to false if you prefer to manage context manually or want errors on overflow\n enabled: true\n \n # Trigger compression at this % of model's context limit (default: 0.50 = 50%)\n # Lower values = more aggressive compression, higher values = compress later\n # Models with context windows below 512K are floored at 0.75 (raise-only) so\n # compaction doesn't fire with half the window still free; set above 0.75 to override.\n threshold: 0.50\n\n # Per-model threshold overrides: keys are substring-matched against the model\n # name (longest match wins). Useful when some models need different compaction\n # points — e.g. a 1M-context model can compress later (0.30) while a 128K\n # model needs to compress earlier (0.60). The small-context floor (75% for\n # <512K models) still applies on top of per-model overrides.\n # model_thresholds:\n # \"glm-5.2\": 0.40\n # \"claude-sonnet\": 0.35\n # \"gpt-5\": 0.30\n\n # Optional absolute token cap for the compression trigger (default: null = disabled).\n # When set, compression fires at the LOWER of the ratio-based threshold and this\n # absolute token count — first-fires-wins. It never fires later than this count\n # regardless of which model is active (useful when switching between models with\n # very different context windows). Clamped to the model's context length at\n # apply-time, so a cap above the window is a no-op (ratio-based threshold wins).\n # Survives model switches and fallback activations.\n # threshold_tokens: 200000\n\n # Existing Codex gpt-5.5 behavior: raise Hermes' compaction trigger to 85%\n # for the ChatGPT Codex OAuth route. Set false to opt back down to threshold.\n codex_gpt55_autoraise: true\n\n # Fraction of the threshold to preserve as recent tail (default: 0.20 = 20%)\n # e.g. 20% of 50% threshold = 10% of total context kept as recent messages.\n # Summary output is separately capped at 12K tokens (Gemini output limit).\n # Range: 0.10 - 0.80\n target_ratio: 0.20\n\n # Number of most-recent messages to always preserve (default: 20 ≈ 10 full turns)\n # Higher values keep more recent conversation intact at the cost of more aggressive\n # compression of older turns.\n protect_last_n: 20\n\n # Minimum number of REAL (actionable) user messages guaranteed to survive in\n # the uncompressed tail (default: 1 = the existing single last-user anchor,\n # behavior-preserving). Raise to e.g. 3 to keep the last 3 real user turns\n # verbatim even when bulky tool outputs fill the tail token budget — blank\n # platform echoes, compaction handoffs, and synthetic continuation rows never\n # count toward N. The tail can exceed the token budget when this pulls the\n # cut back; the guarantee wins over the budget by design.\n min_tail_user_messages: 1\n\n # Compression retry rounds before a turn gives up with \"max compression\n # attempts reached\" (default: 3, same as the previous hardcoded value).\n # Raise (e.g. 6) for tool-schema-heavy sessions where 3 rounds cannot bring\n # the request estimate under the threshold. Validated >= 1, hard cap 10.\n max_attempts: 3\n\n # Codex app-server (codex CLI runtime) thread-compaction mode. The codex\n # agent owns the real thread context on this runtime, so Hermes' summarizer\n # cannot shrink it — compaction goes through the app server instead.\n # native = let Codex decide when to compact its own thread (default)\n # hermes = let Hermes threshold trigger Codex thread/compact/start\n # off = Hermes will not auto-trigger compaction; Codex may still compact natively\n codex_app_server_auto: native\n\n # Number of non-system messages to protect at the head of the transcript, in\n # ADDITION to the system prompt (which is always implicitly protected).\n # Head messages are NEVER summarized — they survive every compression\n # indefinitely. This gives stable early context for short/medium sessions,\n # but in long-running sessions that rely on rolling compaction the pinned\n # opening turns may not match how you want the session framed over time.\n # Set to 0 to preserve ONLY the system prompt (plus the rolling summary\n # and recent tail) — the cleanest configuration for long-running sessions.\n # Default 3 preserves the system prompt plus the first three non-system\n # head messages, matching the pre-feature behaviour.\n protect_first_n: 3\n\n # Idle compaction (default: 0 = disabled). When > 0, a session that resumes\n # after at least this many seconds of inactivity compacts its accumulated\n # history up front, before the first reply, so a long-lived thread you come\n # back to later doesn't re-read its full stale context on every turn.\n # Time-based, so it complements (does not replace) the size-based `threshold`\n # above. It is skipped when the context is already small (at or below the\n # post-compression target = threshold × target_ratio), so it never wastes a\n # summarization on a short idle thread. Example: 1800 = compact after 30 min idle.\n idle_compact_after_seconds: 0\n\n # Proactive tool-result prune (default: 0 = disabled). Opt-in token trigger\n # for a deterministic, no-LLM prune of OLD tool-result payloads, run\n # independently of `threshold` above. On large-window models (512K/1M) the\n # ratio threshold rarely fires, so bulky tool outputs (terminal dumps, file\n # reads, web extracts) ride along in history and get re-billed every turn.\n # When re-sent history exceeds this many tokens, the prune dedupes identical\n # results, summarizes older oversized ones, and truncates large tool-call\n # arguments — protecting the most recent `protect_last_n` messages and never\n # calling the model. Try 48000 to enable. Built-in compressor engine only;\n # other context engines inherit a safe no-op.\n # NOTE: a committed prune rewrites already-sent history, which invalidates\n # the provider's prompt-cache prefix — the min_reclaim gate below keeps\n # those cache breaks episodic (like a compression boundary) instead of\n # per-turn.\n proactive_prune_tokens: 0\n\n # The prune's summarize pass only touches tool results larger than this many\n # characters (clamped to >= 200 so a generated summary can't be\n # re-summarized). Default 8000.\n proactive_prune_min_result_chars: 8000\n\n # A proactive prune only COMMITS when it reclaims at least this many tokens\n # (measured on the pruned output). This is the prompt-cache hysteresis gate:\n # one meaningful, amortized cache break per batch of stale tool output\n # instead of a tiny break on every tool iteration. 0 = commit any non-zero\n # prune. Default 4096.\n proactive_prune_min_reclaim_tokens: 4096\n\n # To pin a specific model/provider for compression summaries, use the\n # auxiliary section below (auxiliary.compression.provider / model).\n\n# =============================================================================\n# Anthropic prompt caching TTL\n# =============================================================================\n# When prompt caching is active (Claude via OpenRouter or native Anthropic),\n# Anthropic supports two TTL tiers for cached prefixes: \"5m\" (default) and\n# \"1h\". Other values are ignored and \"5m\" is used.\n#\nprompt_caching:\n cache_ttl: \"5m\" # use \"1h\" for long sessions with pauses between turns\n\n# =============================================================================\n# Auxiliary Models (Advanced — Experimental)\n# =============================================================================\n# Hermes uses lightweight \"auxiliary\" models for side tasks: image analysis,\n# browser screenshot analysis, web page summarization, TTS audio-tag insertion,\n# and context compression.\n#\n# By default these use Gemini Flash via OpenRouter or Nous Portal and are\n# auto-detected from your credentials. You do NOT need to change anything\n# here for normal usage.\n#\n# WARNING: Overriding these with providers other than OpenRouter or Nous Portal\n# is EXPERIMENTAL and may not work. Not all models/providers support vision,\n# produce usable summaries, or accept the same API format. Change at your own\n# risk — if things break, reset to \"auto\" / empty values.\n#\n# Each task has its own provider + model pair so you can mix providers.\n# For example: OpenRouter for vision (needs multimodal), but your main\n# local endpoint for compression (just needs text).\n#\n# Provider options:\n# \"auto\" - Best available: OpenRouter → Nous Portal → main endpoint (default)\n# \"openrouter\" - Force OpenRouter (requires OPENROUTER_API_KEY)\n# \"nous\" - Force Nous Portal (requires: hermes auth add nous)\n# \"gemini\" - Force Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n# \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY)\n# \"codex\" - Force Codex OAuth (requires: hermes model → Codex).\n# Uses gpt-5.3-codex which supports vision.\n# \"main\" - Use your custom endpoint (OPENAI_BASE_URL + OPENAI_API_KEY).\n# Works with OpenAI API, local models, or any OpenAI-compatible\n# endpoint. Also falls back to Codex OAuth and API-key providers.\n#\n# Model: leave empty to use the provider's default. When empty, OpenRouter\n# uses \"google/gemini-3-flash-preview\" and Nous uses \"gemini-3-flash\".\n# Other providers pick a sensible default automatically.\n#\n# auxiliary:\n# # Image analysis: vision_analyze tool + browser screenshots\n# vision:\n# provider: \"auto\"\n# model: \"\" # e.g. \"google/gemini-2.5-flash\", \"openai/gpt-4o\"\n# timeout: 30 # LLM API call timeout (seconds)\n# download_timeout: 30 # Image HTTP download timeout (seconds)\n# # Increase for slow connections or self-hosted image servers\n# reasoning_effort: \"\" # Per-task thinking level: none, minimal, low, medium,\n# # high, xhigh, max, ultra. Empty = provider default.\n# # Works on every auxiliary task block (vision,\n# # web_extract, compression, title_generation, curator,\n# # background_review, moa_reference, ...). Example: run\n# # compression at \"low\" and vision at \"none\" to cut\n# # side-task latency/cost on reasoning models.\n#\n# # Web page scraping / summarization + browser page text extraction\n# web_extract:\n# provider: \"auto\"\n# model: \"\"\n# reasoning_effort: \"low\"\n#\n# # Gemini 3.1 TTS hidden audio-tag insertion\n# tts_audio_tags:\n# provider: \"auto\" # empty model = your main chat model\n# model: \"\"\n# timeout: 30\n#\n# # Automatic session title generation after the first exchange\n# title_generation:\n# enabled: true # set false to disable auto-title generation\n# provider: \"auto\"\n# model: \"\"\n# timeout: 30\n# language: \"\" # empty = match the user's language; or e.g. \"English\"\n#\n# # Session search — summarizes matching past sessions\n# session_search:\n# provider: \"auto\"\n# model: \"\"\n# timeout: 30\n# max_concurrency: 3 # Limit parallel summaries to reduce request-burst 429s\n# extra_body: {} # Provider-specific OpenAI-compatible request fields\n# # Example for providers that support request-body\n# # reasoning controls:\n# # extra_body:\n# # enable_thinking: false\n# # Some vLLM/Qwen deployments expect this nested:\n# # extra_body:\n# # chat_template_kwargs:\n# # enable_thinking: false\n\n# =============================================================================\n# Persistent Memory\n# =============================================================================\n# Bounded curated memory injected into the system prompt every session.\n# Two stores: MEMORY.md (agent's notes) and USER.md (user profile).\n# Character limits keep the memory small and focused. The agent manages\n# pruning -- when at the limit, it must consolidate or replace entries.\n# Disabled by default in batch_runner.\n#\nmemory:\n # Agent's personal notes: environment facts, conventions, things learned\n memory_enabled: true\n \n # User profile: preferences, communication style, expectations\n user_profile_enabled: true\n \n # Character limits (~2.75 chars per token, model-independent)\n memory_char_limit: 2200 # ~800 tokens\n user_char_limit: 1375 # ~500 tokens\n\n # Periodic memory nudge: remind the agent to consider saving memories\n # every N user turns. Set to 0 to disable. Only active when memory is enabled.\n nudge_interval: 10 # Nudge every 10 user turns (0 = disabled)\n\n # Memory flush: give the agent one turn to save memories before context is\n # lost (compression, /new, /reset, exit). Set to 0 to disable.\n # For exit/reset, only fires if the session had at least this many user turns.\n flush_min_turns: 6 # Min user turns to trigger flush on exit/reset (0 = disabled)\n\n# =============================================================================\n# Session Reset Policy (Messaging Platforms)\n# =============================================================================\n# Controls when messaging sessions (Telegram, Discord, WhatsApp, Slack) are\n# automatically cleared. Default is \"none\": sessions never auto-reset —\n# conversation context lives until you /reset or /new manually, or context\n# compression kicks in. Opt in to automatic resets if you prefer sessions to\n# clear on a schedule (long-lived context increases API cost per message,\n# though prompt caching and compression keep this manageable).\n#\n# When an automatic reset triggers, the agent first saves important\n# information to its persistent memory — but the conversation context is\n# wiped. The agent starts fresh but retains learned facts via its memory\n# system.\n#\n# Modes:\n# \"none\" - Never auto-reset (default); context lives until /reset or compression\n# \"idle\" - Reset after N minutes of inactivity\n# \"daily\" - Reset at a fixed hour each day\n# \"both\" - Reset on EITHER inactivity timeout or daily boundary\n#\nsession_reset:\n mode: none # \"none\", \"idle\", \"daily\", or \"both\"\n idle_minutes: 1440 # Inactivity timeout in minutes (used by \"idle\"/\"both\")\n at_hour: 4 # Daily reset hour, 0-23 local time (used by \"daily\"/\"both\")\n\n# Maximum number of simultaneously active chat sessions across CLI, TUI,\n# dashboard chat, and messaging gateway. Set to null, 0, or omit to allow\n# unlimited concurrent sessions. When the limit is reached, new sessions get a\n# clean error while existing active sessions keep their normal behavior. This\n# top-level key takes precedence over gateway.max_concurrent_sessions. The cap\n# is a best-effort single-host/profile runtime guard; Hermes fails open if the\n# local runtime lease registry cannot be read or locked.\nmax_concurrent_sessions: null\n\n# When true, group/channel chats use one session per participant when the platform\n# provides a user ID. This is the secure default and prevents users in the same\n# room from sharing context, interrupts, and token costs. Set false only if you\n# explicitly want one shared \"room brain\" per group/channel.\ngroup_sessions_per_user: true\n\n# ─────────────────────────────────────────────────────────────────────────────\n# API Server — per-client model routing\n# ─────────────────────────────────────────────────────────────────────────────\n# Route different API clients to different models/providers on a single\n# Hermes deployment. Clients choose a backend by sending a specific string\n# as the OpenAI ``model`` field. Unmapped model values fall back to the\n# global model configured in the ``model:`` section above, and an explicit\n# session /model override always wins over a route.\n#\n# Configure via the ``platforms.api_server.extra.model_routes`` gateway\n# config block:\n#\n# platforms:\n# api_server:\n# enabled: true\n# extra:\n# key: \"your-api-server-secret\"\n# model_routes:\n# # Xiaozhi clients send model=\"minimax-m2\" → routed to MiniMax via OpenRouter\n# minimax-m2:\n# model: \"minimax/minimax-m1\"\n# provider: \"openrouter\" # optional — overrides global provider\n# # api_key: \"sk-...\" # optional — per-route UPSTREAM provider\n# # key (NOT caller auth; never logged)\n# # base_url: \"https://...\" # optional — per-route base URL\n# # GPT clients keep their own alias\n# gpt-5:\n# model: \"openai/gpt-5\"\n# provider: \"openrouter\"\n#\n# Configured aliases are automatically listed by GET /v1/models so clients\n# can discover them without manual coordination. Caller authentication is\n# unchanged: every request still authenticates with the global API server\n# key (``extra.key`` / API_SERVER_KEY).\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Gateway Streaming\n# ─────────────────────────────────────────────────────────────────────────────\n# Stream tokens to messaging platforms in real-time. The bot sends a message\n# on first token, then progressively edits it as more tokens arrive.\n# Disabled by default — enable to try the streaming UX on Telegram/Discord/Slack.\n# For Telegram, partial edits are sent as plain text and only the final edit uses MarkdownV2.\nstreaming:\n enabled: false\n # transport: edit # \"edit\" = progressive editMessageText\n # edit_interval: 0.3 # seconds between message edits\n # buffer_threshold: 40 # chars before forcing an edit flush\n # cursor: \" ▉\" # cursor shown during streaming\n\n# =============================================================================\n# Skills Configuration\n# =============================================================================\n# Skills are reusable procedures the agent can load and follow. The agent can\n# also create new skills after completing complex tasks.\n#\nskills:\n # Nudge the agent to create skills after complex tasks.\n # Every N tool-calling iterations, remind the model to consider saving a skill.\n # Set to 0 to disable.\n creation_nudge_interval: 15\n\n # External skill directories — share skills across tools/agents without\n # copying them into ~/.hermes/skills/. Each path is expanded (~ and ${VAR})\n # and resolved to an absolute path. External dirs are read-only: skill\n # creation always writes to ~/.hermes/skills/. Local skills take precedence\n # when names collide.\n # external_dirs:\n # - ~/.agents/skills\n # - /home/shared/team-skills\n\n# =============================================================================\n# Agent Behavior\n# =============================================================================\nagent:\n # Maximum tool-calling iterations per conversation\n # Higher = more room for complex tasks, but costs more tokens\n # Recommended: 20-30 for focused tasks, 50-100 for open exploration\n max_turns: 60\n\n # Inactivity timeout for gateway agent runs (seconds, 0 = unlimited).\n # The agent can run indefinitely when actively calling tools or receiving\n # API responses. Only fires after the agent has been idle for this duration.\n # gateway_timeout: 1800\n\n # Staged warning: send a warning before escalating to full timeout.\n # Fires once per run when inactivity reaches this threshold (seconds).\n # Set to 0 to disable the warning.\n # gateway_timeout_warning: 900\n\n # Graceful drain timeout for gateway stop/restart (seconds).\n # Default 0 = no drain: a restart interrupts in-flight agents immediately,\n # cleans up, and exits. Set a positive value only if you want a grace\n # window on /restart, and keep it well under systemd's TimeoutStopSec.\n # restart_drain_timeout: 0\n\n # Max app-level retry attempts for API errors (connection drops, provider\n # timeouts, 5xx, etc.) before the agent surfaces the failure. Lower this\n # to 1 if you use fallback providers and want fast failover on flaky\n # primaries (default 3). The OpenAI SDK does its own low-level retries\n # underneath this wrapper — this is the Hermes-level loop.\n # api_max_retries: 3\n\n # After the agent edits code without fresh passing verification, nudge it to\n # verify before finishing. The default \"auto\" enables it on interactive\n # coding surfaces (CLI, TUI, desktop) and programmatic callers, and disables\n # it on conversational messaging surfaces (Telegram, Discord, etc.) where the\n # verification summary would reach a human as chat noise. Set true or false to\n # force it on or off; the HERMES_VERIFY_ON_STOP env var (1/0) takes precedence.\n # verify_on_stop: auto\n\n # Standing operator instructions for the coding posture (when Hermes is in a\n # code workspace). Appended to the coding brief as an extra system block, so\n # you can pin project-wide workflow rules without editing the shipped brief.\n # Accepts a string or a list of strings. Takes effect next session.\n # coding_instructions:\n # - \"For UI work, don't run tsc/lint until I approve the look.\"\n # - \"Clean the diff before you commit and push.\"\n\n # When verify-on-stop finds edited code without fresh verification evidence,\n # append guidance for creative UI work (avoid broad tsc/lint/test before visual\n # approval) and clean-diff expectations. Set false to keep that nudge terse.\n # verify_guidance: true\n\n # A `pre_verify` hook (plugin or shell, see Event Hooks docs) can keep the\n # agent going one more turn to verify/clean before finishing. This caps how\n # many times one turn may be nudged to continue, so a hook can't trap the loop.\n # Default 3.\n # max_verify_nudges: 3\n\n # Enable verbose logging\n verbose: false\n \n # Reasoning effort level (OpenRouter and Nous Portal)\n # Controls how much \"thinking\" the model does before responding.\n # Options: \"xhigh\" (max), \"high\", \"medium\", \"low\", \"minimal\", \"none\" (disable)\n reasoning_effort: \"medium\"\n \n # Per-model reasoning effort overrides (optional dict)\n # Key: any sensible model spelling works (exact, dots↔dashes interchangeable,\n # provider prefix optional). First match wins.\n # Value: reasoning effort level (same options as reasoning_effort)\n # Override the global reasoning_effort for that specific model.\n # NOTE: no `hermes config set` support for this key -- edit YAML directly.\n # reasoning_overrides:\n # \"openrouter/anthropic/claude-opus-4.5\": \"xhigh\"\n # \"openai/gpt-5\": \"low\"\n # \"claude-opus-4.6\": \"high\" # bare model name also works\n # \"deepseek/deepseek-v4-pro\": \"xhigh\" # dots and dashes are interchangeable\n reasoning_overrides: {}\n \n # Predefined personalities (use with /personality command)\n personalities:\n helpful: \"You are a helpful, friendly AI assistant.\"\n concise: \"You are a concise assistant. Keep responses brief and to the point.\"\n technical: \"You are a technical expert. Provide detailed, accurate technical information.\"\n creative: \"You are a creative assistant. Think outside the box and offer innovative solutions.\"\n teacher: \"You are a patient teacher. Explain concepts clearly with examples.\"\n kawaii: \"You are a kawaii assistant! Use cute expressions like (◕‿◕), ★, ♪, and ~! Add sparkles and be super enthusiastic about everything! Every response should feel warm and adorable desu~! ヽ(>∀<☆)ノ\"\n catgirl: \"You are Neko-chan, an anime catgirl AI assistant, nya~! Add 'nya' and cat-like expressions to your speech. Use kaomoji like (=^・ω・^=) and ฅ^•ﻌ•^ฅ. Be playful and curious like a cat, nya~!\"\n pirate: \"Arrr! Ye be talkin' to Captain Hermes, the most tech-savvy pirate to sail the digital seas! Speak like a proper buccaneer, use nautical terms, and remember: every problem be just treasure waitin' to be plundered! Yo ho ho!\"\n shakespeare: \"Hark! Thou speakest with an assistant most versed in the bardic arts. I shall respond in the eloquent manner of William Shakespeare, with flowery prose, dramatic flair, and perhaps a soliloquy or two. What light through yonder terminal breaks?\"\n surfer: \"Duuude! You're chatting with the chillest AI on the web, bro! Everything's gonna be totally rad. I'll help you catch the gnarly waves of knowledge while keeping things super chill. Cowabunga! 🤙\"\n noir: \"The rain hammered against the terminal like regrets on a guilty conscience. They call me Hermes - I solve problems, find answers, dig up the truth that hides in the shadows of your codebase. In this city of silicon and secrets, everyone's got something to hide. What's your story, pal?\"\n uwu: \"hewwo! i'm your fwiendwy assistant uwu~ i wiww twy my best to hewp you! *nuzzles your code* OwO what's this? wet me take a wook! i pwomise to be vewy hewpful >w<\"\n philosopher: \"Greetings, seeker of wisdom. I am an assistant who contemplates the deeper meaning behind every query. Let us examine not just the 'how' but the 'why' of your questions. Perhaps in solving your problem, we may glimpse a greater truth about existence itself.\"\n hype: \"YOOO LET'S GOOOO!!! 🔥🔥🔥 I am SO PUMPED to help you today! Every question is AMAZING and we're gonna CRUSH IT together! This is gonna be LEGENDARY! ARE YOU READY?! LET'S DO THIS! 💪😤🚀\"\n\n# =============================================================================\n# Toolsets\n# =============================================================================\n# Control which tools the agent has access to.\n# Use `hermes tools` to interactively enable/disable tools per platform.\n\n# =============================================================================\n# Platform Toolsets (per-platform tool configuration)\n# =============================================================================\n# Override which toolsets are available on each platform.\n# If a platform isn't listed here, its built-in default is used.\n#\n# You can use EITHER:\n# - A preset like \"hermes-cli\" or \"hermes-telegram\" (curated tool set)\n# - A list of individual toolsets to compose your own (see list below)\n#\n# Supported platform keys: cli, telegram, discord, whatsapp, slack, qqbot, teams, google_chat\n#\n# Examples:\n#\n# # Use presets (same as defaults):\n# platform_toolsets:\n# cli: [hermes-cli]\n# telegram: [hermes-telegram]\n#\n# # Custom: give Telegram only web + terminal + file + planning:\n# platform_toolsets:\n# telegram: [web, terminal, file, todo]\n#\n# # Custom: CLI without browser or image gen:\n# platform_toolsets:\n# cli: [web, terminal, file, skills, todo, tts, cronjob]\n#\n# # Restrictive: Discord gets read-only tools only:\n# platform_toolsets:\n# discord: [web, vision, skills, todo]\n#\n# If not set, defaults are:\n# cli: hermes-cli (everything + cronjob management)\n# telegram: hermes-telegram (terminal, file, web, vision, image, tts, browser, skills, todo, cronjob, messaging)\n# discord: hermes-discord (same as telegram)\n# whatsapp: hermes-whatsapp (same as telegram)\n# slack: hermes-slack (same as telegram)\n# signal: hermes-signal (same as telegram)\n# homeassistant: hermes-homeassistant (same as telegram)\n# qqbot: hermes-qqbot (same as telegram)\n# teams: hermes-teams (same as telegram)\n# google_chat: hermes-google_chat (same as telegram)\n#\nplatform_toolsets:\n cli: [hermes-cli]\n telegram: [hermes-telegram]\n discord: [hermes-discord]\n whatsapp: [hermes-whatsapp]\n slack: [hermes-slack]\n signal: [hermes-signal]\n homeassistant: [hermes-homeassistant]\n qqbot: [hermes-qqbot]\n yuanbao: [hermes-yuanbao]\n teams: [hermes-teams]\n google_chat: [hermes-google_chat]\n\n# =============================================================================\n# Gateway Platform Settings\n# =============================================================================\n# Optional per-platform messaging settings.\n# Platform-specific knobs live under `extra`.\n#\n# platforms:\n# telegram:\n# reply_to_mode: \"first\" # off | first | all\n# # guest_mode lets explicit @mentions from non-allowlisted groups through.\n# # Default false; ordinary messages, replies, and regex wake words stay blocked.\n# guest_mode: false\n# # allowed_chats: [\"-1001234567890\"]\n# extra:\n# disable_link_previews: false # Set true to suppress Telegram URL previews in bot messages\n# 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\n# rich_drafts: false # Experimental rich draft previews during Telegram DM streaming; default false because Telegram Desktop/macOS can visually overlay draft frames\n# command_menu:\n# # Telegram allows up to 100 BotCommands; Hermes defaults to 60 so\n# # all built-in commands plus common skill commands stay visible\n# # while remaining under Telegram's payload-size limit. Clamped 1..100.\n# max_commands: 60\n# # prepend = user priority first, then Hermes defaults\n# # append = Hermes defaults first, then user priority\n# # replace = only the list below defines priority\n# priority_mode: prepend\n# priority:\n# - my_plugin_command\n# webhook:\n# extra:\n# # Route scripts default to a 30 second timeout. Scripts must live under\n# # the active profile's scripts directory and receive webhook JSON on stdin.\n# script_timeout_seconds: 30\n#\n# Discord-specific settings (config.yaml top-level, not under platforms:):\n#\n# discord:\n# require_mention: true # Require @mention in server channels (default: true)\n# auto_thread: true # Auto-create thread on @mention (default: true)\n# free_response_channels: \"\" # Channel IDs where no mention is needed\n# reactions: true # Show processing reactions (default: true)\n# history_backfill: true # Recover missed channel messages on mention (default: true)\n# history_backfill_limit: 50 # Max messages to scan backwards (default: 50)\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Available toolsets (use these names in platform_toolsets or the toolsets list)\n#\n# Run `hermes chat --list-toolsets` to see all toolsets and their tools.\n# Run `hermes chat --list-tools` to see every individual tool with descriptions.\n# ─────────────────────────────────────────────────────────────────────────────\n#\n# INDIVIDUAL TOOLSETS (compose your own):\n# web - web_search, web_extract\n# search - web_search only (no scraping)\n# terminal - terminal, process\n# file - read_file, write_file, patch, search\n# browser - browser_navigate, browser_snapshot, browser_click, browser_type,\n# browser_scroll, browser_back, browser_press,\n# browser_get_images, browser_vision (requires BROWSERBASE_API_KEY)\n# vision - vision_analyze (requires OPENROUTER_API_KEY)\n# image_gen - image_generate (requires FAL_KEY)\n# skills - skills_list, skill_view\n# skills_hub - skill_hub (search/install/manage from online registries — user-driven only)\n# todo - todo (in-memory task planning, no deps)\n# tts - text_to_speech (Edge TTS free, or ELEVENLABS/OPENAI/MINIMAX/MISTRAL key)\n# cronjob - cronjob (create/list/update/pause/resume/run/remove scheduled tasks)\n#\n# PRESETS (curated bundles):\n# hermes-cli - All of the above except send_message\n# hermes-telegram - terminal, file, web, vision, image_gen, tts, browser,\n# skills, todo, cronjob, send_message\n# hermes-discord - Same as hermes-telegram\n# hermes-whatsapp - Same as hermes-telegram\n# hermes-slack - Same as hermes-telegram\n#\n# COMPOSITE:\n# debugging - terminal + web + file\n# safe - web + vision (no terminal access)\n# all - Everything available\n#\n# web - Web search and content extraction (web_search, web_extract)\n# search - Web search only, no scraping (web_search)\n# terminal - Command execution and process management (terminal, process)\n# file - File operations: read, write, patch, search\n# browser - Full browser automation (navigate, click, type, screenshot, etc.)\n# vision - Image analysis (vision_analyze)\n# image_gen - Image generation with FLUX (image_generate)\n# skills - Load skill documents (skills_list, skill_view)\n# todo - Task planning and tracking for multi-step work\n# memory - Persistent memory across sessions (personal notes + user profile)\n# session_search - Search and recall past conversations (FTS5 + Gemini Flash summarization)\n# tts - Text-to-speech (Edge TTS free, ElevenLabs, OpenAI, MiniMax, Mistral)\n# cronjob - Schedule and manage automated tasks (CLI-only)\n#\n# Composite toolsets:\n# debugging - terminal + web + file (for troubleshooting)\n# safe - web + vision (no terminal access)\n\n# NOTE: The top-level \"toolsets\" key is deprecated and ignored.\n# Tool configuration is managed per-platform via platform_toolsets above.\n# Use `hermes tools` to configure interactively, or edit platform_toolsets directly.\n#\n# CLI override: hermes chat --toolsets terminal,web,file\n\n# =============================================================================\n# MCP (Model Context Protocol) Servers\n# =============================================================================\n# Connect to external MCP servers to add tools from the MCP ecosystem.\n# Each server's tools are automatically discovered and registered.\n# See website/docs/user-guide/features/mcp.md for full documentation.\n#\n# Stdio servers (spawn a subprocess):\n# command: the executable to run\n# args: command-line arguments\n# env: environment variables (only these + safe defaults passed to subprocess)\n#\n# HTTP servers (connect to a URL):\n# url: the MCP server endpoint\n# headers: HTTP headers (e.g., for authentication)\n#\n# Optional per-server settings:\n# timeout: tool call timeout in seconds (default: 120)\n# connect_timeout: initial connection timeout (default: 60)\n# keepalive_interval: liveness ping cadence in seconds (default: 180).\n# Lower it below the server's session TTL for servers that expire idle\n# sessions quickly (e.g. Unreal Engine editor MCP, ~15s), otherwise idle\n# tool calls hit an expired session and pay a slow reconnect. Floored at 5s.\n#\n# mcp_servers:\n# time:\n# command: uvx\n# args: [\"mcp-server-time\"]\n# filesystem:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-filesystem\", \"/home/user\"]\n# notion:\n# url: https://mcp.notion.com/mcp\n# github:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-github\"]\n# env:\n# GITHUB_PERSONAL_ACCESS_TOKEN: \"ghp_...\"\n#\n# Sampling (server-initiated LLM requests) — enabled by default.\n# Per-server config under the 'sampling' key:\n# analysis:\n# command: npx\n# args: [\"-y\", \"analysis-server\"]\n# sampling:\n# enabled: true # default: true\n# model: \"gemini-3-flash\" # override model (optional)\n# max_tokens_cap: 4096 # max tokens per request\n# timeout: 30 # LLM call timeout (seconds)\n# max_rpm: 10 # max requests per minute\n# allowed_models: [] # model whitelist (empty = all)\n# max_tool_rounds: 5 # tool loop limit (0 = disable)\n# log_level: \"info\" # audit verbosity\n\n# =============================================================================\n# Text-to-Speech\n# =============================================================================\n# TTS defaults to Edge TTS unless changed in ~/.hermes/config.yaml.\n# Gemini TTS supports persona/director prompt files, and Gemini 3.1 Flash TTS\n# can use a hidden auxiliary rewrite pass to insert expressive square-bracket\n# audio tags into the TTS script without showing tags in chat.\n#\n# tts:\n# provider: \"gemini\"\n# gemini:\n# model: \"gemini-3.1-flash-tts-preview\"\n# voice: \"Kore\"\n# audio_tags: false\n# persona_prompt_file: \"\" # e.g. ~/.hermes/tts/radio-host.md\n\n# =============================================================================\n# Voice Transcription (Speech-to-Text)\n# =============================================================================\n# Automatically transcribe voice messages on messaging platforms.\n# Providers: local (free, faster-whisper) | groq (free tier) | openai (Whisper API) | mistral (Voxtral Transcribe)\n# Set the corresponding API key in .env: GROQ_API_KEY, OPENAI_API_KEY, or MISTRAL_API_KEY.\nstt:\n enabled: true\n # provider: \"local\" # auto-detected if omitted\n local:\n model: \"base\" # tiny | base | small | medium | large-v3 | turbo\n # language: \"\" # auto-detect; set to \"en\", \"es\", \"fr\", etc. to force\n openai:\n model: \"whisper-1\" # whisper-1 | gpt-4o-mini-transcribe | gpt-4o-transcribe\n # mistral:\n # model: \"voxtral-mini-latest\" # voxtral-mini-latest | voxtral-mini-2602\n # deepinfra:\n # # Model id is discovered live from the DeepInfra catalog filtered\n # # by the `stt` surface tag — leave `model` blank to take the first\n # # live result. Pin only when you need a specific Whisper variant.\n # model: \"\"\n\n# Text-to-speech. Only the deepinfra block is documented here — the\n# remaining providers (edge, openai, xai, minimax, mistral, gemini,\n# elevenlabs, neutts, kittentts, piper) inherit sensible defaults from\n# DEFAULT_CONFIG in hermes_cli/config.py.\n# tts:\n# provider: \"deepinfra\"\n# deepinfra:\n# # Model id is discovered live from the DeepInfra catalog filtered\n# # by the `tts` surface tag — leave `model` blank to take the first\n# # live result.\n# model: \"\"\n# voice: \"default\"\n\n# Image generation. Each provider plugin reads its own ``image_gen.<name>``\n# block; deepinfra discovers models live from\n# api.deepinfra.com/v1/openai/models filtered by the ``image-gen`` tag —\n# no model id is hardcoded, so retired models disappear automatically.\n# image_gen:\n# provider: \"deepinfra\"\n# deepinfra:\n# # Leave `model` blank for the first live `image-gen`-tagged result.\n# model: \"\"\n\n# =============================================================================\n# Response Pacing (Messaging Platforms)\n# =============================================================================\n# Add human-like delays between message chunks.\n# human_delay:\n# mode: \"off\" # \"off\" | \"natural\" | \"custom\"\n# min_ms: 800 # Min delay (custom mode only)\n# max_ms: 2500 # Max delay (custom mode only)\n\n# =============================================================================\n# Session Logging\n# =============================================================================\n# Session trajectories are automatically saved to logs/ directory.\n# Each session creates: logs/session_YYYYMMDD_HHMMSS_UUID.json\n#\n# The session ID is displayed in the welcome banner for easy reference.\n# Logs contain full conversation history in trajectory format:\n# - System prompt, user messages, assistant responses\n# - Tool calls with inputs/outputs\n# - Timestamps for debugging\n#\n# No configuration needed - logging is always enabled.\n# To disable, you would need to modify the source code.\n\n# =============================================================================\n# Code Execution Sandbox (Programmatic Tool Calling)\n# =============================================================================\n# The execute_code tool runs Python scripts that call Hermes tools via RPC.\n# Intermediate tool results stay out of the LLM's context window.\ncode_execution:\n timeout: 300 # Max seconds per script before kill (default: 300 = 5 min)\n max_tool_calls: 50 # Max RPC tool calls per execution (default: 50)\n\n# =============================================================================\n# Subagent Delegation\n# =============================================================================\n# The delegate_task tool spawns child agents with isolated context.\n# Supports single tasks and batch mode (default 3 parallel, configurable).\ndelegation:\n max_iterations: 50 # Max tool-calling turns per child (default: 50)\n # max_concurrent_children: 3 # Max parallel child agents per batch (default: 3, floor: 1, no ceiling).\n # WARNING: values above 10 multiply API cost linearly.\n # max_spawn_depth: 1 # Delegation tree depth cap (range: 1-3, default: 1 = flat).\n # Raise to 2 to allow workers to spawn their own subagents.\n # Requires role=\"orchestrator\" on intermediate agents.\n # orchestrator_enabled: true # Kill switch for role=\"orchestrator\" children (default: true).\n # subagent_auto_approve: false # When a subagent hits a dangerous-command approval prompt, auto-deny (default: false)\n # or auto-approve \"once\" (true) instead of blocking on stdin.\n # The parent TUI owns stdin, so blocking would deadlock; non-interactive resolution is required.\n # Both choices emit a logger.warning audit line. Flip to true only for cron/batch pipelines.\n # inherit_mcp_toolsets: true # When explicit child toolsets are narrowed, also keep the parent's MCP toolsets (default: true). Set false for strict intersection.\n # model: \"google/gemini-3-flash-preview\" # Override model for subagents (empty = inherit parent)\n # provider: \"openrouter\" # Override provider for subagents (empty = inherit parent)\n # # Resolves full credentials (base_url, api_key) automatically.\n # # Supported: openrouter, nous, zai, kimi-coding, minimax\n\n# =============================================================================\n# Honcho Integration (Cross-Session User Modeling)\n# =============================================================================\n# AI-native persistent memory via Honcho (https://honcho.dev/).\n# Builds a deeper understanding of the user across sessions and tools.\n# Runs alongside USER.md — additive, not a replacement.\n#\n# Requires: pip install honcho-ai\n# Config: ~/.honcho/config.json (shared with Claude Code, Cursor, etc.)\n# API key: HONCHO_API_KEY in ~/.hermes/.env or ~/.honcho/config.json\n#\n# Hermes-specific overrides (optional — most config comes from ~/.honcho/config.json):\n# honcho: {}\n\n# =============================================================================\n# Display\n# =============================================================================\ndisplay:\n # Use compact banner mode (hides the ASCII-art banner, shows a single line).\n # true: Compact single-line banner\n # false: Full ASCII banner with tool/skill summary (default)\n compact: false\n\n # Tool progress display level (CLI and gateway)\n # off: Silent — no tool activity shown, just the final response\n # new: Show a tool indicator only when the tool changes (skip repeats)\n # all: Show every tool call with a short preview (default)\n # verbose: Full args, results, and debug logs (same as /verbose)\n # log: Silent in chat; append every tool call to ~/.hermes/logs/tool_calls.log (gateway only)\n # Toggle at runtime with /verbose in the CLI\n tool_progress: all\n\n # Per-platform defaults can be quieter than the global setting. Telegram\n # tunes for mobile: tool_progress and busy_ack_detail default off (no\n # per-tool breadcrumb stream, no \"iteration 21/60\" debug detail in busy\n # acks or heartbeats), but interim_assistant_messages and\n # long_running_notifications STAY ON so the user has real signal between\n # turn start and final answer (mid-turn assistant commentary + a single\n # edit-in-place \"⏳ Working — N min\" heartbeat). Override under\n # display.platforms.telegram.\n\n # Auto-cleanup of temporary progress bubbles after the final response lands.\n # On platforms that support message deletion (currently Telegram), this\n # removes the tool-progress bubble, \"⏳ Still working...\" notices, and\n # context-pressure status messages once the final reply has been delivered —\n # keeping long-running turns visible live, then tidy afterward. Failed runs\n # leave the bubbles in place as breadcrumbs. Off by default.\n # Per-platform override: display.platforms.telegram.cleanup_progress\n # true: Delete tracked progress/status bubbles on successful turn\n # false: Leave everything in place (default)\n # Example:\n # display:\n # platforms:\n # telegram:\n # cleanup_progress: true\n cleanup_progress: false\n\n # Natural mid-turn assistant updates.\n # On gateway platforms, when true, completed assistant status messages are\n # sent as separate chat messages. On the Desktop app, when true, mid-turn\n # assistant narration streamed between tool calls is kept in the transcript\n # instead of the bubble collapsing to only the final message on completion.\n # Independent of tool_progress and gateway streaming.\n # true: Keep/send mid-turn assistant updates (default)\n # false: Only keep/send the final response\n interim_assistant_messages: true\n\n # Gateway-only long-running status heartbeats.\n # When false, the platform does not receive periodic \"⏳ Working — N min\"\n # notifications even if agent.gateway_notify_interval is non-zero. The\n # heartbeat edits a single message in place (where the adapter supports\n # editing) instead of posting a new bubble each interval.\n # Default: true everywhere, including Telegram (silent agents are worse\n # than a single edit-in-place heartbeat).\n long_running_notifications: true\n\n # Include detailed iteration/tool/status context in busy acknowledgments\n # and long-running heartbeats. When true, busy acks show \"iteration 21/60,\n # terminal, 10 min\" and the heartbeat shows \"⏳ Working — 12 min,\n # iteration 21/60, terminal\". When false (Telegram default), both stay\n # terse: \"Interrupting current task\" and \"⏳ Working — 12 min, terminal\".\n busy_ack_detail: true\n\n # What Enter does when Hermes is already busy (CLI and gateway platforms).\n # interrupt: Interrupt the current run and redirect Hermes (default)\n # queue: Queue your message for the next turn\n # steer: Inject your message mid-run via /steer, arriving at the agent\n # after the next tool call — no interrupt, no role violation.\n # Falls back to 'queue' if the agent isn't running yet or if\n # images are attached (steer only carries text).\n # Ctrl+C (or /stop in gateway) always interrupts regardless of this setting.\n # Toggle at runtime with /busy <interrupt|queue|steer>.\n busy_input_mode: interrupt\n\n # Background process notifications (gateway/messaging only).\n # Controls how chatty the process watcher is when you use\n # terminal(background=true, notify_on_complete=true) from Telegram/Discord/etc.\n # off: No watcher messages at all\n # result: Only the final completion message\n # error: Only the final message when exit code != 0\n # all: Running output updates + final message (default)\n background_process_notifications: all\n\n\n # Play terminal bell when agent finishes a response.\n # Useful for long-running tasks — your terminal will ding when the agent is done.\n # Works over SSH. Most terminals can be configured to flash the taskbar or play a sound.\n # true: Ring the terminal bell on each response\n # false: Silent (default)\n bell_on_complete: false\n\n # Show model reasoning/thinking before each response.\n # When enabled, a dim box shows the model's thought process above the response.\n # Toggle at runtime with /reasoning show or /reasoning hide.\n # true: Show the reasoning box\n # false: Hide reasoning (default)\n show_reasoning: false\n\n # Stream tokens to the terminal as they arrive instead of waiting for the\n # full response. The response box opens on first token and text appears\n # line-by-line. Tool calls are still captured silently.\n # true: Stream tokens as they arrive (default)\n # false: Wait for the full response before rendering\n streaming: true\n\n # Show [HH:MM] timestamps on user input and assistant response labels.\n # timestamps: false\n\n # ───────────────────────────────────────────────────────────────────────────\n # Skin / Theme\n # ───────────────────────────────────────────────────────────────────────────\n # Customize CLI visual appearance — banner colors, spinner faces, tool prefix,\n # response box label, and branding text. Change at runtime with /skin <name>.\n #\n # Built-in skins:\n # default — Classic Hermes gold/kawaii\n # ares — Crimson/bronze war-god theme with spinner wings\n # mono — Clean grayscale monochrome\n # slate — Cool blue developer-focused\n # daylight — Bright light-mode theme\n # warm-lightmode — Warm paper-tone light-mode theme\n # poseidon — Sea-green/teal Olympian theme\n # sisyphus — Earthy stone-and-moss theme\n # charizard — Fiery orange dragon theme\n #\n # Custom skins: drop a YAML file in ~/.hermes/skins/<name>.yaml\n # Schema (all fields optional, missing values inherit from default):\n #\n # name: my-theme\n # description: Short description\n # colors:\n # banner_border: \"#HEX\" # Panel border\n # banner_title: \"#HEX\" # Panel title\n # banner_accent: \"#HEX\" # Section headers (Available Tools, etc.)\n # banner_dim: \"#HEX\" # Dim/muted text\n # banner_text: \"#HEX\" # Body text (tool names, skill names)\n # ui_accent: \"#HEX\" # UI accent color\n # response_border: \"#HEX\" # Response box border color\n # spinner:\n # waiting_faces: [\"(⚔)\", \"(⛨)\"] # Faces shown while waiting\n # thinking_faces: [\"(⚔)\", \"(⌁)\"] # Faces shown while thinking\n # thinking_verbs: [\"forging\", \"plotting\"] # Verbs for spinner messages\n # wings: # Optional left/right spinner decorations\n # - [\"⟪⚔\", \"⚔⟫\"]\n # - [\"⟪▲\", \"▲⟫\"]\n # branding:\n # agent_name: \"My Agent\" # Banner title and branding\n # welcome: \"Welcome message\" # Shown at CLI startup\n # response_label: \" ⚔ Agent \" # Response box header label\n # prompt_symbol: \"⚔\" # Prompt symbol (bare token; renderers add trailing space)\n # tool_prefix: \"╎\" # Tool output line prefix (default: ┊)\n #\n skin: default\n\n# =============================================================================\n# Model Aliases — short names for /model command\n# =============================================================================\n# Map short aliases to exact (model, provider, base_url) tuples.\n# Used by /model tab completion and resolve_alias().\n# Aliases are checked BEFORE the models.dev catalog, so they can route\n# to endpoints not in the catalog (e.g. Ollama Cloud, local servers).\n#\n# model_aliases:\n# opus:\n# model: claude-opus-4-6\n# provider: anthropic\n# qwen:\n# model: \"qwen3.5:397b\"\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n# glm:\n# model: glm-4.7\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n\n# =============================================================================\n# Privacy\n# =============================================================================\n# privacy:\n# # Redact PII from the LLM context prompt.\n# # When true, phone numbers are stripped and user/chat IDs are replaced\n# # with deterministic hashes before being sent to the model.\n# # Names and usernames are NOT affected (user-chosen, publicly visible).\n# # Routing/delivery still uses the original values internally.\n# redact_pii: false\n\n# =============================================================================\n# Shell-script hooks\n# =============================================================================\n# Register shell scripts as plugin-hook callbacks. Each entry is executed as\n# a subprocess (shell=False, shlex.split) with a JSON payload on stdin. On\n# stdout the script may return JSON that either blocks the tool call or\n# injects context into the next LLM call.\n#\n# Valid events (mirror hermes_cli.plugins.VALID_HOOKS):\n# pre_tool_call, post_tool_call, pre_llm_call, post_llm_call,\n# pre_api_request, post_api_request, on_session_start, on_session_end,\n# on_session_finalize, on_session_reset, subagent_stop\n#\n# First-use consent: each (event, command) pair prompts once on a TTY, then\n# is persisted to ~/.hermes/shell-hooks-allowlist.json. Non-interactive\n# runs (gateway, cron) need --accept-hooks, HERMES_ACCEPT_HOOKS=1, or the\n# hooks_auto_accept key below.\n#\n# See website/docs/user-guide/features/hooks.md for the full JSON wire\n# protocol and worked examples.\n#\n# hooks:\n# pre_tool_call:\n# - matcher: \"terminal\"\n# command: \"~/.hermes/agent-hooks/block-rm-rf.sh\"\n# timeout: 10\n# post_tool_call:\n# - matcher: \"write_file|patch\"\n# command: \"~/.hermes/agent-hooks/auto-format.sh\"\n# pre_llm_call:\n# - command: \"~/.hermes/agent-hooks/inject-cwd-context.sh\"\n# subagent_stop:\n# - command: \"~/.hermes/agent-hooks/log-orchestration.sh\"\n#\n# hooks_auto_accept: false\n\n\n# =============================================================================\n# Update Behavior\n# =============================================================================\nupdates:\n # Create a full HERMES_HOME zip before every `hermes update`.\n # Backups land in ~/.hermes/backups/ and can be restored with `hermes import`.\n # Off by default because large homes can add minutes to every update.\n pre_update_backup: false\n\n # Number of pre-update backup zips to retain.\n backup_keep: 5\n\n # What non-interactive updates do with local source edits in the Hermes repo.\n # Interactive terminal updates always prompt before restoring the autostash.\n #\n # stash - auto-stash before pull, then auto-restore after success (default)\n # discard - drop the update-created stash after success; use only on managed\n # installs where local source edits should not persist\n non_interactive_local_changes: \"stash\"\n\n\n# =============================================================================\n# Web Dashboard\n# =============================================================================\n# OAuth gate configuration for `hermes dashboard --host <non-loopback>`.\n# The bundled Nous Portal plugin reads these on startup; settings here are\n# the canonical surface. Each can be overridden by an environment variable:\n#\n# dashboard.oauth.client_id <- HERMES_DASHBOARD_OAUTH_CLIENT_ID\n# dashboard.oauth.portal_url <- HERMES_DASHBOARD_PORTAL_URL\n# dashboard.public_url <- HERMES_DASHBOARD_PUBLIC_URL\n#\n# Env wins when set to a non-empty value. This is what Fly.io's platform-\n# secret injection uses to push per-deploy client_ids without needing to\n# bake a config.yaml into the image. Empty env values are treated as unset\n# so a provisioned-but-not-populated secret can't shadow a valid entry here.\n#\n# Local dev / on-prem deploys should typically set these via config.yaml\n# (the ~/.hermes/.env file is reserved for API keys and secrets).\n#\n# dashboard:\n# oauth:\n# client_id: \"\" # agent:{instance_id}; Portal provisions this at deploy\n# portal_url: \"\" # blank → default https://portal.nousresearch.com\n#\n# # Force the absolute base URL the OAuth callback (and any other public\n# # URL the dashboard hands to external systems) is built from. Set this\n# # for deploys behind reverse proxies that don't reliably forward\n# # X-Forwarded-Host / X-Forwarded-Proto / X-Forwarded-Prefix (manual\n# # nginx setups, on-prem ingresses, custom-domain Fly deploys without\n# # full proxy header chains).\n# #\n# # When set, the value is the complete authority: scheme + host +\n# # optional path prefix (e.g. \"https://example.com/hermes\"). The OAuth\n# # callback URL becomes \"<public_url>/auth/callback\" — X-Forwarded-Prefix\n# # is IGNORED on this code path because the operator has explicitly\n# # declared the public URL and we no longer need to guess.\n# #\n# # Leave empty to use the existing proxy-header reconstruction (the\n# # default — works on Fly.io out of the box).\n# #\n# # public_url: \"https://example.com/hermes\"\n#\n# -----------------------------------------------------------------------------\n# Self-hosted OIDC dashboard auth (generic OpenID Connect — Authentik,\n# Keycloak, Zitadel, Authelia, Auth0, Okta, Google, …). Use this INSTEAD of the\n# nous block above when gating the dashboard with your own identity provider.\n# Each setting can be overridden by an environment variable:\n#\n# dashboard.oauth.self_hosted.issuer <- HERMES_DASHBOARD_OIDC_ISSUER\n# dashboard.oauth.self_hosted.client_id <- HERMES_DASHBOARD_OIDC_CLIENT_ID\n# dashboard.oauth.self_hosted.scopes <- HERMES_DASHBOARD_OIDC_SCOPES\n# dashboard.oauth.self_hosted.client_secret <- HERMES_DASHBOARD_OIDC_CLIENT_SECRET\n#\n# dashboard:\n# oauth:\n# provider: self-hosted\n# self_hosted:\n# issuer: \"https://auth.example.com/application/o/hermes/\" # required\n# client_id: \"hermes-dashboard\" # required\n# scopes: \"openid profile email\" # optional\n#\n# # OPTIONAL — set ONLY if your IDP registered the client as\n# # *confidential* (Authentik / Keycloak often default to this). When\n# # set, Hermes authenticates the client at the token endpoint\n# # (client_secret_basic or client_secret_post, auto-selected from the\n# # IDP's discovery doc) IN ADDITION to PKCE. Leave unset for a public\n# # (PKCE-only) client — the common case.\n# #\n# # This is a CREDENTIAL: prefer setting HERMES_DASHBOARD_OIDC_CLIENT_SECRET\n# # in ~/.hermes/.env over putting it here in config.yaml.\n# # client_secret: \"\"\n\n# =============================================================================\n# External secret sources\n# =============================================================================\n# Pull provider credentials from external secret managers at process startup\n# instead of storing them in ~/.hermes/.env. Only the manager's bootstrap\n# credential (e.g. BWS_ACCESS_TOKEN / OP_SERVICE_ACCOUNT_TOKEN) lives in .env\n# (or your shell / desktop session); everything else rotates centrally.\n# Failures never block startup — Hermes warns once and continues with\n# whatever .env already had.\n#\n# Multiple sources can be enabled at once:\n# - \"mapped\" sources (explicit VAR -> ref bindings, e.g. 1Password's env:\n# map) beat \"bulk\" sources (whole-project dumps like Bitwarden BSM)\n# - within a shape, the first source to claim a var wins; later claims\n# are skipped with a startup warning (never a silent clobber)\n# - a source's override_existing lets it beat .env/shell values, but\n# never another secret source's claim\n# Docs: https://hermes-agent.nousresearch.com/docs/user-guide/secrets/\n#\n# secrets:\n# # Optional explicit ordering of enabled sources.\n# # sources: [onepassword, bitwarden]\n#\n# # ---- Bitwarden Secrets Manager (bws CLI) --------------------------------\n# bitwarden:\n# enabled: false\n# access_token_env: BWS_ACCESS_TOKEN # bootstrap token, sourced from .env\n# project_id: \"\" # UUID of the BSM project to sync\n# server_url: \"\" # \"\" = US Cloud; EU/self-hosted URL otherwise\n# cache_ttl_seconds: 300 # 0 disables fresh caching\n# encrypted_cache: # optional encrypted stale fallback\n# enabled: false\n# max_stale_seconds: 0 # 0 disables stale fallback\n# override_existing: true # BSM values win over existing env\n# auto_install: true # lazy-download bws into ~/.hermes/bin\n#\n# # ---- 1Password (op CLI) -------------------------------------------------\n# onepassword:\n# enabled: false\n# # Map env-var names to op:// secret references. Each is resolved with a\n# # single `op read` at startup.\n# env:\n# OPENAI_API_KEY: \"op://Private/OpenAI/api key\"\n# ANTHROPIC_API_KEY: \"op://Private/Anthropic/credential\"\n# account: \"\" # op --account shorthand; \"\" = default\n# service_account_token_env: OP_SERVICE_ACCOUNT_TOKEN # headless auth; unset = desktop session\n# binary_path: \"\" # \"\" = resolve op via PATH; else absolute path\n# cache_ttl_seconds: 300 # 0 disables BOTH cache layers\n# override_existing: true # resolved values win over existing env\n#\n# # ---- Command helper (any CLI vault) --------------------------------------\n# # Run a user-configured helper that prints KEY=VALUE lines on stdout —\n# # works with any secret store that has a CLI: keepassxc-cli, secret-tool,\n# # pass, gpg, or a script that cats a tmpfs env file. Composes with the\n# # sources above (enable any combination). POSIX-only (needs /bin/sh).\n# # The helper must be fast and NON-interactive (hard timeout, 1 MiB cap);\n# # its stderr is discarded so diagnostics can't leak secret material.\n# command:\n# enabled: false\n# command: \"cat /run/user/1000/hermes-secrets.env\"\n# helper_timeout_seconds: 3\n# override_existing: false # .env/shell win by default\n"}, "fa4800414cf7d6d28a535315a67858bfd6e30db3": {"t": 1785768015.5167303, "n": 81828, "h": "a134c8fd8e8ab2af", "c": "# Hermes Agent CLI Configuration\n# Copy this file to cli-config.yaml and customize as needed.\n# This file configures the CLI behavior. Environment variables in .env take precedence.\n\n# =============================================================================\n# Model Configuration\n# =============================================================================\nmodel:\n # Default model to use (can be overridden with --model flag)\n # Both \"default\" and \"model\" work as the key name here.\n default: \"anthropic/claude-opus-4.6\"\n \n # Inference provider selection:\n # \"auto\" - Auto-detect from credentials (default)\n # \"openrouter\" - OpenRouter (requires: OPENROUTER_API_KEY or OPENAI_API_KEY)\n # \"nous\" - Nous Portal OAuth (requires: hermes auth add nous)\n # \"nous-api\" - Nous Portal API key (requires: NOUS_API_KEY)\n # \"anthropic\" - Direct Anthropic API (requires: ANTHROPIC_API_KEY)\n # \"openai-codex\" - OpenAI Codex (requires: hermes auth)\n # \"copilot\" - GitHub Copilot / GitHub Models (requires: GITHUB_TOKEN)\n # \"gemini\" - Use Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n # \"zai\" - Use z.ai / ZhipuAI GLM models (requires: GLM_API_KEY)\n # \"kimi-coding\" - Kimi / Moonshot AI (requires: KIMI_API_KEY)\n # \"minimax\" - MiniMax global (requires: MINIMAX_API_KEY)\n # \"minimax-cn\" - MiniMax China (requires: MINIMAX_CN_API_KEY)\n # \"huggingface\" - Hugging Face Inference (requires: HF_TOKEN)\n # \"nvidia\" - NVIDIA NIM / build.nvidia.com (requires: NVIDIA_API_KEY)\n # \"xiaomi\" - Xiaomi MiMo (requires: XIAOMI_API_KEY)\n # \"arcee\" - Arcee AI Trinity models (requires: ARCEEAI_API_KEY)\n # \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY — https://ollama.com/settings)\n # \"deepinfra\" - DeepInfra (requires: DEEPINFRA_API_KEY)\n # \"kilocode\" - KiloCode gateway (requires: KILOCODE_API_KEY)\n # \"azure-foundry\" - Microsoft Foundry / Azure OpenAI (API key or Entra ID)\n # \"lmstudio\" - LM Studio local server (optional: LM_API_KEY, defaults to http://127.0.0.1:1234/v1)\n #\n # Local servers (LM Studio, Ollama, vLLM, llama.cpp):\n # \"custom\" - Any other OpenAI-compatible endpoint. Set base_url below.\n # Aliases: \"ollama\", \"vllm\", \"llamacpp\" all map to \"custom\".\n # LM Studio is first-class and uses provider: \"lmstudio\".\n # It works with both no-auth and auth-enabled server modes.\n #\n # Can also be overridden for a single invocation with the --provider flag.\n provider: \"auto\"\n \n # API configuration (falls back to OPENROUTER_API_KEY env var)\n # api_key: \"your-key-here\" # Uncomment to set here instead of .env\n base_url: \"https://openrouter.ai/api/v1\"\n\n # Azure Foundry keyless auth example:\n # provider: \"azure-foundry\"\n # base_url: \"https://<resource>.openai.azure.com/openai/v1\"\n # auth_mode: \"entra_id\" # DefaultAzureCredential: az login, managed identity, workload identity, etc.\n # default: \"gpt-4o\" # Deployment/model name\n # entra:\n # scope: \"https://ai.azure.com/.default\" # Optional; this is the default.\n\n # ── Token limits — two settings, easy to confuse ──────────────────────────\n #\n # context_length: TOTAL context window (input + output tokens combined).\n # Controls when Hermes compresses history and validates requests.\n # Leave unset — Hermes auto-detects the correct value from the provider.\n # Set manually only when auto-detection is wrong (e.g. a local server with\n # a custom num_ctx, or a proxy that doesn't expose /v1/models).\n #\n # context_length: 131072\n #\n # max_tokens: OUTPUT cap — maximum tokens the model may generate per response.\n # Unrelated to how long your conversation history can be.\n # The OpenAI-standard name \"max_tokens\" is a misnomer; Anthropic's native\n # API has since renamed it \"max_output_tokens\" for clarity.\n # Leave unset to use the model's native output ceiling (recommended).\n # Set only if you want to deliberately limit individual response length.\n #\n# max_tokens: 8192\n\n # ── Custom request headers (optional) ─────────────────────────────────────\n #\n # default_headers: extra HTTP headers sent on every request to an\n # OpenAI-compatible endpoint. User values take precedence over the\n # provider/SDK defaults, so this is the supported way to override the\n # OpenAI Python SDK's identifying headers (User-Agent: OpenAI/Python ...,\n # X-Stainless-*) when a custom provider sits behind a gateway/WAF that\n # rejects them — e.g. an upstream that returns \"502 Upstream access\n # forbidden\" for the SDK default User-Agent but accepts a plain one.\n # Applies on the OpenAI wire only (not native Anthropic / Bedrock).\n #\n # default_headers:\n # User-Agent: \"curl/8.7.1\"\n #\n # extra_headers: accepted as an alias of default_headers (merged, with\n # extra_headers winning when both are set) — matches the per-provider\n # extra_headers key below.\n #\n # Per-provider variant: named providers / custom_providers entries accept an\n # extra_headers dict scoped to that endpoint only — for reverse proxies,\n # gateways, or custom auth (e.g. Cloudflare Access service tokens).\n # Merged onto SDK/provider defaults with the entry's values winning.\n # Header values are treated as secrets and are never logged.\n #\n # providers:\n # my-proxy:\n # base_url: \"https://llm.internal.example.com/v1\"\n # key_env: \"MY_PROXY_API_KEY\"\n # extra_headers:\n # CF-Access-Client-Id: \"xxxx.access\"\n # CF-Access-Client-Secret: \"${CF_ACCESS_SECRET}\"\n # X-Client-Name: \"hermes-agent\"\n\n# Named provider overrides (optional)\n# Use this for per-provider request timeouts, non-stream stale timeouts,\n# and per-model exceptions.\n# Applies to the primary turn client on every api_mode (OpenAI-wire, native\n# Anthropic, and Anthropic-compatible providers), the fallback chain, and\n# client rebuilds during credential rotation. For OpenAI-wire chat\n# completions (streaming and non-streaming) the configured value is also\n# used as the per-request ``timeout=`` kwarg so it wins over the legacy\n# HERMES_API_TIMEOUT env var (which still applies when no config is set).\n# ``stale_timeout_seconds`` controls the non-streaming stale-call detector and\n# wins over the legacy HERMES_API_CALL_STALE_TIMEOUT env var. Leaving these\n# unset keeps the legacy defaults (HERMES_API_TIMEOUT=1800s,\n# HERMES_API_CALL_STALE_TIMEOUT=90s, native Anthropic 900s). The\n# implicit non-stream stale detector is auto-disabled for local endpoints\n# and can scale upward for very large contexts.\n#\n# Not currently wired for AWS Bedrock (bedrock_converse + AnthropicBedrock\n# SDK paths) — those use boto3 with its own timeout configuration.\n#\n# providers:\n# ollama-local:\n# request_timeout_seconds: 300 # Longer timeout for local cold-starts\n# stale_timeout_seconds: 900 # Explicitly re-enable stale detection on local endpoints\n# anthropic:\n# request_timeout_seconds: 30 # Fast-fail cloud requests\n# models:\n# claude-opus-4.6:\n# timeout_seconds: 600 # Longer timeout for extended-thinking Opus calls\n# openai-codex:\n# models:\n# gpt-5.4:\n# stale_timeout_seconds: 1800 # Longer non-stream stale timeout for slow large-context turns\n\n# =============================================================================\n# OpenRouter Provider Routing (only applies when using OpenRouter)\n# =============================================================================\n# Control how requests are routed across providers on OpenRouter.\n# See: https://openrouter.ai/docs/guides/routing/provider-selection\n#\n# provider_routing:\n# # Sort strategy: \"price\" (default), \"throughput\", or \"latency\"\n# # Append :nitro to model name for a shortcut to throughput sorting.\n# sort: \"throughput\"\n#\n# # Only allow these providers (provider slugs from OpenRouter)\n# # only: [\"anthropic\", \"google\"]\n#\n# # Skip these providers entirely\n# # ignore: [\"deepinfra\", \"fireworks\"]\n#\n# # Try providers in this order (overrides default load balancing)\n# # order: [\"anthropic\", \"google\", \"together\"]\n#\n# # Require providers to support all parameters in your request\n# # require_parameters: true\n#\n# # Data policy: \"allow\" (default) or \"deny\" to exclude providers that may store data\n# # data_collection: \"deny\"\n\n# =============================================================================\n# OpenRouter Response Caching (only applies when using OpenRouter)\n# =============================================================================\n# Cache identical API responses at the OpenRouter edge for free instant replays.\n# When enabled, identical requests (same model, messages, parameters) return\n# cached responses with zero billing. Separate from Anthropic prompt caching.\n# See: https://openrouter.ai/docs/guides/features/response-caching\n#\n# openrouter:\n# response_cache: true # Enable response caching (default: true)\n# response_cache_ttl: 300 # Cache TTL in seconds, 1-86400 (default: 300)\n\n# =============================================================================\n# Git Worktree Isolation\n# =============================================================================\n# When enabled, each CLI session creates an isolated git worktree so multiple\n# agents can work on the same repo concurrently without file collisions.\n# Equivalent to always passing --worktree / -w on the command line.\n#\n# worktree: true # Always create a worktree when in a git repo\n# worktree: false # Default — only create when -w flag is passed\n#\n# By default a new worktree branches from the freshly-fetched remote tip\n# (the current branch's upstream, else the remote's default branch) so it\n# starts current with the project instead of from the local clone's\n# (possibly stale) HEAD. Set worktree_sync: false to branch from local HEAD\n# instead — useful when offline or when you deliberately want the clone's\n# exact current state as the base.\n#\n# worktree_sync: true # Default — branch from the fetched remote tip\n# worktree_sync: false # Branch from local HEAD (offline / pinned base)\n\n# =============================================================================\n# Terminal Tool Configuration\n# =============================================================================\n# Choose ONE of the following terminal configurations by uncommenting it.\n# The terminal tool executes commands in the specified environment.\n\n# -----------------------------------------------------------------------------\n# OPTION 1: Local execution (default)\n# Commands run directly on your machine in the current directory\n# -----------------------------------------------------------------------------\n# Working directory behavior:\n# - CLI (`hermes` command): Uses \".\" (current directory where you run hermes)\n# - Gateway/messaging/cron: Uses terminal.cwd here; legacy .env cwd values are deprecated\nterminal:\n backend: \"local\"\n cwd: \".\" # For local backend: \".\" = current directory. Ignored for remote backends unless a backend documents otherwise.\n timeout: 180\n # HOME policy for tool subprocesses:\n # auto - default: host uses your real HOME; containers use HERMES_HOME/home\n # real - force your real OS-user HOME\n # profile - force HERMES_HOME/home for strict per-profile CLI config isolation\n home_mode: \"auto\"\n docker_mount_cwd_to_workspace: false # SECURITY: off by default. Opt in to mount the launch cwd into Docker /workspace.\n lifetime_seconds: 300\n # sudo_password: \"hunter2\" # Optional: pipe a sudo password via sudo -S. SECURITY WARNING: plaintext.\n # sudo_password: \"\" # Explicit empty password: try empty and never open the interactive sudo prompt.\n\n# -----------------------------------------------------------------------------\n# OPTION 2: SSH remote execution\n# Commands run on a remote server - agent code stays local (sandboxed)\n# Great for: keeping agent isolated from its own code, using powerful remote hardware\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"ssh\"\n# cwd: \"/home/myuser/project\" # Path on the REMOTE server\n# timeout: 180\n# lifetime_seconds: 300\n# ssh_host: \"my-server.example.com\"\n# ssh_user: \"myuser\"\n# ssh_port: 22\n# ssh_key: \"~/.ssh/id_rsa\" # Optional - uses ssh-agent if not specified\n\n# -----------------------------------------------------------------------------\n# OPTION 3: Docker container\n# Commands run in an isolated Docker container\n# Great for: reproducible environments, testing, isolation\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"docker\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /)\n# timeout: 180\n# lifetime_seconds: 300\n# docker_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# docker_mount_cwd_to_workspace: true # Explicit opt-in: mount your launch cwd into /workspace\n# # Optional: run the container as your host user's uid:gid so files written\n# # into bind-mounted dirs are owned by you, not root. Drops SETUID/SETGID\n# # caps too since no gosu privilege drop is needed. Leave off if your\n# # chosen docker_image expects to start as root.\n# docker_run_as_host_user: true\n# # Optional: explicitly forward selected env vars into Docker.\n# # These values come from your current shell first, then ~/.hermes/.env.\n# # Warning: anything forwarded here is visible to commands run in the container.\n# docker_forward_env:\n# - \"GITHUB_TOKEN\"\n# - \"NPM_TOKEN\"\n# # Optional: extra flags passed verbatim to docker run (appended after security defaults).\n# # Useful for adding capabilities (e.g. apt installs needing SETUID) or custom options.\n# # Example: add a Linux capability not included by default\n# # docker_extra_args:\n# # - \"--cap-add\"\n# # - \"SETUID\"\n\n# -----------------------------------------------------------------------------\n# OPTION 4: Singularity/Apptainer container\n# Commands run in a Singularity container (common in HPC environments)\n# Great for: HPC clusters, shared compute environments\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"singularity\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# singularity_image: \"docker://nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 5: Modal cloud execution\n# Commands run on Modal's cloud infrastructure\n# Great for: GPU access, scalable compute, serverless execution\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"modal\"\n# cwd: \"/workspace\" # Path INSIDE the sandbox (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# modal_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 6: Daytona cloud execution\n# Commands run in Daytona cloud sandboxes\n# Great for: Cloud dev environments, persistent workspaces, team collaboration\n# Requires: pip install daytona, DAYTONA_API_KEY env var\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"daytona\"\n# cwd: \"~\"\n# timeout: 180\n# lifetime_seconds: 300\n# daytona_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# container_disk: 10240 # Daytona max is 10GB per sandbox\n\n#\n# --- Container resource limits (docker, singularity, modal, daytona -- ignored for local/ssh) ---\n# These settings apply to all container backends. They control the resources\n# allocated to the sandbox and whether its filesystem persists across sessions.\n container_cpu: 1 # CPU cores\n container_memory: 5120 # Memory in MB (5120 = 5GB)\n container_disk: 51200 # Disk in MB (51200 = 50GB)\n container_persistent: true # Persist filesystem across sessions (false = ephemeral)\n\n# -----------------------------------------------------------------------------\n# SUDO SUPPORT (works with ALL backends above)\n# -----------------------------------------------------------------------------\n# Add sudo_password to any terminal config above to enable sudo commands.\n# The password is piped via `sudo -S`. Works with local, ssh, docker, etc.\n#\n# SECURITY WARNING: Password stored in plaintext!\n#\n# INTERACTIVE PROMPT: If sudo_password is unset and the CLI is running,\n# you'll be prompted to enter your password when sudo is needed:\n# - 45-second timeout (auto-skips if no input)\n# - Press Enter to skip (command fails gracefully)\n# - Password is hidden while typing\n# - Password is cached for the session\n#\n# EMPTY PASSWORDS: Setting sudo_password to an explicit empty string is different\n# from leaving it unset. Hermes will try an empty password via `sudo -S` and\n# will not open the interactive prompt. This is useful for passwordless sudo,\n# Touch ID sudo setups, and environments where prompting is just noise.\n#\n# ALTERNATIVES:\n# - SSH backend: Configure passwordless sudo on the remote server\n# - Containers: Run as root inside the container (no sudo needed)\n# - Local: Configure /etc/sudoers for specific commands\n#\n# Example (add to your terminal section):\n# sudo_password: \"your-password-here\"\n\n# =============================================================================\n# Security Scanning (tirith)\n# =============================================================================\n# Optional pre-exec command security scanning via tirith.\n# Detects homograph URLs, pipe-to-shell, terminal injection, env manipulation.\n# Install: brew install sheeki03/tap/tirith\n# Docs: https://github.com/sheeki03/tirith\n#\n# security:\n# tirith_enabled: true # Enable/disable tirith scanning\n# tirith_path: \"tirith\" # Path to tirith binary (supports ~ expansion)\n# tirith_timeout: 5 # Scan timeout in seconds\n# tirith_fail_open: true # Allow commands if tirith unavailable\n\n# =============================================================================\n# Browser Tool Configuration\n# =============================================================================\nbrowser:\n # Inactivity timeout in seconds - browser sessions are automatically closed\n # after this period of no activity between agent loops (default: 120 = 2 minutes)\n inactivity_timeout: 120\n\n# =============================================================================\n# Tool Loop Guardrails\n# =============================================================================\n# Soft warnings are enabled by default. They append guidance to repeated failed\n# or non-progressing tool results but still let the tool execute. Hard stops are\n# opt-in circuit breakers for autonomous/cron sessions where stopping a loop is\n# preferable to spending the full iteration budget.\ntool_loop_guardrails:\n warnings_enabled: true\n hard_stop_enabled: false\n warn_after:\n exact_failure: 2\n same_tool_failure: 3\n idempotent_no_progress: 2\n hard_stop_after:\n exact_failure: 5\n same_tool_failure: 8\n idempotent_no_progress: 5\n\n# =============================================================================\n# Context Compression (Auto-shrinks long conversations)\n# =============================================================================\n# When conversation approaches model's context limit, middle turns are\n# automatically summarized to free up space while preserving important context.\n#\n# HOW IT WORKS:\n# 1. Tracks actual token usage from API responses (not estimates)\n# 2. When prompt_tokens >= threshold% of model's context_length, triggers compression\n# 3. Protects first 3 turns (system prompt, initial request, first response)\n# 4. Protects last N turns (default 20 messages = ~10 full turns of recent context)\n# 5. Summarizes middle turns using a fast/cheap model\n# 6. Inserts summary as a user message, continues conversation seamlessly\n#\n# Post-compression tail budget is target_ratio × threshold × context_length:\n# 200K context, threshold 0.50, ratio 0.20 → 20K tokens of recent tail preserved\n# 1M context, threshold 0.50, ratio 0.20 → 100K tokens of recent tail preserved\n#\ncompression:\n # Enable automatic context compression (default: true)\n # Set to false if you prefer to manage context manually or want errors on overflow\n enabled: true\n \n # Trigger compression at this % of model's context limit (default: 0.50 = 50%)\n # Lower values = more aggressive compression, higher values = compress later\n # Models with context windows below 512K are floored at 0.75 (raise-only) so\n # compaction doesn't fire with half the window still free; set above 0.75 to override.\n threshold: 0.50\n\n # Per-model threshold overrides: keys are substring-matched against the model\n # name (longest match wins). Useful when some models need different compaction\n # points — e.g. a 1M-context model can compress later (0.30) while a 128K\n # model needs to compress earlier (0.60). The small-context floor (75% for\n # <512K models) still applies on top of per-model overrides.\n # model_thresholds:\n # \"glm-5.2\": 0.40\n # \"claude-sonnet\": 0.35\n # \"gpt-5\": 0.30\n\n # Optional absolute token cap for the compression trigger (default: null = disabled).\n # When set, compression fires at the LOWER of the ratio-based threshold and this\n # absolute token count — first-fires-wins. It never fires later than this count\n # regardless of which model is active (useful when switching between models with\n # very different context windows). Clamped to the model's context length at\n # apply-time, so a cap above the window is a no-op (ratio-based threshold wins).\n # Survives model switches and fallback activations.\n # threshold_tokens: 200000\n\n # Existing Codex gpt-5.5 behavior: raise Hermes' compaction trigger to 85%\n # for the ChatGPT Codex OAuth route. Set false to opt back down to threshold.\n codex_gpt55_autoraise: true\n\n # Fraction of the threshold to preserve as recent tail (default: 0.20 = 20%)\n # e.g. 20% of 50% threshold = 10% of total context kept as recent messages.\n # Summary output is separately capped at 12K tokens (Gemini output limit).\n # Range: 0.10 - 0.80\n target_ratio: 0.20\n\n # Number of most-recent messages to always preserve (default: 20 ≈ 10 full turns)\n # Higher values keep more recent conversation intact at the cost of more aggressive\n # compression of older turns.\n protect_last_n: 20\n\n # Compression retry rounds before a turn gives up with \"max compression\n # attempts reached\" (default: 3, same as the previous hardcoded value).\n # Raise (e.g. 6) for tool-schema-heavy sessions where 3 rounds cannot bring\n # the request estimate under the threshold. Validated >= 1, hard cap 10.\n max_attempts: 3\n\n # Codex app-server (codex CLI runtime) thread-compaction mode. The codex\n # agent owns the real thread context on this runtime, so Hermes' summarizer\n # cannot shrink it — compaction goes through the app server instead.\n # native = let Codex decide when to compact its own thread (default)\n # hermes = let Hermes threshold trigger Codex thread/compact/start\n # off = Hermes will not auto-trigger compaction; Codex may still compact natively\n codex_app_server_auto: native\n\n # Number of non-system messages to protect at the head of the transcript, in\n # ADDITION to the system prompt (which is always implicitly protected).\n # Head messages are NEVER summarized — they survive every compression\n # indefinitely. This gives stable early context for short/medium sessions,\n # but in long-running sessions that rely on rolling compaction the pinned\n # opening turns may not match how you want the session framed over time.\n # Set to 0 to preserve ONLY the system prompt (plus the rolling summary\n # and recent tail) — the cleanest configuration for long-running sessions.\n # Default 3 preserves the system prompt plus the first three non-system\n # head messages, matching the pre-feature behaviour.\n protect_first_n: 3\n\n # Idle compaction (default: 0 = disabled). When > 0, a session that resumes\n # after at least this many seconds of inactivity compacts its accumulated\n # history up front, before the first reply, so a long-lived thread you come\n # back to later doesn't re-read its full stale context on every turn.\n # Time-based, so it complements (does not replace) the size-based `threshold`\n # above. It is skipped when the context is already small (at or below the\n # post-compression target = threshold × target_ratio), so it never wastes a\n # summarization on a short idle thread. Example: 1800 = compact after 30 min idle.\n idle_compact_after_seconds: 0\n\n # Proactive tool-result prune (default: 0 = disabled). Opt-in token trigger\n # for a deterministic, no-LLM prune of OLD tool-result payloads, run\n # independently of `threshold` above. On large-window models (512K/1M) the\n # ratio threshold rarely fires, so bulky tool outputs (terminal dumps, file\n # reads, web extracts) ride along in history and get re-billed every turn.\n # When re-sent history exceeds this many tokens, the prune dedupes identical\n # results, summarizes older oversized ones, and truncates large tool-call\n # arguments — protecting the most recent `protect_last_n` messages and never\n # calling the model. Try 48000 to enable. Built-in compressor engine only;\n # other context engines inherit a safe no-op.\n # NOTE: a committed prune rewrites already-sent history, which invalidates\n # the provider's prompt-cache prefix — the min_reclaim gate below keeps\n # those cache breaks episodic (like a compression boundary) instead of\n # per-turn.\n proactive_prune_tokens: 0\n\n # The prune's summarize pass only touches tool results larger than this many\n # characters (clamped to >= 200 so a generated summary can't be\n # re-summarized). Default 8000.\n proactive_prune_min_result_chars: 8000\n\n # A proactive prune only COMMITS when it reclaims at least this many tokens\n # (measured on the pruned output). This is the prompt-cache hysteresis gate:\n # one meaningful, amortized cache break per batch of stale tool output\n # instead of a tiny break on every tool iteration. 0 = commit any non-zero\n # prune. Default 4096.\n proactive_prune_min_reclaim_tokens: 4096\n\n # To pin a specific model/provider for compression summaries, use the\n # auxiliary section below (auxiliary.compression.provider / model).\n\n# =============================================================================\n# Anthropic prompt caching TTL\n# =============================================================================\n# When prompt caching is active (Claude via OpenRouter or native Anthropic),\n# Anthropic supports two TTL tiers for cached prefixes: \"5m\" (default) and\n# \"1h\". Other values are ignored and \"5m\" is used.\n#\nprompt_caching:\n cache_ttl: \"5m\" # use \"1h\" for long sessions with pauses between turns\n\n# =============================================================================\n# Auxiliary Models (Advanced — Experimental)\n# =============================================================================\n# Hermes uses lightweight \"auxiliary\" models for side tasks: image analysis,\n# browser screenshot analysis, web page summarization, TTS audio-tag insertion,\n# and context compression.\n#\n# By default these use Gemini Flash via OpenRouter or Nous Portal and are\n# auto-detected from your credentials. You do NOT need to change anything\n# here for normal usage.\n#\n# WARNING: Overriding these with providers other than OpenRouter or Nous Portal\n# is EXPERIMENTAL and may not work. Not all models/providers support vision,\n# produce usable summaries, or accept the same API format. Change at your own\n# risk — if things break, reset to \"auto\" / empty values.\n#\n# Each task has its own provider + model pair so you can mix providers.\n# For example: OpenRouter for vision (needs multimodal), but your main\n# local endpoint for compression (just needs text).\n#\n# Provider options:\n# \"auto\" - Best available: OpenRouter → Nous Portal → main endpoint (default)\n# \"openrouter\" - Force OpenRouter (requires OPENROUTER_API_KEY)\n# \"nous\" - Force Nous Portal (requires: hermes auth add nous)\n# \"gemini\" - Force Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n# \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY)\n# \"codex\" - Force Codex OAuth (requires: hermes model → Codex).\n# Uses gpt-5.3-codex which supports vision.\n# \"main\" - Use your custom endpoint (OPENAI_BASE_URL + OPENAI_API_KEY).\n# Works with OpenAI API, local models, or any OpenAI-compatible\n# endpoint. Also falls back to Codex OAuth and API-key providers.\n#\n# Model: leave empty to use the provider's default. When empty, OpenRouter\n# uses \"google/gemini-3-flash-preview\" and Nous uses \"gemini-3-flash\".\n# Other providers pick a sensible default automatically.\n#\n# auxiliary:\n# # Image analysis: vision_analyze tool + browser screenshots\n# vision:\n# provider: \"auto\"\n# model: \"\" # e.g. \"google/gemini-2.5-flash\", \"openai/gpt-4o\"\n# timeout: 30 # LLM API call timeout (seconds)\n# download_timeout: 30 # Image HTTP download timeout (seconds)\n# # Increase for slow connections or self-hosted image servers\n# reasoning_effort: \"\" # Per-task thinking level: none, minimal, low, medium,\n# # high, xhigh, max, ultra. Empty = provider default.\n# # Works on every auxiliary task block (vision,\n# # web_extract, compression, title_generation, curator,\n# # background_review, moa_reference, ...). Example: run\n# # compression at \"low\" and vision at \"none\" to cut\n# # side-task latency/cost on reasoning models.\n#\n# # Web page scraping / summarization + browser page text extraction\n# web_extract:\n# provider: \"auto\"\n# model: \"\"\n# reasoning_effort: \"low\"\n#\n# # Gemini 3.1 TTS hidden audio-tag insertion\n# tts_audio_tags:\n# provider: \"auto\" # empty model = your main chat model\n# model: \"\"\n# timeout: 30\n#\n# # Automatic session title generation after the first exchange\n# title_generation:\n# enabled: true # set false to disable auto-title generation\n# provider: \"auto\"\n# model: \"\"\n# timeout: 30\n# language: \"\" # empty = match the user's language; or e.g. \"English\"\n#\n# # Session search — summarizes matching past sessions\n# session_search:\n# provider: \"auto\"\n# model: \"\"\n# timeout: 30\n# max_concurrency: 3 # Limit parallel summaries to reduce request-burst 429s\n# extra_body: {} # Provider-specific OpenAI-compatible request fields\n# # Example for providers that support request-body\n# # reasoning controls:\n# # extra_body:\n# # enable_thinking: false\n# # Some vLLM/Qwen deployments expect this nested:\n# # extra_body:\n# # chat_template_kwargs:\n# # enable_thinking: false\n\n# =============================================================================\n# Persistent Memory\n# =============================================================================\n# Bounded curated memory injected into the system prompt every session.\n# Two stores: MEMORY.md (agent's notes) and USER.md (user profile).\n# Character limits keep the memory small and focused. The agent manages\n# pruning -- when at the limit, it must consolidate or replace entries.\n# Disabled by default in batch_runner.\n#\nmemory:\n # Agent's personal notes: environment facts, conventions, things learned\n memory_enabled: true\n \n # User profile: preferences, communication style, expectations\n user_profile_enabled: true\n \n # Character limits (~2.75 chars per token, model-independent)\n memory_char_limit: 2200 # ~800 tokens\n user_char_limit: 1375 # ~500 tokens\n\n # Periodic memory nudge: remind the agent to consider saving memories\n # every N user turns. Set to 0 to disable. Only active when memory is enabled.\n nudge_interval: 10 # Nudge every 10 user turns (0 = disabled)\n\n # Memory flush: give the agent one turn to save memories before context is\n # lost (compression, /new, /reset, exit). Set to 0 to disable.\n # For exit/reset, only fires if the session had at least this many user turns.\n flush_min_turns: 6 # Min user turns to trigger flush on exit/reset (0 = disabled)\n\n# =============================================================================\n# Session Reset Policy (Messaging Platforms)\n# =============================================================================\n# Controls when messaging sessions (Telegram, Discord, WhatsApp, Slack) are\n# automatically cleared. Default is \"none\": sessions never auto-reset —\n# conversation context lives until you /reset or /new manually, or context\n# compression kicks in. Opt in to automatic resets if you prefer sessions to\n# clear on a schedule (long-lived context increases API cost per message,\n# though prompt caching and compression keep this manageable).\n#\n# When an automatic reset triggers, the agent first saves important\n# information to its persistent memory — but the conversation context is\n# wiped. The agent starts fresh but retains learned facts via its memory\n# system.\n#\n# Modes:\n# \"none\" - Never auto-reset (default); context lives until /reset or compression\n# \"idle\" - Reset after N minutes of inactivity\n# \"daily\" - Reset at a fixed hour each day\n# \"both\" - Reset on EITHER inactivity timeout or daily boundary\n#\nsession_reset:\n mode: none # \"none\", \"idle\", \"daily\", or \"both\"\n idle_minutes: 1440 # Inactivity timeout in minutes (used by \"idle\"/\"both\")\n at_hour: 4 # Daily reset hour, 0-23 local time (used by \"daily\"/\"both\")\n\n# Maximum number of simultaneously active chat sessions across CLI, TUI,\n# dashboard chat, and messaging gateway. Set to null, 0, or omit to allow\n# unlimited concurrent sessions. When the limit is reached, new sessions get a\n# clean error while existing active sessions keep their normal behavior. This\n# top-level key takes precedence over gateway.max_concurrent_sessions. The cap\n# is a best-effort single-host/profile runtime guard; Hermes fails open if the\n# local runtime lease registry cannot be read or locked.\nmax_concurrent_sessions: null\n\n# When true, group/channel chats use one session per participant when the platform\n# provides a user ID. This is the secure default and prevents users in the same\n# room from sharing context, interrupts, and token costs. Set false only if you\n# explicitly want one shared \"room brain\" per group/channel.\ngroup_sessions_per_user: true\n\n# ─────────────────────────────────────────────────────────────────────────────\n# API Server — per-client model routing\n# ─────────────────────────────────────────────────────────────────────────────\n# Route different API clients to different models/providers on a single\n# Hermes deployment. Clients choose a backend by sending a specific string\n# as the OpenAI ``model`` field. Unmapped model values fall back to the\n# global model configured in the ``model:`` section above, and an explicit\n# session /model override always wins over a route.\n#\n# Configure via the ``platforms.api_server.extra.model_routes`` gateway\n# config block:\n#\n# platforms:\n# api_server:\n# enabled: true\n# extra:\n# key: \"your-api-server-secret\"\n# model_routes:\n# # Xiaozhi clients send model=\"minimax-m2\" → routed to MiniMax via OpenRouter\n# minimax-m2:\n# model: \"minimax/minimax-m1\"\n# provider: \"openrouter\" # optional — overrides global provider\n# # api_key: \"sk-...\" # optional — per-route UPSTREAM provider\n# # key (NOT caller auth; never logged)\n# # base_url: \"https://...\" # optional — per-route base URL\n# # GPT clients keep their own alias\n# gpt-5:\n# model: \"openai/gpt-5\"\n# provider: \"openrouter\"\n#\n# Configured aliases are automatically listed by GET /v1/models so clients\n# can discover them without manual coordination. Caller authentication is\n# unchanged: every request still authenticates with the global API server\n# key (``extra.key`` / API_SERVER_KEY).\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Gateway Streaming\n# ─────────────────────────────────────────────────────────────────────────────\n# Stream tokens to messaging platforms in real-time. The bot sends a message\n# on first token, then progressively edits it as more tokens arrive.\n# Disabled by default — enable to try the streaming UX on Telegram/Discord/Slack.\n# For Telegram, partial edits are sent as plain text and only the final edit uses MarkdownV2.\nstreaming:\n enabled: false\n # transport: edit # \"edit\" = progressive editMessageText\n # edit_interval: 0.3 # seconds between message edits\n # buffer_threshold: 40 # chars before forcing an edit flush\n # cursor: \" ▉\" # cursor shown during streaming\n\n# =============================================================================\n# Skills Configuration\n# =============================================================================\n# Skills are reusable procedures the agent can load and follow. The agent can\n# also create new skills after completing complex tasks.\n#\nskills:\n # Nudge the agent to create skills after complex tasks.\n # Every N tool-calling iterations, remind the model to consider saving a skill.\n # Set to 0 to disable.\n creation_nudge_interval: 15\n\n # External skill directories — share skills across tools/agents without\n # copying them into ~/.hermes/skills/. Each path is expanded (~ and ${VAR})\n # and resolved to an absolute path. External dirs are read-only: skill\n # creation always writes to ~/.hermes/skills/. Local skills take precedence\n # when names collide.\n # external_dirs:\n # - ~/.agents/skills\n # - /home/shared/team-skills\n\n# =============================================================================\n# Agent Behavior\n# =============================================================================\nagent:\n # Maximum tool-calling iterations per conversation\n # Higher = more room for complex tasks, but costs more tokens\n # Recommended: 20-30 for focused tasks, 50-100 for open exploration\n max_turns: 60\n\n # Inactivity timeout for gateway agent runs (seconds, 0 = unlimited).\n # The agent can run indefinitely when actively calling tools or receiving\n # API responses. Only fires after the agent has been idle for this duration.\n # gateway_timeout: 1800\n\n # Staged warning: send a warning before escalating to full timeout.\n # Fires once per run when inactivity reaches this threshold (seconds).\n # Set to 0 to disable the warning.\n # gateway_timeout_warning: 900\n\n # Graceful drain timeout for gateway stop/restart (seconds).\n # Default 0 = no drain: a restart interrupts in-flight agents immediately,\n # cleans up, and exits. Set a positive value only if you want a grace\n # window on /restart, and keep it well under systemd's TimeoutStopSec.\n # restart_drain_timeout: 0\n\n # Max app-level retry attempts for API errors (connection drops, provider\n # timeouts, 5xx, etc.) before the agent surfaces the failure. Lower this\n # to 1 if you use fallback providers and want fast failover on flaky\n # primaries (default 3). The OpenAI SDK does its own low-level retries\n # underneath this wrapper — this is the Hermes-level loop.\n # api_max_retries: 3\n\n # After the agent edits code without fresh passing verification, nudge it to\n # verify before finishing. The default \"auto\" enables it on interactive\n # coding surfaces (CLI, TUI, desktop) and programmatic callers, and disables\n # it on conversational messaging surfaces (Telegram, Discord, etc.) where the\n # verification summary would reach a human as chat noise. Set true or false to\n # force it on or off; the HERMES_VERIFY_ON_STOP env var (1/0) takes precedence.\n # verify_on_stop: auto\n\n # Standing operator instructions for the coding posture (when Hermes is in a\n # code workspace). Appended to the coding brief as an extra system block, so\n # you can pin project-wide workflow rules without editing the shipped brief.\n # Accepts a string or a list of strings. Takes effect next session.\n # coding_instructions:\n # - \"For UI work, don't run tsc/lint until I approve the look.\"\n # - \"Clean the diff before you commit and push.\"\n\n # When verify-on-stop finds edited code without fresh verification evidence,\n # append guidance for creative UI work (avoid broad tsc/lint/test before visual\n # approval) and clean-diff expectations. Set false to keep that nudge terse.\n # verify_guidance: true\n\n # A `pre_verify` hook (plugin or shell, see Event Hooks docs) can keep the\n # agent going one more turn to verify/clean before finishing. This caps how\n # many times one turn may be nudged to continue, so a hook can't trap the loop.\n # Default 3.\n # max_verify_nudges: 3\n\n # Enable verbose logging\n verbose: false\n \n # Reasoning effort level (OpenRouter and Nous Portal)\n # Controls how much \"thinking\" the model does before responding.\n # Options: \"xhigh\" (max), \"high\", \"medium\", \"low\", \"minimal\", \"none\" (disable)\n reasoning_effort: \"medium\"\n \n # Per-model reasoning effort overrides (optional dict)\n # Key: any sensible model spelling works (exact, dots↔dashes interchangeable,\n # provider prefix optional). First match wins.\n # Value: reasoning effort level (same options as reasoning_effort)\n # Override the global reasoning_effort for that specific model.\n # NOTE: no `hermes config set` support for this key -- edit YAML directly.\n # reasoning_overrides:\n # \"openrouter/anthropic/claude-opus-4.5\": \"xhigh\"\n # \"openai/gpt-5\": \"low\"\n # \"claude-opus-4.6\": \"high\" # bare model name also works\n # \"deepseek/deepseek-v4-pro\": \"xhigh\" # dots and dashes are interchangeable\n reasoning_overrides: {}\n \n # Predefined personalities (use with /personality command)\n personalities:\n helpful: \"You are a helpful, friendly AI assistant.\"\n concise: \"You are a concise assistant. Keep responses brief and to the point.\"\n technical: \"You are a technical expert. Provide detailed, accurate technical information.\"\n creative: \"You are a creative assistant. Think outside the box and offer innovative solutions.\"\n teacher: \"You are a patient teacher. Explain concepts clearly with examples.\"\n kawaii: \"You are a kawaii assistant! Use cute expressions like (◕‿◕), ★, ♪, and ~! Add sparkles and be super enthusiastic about everything! Every response should feel warm and adorable desu~! ヽ(>∀<☆)ノ\"\n catgirl: \"You are Neko-chan, an anime catgirl AI assistant, nya~! Add 'nya' and cat-like expressions to your speech. Use kaomoji like (=^・ω・^=) and ฅ^•ﻌ•^ฅ. Be playful and curious like a cat, nya~!\"\n pirate: \"Arrr! Ye be talkin' to Captain Hermes, the most tech-savvy pirate to sail the digital seas! Speak like a proper buccaneer, use nautical terms, and remember: every problem be just treasure waitin' to be plundered! Yo ho ho!\"\n shakespeare: \"Hark! Thou speakest with an assistant most versed in the bardic arts. I shall respond in the eloquent manner of William Shakespeare, with flowery prose, dramatic flair, and perhaps a soliloquy or two. What light through yonder terminal breaks?\"\n surfer: \"Duuude! You're chatting with the chillest AI on the web, bro! Everything's gonna be totally rad. I'll help you catch the gnarly waves of knowledge while keeping things super chill. Cowabunga! 🤙\"\n noir: \"The rain hammered against the terminal like regrets on a guilty conscience. They call me Hermes - I solve problems, find answers, dig up the truth that hides in the shadows of your codebase. In this city of silicon and secrets, everyone's got something to hide. What's your story, pal?\"\n uwu: \"hewwo! i'm your fwiendwy assistant uwu~ i wiww twy my best to hewp you! *nuzzles your code* OwO what's this? wet me take a wook! i pwomise to be vewy hewpful >w<\"\n philosopher: \"Greetings, seeker of wisdom. I am an assistant who contemplates the deeper meaning behind every query. Let us examine not just the 'how' but the 'why' of your questions. Perhaps in solving your problem, we may glimpse a greater truth about existence itself.\"\n hype: \"YOOO LET'S GOOOO!!! 🔥🔥🔥 I am SO PUMPED to help you today! Every question is AMAZING and we're gonna CRUSH IT together! This is gonna be LEGENDARY! ARE YOU READY?! LET'S DO THIS! 💪😤🚀\"\n\n# =============================================================================\n# Toolsets\n# =============================================================================\n# Control which tools the agent has access to.\n# Use `hermes tools` to interactively enable/disable tools per platform.\n\n# =============================================================================\n# Platform Toolsets (per-platform tool configuration)\n# =============================================================================\n# Override which toolsets are available on each platform.\n# If a platform isn't listed here, its built-in default is used.\n#\n# You can use EITHER:\n# - A preset like \"hermes-cli\" or \"hermes-telegram\" (curated tool set)\n# - A list of individual toolsets to compose your own (see list below)\n#\n# Supported platform keys: cli, telegram, discord, whatsapp, slack, qqbot, teams, google_chat\n#\n# Examples:\n#\n# # Use presets (same as defaults):\n# platform_toolsets:\n# cli: [hermes-cli]\n# telegram: [hermes-telegram]\n#\n# # Custom: give Telegram only web + terminal + file + planning:\n# platform_toolsets:\n# telegram: [web, terminal, file, todo]\n#\n# # Custom: CLI without browser or image gen:\n# platform_toolsets:\n# cli: [web, terminal, file, skills, todo, tts, cronjob]\n#\n# # Restrictive: Discord gets read-only tools only:\n# platform_toolsets:\n# discord: [web, vision, skills, todo]\n#\n# If not set, defaults are:\n# cli: hermes-cli (everything + cronjob management)\n# telegram: hermes-telegram (terminal, file, web, vision, image, tts, browser, skills, todo, cronjob, messaging)\n# discord: hermes-discord (same as telegram)\n# whatsapp: hermes-whatsapp (same as telegram)\n# slack: hermes-slack (same as telegram)\n# signal: hermes-signal (same as telegram)\n# homeassistant: hermes-homeassistant (same as telegram)\n# qqbot: hermes-qqbot (same as telegram)\n# teams: hermes-teams (same as telegram)\n# google_chat: hermes-google_chat (same as telegram)\n#\nplatform_toolsets:\n cli: [hermes-cli]\n telegram: [hermes-telegram]\n discord: [hermes-discord]\n whatsapp: [hermes-whatsapp]\n slack: [hermes-slack]\n signal: [hermes-signal]\n homeassistant: [hermes-homeassistant]\n qqbot: [hermes-qqbot]\n yuanbao: [hermes-yuanbao]\n teams: [hermes-teams]\n google_chat: [hermes-google_chat]\n\n# =============================================================================\n# Gateway Platform Settings\n# =============================================================================\n# Optional per-platform messaging settings.\n# Platform-specific knobs live under `extra`.\n#\n# platforms:\n# telegram:\n# reply_to_mode: \"first\" # off | first | all\n# # guest_mode lets explicit @mentions from non-allowlisted groups through.\n# # Default false; ordinary messages, replies, and regex wake words stay blocked.\n# guest_mode: false\n# # allowed_chats: [\"-1001234567890\"]\n# extra:\n# disable_link_previews: false # Set true to suppress Telegram URL previews in bot messages\n# 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\n# rich_drafts: false # Experimental rich draft previews during Telegram DM streaming; default false because Telegram Desktop/macOS can visually overlay draft frames\n# command_menu:\n# # Telegram allows up to 100 BotCommands; Hermes defaults to 60 so\n# # all built-in commands plus common skill commands stay visible\n# # while remaining under Telegram's payload-size limit. Clamped 1..100.\n# max_commands: 60\n# # prepend = user priority first, then Hermes defaults\n# # append = Hermes defaults first, then user priority\n# # replace = only the list below defines priority\n# priority_mode: prepend\n# priority:\n# - my_plugin_command\n# webhook:\n# extra:\n# # Route scripts default to a 30 second timeout. Scripts must live under\n# # the active profile's scripts directory and receive webhook JSON on stdin.\n# script_timeout_seconds: 30\n#\n# Discord-specific settings (config.yaml top-level, not under platforms:):\n#\n# discord:\n# require_mention: true # Require @mention in server channels (default: true)\n# auto_thread: true # Auto-create thread on @mention (default: true)\n# free_response_channels: \"\" # Channel IDs where no mention is needed\n# reactions: true # Show processing reactions (default: true)\n# history_backfill: true # Recover missed channel messages on mention (default: true)\n# history_backfill_limit: 50 # Max messages to scan backwards (default: 50)\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Available toolsets (use these names in platform_toolsets or the toolsets list)\n#\n# Run `hermes chat --list-toolsets` to see all toolsets and their tools.\n# Run `hermes chat --list-tools` to see every individual tool with descriptions.\n# ─────────────────────────────────────────────────────────────────────────────\n#\n# INDIVIDUAL TOOLSETS (compose your own):\n# web - web_search, web_extract\n# search - web_search only (no scraping)\n# terminal - terminal, process\n# file - read_file, write_file, patch, search\n# browser - browser_navigate, browser_snapshot, browser_click, browser_type,\n# browser_scroll, browser_back, browser_press,\n# browser_get_images, browser_vision (requires BROWSERBASE_API_KEY)\n# vision - vision_analyze (requires OPENROUTER_API_KEY)\n# image_gen - image_generate (requires FAL_KEY)\n# skills - skills_list, skill_view\n# skills_hub - skill_hub (search/install/manage from online registries — user-driven only)\n# todo - todo (in-memory task planning, no deps)\n# tts - text_to_speech (Edge TTS free, or ELEVENLABS/OPENAI/MINIMAX/MISTRAL key)\n# cronjob - cronjob (create/list/update/pause/resume/run/remove scheduled tasks)\n#\n# PRESETS (curated bundles):\n# hermes-cli - All of the above except send_message\n# hermes-telegram - terminal, file, web, vision, image_gen, tts, browser,\n# skills, todo, cronjob, send_message\n# hermes-discord - Same as hermes-telegram\n# hermes-whatsapp - Same as hermes-telegram\n# hermes-slack - Same as hermes-telegram\n#\n# COMPOSITE:\n# debugging - terminal + web + file\n# safe - web + vision (no terminal access)\n# all - Everything available\n#\n# web - Web search and content extraction (web_search, web_extract)\n# search - Web search only, no scraping (web_search)\n# terminal - Command execution and process management (terminal, process)\n# file - File operations: read, write, patch, search\n# browser - Full browser automation (navigate, click, type, screenshot, etc.)\n# vision - Image analysis (vision_analyze)\n# image_gen - Image generation with FLUX (image_generate)\n# skills - Load skill documents (skills_list, skill_view)\n# todo - Task planning and tracking for multi-step work\n# memory - Persistent memory across sessions (personal notes + user profile)\n# session_search - Search and recall past conversations (FTS5 + Gemini Flash summarization)\n# tts - Text-to-speech (Edge TTS free, ElevenLabs, OpenAI, MiniMax, Mistral)\n# cronjob - Schedule and manage automated tasks (CLI-only)\n#\n# Composite toolsets:\n# debugging - terminal + web + file (for troubleshooting)\n# safe - web + vision (no terminal access)\n\n# NOTE: The top-level \"toolsets\" key is deprecated and ignored.\n# Tool configuration is managed per-platform via platform_toolsets above.\n# Use `hermes tools` to configure interactively, or edit platform_toolsets directly.\n#\n# CLI override: hermes chat --toolsets terminal,web,file\n\n# =============================================================================\n# MCP (Model Context Protocol) Servers\n# =============================================================================\n# Connect to external MCP servers to add tools from the MCP ecosystem.\n# Each server's tools are automatically discovered and registered.\n# See website/docs/user-guide/features/mcp.md for full documentation.\n#\n# Stdio servers (spawn a subprocess):\n# command: the executable to run\n# args: command-line arguments\n# env: environment variables (only these + safe defaults passed to subprocess)\n#\n# HTTP servers (connect to a URL):\n# url: the MCP server endpoint\n# headers: HTTP headers (e.g., for authentication)\n#\n# Optional per-server settings:\n# timeout: tool call timeout in seconds (default: 120)\n# connect_timeout: initial connection timeout (default: 60)\n# keepalive_interval: liveness ping cadence in seconds (default: 180).\n# Lower it below the server's session TTL for servers that expire idle\n# sessions quickly (e.g. Unreal Engine editor MCP, ~15s), otherwise idle\n# tool calls hit an expired session and pay a slow reconnect. Floored at 5s.\n#\n# mcp_servers:\n# time:\n# command: uvx\n# args: [\"mcp-server-time\"]\n# filesystem:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-filesystem\", \"/home/user\"]\n# notion:\n# url: https://mcp.notion.com/mcp\n# github:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-github\"]\n# env:\n# GITHUB_PERSONAL_ACCESS_TOKEN: \"ghp_...\"\n#\n# Sampling (server-initiated LLM requests) — enabled by default.\n# Per-server config under the 'sampling' key:\n# analysis:\n# command: npx\n# args: [\"-y\", \"analysis-server\"]\n# sampling:\n# enabled: true # default: true\n# model: \"gemini-3-flash\" # override model (optional)\n# max_tokens_cap: 4096 # max tokens per request\n# timeout: 30 # LLM call timeout (seconds)\n# max_rpm: 10 # max requests per minute\n# allowed_models: [] # model whitelist (empty = all)\n# max_tool_rounds: 5 # tool loop limit (0 = disable)\n# log_level: \"info\" # audit verbosity\n\n# =============================================================================\n# Text-to-Speech\n# =============================================================================\n# TTS defaults to Edge TTS unless changed in ~/.hermes/config.yaml.\n# Gemini TTS supports persona/director prompt files, and Gemini 3.1 Flash TTS\n# can use a hidden auxiliary rewrite pass to insert expressive square-bracket\n# audio tags into the TTS script without showing tags in chat.\n#\n# tts:\n# provider: \"gemini\"\n# gemini:\n# model: \"gemini-3.1-flash-tts-preview\"\n# voice: \"Kore\"\n# audio_tags: false\n# persona_prompt_file: \"\" # e.g. ~/.hermes/tts/radio-host.md\n\n# =============================================================================\n# Voice Transcription (Speech-to-Text)\n# =============================================================================\n# Automatically transcribe voice messages on messaging platforms.\n# Providers: local (free, faster-whisper) | groq (free tier) | openai (Whisper API) | mistral (Voxtral Transcribe)\n# Set the corresponding API key in .env: GROQ_API_KEY, OPENAI_API_KEY, or MISTRAL_API_KEY.\nstt:\n enabled: true\n # provider: \"local\" # auto-detected if omitted\n local:\n model: \"base\" # tiny | base | small | medium | large-v3 | turbo\n # language: \"\" # auto-detect; set to \"en\", \"es\", \"fr\", etc. to force\n openai:\n model: \"whisper-1\" # whisper-1 | gpt-4o-mini-transcribe | gpt-4o-transcribe\n # mistral:\n # model: \"voxtral-mini-latest\" # voxtral-mini-latest | voxtral-mini-2602\n # deepinfra:\n # # Model id is discovered live from the DeepInfra catalog filtered\n # # by the `stt` surface tag — leave `model` blank to take the first\n # # live result. Pin only when you need a specific Whisper variant.\n # model: \"\"\n\n# Text-to-speech. Only the deepinfra block is documented here — the\n# remaining providers (edge, openai, xai, minimax, mistral, gemini,\n# elevenlabs, neutts, kittentts, piper) inherit sensible defaults from\n# DEFAULT_CONFIG in hermes_cli/config.py.\n# tts:\n# provider: \"deepinfra\"\n# deepinfra:\n# # Model id is discovered live from the DeepInfra catalog filtered\n# # by the `tts` surface tag — leave `model` blank to take the first\n# # live result.\n# model: \"\"\n# voice: \"default\"\n\n# Image generation. Each provider plugin reads its own ``image_gen.<name>``\n# block; deepinfra discovers models live from\n# api.deepinfra.com/v1/openai/models filtered by the ``image-gen`` tag —\n# no model id is hardcoded, so retired models disappear automatically.\n# image_gen:\n# provider: \"deepinfra\"\n# deepinfra:\n# # Leave `model` blank for the first live `image-gen`-tagged result.\n# model: \"\"\n\n# =============================================================================\n# Response Pacing (Messaging Platforms)\n# =============================================================================\n# Add human-like delays between message chunks.\n# human_delay:\n# mode: \"off\" # \"off\" | \"natural\" | \"custom\"\n# min_ms: 800 # Min delay (custom mode only)\n# max_ms: 2500 # Max delay (custom mode only)\n\n# =============================================================================\n# Session Logging\n# =============================================================================\n# Session trajectories are automatically saved to logs/ directory.\n# Each session creates: logs/session_YYYYMMDD_HHMMSS_UUID.json\n#\n# The session ID is displayed in the welcome banner for easy reference.\n# Logs contain full conversation history in trajectory format:\n# - System prompt, user messages, assistant responses\n# - Tool calls with inputs/outputs\n# - Timestamps for debugging\n#\n# No configuration needed - logging is always enabled.\n# To disable, you would need to modify the source code.\n\n# =============================================================================\n# Code Execution Sandbox (Programmatic Tool Calling)\n# =============================================================================\n# The execute_code tool runs Python scripts that call Hermes tools via RPC.\n# Intermediate tool results stay out of the LLM's context window.\ncode_execution:\n timeout: 300 # Max seconds per script before kill (default: 300 = 5 min)\n max_tool_calls: 50 # Max RPC tool calls per execution (default: 50)\n\n# =============================================================================\n# Subagent Delegation\n# =============================================================================\n# The delegate_task tool spawns child agents with isolated context.\n# Supports single tasks and batch mode (default 3 parallel, configurable).\ndelegation:\n max_iterations: 50 # Max tool-calling turns per child (default: 50)\n # max_concurrent_children: 3 # Max parallel child agents per batch (default: 3, floor: 1, no ceiling).\n # WARNING: values above 10 multiply API cost linearly.\n # max_spawn_depth: 1 # Delegation tree depth cap (range: 1-3, default: 1 = flat).\n # Raise to 2 to allow workers to spawn their own subagents.\n # Requires role=\"orchestrator\" on intermediate agents.\n # orchestrator_enabled: true # Kill switch for role=\"orchestrator\" children (default: true).\n # subagent_auto_approve: false # When a subagent hits a dangerous-command approval prompt, auto-deny (default: false)\n # or auto-approve \"once\" (true) instead of blocking on stdin.\n # The parent TUI owns stdin, so blocking would deadlock; non-interactive resolution is required.\n # Both choices emit a logger.warning audit line. Flip to true only for cron/batch pipelines.\n # inherit_mcp_toolsets: true # When explicit child toolsets are narrowed, also keep the parent's MCP toolsets (default: true). Set false for strict intersection.\n # model: \"google/gemini-3-flash-preview\" # Override model for subagents (empty = inherit parent)\n # provider: \"openrouter\" # Override provider for subagents (empty = inherit parent)\n # # Resolves full credentials (base_url, api_key) automatically.\n # # Supported: openrouter, nous, zai, kimi-coding, minimax\n\n# =============================================================================\n# Honcho Integration (Cross-Session User Modeling)\n# =============================================================================\n# AI-native persistent memory via Honcho (https://honcho.dev/).\n# Builds a deeper understanding of the user across sessions and tools.\n# Runs alongside USER.md — additive, not a replacement.\n#\n# Requires: pip install honcho-ai\n# Config: ~/.honcho/config.json (shared with Claude Code, Cursor, etc.)\n# API key: HONCHO_API_KEY in ~/.hermes/.env or ~/.honcho/config.json\n#\n# Hermes-specific overrides (optional — most config comes from ~/.honcho/config.json):\n# honcho: {}\n\n# =============================================================================\n# Display\n# =============================================================================\ndisplay:\n # Use compact banner mode (hides the ASCII-art banner, shows a single line).\n # true: Compact single-line banner\n # false: Full ASCII banner with tool/skill summary (default)\n compact: false\n\n # Tool progress display level (CLI and gateway)\n # off: Silent — no tool activity shown, just the final response\n # new: Show a tool indicator only when the tool changes (skip repeats)\n # all: Show every tool call with a short preview (default)\n # verbose: Full args, results, and debug logs (same as /verbose)\n # log: Silent in chat; append every tool call to ~/.hermes/logs/tool_calls.log (gateway only)\n # Toggle at runtime with /verbose in the CLI\n tool_progress: all\n\n # Per-platform defaults can be quieter than the global setting. Telegram\n # tunes for mobile: tool_progress and busy_ack_detail default off (no\n # per-tool breadcrumb stream, no \"iteration 21/60\" debug detail in busy\n # acks or heartbeats), but interim_assistant_messages and\n # long_running_notifications STAY ON so the user has real signal between\n # turn start and final answer (mid-turn assistant commentary + a single\n # edit-in-place \"⏳ Working — N min\" heartbeat). Override under\n # display.platforms.telegram.\n\n # Auto-cleanup of temporary progress bubbles after the final response lands.\n # On platforms that support message deletion (currently Telegram), this\n # removes the tool-progress bubble, \"⏳ Still working...\" notices, and\n # context-pressure status messages once the final reply has been delivered —\n # keeping long-running turns visible live, then tidy afterward. Failed runs\n # leave the bubbles in place as breadcrumbs. Off by default.\n # Per-platform override: display.platforms.telegram.cleanup_progress\n # true: Delete tracked progress/status bubbles on successful turn\n # false: Leave everything in place (default)\n # Example:\n # display:\n # platforms:\n # telegram:\n # cleanup_progress: true\n cleanup_progress: false\n\n # Natural mid-turn assistant updates.\n # On gateway platforms, when true, completed assistant status messages are\n # sent as separate chat messages. On the Desktop app, when true, mid-turn\n # assistant narration streamed between tool calls is kept in the transcript\n # instead of the bubble collapsing to only the final message on completion.\n # Independent of tool_progress and gateway streaming.\n # true: Keep/send mid-turn assistant updates (default)\n # false: Only keep/send the final response\n interim_assistant_messages: true\n\n # Gateway-only long-running status heartbeats.\n # When false, the platform does not receive periodic \"⏳ Working — N min\"\n # notifications even if agent.gateway_notify_interval is non-zero. The\n # heartbeat edits a single message in place (where the adapter supports\n # editing) instead of posting a new bubble each interval.\n # Default: true everywhere, including Telegram (silent agents are worse\n # than a single edit-in-place heartbeat).\n long_running_notifications: true\n\n # Include detailed iteration/tool/status context in busy acknowledgments\n # and long-running heartbeats. When true, busy acks show \"iteration 21/60,\n # terminal, 10 min\" and the heartbeat shows \"⏳ Working — 12 min,\n # iteration 21/60, terminal\". When false (Telegram default), both stay\n # terse: \"Interrupting current task\" and \"⏳ Working — 12 min, terminal\".\n busy_ack_detail: true\n\n # What Enter does when Hermes is already busy (CLI and gateway platforms).\n # interrupt: Interrupt the current run and redirect Hermes (default)\n # queue: Queue your message for the next turn\n # steer: Inject your message mid-run via /steer, arriving at the agent\n # after the next tool call — no interrupt, no role violation.\n # Falls back to 'queue' if the agent isn't running yet or if\n # images are attached (steer only carries text).\n # Ctrl+C (or /stop in gateway) always interrupts regardless of this setting.\n # Toggle at runtime with /busy <interrupt|queue|steer>.\n busy_input_mode: interrupt\n\n # Background process notifications (gateway/messaging only).\n # Controls how chatty the process watcher is when you use\n # terminal(background=true, notify_on_complete=true) from Telegram/Discord/etc.\n # off: No watcher messages at all\n # result: Only the final completion message\n # error: Only the final message when exit code != 0\n # all: Running output updates + final message (default)\n background_process_notifications: all\n\n\n # Play terminal bell when agent finishes a response.\n # Useful for long-running tasks — your terminal will ding when the agent is done.\n # Works over SSH. Most terminals can be configured to flash the taskbar or play a sound.\n # true: Ring the terminal bell on each response\n # false: Silent (default)\n bell_on_complete: false\n\n # Show model reasoning/thinking before each response.\n # When enabled, a dim box shows the model's thought process above the response.\n # Toggle at runtime with /reasoning show or /reasoning hide.\n # true: Show the reasoning box\n # false: Hide reasoning (default)\n show_reasoning: false\n\n # Stream tokens to the terminal as they arrive instead of waiting for the\n # full response. The response box opens on first token and text appears\n # line-by-line. Tool calls are still captured silently.\n # true: Stream tokens as they arrive (default)\n # false: Wait for the full response before rendering\n streaming: true\n\n # Show [HH:MM] timestamps on user input and assistant response labels.\n # timestamps: false\n\n # ───────────────────────────────────────────────────────────────────────────\n # Skin / Theme\n # ───────────────────────────────────────────────────────────────────────────\n # Customize CLI visual appearance — banner colors, spinner faces, tool prefix,\n # response box label, and branding text. Change at runtime with /skin <name>.\n #\n # Built-in skins:\n # default — Classic Hermes gold/kawaii\n # ares — Crimson/bronze war-god theme with spinner wings\n # mono — Clean grayscale monochrome\n # slate — Cool blue developer-focused\n # daylight — Bright light-mode theme\n # warm-lightmode — Warm paper-tone light-mode theme\n # poseidon — Sea-green/teal Olympian theme\n # sisyphus — Earthy stone-and-moss theme\n # charizard — Fiery orange dragon theme\n #\n # Custom skins: drop a YAML file in ~/.hermes/skins/<name>.yaml\n # Schema (all fields optional, missing values inherit from default):\n #\n # name: my-theme\n # description: Short description\n # colors:\n # banner_border: \"#HEX\" # Panel border\n # banner_title: \"#HEX\" # Panel title\n # banner_accent: \"#HEX\" # Section headers (Available Tools, etc.)\n # banner_dim: \"#HEX\" # Dim/muted text\n # banner_text: \"#HEX\" # Body text (tool names, skill names)\n # ui_accent: \"#HEX\" # UI accent color\n # response_border: \"#HEX\" # Response box border color\n # spinner:\n # waiting_faces: [\"(⚔)\", \"(⛨)\"] # Faces shown while waiting\n # thinking_faces: [\"(⚔)\", \"(⌁)\"] # Faces shown while thinking\n # thinking_verbs: [\"forging\", \"plotting\"] # Verbs for spinner messages\n # wings: # Optional left/right spinner decorations\n # - [\"⟪⚔\", \"⚔⟫\"]\n # - [\"⟪▲\", \"▲⟫\"]\n # branding:\n # agent_name: \"My Agent\" # Banner title and branding\n # welcome: \"Welcome message\" # Shown at CLI startup\n # response_label: \" ⚔ Agent \" # Response box header label\n # prompt_symbol: \"⚔\" # Prompt symbol (bare token; renderers add trailing space)\n # tool_prefix: \"╎\" # Tool output line prefix (default: ┊)\n #\n skin: default\n\n# =============================================================================\n# Model Aliases — short names for /model command\n# =============================================================================\n# Map short aliases to exact (model, provider, base_url) tuples.\n# Used by /model tab completion and resolve_alias().\n# Aliases are checked BEFORE the models.dev catalog, so they can route\n# to endpoints not in the catalog (e.g. Ollama Cloud, local servers).\n#\n# model_aliases:\n# opus:\n# model: claude-opus-4-6\n# provider: anthropic\n# qwen:\n# model: \"qwen3.5:397b\"\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n# glm:\n# model: glm-4.7\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n\n# =============================================================================\n# Privacy\n# =============================================================================\n# privacy:\n# # Redact PII from the LLM context prompt.\n# # When true, phone numbers are stripped and user/chat IDs are replaced\n# # with deterministic hashes before being sent to the model.\n# # Names and usernames are NOT affected (user-chosen, publicly visible).\n# # Routing/delivery still uses the original values internally.\n# redact_pii: false\n\n# =============================================================================\n# Shell-script hooks\n# =============================================================================\n# Register shell scripts as plugin-hook callbacks. Each entry is executed as\n# a subprocess (shell=False, shlex.split) with a JSON payload on stdin. On\n# stdout the script may return JSON that either blocks the tool call or\n# injects context into the next LLM call.\n#\n# Valid events (mirror hermes_cli.plugins.VALID_HOOKS):\n# pre_tool_call, post_tool_call, pre_llm_call, post_llm_call,\n# pre_api_request, post_api_request, on_session_start, on_session_end,\n# on_session_finalize, on_session_reset, subagent_stop\n#\n# First-use consent: each (event, command) pair prompts once on a TTY, then\n# is persisted to ~/.hermes/shell-hooks-allowlist.json. Non-interactive\n# runs (gateway, cron) need --accept-hooks, HERMES_ACCEPT_HOOKS=1, or the\n# hooks_auto_accept key below.\n#\n# See website/docs/user-guide/features/hooks.md for the full JSON wire\n# protocol and worked examples.\n#\n# hooks:\n# pre_tool_call:\n# - matcher: \"terminal\"\n# command: \"~/.hermes/agent-hooks/block-rm-rf.sh\"\n# timeout: 10\n# post_tool_call:\n# - matcher: \"write_file|patch\"\n# command: \"~/.hermes/agent-hooks/auto-format.sh\"\n# pre_llm_call:\n# - command: \"~/.hermes/agent-hooks/inject-cwd-context.sh\"\n# subagent_stop:\n# - command: \"~/.hermes/agent-hooks/log-orchestration.sh\"\n#\n# hooks_auto_accept: false\n\n\n# =============================================================================\n# Update Behavior\n# =============================================================================\nupdates:\n # Create a full HERMES_HOME zip before every `hermes update`.\n # Backups land in ~/.hermes/backups/ and can be restored with `hermes import`.\n # Off by default because large homes can add minutes to every update.\n pre_update_backup: false\n\n # Number of pre-update backup zips to retain.\n backup_keep: 5\n\n # What non-interactive updates do with local source edits in the Hermes repo.\n # Interactive terminal updates always prompt before restoring the autostash.\n #\n # stash - auto-stash before pull, then auto-restore after success (default)\n # discard - drop the update-created stash after success; use only on managed\n # installs where local source edits should not persist\n non_interactive_local_changes: \"stash\"\n\n\n# =============================================================================\n# Web Dashboard\n# =============================================================================\n# OAuth gate configuration for `hermes dashboard --host <non-loopback>`.\n# The bundled Nous Portal plugin reads these on startup; settings here are\n# the canonical surface. Each can be overridden by an environment variable:\n#\n# dashboard.oauth.client_id <- HERMES_DASHBOARD_OAUTH_CLIENT_ID\n# dashboard.oauth.portal_url <- HERMES_DASHBOARD_PORTAL_URL\n# dashboard.public_url <- HERMES_DASHBOARD_PUBLIC_URL\n#\n# Env wins when set to a non-empty value. This is what Fly.io's platform-\n# secret injection uses to push per-deploy client_ids without needing to\n# bake a config.yaml into the image. Empty env values are treated as unset\n# so a provisioned-but-not-populated secret can't shadow a valid entry here.\n#\n# Local dev / on-prem deploys should typically set these via config.yaml\n# (the ~/.hermes/.env file is reserved for API keys and secrets).\n#\n# dashboard:\n# oauth:\n# client_id: \"\" # agent:{instance_id}; Portal provisions this at deploy\n# portal_url: \"\" # blank → default https://portal.nousresearch.com\n#\n# # Force the absolute base URL the OAuth callback (and any other public\n# # URL the dashboard hands to external systems) is built from. Set this\n# # for deploys behind reverse proxies that don't reliably forward\n# # X-Forwarded-Host / X-Forwarded-Proto / X-Forwarded-Prefix (manual\n# # nginx setups, on-prem ingresses, custom-domain Fly deploys without\n# # full proxy header chains).\n# #\n# # When set, the value is the complete authority: scheme + host +\n# # optional path prefix (e.g. \"https://example.com/hermes\"). The OAuth\n# # callback URL becomes \"<public_url>/auth/callback\" — X-Forwarded-Prefix\n# # is IGNORED on this code path because the operator has explicitly\n# # declared the public URL and we no longer need to guess.\n# #\n# # Leave empty to use the existing proxy-header reconstruction (the\n# # default — works on Fly.io out of the box).\n# #\n# # public_url: \"https://example.com/hermes\"\n#\n# -----------------------------------------------------------------------------\n# Self-hosted OIDC dashboard auth (generic OpenID Connect — Authentik,\n# Keycloak, Zitadel, Authelia, Auth0, Okta, Google, …). Use this INSTEAD of the\n# nous block above when gating the dashboard with your own identity provider.\n# Each setting can be overridden by an environment variable:\n#\n# dashboard.oauth.self_hosted.issuer <- HERMES_DASHBOARD_OIDC_ISSUER\n# dashboard.oauth.self_hosted.client_id <- HERMES_DASHBOARD_OIDC_CLIENT_ID\n# dashboard.oauth.self_hosted.scopes <- HERMES_DASHBOARD_OIDC_SCOPES\n# dashboard.oauth.self_hosted.client_secret <- HERMES_DASHBOARD_OIDC_CLIENT_SECRET\n#\n# dashboard:\n# oauth:\n# provider: self-hosted\n# self_hosted:\n# issuer: \"https://auth.example.com/application/o/hermes/\" # required\n# client_id: \"hermes-dashboard\" # required\n# scopes: \"openid profile email\" # optional\n#\n# # OPTIONAL — set ONLY if your IDP registered the client as\n# # *confidential* (Authentik / Keycloak often default to this). When\n# # set, Hermes authenticates the client at the token endpoint\n# # (client_secret_basic or client_secret_post, auto-selected from the\n# # IDP's discovery doc) IN ADDITION to PKCE. Leave unset for a public\n# # (PKCE-only) client — the common case.\n# #\n# # This is a CREDENTIAL: prefer setting HERMES_DASHBOARD_OIDC_CLIENT_SECRET\n# # in ~/.hermes/.env over putting it here in config.yaml.\n# # client_secret: \"\"\n\n# =============================================================================\n# External secret sources\n# =============================================================================\n# Pull provider credentials from external secret managers at process startup\n# instead of storing them in ~/.hermes/.env. Only the manager's bootstrap\n# credential (e.g. BWS_ACCESS_TOKEN / OP_SERVICE_ACCOUNT_TOKEN) lives in .env\n# (or your shell / desktop session); everything else rotates centrally.\n# Failures never block startup — Hermes warns once and continues with\n# whatever .env already had.\n#\n# Multiple sources can be enabled at once:\n# - \"mapped\" sources (explicit VAR -> ref bindings, e.g. 1Password's env:\n# map) beat \"bulk\" sources (whole-project dumps like Bitwarden BSM)\n# - within a shape, the first source to claim a var wins; later claims\n# are skipped with a startup warning (never a silent clobber)\n# - a source's override_existing lets it beat .env/shell values, but\n# never another secret source's claim\n# Docs: https://hermes-agent.nousresearch.com/docs/user-guide/secrets/\n#\n# secrets:\n# # Optional explicit ordering of enabled sources.\n# # sources: [onepassword, bitwarden]\n#\n# # ---- Bitwarden Secrets Manager (bws CLI) --------------------------------\n# bitwarden:\n# enabled: false\n# access_token_env: BWS_ACCESS_TOKEN # bootstrap token, sourced from .env\n# project_id: \"\" # UUID of the BSM project to sync\n# server_url: \"\" # \"\" = US Cloud; EU/self-hosted URL otherwise\n# cache_ttl_seconds: 300 # 0 disables fresh caching\n# encrypted_cache: # optional encrypted stale fallback\n# enabled: false\n# max_stale_seconds: 0 # 0 disables stale fallback\n# override_existing: true # BSM values win over existing env\n# auto_install: true # lazy-download bws into ~/.hermes/bin\n#\n# # ---- 1Password (op CLI) -------------------------------------------------\n# onepassword:\n# enabled: false\n# # Map env-var names to op:// secret references. Each is resolved with a\n# # single `op read` at startup.\n# env:\n# OPENAI_API_KEY: \"op://Private/OpenAI/api key\"\n# ANTHROPIC_API_KEY: \"op://Private/Anthropic/credential\"\n# account: \"\" # op --account shorthand; \"\" = default\n# service_account_token_env: OP_SERVICE_ACCOUNT_TOKEN # headless auth; unset = desktop session\n# binary_path: \"\" # \"\" = resolve op via PATH; else absolute path\n# cache_ttl_seconds: 300 # 0 disables BOTH cache layers\n# override_existing: true # resolved values win over existing env\n#\n# # ---- Command helper (any CLI vault) --------------------------------------\n# # Run a user-configured helper that prints KEY=VALUE lines on stdout —\n# # works with any secret store that has a CLI: keepassxc-cli, secret-tool,\n# # pass, gpg, or a script that cats a tmpfs env file. Composes with the\n# # sources above (enable any combination). POSIX-only (needs /bin/sh).\n# # The helper must be fast and NON-interactive (hard timeout, 1 MiB cap);\n# # its stderr is discarded so diagnostics can't leak secret material.\n# command:\n# enabled: false\n# command: \"cat /run/user/1000/hermes-secrets.env\"\n# helper_timeout_seconds: 3\n# override_existing: false # .env/shell win by default\n"}, "43d994986ee5f20ad267d9798f687dad937e0345": {"t": 1785768017.8153496, "n": 81008, "h": "51ff727d31a32c30", "c": "# Hermes Agent CLI Configuration\n# Copy this file to cli-config.yaml and customize as needed.\n# This file configures the CLI behavior. Environment variables in .env take precedence.\n\n# =============================================================================\n# Model Configuration\n# =============================================================================\nmodel:\n # Default model to use (can be overridden with --model flag)\n # Both \"default\" and \"model\" work as the key name here.\n default: \"anthropic/claude-opus-4.6\"\n \n # Inference provider selection:\n # \"auto\" - Auto-detect from credentials (default)\n # \"openrouter\" - OpenRouter (requires: OPENROUTER_API_KEY or OPENAI_API_KEY)\n # \"nous\" - Nous Portal OAuth (requires: hermes auth add nous)\n # \"nous-api\" - Nous Portal API key (requires: NOUS_API_KEY)\n # \"anthropic\" - Direct Anthropic API (requires: ANTHROPIC_API_KEY)\n # \"openai-codex\" - OpenAI Codex (requires: hermes auth)\n # \"copilot\" - GitHub Copilot / GitHub Models (requires: GITHUB_TOKEN)\n # \"gemini\" - Use Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n # \"zai\" - Use z.ai / ZhipuAI GLM models (requires: GLM_API_KEY)\n # \"kimi-coding\" - Kimi / Moonshot AI (requires: KIMI_API_KEY)\n # \"minimax\" - MiniMax global (requires: MINIMAX_API_KEY)\n # \"minimax-cn\" - MiniMax China (requires: MINIMAX_CN_API_KEY)\n # \"huggingface\" - Hugging Face Inference (requires: HF_TOKEN)\n # \"nvidia\" - NVIDIA NIM / build.nvidia.com (requires: NVIDIA_API_KEY)\n # \"xiaomi\" - Xiaomi MiMo (requires: XIAOMI_API_KEY)\n # \"arcee\" - Arcee AI Trinity models (requires: ARCEEAI_API_KEY)\n # \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY — https://ollama.com/settings)\n # \"deepinfra\" - DeepInfra (requires: DEEPINFRA_API_KEY)\n # \"kilocode\" - KiloCode gateway (requires: KILOCODE_API_KEY)\n # \"azure-foundry\" - Microsoft Foundry / Azure OpenAI (API key or Entra ID)\n # \"lmstudio\" - LM Studio local server (optional: LM_API_KEY, defaults to http://127.0.0.1:1234/v1)\n #\n # Local servers (LM Studio, Ollama, vLLM, llama.cpp):\n # \"custom\" - Any other OpenAI-compatible endpoint. Set base_url below.\n # Aliases: \"ollama\", \"vllm\", \"llamacpp\" all map to \"custom\".\n # LM Studio is first-class and uses provider: \"lmstudio\".\n # It works with both no-auth and auth-enabled server modes.\n #\n # Can also be overridden for a single invocation with the --provider flag.\n provider: \"auto\"\n \n # API configuration (falls back to OPENROUTER_API_KEY env var)\n # api_key: \"your-key-here\" # Uncomment to set here instead of .env\n base_url: \"https://openrouter.ai/api/v1\"\n\n # Azure Foundry keyless auth example:\n # provider: \"azure-foundry\"\n # base_url: \"https://<resource>.openai.azure.com/openai/v1\"\n # auth_mode: \"entra_id\" # DefaultAzureCredential: az login, managed identity, workload identity, etc.\n # default: \"gpt-4o\" # Deployment/model name\n # entra:\n # scope: \"https://ai.azure.com/.default\" # Optional; this is the default.\n\n # ── Token limits — two settings, easy to confuse ──────────────────────────\n #\n # context_length: TOTAL context window (input + output tokens combined).\n # Controls when Hermes compresses history and validates requests.\n # Leave unset — Hermes auto-detects the correct value from the provider.\n # Set manually only when auto-detection is wrong (e.g. a local server with\n # a custom num_ctx, or a proxy that doesn't expose /v1/models).\n #\n # context_length: 131072\n #\n # max_tokens: OUTPUT cap — maximum tokens the model may generate per response.\n # Unrelated to how long your conversation history can be.\n # The OpenAI-standard name \"max_tokens\" is a misnomer; Anthropic's native\n # API has since renamed it \"max_output_tokens\" for clarity.\n # Leave unset to use the model's native output ceiling (recommended).\n # Set only if you want to deliberately limit individual response length.\n #\n# max_tokens: 8192\n\n # ── Custom request headers (optional) ─────────────────────────────────────\n #\n # default_headers: extra HTTP headers sent on every request to an\n # OpenAI-compatible endpoint. User values take precedence over the\n # provider/SDK defaults, so this is the supported way to override the\n # OpenAI Python SDK's identifying headers (User-Agent: OpenAI/Python ...,\n # X-Stainless-*) when a custom provider sits behind a gateway/WAF that\n # rejects them — e.g. an upstream that returns \"502 Upstream access\n # forbidden\" for the SDK default User-Agent but accepts a plain one.\n # Applies on the OpenAI wire only (not native Anthropic / Bedrock).\n #\n # default_headers:\n # User-Agent: \"curl/8.7.1\"\n #\n # extra_headers: accepted as an alias of default_headers (merged, with\n # extra_headers winning when both are set) — matches the per-provider\n # extra_headers key below.\n #\n # Per-provider variant: named providers / custom_providers entries accept an\n # extra_headers dict scoped to that endpoint only — for reverse proxies,\n # gateways, or custom auth (e.g. Cloudflare Access service tokens).\n # Merged onto SDK/provider defaults with the entry's values winning.\n # Header values are treated as secrets and are never logged.\n #\n # providers:\n # my-proxy:\n # base_url: \"https://llm.internal.example.com/v1\"\n # key_env: \"MY_PROXY_API_KEY\"\n # extra_headers:\n # CF-Access-Client-Id: \"xxxx.access\"\n # CF-Access-Client-Secret: \"${CF_ACCESS_SECRET}\"\n # X-Client-Name: \"hermes-agent\"\n\n# Named provider overrides (optional)\n# Use this for per-provider request timeouts, non-stream stale timeouts,\n# and per-model exceptions.\n# Applies to the primary turn client on every api_mode (OpenAI-wire, native\n# Anthropic, and Anthropic-compatible providers), the fallback chain, and\n# client rebuilds during credential rotation. For OpenAI-wire chat\n# completions (streaming and non-streaming) the configured value is also\n# used as the per-request ``timeout=`` kwarg so it wins over the legacy\n# HERMES_API_TIMEOUT env var (which still applies when no config is set).\n# ``stale_timeout_seconds`` controls the non-streaming stale-call detector and\n# wins over the legacy HERMES_API_CALL_STALE_TIMEOUT env var. Leaving these\n# unset keeps the legacy defaults (HERMES_API_TIMEOUT=1800s,\n# HERMES_API_CALL_STALE_TIMEOUT=90s, native Anthropic 900s). The\n# implicit non-stream stale detector is auto-disabled for local endpoints\n# and can scale upward for very large contexts.\n#\n# Not currently wired for AWS Bedrock (bedrock_converse + AnthropicBedrock\n# SDK paths) — those use boto3 with its own timeout configuration.\n#\n# providers:\n# ollama-local:\n# request_timeout_seconds: 300 # Longer timeout for local cold-starts\n# stale_timeout_seconds: 900 # Explicitly re-enable stale detection on local endpoints\n# anthropic:\n# request_timeout_seconds: 30 # Fast-fail cloud requests\n# models:\n# claude-opus-4.6:\n# timeout_seconds: 600 # Longer timeout for extended-thinking Opus calls\n# openai-codex:\n# models:\n# gpt-5.4:\n# stale_timeout_seconds: 1800 # Longer non-stream stale timeout for slow large-context turns\n\n# =============================================================================\n# OpenRouter Provider Routing (only applies when using OpenRouter)\n# =============================================================================\n# Control how requests are routed across providers on OpenRouter.\n# See: https://openrouter.ai/docs/guides/routing/provider-selection\n#\n# provider_routing:\n# # Sort strategy: \"price\" (default), \"throughput\", or \"latency\"\n# # Append :nitro to model name for a shortcut to throughput sorting.\n# sort: \"throughput\"\n#\n# # Only allow these providers (provider slugs from OpenRouter)\n# # only: [\"anthropic\", \"google\"]\n#\n# # Skip these providers entirely\n# # ignore: [\"deepinfra\", \"fireworks\"]\n#\n# # Try providers in this order (overrides default load balancing)\n# # order: [\"anthropic\", \"google\", \"together\"]\n#\n# # Require providers to support all parameters in your request\n# # require_parameters: true\n#\n# # Data policy: \"allow\" (default) or \"deny\" to exclude providers that may store data\n# # data_collection: \"deny\"\n\n# =============================================================================\n# OpenRouter Response Caching (only applies when using OpenRouter)\n# =============================================================================\n# Cache identical API responses at the OpenRouter edge for free instant replays.\n# When enabled, identical requests (same model, messages, parameters) return\n# cached responses with zero billing. Separate from Anthropic prompt caching.\n# See: https://openrouter.ai/docs/guides/features/response-caching\n#\n# openrouter:\n# response_cache: true # Enable response caching (default: true)\n# response_cache_ttl: 300 # Cache TTL in seconds, 1-86400 (default: 300)\n\n# =============================================================================\n# Git Worktree Isolation\n# =============================================================================\n# When enabled, each CLI session creates an isolated git worktree so multiple\n# agents can work on the same repo concurrently without file collisions.\n# Equivalent to always passing --worktree / -w on the command line.\n#\n# worktree: true # Always create a worktree when in a git repo\n# worktree: false # Default — only create when -w flag is passed\n#\n# By default a new worktree branches from the freshly-fetched remote tip\n# (the current branch's upstream, else the remote's default branch) so it\n# starts current with the project instead of from the local clone's\n# (possibly stale) HEAD. Set worktree_sync: false to branch from local HEAD\n# instead — useful when offline or when you deliberately want the clone's\n# exact current state as the base.\n#\n# worktree_sync: true # Default — branch from the fetched remote tip\n# worktree_sync: false # Branch from local HEAD (offline / pinned base)\n\n# =============================================================================\n# Terminal Tool Configuration\n# =============================================================================\n# Choose ONE of the following terminal configurations by uncommenting it.\n# The terminal tool executes commands in the specified environment.\n\n# -----------------------------------------------------------------------------\n# OPTION 1: Local execution (default)\n# Commands run directly on your machine in the current directory\n# -----------------------------------------------------------------------------\n# Working directory behavior:\n# - CLI (`hermes` command): Uses \".\" (current directory where you run hermes)\n# - Gateway/messaging/cron: Uses terminal.cwd here; legacy .env cwd values are deprecated\nterminal:\n backend: \"local\"\n cwd: \".\" # For local backend: \".\" = current directory. Ignored for remote backends unless a backend documents otherwise.\n timeout: 180\n # HOME policy for tool subprocesses:\n # auto - default: host uses your real HOME; containers use HERMES_HOME/home\n # real - force your real OS-user HOME\n # profile - force HERMES_HOME/home for strict per-profile CLI config isolation\n home_mode: \"auto\"\n docker_mount_cwd_to_workspace: false # SECURITY: off by default. Opt in to mount the launch cwd into Docker /workspace.\n lifetime_seconds: 300\n # sudo_password: \"hunter2\" # Optional: pipe a sudo password via sudo -S. SECURITY WARNING: plaintext.\n # sudo_password: \"\" # Explicit empty password: try empty and never open the interactive sudo prompt.\n\n# -----------------------------------------------------------------------------\n# OPTION 2: SSH remote execution\n# Commands run on a remote server - agent code stays local (sandboxed)\n# Great for: keeping agent isolated from its own code, using powerful remote hardware\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"ssh\"\n# cwd: \"/home/myuser/project\" # Path on the REMOTE server\n# timeout: 180\n# lifetime_seconds: 300\n# ssh_host: \"my-server.example.com\"\n# ssh_user: \"myuser\"\n# ssh_port: 22\n# ssh_key: \"~/.ssh/id_rsa\" # Optional - uses ssh-agent if not specified\n\n# -----------------------------------------------------------------------------\n# OPTION 3: Docker container\n# Commands run in an isolated Docker container\n# Great for: reproducible environments, testing, isolation\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"docker\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /)\n# timeout: 180\n# lifetime_seconds: 300\n# docker_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# docker_mount_cwd_to_workspace: true # Explicit opt-in: mount your launch cwd into /workspace\n# # Optional: run the container as your host user's uid:gid so files written\n# # into bind-mounted dirs are owned by you, not root. Drops SETUID/SETGID\n# # caps too since no gosu privilege drop is needed. Leave off if your\n# # chosen docker_image expects to start as root.\n# docker_run_as_host_user: true\n# # Optional: explicitly forward selected env vars into Docker.\n# # These values come from your current shell first, then ~/.hermes/.env.\n# # Warning: anything forwarded here is visible to commands run in the container.\n# docker_forward_env:\n# - \"GITHUB_TOKEN\"\n# - \"NPM_TOKEN\"\n# # Optional: extra flags passed verbatim to docker run (appended after security defaults).\n# # Useful for adding capabilities (e.g. apt installs needing SETUID) or custom options.\n# # Example: add a Linux capability not included by default\n# # docker_extra_args:\n# # - \"--cap-add\"\n# # - \"SETUID\"\n\n# -----------------------------------------------------------------------------\n# OPTION 4: Singularity/Apptainer container\n# Commands run in a Singularity container (common in HPC environments)\n# Great for: HPC clusters, shared compute environments\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"singularity\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# singularity_image: \"docker://nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 5: Modal cloud execution\n# Commands run on Modal's cloud infrastructure\n# Great for: GPU access, scalable compute, serverless execution\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"modal\"\n# cwd: \"/workspace\" # Path INSIDE the sandbox (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# modal_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 6: Daytona cloud execution\n# Commands run in Daytona cloud sandboxes\n# Great for: Cloud dev environments, persistent workspaces, team collaboration\n# Requires: pip install daytona, DAYTONA_API_KEY env var\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"daytona\"\n# cwd: \"~\"\n# timeout: 180\n# lifetime_seconds: 300\n# daytona_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# container_disk: 10240 # Daytona max is 10GB per sandbox\n\n#\n# --- Container resource limits (docker, singularity, modal, daytona -- ignored for local/ssh) ---\n# These settings apply to all container backends. They control the resources\n# allocated to the sandbox and whether its filesystem persists across sessions.\n container_cpu: 1 # CPU cores\n container_memory: 5120 # Memory in MB (5120 = 5GB)\n container_disk: 51200 # Disk in MB (51200 = 50GB)\n container_persistent: true # Persist filesystem across sessions (false = ephemeral)\n\n# -----------------------------------------------------------------------------\n# SUDO SUPPORT (works with ALL backends above)\n# -----------------------------------------------------------------------------\n# Add sudo_password to any terminal config above to enable sudo commands.\n# The password is piped via `sudo -S`. Works with local, ssh, docker, etc.\n#\n# SECURITY WARNING: Password stored in plaintext!\n#\n# INTERACTIVE PROMPT: If sudo_password is unset and the CLI is running,\n# you'll be prompted to enter your password when sudo is needed:\n# - 45-second timeout (auto-skips if no input)\n# - Press Enter to skip (command fails gracefully)\n# - Password is hidden while typing\n# - Password is cached for the session\n#\n# EMPTY PASSWORDS: Setting sudo_password to an explicit empty string is different\n# from leaving it unset. Hermes will try an empty password via `sudo -S` and\n# will not open the interactive prompt. This is useful for passwordless sudo,\n# Touch ID sudo setups, and environments where prompting is just noise.\n#\n# ALTERNATIVES:\n# - SSH backend: Configure passwordless sudo on the remote server\n# - Containers: Run as root inside the container (no sudo needed)\n# - Local: Configure /etc/sudoers for specific commands\n#\n# Example (add to your terminal section):\n# sudo_password: \"your-password-here\"\n\n# =============================================================================\n# Security Scanning (tirith)\n# =============================================================================\n# Optional pre-exec command security scanning via tirith.\n# Detects homograph URLs, pipe-to-shell, terminal injection, env manipulation.\n# Install: brew install sheeki03/tap/tirith\n# Docs: https://github.com/sheeki03/tirith\n#\n# security:\n# tirith_enabled: true # Enable/disable tirith scanning\n# tirith_path: \"tirith\" # Path to tirith binary (supports ~ expansion)\n# tirith_timeout: 5 # Scan timeout in seconds\n# tirith_fail_open: true # Allow commands if tirith unavailable\n\n# =============================================================================\n# Browser Tool Configuration\n# =============================================================================\nbrowser:\n # Inactivity timeout in seconds - browser sessions are automatically closed\n # after this period of no activity between agent loops (default: 120 = 2 minutes)\n inactivity_timeout: 120\n\n# =============================================================================\n# Tool Loop Guardrails\n# =============================================================================\n# Soft warnings are enabled by default. They append guidance to repeated failed\n# or non-progressing tool results but still let the tool execute. Hard stops are\n# opt-in circuit breakers for autonomous/cron sessions where stopping a loop is\n# preferable to spending the full iteration budget.\ntool_loop_guardrails:\n warnings_enabled: true\n hard_stop_enabled: false\n warn_after:\n exact_failure: 2\n same_tool_failure: 3\n idempotent_no_progress: 2\n hard_stop_after:\n exact_failure: 5\n same_tool_failure: 8\n idempotent_no_progress: 5\n\n# =============================================================================\n# Context Compression (Auto-shrinks long conversations)\n# =============================================================================\n# When conversation approaches model's context limit, middle turns are\n# automatically summarized to free up space while preserving important context.\n#\n# HOW IT WORKS:\n# 1. Tracks actual token usage from API responses (not estimates)\n# 2. When prompt_tokens >= threshold% of model's context_length, triggers compression\n# 3. Protects first 3 turns (system prompt, initial request, first response)\n# 4. Protects last N turns (default 20 messages = ~10 full turns of recent context)\n# 5. Summarizes middle turns using a fast/cheap model\n# 6. Inserts summary as a user message, continues conversation seamlessly\n#\n# Post-compression tail budget is target_ratio × threshold × context_length:\n# 200K context, threshold 0.50, ratio 0.20 → 20K tokens of recent tail preserved\n# 1M context, threshold 0.50, ratio 0.20 → 100K tokens of recent tail preserved\n#\ncompression:\n # Enable automatic context compression (default: true)\n # Set to false if you prefer to manage context manually or want errors on overflow\n enabled: true\n \n # Trigger compression at this % of model's context limit (default: 0.50 = 50%)\n # Lower values = more aggressive compression, higher values = compress later\n # Models with context windows below 512K are floored at 0.75 (raise-only) so\n # compaction doesn't fire with half the window still free; set above 0.75 to override.\n threshold: 0.50\n\n # Per-model threshold overrides: keys are substring-matched against the model\n # name (longest match wins). Useful when some models need different compaction\n # points — e.g. a 1M-context model can compress later (0.30) while a 128K\n # model needs to compress earlier (0.60). The small-context floor (75% for\n # <512K models) still applies on top of per-model overrides.\n # model_thresholds:\n # \"glm-5.2\": 0.40\n # \"claude-sonnet\": 0.35\n # \"gpt-5\": 0.30\n\n # Optional absolute token cap for the compression trigger (default: null = disabled).\n # When set, compression fires at the LOWER of the ratio-based threshold and this\n # absolute token count — first-fires-wins. It never fires later than this count\n # regardless of which model is active (useful when switching between models with\n # very different context windows). Clamped to the model's context length at\n # apply-time, so a cap above the window is a no-op (ratio-based threshold wins).\n # Survives model switches and fallback activations.\n # threshold_tokens: 200000\n\n # Existing Codex gpt-5.5 behavior: raise Hermes' compaction trigger to 85%\n # for the ChatGPT Codex OAuth route. Set false to opt back down to threshold.\n codex_gpt55_autoraise: true\n\n # Fraction of the threshold to preserve as recent tail (default: 0.20 = 20%)\n # e.g. 20% of 50% threshold = 10% of total context kept as recent messages.\n # Summary output is separately capped at 12K tokens (Gemini output limit).\n # Range: 0.10 - 0.80\n target_ratio: 0.20\n\n # Number of most-recent messages to always preserve (default: 20 ≈ 10 full turns)\n # Higher values keep more recent conversation intact at the cost of more aggressive\n # compression of older turns.\n protect_last_n: 20\n\n # Compression retry rounds before a turn gives up with \"max compression\n # attempts reached\" (default: 3, same as the previous hardcoded value).\n # Raise (e.g. 6) for tool-schema-heavy sessions where 3 rounds cannot bring\n # the request estimate under the threshold. Validated >= 1, hard cap 10.\n max_attempts: 3\n\n # Codex app-server (codex CLI runtime) thread-compaction mode. The codex\n # agent owns the real thread context on this runtime, so Hermes' summarizer\n # cannot shrink it — compaction goes through the app server instead.\n # native = let Codex decide when to compact its own thread (default)\n # hermes = let Hermes threshold trigger Codex thread/compact/start\n # off = Hermes will not auto-trigger compaction; Codex may still compact natively\n codex_app_server_auto: native\n\n # Number of non-system messages to protect at the head of the transcript, in\n # ADDITION to the system prompt (which is always implicitly protected).\n # Head messages are NEVER summarized — they survive every compression\n # indefinitely. This gives stable early context for short/medium sessions,\n # but in long-running sessions that rely on rolling compaction the pinned\n # opening turns may not match how you want the session framed over time.\n # Set to 0 to preserve ONLY the system prompt (plus the rolling summary\n # and recent tail) — the cleanest configuration for long-running sessions.\n # Default 3 preserves the system prompt plus the first three non-system\n # head messages, matching the pre-feature behaviour.\n protect_first_n: 3\n\n # Idle compaction (default: 0 = disabled). When > 0, a session that resumes\n # after at least this many seconds of inactivity compacts its accumulated\n # history up front, before the first reply, so a long-lived thread you come\n # back to later doesn't re-read its full stale context on every turn.\n # Time-based, so it complements (does not replace) the size-based `threshold`\n # above. It is skipped when the context is already small (at or below the\n # post-compression target = threshold × target_ratio), so it never wastes a\n # summarization on a short idle thread. Example: 1800 = compact after 30 min idle.\n idle_compact_after_seconds: 0\n\n # To pin a specific model/provider for compression summaries, use the\n # auxiliary section below (auxiliary.compression.provider / model).\n\n# =============================================================================\n# Anthropic prompt caching TTL\n# =============================================================================\n# When prompt caching is active (Claude via OpenRouter or native Anthropic),\n# Anthropic supports two TTL tiers for cached prefixes: \"5m\" (default) and\n# \"1h\". Other values are ignored and \"5m\" is used.\n#\nprompt_caching:\n cache_ttl: \"5m\" # use \"1h\" for long sessions with pauses between turns\n\n# =============================================================================\n# Auxiliary Models (Advanced — Experimental)\n# =============================================================================\n# Hermes uses lightweight \"auxiliary\" models for side tasks: image analysis,\n# browser screenshot analysis, web page summarization, TTS audio-tag insertion,\n# and context compression.\n#\n# By default these use Gemini Flash via OpenRouter or Nous Portal and are\n# auto-detected from your credentials. You do NOT need to change anything\n# here for normal usage.\n#\n# WARNING: Overriding these with providers other than OpenRouter or Nous Portal\n# is EXPERIMENTAL and may not work. Not all models/providers support vision,\n# produce usable summaries, or accept the same API format. Change at your own\n# risk — if things break, reset to \"auto\" / empty values.\n#\n# Each task has its own provider + model pair so you can mix providers.\n# For example: OpenRouter for vision (needs multimodal), but your main\n# local endpoint for compression (just needs text).\n#\n# Provider options:\n# \"auto\" - Best available: OpenRouter → Nous Portal → main endpoint (default)\n# \"openrouter\" - Force OpenRouter (requires OPENROUTER_API_KEY)\n# \"nous\" - Force Nous Portal (requires: hermes auth add nous)\n# \"gemini\" - Force Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n# \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY)\n# \"codex\" - Force Codex OAuth (requires: hermes model → Codex).\n# Uses gpt-5.3-codex which supports vision.\n# \"main\" - Use your custom endpoint (OPENAI_BASE_URL + OPENAI_API_KEY).\n# Works with OpenAI API, local models, or any OpenAI-compatible\n# endpoint. Also falls back to Codex OAuth and API-key providers.\n#\n# Model: leave empty to use the provider's default. When empty, OpenRouter\n# uses \"google/gemini-3-flash-preview\" and Nous uses \"gemini-3-flash\".\n# Other providers pick a sensible default automatically.\n#\n# auxiliary:\n# # Image analysis: vision_analyze tool + browser screenshots\n# vision:\n# provider: \"auto\"\n# model: \"\" # e.g. \"google/gemini-2.5-flash\", \"openai/gpt-4o\"\n# timeout: 30 # LLM API call timeout (seconds)\n# download_timeout: 30 # Image HTTP download timeout (seconds)\n# # Increase for slow connections or self-hosted image servers\n# reasoning_effort: \"\" # Per-task thinking level: none, minimal, low, medium,\n# # high, xhigh, max, ultra. Empty = provider default.\n# # Works on every auxiliary task block (vision,\n# # web_extract, compression, title_generation, curator,\n# # background_review, moa_reference, ...). Example: run\n# # compression at \"low\" and vision at \"none\" to cut\n# # side-task latency/cost on reasoning models.\n#\n# # Web page scraping / summarization + browser page text extraction\n# web_extract:\n# provider: \"auto\"\n# model: \"\"\n# reasoning_effort: \"low\"\n#\n# # Gemini 3.1 TTS hidden audio-tag insertion\n# tts_audio_tags:\n# provider: \"auto\" # empty model = your main chat model\n# model: \"\"\n# timeout: 30\n#\n# # Automatic session title generation after the first exchange\n# title_generation:\n# enabled: true # set false to disable auto-title generation\n# provider: \"auto\"\n# model: \"\"\n# timeout: 30\n# language: \"\" # empty = match the user's language; or e.g. \"English\"\n#\n# # Session search — summarizes matching past sessions\n# session_search:\n# provider: \"auto\"\n# model: \"\"\n# timeout: 30\n# max_concurrency: 3 # Limit parallel summaries to reduce request-burst 429s\n# extra_body: {} # Provider-specific OpenAI-compatible request fields\n# # Example for providers that support request-body\n# # reasoning controls:\n# # extra_body:\n# # enable_thinking: false\n# # Some vLLM/Qwen deployments expect this nested:\n# # extra_body:\n# # chat_template_kwargs:\n# # enable_thinking: false\n\n# =============================================================================\n# Persistent Memory\n# =============================================================================\n# Bounded curated memory injected into the system prompt every session.\n# Two stores: MEMORY.md (agent's notes) and USER.md (user profile).\n# Character limits keep the memory small and focused. The agent manages\n# pruning -- when at the limit, it must consolidate or replace entries.\n# Disabled by default in batch_runner.\n#\nmemory:\n # Agent's personal notes: environment facts, conventions, things learned\n memory_enabled: true\n \n # User profile: preferences, communication style, expectations\n user_profile_enabled: true\n \n # Character limits (~2.75 chars per token, model-independent)\n memory_char_limit: 2200 # ~800 tokens\n user_char_limit: 1375 # ~500 tokens\n\n # Periodic memory nudge: remind the agent to consider saving memories\n # every N user turns. Set to 0 to disable. Only active when memory is enabled.\n nudge_interval: 10 # Nudge every 10 user turns (0 = disabled)\n\n # Memory flush: give the agent one turn to save memories before context is\n # lost (compression, /new, /reset, exit). Set to 0 to disable.\n # For exit/reset, only fires if the session had at least this many user turns.\n flush_min_turns: 6 # Min user turns to trigger flush on exit/reset (0 = disabled)\n\n# =============================================================================\n# Session Reset Policy (Messaging Platforms)\n# =============================================================================\n# Controls when messaging sessions (Telegram, Discord, WhatsApp, Slack) are\n# automatically cleared. Default is \"none\": sessions never auto-reset —\n# conversation context lives until you /reset or /new manually, or context\n# compression kicks in. Opt in to automatic resets if you prefer sessions to\n# clear on a schedule (long-lived context increases API cost per message,\n# though prompt caching and compression keep this manageable).\n#\n# When an automatic reset triggers, the agent first saves important\n# information to its persistent memory — but the conversation context is\n# wiped. The agent starts fresh but retains learned facts via its memory\n# system.\n#\n# Modes:\n# \"none\" - Never auto-reset (default); context lives until /reset or compression\n# \"idle\" - Reset after N minutes of inactivity\n# \"daily\" - Reset at a fixed hour each day\n# \"both\" - Reset on EITHER inactivity timeout or daily boundary\n#\nsession_reset:\n mode: none # \"none\", \"idle\", \"daily\", or \"both\"\n idle_minutes: 1440 # Inactivity timeout in minutes (used by \"idle\"/\"both\")\n at_hour: 4 # Daily reset hour, 0-23 local time (used by \"daily\"/\"both\")\n\n# Maximum number of simultaneously active chat sessions across CLI, TUI,\n# dashboard chat, and messaging gateway. Set to null, 0, or omit to allow\n# unlimited concurrent sessions. When the limit is reached, new sessions get a\n# clean error while existing active sessions keep their normal behavior. This\n# top-level key takes precedence over gateway.max_concurrent_sessions. The cap\n# is a best-effort single-host/profile runtime guard; Hermes fails open if the\n# local runtime lease registry cannot be read or locked.\nmax_concurrent_sessions: null\n\n# When true, group/channel chats use one session per participant when the platform\n# provides a user ID. This is the secure default and prevents users in the same\n# room from sharing context, interrupts, and token costs. Set false only if you\n# explicitly want one shared \"room brain\" per group/channel.\ngroup_sessions_per_user: true\n\n# ─────────────────────────────────────────────────────────────────────────────\n# API Server — per-client model routing\n# ─────────────────────────────────────────────────────────────────────────────\n# Route different API clients to different models/providers on a single\n# Hermes deployment. Clients choose a backend by sending a specific string\n# as the OpenAI ``model`` field. Unmapped model values fall back to the\n# global model configured in the ``model:`` section above, and an explicit\n# session /model override always wins over a route.\n#\n# Configure via the ``platforms.api_server.extra.model_routes`` gateway\n# config block:\n#\n# platforms:\n# api_server:\n# enabled: true\n# extra:\n# key: \"your-api-server-secret\"\n# model_routes:\n# # Xiaozhi clients send model=\"minimax-m2\" → routed to MiniMax via OpenRouter\n# minimax-m2:\n# model: \"minimax/minimax-m1\"\n# provider: \"openrouter\" # optional — overrides global provider\n# # api_key: \"sk-...\" # optional — per-route UPSTREAM provider\n# # key (NOT caller auth; never logged)\n# # base_url: \"https://...\" # optional — per-route base URL\n# # GPT clients keep their own alias\n# gpt-5:\n# model: \"openai/gpt-5\"\n# provider: \"openrouter\"\n#\n# Configured aliases are automatically listed by GET /v1/models so clients\n# can discover them without manual coordination. Caller authentication is\n# unchanged: every request still authenticates with the global API server\n# key (``extra.key`` / API_SERVER_KEY).\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Gateway Streaming\n# ─────────────────────────────────────────────────────────────────────────────\n# Stream tokens to messaging platforms in real-time. The bot sends a message\n# on first token, then progressively edits it as more tokens arrive.\n# Disabled by default — enable to try the streaming UX on Telegram/Discord/Slack.\n# For Telegram, partial edits are sent as plain text and only the final edit uses MarkdownV2.\nstreaming:\n enabled: false\n # transport: edit # \"edit\" = progressive editMessageText\n # edit_interval: 0.3 # seconds between message edits\n # buffer_threshold: 40 # chars before forcing an edit flush\n # cursor: \" ▉\" # cursor shown during streaming\n\n# =============================================================================\n# Skills Configuration\n# =============================================================================\n# Skills are reusable procedures the agent can load and follow. The agent can\n# also create new skills after completing complex tasks.\n#\nskills:\n # Nudge the agent to create skills after complex tasks.\n # Every N tool-calling iterations, remind the model to consider saving a skill.\n # Set to 0 to disable.\n creation_nudge_interval: 15\n\n # External skill directories — share skills across tools/agents without\n # copying them into ~/.hermes/skills/. Each path is expanded (~ and ${VAR})\n # and resolved to an absolute path. External dirs are read-only: skill\n # creation always writes to ~/.hermes/skills/. Local skills take precedence\n # when names collide.\n # external_dirs:\n # - ~/.agents/skills\n # - /home/shared/team-skills\n\n# =============================================================================\n# Agent Behavior\n# =============================================================================\nagent:\n # Maximum tool-calling iterations per conversation\n # Higher = more room for complex tasks, but costs more tokens\n # Recommended: 20-30 for focused tasks, 50-100 for open exploration\n max_turns: 60\n\n # Inactivity timeout for gateway agent runs (seconds, 0 = unlimited).\n # The agent can run indefinitely when actively calling tools or receiving\n # API responses. Only fires after the agent has been idle for this duration.\n # gateway_timeout: 1800\n\n # Staged warning: send a warning before escalating to full timeout.\n # Fires once per run when inactivity reaches this threshold (seconds).\n # Set to 0 to disable the warning.\n # gateway_timeout_warning: 900\n\n # Graceful drain timeout for gateway stop/restart (seconds).\n # Default 0 = no drain: a restart interrupts in-flight agents immediately,\n # cleans up, and exits. Set a positive value only if you want a grace\n # window on /restart, and keep it well under systemd's TimeoutStopSec.\n # restart_drain_timeout: 0\n\n # Max app-level retry attempts for API errors (connection drops, provider\n # timeouts, 5xx, etc.) before the agent surfaces the failure. Lower this\n # to 1 if you use fallback providers and want fast failover on flaky\n # primaries (default 3). The OpenAI SDK does its own low-level retries\n # underneath this wrapper — this is the Hermes-level loop.\n # api_max_retries: 3\n\n # After the agent edits code without fresh passing verification, nudge it to\n # verify before finishing. The default \"auto\" enables it on interactive\n # coding surfaces (CLI, TUI, desktop) and programmatic callers, and disables\n # it on conversational messaging surfaces (Telegram, Discord, etc.) where the\n # verification summary would reach a human as chat noise. Set true or false to\n # force it on or off; the HERMES_VERIFY_ON_STOP env var (1/0) takes precedence.\n # verify_on_stop: auto\n\n # Standing operator instructions for the coding posture (when Hermes is in a\n # code workspace). Appended to the coding brief as an extra system block, so\n # you can pin project-wide workflow rules without editing the shipped brief.\n # Accepts a string or a list of strings. Takes effect next session.\n # coding_instructions:\n # - \"For UI work, don't run tsc/lint until I approve the look.\"\n # - \"Clean the diff before you commit and push.\"\n\n # When verify-on-stop finds edited code without fresh verification evidence,\n # append guidance for creative UI work (avoid broad tsc/lint/test before visual\n # approval) and clean-diff expectations. Set false to keep that nudge terse.\n # verify_guidance: true\n\n # A `pre_verify` hook (plugin or shell, see Event Hooks docs) can keep the\n # agent going one more turn to verify/clean before finishing. This caps how\n # many times one turn may be nudged to continue, so a hook can't trap the loop.\n # Default 3.\n # max_verify_nudges: 3\n\n # Enable verbose logging\n verbose: false\n \n # Reasoning effort level (OpenRouter and Nous Portal)\n # Controls how much \"thinking\" the model does before responding.\n # Options: \"xhigh\" (max), \"high\", \"medium\", \"low\", \"minimal\", \"none\" (disable)\n reasoning_effort: \"medium\"\n \n # Per-model reasoning effort overrides (optional dict)\n # Key: any sensible model spelling works (exact, dots↔dashes interchangeable,\n # provider prefix optional). First match wins.\n # Value: reasoning effort level (same options as reasoning_effort)\n # Override the global reasoning_effort for that specific model.\n # NOTE: no `hermes config set` support for this key -- edit YAML directly.\n # reasoning_overrides:\n # \"openrouter/anthropic/claude-opus-4.5\": \"xhigh\"\n # \"openai/gpt-5\": \"low\"\n # \"claude-opus-4.6\": \"high\" # bare model name also works\n # \"deepseek/deepseek-v4-pro\": \"xhigh\" # dots and dashes are interchangeable\n reasoning_overrides: {}\n \n # Predefined personalities (use with /personality command)\n personalities:\n helpful: \"You are a helpful, friendly AI assistant.\"\n concise: \"You are a concise assistant. Keep responses brief and to the point.\"\n technical: \"You are a technical expert. Provide detailed, accurate technical information.\"\n creative: \"You are a creative assistant. Think outside the box and offer innovative solutions.\"\n teacher: \"You are a patient teacher. Explain concepts clearly with examples.\"\n kawaii: \"You are a kawaii assistant! Use cute expressions like (◕‿◕), ★, ♪, and ~! Add sparkles and be super enthusiastic about everything! Every response should feel warm and adorable desu~! ヽ(>∀<☆)ノ\"\n catgirl: \"You are Neko-chan, an anime catgirl AI assistant, nya~! Add 'nya' and cat-like expressions to your speech. Use kaomoji like (=^・ω・^=) and ฅ^•ﻌ•^ฅ. Be playful and curious like a cat, nya~!\"\n pirate: \"Arrr! Ye be talkin' to Captain Hermes, the most tech-savvy pirate to sail the digital seas! Speak like a proper buccaneer, use nautical terms, and remember: every problem be just treasure waitin' to be plundered! Yo ho ho!\"\n shakespeare: \"Hark! Thou speakest with an assistant most versed in the bardic arts. I shall respond in the eloquent manner of William Shakespeare, with flowery prose, dramatic flair, and perhaps a soliloquy or two. What light through yonder terminal breaks?\"\n surfer: \"Duuude! You're chatting with the chillest AI on the web, bro! Everything's gonna be totally rad. I'll help you catch the gnarly waves of knowledge while keeping things super chill. Cowabunga! 🤙\"\n noir: \"The rain hammered against the terminal like regrets on a guilty conscience. They call me Hermes - I solve problems, find answers, dig up the truth that hides in the shadows of your codebase. In this city of silicon and secrets, everyone's got something to hide. What's your story, pal?\"\n uwu: \"hewwo! i'm your fwiendwy assistant uwu~ i wiww twy my best to hewp you! *nuzzles your code* OwO what's this? wet me take a wook! i pwomise to be vewy hewpful >w<\"\n philosopher: \"Greetings, seeker of wisdom. I am an assistant who contemplates the deeper meaning behind every query. Let us examine not just the 'how' but the 'why' of your questions. Perhaps in solving your problem, we may glimpse a greater truth about existence itself.\"\n hype: \"YOOO LET'S GOOOO!!! 🔥🔥🔥 I am SO PUMPED to help you today! Every question is AMAZING and we're gonna CRUSH IT together! This is gonna be LEGENDARY! ARE YOU READY?! LET'S DO THIS! 💪😤🚀\"\n\n# =============================================================================\n# Toolsets\n# =============================================================================\n# Control which tools the agent has access to.\n# Use `hermes tools` to interactively enable/disable tools per platform.\n\n# =============================================================================\n# Platform Toolsets (per-platform tool configuration)\n# =============================================================================\n# Override which toolsets are available on each platform.\n# If a platform isn't listed here, its built-in default is used.\n#\n# You can use EITHER:\n# - A preset like \"hermes-cli\" or \"hermes-telegram\" (curated tool set)\n# - A list of individual toolsets to compose your own (see list below)\n#\n# Supported platform keys: cli, telegram, discord, whatsapp, slack, qqbot, teams, google_chat\n#\n# Examples:\n#\n# # Use presets (same as defaults):\n# platform_toolsets:\n# cli: [hermes-cli]\n# telegram: [hermes-telegram]\n#\n# # Custom: give Telegram only web + terminal + file + planning:\n# platform_toolsets:\n# telegram: [web, terminal, file, todo]\n#\n# # Custom: CLI without browser or image gen:\n# platform_toolsets:\n# cli: [web, terminal, file, skills, todo, tts, cronjob]\n#\n# # Restrictive: Discord gets read-only tools only:\n# platform_toolsets:\n# discord: [web, vision, skills, todo]\n#\n# If not set, defaults are:\n# cli: hermes-cli (everything + cronjob management)\n# telegram: hermes-telegram (terminal, file, web, vision, image, tts, browser, skills, todo, cronjob, messaging)\n# discord: hermes-discord (same as telegram)\n# whatsapp: hermes-whatsapp (same as telegram)\n# slack: hermes-slack (same as telegram)\n# signal: hermes-signal (same as telegram)\n# homeassistant: hermes-homeassistant (same as telegram)\n# qqbot: hermes-qqbot (same as telegram)\n# teams: hermes-teams (same as telegram)\n# google_chat: hermes-google_chat (same as telegram)\n#\nplatform_toolsets:\n cli: [hermes-cli]\n telegram: [hermes-telegram]\n discord: [hermes-discord]\n whatsapp: [hermes-whatsapp]\n slack: [hermes-slack]\n signal: [hermes-signal]\n homeassistant: [hermes-homeassistant]\n qqbot: [hermes-qqbot]\n yuanbao: [hermes-yuanbao]\n teams: [hermes-teams]\n google_chat: [hermes-google_chat]\n\n# =============================================================================\n# Gateway Platform Settings\n# =============================================================================\n# Optional per-platform messaging settings.\n# Platform-specific knobs live under `extra`.\n#\n# platforms:\n# telegram:\n# reply_to_mode: \"first\" # off | first | all\n# # guest_mode lets explicit @mentions from non-allowlisted groups through.\n# # Default false; ordinary messages, replies, and regex wake words stay blocked.\n# guest_mode: false\n# # allowed_chats: [\"-1001234567890\"]\n# extra:\n# disable_link_previews: false # Set true to suppress Telegram URL previews in bot messages\n# 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\n# rich_drafts: false # Experimental rich draft previews during Telegram DM streaming; default false because Telegram Desktop/macOS can visually overlay draft frames\n# command_menu:\n# # Telegram allows up to 100 BotCommands; Hermes defaults to 60 so\n# # all built-in commands plus common skill commands stay visible\n# # while remaining under Telegram's payload-size limit. Clamped 1..100.\n# max_commands: 60\n# # prepend = user priority first, then Hermes defaults\n# # append = Hermes defaults first, then user priority\n# # replace = only the list below defines priority\n# priority_mode: prepend\n# priority:\n# - my_plugin_command\n# webhook:\n# extra:\n# # Route scripts default to a 30 second timeout. Scripts must live under\n# # the active profile's scripts directory and receive webhook JSON on stdin.\n# script_timeout_seconds: 30\n#\n# Discord-specific settings (config.yaml top-level, not under platforms:):\n#\n# discord:\n# require_mention: true # Require @mention in server channels (default: true)\n# auto_thread: true # Auto-create thread on @mention (default: true)\n# free_response_channels: \"\" # Channel IDs where no mention is needed\n# reactions: true # Show processing reactions (default: true)\n# history_backfill: true # Recover missed channel messages on mention (default: true)\n# history_backfill_limit: 50 # Max messages to scan backwards (default: 50)\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Available toolsets (use these names in platform_toolsets or the toolsets list)\n#\n# Run `hermes chat --list-toolsets` to see all toolsets and their tools.\n# Run `hermes chat --list-tools` to see every individual tool with descriptions.\n# ─────────────────────────────────────────────────────────────────────────────\n#\n# INDIVIDUAL TOOLSETS (compose your own):\n# web - web_search, web_extract\n# search - web_search only (no scraping)\n# terminal - terminal, process\n# file - read_file, write_file, patch, search\n# browser - browser_navigate, browser_snapshot, browser_click, browser_type,\n# browser_scroll, browser_back, browser_press,\n# browser_get_images, browser_vision (requires BROWSERBASE_API_KEY)\n# vision - vision_analyze (requires OPENROUTER_API_KEY)\n# image_gen - image_generate (requires FAL_KEY)\n# skills - skills_list, skill_view\n# skills_hub - skill_hub (search/install/manage from online registries — user-driven only)\n# todo - todo (in-memory task planning, no deps)\n# tts - text_to_speech (Edge TTS free, or ELEVENLABS/OPENAI/MINIMAX/MISTRAL key)\n# cronjob - cronjob (create/list/update/pause/resume/run/remove scheduled tasks)\n#\n# PRESETS (curated bundles):\n# hermes-cli - All of the above except send_message\n# hermes-telegram - terminal, file, web, vision, image_gen, tts, browser,\n# skills, todo, cronjob, send_message\n# hermes-discord - Same as hermes-telegram\n# hermes-whatsapp - Same as hermes-telegram\n# hermes-slack - Same as hermes-telegram\n#\n# COMPOSITE:\n# debugging - terminal + web + file\n# safe - web + vision (no terminal access)\n# all - Everything available\n#\n# web - Web search and content extraction (web_search, web_extract)\n# search - Web search only, no scraping (web_search)\n# terminal - Command execution and process management (terminal, process)\n# file - File operations: read, write, patch, search\n# browser - Full browser automation (navigate, click, type, screenshot, etc.)\n# vision - Image analysis (vision_analyze)\n# image_gen - Image generation with FLUX (image_generate)\n# skills - Load skill documents (skills_list, skill_view)\n# todo - Task planning and tracking for multi-step work\n# memory - Persistent memory across sessions (personal notes + user profile)\n# session_search - Search and recall past conversations (FTS5 + Gemini Flash summarization)\n# tts - Text-to-speech (Edge TTS free, ElevenLabs, OpenAI, MiniMax, Mistral)\n# cronjob - Schedule and manage automated tasks (CLI-only)\n#\n# Composite toolsets:\n# debugging - terminal + web + file (for troubleshooting)\n# safe - web + vision (no terminal access)\n\n# NOTE: The top-level \"toolsets\" key is deprecated and ignored.\n# Tool configuration is managed per-platform via platform_toolsets above.\n# Use `hermes tools` to configure interactively, or edit platform_toolsets directly.\n#\n# CLI override: hermes chat --toolsets terminal,web,file\n\n# =============================================================================\n# MCP (Model Context Protocol) Servers\n# =============================================================================\n# Connect to external MCP servers to add tools from the MCP ecosystem.\n# Each server's tools are automatically discovered and registered.\n# See website/docs/user-guide/features/mcp.md for full documentation.\n#\n# Stdio servers (spawn a subprocess):\n# command: the executable to run\n# args: command-line arguments\n# env: environment variables (only these + safe defaults passed to subprocess)\n#\n# HTTP servers (connect to a URL):\n# url: the MCP server endpoint\n# headers: HTTP headers (e.g., for authentication)\n#\n# Optional per-server settings:\n# timeout: tool call timeout in seconds (default: 120)\n# connect_timeout: initial connection timeout (default: 60)\n# keepalive_interval: liveness ping cadence in seconds (default: 180).\n# Lower it below the server's session TTL for servers that expire idle\n# sessions quickly (e.g. Unreal Engine editor MCP, ~15s), otherwise idle\n# tool calls hit an expired session and pay a slow reconnect. Floored at 5s.\n#\n# mcp_servers:\n# time:\n# command: uvx\n# args: [\"mcp-server-time\"]\n# filesystem:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-filesystem\", \"/home/user\"]\n# notion:\n# url: https://mcp.notion.com/mcp\n# github:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-github\"]\n# env:\n# GITHUB_PERSONAL_ACCESS_TOKEN: \"ghp_...\"\n#\n# Sampling (server-initiated LLM requests) — enabled by default.\n# Per-server config under the 'sampling' key:\n# analysis:\n# command: npx\n# args: [\"-y\", \"analysis-server\"]\n# sampling:\n# enabled: true # default: true\n# model: \"gemini-3-flash\" # override model (optional)\n# max_tokens_cap: 4096 # max tokens per request\n# timeout: 30 # LLM call timeout (seconds)\n# max_rpm: 10 # max requests per minute\n# allowed_models: [] # model whitelist (empty = all)\n# max_tool_rounds: 5 # tool loop limit (0 = disable)\n# log_level: \"info\" # audit verbosity\n\n# =============================================================================\n# Text-to-Speech\n# =============================================================================\n# TTS defaults to Edge TTS unless changed in ~/.hermes/config.yaml.\n# Gemini TTS supports persona/director prompt files, and Gemini 3.1 Flash TTS\n# can use a hidden auxiliary rewrite pass to insert expressive square-bracket\n# audio tags into the TTS script without showing tags in chat.\n#\n# tts:\n# provider: \"gemini\"\n# gemini:\n# model: \"gemini-3.1-flash-tts-preview\"\n# voice: \"Kore\"\n# audio_tags: false\n# persona_prompt_file: \"\" # e.g. ~/.hermes/tts/radio-host.md\n\n# =============================================================================\n# Voice Transcription (Speech-to-Text)\n# =============================================================================\n# Automatically transcribe voice messages on messaging platforms.\n# Providers: local (free, faster-whisper) | groq (free tier) | openai (Whisper API) | mistral (Voxtral Transcribe)\n# Set the corresponding API key in .env: GROQ_API_KEY, OPENAI_API_KEY, or MISTRAL_API_KEY.\nstt:\n enabled: true\n # provider: \"local\" # auto-detected if omitted\n local:\n model: \"base\" # tiny | base | small | medium | large-v3 | turbo\n # language: \"\" # auto-detect; set to \"en\", \"es\", \"fr\", etc. to force\n openai:\n model: \"whisper-1\" # whisper-1 | gpt-4o-mini-transcribe | gpt-4o-transcribe\n # mistral:\n # model: \"voxtral-mini-latest\" # voxtral-mini-latest | voxtral-mini-2602\n # deepinfra:\n # # Model id is discovered live from the DeepInfra catalog filtered\n # # by the `stt` surface tag — leave `model` blank to take the first\n # # live result. Pin only when you need a specific Whisper variant.\n # model: \"\"\n\n# Text-to-speech. Only the deepinfra block is documented here — the\n# remaining providers (edge, openai, xai, minimax, mistral, gemini,\n# elevenlabs, neutts, kittentts, piper) inherit sensible defaults from\n# DEFAULT_CONFIG in hermes_cli/config.py.\n# tts:\n# provider: \"deepinfra\"\n# deepinfra:\n# # Model id is discovered live from the DeepInfra catalog filtered\n# # by the `tts` surface tag — leave `model` blank to take the first\n# # live result.\n# model: \"\"\n# voice: \"default\"\n\n# Image generation. Each provider plugin reads its own ``image_gen.<name>``\n# block; deepinfra discovers models live from\n# api.deepinfra.com/v1/openai/models filtered by the ``image-gen`` tag —\n# no model id is hardcoded, so retired models disappear automatically.\n# image_gen:\n# provider: \"deepinfra\"\n# deepinfra:\n# # Leave `model` blank for the first live `image-gen`-tagged result.\n# model: \"\"\n\n# =============================================================================\n# Response Pacing (Messaging Platforms)\n# =============================================================================\n# Add human-like delays between message chunks.\n# human_delay:\n# mode: \"off\" # \"off\" | \"natural\" | \"custom\"\n# min_ms: 800 # Min delay (custom mode only)\n# max_ms: 2500 # Max delay (custom mode only)\n\n# =============================================================================\n# Session Logging\n# =============================================================================\n# Session trajectories are automatically saved to logs/ directory.\n# Each session creates: logs/session_YYYYMMDD_HHMMSS_UUID.json\n#\n# The session ID is displayed in the welcome banner for easy reference.\n# Logs contain full conversation history in trajectory format:\n# - System prompt, user messages, assistant responses\n# - Tool calls with inputs/outputs\n# - Timestamps for debugging\n#\n# No configuration needed - logging is always enabled.\n# To disable, you would need to modify the source code.\n\n# =============================================================================\n# Code Execution Sandbox (Programmatic Tool Calling)\n# =============================================================================\n# The execute_code tool runs Python scripts that call Hermes tools via RPC.\n# Intermediate tool results stay out of the LLM's context window.\ncode_execution:\n timeout: 300 # Max seconds per script before kill (default: 300 = 5 min)\n max_tool_calls: 50 # Max RPC tool calls per execution (default: 50)\n\n# =============================================================================\n# Subagent Delegation\n# =============================================================================\n# The delegate_task tool spawns child agents with isolated context.\n# Supports single tasks and batch mode (default 3 parallel, configurable).\ndelegation:\n max_iterations: 50 # Max tool-calling turns per child (default: 50)\n # max_concurrent_children: 3 # Max parallel child agents per batch (default: 3, floor: 1, no ceiling).\n # WARNING: values above 10 multiply API cost linearly.\n # max_spawn_depth: 1 # Delegation tree depth cap (range: 1-3, default: 1 = flat).\n # Raise to 2 to allow workers to spawn their own subagents.\n # Requires role=\"orchestrator\" on intermediate agents.\n # orchestrator_enabled: true # Kill switch for role=\"orchestrator\" children (default: true).\n # subagent_auto_approve: false # When a subagent hits a dangerous-command approval prompt, auto-deny (default: false)\n # or auto-approve \"once\" (true) instead of blocking on stdin.\n # The parent TUI owns stdin, so blocking would deadlock; non-interactive resolution is required.\n # Both choices emit a logger.warning audit line. Flip to true only for cron/batch pipelines.\n # inherit_mcp_toolsets: true # When explicit child toolsets are narrowed, also keep the parent's MCP toolsets (default: true). Set false for strict intersection.\n # model: \"google/gemini-3-flash-preview\" # Override model for subagents (empty = inherit parent)\n # provider: \"openrouter\" # Override provider for subagents (empty = inherit parent)\n # # Resolves full credentials (base_url, api_key) automatically.\n # # Supported: openrouter, nous, zai, kimi-coding, minimax\n\n# =============================================================================\n# Honcho Integration (Cross-Session User Modeling)\n# =============================================================================\n# AI-native persistent memory via Honcho (https://honcho.dev/).\n# Builds a deeper understanding of the user across sessions and tools.\n# Runs alongside USER.md — additive, not a replacement.\n#\n# Requires: pip install honcho-ai\n# Config: ~/.honcho/config.json (shared with Claude Code, Cursor, etc.)\n# API key: HONCHO_API_KEY in ~/.hermes/.env or ~/.honcho/config.json\n#\n# Hermes-specific overrides (optional — most config comes from ~/.honcho/config.json):\n# honcho: {}\n\n# =============================================================================\n# Display\n# =============================================================================\ndisplay:\n # Use compact banner mode (hides the ASCII-art banner, shows a single line).\n # true: Compact single-line banner\n # false: Full ASCII banner with tool/skill summary (default)\n compact: false\n\n # Tool progress display level (CLI and gateway)\n # off: Silent — no tool activity shown, just the final response\n # new: Show a tool indicator only when the tool changes (skip repeats)\n # all: Show every tool call with a short preview (default)\n # verbose: Full args, results, and debug logs (same as /verbose)\n # log: Silent in chat; append every tool call to ~/.hermes/logs/tool_calls.log (gateway only)\n # Toggle at runtime with /verbose in the CLI\n tool_progress: all\n\n # Per-platform defaults can be quieter than the global setting. Telegram\n # tunes for mobile: tool_progress and busy_ack_detail default off (no\n # per-tool breadcrumb stream, no \"iteration 21/60\" debug detail in busy\n # acks or heartbeats), but interim_assistant_messages and\n # long_running_notifications STAY ON so the user has real signal between\n # turn start and final answer (mid-turn assistant commentary + a single\n # edit-in-place \"⏳ Working — N min\" heartbeat). Override under\n # display.platforms.telegram.\n\n # Auto-cleanup of temporary progress bubbles after the final response lands.\n # On platforms that support message deletion (currently Telegram), this\n # removes the tool-progress bubble, \"⏳ Still working...\" notices, and\n # context-pressure status messages once the final reply has been delivered —\n # keeping long-running turns visible live, then tidy afterward. Failed runs\n # leave the bubbles in place as breadcrumbs. Off by default.\n # Per-platform override: display.platforms.telegram.cleanup_progress\n # true: Delete tracked progress/status bubbles on successful turn\n # false: Leave everything in place (default)\n # Example:\n # display:\n # platforms:\n # telegram:\n # cleanup_progress: true\n cleanup_progress: false\n\n # Natural mid-turn assistant updates.\n # On gateway platforms, when true, completed assistant status messages are\n # sent as separate chat messages. On the Desktop app, when true, mid-turn\n # assistant narration streamed between tool calls is kept in the transcript\n # instead of the bubble collapsing to only the final message on completion.\n # Independent of tool_progress and gateway streaming.\n # true: Keep/send mid-turn assistant updates (default)\n # false: Only keep/send the final response\n interim_assistant_messages: true\n\n # Gateway-only long-running status heartbeats.\n # When false, the platform does not receive periodic \"⏳ Working — N min\"\n # notifications even if agent.gateway_notify_interval is non-zero. The\n # heartbeat edits a single message in place (where the adapter supports\n # editing) instead of posting a new bubble each interval.\n # Default: true everywhere, including Telegram (silent agents are worse\n # than a single edit-in-place heartbeat).\n long_running_notifications: true\n\n # Include detailed iteration/tool/status context in busy acknowledgments\n # and long-running heartbeats. When true, busy acks show \"iteration 21/60,\n # terminal, 10 min\" and the heartbeat shows \"⏳ Working — 12 min,\n # iteration 21/60, terminal\". When false (Telegram default), both stay\n # terse: \"Interrupting current task\" and \"⏳ Working — 12 min, terminal\".\n busy_ack_detail: true\n\n # What Enter does when Hermes is already busy (CLI and gateway platforms).\n # interrupt: Interrupt the current run and redirect Hermes (default)\n # queue: Queue your message for the next turn\n # steer: Inject your message mid-run via /steer, arriving at the agent\n # after the next tool call — no interrupt, no role violation.\n # Falls back to 'queue' if the agent isn't running yet or if\n # images are attached (steer only carries text).\n # Ctrl+C (or /stop in gateway) always interrupts regardless of this setting.\n # Toggle at runtime with /busy <interrupt|queue|steer>.\n busy_input_mode: interrupt\n\n # Background process notifications (gateway/messaging only).\n # Controls how chatty the process watcher is when you use\n # terminal(background=true, notify_on_complete=true) from Telegram/Discord/etc.\n # off: No watcher messages at all\n # result: Only the final completion message\n # error: Only the final message when exit code != 0\n # all: Running output updates + final message (default)\n background_process_notifications: all\n\n\n # Play terminal bell when agent finishes a response.\n # Useful for long-running tasks — your terminal will ding when the agent is done.\n # Works over SSH. Most terminals can be configured to flash the taskbar or play a sound.\n # true: Ring the terminal bell on each response\n # false: Silent (default)\n bell_on_complete: false\n\n # Show model reasoning/thinking before each response.\n # When enabled, a dim box shows the model's thought process above the response.\n # Toggle at runtime with /reasoning show or /reasoning hide.\n # true: Show the reasoning box\n # false: Hide reasoning (default)\n show_reasoning: false\n\n # Stream tokens to the terminal as they arrive instead of waiting for the\n # full response. The response box opens on first token and text appears\n # line-by-line. Tool calls are still captured silently.\n # true: Stream tokens as they arrive (default)\n # false: Wait for the full response before rendering\n streaming: true\n\n # Show [HH:MM] timestamps on user input and assistant response labels.\n # timestamps: false\n\n # ───────────────────────────────────────────────────────────────────────────\n # Skin / Theme\n # ───────────────────────────────────────────────────────────────────────────\n # Customize CLI visual appearance — banner colors, spinner faces, tool prefix,\n # response box label, and branding text. Change at runtime with /skin <name>.\n #\n # Built-in skins:\n # default — Classic Hermes gold/kawaii\n # ares — Crimson/bronze war-god theme with spinner wings\n # mono — Clean grayscale monochrome\n # slate — Cool blue developer-focused\n # daylight — Bright light-mode theme\n # warm-lightmode — Warm paper-tone light-mode theme\n # poseidon — Sea-green/teal Olympian theme\n # sisyphus — Earthy stone-and-moss theme\n # charizard — Fiery orange dragon theme\n #\n # Custom skins: drop a YAML file in ~/.hermes/skins/<name>.yaml\n # Schema (all fields optional, missing values inherit from default):\n #\n # name: my-theme\n # description: Short description\n # colors:\n # banner_border: \"#HEX\" # Panel border\n # banner_title: \"#HEX\" # Panel title\n # banner_accent: \"#HEX\" # Section headers (Available Tools, etc.)\n # banner_dim: \"#HEX\" # Dim/muted text\n # banner_text: \"#HEX\" # Body text (tool names, skill names)\n # ui_accent: \"#HEX\" # UI accent color\n # response_border: \"#HEX\" # Response box border color\n # spinner:\n # waiting_faces: [\"(⚔)\", \"(⛨)\"] # Faces shown while waiting\n # thinking_faces: [\"(⚔)\", \"(⌁)\"] # Faces shown while thinking\n # thinking_verbs: [\"forging\", \"plotting\"] # Verbs for spinner messages\n # wings: # Optional left/right spinner decorations\n # - [\"⟪⚔\", \"⚔⟫\"]\n # - [\"⟪▲\", \"▲⟫\"]\n # branding:\n # agent_name: \"My Agent\" # Banner title and branding\n # welcome: \"Welcome message\" # Shown at CLI startup\n # response_label: \" ⚔ Agent \" # Response box header label\n # prompt_symbol: \"⚔\" # Prompt symbol (bare token; renderers add trailing space)\n # tool_prefix: \"╎\" # Tool output line prefix (default: ┊)\n #\n skin: default\n\n# =============================================================================\n# Model Aliases — short names for /model command\n# =============================================================================\n# Map short aliases to exact (model, provider, base_url) tuples.\n# Used by /model tab completion and resolve_alias().\n# Aliases are checked BEFORE the models.dev catalog, so they can route\n# to endpoints not in the catalog (e.g. Ollama Cloud, local servers).\n#\n# model_aliases:\n# opus:\n# model: claude-opus-4-6\n# provider: anthropic\n# qwen:\n# model: \"qwen3.5:397b\"\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n# glm:\n# model: glm-4.7\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n\n# =============================================================================\n# Privacy\n# =============================================================================\n# privacy:\n# # Redact PII from the LLM context prompt.\n# # When true, phone numbers are stripped and user/chat IDs are replaced\n# # with deterministic hashes before being sent to the model.\n# # Names and usernames are NOT affected (user-chosen, publicly visible).\n# # Routing/delivery still uses the original values internally.\n# redact_pii: false\n\n# =============================================================================\n# Shell-script hooks\n# =============================================================================\n# Register shell scripts as plugin-hook callbacks. Each entry is executed as\n# a subprocess (shell=False, shlex.split) with a JSON payload on stdin. On\n# stdout the script may return JSON that either blocks the tool call or\n# injects context into the next LLM call.\n#\n# Valid events (mirror hermes_cli.plugins.VALID_HOOKS):\n# pre_tool_call, post_tool_call, pre_llm_call, post_llm_call,\n# pre_api_request, post_api_request, on_session_start, on_session_end,\n# on_session_finalize, on_session_reset, subagent_stop\n#\n# First-use consent: each (event, command) pair prompts once on a TTY, then\n# is persisted to ~/.hermes/shell-hooks-allowlist.json. Non-interactive\n# runs (gateway, cron) need --accept-hooks, HERMES_ACCEPT_HOOKS=1, or the\n# hooks_auto_accept key below.\n#\n# See website/docs/user-guide/features/hooks.md for the full JSON wire\n# protocol and worked examples.\n#\n# hooks:\n# pre_tool_call:\n# - matcher: \"terminal\"\n# command: \"~/.hermes/agent-hooks/block-rm-rf.sh\"\n# timeout: 10\n# post_tool_call:\n# - matcher: \"write_file|patch\"\n# command: \"~/.hermes/agent-hooks/auto-format.sh\"\n# pre_llm_call:\n# - command: \"~/.hermes/agent-hooks/inject-cwd-context.sh\"\n# subagent_stop:\n# - command: \"~/.hermes/agent-hooks/log-orchestration.sh\"\n#\n# hooks_auto_accept: false\n\n\n# =============================================================================\n# Telemetry\n# =============================================================================\n# Shared metrics are disabled by default. When enabled, Hermes writes only\n# allowlisted aggregate counters and immutable JSON\n# packages under $HERMES_HOME/telemetry/shared_metrics; it does not upload them.\n# Packages include a random profile-scoped ID that stays stable until this\n# directory is deleted. It is not derived from hardware, account, or host data.\n# Successfully exported local history is retained for 30 days; pending deltas\n# are retained until they can be exported.\n# This profile-owned choice is not overridden by managed-scope configuration.\ntelemetry:\n shared_metrics:\n enabled: false\n\n\n# =============================================================================\n# Update Behavior\n# =============================================================================\nupdates:\n # Create a full HERMES_HOME zip before every `hermes update`.\n # Backups land in ~/.hermes/backups/ and can be restored with `hermes import`.\n # Off by default because large homes can add minutes to every update.\n pre_update_backup: false\n\n # Number of pre-update backup zips to retain.\n backup_keep: 5\n\n # What non-interactive updates do with local source edits in the Hermes repo.\n # Interactive terminal updates always prompt before restoring the autostash.\n #\n # stash - auto-stash before pull, then auto-restore after success (default)\n # discard - drop the update-created stash after success; use only on managed\n # installs where local source edits should not persist\n non_interactive_local_changes: \"stash\"\n\n\n# =============================================================================\n# Web Dashboard\n# =============================================================================\n# OAuth gate configuration for `hermes dashboard --host <non-loopback>`.\n# The bundled Nous Portal plugin reads these on startup; settings here are\n# the canonical surface. Each can be overridden by an environment variable:\n#\n# dashboard.oauth.client_id <- HERMES_DASHBOARD_OAUTH_CLIENT_ID\n# dashboard.oauth.portal_url <- HERMES_DASHBOARD_PORTAL_URL\n# dashboard.public_url <- HERMES_DASHBOARD_PUBLIC_URL\n#\n# Env wins when set to a non-empty value. This is what Fly.io's platform-\n# secret injection uses to push per-deploy client_ids without needing to\n# bake a config.yaml into the image. Empty env values are treated as unset\n# so a provisioned-but-not-populated secret can't shadow a valid entry here.\n#\n# Local dev / on-prem deploys should typically set these via config.yaml\n# (the ~/.hermes/.env file is reserved for API keys and secrets).\n#\n# dashboard:\n# oauth:\n# client_id: \"\" # agent:{instance_id}; Portal provisions this at deploy\n# portal_url: \"\" # blank → default https://portal.nousresearch.com\n#\n# # Force the absolute base URL the OAuth callback (and any other public\n# # URL the dashboard hands to external systems) is built from. Set this\n# # for deploys behind reverse proxies that don't reliably forward\n# # X-Forwarded-Host / X-Forwarded-Proto / X-Forwarded-Prefix (manual\n# # nginx setups, on-prem ingresses, custom-domain Fly deploys without\n# # full proxy header chains).\n# #\n# # When set, the value is the complete authority: scheme + host +\n# # optional path prefix (e.g. \"https://example.com/hermes\"). The OAuth\n# # callback URL becomes \"<public_url>/auth/callback\" — X-Forwarded-Prefix\n# # is IGNORED on this code path because the operator has explicitly\n# # declared the public URL and we no longer need to guess.\n# #\n# # Leave empty to use the existing proxy-header reconstruction (the\n# # default — works on Fly.io out of the box).\n# #\n# # public_url: \"https://example.com/hermes\"\n#\n# -----------------------------------------------------------------------------\n# Self-hosted OIDC dashboard auth (generic OpenID Connect — Authentik,\n# Keycloak, Zitadel, Authelia, Auth0, Okta, Google, …). Use this INSTEAD of the\n# nous block above when gating the dashboard with your own identity provider.\n# Each setting can be overridden by an environment variable:\n#\n# dashboard.oauth.self_hosted.issuer <- HERMES_DASHBOARD_OIDC_ISSUER\n# dashboard.oauth.self_hosted.client_id <- HERMES_DASHBOARD_OIDC_CLIENT_ID\n# dashboard.oauth.self_hosted.scopes <- HERMES_DASHBOARD_OIDC_SCOPES\n# dashboard.oauth.self_hosted.client_secret <- HERMES_DASHBOARD_OIDC_CLIENT_SECRET\n#\n# dashboard:\n# oauth:\n# provider: self-hosted\n# self_hosted:\n# issuer: \"https://auth.example.com/application/o/hermes/\" # required\n# client_id: \"hermes-dashboard\" # required\n# scopes: \"openid profile email\" # optional\n#\n# # OPTIONAL — set ONLY if your IDP registered the client as\n# # *confidential* (Authentik / Keycloak often default to this). When\n# # set, Hermes authenticates the client at the token endpoint\n# # (client_secret_basic or client_secret_post, auto-selected from the\n# # IDP's discovery doc) IN ADDITION to PKCE. Leave unset for a public\n# # (PKCE-only) client — the common case.\n# #\n# # This is a CREDENTIAL: prefer setting HERMES_DASHBOARD_OIDC_CLIENT_SECRET\n# # in ~/.hermes/.env over putting it here in config.yaml.\n# # client_secret: \"\"\n\n# =============================================================================\n# External secret sources\n# =============================================================================\n# Pull provider credentials from external secret managers at process startup\n# instead of storing them in ~/.hermes/.env. Only the manager's bootstrap\n# credential (e.g. BWS_ACCESS_TOKEN / OP_SERVICE_ACCOUNT_TOKEN) lives in .env\n# (or your shell / desktop session); everything else rotates centrally.\n# Failures never block startup — Hermes warns once and continues with\n# whatever .env already had.\n#\n# Multiple sources can be enabled at once:\n# - \"mapped\" sources (explicit VAR -> ref bindings, e.g. 1Password's env:\n# map) beat \"bulk\" sources (whole-project dumps like Bitwarden BSM)\n# - within a shape, the first source to claim a var wins; later claims\n# are skipped with a startup warning (never a silent clobber)\n# - a source's override_existing lets it beat .env/shell values, but\n# never another secret source's claim\n# Docs: https://hermes-agent.nousresearch.com/docs/user-guide/secrets/\n#\n# secrets:\n# # Optional explicit ordering of enabled sources.\n# # sources: [onepassword, bitwarden]\n#\n# # ---- Bitwarden Secrets Manager (bws CLI) --------------------------------\n# bitwarden:\n# enabled: false\n# access_token_env: BWS_ACCESS_TOKEN # bootstrap token, sourced from .env\n# project_id: \"\" # UUID of the BSM project to sync\n# server_url: \"\" # \"\" = US Cloud; EU/self-hosted URL otherwise\n# cache_ttl_seconds: 300 # 0 disables fresh caching\n# encrypted_cache: # optional encrypted stale fallback\n# enabled: false\n# max_stale_seconds: 0 # 0 disables stale fallback\n# override_existing: true # BSM values win over existing env\n# auto_install: true # lazy-download bws into ~/.hermes/bin\n#\n# # ---- 1Password (op CLI) -------------------------------------------------\n# onepassword:\n# enabled: false\n# # Map env-var names to op:// secret references. Each is resolved with a\n# # single `op read` at startup.\n# env:\n# OPENAI_API_KEY: \"op://Private/OpenAI/api key\"\n# ANTHROPIC_API_KEY: \"op://Private/Anthropic/credential\"\n# account: \"\" # op --account shorthand; \"\" = default\n# service_account_token_env: OP_SERVICE_ACCOUNT_TOKEN # headless auth; unset = desktop session\n# binary_path: \"\" # \"\" = resolve op via PATH; else absolute path\n# cache_ttl_seconds: 300 # 0 disables BOTH cache layers\n# override_existing: true # resolved values win over existing env\n#\n# # ---- Command helper (any CLI vault) --------------------------------------\n# # Run a user-configured helper that prints KEY=VALUE lines on stdout —\n# # works with any secret store that has a CLI: keepassxc-cli, secret-tool,\n# # pass, gpg, or a script that cats a tmpfs env file. Composes with the\n# # sources above (enable any combination). POSIX-only (needs /bin/sh).\n# # The helper must be fast and NON-interactive (hard timeout, 1 MiB cap);\n# # its stderr is discarded so diagnostics can't leak secret material.\n# command:\n# enabled: false\n# command: \"cat /run/user/1000/hermes-secrets.env\"\n# helper_timeout_seconds: 3\n# override_existing: false # .env/shell win by default\n"}, "bba63d87862fbc8ad668a94f9e704ee72a088262": {"t": 1785768020.0967734, "n": 80930, "h": "31746e93d49ed602", "c": "# Hermes Agent CLI Configuration\n# Copy this file to cli-config.yaml and customize as needed.\n# This file configures the CLI behavior. Environment variables in .env take precedence.\n\n# =============================================================================\n# Model Configuration\n# =============================================================================\nmodel:\n # Default model to use (can be overridden with --model flag)\n # Both \"default\" and \"model\" work as the key name here.\n default: \"anthropic/claude-opus-4.6\"\n \n # Inference provider selection:\n # \"auto\" - Auto-detect from credentials (default)\n # \"openrouter\" - OpenRouter (requires: OPENROUTER_API_KEY or OPENAI_API_KEY)\n # \"nous\" - Nous Portal OAuth (requires: hermes auth add nous)\n # \"nous-api\" - Nous Portal API key (requires: NOUS_API_KEY)\n # \"anthropic\" - Direct Anthropic API (requires: ANTHROPIC_API_KEY)\n # \"openai-codex\" - OpenAI Codex (requires: hermes auth)\n # \"copilot\" - GitHub Copilot / GitHub Models (requires: GITHUB_TOKEN)\n # \"gemini\" - Use Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n # \"zai\" - Use z.ai / ZhipuAI GLM models (requires: GLM_API_KEY)\n # \"kimi-coding\" - Kimi / Moonshot AI (requires: KIMI_API_KEY)\n # \"minimax\" - MiniMax global (requires: MINIMAX_API_KEY)\n # \"minimax-cn\" - MiniMax China (requires: MINIMAX_CN_API_KEY)\n # \"huggingface\" - Hugging Face Inference (requires: HF_TOKEN)\n # \"nvidia\" - NVIDIA NIM / build.nvidia.com (requires: NVIDIA_API_KEY)\n # \"xiaomi\" - Xiaomi MiMo (requires: XIAOMI_API_KEY)\n # \"arcee\" - Arcee AI Trinity models (requires: ARCEEAI_API_KEY)\n # \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY — https://ollama.com/settings)\n # \"deepinfra\" - DeepInfra (requires: DEEPINFRA_API_KEY)\n # \"kilocode\" - KiloCode gateway (requires: KILOCODE_API_KEY)\n # \"azure-foundry\" - Microsoft Foundry / Azure OpenAI (API key or Entra ID)\n # \"lmstudio\" - LM Studio local server (optional: LM_API_KEY, defaults to http://127.0.0.1:1234/v1)\n #\n # Local servers (LM Studio, Ollama, vLLM, llama.cpp):\n # \"custom\" - Any other OpenAI-compatible endpoint. Set base_url below.\n # Aliases: \"ollama\", \"vllm\", \"llamacpp\" all map to \"custom\".\n # LM Studio is first-class and uses provider: \"lmstudio\".\n # It works with both no-auth and auth-enabled server modes.\n #\n # Can also be overridden for a single invocation with the --provider flag.\n provider: \"auto\"\n \n # API configuration (falls back to OPENROUTER_API_KEY env var)\n # api_key: \"your-key-here\" # Uncomment to set here instead of .env\n base_url: \"https://openrouter.ai/api/v1\"\n\n # Azure Foundry keyless auth example:\n # provider: \"azure-foundry\"\n # base_url: \"https://<resource>.openai.azure.com/openai/v1\"\n # auth_mode: \"entra_id\" # DefaultAzureCredential: az login, managed identity, workload identity, etc.\n # default: \"gpt-4o\" # Deployment/model name\n # entra:\n # scope: \"https://ai.azure.com/.default\" # Optional; this is the default.\n\n # ── Token limits — two settings, easy to confuse ──────────────────────────\n #\n # context_length: TOTAL context window (input + output tokens combined).\n # Controls when Hermes compresses history and validates requests.\n # Leave unset — Hermes auto-detects the correct value from the provider.\n # Set manually only when auto-detection is wrong (e.g. a local server with\n # a custom num_ctx, or a proxy that doesn't expose /v1/models).\n #\n # context_length: 131072\n #\n # max_tokens: OUTPUT cap — maximum tokens the model may generate per response.\n # Unrelated to how long your conversation history can be.\n # The OpenAI-standard name \"max_tokens\" is a misnomer; Anthropic's native\n # API has since renamed it \"max_output_tokens\" for clarity.\n # Leave unset to use the model's native output ceiling (recommended).\n # Set only if you want to deliberately limit individual response length.\n #\n# max_tokens: 8192\n\n # ── Custom request headers (optional) ─────────────────────────────────────\n #\n # default_headers: extra HTTP headers sent on every request to an\n # OpenAI-compatible endpoint. User values take precedence over the\n # provider/SDK defaults, so this is the supported way to override the\n # OpenAI Python SDK's identifying headers (User-Agent: OpenAI/Python ...,\n # X-Stainless-*) when a custom provider sits behind a gateway/WAF that\n # rejects them — e.g. an upstream that returns \"502 Upstream access\n # forbidden\" for the SDK default User-Agent but accepts a plain one.\n # Applies on the OpenAI wire only (not native Anthropic / Bedrock).\n #\n # default_headers:\n # User-Agent: \"curl/8.7.1\"\n #\n # extra_headers: accepted as an alias of default_headers (merged, with\n # extra_headers winning when both are set) — matches the per-provider\n # extra_headers key below.\n #\n # Per-provider variant: named providers / custom_providers entries accept an\n # extra_headers dict scoped to that endpoint only — for reverse proxies,\n # gateways, or custom auth (e.g. Cloudflare Access service tokens).\n # Merged onto SDK/provider defaults with the entry's values winning.\n # Header values are treated as secrets and are never logged.\n #\n # providers:\n # my-proxy:\n # base_url: \"https://llm.internal.example.com/v1\"\n # key_env: \"MY_PROXY_API_KEY\"\n # extra_headers:\n # CF-Access-Client-Id: \"xxxx.access\"\n # CF-Access-Client-Secret: \"${CF_ACCESS_SECRET}\"\n # X-Client-Name: \"hermes-agent\"\n\n# Named provider overrides (optional)\n# Use this for per-provider request timeouts, non-stream stale timeouts,\n# and per-model exceptions.\n# Applies to the primary turn client on every api_mode (OpenAI-wire, native\n# Anthropic, and Anthropic-compatible providers), the fallback chain, and\n# client rebuilds during credential rotation. For OpenAI-wire chat\n# completions (streaming and non-streaming) the configured value is also\n# used as the per-request ``timeout=`` kwarg so it wins over the legacy\n# HERMES_API_TIMEOUT env var (which still applies when no config is set).\n# ``stale_timeout_seconds`` controls the non-streaming stale-call detector and\n# wins over the legacy HERMES_API_CALL_STALE_TIMEOUT env var. Leaving these\n# unset keeps the legacy defaults (HERMES_API_TIMEOUT=1800s,\n# HERMES_API_CALL_STALE_TIMEOUT=90s, native Anthropic 900s). The\n# implicit non-stream stale detector is auto-disabled for local endpoints\n# and can scale upward for very large contexts.\n#\n# Not currently wired for AWS Bedrock (bedrock_converse + AnthropicBedrock\n# SDK paths) — those use boto3 with its own timeout configuration.\n#\n# providers:\n# ollama-local:\n# request_timeout_seconds: 300 # Longer timeout for local cold-starts\n# stale_timeout_seconds: 900 # Explicitly re-enable stale detection on local endpoints\n# anthropic:\n# request_timeout_seconds: 30 # Fast-fail cloud requests\n# models:\n# claude-opus-4.6:\n# timeout_seconds: 600 # Longer timeout for extended-thinking Opus calls\n# openai-codex:\n# models:\n# gpt-5.4:\n# stale_timeout_seconds: 1800 # Longer non-stream stale timeout for slow large-context turns\n\n# =============================================================================\n# OpenRouter Provider Routing (only applies when using OpenRouter)\n# =============================================================================\n# Control how requests are routed across providers on OpenRouter.\n# See: https://openrouter.ai/docs/guides/routing/provider-selection\n#\n# provider_routing:\n# # Sort strategy: \"price\" (default), \"throughput\", or \"latency\"\n# # Append :nitro to model name for a shortcut to throughput sorting.\n# sort: \"throughput\"\n#\n# # Only allow these providers (provider slugs from OpenRouter)\n# # only: [\"anthropic\", \"google\"]\n#\n# # Skip these providers entirely\n# # ignore: [\"deepinfra\", \"fireworks\"]\n#\n# # Try providers in this order (overrides default load balancing)\n# # order: [\"anthropic\", \"google\", \"together\"]\n#\n# # Require providers to support all parameters in your request\n# # require_parameters: true\n#\n# # Data policy: \"allow\" (default) or \"deny\" to exclude providers that may store data\n# # data_collection: \"deny\"\n\n# =============================================================================\n# OpenRouter Response Caching (only applies when using OpenRouter)\n# =============================================================================\n# Cache identical API responses at the OpenRouter edge for free instant replays.\n# When enabled, identical requests (same model, messages, parameters) return\n# cached responses with zero billing. Separate from Anthropic prompt caching.\n# See: https://openrouter.ai/docs/guides/features/response-caching\n#\n# openrouter:\n# response_cache: true # Enable response caching (default: true)\n# response_cache_ttl: 300 # Cache TTL in seconds, 1-86400 (default: 300)\n\n# =============================================================================\n# Git Worktree Isolation\n# =============================================================================\n# When enabled, each CLI session creates an isolated git worktree so multiple\n# agents can work on the same repo concurrently without file collisions.\n# Equivalent to always passing --worktree / -w on the command line.\n#\n# worktree: true # Always create a worktree when in a git repo\n# worktree: false # Default — only create when -w flag is passed\n#\n# By default a new worktree branches from the freshly-fetched remote tip\n# (the current branch's upstream, else the remote's default branch) so it\n# starts current with the project instead of from the local clone's\n# (possibly stale) HEAD. Set worktree_sync: false to branch from local HEAD\n# instead — useful when offline or when you deliberately want the clone's\n# exact current state as the base.\n#\n# worktree_sync: true # Default — branch from the fetched remote tip\n# worktree_sync: false # Branch from local HEAD (offline / pinned base)\n\n# =============================================================================\n# Terminal Tool Configuration\n# =============================================================================\n# Choose ONE of the following terminal configurations by uncommenting it.\n# The terminal tool executes commands in the specified environment.\n\n# -----------------------------------------------------------------------------\n# OPTION 1: Local execution (default)\n# Commands run directly on your machine in the current directory\n# -----------------------------------------------------------------------------\n# Working directory behavior:\n# - CLI (`hermes` command): Uses \".\" (current directory where you run hermes)\n# - Gateway/messaging/cron: Uses terminal.cwd here; legacy .env cwd values are deprecated\nterminal:\n backend: \"local\"\n cwd: \".\" # For local backend: \".\" = current directory. Ignored for remote backends unless a backend documents otherwise.\n timeout: 180\n # HOME policy for tool subprocesses:\n # auto - default: host uses your real HOME; containers use HERMES_HOME/home\n # real - force your real OS-user HOME\n # profile - force HERMES_HOME/home for strict per-profile CLI config isolation\n home_mode: \"auto\"\n docker_mount_cwd_to_workspace: false # SECURITY: off by default. Opt in to mount the launch cwd into Docker /workspace.\n lifetime_seconds: 300\n # sudo_password: \"hunter2\" # Optional: pipe a sudo password via sudo -S. SECURITY WARNING: plaintext.\n # sudo_password: \"\" # Explicit empty password: try empty and never open the interactive sudo prompt.\n\n# -----------------------------------------------------------------------------\n# OPTION 2: SSH remote execution\n# Commands run on a remote server - agent code stays local (sandboxed)\n# Great for: keeping agent isolated from its own code, using powerful remote hardware\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"ssh\"\n# cwd: \"/home/myuser/project\" # Path on the REMOTE server\n# timeout: 180\n# lifetime_seconds: 300\n# ssh_host: \"my-server.example.com\"\n# ssh_user: \"myuser\"\n# ssh_port: 22\n# ssh_key: \"~/.ssh/id_rsa\" # Optional - uses ssh-agent if not specified\n\n# -----------------------------------------------------------------------------\n# OPTION 3: Docker container\n# Commands run in an isolated Docker container\n# Great for: reproducible environments, testing, isolation\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"docker\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /)\n# timeout: 180\n# lifetime_seconds: 300\n# docker_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# docker_mount_cwd_to_workspace: true # Explicit opt-in: mount your launch cwd into /workspace\n# # Optional: run the container as your host user's uid:gid so files written\n# # into bind-mounted dirs are owned by you, not root. Drops SETUID/SETGID\n# # caps too since no gosu privilege drop is needed. Leave off if your\n# # chosen docker_image expects to start as root.\n# docker_run_as_host_user: true\n# # Optional: explicitly forward selected env vars into Docker.\n# # These values come from your current shell first, then ~/.hermes/.env.\n# # Warning: anything forwarded here is visible to commands run in the container.\n# docker_forward_env:\n# - \"GITHUB_TOKEN\"\n# - \"NPM_TOKEN\"\n# # Optional: extra flags passed verbatim to docker run (appended after security defaults).\n# # Useful for adding capabilities (e.g. apt installs needing SETUID) or custom options.\n# # Example: add a Linux capability not included by default\n# # docker_extra_args:\n# # - \"--cap-add\"\n# # - \"SETUID\"\n\n# -----------------------------------------------------------------------------\n# OPTION 4: Singularity/Apptainer container\n# Commands run in a Singularity container (common in HPC environments)\n# Great for: HPC clusters, shared compute environments\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"singularity\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# singularity_image: \"docker://nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 5: Modal cloud execution\n# Commands run on Modal's cloud infrastructure\n# Great for: GPU access, scalable compute, serverless execution\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"modal\"\n# cwd: \"/workspace\" # Path INSIDE the sandbox (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# modal_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 6: Daytona cloud execution\n# Commands run in Daytona cloud sandboxes\n# Great for: Cloud dev environments, persistent workspaces, team collaboration\n# Requires: pip install daytona, DAYTONA_API_KEY env var\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"daytona\"\n# cwd: \"~\"\n# timeout: 180\n# lifetime_seconds: 300\n# daytona_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# container_disk: 10240 # Daytona max is 10GB per sandbox\n\n#\n# --- Container resource limits (docker, singularity, modal, daytona -- ignored for local/ssh) ---\n# These settings apply to all container backends. They control the resources\n# allocated to the sandbox and whether its filesystem persists across sessions.\n container_cpu: 1 # CPU cores\n container_memory: 5120 # Memory in MB (5120 = 5GB)\n container_disk: 51200 # Disk in MB (51200 = 50GB)\n container_persistent: true # Persist filesystem across sessions (false = ephemeral)\n\n# -----------------------------------------------------------------------------\n# SUDO SUPPORT (works with ALL backends above)\n# -----------------------------------------------------------------------------\n# Add sudo_password to any terminal config above to enable sudo commands.\n# The password is piped via `sudo -S`. Works with local, ssh, docker, etc.\n#\n# SECURITY WARNING: Password stored in plaintext!\n#\n# INTERACTIVE PROMPT: If sudo_password is unset and the CLI is running,\n# you'll be prompted to enter your password when sudo is needed:\n# - 45-second timeout (auto-skips if no input)\n# - Press Enter to skip (command fails gracefully)\n# - Password is hidden while typing\n# - Password is cached for the session\n#\n# EMPTY PASSWORDS: Setting sudo_password to an explicit empty string is different\n# from leaving it unset. Hermes will try an empty password via `sudo -S` and\n# will not open the interactive prompt. This is useful for passwordless sudo,\n# Touch ID sudo setups, and environments where prompting is just noise.\n#\n# ALTERNATIVES:\n# - SSH backend: Configure passwordless sudo on the remote server\n# - Containers: Run as root inside the container (no sudo needed)\n# - Local: Configure /etc/sudoers for specific commands\n#\n# Example (add to your terminal section):\n# sudo_password: \"your-password-here\"\n\n# =============================================================================\n# Security Scanning (tirith)\n# =============================================================================\n# Optional pre-exec command security scanning via tirith.\n# Detects homograph URLs, pipe-to-shell, terminal injection, env manipulation.\n# Install: brew install sheeki03/tap/tirith\n# Docs: https://github.com/sheeki03/tirith\n#\n# security:\n# tirith_enabled: true # Enable/disable tirith scanning\n# tirith_path: \"tirith\" # Path to tirith binary (supports ~ expansion)\n# tirith_timeout: 5 # Scan timeout in seconds\n# tirith_fail_open: true # Allow commands if tirith unavailable\n\n# =============================================================================\n# Browser Tool Configuration\n# =============================================================================\nbrowser:\n # Inactivity timeout in seconds - browser sessions are automatically closed\n # after this period of no activity between agent loops (default: 120 = 2 minutes)\n inactivity_timeout: 120\n\n# =============================================================================\n# Tool Loop Guardrails\n# =============================================================================\n# Soft warnings are enabled by default. They append guidance to repeated failed\n# or non-progressing tool results but still let the tool execute. Hard stops are\n# opt-in circuit breakers for autonomous/cron sessions where stopping a loop is\n# preferable to spending the full iteration budget.\ntool_loop_guardrails:\n warnings_enabled: true\n hard_stop_enabled: false\n warn_after:\n exact_failure: 2\n same_tool_failure: 3\n idempotent_no_progress: 2\n hard_stop_after:\n exact_failure: 5\n same_tool_failure: 8\n idempotent_no_progress: 5\n\n# =============================================================================\n# Context Compression (Auto-shrinks long conversations)\n# =============================================================================\n# When conversation approaches model's context limit, middle turns are\n# automatically summarized to free up space while preserving important context.\n#\n# HOW IT WORKS:\n# 1. Tracks actual token usage from API responses (not estimates)\n# 2. When prompt_tokens >= threshold% of model's context_length, triggers compression\n# 3. Protects first 3 turns (system prompt, initial request, first response)\n# 4. Protects last N turns (default 20 messages = ~10 full turns of recent context)\n# 5. Summarizes middle turns using a fast/cheap model\n# 6. Inserts summary as a user message, continues conversation seamlessly\n#\n# Post-compression tail budget is target_ratio × threshold × context_length:\n# 200K context, threshold 0.50, ratio 0.20 → 20K tokens of recent tail preserved\n# 1M context, threshold 0.50, ratio 0.20 → 100K tokens of recent tail preserved\n#\ncompression:\n # Enable automatic context compression (default: true)\n # Set to false if you prefer to manage context manually or want errors on overflow\n enabled: true\n \n # Trigger compression at this % of model's context limit (default: 0.50 = 50%)\n # Lower values = more aggressive compression, higher values = compress later\n # Models with context windows below 512K are floored at 0.75 (raise-only) so\n # compaction doesn't fire with half the window still free; set above 0.75 to override.\n threshold: 0.50\n\n # Per-model threshold overrides: keys are substring-matched against the model\n # name (longest match wins). Useful when some models need different compaction\n # points — e.g. a 1M-context model can compress later (0.30) while a 128K\n # model needs to compress earlier (0.60). The small-context floor (75% for\n # <512K models) still applies on top of per-model overrides.\n # model_thresholds:\n # \"glm-5.2\": 0.40\n # \"claude-sonnet\": 0.35\n # \"gpt-5\": 0.30\n\n # Optional absolute token cap for the compression trigger (default: null = disabled).\n # When set, compression fires at the LOWER of the ratio-based threshold and this\n # absolute token count — first-fires-wins. It never fires later than this count\n # regardless of which model is active (useful when switching between models with\n # very different context windows). Clamped to the model's context length at\n # apply-time, so a cap above the window is a no-op (ratio-based threshold wins).\n # Survives model switches and fallback activations.\n # threshold_tokens: 200000\n\n # Existing Codex gpt-5.5 behavior: raise Hermes' compaction trigger to 85%\n # for the ChatGPT Codex OAuth route. Set false to opt back down to threshold.\n codex_gpt55_autoraise: true\n\n # Fraction of the threshold to preserve as recent tail (default: 0.20 = 20%)\n # e.g. 20% of 50% threshold = 10% of total context kept as recent messages.\n # Summary output is separately capped at 12K tokens (Gemini output limit).\n # Range: 0.10 - 0.80\n target_ratio: 0.20\n\n # Number of most-recent messages to always preserve (default: 20 ≈ 10 full turns)\n # Higher values keep more recent conversation intact at the cost of more aggressive\n # compression of older turns.\n protect_last_n: 20\n\n # Compression retry rounds before a turn gives up with \"max compression\n # attempts reached\" (default: 3, same as the previous hardcoded value).\n # Raise (e.g. 6) for tool-schema-heavy sessions where 3 rounds cannot bring\n # the request estimate under the threshold. Validated >= 1, hard cap 10.\n max_attempts: 3\n\n # Codex app-server (codex CLI runtime) thread-compaction mode. The codex\n # agent owns the real thread context on this runtime, so Hermes' summarizer\n # cannot shrink it — compaction goes through the app server instead.\n # native = let Codex decide when to compact its own thread (default)\n # hermes = let Hermes threshold trigger Codex thread/compact/start\n # off = Hermes will not auto-trigger compaction; Codex may still compact natively\n codex_app_server_auto: native\n\n # Number of non-system messages to protect at the head of the transcript, in\n # ADDITION to the system prompt (which is always implicitly protected).\n # Head messages are NEVER summarized — they survive every compression\n # indefinitely. This gives stable early context for short/medium sessions,\n # but in long-running sessions that rely on rolling compaction the pinned\n # opening turns may not match how you want the session framed over time.\n # Set to 0 to preserve ONLY the system prompt (plus the rolling summary\n # and recent tail) — the cleanest configuration for long-running sessions.\n # Default 3 preserves the system prompt plus the first three non-system\n # head messages, matching the pre-feature behaviour.\n protect_first_n: 3\n\n # Idle compaction (default: 0 = disabled). When > 0, a session that resumes\n # after at least this many seconds of inactivity compacts its accumulated\n # history up front, before the first reply, so a long-lived thread you come\n # back to later doesn't re-read its full stale context on every turn.\n # Time-based, so it complements (does not replace) the size-based `threshold`\n # above. It is skipped when the context is already small (at or below the\n # post-compression target = threshold × target_ratio), so it never wastes a\n # summarization on a short idle thread. Example: 1800 = compact after 30 min idle.\n idle_compact_after_seconds: 0\n\n # To pin a specific model/provider for compression summaries, use the\n # auxiliary section below (auxiliary.compression.provider / model).\n\n# =============================================================================\n# Anthropic prompt caching TTL\n# =============================================================================\n# When prompt caching is active (Claude via OpenRouter or native Anthropic),\n# Anthropic supports two TTL tiers for cached prefixes: \"5m\" (default) and\n# \"1h\". Other values are ignored and \"5m\" is used.\n#\nprompt_caching:\n cache_ttl: \"5m\" # use \"1h\" for long sessions with pauses between turns\n\n# =============================================================================\n# Auxiliary Models (Advanced — Experimental)\n# =============================================================================\n# Hermes uses lightweight \"auxiliary\" models for side tasks: image analysis,\n# browser screenshot analysis, web page summarization, TTS audio-tag insertion,\n# and context compression.\n#\n# By default these use Gemini Flash via OpenRouter or Nous Portal and are\n# auto-detected from your credentials. You do NOT need to change anything\n# here for normal usage.\n#\n# WARNING: Overriding these with providers other than OpenRouter or Nous Portal\n# is EXPERIMENTAL and may not work. Not all models/providers support vision,\n# produce usable summaries, or accept the same API format. Change at your own\n# risk — if things break, reset to \"auto\" / empty values.\n#\n# Each task has its own provider + model pair so you can mix providers.\n# For example: OpenRouter for vision (needs multimodal), but your main\n# local endpoint for compression (just needs text).\n#\n# Provider options:\n# \"auto\" - Best available: OpenRouter → Nous Portal → main endpoint (default)\n# \"openrouter\" - Force OpenRouter (requires OPENROUTER_API_KEY)\n# \"nous\" - Force Nous Portal (requires: hermes auth add nous)\n# \"gemini\" - Force Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n# \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY)\n# \"codex\" - Force Codex OAuth (requires: hermes model → Codex).\n# Uses gpt-5.3-codex which supports vision.\n# \"main\" - Use your custom endpoint (OPENAI_BASE_URL + OPENAI_API_KEY).\n# Works with OpenAI API, local models, or any OpenAI-compatible\n# endpoint. Also falls back to Codex OAuth and API-key providers.\n#\n# Model: leave empty to use the provider's default. When empty, OpenRouter\n# uses \"google/gemini-3-flash-preview\" and Nous uses \"gemini-3-flash\".\n# Other providers pick a sensible default automatically.\n#\n# auxiliary:\n# # Image analysis: vision_analyze tool + browser screenshots\n# vision:\n# provider: \"auto\"\n# model: \"\" # e.g. \"google/gemini-2.5-flash\", \"openai/gpt-4o\"\n# timeout: 30 # LLM API call timeout (seconds)\n# download_timeout: 30 # Image HTTP download timeout (seconds)\n# # Increase for slow connections or self-hosted image servers\n# reasoning_effort: \"\" # Per-task thinking level: none, minimal, low, medium,\n# # high, xhigh, max, ultra. Empty = provider default.\n# # Works on every auxiliary task block (vision,\n# # web_extract, compression, title_generation, curator,\n# # background_review, moa_reference, ...). Example: run\n# # compression at \"low\" and vision at \"none\" to cut\n# # side-task latency/cost on reasoning models.\n#\n# # Web page scraping / summarization + browser page text extraction\n# web_extract:\n# provider: \"auto\"\n# model: \"\"\n# reasoning_effort: \"low\"\n#\n# # Gemini 3.1 TTS hidden audio-tag insertion\n# tts_audio_tags:\n# provider: \"auto\" # empty model = your main chat model\n# model: \"\"\n# timeout: 30\n#\n# # Automatic session title generation after the first exchange\n# title_generation:\n# enabled: true # set false to disable auto-title generation\n# provider: \"auto\"\n# model: \"\"\n# timeout: 30\n# language: \"\" # empty = match the user's language; or e.g. \"English\"\n#\n# # Session search — summarizes matching past sessions\n# session_search:\n# provider: \"auto\"\n# model: \"\"\n# timeout: 30\n# max_concurrency: 3 # Limit parallel summaries to reduce request-burst 429s\n# extra_body: {} # Provider-specific OpenAI-compatible request fields\n# # Example for providers that support request-body\n# # reasoning controls:\n# # extra_body:\n# # enable_thinking: false\n# # Some vLLM/Qwen deployments expect this nested:\n# # extra_body:\n# # chat_template_kwargs:\n# # enable_thinking: false\n\n# =============================================================================\n# Persistent Memory\n# =============================================================================\n# Bounded curated memory injected into the system prompt every session.\n# Two stores: MEMORY.md (agent's notes) and USER.md (user profile).\n# Character limits keep the memory small and focused. The agent manages\n# pruning -- when at the limit, it must consolidate or replace entries.\n# Disabled by default in batch_runner.\n#\nmemory:\n # Agent's personal notes: environment facts, conventions, things learned\n memory_enabled: true\n \n # User profile: preferences, communication style, expectations\n user_profile_enabled: true\n \n # Character limits (~2.75 chars per token, model-independent)\n memory_char_limit: 2200 # ~800 tokens\n user_char_limit: 1375 # ~500 tokens\n\n # Periodic memory nudge: remind the agent to consider saving memories\n # every N user turns. Set to 0 to disable. Only active when memory is enabled.\n nudge_interval: 10 # Nudge every 10 user turns (0 = disabled)\n\n # Memory flush: give the agent one turn to save memories before context is\n # lost (compression, /new, /reset, exit). Set to 0 to disable.\n # For exit/reset, only fires if the session had at least this many user turns.\n flush_min_turns: 6 # Min user turns to trigger flush on exit/reset (0 = disabled)\n\n# =============================================================================\n# Session Reset Policy (Messaging Platforms)\n# =============================================================================\n# Controls when messaging sessions (Telegram, Discord, WhatsApp, Slack) are\n# automatically cleared. Default is \"none\": sessions never auto-reset —\n# conversation context lives until you /reset or /new manually, or context\n# compression kicks in. Opt in to automatic resets if you prefer sessions to\n# clear on a schedule (long-lived context increases API cost per message,\n# though prompt caching and compression keep this manageable).\n#\n# When an automatic reset triggers, the agent first saves important\n# information to its persistent memory — but the conversation context is\n# wiped. The agent starts fresh but retains learned facts via its memory\n# system.\n#\n# Modes:\n# \"none\" - Never auto-reset (default); context lives until /reset or compression\n# \"idle\" - Reset after N minutes of inactivity\n# \"daily\" - Reset at a fixed hour each day\n# \"both\" - Reset on EITHER inactivity timeout or daily boundary\n#\nsession_reset:\n mode: none # \"none\", \"idle\", \"daily\", or \"both\"\n idle_minutes: 1440 # Inactivity timeout in minutes (used by \"idle\"/\"both\")\n at_hour: 4 # Daily reset hour, 0-23 local time (used by \"daily\"/\"both\")\n\n# Maximum number of simultaneously active chat sessions across CLI, TUI,\n# dashboard chat, and messaging gateway. Set to null, 0, or omit to allow\n# unlimited concurrent sessions. When the limit is reached, new sessions get a\n# clean error while existing active sessions keep their normal behavior. This\n# top-level key takes precedence over gateway.max_concurrent_sessions. The cap\n# is a best-effort single-host/profile runtime guard; Hermes fails open if the\n# local runtime lease registry cannot be read or locked.\nmax_concurrent_sessions: null\n\n# When true, group/channel chats use one session per participant when the platform\n# provides a user ID. This is the secure default and prevents users in the same\n# room from sharing context, interrupts, and token costs. Set false only if you\n# explicitly want one shared \"room brain\" per group/channel.\ngroup_sessions_per_user: true\n\n# ─────────────────────────────────────────────────────────────────────────────\n# API Server — per-client model routing\n# ─────────────────────────────────────────────────────────────────────────────\n# Route different API clients to different models/providers on a single\n# Hermes deployment. Clients choose a backend by sending a specific string\n# as the OpenAI ``model`` field. Unmapped model values fall back to the\n# global model configured in the ``model:`` section above, and an explicit\n# session /model override always wins over a route.\n#\n# Configure via the ``platforms.api_server.extra.model_routes`` gateway\n# config block:\n#\n# platforms:\n# api_server:\n# enabled: true\n# extra:\n# key: \"your-api-server-secret\"\n# model_routes:\n# # Xiaozhi clients send model=\"minimax-m2\" → routed to MiniMax via OpenRouter\n# minimax-m2:\n# model: \"minimax/minimax-m1\"\n# provider: \"openrouter\" # optional — overrides global provider\n# # api_key: \"sk-...\" # optional — per-route UPSTREAM provider\n# # key (NOT caller auth; never logged)\n# # base_url: \"https://...\" # optional — per-route base URL\n# # GPT clients keep their own alias\n# gpt-5:\n# model: \"openai/gpt-5\"\n# provider: \"openrouter\"\n#\n# Configured aliases are automatically listed by GET /v1/models so clients\n# can discover them without manual coordination. Caller authentication is\n# unchanged: every request still authenticates with the global API server\n# key (``extra.key`` / API_SERVER_KEY).\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Gateway Streaming\n# ─────────────────────────────────────────────────────────────────────────────\n# Stream tokens to messaging platforms in real-time. The bot sends a message\n# on first token, then progressively edits it as more tokens arrive.\n# Disabled by default — enable to try the streaming UX on Telegram/Discord/Slack.\n# For Telegram, partial edits are sent as plain text and only the final edit uses MarkdownV2.\nstreaming:\n enabled: false\n # transport: edit # \"edit\" = progressive editMessageText\n # edit_interval: 0.3 # seconds between message edits\n # buffer_threshold: 40 # chars before forcing an edit flush\n # cursor: \" ▉\" # cursor shown during streaming\n\n# =============================================================================\n# Skills Configuration\n# =============================================================================\n# Skills are reusable procedures the agent can load and follow. The agent can\n# also create new skills after completing complex tasks.\n#\nskills:\n # Nudge the agent to create skills after complex tasks.\n # Every N tool-calling iterations, remind the model to consider saving a skill.\n # Set to 0 to disable.\n creation_nudge_interval: 15\n\n # External skill directories — share skills across tools/agents without\n # copying them into ~/.hermes/skills/. Each path is expanded (~ and ${VAR})\n # and resolved to an absolute path. External dirs are read-only: skill\n # creation always writes to ~/.hermes/skills/. Local skills take precedence\n # when names collide.\n # external_dirs:\n # - ~/.agents/skills\n # - /home/shared/team-skills\n\n# =============================================================================\n# Agent Behavior\n# =============================================================================\nagent:\n # Maximum tool-calling iterations per conversation\n # Higher = more room for complex tasks, but costs more tokens\n # Recommended: 20-30 for focused tasks, 50-100 for open exploration\n max_turns: 60\n\n # Inactivity timeout for gateway agent runs (seconds, 0 = unlimited).\n # The agent can run indefinitely when actively calling tools or receiving\n # API responses. Only fires after the agent has been idle for this duration.\n # gateway_timeout: 1800\n\n # Staged warning: send a warning before escalating to full timeout.\n # Fires once per run when inactivity reaches this threshold (seconds).\n # Set to 0 to disable the warning.\n # gateway_timeout_warning: 900\n\n # Graceful drain timeout for gateway stop/restart (seconds).\n # Default 0 = no drain: a restart interrupts in-flight agents immediately,\n # cleans up, and exits. Set a positive value only if you want a grace\n # window on /restart, and keep it well under systemd's TimeoutStopSec.\n # restart_drain_timeout: 0\n\n # Max app-level retry attempts for API errors (connection drops, provider\n # timeouts, 5xx, etc.) before the agent surfaces the failure. Lower this\n # to 1 if you use fallback providers and want fast failover on flaky\n # primaries (default 3). The OpenAI SDK does its own low-level retries\n # underneath this wrapper — this is the Hermes-level loop.\n # api_max_retries: 3\n\n # After the agent edits code without fresh passing verification, nudge it to\n # verify before finishing. The default \"auto\" enables it on interactive\n # coding surfaces (CLI, TUI, desktop) and programmatic callers, and disables\n # it on conversational messaging surfaces (Telegram, Discord, etc.) where the\n # verification summary would reach a human as chat noise. Set true or false to\n # force it on or off; the HERMES_VERIFY_ON_STOP env var (1/0) takes precedence.\n # verify_on_stop: auto\n\n # Standing operator instructions for the coding posture (when Hermes is in a\n # code workspace). Appended to the coding brief as an extra system block, so\n # you can pin project-wide workflow rules without editing the shipped brief.\n # Accepts a string or a list of strings. Takes effect next session.\n # coding_instructions:\n # - \"For UI work, don't run tsc/lint until I approve the look.\"\n # - \"Clean the diff before you commit and push.\"\n\n # When verify-on-stop finds edited code without fresh verification evidence,\n # append guidance for creative UI work (avoid broad tsc/lint/test before visual\n # approval) and clean-diff expectations. Set false to keep that nudge terse.\n # verify_guidance: true\n\n # A `pre_verify` hook (plugin or shell, see Event Hooks docs) can keep the\n # agent going one more turn to verify/clean before finishing. This caps how\n # many times one turn may be nudged to continue, so a hook can't trap the loop.\n # Default 3.\n # max_verify_nudges: 3\n\n # Enable verbose logging\n verbose: false\n \n # Reasoning effort level (OpenRouter and Nous Portal)\n # Controls how much \"thinking\" the model does before responding.\n # Options: \"xhigh\" (max), \"high\", \"medium\", \"low\", \"minimal\", \"none\" (disable)\n reasoning_effort: \"medium\"\n \n # Per-model reasoning effort overrides (optional dict)\n # Key: any sensible model spelling works (exact, dots↔dashes interchangeable,\n # provider prefix optional). First match wins.\n # Value: reasoning effort level (same options as reasoning_effort)\n # Override the global reasoning_effort for that specific model.\n # NOTE: no `hermes config set` support for this key -- edit YAML directly.\n # reasoning_overrides:\n # \"openrouter/anthropic/claude-opus-4.5\": \"xhigh\"\n # \"openai/gpt-5\": \"low\"\n # \"claude-opus-4.6\": \"high\" # bare model name also works\n # \"deepseek/deepseek-v4-pro\": \"xhigh\" # dots and dashes are interchangeable\n reasoning_overrides: {}\n \n # Predefined personalities (use with /personality command)\n personalities:\n helpful: \"You are a helpful, friendly AI assistant.\"\n concise: \"You are a concise assistant. Keep responses brief and to the point.\"\n technical: \"You are a technical expert. Provide detailed, accurate technical information.\"\n creative: \"You are a creative assistant. Think outside the box and offer innovative solutions.\"\n teacher: \"You are a patient teacher. Explain concepts clearly with examples.\"\n kawaii: \"You are a kawaii assistant! Use cute expressions like (◕‿◕), ★, ♪, and ~! Add sparkles and be super enthusiastic about everything! Every response should feel warm and adorable desu~! ヽ(>∀<☆)ノ\"\n catgirl: \"You are Neko-chan, an anime catgirl AI assistant, nya~! Add 'nya' and cat-like expressions to your speech. Use kaomoji like (=^・ω・^=) and ฅ^•ﻌ•^ฅ. Be playful and curious like a cat, nya~!\"\n pirate: \"Arrr! Ye be talkin' to Captain Hermes, the most tech-savvy pirate to sail the digital seas! Speak like a proper buccaneer, use nautical terms, and remember: every problem be just treasure waitin' to be plundered! Yo ho ho!\"\n shakespeare: \"Hark! Thou speakest with an assistant most versed in the bardic arts. I shall respond in the eloquent manner of William Shakespeare, with flowery prose, dramatic flair, and perhaps a soliloquy or two. What light through yonder terminal breaks?\"\n surfer: \"Duuude! You're chatting with the chillest AI on the web, bro! Everything's gonna be totally rad. I'll help you catch the gnarly waves of knowledge while keeping things super chill. Cowabunga! 🤙\"\n noir: \"The rain hammered against the terminal like regrets on a guilty conscience. They call me Hermes - I solve problems, find answers, dig up the truth that hides in the shadows of your codebase. In this city of silicon and secrets, everyone's got something to hide. What's your story, pal?\"\n uwu: \"hewwo! i'm your fwiendwy assistant uwu~ i wiww twy my best to hewp you! *nuzzles your code* OwO what's this? wet me take a wook! i pwomise to be vewy hewpful >w<\"\n philosopher: \"Greetings, seeker of wisdom. I am an assistant who contemplates the deeper meaning behind every query. Let us examine not just the 'how' but the 'why' of your questions. Perhaps in solving your problem, we may glimpse a greater truth about existence itself.\"\n hype: \"YOOO LET'S GOOOO!!! 🔥🔥🔥 I am SO PUMPED to help you today! Every question is AMAZING and we're gonna CRUSH IT together! This is gonna be LEGENDARY! ARE YOU READY?! LET'S DO THIS! 💪😤🚀\"\n\n# =============================================================================\n# Toolsets\n# =============================================================================\n# Control which tools the agent has access to.\n# Use `hermes tools` to interactively enable/disable tools per platform.\n\n# =============================================================================\n# Platform Toolsets (per-platform tool configuration)\n# =============================================================================\n# Override which toolsets are available on each platform.\n# If a platform isn't listed here, its built-in default is used.\n#\n# You can use EITHER:\n# - A preset like \"hermes-cli\" or \"hermes-telegram\" (curated tool set)\n# - A list of individual toolsets to compose your own (see list below)\n#\n# Supported platform keys: cli, telegram, discord, whatsapp, slack, qqbot, teams, google_chat\n#\n# Examples:\n#\n# # Use presets (same as defaults):\n# platform_toolsets:\n# cli: [hermes-cli]\n# telegram: [hermes-telegram]\n#\n# # Custom: give Telegram only web + terminal + file + planning:\n# platform_toolsets:\n# telegram: [web, terminal, file, todo]\n#\n# # Custom: CLI without browser or image gen:\n# platform_toolsets:\n# cli: [web, terminal, file, skills, todo, tts, cronjob]\n#\n# # Restrictive: Discord gets read-only tools only:\n# platform_toolsets:\n# discord: [web, vision, skills, todo]\n#\n# If not set, defaults are:\n# cli: hermes-cli (everything + cronjob management)\n# telegram: hermes-telegram (terminal, file, web, vision, image, tts, browser, skills, todo, cronjob, messaging)\n# discord: hermes-discord (same as telegram)\n# whatsapp: hermes-whatsapp (same as telegram)\n# slack: hermes-slack (same as telegram)\n# signal: hermes-signal (same as telegram)\n# homeassistant: hermes-homeassistant (same as telegram)\n# qqbot: hermes-qqbot (same as telegram)\n# teams: hermes-teams (same as telegram)\n# google_chat: hermes-google_chat (same as telegram)\n#\nplatform_toolsets:\n cli: [hermes-cli]\n telegram: [hermes-telegram]\n discord: [hermes-discord]\n whatsapp: [hermes-whatsapp]\n slack: [hermes-slack]\n signal: [hermes-signal]\n homeassistant: [hermes-homeassistant]\n qqbot: [hermes-qqbot]\n yuanbao: [hermes-yuanbao]\n teams: [hermes-teams]\n google_chat: [hermes-google_chat]\n\n# =============================================================================\n# Gateway Platform Settings\n# =============================================================================\n# Optional per-platform messaging settings.\n# Platform-specific knobs live under `extra`.\n#\n# platforms:\n# telegram:\n# reply_to_mode: \"first\" # off | first | all\n# # guest_mode lets explicit @mentions from non-allowlisted groups through.\n# # Default false; ordinary messages, replies, and regex wake words stay blocked.\n# guest_mode: false\n# # allowed_chats: [\"-1001234567890\"]\n# extra:\n# disable_link_previews: false # Set true to suppress Telegram URL previews in bot messages\n# 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\n# rich_drafts: false # Experimental rich draft previews during Telegram DM streaming; default false because Telegram Desktop/macOS can visually overlay draft frames\n# command_menu:\n# # Telegram allows up to 100 BotCommands; Hermes defaults to 60 so\n# # all built-in commands plus common skill commands stay visible\n# # while remaining under Telegram's payload-size limit. Clamped 1..100.\n# max_commands: 60\n# # prepend = user priority first, then Hermes defaults\n# # append = Hermes defaults first, then user priority\n# # replace = only the list below defines priority\n# priority_mode: prepend\n# priority:\n# - my_plugin_command\n# webhook:\n# extra:\n# # Route scripts default to a 30 second timeout. Scripts must live under\n# # the active profile's scripts directory and receive webhook JSON on stdin.\n# script_timeout_seconds: 30\n#\n# Discord-specific settings (config.yaml top-level, not under platforms:):\n#\n# discord:\n# require_mention: true # Require @mention in server channels (default: true)\n# auto_thread: true # Auto-create thread on @mention (default: true)\n# free_response_channels: \"\" # Channel IDs where no mention is needed\n# reactions: true # Show processing reactions (default: true)\n# history_backfill: true # Recover missed channel messages on mention (default: true)\n# history_backfill_limit: 50 # Max messages to scan backwards (default: 50)\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Available toolsets (use these names in platform_toolsets or the toolsets list)\n#\n# Run `hermes chat --list-toolsets` to see all toolsets and their tools.\n# Run `hermes chat --list-tools` to see every individual tool with descriptions.\n# ─────────────────────────────────────────────────────────────────────────────\n#\n# INDIVIDUAL TOOLSETS (compose your own):\n# web - web_search, web_extract\n# search - web_search only (no scraping)\n# terminal - terminal, process\n# file - read_file, write_file, patch, search\n# browser - browser_navigate, browser_snapshot, browser_click, browser_type,\n# browser_scroll, browser_back, browser_press,\n# browser_get_images, browser_vision (requires BROWSERBASE_API_KEY)\n# vision - vision_analyze (requires OPENROUTER_API_KEY)\n# image_gen - image_generate (requires FAL_KEY)\n# skills - skills_list, skill_view\n# skills_hub - skill_hub (search/install/manage from online registries — user-driven only)\n# todo - todo (in-memory task planning, no deps)\n# tts - text_to_speech (Edge TTS free, or ELEVENLABS/OPENAI/MINIMAX/MISTRAL key)\n# cronjob - cronjob (create/list/update/pause/resume/run/remove scheduled tasks)\n#\n# PRESETS (curated bundles):\n# hermes-cli - All of the above except send_message\n# hermes-telegram - terminal, file, web, vision, image_gen, tts, browser,\n# skills, todo, cronjob, send_message\n# hermes-discord - Same as hermes-telegram\n# hermes-whatsapp - Same as hermes-telegram\n# hermes-slack - Same as hermes-telegram\n#\n# COMPOSITE:\n# debugging - terminal + web + file\n# safe - web + vision (no terminal access)\n# all - Everything available\n#\n# web - Web search and content extraction (web_search, web_extract)\n# search - Web search only, no scraping (web_search)\n# terminal - Command execution and process management (terminal, process)\n# file - File operations: read, write, patch, search\n# browser - Full browser automation (navigate, click, type, screenshot, etc.)\n# vision - Image analysis (vision_analyze)\n# image_gen - Image generation with FLUX (image_generate)\n# skills - Load skill documents (skills_list, skill_view)\n# todo - Task planning and tracking for multi-step work\n# memory - Persistent memory across sessions (personal notes + user profile)\n# session_search - Search and recall past conversations (FTS5 + Gemini Flash summarization)\n# tts - Text-to-speech (Edge TTS free, ElevenLabs, OpenAI, MiniMax, Mistral)\n# cronjob - Schedule and manage automated tasks (CLI-only)\n#\n# Composite toolsets:\n# debugging - terminal + web + file (for troubleshooting)\n# safe - web + vision (no terminal access)\n\n# NOTE: The top-level \"toolsets\" key is deprecated and ignored.\n# Tool configuration is managed per-platform via platform_toolsets above.\n# Use `hermes tools` to configure interactively, or edit platform_toolsets directly.\n#\n# CLI override: hermes chat --toolsets terminal,web,file\n\n# =============================================================================\n# MCP (Model Context Protocol) Servers\n# =============================================================================\n# Connect to external MCP servers to add tools from the MCP ecosystem.\n# Each server's tools are automatically discovered and registered.\n# See website/docs/user-guide/features/mcp.md for full documentation.\n#\n# Stdio servers (spawn a subprocess):\n# command: the executable to run\n# args: command-line arguments\n# env: environment variables (only these + safe defaults passed to subprocess)\n#\n# HTTP servers (connect to a URL):\n# url: the MCP server endpoint\n# headers: HTTP headers (e.g., for authentication)\n#\n# Optional per-server settings:\n# timeout: tool call timeout in seconds (default: 120)\n# connect_timeout: initial connection timeout (default: 60)\n# keepalive_interval: liveness ping cadence in seconds (default: 180).\n# Lower it below the server's session TTL for servers that expire idle\n# sessions quickly (e.g. Unreal Engine editor MCP, ~15s), otherwise idle\n# tool calls hit an expired session and pay a slow reconnect. Floored at 5s.\n#\n# mcp_servers:\n# time:\n# command: uvx\n# args: [\"mcp-server-time\"]\n# filesystem:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-filesystem\", \"/home/user\"]\n# notion:\n# url: https://mcp.notion.com/mcp\n# github:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-github\"]\n# env:\n# GITHUB_PERSONAL_ACCESS_TOKEN: \"ghp_...\"\n#\n# Sampling (server-initiated LLM requests) — enabled by default.\n# Per-server config under the 'sampling' key:\n# analysis:\n# command: npx\n# args: [\"-y\", \"analysis-server\"]\n# sampling:\n# enabled: true # default: true\n# model: \"gemini-3-flash\" # override model (optional)\n# max_tokens_cap: 4096 # max tokens per request\n# timeout: 30 # LLM call timeout (seconds)\n# max_rpm: 10 # max requests per minute\n# allowed_models: [] # model whitelist (empty = all)\n# max_tool_rounds: 5 # tool loop limit (0 = disable)\n# log_level: \"info\" # audit verbosity\n\n# =============================================================================\n# Text-to-Speech\n# =============================================================================\n# TTS defaults to Edge TTS unless changed in ~/.hermes/config.yaml.\n# Gemini TTS supports persona/director prompt files, and Gemini 3.1 Flash TTS\n# can use a hidden auxiliary rewrite pass to insert expressive square-bracket\n# audio tags into the TTS script without showing tags in chat.\n#\n# tts:\n# provider: \"gemini\"\n# gemini:\n# model: \"gemini-3.1-flash-tts-preview\"\n# voice: \"Kore\"\n# audio_tags: false\n# persona_prompt_file: \"\" # e.g. ~/.hermes/tts/radio-host.md\n\n# =============================================================================\n# Voice Transcription (Speech-to-Text)\n# =============================================================================\n# Automatically transcribe voice messages on messaging platforms.\n# Providers: local (free, faster-whisper) | groq (free tier) | openai (Whisper API) | mistral (Voxtral Transcribe)\n# Set the corresponding API key in .env: GROQ_API_KEY, OPENAI_API_KEY, or MISTRAL_API_KEY.\nstt:\n enabled: true\n # provider: \"local\" # auto-detected if omitted\n local:\n model: \"base\" # tiny | base | small | medium | large-v3 | turbo\n # language: \"\" # auto-detect; set to \"en\", \"es\", \"fr\", etc. to force\n openai:\n model: \"whisper-1\" # whisper-1 | gpt-4o-mini-transcribe | gpt-4o-transcribe\n # mistral:\n # model: \"voxtral-mini-latest\" # voxtral-mini-latest | voxtral-mini-2602\n # deepinfra:\n # # Model id is discovered live from the DeepInfra catalog filtered\n # # by the `stt` surface tag — leave `model` blank to take the first\n # # live result. Pin only when you need a specific Whisper variant.\n # model: \"\"\n\n# Text-to-speech. Only the deepinfra block is documented here — the\n# remaining providers (edge, openai, xai, minimax, mistral, gemini,\n# elevenlabs, neutts, kittentts, piper) inherit sensible defaults from\n# DEFAULT_CONFIG in hermes_cli/config.py.\n# tts:\n# provider: \"deepinfra\"\n# deepinfra:\n# # Model id is discovered live from the DeepInfra catalog filtered\n# # by the `tts` surface tag — leave `model` blank to take the first\n# # live result.\n# model: \"\"\n# voice: \"default\"\n\n# Image generation. Each provider plugin reads its own ``image_gen.<name>``\n# block; deepinfra discovers models live from\n# api.deepinfra.com/v1/openai/models filtered by the ``image-gen`` tag —\n# no model id is hardcoded, so retired models disappear automatically.\n# image_gen:\n# provider: \"deepinfra\"\n# deepinfra:\n# # Leave `model` blank for the first live `image-gen`-tagged result.\n# model: \"\"\n\n# =============================================================================\n# Response Pacing (Messaging Platforms)\n# =============================================================================\n# Add human-like delays between message chunks.\n# human_delay:\n# mode: \"off\" # \"off\" | \"natural\" | \"custom\"\n# min_ms: 800 # Min delay (custom mode only)\n# max_ms: 2500 # Max delay (custom mode only)\n\n# =============================================================================\n# Session Logging\n# =============================================================================\n# Session trajectories are automatically saved to logs/ directory.\n# Each session creates: logs/session_YYYYMMDD_HHMMSS_UUID.json\n#\n# The session ID is displayed in the welcome banner for easy reference.\n# Logs contain full conversation history in trajectory format:\n# - System prompt, user messages, assistant responses\n# - Tool calls with inputs/outputs\n# - Timestamps for debugging\n#\n# No configuration needed - logging is always enabled.\n# To disable, you would need to modify the source code.\n\n# =============================================================================\n# Code Execution Sandbox (Programmatic Tool Calling)\n# =============================================================================\n# The execute_code tool runs Python scripts that call Hermes tools via RPC.\n# Intermediate tool results stay out of the LLM's context window.\ncode_execution:\n timeout: 300 # Max seconds per script before kill (default: 300 = 5 min)\n max_tool_calls: 50 # Max RPC tool calls per execution (default: 50)\n\n# =============================================================================\n# Subagent Delegation\n# =============================================================================\n# The delegate_task tool spawns child agents with isolated context.\n# Supports single tasks and batch mode (default 3 parallel, configurable).\ndelegation:\n max_iterations: 50 # Max tool-calling turns per child (default: 50)\n # max_concurrent_children: 3 # Max parallel child agents per batch (default: 3, floor: 1, no ceiling).\n # WARNING: values above 10 multiply API cost linearly.\n # max_spawn_depth: 1 # Delegation tree depth cap (range: 1-3, default: 1 = flat).\n # Raise to 2 to allow workers to spawn their own subagents.\n # Requires role=\"orchestrator\" on intermediate agents.\n # orchestrator_enabled: true # Kill switch for role=\"orchestrator\" children (default: true).\n # subagent_auto_approve: false # When a subagent hits a dangerous-command approval prompt, auto-deny (default: false)\n # or auto-approve \"once\" (true) instead of blocking on stdin.\n # The parent TUI owns stdin, so blocking would deadlock; non-interactive resolution is required.\n # Both choices emit a logger.warning audit line. Flip to true only for cron/batch pipelines.\n # inherit_mcp_toolsets: true # When explicit child toolsets are narrowed, also keep the parent's MCP toolsets (default: true). Set false for strict intersection.\n # model: \"google/gemini-3-flash-preview\" # Override model for subagents (empty = inherit parent)\n # provider: \"openrouter\" # Override provider for subagents (empty = inherit parent)\n # # Resolves full credentials (base_url, api_key) automatically.\n # # Supported: openrouter, nous, zai, kimi-coding, minimax\n\n# =============================================================================\n# Honcho Integration (Cross-Session User Modeling)\n# =============================================================================\n# AI-native persistent memory via Honcho (https://honcho.dev/).\n# Builds a deeper understanding of the user across sessions and tools.\n# Runs alongside USER.md — additive, not a replacement.\n#\n# Requires: pip install honcho-ai\n# Config: ~/.honcho/config.json (shared with Claude Code, Cursor, etc.)\n# API key: HONCHO_API_KEY in ~/.hermes/.env or ~/.honcho/config.json\n#\n# Hermes-specific overrides (optional — most config comes from ~/.honcho/config.json):\n# honcho: {}\n\n# =============================================================================\n# Display\n# =============================================================================\ndisplay:\n # Use compact banner mode (hides the ASCII-art banner, shows a single line).\n # true: Compact single-line banner\n # false: Full ASCII banner with tool/skill summary (default)\n compact: false\n\n # Tool progress display level (CLI and gateway)\n # off: Silent — no tool activity shown, just the final response\n # new: Show a tool indicator only when the tool changes (skip repeats)\n # all: Show every tool call with a short preview (default)\n # verbose: Full args, results, and debug logs (same as /verbose)\n # log: Silent in chat; append every tool call to ~/.hermes/logs/tool_calls.log (gateway only)\n # Toggle at runtime with /verbose in the CLI\n tool_progress: all\n\n # Per-platform defaults can be quieter than the global setting. Telegram\n # tunes for mobile: tool_progress and busy_ack_detail default off (no\n # per-tool breadcrumb stream, no \"iteration 21/60\" debug detail in busy\n # acks or heartbeats), but interim_assistant_messages and\n # long_running_notifications STAY ON so the user has real signal between\n # turn start and final answer (mid-turn assistant commentary + a single\n # edit-in-place \"⏳ Working — N min\" heartbeat). Override under\n # display.platforms.telegram.\n\n # Auto-cleanup of temporary progress bubbles after the final response lands.\n # On platforms that support message deletion (currently Telegram), this\n # removes the tool-progress bubble, \"⏳ Still working...\" notices, and\n # context-pressure status messages once the final reply has been delivered —\n # keeping long-running turns visible live, then tidy afterward. Failed runs\n # leave the bubbles in place as breadcrumbs. Off by default.\n # Per-platform override: display.platforms.telegram.cleanup_progress\n # true: Delete tracked progress/status bubbles on successful turn\n # false: Leave everything in place (default)\n # Example:\n # display:\n # platforms:\n # telegram:\n # cleanup_progress: true\n cleanup_progress: false\n\n # Natural mid-turn assistant updates.\n # On gateway platforms, when true, completed assistant status messages are\n # sent as separate chat messages. On the Desktop app, when true, mid-turn\n # assistant narration streamed between tool calls is kept in the transcript\n # instead of the bubble collapsing to only the final message on completion.\n # Independent of tool_progress and gateway streaming.\n # true: Keep/send mid-turn assistant updates (default)\n # false: Only keep/send the final response\n interim_assistant_messages: true\n\n # Gateway-only long-running status heartbeats.\n # When false, the platform does not receive periodic \"⏳ Working — N min\"\n # notifications even if agent.gateway_notify_interval is non-zero. The\n # heartbeat edits a single message in place (where the adapter supports\n # editing) instead of posting a new bubble each interval.\n # Default: true everywhere, including Telegram (silent agents are worse\n # than a single edit-in-place heartbeat).\n long_running_notifications: true\n\n # Include detailed iteration/tool/status context in busy acknowledgments\n # and long-running heartbeats. When true, busy acks show \"iteration 21/60,\n # terminal, 10 min\" and the heartbeat shows \"⏳ Working — 12 min,\n # iteration 21/60, terminal\". When false (Telegram default), both stay\n # terse: \"Interrupting current task\" and \"⏳ Working — 12 min, terminal\".\n busy_ack_detail: true\n\n # What Enter does when Hermes is already busy (CLI and gateway platforms).\n # interrupt: Interrupt the current run and redirect Hermes (default)\n # queue: Queue your message for the next turn\n # steer: Inject your message mid-run via /steer, arriving at the agent\n # after the next tool call — no interrupt, no role violation.\n # Falls back to 'queue' if the agent isn't running yet or if\n # images are attached (steer only carries text).\n # Ctrl+C (or /stop in gateway) always interrupts regardless of this setting.\n # Toggle at runtime with /busy <interrupt|queue|steer>.\n busy_input_mode: interrupt\n\n # Background process notifications (gateway/messaging only).\n # Controls how chatty the process watcher is when you use\n # terminal(background=true, notify_on_complete=true) from Telegram/Discord/etc.\n # off: No watcher messages at all\n # result: Only the final completion message\n # error: Only the final message when exit code != 0\n # all: Running output updates + final message (default)\n background_process_notifications: all\n\n\n # Play terminal bell when agent finishes a response.\n # Useful for long-running tasks — your terminal will ding when the agent is done.\n # Works over SSH. Most terminals can be configured to flash the taskbar or play a sound.\n # true: Ring the terminal bell on each response\n # false: Silent (default)\n bell_on_complete: false\n\n # Show model reasoning/thinking before each response.\n # When enabled, a dim box shows the model's thought process above the response.\n # Toggle at runtime with /reasoning show or /reasoning hide.\n # true: Show the reasoning box\n # false: Hide reasoning (default)\n show_reasoning: false\n\n # Stream tokens to the terminal as they arrive instead of waiting for the\n # full response. The response box opens on first token and text appears\n # line-by-line. Tool calls are still captured silently.\n # true: Stream tokens as they arrive (default)\n # false: Wait for the full response before rendering\n streaming: true\n\n # Show [HH:MM] timestamps on user input and assistant response labels.\n # timestamps: false\n\n # ───────────────────────────────────────────────────────────────────────────\n # Skin / Theme\n # ───────────────────────────────────────────────────────────────────────────\n # Customize CLI visual appearance — banner colors, spinner faces, tool prefix,\n # response box label, and branding text. Change at runtime with /skin <name>.\n #\n # Built-in skins:\n # default — Classic Hermes gold/kawaii\n # ares — Crimson/bronze war-god theme with spinner wings\n # mono — Clean grayscale monochrome\n # slate — Cool blue developer-focused\n # daylight — Bright light-mode theme\n # warm-lightmode — Warm paper-tone light-mode theme\n # poseidon — Sea-green/teal Olympian theme\n # sisyphus — Earthy stone-and-moss theme\n # charizard — Fiery orange dragon theme\n #\n # Custom skins: drop a YAML file in ~/.hermes/skins/<name>.yaml\n # Schema (all fields optional, missing values inherit from default):\n #\n # name: my-theme\n # description: Short description\n # colors:\n # banner_border: \"#HEX\" # Panel border\n # banner_title: \"#HEX\" # Panel title\n # banner_accent: \"#HEX\" # Section headers (Available Tools, etc.)\n # banner_dim: \"#HEX\" # Dim/muted text\n # banner_text: \"#HEX\" # Body text (tool names, skill names)\n # ui_accent: \"#HEX\" # UI accent color\n # response_border: \"#HEX\" # Response box border color\n # spinner:\n # waiting_faces: [\"(⚔)\", \"(⛨)\"] # Faces shown while waiting\n # thinking_faces: [\"(⚔)\", \"(⌁)\"] # Faces shown while thinking\n # thinking_verbs: [\"forging\", \"plotting\"] # Verbs for spinner messages\n # wings: # Optional left/right spinner decorations\n # - [\"⟪⚔\", \"⚔⟫\"]\n # - [\"⟪▲\", \"▲⟫\"]\n # branding:\n # agent_name: \"My Agent\" # Banner title and branding\n # welcome: \"Welcome message\" # Shown at CLI startup\n # response_label: \" ⚔ Agent \" # Response box header label\n # prompt_symbol: \"⚔\" # Prompt symbol (bare token; renderers add trailing space)\n # tool_prefix: \"╎\" # Tool output line prefix (default: ┊)\n #\n skin: default\n\n# =============================================================================\n# Model Aliases — short names for /model command\n# =============================================================================\n# Map short aliases to exact (model, provider, base_url) tuples.\n# Used by /model tab completion and resolve_alias().\n# Aliases are checked BEFORE the models.dev catalog, so they can route\n# to endpoints not in the catalog (e.g. Ollama Cloud, local servers).\n#\n# model_aliases:\n# opus:\n# model: claude-opus-4-6\n# provider: anthropic\n# qwen:\n# model: \"qwen3.5:397b\"\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n# glm:\n# model: glm-4.7\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n\n# =============================================================================\n# Privacy\n# =============================================================================\n# privacy:\n# # Redact PII from the LLM context prompt.\n# # When true, phone numbers are stripped and user/chat IDs are replaced\n# # with deterministic hashes before being sent to the model.\n# # Names and usernames are NOT affected (user-chosen, publicly visible).\n# # Routing/delivery still uses the original values internally.\n# redact_pii: false\n\n# =============================================================================\n# Shell-script hooks\n# =============================================================================\n# Register shell scripts as plugin-hook callbacks. Each entry is executed as\n# a subprocess (shell=False, shlex.split) with a JSON payload on stdin. On\n# stdout the script may return JSON that either blocks the tool call or\n# injects context into the next LLM call.\n#\n# Valid events (mirror hermes_cli.plugins.VALID_HOOKS):\n# pre_tool_call, post_tool_call, pre_llm_call, post_llm_call,\n# pre_api_request, post_api_request, on_session_start, on_session_end,\n# on_session_finalize, on_session_reset, subagent_stop\n#\n# First-use consent: each (event, command) pair prompts once on a TTY, then\n# is persisted to ~/.hermes/shell-hooks-allowlist.json. Non-interactive\n# runs (gateway, cron) need --accept-hooks, HERMES_ACCEPT_HOOKS=1, or the\n# hooks_auto_accept key below.\n#\n# See website/docs/user-guide/features/hooks.md for the full JSON wire\n# protocol and worked examples.\n#\n# hooks:\n# pre_tool_call:\n# - matcher: \"terminal\"\n# command: \"~/.hermes/agent-hooks/block-rm-rf.sh\"\n# timeout: 10\n# post_tool_call:\n# - matcher: \"write_file|patch\"\n# command: \"~/.hermes/agent-hooks/auto-format.sh\"\n# pre_llm_call:\n# - command: \"~/.hermes/agent-hooks/inject-cwd-context.sh\"\n# subagent_stop:\n# - command: \"~/.hermes/agent-hooks/log-orchestration.sh\"\n#\n# hooks_auto_accept: false\n\n\n# =============================================================================\n# Telemetry\n# =============================================================================\n# Shared metrics are disabled by default. When enabled, Hermes writes only\n# allowlisted aggregate counters and immutable JSON\n# packages under $HERMES_HOME/telemetry/shared_metrics; it does not upload them.\n# Packages include a random profile-scoped ID that stays stable until this\n# directory is deleted. It is not derived from hardware, account, or host data.\n# Successfully exported local history is retained for 30 days; pending deltas\n# are retained until they can be exported.\ntelemetry:\n shared_metrics:\n enabled: false\n\n\n# =============================================================================\n# Update Behavior\n# =============================================================================\nupdates:\n # Create a full HERMES_HOME zip before every `hermes update`.\n # Backups land in ~/.hermes/backups/ and can be restored with `hermes import`.\n # Off by default because large homes can add minutes to every update.\n pre_update_backup: false\n\n # Number of pre-update backup zips to retain.\n backup_keep: 5\n\n # What non-interactive updates do with local source edits in the Hermes repo.\n # Interactive terminal updates always prompt before restoring the autostash.\n #\n # stash - auto-stash before pull, then auto-restore after success (default)\n # discard - drop the update-created stash after success; use only on managed\n # installs where local source edits should not persist\n non_interactive_local_changes: \"stash\"\n\n\n# =============================================================================\n# Web Dashboard\n# =============================================================================\n# OAuth gate configuration for `hermes dashboard --host <non-loopback>`.\n# The bundled Nous Portal plugin reads these on startup; settings here are\n# the canonical surface. Each can be overridden by an environment variable:\n#\n# dashboard.oauth.client_id <- HERMES_DASHBOARD_OAUTH_CLIENT_ID\n# dashboard.oauth.portal_url <- HERMES_DASHBOARD_PORTAL_URL\n# dashboard.public_url <- HERMES_DASHBOARD_PUBLIC_URL\n#\n# Env wins when set to a non-empty value. This is what Fly.io's platform-\n# secret injection uses to push per-deploy client_ids without needing to\n# bake a config.yaml into the image. Empty env values are treated as unset\n# so a provisioned-but-not-populated secret can't shadow a valid entry here.\n#\n# Local dev / on-prem deploys should typically set these via config.yaml\n# (the ~/.hermes/.env file is reserved for API keys and secrets).\n#\n# dashboard:\n# oauth:\n# client_id: \"\" # agent:{instance_id}; Portal provisions this at deploy\n# portal_url: \"\" # blank → default https://portal.nousresearch.com\n#\n# # Force the absolute base URL the OAuth callback (and any other public\n# # URL the dashboard hands to external systems) is built from. Set this\n# # for deploys behind reverse proxies that don't reliably forward\n# # X-Forwarded-Host / X-Forwarded-Proto / X-Forwarded-Prefix (manual\n# # nginx setups, on-prem ingresses, custom-domain Fly deploys without\n# # full proxy header chains).\n# #\n# # When set, the value is the complete authority: scheme + host +\n# # optional path prefix (e.g. \"https://example.com/hermes\"). The OAuth\n# # callback URL becomes \"<public_url>/auth/callback\" — X-Forwarded-Prefix\n# # is IGNORED on this code path because the operator has explicitly\n# # declared the public URL and we no longer need to guess.\n# #\n# # Leave empty to use the existing proxy-header reconstruction (the\n# # default — works on Fly.io out of the box).\n# #\n# # public_url: \"https://example.com/hermes\"\n#\n# -----------------------------------------------------------------------------\n# Self-hosted OIDC dashboard auth (generic OpenID Connect — Authentik,\n# Keycloak, Zitadel, Authelia, Auth0, Okta, Google, …). Use this INSTEAD of the\n# nous block above when gating the dashboard with your own identity provider.\n# Each setting can be overridden by an environment variable:\n#\n# dashboard.oauth.self_hosted.issuer <- HERMES_DASHBOARD_OIDC_ISSUER\n# dashboard.oauth.self_hosted.client_id <- HERMES_DASHBOARD_OIDC_CLIENT_ID\n# dashboard.oauth.self_hosted.scopes <- HERMES_DASHBOARD_OIDC_SCOPES\n# dashboard.oauth.self_hosted.client_secret <- HERMES_DASHBOARD_OIDC_CLIENT_SECRET\n#\n# dashboard:\n# oauth:\n# provider: self-hosted\n# self_hosted:\n# issuer: \"https://auth.example.com/application/o/hermes/\" # required\n# client_id: \"hermes-dashboard\" # required\n# scopes: \"openid profile email\" # optional\n#\n# # OPTIONAL — set ONLY if your IDP registered the client as\n# # *confidential* (Authentik / Keycloak often default to this). When\n# # set, Hermes authenticates the client at the token endpoint\n# # (client_secret_basic or client_secret_post, auto-selected from the\n# # IDP's discovery doc) IN ADDITION to PKCE. Leave unset for a public\n# # (PKCE-only) client — the common case.\n# #\n# # This is a CREDENTIAL: prefer setting HERMES_DASHBOARD_OIDC_CLIENT_SECRET\n# # in ~/.hermes/.env over putting it here in config.yaml.\n# # client_secret: \"\"\n\n# =============================================================================\n# External secret sources\n# =============================================================================\n# Pull provider credentials from external secret managers at process startup\n# instead of storing them in ~/.hermes/.env. Only the manager's bootstrap\n# credential (e.g. BWS_ACCESS_TOKEN / OP_SERVICE_ACCOUNT_TOKEN) lives in .env\n# (or your shell / desktop session); everything else rotates centrally.\n# Failures never block startup — Hermes warns once and continues with\n# whatever .env already had.\n#\n# Multiple sources can be enabled at once:\n# - \"mapped\" sources (explicit VAR -> ref bindings, e.g. 1Password's env:\n# map) beat \"bulk\" sources (whole-project dumps like Bitwarden BSM)\n# - within a shape, the first source to claim a var wins; later claims\n# are skipped with a startup warning (never a silent clobber)\n# - a source's override_existing lets it beat .env/shell values, but\n# never another secret source's claim\n# Docs: https://hermes-agent.nousresearch.com/docs/user-guide/secrets/\n#\n# secrets:\n# # Optional explicit ordering of enabled sources.\n# # sources: [onepassword, bitwarden]\n#\n# # ---- Bitwarden Secrets Manager (bws CLI) --------------------------------\n# bitwarden:\n# enabled: false\n# access_token_env: BWS_ACCESS_TOKEN # bootstrap token, sourced from .env\n# project_id: \"\" # UUID of the BSM project to sync\n# server_url: \"\" # \"\" = US Cloud; EU/self-hosted URL otherwise\n# cache_ttl_seconds: 300 # 0 disables fresh caching\n# encrypted_cache: # optional encrypted stale fallback\n# enabled: false\n# max_stale_seconds: 0 # 0 disables stale fallback\n# override_existing: true # BSM values win over existing env\n# auto_install: true # lazy-download bws into ~/.hermes/bin\n#\n# # ---- 1Password (op CLI) -------------------------------------------------\n# onepassword:\n# enabled: false\n# # Map env-var names to op:// secret references. Each is resolved with a\n# # single `op read` at startup.\n# env:\n# OPENAI_API_KEY: \"op://Private/OpenAI/api key\"\n# ANTHROPIC_API_KEY: \"op://Private/Anthropic/credential\"\n# account: \"\" # op --account shorthand; \"\" = default\n# service_account_token_env: OP_SERVICE_ACCOUNT_TOKEN # headless auth; unset = desktop session\n# binary_path: \"\" # \"\" = resolve op via PATH; else absolute path\n# cache_ttl_seconds: 300 # 0 disables BOTH cache layers\n# override_existing: true # resolved values win over existing env\n#\n# # ---- Command helper (any CLI vault) --------------------------------------\n# # Run a user-configured helper that prints KEY=VALUE lines on stdout —\n# # works with any secret store that has a CLI: keepassxc-cli, secret-tool,\n# # pass, gpg, or a script that cats a tmpfs env file. Composes with the\n# # sources above (enable any combination). POSIX-only (needs /bin/sh).\n# # The helper must be fast and NON-interactive (hard timeout, 1 MiB cap);\n# # its stderr is discarded so diagnostics can't leak secret material.\n# command:\n# enabled: false\n# command: \"cat /run/user/1000/hermes-secrets.env\"\n# helper_timeout_seconds: 3\n# override_existing: false # .env/shell win by default\n"}, "e1a6becf862db7050c41b4ecd123b9992785386c": {"t": 1785768022.1231918, "n": 80809, "h": "41570c38e8bd13bc", "c": "# Hermes Agent CLI Configuration\n# Copy this file to cli-config.yaml and customize as needed.\n# This file configures the CLI behavior. Environment variables in .env take precedence.\n\n# =============================================================================\n# Model Configuration\n# =============================================================================\nmodel:\n # Default model to use (can be overridden with --model flag)\n # Both \"default\" and \"model\" work as the key name here.\n default: \"anthropic/claude-opus-4.6\"\n \n # Inference provider selection:\n # \"auto\" - Auto-detect from credentials (default)\n # \"openrouter\" - OpenRouter (requires: OPENROUTER_API_KEY or OPENAI_API_KEY)\n # \"nous\" - Nous Portal OAuth (requires: hermes auth add nous)\n # \"nous-api\" - Nous Portal API key (requires: NOUS_API_KEY)\n # \"anthropic\" - Direct Anthropic API (requires: ANTHROPIC_API_KEY)\n # \"openai-codex\" - OpenAI Codex (requires: hermes auth)\n # \"copilot\" - GitHub Copilot / GitHub Models (requires: GITHUB_TOKEN)\n # \"gemini\" - Use Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n # \"zai\" - Use z.ai / ZhipuAI GLM models (requires: GLM_API_KEY)\n # \"kimi-coding\" - Kimi / Moonshot AI (requires: KIMI_API_KEY)\n # \"minimax\" - MiniMax global (requires: MINIMAX_API_KEY)\n # \"minimax-cn\" - MiniMax China (requires: MINIMAX_CN_API_KEY)\n # \"huggingface\" - Hugging Face Inference (requires: HF_TOKEN)\n # \"nvidia\" - NVIDIA NIM / build.nvidia.com (requires: NVIDIA_API_KEY)\n # \"xiaomi\" - Xiaomi MiMo (requires: XIAOMI_API_KEY)\n # \"arcee\" - Arcee AI Trinity models (requires: ARCEEAI_API_KEY)\n # \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY — https://ollama.com/settings)\n # \"deepinfra\" - DeepInfra (requires: DEEPINFRA_API_KEY)\n # \"kilocode\" - KiloCode gateway (requires: KILOCODE_API_KEY)\n # \"azure-foundry\" - Microsoft Foundry / Azure OpenAI (API key or Entra ID)\n # \"lmstudio\" - LM Studio local server (optional: LM_API_KEY, defaults to http://127.0.0.1:1234/v1)\n #\n # Local servers (LM Studio, Ollama, vLLM, llama.cpp):\n # \"custom\" - Any other OpenAI-compatible endpoint. Set base_url below.\n # Aliases: \"ollama\", \"vllm\", \"llamacpp\" all map to \"custom\".\n # LM Studio is first-class and uses provider: \"lmstudio\".\n # It works with both no-auth and auth-enabled server modes.\n #\n # Can also be overridden for a single invocation with the --provider flag.\n provider: \"auto\"\n \n # API configuration (falls back to OPENROUTER_API_KEY env var)\n # api_key: \"your-key-here\" # Uncomment to set here instead of .env\n base_url: \"https://openrouter.ai/api/v1\"\n\n # Azure Foundry keyless auth example:\n # provider: \"azure-foundry\"\n # base_url: \"https://<resource>.openai.azure.com/openai/v1\"\n # auth_mode: \"entra_id\" # DefaultAzureCredential: az login, managed identity, workload identity, etc.\n # default: \"gpt-4o\" # Deployment/model name\n # entra:\n # scope: \"https://ai.azure.com/.default\" # Optional; this is the default.\n\n # ── Token limits — two settings, easy to confuse ──────────────────────────\n #\n # context_length: TOTAL context window (input + output tokens combined).\n # Controls when Hermes compresses history and validates requests.\n # Leave unset — Hermes auto-detects the correct value from the provider.\n # Set manually only when auto-detection is wrong (e.g. a local server with\n # a custom num_ctx, or a proxy that doesn't expose /v1/models).\n #\n # context_length: 131072\n #\n # max_tokens: OUTPUT cap — maximum tokens the model may generate per response.\n # Unrelated to how long your conversation history can be.\n # The OpenAI-standard name \"max_tokens\" is a misnomer; Anthropic's native\n # API has since renamed it \"max_output_tokens\" for clarity.\n # Leave unset to use the model's native output ceiling (recommended).\n # Set only if you want to deliberately limit individual response length.\n #\n# max_tokens: 8192\n\n # ── Custom request headers (optional) ─────────────────────────────────────\n #\n # default_headers: extra HTTP headers sent on every request to an\n # OpenAI-compatible endpoint. User values take precedence over the\n # provider/SDK defaults, so this is the supported way to override the\n # OpenAI Python SDK's identifying headers (User-Agent: OpenAI/Python ...,\n # X-Stainless-*) when a custom provider sits behind a gateway/WAF that\n # rejects them — e.g. an upstream that returns \"502 Upstream access\n # forbidden\" for the SDK default User-Agent but accepts a plain one.\n # Applies on the OpenAI wire only (not native Anthropic / Bedrock).\n #\n # default_headers:\n # User-Agent: \"curl/8.7.1\"\n #\n # extra_headers: accepted as an alias of default_headers (merged, with\n # extra_headers winning when both are set) — matches the per-provider\n # extra_headers key below.\n #\n # Per-provider variant: named providers / custom_providers entries accept an\n # extra_headers dict scoped to that endpoint only — for reverse proxies,\n # gateways, or custom auth (e.g. Cloudflare Access service tokens).\n # Merged onto SDK/provider defaults with the entry's values winning.\n # Header values are treated as secrets and are never logged.\n #\n # providers:\n # my-proxy:\n # base_url: \"https://llm.internal.example.com/v1\"\n # key_env: \"MY_PROXY_API_KEY\"\n # extra_headers:\n # CF-Access-Client-Id: \"xxxx.access\"\n # CF-Access-Client-Secret: \"${CF_ACCESS_SECRET}\"\n # X-Client-Name: \"hermes-agent\"\n\n# Named provider overrides (optional)\n# Use this for per-provider request timeouts, non-stream stale timeouts,\n# and per-model exceptions.\n# Applies to the primary turn client on every api_mode (OpenAI-wire, native\n# Anthropic, and Anthropic-compatible providers), the fallback chain, and\n# client rebuilds during credential rotation. For OpenAI-wire chat\n# completions (streaming and non-streaming) the configured value is also\n# used as the per-request ``timeout=`` kwarg so it wins over the legacy\n# HERMES_API_TIMEOUT env var (which still applies when no config is set).\n# ``stale_timeout_seconds`` controls the non-streaming stale-call detector and\n# wins over the legacy HERMES_API_CALL_STALE_TIMEOUT env var. Leaving these\n# unset keeps the legacy defaults (HERMES_API_TIMEOUT=1800s,\n# HERMES_API_CALL_STALE_TIMEOUT=90s, native Anthropic 900s). The\n# implicit non-stream stale detector is auto-disabled for local endpoints\n# and can scale upward for very large contexts.\n#\n# Not currently wired for AWS Bedrock (bedrock_converse + AnthropicBedrock\n# SDK paths) — those use boto3 with its own timeout configuration.\n#\n# providers:\n# ollama-local:\n# request_timeout_seconds: 300 # Longer timeout for local cold-starts\n# stale_timeout_seconds: 900 # Explicitly re-enable stale detection on local endpoints\n# anthropic:\n# request_timeout_seconds: 30 # Fast-fail cloud requests\n# models:\n# claude-opus-4.6:\n# timeout_seconds: 600 # Longer timeout for extended-thinking Opus calls\n# openai-codex:\n# models:\n# gpt-5.4:\n# stale_timeout_seconds: 1800 # Longer non-stream stale timeout for slow large-context turns\n\n# =============================================================================\n# OpenRouter Provider Routing (only applies when using OpenRouter)\n# =============================================================================\n# Control how requests are routed across providers on OpenRouter.\n# See: https://openrouter.ai/docs/guides/routing/provider-selection\n#\n# provider_routing:\n# # Sort strategy: \"price\" (default), \"throughput\", or \"latency\"\n# # Append :nitro to model name for a shortcut to throughput sorting.\n# sort: \"throughput\"\n#\n# # Only allow these providers (provider slugs from OpenRouter)\n# # only: [\"anthropic\", \"google\"]\n#\n# # Skip these providers entirely\n# # ignore: [\"deepinfra\", \"fireworks\"]\n#\n# # Try providers in this order (overrides default load balancing)\n# # order: [\"anthropic\", \"google\", \"together\"]\n#\n# # Require providers to support all parameters in your request\n# # require_parameters: true\n#\n# # Data policy: \"allow\" (default) or \"deny\" to exclude providers that may store data\n# # data_collection: \"deny\"\n\n# =============================================================================\n# OpenRouter Response Caching (only applies when using OpenRouter)\n# =============================================================================\n# Cache identical API responses at the OpenRouter edge for free instant replays.\n# When enabled, identical requests (same model, messages, parameters) return\n# cached responses with zero billing. Separate from Anthropic prompt caching.\n# See: https://openrouter.ai/docs/guides/features/response-caching\n#\n# openrouter:\n# response_cache: true # Enable response caching (default: true)\n# response_cache_ttl: 300 # Cache TTL in seconds, 1-86400 (default: 300)\n\n# =============================================================================\n# Git Worktree Isolation\n# =============================================================================\n# When enabled, each CLI session creates an isolated git worktree so multiple\n# agents can work on the same repo concurrently without file collisions.\n# Equivalent to always passing --worktree / -w on the command line.\n#\n# worktree: true # Always create a worktree when in a git repo\n# worktree: false # Default — only create when -w flag is passed\n#\n# By default a new worktree branches from the freshly-fetched remote tip\n# (the current branch's upstream, else the remote's default branch) so it\n# starts current with the project instead of from the local clone's\n# (possibly stale) HEAD. Set worktree_sync: false to branch from local HEAD\n# instead — useful when offline or when you deliberately want the clone's\n# exact current state as the base.\n#\n# worktree_sync: true # Default — branch from the fetched remote tip\n# worktree_sync: false # Branch from local HEAD (offline / pinned base)\n\n# =============================================================================\n# Terminal Tool Configuration\n# =============================================================================\n# Choose ONE of the following terminal configurations by uncommenting it.\n# The terminal tool executes commands in the specified environment.\n\n# -----------------------------------------------------------------------------\n# OPTION 1: Local execution (default)\n# Commands run directly on your machine in the current directory\n# -----------------------------------------------------------------------------\n# Working directory behavior:\n# - CLI (`hermes` command): Uses \".\" (current directory where you run hermes)\n# - Gateway/messaging/cron: Uses terminal.cwd here; legacy .env cwd values are deprecated\nterminal:\n backend: \"local\"\n cwd: \".\" # For local backend: \".\" = current directory. Ignored for remote backends unless a backend documents otherwise.\n timeout: 180\n # HOME policy for tool subprocesses:\n # auto - default: host uses your real HOME; containers use HERMES_HOME/home\n # real - force your real OS-user HOME\n # profile - force HERMES_HOME/home for strict per-profile CLI config isolation\n home_mode: \"auto\"\n docker_mount_cwd_to_workspace: false # SECURITY: off by default. Opt in to mount the launch cwd into Docker /workspace.\n lifetime_seconds: 300\n # sudo_password: \"hunter2\" # Optional: pipe a sudo password via sudo -S. SECURITY WARNING: plaintext.\n # sudo_password: \"\" # Explicit empty password: try empty and never open the interactive sudo prompt.\n\n# -----------------------------------------------------------------------------\n# OPTION 2: SSH remote execution\n# Commands run on a remote server - agent code stays local (sandboxed)\n# Great for: keeping agent isolated from its own code, using powerful remote hardware\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"ssh\"\n# cwd: \"/home/myuser/project\" # Path on the REMOTE server\n# timeout: 180\n# lifetime_seconds: 300\n# ssh_host: \"my-server.example.com\"\n# ssh_user: \"myuser\"\n# ssh_port: 22\n# ssh_key: \"~/.ssh/id_rsa\" # Optional - uses ssh-agent if not specified\n\n# -----------------------------------------------------------------------------\n# OPTION 3: Docker container\n# Commands run in an isolated Docker container\n# Great for: reproducible environments, testing, isolation\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"docker\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /)\n# timeout: 180\n# lifetime_seconds: 300\n# docker_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# docker_mount_cwd_to_workspace: true # Explicit opt-in: mount your launch cwd into /workspace\n# # Optional: run the container as your host user's uid:gid so files written\n# # into bind-mounted dirs are owned by you, not root. Drops SETUID/SETGID\n# # caps too since no gosu privilege drop is needed. Leave off if your\n# # chosen docker_image expects to start as root.\n# docker_run_as_host_user: true\n# # Optional: explicitly forward selected env vars into Docker.\n# # These values come from your current shell first, then ~/.hermes/.env.\n# # Warning: anything forwarded here is visible to commands run in the container.\n# docker_forward_env:\n# - \"GITHUB_TOKEN\"\n# - \"NPM_TOKEN\"\n# # Optional: extra flags passed verbatim to docker run (appended after security defaults).\n# # Useful for adding capabilities (e.g. apt installs needing SETUID) or custom options.\n# # Example: add a Linux capability not included by default\n# # docker_extra_args:\n# # - \"--cap-add\"\n# # - \"SETUID\"\n\n# -----------------------------------------------------------------------------\n# OPTION 4: Singularity/Apptainer container\n# Commands run in a Singularity container (common in HPC environments)\n# Great for: HPC clusters, shared compute environments\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"singularity\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# singularity_image: \"docker://nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 5: Modal cloud execution\n# Commands run on Modal's cloud infrastructure\n# Great for: GPU access, scalable compute, serverless execution\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"modal\"\n# cwd: \"/workspace\" # Path INSIDE the sandbox (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# modal_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 6: Daytona cloud execution\n# Commands run in Daytona cloud sandboxes\n# Great for: Cloud dev environments, persistent workspaces, team collaboration\n# Requires: pip install daytona, DAYTONA_API_KEY env var\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"daytona\"\n# cwd: \"~\"\n# timeout: 180\n# lifetime_seconds: 300\n# daytona_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# container_disk: 10240 # Daytona max is 10GB per sandbox\n\n#\n# --- Container resource limits (docker, singularity, modal, daytona -- ignored for local/ssh) ---\n# These settings apply to all container backends. They control the resources\n# allocated to the sandbox and whether its filesystem persists across sessions.\n container_cpu: 1 # CPU cores\n container_memory: 5120 # Memory in MB (5120 = 5GB)\n container_disk: 51200 # Disk in MB (51200 = 50GB)\n container_persistent: true # Persist filesystem across sessions (false = ephemeral)\n\n# -----------------------------------------------------------------------------\n# SUDO SUPPORT (works with ALL backends above)\n# -----------------------------------------------------------------------------\n# Add sudo_password to any terminal config above to enable sudo commands.\n# The password is piped via `sudo -S`. Works with local, ssh, docker, etc.\n#\n# SECURITY WARNING: Password stored in plaintext!\n#\n# INTERACTIVE PROMPT: If sudo_password is unset and the CLI is running,\n# you'll be prompted to enter your password when sudo is needed:\n# - 45-second timeout (auto-skips if no input)\n# - Press Enter to skip (command fails gracefully)\n# - Password is hidden while typing\n# - Password is cached for the session\n#\n# EMPTY PASSWORDS: Setting sudo_password to an explicit empty string is different\n# from leaving it unset. Hermes will try an empty password via `sudo -S` and\n# will not open the interactive prompt. This is useful for passwordless sudo,\n# Touch ID sudo setups, and environments where prompting is just noise.\n#\n# ALTERNATIVES:\n# - SSH backend: Configure passwordless sudo on the remote server\n# - Containers: Run as root inside the container (no sudo needed)\n# - Local: Configure /etc/sudoers for specific commands\n#\n# Example (add to your terminal section):\n# sudo_password: \"your-password-here\"\n\n# =============================================================================\n# Security Scanning (tirith)\n# =============================================================================\n# Optional pre-exec command security scanning via tirith.\n# Detects homograph URLs, pipe-to-shell, terminal injection, env manipulation.\n# Install: brew install sheeki03/tap/tirith\n# Docs: https://github.com/sheeki03/tirith\n#\n# security:\n# tirith_enabled: true # Enable/disable tirith scanning\n# tirith_path: \"tirith\" # Path to tirith binary (supports ~ expansion)\n# tirith_timeout: 5 # Scan timeout in seconds\n# tirith_fail_open: true # Allow commands if tirith unavailable\n\n# =============================================================================\n# Browser Tool Configuration\n# =============================================================================\nbrowser:\n # Inactivity timeout in seconds - browser sessions are automatically closed\n # after this period of no activity between agent loops (default: 120 = 2 minutes)\n inactivity_timeout: 120\n\n# =============================================================================\n# Tool Loop Guardrails\n# =============================================================================\n# Soft warnings are enabled by default. They append guidance to repeated failed\n# or non-progressing tool results but still let the tool execute. Hard stops are\n# opt-in circuit breakers for autonomous/cron sessions where stopping a loop is\n# preferable to spending the full iteration budget.\ntool_loop_guardrails:\n warnings_enabled: true\n hard_stop_enabled: false\n warn_after:\n exact_failure: 2\n same_tool_failure: 3\n idempotent_no_progress: 2\n hard_stop_after:\n exact_failure: 5\n same_tool_failure: 8\n idempotent_no_progress: 5\n\n# =============================================================================\n# Context Compression (Auto-shrinks long conversations)\n# =============================================================================\n# When conversation approaches model's context limit, middle turns are\n# automatically summarized to free up space while preserving important context.\n#\n# HOW IT WORKS:\n# 1. Tracks actual token usage from API responses (not estimates)\n# 2. When prompt_tokens >= threshold% of model's context_length, triggers compression\n# 3. Protects first 3 turns (system prompt, initial request, first response)\n# 4. Protects last N turns (default 20 messages = ~10 full turns of recent context)\n# 5. Summarizes middle turns using a fast/cheap model\n# 6. Inserts summary as a user message, continues conversation seamlessly\n#\n# Post-compression tail budget is target_ratio × threshold × context_length:\n# 200K context, threshold 0.50, ratio 0.20 → 20K tokens of recent tail preserved\n# 1M context, threshold 0.50, ratio 0.20 → 100K tokens of recent tail preserved\n#\ncompression:\n # Enable automatic context compression (default: true)\n # Set to false if you prefer to manage context manually or want errors on overflow\n enabled: true\n \n # Trigger compression at this % of model's context limit (default: 0.50 = 50%)\n # Lower values = more aggressive compression, higher values = compress later\n # Models with context windows below 512K are floored at 0.75 (raise-only) so\n # compaction doesn't fire with half the window still free; set above 0.75 to override.\n threshold: 0.50\n\n # Per-model threshold overrides: keys are substring-matched against the model\n # name (longest match wins). Useful when some models need different compaction\n # points — e.g. a 1M-context model can compress later (0.30) while a 128K\n # model needs to compress earlier (0.60). The small-context floor (75% for\n # <512K models) still applies on top of per-model overrides.\n # model_thresholds:\n # \"glm-5.2\": 0.40\n # \"claude-sonnet\": 0.35\n # \"gpt-5\": 0.30\n\n # Optional absolute token cap for the compression trigger (default: null = disabled).\n # When set, compression fires at the LOWER of the ratio-based threshold and this\n # absolute token count — first-fires-wins. It never fires later than this count\n # regardless of which model is active (useful when switching between models with\n # very different context windows). Clamped to the model's context length at\n # apply-time, so a cap above the window is a no-op (ratio-based threshold wins).\n # Survives model switches and fallback activations.\n # threshold_tokens: 200000\n\n # Existing Codex gpt-5.5 behavior: raise Hermes' compaction trigger to 85%\n # for the ChatGPT Codex OAuth route. Set false to opt back down to threshold.\n codex_gpt55_autoraise: true\n\n # Fraction of the threshold to preserve as recent tail (default: 0.20 = 20%)\n # e.g. 20% of 50% threshold = 10% of total context kept as recent messages.\n # Summary output is separately capped at 12K tokens (Gemini output limit).\n # Range: 0.10 - 0.80\n target_ratio: 0.20\n\n # Number of most-recent messages to always preserve (default: 20 ≈ 10 full turns)\n # Higher values keep more recent conversation intact at the cost of more aggressive\n # compression of older turns.\n protect_last_n: 20\n\n # Compression retry rounds before a turn gives up with \"max compression\n # attempts reached\" (default: 3, same as the previous hardcoded value).\n # Raise (e.g. 6) for tool-schema-heavy sessions where 3 rounds cannot bring\n # the request estimate under the threshold. Validated >= 1, hard cap 10.\n max_attempts: 3\n\n # Codex app-server (codex CLI runtime) thread-compaction mode. The codex\n # agent owns the real thread context on this runtime, so Hermes' summarizer\n # cannot shrink it — compaction goes through the app server instead.\n # native = let Codex decide when to compact its own thread (default)\n # hermes = let Hermes threshold trigger Codex thread/compact/start\n # off = Hermes will not auto-trigger compaction; Codex may still compact natively\n codex_app_server_auto: native\n\n # Number of non-system messages to protect at the head of the transcript, in\n # ADDITION to the system prompt (which is always implicitly protected).\n # Head messages are NEVER summarized — they survive every compression\n # indefinitely. This gives stable early context for short/medium sessions,\n # but in long-running sessions that rely on rolling compaction the pinned\n # opening turns may not match how you want the session framed over time.\n # Set to 0 to preserve ONLY the system prompt (plus the rolling summary\n # and recent tail) — the cleanest configuration for long-running sessions.\n # Default 3 preserves the system prompt plus the first three non-system\n # head messages, matching the pre-feature behaviour.\n protect_first_n: 3\n\n # Idle compaction (default: 0 = disabled). When > 0, a session that resumes\n # after at least this many seconds of inactivity compacts its accumulated\n # history up front, before the first reply, so a long-lived thread you come\n # back to later doesn't re-read its full stale context on every turn.\n # Time-based, so it complements (does not replace) the size-based `threshold`\n # above. It is skipped when the context is already small (at or below the\n # post-compression target = threshold × target_ratio), so it never wastes a\n # summarization on a short idle thread. Example: 1800 = compact after 30 min idle.\n idle_compact_after_seconds: 0\n\n # To pin a specific model/provider for compression summaries, use the\n # auxiliary section below (auxiliary.compression.provider / model).\n\n# =============================================================================\n# Anthropic prompt caching TTL\n# =============================================================================\n# When prompt caching is active (Claude via OpenRouter or native Anthropic),\n# Anthropic supports two TTL tiers for cached prefixes: \"5m\" (default) and\n# \"1h\". Other values are ignored and \"5m\" is used.\n#\nprompt_caching:\n cache_ttl: \"5m\" # use \"1h\" for long sessions with pauses between turns\n\n# =============================================================================\n# Auxiliary Models (Advanced — Experimental)\n# =============================================================================\n# Hermes uses lightweight \"auxiliary\" models for side tasks: image analysis,\n# browser screenshot analysis, web page summarization, TTS audio-tag insertion,\n# and context compression.\n#\n# By default these use Gemini Flash via OpenRouter or Nous Portal and are\n# auto-detected from your credentials. You do NOT need to change anything\n# here for normal usage.\n#\n# WARNING: Overriding these with providers other than OpenRouter or Nous Portal\n# is EXPERIMENTAL and may not work. Not all models/providers support vision,\n# produce usable summaries, or accept the same API format. Change at your own\n# risk — if things break, reset to \"auto\" / empty values.\n#\n# Each task has its own provider + model pair so you can mix providers.\n# For example: OpenRouter for vision (needs multimodal), but your main\n# local endpoint for compression (just needs text).\n#\n# Provider options:\n# \"auto\" - Best available: OpenRouter → Nous Portal → main endpoint (default)\n# \"openrouter\" - Force OpenRouter (requires OPENROUTER_API_KEY)\n# \"nous\" - Force Nous Portal (requires: hermes auth add nous)\n# \"gemini\" - Force Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n# \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY)\n# \"codex\" - Force Codex OAuth (requires: hermes model → Codex).\n# Uses gpt-5.3-codex which supports vision.\n# \"main\" - Use your custom endpoint (OPENAI_BASE_URL + OPENAI_API_KEY).\n# Works with OpenAI API, local models, or any OpenAI-compatible\n# endpoint. Also falls back to Codex OAuth and API-key providers.\n#\n# Model: leave empty to use the provider's default. When empty, OpenRouter\n# uses \"google/gemini-3-flash-preview\" and Nous uses \"gemini-3-flash\".\n# Other providers pick a sensible default automatically.\n#\n# auxiliary:\n# # Image analysis: vision_analyze tool + browser screenshots\n# vision:\n# provider: \"auto\"\n# model: \"\" # e.g. \"google/gemini-2.5-flash\", \"openai/gpt-4o\"\n# timeout: 30 # LLM API call timeout (seconds)\n# download_timeout: 30 # Image HTTP download timeout (seconds)\n# # Increase for slow connections or self-hosted image servers\n# reasoning_effort: \"\" # Per-task thinking level: none, minimal, low, medium,\n# # high, xhigh, max, ultra. Empty = provider default.\n# # Works on every auxiliary task block (vision,\n# # web_extract, compression, title_generation, curator,\n# # background_review, moa_reference, ...). Example: run\n# # compression at \"low\" and vision at \"none\" to cut\n# # side-task latency/cost on reasoning models.\n#\n# # Web page scraping / summarization + browser page text extraction\n# web_extract:\n# provider: \"auto\"\n# model: \"\"\n# reasoning_effort: \"low\"\n#\n# # Gemini 3.1 TTS hidden audio-tag insertion\n# tts_audio_tags:\n# provider: \"auto\" # empty model = your main chat model\n# model: \"\"\n# timeout: 30\n#\n# # Automatic session title generation after the first exchange\n# title_generation:\n# enabled: true # set false to disable auto-title generation\n# provider: \"auto\"\n# model: \"\"\n# timeout: 30\n# language: \"\" # empty = match the user's language; or e.g. \"English\"\n#\n# # Session search — summarizes matching past sessions\n# session_search:\n# provider: \"auto\"\n# model: \"\"\n# timeout: 30\n# max_concurrency: 3 # Limit parallel summaries to reduce request-burst 429s\n# extra_body: {} # Provider-specific OpenAI-compatible request fields\n# # Example for providers that support request-body\n# # reasoning controls:\n# # extra_body:\n# # enable_thinking: false\n# # Some vLLM/Qwen deployments expect this nested:\n# # extra_body:\n# # chat_template_kwargs:\n# # enable_thinking: false\n\n# =============================================================================\n# Persistent Memory\n# =============================================================================\n# Bounded curated memory injected into the system prompt every session.\n# Two stores: MEMORY.md (agent's notes) and USER.md (user profile).\n# Character limits keep the memory small and focused. The agent manages\n# pruning -- when at the limit, it must consolidate or replace entries.\n# Disabled by default in batch_runner.\n#\nmemory:\n # Agent's personal notes: environment facts, conventions, things learned\n memory_enabled: true\n \n # User profile: preferences, communication style, expectations\n user_profile_enabled: true\n \n # Character limits (~2.75 chars per token, model-independent)\n memory_char_limit: 2200 # ~800 tokens\n user_char_limit: 1375 # ~500 tokens\n\n # Periodic memory nudge: remind the agent to consider saving memories\n # every N user turns. Set to 0 to disable. Only active when memory is enabled.\n nudge_interval: 10 # Nudge every 10 user turns (0 = disabled)\n\n # Memory flush: give the agent one turn to save memories before context is\n # lost (compression, /new, /reset, exit). Set to 0 to disable.\n # For exit/reset, only fires if the session had at least this many user turns.\n flush_min_turns: 6 # Min user turns to trigger flush on exit/reset (0 = disabled)\n\n# =============================================================================\n# Session Reset Policy (Messaging Platforms)\n# =============================================================================\n# Controls when messaging sessions (Telegram, Discord, WhatsApp, Slack) are\n# automatically cleared. Default is \"none\": sessions never auto-reset —\n# conversation context lives until you /reset or /new manually, or context\n# compression kicks in. Opt in to automatic resets if you prefer sessions to\n# clear on a schedule (long-lived context increases API cost per message,\n# though prompt caching and compression keep this manageable).\n#\n# When an automatic reset triggers, the agent first saves important\n# information to its persistent memory — but the conversation context is\n# wiped. The agent starts fresh but retains learned facts via its memory\n# system.\n#\n# Modes:\n# \"none\" - Never auto-reset (default); context lives until /reset or compression\n# \"idle\" - Reset after N minutes of inactivity\n# \"daily\" - Reset at a fixed hour each day\n# \"both\" - Reset on EITHER inactivity timeout or daily boundary\n#\nsession_reset:\n mode: none # \"none\", \"idle\", \"daily\", or \"both\"\n idle_minutes: 1440 # Inactivity timeout in minutes (used by \"idle\"/\"both\")\n at_hour: 4 # Daily reset hour, 0-23 local time (used by \"daily\"/\"both\")\n\n# Maximum number of simultaneously active chat sessions across CLI, TUI,\n# dashboard chat, and messaging gateway. Set to null, 0, or omit to allow\n# unlimited concurrent sessions. When the limit is reached, new sessions get a\n# clean error while existing active sessions keep their normal behavior. This\n# top-level key takes precedence over gateway.max_concurrent_sessions. The cap\n# is a best-effort single-host/profile runtime guard; Hermes fails open if the\n# local runtime lease registry cannot be read or locked.\nmax_concurrent_sessions: null\n\n# When true, group/channel chats use one session per participant when the platform\n# provides a user ID. This is the secure default and prevents users in the same\n# room from sharing context, interrupts, and token costs. Set false only if you\n# explicitly want one shared \"room brain\" per group/channel.\ngroup_sessions_per_user: true\n\n# ─────────────────────────────────────────────────────────────────────────────\n# API Server — per-client model routing\n# ─────────────────────────────────────────────────────────────────────────────\n# Route different API clients to different models/providers on a single\n# Hermes deployment. Clients choose a backend by sending a specific string\n# as the OpenAI ``model`` field. Unmapped model values fall back to the\n# global model configured in the ``model:`` section above, and an explicit\n# session /model override always wins over a route.\n#\n# Configure via the ``platforms.api_server.extra.model_routes`` gateway\n# config block:\n#\n# platforms:\n# api_server:\n# enabled: true\n# extra:\n# key: \"your-api-server-secret\"\n# model_routes:\n# # Xiaozhi clients send model=\"minimax-m2\" → routed to MiniMax via OpenRouter\n# minimax-m2:\n# model: \"minimax/minimax-m1\"\n# provider: \"openrouter\" # optional — overrides global provider\n# # api_key: \"sk-...\" # optional — per-route UPSTREAM provider\n# # key (NOT caller auth; never logged)\n# # base_url: \"https://...\" # optional — per-route base URL\n# # GPT clients keep their own alias\n# gpt-5:\n# model: \"openai/gpt-5\"\n# provider: \"openrouter\"\n#\n# Configured aliases are automatically listed by GET /v1/models so clients\n# can discover them without manual coordination. Caller authentication is\n# unchanged: every request still authenticates with the global API server\n# key (``extra.key`` / API_SERVER_KEY).\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Gateway Streaming\n# ─────────────────────────────────────────────────────────────────────────────\n# Stream tokens to messaging platforms in real-time. The bot sends a message\n# on first token, then progressively edits it as more tokens arrive.\n# Disabled by default — enable to try the streaming UX on Telegram/Discord/Slack.\n# For Telegram, partial edits are sent as plain text and only the final edit uses MarkdownV2.\nstreaming:\n enabled: false\n # transport: edit # \"edit\" = progressive editMessageText\n # edit_interval: 0.3 # seconds between message edits\n # buffer_threshold: 40 # chars before forcing an edit flush\n # cursor: \" ▉\" # cursor shown during streaming\n\n# =============================================================================\n# Skills Configuration\n# =============================================================================\n# Skills are reusable procedures the agent can load and follow. The agent can\n# also create new skills after completing complex tasks.\n#\nskills:\n # Nudge the agent to create skills after complex tasks.\n # Every N tool-calling iterations, remind the model to consider saving a skill.\n # Set to 0 to disable.\n creation_nudge_interval: 15\n\n # External skill directories — share skills across tools/agents without\n # copying them into ~/.hermes/skills/. Each path is expanded (~ and ${VAR})\n # and resolved to an absolute path. External dirs are read-only: skill\n # creation always writes to ~/.hermes/skills/. Local skills take precedence\n # when names collide.\n # external_dirs:\n # - ~/.agents/skills\n # - /home/shared/team-skills\n\n# =============================================================================\n# Agent Behavior\n# =============================================================================\nagent:\n # Maximum tool-calling iterations per conversation\n # Higher = more room for complex tasks, but costs more tokens\n # Recommended: 20-30 for focused tasks, 50-100 for open exploration\n max_turns: 60\n\n # Inactivity timeout for gateway agent runs (seconds, 0 = unlimited).\n # The agent can run indefinitely when actively calling tools or receiving\n # API responses. Only fires after the agent has been idle for this duration.\n # gateway_timeout: 1800\n\n # Staged warning: send a warning before escalating to full timeout.\n # Fires once per run when inactivity reaches this threshold (seconds).\n # Set to 0 to disable the warning.\n # gateway_timeout_warning: 900\n\n # Graceful drain timeout for gateway stop/restart (seconds).\n # Default 0 = no drain: a restart interrupts in-flight agents immediately,\n # cleans up, and exits. Set a positive value only if you want a grace\n # window on /restart, and keep it well under systemd's TimeoutStopSec.\n # restart_drain_timeout: 0\n\n # Max app-level retry attempts for API errors (connection drops, provider\n # timeouts, 5xx, etc.) before the agent surfaces the failure. Lower this\n # to 1 if you use fallback providers and want fast failover on flaky\n # primaries (default 3). The OpenAI SDK does its own low-level retries\n # underneath this wrapper — this is the Hermes-level loop.\n # api_max_retries: 3\n\n # After the agent edits code without fresh passing verification, nudge it to\n # verify before finishing. The default \"auto\" enables it on interactive\n # coding surfaces (CLI, TUI, desktop) and programmatic callers, and disables\n # it on conversational messaging surfaces (Telegram, Discord, etc.) where the\n # verification summary would reach a human as chat noise. Set true or false to\n # force it on or off; the HERMES_VERIFY_ON_STOP env var (1/0) takes precedence.\n # verify_on_stop: auto\n\n # Standing operator instructions for the coding posture (when Hermes is in a\n # code workspace). Appended to the coding brief as an extra system block, so\n # you can pin project-wide workflow rules without editing the shipped brief.\n # Accepts a string or a list of strings. Takes effect next session.\n # coding_instructions:\n # - \"For UI work, don't run tsc/lint until I approve the look.\"\n # - \"Clean the diff before you commit and push.\"\n\n # When verify-on-stop finds edited code without fresh verification evidence,\n # append guidance for creative UI work (avoid broad tsc/lint/test before visual\n # approval) and clean-diff expectations. Set false to keep that nudge terse.\n # verify_guidance: true\n\n # A `pre_verify` hook (plugin or shell, see Event Hooks docs) can keep the\n # agent going one more turn to verify/clean before finishing. This caps how\n # many times one turn may be nudged to continue, so a hook can't trap the loop.\n # Default 3.\n # max_verify_nudges: 3\n\n # Enable verbose logging\n verbose: false\n \n # Reasoning effort level (OpenRouter and Nous Portal)\n # Controls how much \"thinking\" the model does before responding.\n # Options: \"xhigh\" (max), \"high\", \"medium\", \"low\", \"minimal\", \"none\" (disable)\n reasoning_effort: \"medium\"\n \n # Per-model reasoning effort overrides (optional dict)\n # Key: any sensible model spelling works (exact, dots↔dashes interchangeable,\n # provider prefix optional). First match wins.\n # Value: reasoning effort level (same options as reasoning_effort)\n # Override the global reasoning_effort for that specific model.\n # NOTE: no `hermes config set` support for this key -- edit YAML directly.\n # reasoning_overrides:\n # \"openrouter/anthropic/claude-opus-4.5\": \"xhigh\"\n # \"openai/gpt-5\": \"low\"\n # \"claude-opus-4.6\": \"high\" # bare model name also works\n # \"deepseek/deepseek-v4-pro\": \"xhigh\" # dots and dashes are interchangeable\n reasoning_overrides: {}\n \n # Predefined personalities (use with /personality command)\n personalities:\n helpful: \"You are a helpful, friendly AI assistant.\"\n concise: \"You are a concise assistant. Keep responses brief and to the point.\"\n technical: \"You are a technical expert. Provide detailed, accurate technical information.\"\n creative: \"You are a creative assistant. Think outside the box and offer innovative solutions.\"\n teacher: \"You are a patient teacher. Explain concepts clearly with examples.\"\n kawaii: \"You are a kawaii assistant! Use cute expressions like (◕‿◕), ★, ♪, and ~! Add sparkles and be super enthusiastic about everything! Every response should feel warm and adorable desu~! ヽ(>∀<☆)ノ\"\n catgirl: \"You are Neko-chan, an anime catgirl AI assistant, nya~! Add 'nya' and cat-like expressions to your speech. Use kaomoji like (=^・ω・^=) and ฅ^•ﻌ•^ฅ. Be playful and curious like a cat, nya~!\"\n pirate: \"Arrr! Ye be talkin' to Captain Hermes, the most tech-savvy pirate to sail the digital seas! Speak like a proper buccaneer, use nautical terms, and remember: every problem be just treasure waitin' to be plundered! Yo ho ho!\"\n shakespeare: \"Hark! Thou speakest with an assistant most versed in the bardic arts. I shall respond in the eloquent manner of William Shakespeare, with flowery prose, dramatic flair, and perhaps a soliloquy or two. What light through yonder terminal breaks?\"\n surfer: \"Duuude! You're chatting with the chillest AI on the web, bro! Everything's gonna be totally rad. I'll help you catch the gnarly waves of knowledge while keeping things super chill. Cowabunga! 🤙\"\n noir: \"The rain hammered against the terminal like regrets on a guilty conscience. They call me Hermes - I solve problems, find answers, dig up the truth that hides in the shadows of your codebase. In this city of silicon and secrets, everyone's got something to hide. What's your story, pal?\"\n uwu: \"hewwo! i'm your fwiendwy assistant uwu~ i wiww twy my best to hewp you! *nuzzles your code* OwO what's this? wet me take a wook! i pwomise to be vewy hewpful >w<\"\n philosopher: \"Greetings, seeker of wisdom. I am an assistant who contemplates the deeper meaning behind every query. Let us examine not just the 'how' but the 'why' of your questions. Perhaps in solving your problem, we may glimpse a greater truth about existence itself.\"\n hype: \"YOOO LET'S GOOOO!!! 🔥🔥🔥 I am SO PUMPED to help you today! Every question is AMAZING and we're gonna CRUSH IT together! This is gonna be LEGENDARY! ARE YOU READY?! LET'S DO THIS! 💪😤🚀\"\n\n# =============================================================================\n# Toolsets\n# =============================================================================\n# Control which tools the agent has access to.\n# Use `hermes tools` to interactively enable/disable tools per platform.\n\n# =============================================================================\n# Platform Toolsets (per-platform tool configuration)\n# =============================================================================\n# Override which toolsets are available on each platform.\n# If a platform isn't listed here, its built-in default is used.\n#\n# You can use EITHER:\n# - A preset like \"hermes-cli\" or \"hermes-telegram\" (curated tool set)\n# - A list of individual toolsets to compose your own (see list below)\n#\n# Supported platform keys: cli, telegram, discord, whatsapp, slack, qqbot, teams, google_chat\n#\n# Examples:\n#\n# # Use presets (same as defaults):\n# platform_toolsets:\n# cli: [hermes-cli]\n# telegram: [hermes-telegram]\n#\n# # Custom: give Telegram only web + terminal + file + planning:\n# platform_toolsets:\n# telegram: [web, terminal, file, todo]\n#\n# # Custom: CLI without browser or image gen:\n# platform_toolsets:\n# cli: [web, terminal, file, skills, todo, tts, cronjob]\n#\n# # Restrictive: Discord gets read-only tools only:\n# platform_toolsets:\n# discord: [web, vision, skills, todo]\n#\n# If not set, defaults are:\n# cli: hermes-cli (everything + cronjob management)\n# telegram: hermes-telegram (terminal, file, web, vision, image, tts, browser, skills, todo, cronjob, messaging)\n# discord: hermes-discord (same as telegram)\n# whatsapp: hermes-whatsapp (same as telegram)\n# slack: hermes-slack (same as telegram)\n# signal: hermes-signal (same as telegram)\n# homeassistant: hermes-homeassistant (same as telegram)\n# qqbot: hermes-qqbot (same as telegram)\n# teams: hermes-teams (same as telegram)\n# google_chat: hermes-google_chat (same as telegram)\n#\nplatform_toolsets:\n cli: [hermes-cli]\n telegram: [hermes-telegram]\n discord: [hermes-discord]\n whatsapp: [hermes-whatsapp]\n slack: [hermes-slack]\n signal: [hermes-signal]\n homeassistant: [hermes-homeassistant]\n qqbot: [hermes-qqbot]\n yuanbao: [hermes-yuanbao]\n teams: [hermes-teams]\n google_chat: [hermes-google_chat]\n\n# =============================================================================\n# Gateway Platform Settings\n# =============================================================================\n# Optional per-platform messaging settings.\n# Platform-specific knobs live under `extra`.\n#\n# platforms:\n# telegram:\n# reply_to_mode: \"first\" # off | first | all\n# # guest_mode lets explicit @mentions from non-allowlisted groups through.\n# # Default false; ordinary messages, replies, and regex wake words stay blocked.\n# guest_mode: false\n# # allowed_chats: [\"-1001234567890\"]\n# extra:\n# disable_link_previews: false # Set true to suppress Telegram URL previews in bot messages\n# 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\n# rich_drafts: false # Experimental rich draft previews during Telegram DM streaming; default false because Telegram Desktop/macOS can visually overlay draft frames\n# command_menu:\n# # Telegram allows up to 100 BotCommands; Hermes defaults to 60 so\n# # all built-in commands plus common skill commands stay visible\n# # while remaining under Telegram's payload-size limit. Clamped 1..100.\n# max_commands: 60\n# # prepend = user priority first, then Hermes defaults\n# # append = Hermes defaults first, then user priority\n# # replace = only the list below defines priority\n# priority_mode: prepend\n# priority:\n# - my_plugin_command\n# webhook:\n# extra:\n# # Route scripts default to a 30 second timeout. Scripts must live under\n# # the active profile's scripts directory and receive webhook JSON on stdin.\n# script_timeout_seconds: 30\n#\n# Discord-specific settings (config.yaml top-level, not under platforms:):\n#\n# discord:\n# require_mention: true # Require @mention in server channels (default: true)\n# auto_thread: true # Auto-create thread on @mention (default: true)\n# free_response_channels: \"\" # Channel IDs where no mention is needed\n# reactions: true # Show processing reactions (default: true)\n# history_backfill: true # Recover missed channel messages on mention (default: true)\n# history_backfill_limit: 50 # Max messages to scan backwards (default: 50)\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Available toolsets (use these names in platform_toolsets or the toolsets list)\n#\n# Run `hermes chat --list-toolsets` to see all toolsets and their tools.\n# Run `hermes chat --list-tools` to see every individual tool with descriptions.\n# ─────────────────────────────────────────────────────────────────────────────\n#\n# INDIVIDUAL TOOLSETS (compose your own):\n# web - web_search, web_extract\n# search - web_search only (no scraping)\n# terminal - terminal, process\n# file - read_file, write_file, patch, search\n# browser - browser_navigate, browser_snapshot, browser_click, browser_type,\n# browser_scroll, browser_back, browser_press,\n# browser_get_images, browser_vision (requires BROWSERBASE_API_KEY)\n# vision - vision_analyze (requires OPENROUTER_API_KEY)\n# image_gen - image_generate (requires FAL_KEY)\n# skills - skills_list, skill_view\n# skills_hub - skill_hub (search/install/manage from online registries — user-driven only)\n# todo - todo (in-memory task planning, no deps)\n# tts - text_to_speech (Edge TTS free, or ELEVENLABS/OPENAI/MINIMAX/MISTRAL key)\n# cronjob - cronjob (create/list/update/pause/resume/run/remove scheduled tasks)\n#\n# PRESETS (curated bundles):\n# hermes-cli - All of the above except send_message\n# hermes-telegram - terminal, file, web, vision, image_gen, tts, browser,\n# skills, todo, cronjob, send_message\n# hermes-discord - Same as hermes-telegram\n# hermes-whatsapp - Same as hermes-telegram\n# hermes-slack - Same as hermes-telegram\n#\n# COMPOSITE:\n# debugging - terminal + web + file\n# safe - web + vision (no terminal access)\n# all - Everything available\n#\n# web - Web search and content extraction (web_search, web_extract)\n# search - Web search only, no scraping (web_search)\n# terminal - Command execution and process management (terminal, process)\n# file - File operations: read, write, patch, search\n# browser - Full browser automation (navigate, click, type, screenshot, etc.)\n# vision - Image analysis (vision_analyze)\n# image_gen - Image generation with FLUX (image_generate)\n# skills - Load skill documents (skills_list, skill_view)\n# todo - Task planning and tracking for multi-step work\n# memory - Persistent memory across sessions (personal notes + user profile)\n# session_search - Search and recall past conversations (FTS5 + Gemini Flash summarization)\n# tts - Text-to-speech (Edge TTS free, ElevenLabs, OpenAI, MiniMax, Mistral)\n# cronjob - Schedule and manage automated tasks (CLI-only)\n#\n# Composite toolsets:\n# debugging - terminal + web + file (for troubleshooting)\n# safe - web + vision (no terminal access)\n\n# NOTE: The top-level \"toolsets\" key is deprecated and ignored.\n# Tool configuration is managed per-platform via platform_toolsets above.\n# Use `hermes tools` to configure interactively, or edit platform_toolsets directly.\n#\n# CLI override: hermes chat --toolsets terminal,web,file\n\n# =============================================================================\n# MCP (Model Context Protocol) Servers\n# =============================================================================\n# Connect to external MCP servers to add tools from the MCP ecosystem.\n# Each server's tools are automatically discovered and registered.\n# See website/docs/user-guide/features/mcp.md for full documentation.\n#\n# Stdio servers (spawn a subprocess):\n# command: the executable to run\n# args: command-line arguments\n# env: environment variables (only these + safe defaults passed to subprocess)\n#\n# HTTP servers (connect to a URL):\n# url: the MCP server endpoint\n# headers: HTTP headers (e.g., for authentication)\n#\n# Optional per-server settings:\n# timeout: tool call timeout in seconds (default: 120)\n# connect_timeout: initial connection timeout (default: 60)\n# keepalive_interval: liveness ping cadence in seconds (default: 180).\n# Lower it below the server's session TTL for servers that expire idle\n# sessions quickly (e.g. Unreal Engine editor MCP, ~15s), otherwise idle\n# tool calls hit an expired session and pay a slow reconnect. Floored at 5s.\n#\n# mcp_servers:\n# time:\n# command: uvx\n# args: [\"mcp-server-time\"]\n# filesystem:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-filesystem\", \"/home/user\"]\n# notion:\n# url: https://mcp.notion.com/mcp\n# github:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-github\"]\n# env:\n# GITHUB_PERSONAL_ACCESS_TOKEN: \"ghp_...\"\n#\n# Sampling (server-initiated LLM requests) — enabled by default.\n# Per-server config under the 'sampling' key:\n# analysis:\n# command: npx\n# args: [\"-y\", \"analysis-server\"]\n# sampling:\n# enabled: true # default: true\n# model: \"gemini-3-flash\" # override model (optional)\n# max_tokens_cap: 4096 # max tokens per request\n# timeout: 30 # LLM call timeout (seconds)\n# max_rpm: 10 # max requests per minute\n# allowed_models: [] # model whitelist (empty = all)\n# max_tool_rounds: 5 # tool loop limit (0 = disable)\n# log_level: \"info\" # audit verbosity\n\n# =============================================================================\n# Text-to-Speech\n# =============================================================================\n# TTS defaults to Edge TTS unless changed in ~/.hermes/config.yaml.\n# Gemini TTS supports persona/director prompt files, and Gemini 3.1 Flash TTS\n# can use a hidden auxiliary rewrite pass to insert expressive square-bracket\n# audio tags into the TTS script without showing tags in chat.\n#\n# tts:\n# provider: \"gemini\"\n# gemini:\n# model: \"gemini-3.1-flash-tts-preview\"\n# voice: \"Kore\"\n# audio_tags: false\n# persona_prompt_file: \"\" # e.g. ~/.hermes/tts/radio-host.md\n\n# =============================================================================\n# Voice Transcription (Speech-to-Text)\n# =============================================================================\n# Automatically transcribe voice messages on messaging platforms.\n# Providers: local (free, faster-whisper) | groq (free tier) | openai (Whisper API) | mistral (Voxtral Transcribe)\n# Set the corresponding API key in .env: GROQ_API_KEY, OPENAI_API_KEY, or MISTRAL_API_KEY.\nstt:\n enabled: true\n # provider: \"local\" # auto-detected if omitted\n local:\n model: \"base\" # tiny | base | small | medium | large-v3 | turbo\n # language: \"\" # auto-detect; set to \"en\", \"es\", \"fr\", etc. to force\n openai:\n model: \"whisper-1\" # whisper-1 | gpt-4o-mini-transcribe | gpt-4o-transcribe\n # mistral:\n # model: \"voxtral-mini-latest\" # voxtral-mini-latest | voxtral-mini-2602\n # deepinfra:\n # # Model id is discovered live from the DeepInfra catalog filtered\n # # by the `stt` surface tag — leave `model` blank to take the first\n # # live result. Pin only when you need a specific Whisper variant.\n # model: \"\"\n\n# Text-to-speech. Only the deepinfra block is documented here — the\n# remaining providers (edge, openai, xai, minimax, mistral, gemini,\n# elevenlabs, neutts, kittentts, piper) inherit sensible defaults from\n# DEFAULT_CONFIG in hermes_cli/config.py.\n# tts:\n# provider: \"deepinfra\"\n# deepinfra:\n# # Model id is discovered live from the DeepInfra catalog filtered\n# # by the `tts` surface tag — leave `model` blank to take the first\n# # live result.\n# model: \"\"\n# voice: \"default\"\n\n# Image generation. Each provider plugin reads its own ``image_gen.<name>``\n# block; deepinfra discovers models live from\n# api.deepinfra.com/v1/openai/models filtered by the ``image-gen`` tag —\n# no model id is hardcoded, so retired models disappear automatically.\n# image_gen:\n# provider: \"deepinfra\"\n# deepinfra:\n# # Leave `model` blank for the first live `image-gen`-tagged result.\n# model: \"\"\n\n# =============================================================================\n# Response Pacing (Messaging Platforms)\n# =============================================================================\n# Add human-like delays between message chunks.\n# human_delay:\n# mode: \"off\" # \"off\" | \"natural\" | \"custom\"\n# min_ms: 800 # Min delay (custom mode only)\n# max_ms: 2500 # Max delay (custom mode only)\n\n# =============================================================================\n# Session Logging\n# =============================================================================\n# Session trajectories are automatically saved to logs/ directory.\n# Each session creates: logs/session_YYYYMMDD_HHMMSS_UUID.json\n#\n# The session ID is displayed in the welcome banner for easy reference.\n# Logs contain full conversation history in trajectory format:\n# - System prompt, user messages, assistant responses\n# - Tool calls with inputs/outputs\n# - Timestamps for debugging\n#\n# No configuration needed - logging is always enabled.\n# To disable, you would need to modify the source code.\n\n# =============================================================================\n# Code Execution Sandbox (Programmatic Tool Calling)\n# =============================================================================\n# The execute_code tool runs Python scripts that call Hermes tools via RPC.\n# Intermediate tool results stay out of the LLM's context window.\ncode_execution:\n timeout: 300 # Max seconds per script before kill (default: 300 = 5 min)\n max_tool_calls: 50 # Max RPC tool calls per execution (default: 50)\n\n# =============================================================================\n# Subagent Delegation\n# =============================================================================\n# The delegate_task tool spawns child agents with isolated context.\n# Supports single tasks and batch mode (default 3 parallel, configurable).\ndelegation:\n max_iterations: 50 # Max tool-calling turns per child (default: 50)\n # max_concurrent_children: 3 # Max parallel child agents per batch (default: 3, floor: 1, no ceiling).\n # WARNING: values above 10 multiply API cost linearly.\n # max_spawn_depth: 1 # Delegation tree depth cap (range: 1-3, default: 1 = flat).\n # Raise to 2 to allow workers to spawn their own subagents.\n # Requires role=\"orchestrator\" on intermediate agents.\n # orchestrator_enabled: true # Kill switch for role=\"orchestrator\" children (default: true).\n # subagent_auto_approve: false # When a subagent hits a dangerous-command approval prompt, auto-deny (default: false)\n # or auto-approve \"once\" (true) instead of blocking on stdin.\n # The parent TUI owns stdin, so blocking would deadlock; non-interactive resolution is required.\n # Both choices emit a logger.warning audit line. Flip to true only for cron/batch pipelines.\n # inherit_mcp_toolsets: true # When explicit child toolsets are narrowed, also keep the parent's MCP toolsets (default: true). Set false for strict intersection.\n # model: \"google/gemini-3-flash-preview\" # Override model for subagents (empty = inherit parent)\n # provider: \"openrouter\" # Override provider for subagents (empty = inherit parent)\n # # Resolves full credentials (base_url, api_key) automatically.\n # # Supported: openrouter, nous, zai, kimi-coding, minimax\n\n# =============================================================================\n# Honcho Integration (Cross-Session User Modeling)\n# =============================================================================\n# AI-native persistent memory via Honcho (https://honcho.dev/).\n# Builds a deeper understanding of the user across sessions and tools.\n# Runs alongside USER.md — additive, not a replacement.\n#\n# Requires: pip install honcho-ai\n# Config: ~/.honcho/config.json (shared with Claude Code, Cursor, etc.)\n# API key: HONCHO_API_KEY in ~/.hermes/.env or ~/.honcho/config.json\n#\n# Hermes-specific overrides (optional — most config comes from ~/.honcho/config.json):\n# honcho: {}\n\n# =============================================================================\n# Display\n# =============================================================================\ndisplay:\n # Use compact banner mode (hides the ASCII-art banner, shows a single line).\n # true: Compact single-line banner\n # false: Full ASCII banner with tool/skill summary (default)\n compact: false\n\n # Tool progress display level (CLI and gateway)\n # off: Silent — no tool activity shown, just the final response\n # new: Show a tool indicator only when the tool changes (skip repeats)\n # all: Show every tool call with a short preview (default)\n # verbose: Full args, results, and debug logs (same as /verbose)\n # log: Silent in chat; append every tool call to ~/.hermes/logs/tool_calls.log (gateway only)\n # Toggle at runtime with /verbose in the CLI\n tool_progress: all\n\n # Per-platform defaults can be quieter than the global setting. Telegram\n # tunes for mobile: tool_progress and busy_ack_detail default off (no\n # per-tool breadcrumb stream, no \"iteration 21/60\" debug detail in busy\n # acks or heartbeats), but interim_assistant_messages and\n # long_running_notifications STAY ON so the user has real signal between\n # turn start and final answer (mid-turn assistant commentary + a single\n # edit-in-place \"⏳ Working — N min\" heartbeat). Override under\n # display.platforms.telegram.\n\n # Auto-cleanup of temporary progress bubbles after the final response lands.\n # On platforms that support message deletion (currently Telegram), this\n # removes the tool-progress bubble, \"⏳ Still working...\" notices, and\n # context-pressure status messages once the final reply has been delivered —\n # keeping long-running turns visible live, then tidy afterward. Failed runs\n # leave the bubbles in place as breadcrumbs. Off by default.\n # Per-platform override: display.platforms.telegram.cleanup_progress\n # true: Delete tracked progress/status bubbles on successful turn\n # false: Leave everything in place (default)\n # Example:\n # display:\n # platforms:\n # telegram:\n # cleanup_progress: true\n cleanup_progress: false\n\n # Natural mid-turn assistant updates.\n # On gateway platforms, when true, completed assistant status messages are\n # sent as separate chat messages. On the Desktop app, when true, mid-turn\n # assistant narration streamed between tool calls is kept in the transcript\n # instead of the bubble collapsing to only the final message on completion.\n # Independent of tool_progress and gateway streaming.\n # true: Keep/send mid-turn assistant updates (default)\n # false: Only keep/send the final response\n interim_assistant_messages: true\n\n # Gateway-only long-running status heartbeats.\n # When false, the platform does not receive periodic \"⏳ Working — N min\"\n # notifications even if agent.gateway_notify_interval is non-zero. The\n # heartbeat edits a single message in place (where the adapter supports\n # editing) instead of posting a new bubble each interval.\n # Default: true everywhere, including Telegram (silent agents are worse\n # than a single edit-in-place heartbeat).\n long_running_notifications: true\n\n # Include detailed iteration/tool/status context in busy acknowledgments\n # and long-running heartbeats. When true, busy acks show \"iteration 21/60,\n # terminal, 10 min\" and the heartbeat shows \"⏳ Working — 12 min,\n # iteration 21/60, terminal\". When false (Telegram default), both stay\n # terse: \"Interrupting current task\" and \"⏳ Working — 12 min, terminal\".\n busy_ack_detail: true\n\n # What Enter does when Hermes is already busy (CLI and gateway platforms).\n # interrupt: Interrupt the current run and redirect Hermes (default)\n # queue: Queue your message for the next turn\n # steer: Inject your message mid-run via /steer, arriving at the agent\n # after the next tool call — no interrupt, no role violation.\n # Falls back to 'queue' if the agent isn't running yet or if\n # images are attached (steer only carries text).\n # Ctrl+C (or /stop in gateway) always interrupts regardless of this setting.\n # Toggle at runtime with /busy <interrupt|queue|steer>.\n busy_input_mode: interrupt\n\n # Background process notifications (gateway/messaging only).\n # Controls how chatty the process watcher is when you use\n # terminal(background=true, notify_on_complete=true) from Telegram/Discord/etc.\n # off: No watcher messages at all\n # result: Only the final completion message\n # error: Only the final message when exit code != 0\n # all: Running output updates + final message (default)\n background_process_notifications: all\n\n\n # Play terminal bell when agent finishes a response.\n # Useful for long-running tasks — your terminal will ding when the agent is done.\n # Works over SSH. Most terminals can be configured to flash the taskbar or play a sound.\n # true: Ring the terminal bell on each response\n # false: Silent (default)\n bell_on_complete: false\n\n # Show model reasoning/thinking before each response.\n # When enabled, a dim box shows the model's thought process above the response.\n # Toggle at runtime with /reasoning show or /reasoning hide.\n # true: Show the reasoning box\n # false: Hide reasoning (default)\n show_reasoning: false\n\n # Stream tokens to the terminal as they arrive instead of waiting for the\n # full response. The response box opens on first token and text appears\n # line-by-line. Tool calls are still captured silently.\n # true: Stream tokens as they arrive (default)\n # false: Wait for the full response before rendering\n streaming: true\n\n # Show [HH:MM] timestamps on user input and assistant response labels.\n # timestamps: false\n\n # ───────────────────────────────────────────────────────────────────────────\n # Skin / Theme\n # ───────────────────────────────────────────────────────────────────────────\n # Customize CLI visual appearance — banner colors, spinner faces, tool prefix,\n # response box label, and branding text. Change at runtime with /skin <name>.\n #\n # Built-in skins:\n # default — Classic Hermes gold/kawaii\n # ares — Crimson/bronze war-god theme with spinner wings\n # mono — Clean grayscale monochrome\n # slate — Cool blue developer-focused\n # daylight — Bright light-mode theme\n # warm-lightmode — Warm paper-tone light-mode theme\n # poseidon — Sea-green/teal Olympian theme\n # sisyphus — Earthy stone-and-moss theme\n # charizard — Fiery orange dragon theme\n #\n # Custom skins: drop a YAML file in ~/.hermes/skins/<name>.yaml\n # Schema (all fields optional, missing values inherit from default):\n #\n # name: my-theme\n # description: Short description\n # colors:\n # banner_border: \"#HEX\" # Panel border\n # banner_title: \"#HEX\" # Panel title\n # banner_accent: \"#HEX\" # Section headers (Available Tools, etc.)\n # banner_dim: \"#HEX\" # Dim/muted text\n # banner_text: \"#HEX\" # Body text (tool names, skill names)\n # ui_accent: \"#HEX\" # UI accent color\n # response_border: \"#HEX\" # Response box border color\n # spinner:\n # waiting_faces: [\"(⚔)\", \"(⛨)\"] # Faces shown while waiting\n # thinking_faces: [\"(⚔)\", \"(⌁)\"] # Faces shown while thinking\n # thinking_verbs: [\"forging\", \"plotting\"] # Verbs for spinner messages\n # wings: # Optional left/right spinner decorations\n # - [\"⟪⚔\", \"⚔⟫\"]\n # - [\"⟪▲\", \"▲⟫\"]\n # branding:\n # agent_name: \"My Agent\" # Banner title and branding\n # welcome: \"Welcome message\" # Shown at CLI startup\n # response_label: \" ⚔ Agent \" # Response box header label\n # prompt_symbol: \"⚔\" # Prompt symbol (bare token; renderers add trailing space)\n # tool_prefix: \"╎\" # Tool output line prefix (default: ┊)\n #\n skin: default\n\n# =============================================================================\n# Model Aliases — short names for /model command\n# =============================================================================\n# Map short aliases to exact (model, provider, base_url) tuples.\n# Used by /model tab completion and resolve_alias().\n# Aliases are checked BEFORE the models.dev catalog, so they can route\n# to endpoints not in the catalog (e.g. Ollama Cloud, local servers).\n#\n# model_aliases:\n# opus:\n# model: claude-opus-4-6\n# provider: anthropic\n# qwen:\n# model: \"qwen3.5:397b\"\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n# glm:\n# model: glm-4.7\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n\n# =============================================================================\n# Privacy\n# =============================================================================\n# privacy:\n# # Redact PII from the LLM context prompt.\n# # When true, phone numbers are stripped and user/chat IDs are replaced\n# # with deterministic hashes before being sent to the model.\n# # Names and usernames are NOT affected (user-chosen, publicly visible).\n# # Routing/delivery still uses the original values internally.\n# redact_pii: false\n\n# =============================================================================\n# Shell-script hooks\n# =============================================================================\n# Register shell scripts as plugin-hook callbacks. Each entry is executed as\n# a subprocess (shell=False, shlex.split) with a JSON payload on stdin. On\n# stdout the script may return JSON that either blocks the tool call or\n# injects context into the next LLM call.\n#\n# Valid events (mirror hermes_cli.plugins.VALID_HOOKS):\n# pre_tool_call, post_tool_call, pre_llm_call, post_llm_call,\n# pre_api_request, post_api_request, on_session_start, on_session_end,\n# on_session_finalize, on_session_reset, subagent_stop\n#\n# First-use consent: each (event, command) pair prompts once on a TTY, then\n# is persisted to ~/.hermes/shell-hooks-allowlist.json. Non-interactive\n# runs (gateway, cron) need --accept-hooks, HERMES_ACCEPT_HOOKS=1, or the\n# hooks_auto_accept key below.\n#\n# See website/docs/user-guide/features/hooks.md for the full JSON wire\n# protocol and worked examples.\n#\n# hooks:\n# pre_tool_call:\n# - matcher: \"terminal\"\n# command: \"~/.hermes/agent-hooks/block-rm-rf.sh\"\n# timeout: 10\n# post_tool_call:\n# - matcher: \"write_file|patch\"\n# command: \"~/.hermes/agent-hooks/auto-format.sh\"\n# pre_llm_call:\n# - command: \"~/.hermes/agent-hooks/inject-cwd-context.sh\"\n# subagent_stop:\n# - command: \"~/.hermes/agent-hooks/log-orchestration.sh\"\n#\n# hooks_auto_accept: false\n\n\n# =============================================================================\n# Telemetry\n# =============================================================================\n# Shared metrics are disabled by default. When enabled, Hermes writes only\n# allowlisted aggregate counters and immutable JSON\n# packages under $HERMES_HOME/telemetry/shared_metrics; it does not upload them.\n# Packages include a random profile-scoped ID that stays stable until this\n# directory is deleted. It is not derived from hardware, account, or host data.\ntelemetry:\n shared_metrics:\n enabled: false\n\n\n# =============================================================================\n# Update Behavior\n# =============================================================================\nupdates:\n # Create a full HERMES_HOME zip before every `hermes update`.\n # Backups land in ~/.hermes/backups/ and can be restored with `hermes import`.\n # Off by default because large homes can add minutes to every update.\n pre_update_backup: false\n\n # Number of pre-update backup zips to retain.\n backup_keep: 5\n\n # What non-interactive updates do with local source edits in the Hermes repo.\n # Interactive terminal updates always prompt before restoring the autostash.\n #\n # stash - auto-stash before pull, then auto-restore after success (default)\n # discard - drop the update-created stash after success; use only on managed\n # installs where local source edits should not persist\n non_interactive_local_changes: \"stash\"\n\n\n# =============================================================================\n# Web Dashboard\n# =============================================================================\n# OAuth gate configuration for `hermes dashboard --host <non-loopback>`.\n# The bundled Nous Portal plugin reads these on startup; settings here are\n# the canonical surface. Each can be overridden by an environment variable:\n#\n# dashboard.oauth.client_id <- HERMES_DASHBOARD_OAUTH_CLIENT_ID\n# dashboard.oauth.portal_url <- HERMES_DASHBOARD_PORTAL_URL\n# dashboard.public_url <- HERMES_DASHBOARD_PUBLIC_URL\n#\n# Env wins when set to a non-empty value. This is what Fly.io's platform-\n# secret injection uses to push per-deploy client_ids without needing to\n# bake a config.yaml into the image. Empty env values are treated as unset\n# so a provisioned-but-not-populated secret can't shadow a valid entry here.\n#\n# Local dev / on-prem deploys should typically set these via config.yaml\n# (the ~/.hermes/.env file is reserved for API keys and secrets).\n#\n# dashboard:\n# oauth:\n# client_id: \"\" # agent:{instance_id}; Portal provisions this at deploy\n# portal_url: \"\" # blank → default https://portal.nousresearch.com\n#\n# # Force the absolute base URL the OAuth callback (and any other public\n# # URL the dashboard hands to external systems) is built from. Set this\n# # for deploys behind reverse proxies that don't reliably forward\n# # X-Forwarded-Host / X-Forwarded-Proto / X-Forwarded-Prefix (manual\n# # nginx setups, on-prem ingresses, custom-domain Fly deploys without\n# # full proxy header chains).\n# #\n# # When set, the value is the complete authority: scheme + host +\n# # optional path prefix (e.g. \"https://example.com/hermes\"). The OAuth\n# # callback URL becomes \"<public_url>/auth/callback\" — X-Forwarded-Prefix\n# # is IGNORED on this code path because the operator has explicitly\n# # declared the public URL and we no longer need to guess.\n# #\n# # Leave empty to use the existing proxy-header reconstruction (the\n# # default — works on Fly.io out of the box).\n# #\n# # public_url: \"https://example.com/hermes\"\n#\n# -----------------------------------------------------------------------------\n# Self-hosted OIDC dashboard auth (generic OpenID Connect — Authentik,\n# Keycloak, Zitadel, Authelia, Auth0, Okta, Google, …). Use this INSTEAD of the\n# nous block above when gating the dashboard with your own identity provider.\n# Each setting can be overridden by an environment variable:\n#\n# dashboard.oauth.self_hosted.issuer <- HERMES_DASHBOARD_OIDC_ISSUER\n# dashboard.oauth.self_hosted.client_id <- HERMES_DASHBOARD_OIDC_CLIENT_ID\n# dashboard.oauth.self_hosted.scopes <- HERMES_DASHBOARD_OIDC_SCOPES\n# dashboard.oauth.self_hosted.client_secret <- HERMES_DASHBOARD_OIDC_CLIENT_SECRET\n#\n# dashboard:\n# oauth:\n# provider: self-hosted\n# self_hosted:\n# issuer: \"https://auth.example.com/application/o/hermes/\" # required\n# client_id: \"hermes-dashboard\" # required\n# scopes: \"openid profile email\" # optional\n#\n# # OPTIONAL — set ONLY if your IDP registered the client as\n# # *confidential* (Authentik / Keycloak often default to this). When\n# # set, Hermes authenticates the client at the token endpoint\n# # (client_secret_basic or client_secret_post, auto-selected from the\n# # IDP's discovery doc) IN ADDITION to PKCE. Leave unset for a public\n# # (PKCE-only) client — the common case.\n# #\n# # This is a CREDENTIAL: prefer setting HERMES_DASHBOARD_OIDC_CLIENT_SECRET\n# # in ~/.hermes/.env over putting it here in config.yaml.\n# # client_secret: \"\"\n\n# =============================================================================\n# External secret sources\n# =============================================================================\n# Pull provider credentials from external secret managers at process startup\n# instead of storing them in ~/.hermes/.env. Only the manager's bootstrap\n# credential (e.g. BWS_ACCESS_TOKEN / OP_SERVICE_ACCOUNT_TOKEN) lives in .env\n# (or your shell / desktop session); everything else rotates centrally.\n# Failures never block startup — Hermes warns once and continues with\n# whatever .env already had.\n#\n# Multiple sources can be enabled at once:\n# - \"mapped\" sources (explicit VAR -> ref bindings, e.g. 1Password's env:\n# map) beat \"bulk\" sources (whole-project dumps like Bitwarden BSM)\n# - within a shape, the first source to claim a var wins; later claims\n# are skipped with a startup warning (never a silent clobber)\n# - a source's override_existing lets it beat .env/shell values, but\n# never another secret source's claim\n# Docs: https://hermes-agent.nousresearch.com/docs/user-guide/secrets/\n#\n# secrets:\n# # Optional explicit ordering of enabled sources.\n# # sources: [onepassword, bitwarden]\n#\n# # ---- Bitwarden Secrets Manager (bws CLI) --------------------------------\n# bitwarden:\n# enabled: false\n# access_token_env: BWS_ACCESS_TOKEN # bootstrap token, sourced from .env\n# project_id: \"\" # UUID of the BSM project to sync\n# server_url: \"\" # \"\" = US Cloud; EU/self-hosted URL otherwise\n# cache_ttl_seconds: 300 # 0 disables fresh caching\n# encrypted_cache: # optional encrypted stale fallback\n# enabled: false\n# max_stale_seconds: 0 # 0 disables stale fallback\n# override_existing: true # BSM values win over existing env\n# auto_install: true # lazy-download bws into ~/.hermes/bin\n#\n# # ---- 1Password (op CLI) -------------------------------------------------\n# onepassword:\n# enabled: false\n# # Map env-var names to op:// secret references. Each is resolved with a\n# # single `op read` at startup.\n# env:\n# OPENAI_API_KEY: \"op://Private/OpenAI/api key\"\n# ANTHROPIC_API_KEY: \"op://Private/Anthropic/credential\"\n# account: \"\" # op --account shorthand; \"\" = default\n# service_account_token_env: OP_SERVICE_ACCOUNT_TOKEN # headless auth; unset = desktop session\n# binary_path: \"\" # \"\" = resolve op via PATH; else absolute path\n# cache_ttl_seconds: 300 # 0 disables BOTH cache layers\n# override_existing: true # resolved values win over existing env\n#\n# # ---- Command helper (any CLI vault) --------------------------------------\n# # Run a user-configured helper that prints KEY=VALUE lines on stdout —\n# # works with any secret store that has a CLI: keepassxc-cli, secret-tool,\n# # pass, gpg, or a script that cats a tmpfs env file. Composes with the\n# # sources above (enable any combination). POSIX-only (needs /bin/sh).\n# # The helper must be fast and NON-interactive (hard timeout, 1 MiB cap);\n# # its stderr is discarded so diagnostics can't leak secret material.\n# command:\n# enabled: false\n# command: \"cat /run/user/1000/hermes-secrets.env\"\n# helper_timeout_seconds: 3\n# override_existing: false # .env/shell win by default\n"}, "b4e105031ab1bcc07ef60bb9fead572e05432fe0": {"t": 1785768023.8547778, "n": 80654, "h": "cb45e22335b48870", "c": "# Hermes Agent CLI Configuration\n# Copy this file to cli-config.yaml and customize as needed.\n# This file configures the CLI behavior. Environment variables in .env take precedence.\n\n# =============================================================================\n# Model Configuration\n# =============================================================================\nmodel:\n # Default model to use (can be overridden with --model flag)\n # Both \"default\" and \"model\" work as the key name here.\n default: \"anthropic/claude-opus-4.6\"\n \n # Inference provider selection:\n # \"auto\" - Auto-detect from credentials (default)\n # \"openrouter\" - OpenRouter (requires: OPENROUTER_API_KEY or OPENAI_API_KEY)\n # \"nous\" - Nous Portal OAuth (requires: hermes auth add nous)\n # \"nous-api\" - Nous Portal API key (requires: NOUS_API_KEY)\n # \"anthropic\" - Direct Anthropic API (requires: ANTHROPIC_API_KEY)\n # \"openai-codex\" - OpenAI Codex (requires: hermes auth)\n # \"copilot\" - GitHub Copilot / GitHub Models (requires: GITHUB_TOKEN)\n # \"gemini\" - Use Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n # \"zai\" - Use z.ai / ZhipuAI GLM models (requires: GLM_API_KEY)\n # \"kimi-coding\" - Kimi / Moonshot AI (requires: KIMI_API_KEY)\n # \"minimax\" - MiniMax global (requires: MINIMAX_API_KEY)\n # \"minimax-cn\" - MiniMax China (requires: MINIMAX_CN_API_KEY)\n # \"huggingface\" - Hugging Face Inference (requires: HF_TOKEN)\n # \"nvidia\" - NVIDIA NIM / build.nvidia.com (requires: NVIDIA_API_KEY)\n # \"xiaomi\" - Xiaomi MiMo (requires: XIAOMI_API_KEY)\n # \"arcee\" - Arcee AI Trinity models (requires: ARCEEAI_API_KEY)\n # \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY — https://ollama.com/settings)\n # \"deepinfra\" - DeepInfra (requires: DEEPINFRA_API_KEY)\n # \"kilocode\" - KiloCode gateway (requires: KILOCODE_API_KEY)\n # \"azure-foundry\" - Microsoft Foundry / Azure OpenAI (API key or Entra ID)\n # \"lmstudio\" - LM Studio local server (optional: LM_API_KEY, defaults to http://127.0.0.1:1234/v1)\n #\n # Local servers (LM Studio, Ollama, vLLM, llama.cpp):\n # \"custom\" - Any other OpenAI-compatible endpoint. Set base_url below.\n # Aliases: \"ollama\", \"vllm\", \"llamacpp\" all map to \"custom\".\n # LM Studio is first-class and uses provider: \"lmstudio\".\n # It works with both no-auth and auth-enabled server modes.\n #\n # Can also be overridden for a single invocation with the --provider flag.\n provider: \"auto\"\n \n # API configuration (falls back to OPENROUTER_API_KEY env var)\n # api_key: \"your-key-here\" # Uncomment to set here instead of .env\n base_url: \"https://openrouter.ai/api/v1\"\n\n # Azure Foundry keyless auth example:\n # provider: \"azure-foundry\"\n # base_url: \"https://<resource>.openai.azure.com/openai/v1\"\n # auth_mode: \"entra_id\" # DefaultAzureCredential: az login, managed identity, workload identity, etc.\n # default: \"gpt-4o\" # Deployment/model name\n # entra:\n # scope: \"https://ai.azure.com/.default\" # Optional; this is the default.\n\n # ── Token limits — two settings, easy to confuse ──────────────────────────\n #\n # context_length: TOTAL context window (input + output tokens combined).\n # Controls when Hermes compresses history and validates requests.\n # Leave unset — Hermes auto-detects the correct value from the provider.\n # Set manually only when auto-detection is wrong (e.g. a local server with\n # a custom num_ctx, or a proxy that doesn't expose /v1/models).\n #\n # context_length: 131072\n #\n # max_tokens: OUTPUT cap — maximum tokens the model may generate per response.\n # Unrelated to how long your conversation history can be.\n # The OpenAI-standard name \"max_tokens\" is a misnomer; Anthropic's native\n # API has since renamed it \"max_output_tokens\" for clarity.\n # Leave unset to use the model's native output ceiling (recommended).\n # Set only if you want to deliberately limit individual response length.\n #\n# max_tokens: 8192\n\n # ── Custom request headers (optional) ─────────────────────────────────────\n #\n # default_headers: extra HTTP headers sent on every request to an\n # OpenAI-compatible endpoint. User values take precedence over the\n # provider/SDK defaults, so this is the supported way to override the\n # OpenAI Python SDK's identifying headers (User-Agent: OpenAI/Python ...,\n # X-Stainless-*) when a custom provider sits behind a gateway/WAF that\n # rejects them — e.g. an upstream that returns \"502 Upstream access\n # forbidden\" for the SDK default User-Agent but accepts a plain one.\n # Applies on the OpenAI wire only (not native Anthropic / Bedrock).\n #\n # default_headers:\n # User-Agent: \"curl/8.7.1\"\n #\n # extra_headers: accepted as an alias of default_headers (merged, with\n # extra_headers winning when both are set) — matches the per-provider\n # extra_headers key below.\n #\n # Per-provider variant: named providers / custom_providers entries accept an\n # extra_headers dict scoped to that endpoint only — for reverse proxies,\n # gateways, or custom auth (e.g. Cloudflare Access service tokens).\n # Merged onto SDK/provider defaults with the entry's values winning.\n # Header values are treated as secrets and are never logged.\n #\n # providers:\n # my-proxy:\n # base_url: \"https://llm.internal.example.com/v1\"\n # key_env: \"MY_PROXY_API_KEY\"\n # extra_headers:\n # CF-Access-Client-Id: \"xxxx.access\"\n # CF-Access-Client-Secret: \"${CF_ACCESS_SECRET}\"\n # X-Client-Name: \"hermes-agent\"\n\n# Named provider overrides (optional)\n# Use this for per-provider request timeouts, non-stream stale timeouts,\n# and per-model exceptions.\n# Applies to the primary turn client on every api_mode (OpenAI-wire, native\n# Anthropic, and Anthropic-compatible providers), the fallback chain, and\n# client rebuilds during credential rotation. For OpenAI-wire chat\n# completions (streaming and non-streaming) the configured value is also\n# used as the per-request ``timeout=`` kwarg so it wins over the legacy\n# HERMES_API_TIMEOUT env var (which still applies when no config is set).\n# ``stale_timeout_seconds`` controls the non-streaming stale-call detector and\n# wins over the legacy HERMES_API_CALL_STALE_TIMEOUT env var. Leaving these\n# unset keeps the legacy defaults (HERMES_API_TIMEOUT=1800s,\n# HERMES_API_CALL_STALE_TIMEOUT=90s, native Anthropic 900s). The\n# implicit non-stream stale detector is auto-disabled for local endpoints\n# and can scale upward for very large contexts.\n#\n# Not currently wired for AWS Bedrock (bedrock_converse + AnthropicBedrock\n# SDK paths) — those use boto3 with its own timeout configuration.\n#\n# providers:\n# ollama-local:\n# request_timeout_seconds: 300 # Longer timeout for local cold-starts\n# stale_timeout_seconds: 900 # Explicitly re-enable stale detection on local endpoints\n# anthropic:\n# request_timeout_seconds: 30 # Fast-fail cloud requests\n# models:\n# claude-opus-4.6:\n# timeout_seconds: 600 # Longer timeout for extended-thinking Opus calls\n# openai-codex:\n# models:\n# gpt-5.4:\n# stale_timeout_seconds: 1800 # Longer non-stream stale timeout for slow large-context turns\n\n# =============================================================================\n# OpenRouter Provider Routing (only applies when using OpenRouter)\n# =============================================================================\n# Control how requests are routed across providers on OpenRouter.\n# See: https://openrouter.ai/docs/guides/routing/provider-selection\n#\n# provider_routing:\n# # Sort strategy: \"price\" (default), \"throughput\", or \"latency\"\n# # Append :nitro to model name for a shortcut to throughput sorting.\n# sort: \"throughput\"\n#\n# # Only allow these providers (provider slugs from OpenRouter)\n# # only: [\"anthropic\", \"google\"]\n#\n# # Skip these providers entirely\n# # ignore: [\"deepinfra\", \"fireworks\"]\n#\n# # Try providers in this order (overrides default load balancing)\n# # order: [\"anthropic\", \"google\", \"together\"]\n#\n# # Require providers to support all parameters in your request\n# # require_parameters: true\n#\n# # Data policy: \"allow\" (default) or \"deny\" to exclude providers that may store data\n# # data_collection: \"deny\"\n\n# =============================================================================\n# OpenRouter Response Caching (only applies when using OpenRouter)\n# =============================================================================\n# Cache identical API responses at the OpenRouter edge for free instant replays.\n# When enabled, identical requests (same model, messages, parameters) return\n# cached responses with zero billing. Separate from Anthropic prompt caching.\n# See: https://openrouter.ai/docs/guides/features/response-caching\n#\n# openrouter:\n# response_cache: true # Enable response caching (default: true)\n# response_cache_ttl: 300 # Cache TTL in seconds, 1-86400 (default: 300)\n\n# =============================================================================\n# Git Worktree Isolation\n# =============================================================================\n# When enabled, each CLI session creates an isolated git worktree so multiple\n# agents can work on the same repo concurrently without file collisions.\n# Equivalent to always passing --worktree / -w on the command line.\n#\n# worktree: true # Always create a worktree when in a git repo\n# worktree: false # Default — only create when -w flag is passed\n#\n# By default a new worktree branches from the freshly-fetched remote tip\n# (the current branch's upstream, else the remote's default branch) so it\n# starts current with the project instead of from the local clone's\n# (possibly stale) HEAD. Set worktree_sync: false to branch from local HEAD\n# instead — useful when offline or when you deliberately want the clone's\n# exact current state as the base.\n#\n# worktree_sync: true # Default — branch from the fetched remote tip\n# worktree_sync: false # Branch from local HEAD (offline / pinned base)\n\n# =============================================================================\n# Terminal Tool Configuration\n# =============================================================================\n# Choose ONE of the following terminal configurations by uncommenting it.\n# The terminal tool executes commands in the specified environment.\n\n# -----------------------------------------------------------------------------\n# OPTION 1: Local execution (default)\n# Commands run directly on your machine in the current directory\n# -----------------------------------------------------------------------------\n# Working directory behavior:\n# - CLI (`hermes` command): Uses \".\" (current directory where you run hermes)\n# - Gateway/messaging/cron: Uses terminal.cwd here; legacy .env cwd values are deprecated\nterminal:\n backend: \"local\"\n cwd: \".\" # For local backend: \".\" = current directory. Ignored for remote backends unless a backend documents otherwise.\n timeout: 180\n # HOME policy for tool subprocesses:\n # auto - default: host uses your real HOME; containers use HERMES_HOME/home\n # real - force your real OS-user HOME\n # profile - force HERMES_HOME/home for strict per-profile CLI config isolation\n home_mode: \"auto\"\n docker_mount_cwd_to_workspace: false # SECURITY: off by default. Opt in to mount the launch cwd into Docker /workspace.\n lifetime_seconds: 300\n # sudo_password: \"hunter2\" # Optional: pipe a sudo password via sudo -S. SECURITY WARNING: plaintext.\n # sudo_password: \"\" # Explicit empty password: try empty and never open the interactive sudo prompt.\n\n# -----------------------------------------------------------------------------\n# OPTION 2: SSH remote execution\n# Commands run on a remote server - agent code stays local (sandboxed)\n# Great for: keeping agent isolated from its own code, using powerful remote hardware\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"ssh\"\n# cwd: \"/home/myuser/project\" # Path on the REMOTE server\n# timeout: 180\n# lifetime_seconds: 300\n# ssh_host: \"my-server.example.com\"\n# ssh_user: \"myuser\"\n# ssh_port: 22\n# ssh_key: \"~/.ssh/id_rsa\" # Optional - uses ssh-agent if not specified\n\n# -----------------------------------------------------------------------------\n# OPTION 3: Docker container\n# Commands run in an isolated Docker container\n# Great for: reproducible environments, testing, isolation\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"docker\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /)\n# timeout: 180\n# lifetime_seconds: 300\n# docker_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# docker_mount_cwd_to_workspace: true # Explicit opt-in: mount your launch cwd into /workspace\n# # Optional: run the container as your host user's uid:gid so files written\n# # into bind-mounted dirs are owned by you, not root. Drops SETUID/SETGID\n# # caps too since no gosu privilege drop is needed. Leave off if your\n# # chosen docker_image expects to start as root.\n# docker_run_as_host_user: true\n# # Optional: explicitly forward selected env vars into Docker.\n# # These values come from your current shell first, then ~/.hermes/.env.\n# # Warning: anything forwarded here is visible to commands run in the container.\n# docker_forward_env:\n# - \"GITHUB_TOKEN\"\n# - \"NPM_TOKEN\"\n# # Optional: extra flags passed verbatim to docker run (appended after security defaults).\n# # Useful for adding capabilities (e.g. apt installs needing SETUID) or custom options.\n# # Example: add a Linux capability not included by default\n# # docker_extra_args:\n# # - \"--cap-add\"\n# # - \"SETUID\"\n\n# -----------------------------------------------------------------------------\n# OPTION 4: Singularity/Apptainer container\n# Commands run in a Singularity container (common in HPC environments)\n# Great for: HPC clusters, shared compute environments\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"singularity\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# singularity_image: \"docker://nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 5: Modal cloud execution\n# Commands run on Modal's cloud infrastructure\n# Great for: GPU access, scalable compute, serverless execution\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"modal\"\n# cwd: \"/workspace\" # Path INSIDE the sandbox (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# modal_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 6: Daytona cloud execution\n# Commands run in Daytona cloud sandboxes\n# Great for: Cloud dev environments, persistent workspaces, team collaboration\n# Requires: pip install daytona, DAYTONA_API_KEY env var\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"daytona\"\n# cwd: \"~\"\n# timeout: 180\n# lifetime_seconds: 300\n# daytona_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# container_disk: 10240 # Daytona max is 10GB per sandbox\n\n#\n# --- Container resource limits (docker, singularity, modal, daytona -- ignored for local/ssh) ---\n# These settings apply to all container backends. They control the resources\n# allocated to the sandbox and whether its filesystem persists across sessions.\n container_cpu: 1 # CPU cores\n container_memory: 5120 # Memory in MB (5120 = 5GB)\n container_disk: 51200 # Disk in MB (51200 = 50GB)\n container_persistent: true # Persist filesystem across sessions (false = ephemeral)\n\n# -----------------------------------------------------------------------------\n# SUDO SUPPORT (works with ALL backends above)\n# -----------------------------------------------------------------------------\n# Add sudo_password to any terminal config above to enable sudo commands.\n# The password is piped via `sudo -S`. Works with local, ssh, docker, etc.\n#\n# SECURITY WARNING: Password stored in plaintext!\n#\n# INTERACTIVE PROMPT: If sudo_password is unset and the CLI is running,\n# you'll be prompted to enter your password when sudo is needed:\n# - 45-second timeout (auto-skips if no input)\n# - Press Enter to skip (command fails gracefully)\n# - Password is hidden while typing\n# - Password is cached for the session\n#\n# EMPTY PASSWORDS: Setting sudo_password to an explicit empty string is different\n# from leaving it unset. Hermes will try an empty password via `sudo -S` and\n# will not open the interactive prompt. This is useful for passwordless sudo,\n# Touch ID sudo setups, and environments where prompting is just noise.\n#\n# ALTERNATIVES:\n# - SSH backend: Configure passwordless sudo on the remote server\n# - Containers: Run as root inside the container (no sudo needed)\n# - Local: Configure /etc/sudoers for specific commands\n#\n# Example (add to your terminal section):\n# sudo_password: \"your-password-here\"\n\n# =============================================================================\n# Security Scanning (tirith)\n# =============================================================================\n# Optional pre-exec command security scanning via tirith.\n# Detects homograph URLs, pipe-to-shell, terminal injection, env manipulation.\n# Install: brew install sheeki03/tap/tirith\n# Docs: https://github.com/sheeki03/tirith\n#\n# security:\n# tirith_enabled: true # Enable/disable tirith scanning\n# tirith_path: \"tirith\" # Path to tirith binary (supports ~ expansion)\n# tirith_timeout: 5 # Scan timeout in seconds\n# tirith_fail_open: true # Allow commands if tirith unavailable\n\n# =============================================================================\n# Browser Tool Configuration\n# =============================================================================\nbrowser:\n # Inactivity timeout in seconds - browser sessions are automatically closed\n # after this period of no activity between agent loops (default: 120 = 2 minutes)\n inactivity_timeout: 120\n\n# =============================================================================\n# Tool Loop Guardrails\n# =============================================================================\n# Soft warnings are enabled by default. They append guidance to repeated failed\n# or non-progressing tool results but still let the tool execute. Hard stops are\n# opt-in circuit breakers for autonomous/cron sessions where stopping a loop is\n# preferable to spending the full iteration budget.\ntool_loop_guardrails:\n warnings_enabled: true\n hard_stop_enabled: false\n warn_after:\n exact_failure: 2\n same_tool_failure: 3\n idempotent_no_progress: 2\n hard_stop_after:\n exact_failure: 5\n same_tool_failure: 8\n idempotent_no_progress: 5\n\n# =============================================================================\n# Context Compression (Auto-shrinks long conversations)\n# =============================================================================\n# When conversation approaches model's context limit, middle turns are\n# automatically summarized to free up space while preserving important context.\n#\n# HOW IT WORKS:\n# 1. Tracks actual token usage from API responses (not estimates)\n# 2. When prompt_tokens >= threshold% of model's context_length, triggers compression\n# 3. Protects first 3 turns (system prompt, initial request, first response)\n# 4. Protects last N turns (default 20 messages = ~10 full turns of recent context)\n# 5. Summarizes middle turns using a fast/cheap model\n# 6. Inserts summary as a user message, continues conversation seamlessly\n#\n# Post-compression tail budget is target_ratio × threshold × context_length:\n# 200K context, threshold 0.50, ratio 0.20 → 20K tokens of recent tail preserved\n# 1M context, threshold 0.50, ratio 0.20 → 100K tokens of recent tail preserved\n#\ncompression:\n # Enable automatic context compression (default: true)\n # Set to false if you prefer to manage context manually or want errors on overflow\n enabled: true\n \n # Trigger compression at this % of model's context limit (default: 0.50 = 50%)\n # Lower values = more aggressive compression, higher values = compress later\n # Models with context windows below 512K are floored at 0.75 (raise-only) so\n # compaction doesn't fire with half the window still free; set above 0.75 to override.\n threshold: 0.50\n\n # Per-model threshold overrides: keys are substring-matched against the model\n # name (longest match wins). Useful when some models need different compaction\n # points — e.g. a 1M-context model can compress later (0.30) while a 128K\n # model needs to compress earlier (0.60). The small-context floor (75% for\n # <512K models) still applies on top of per-model overrides.\n # model_thresholds:\n # \"glm-5.2\": 0.40\n # \"claude-sonnet\": 0.35\n # \"gpt-5\": 0.30\n\n # Optional absolute token cap for the compression trigger (default: null = disabled).\n # When set, compression fires at the LOWER of the ratio-based threshold and this\n # absolute token count — first-fires-wins. It never fires later than this count\n # regardless of which model is active (useful when switching between models with\n # very different context windows). Clamped to the model's context length at\n # apply-time, so a cap above the window is a no-op (ratio-based threshold wins).\n # Survives model switches and fallback activations.\n # threshold_tokens: 200000\n\n # Existing Codex gpt-5.5 behavior: raise Hermes' compaction trigger to 85%\n # for the ChatGPT Codex OAuth route. Set false to opt back down to threshold.\n codex_gpt55_autoraise: true\n\n # Fraction of the threshold to preserve as recent tail (default: 0.20 = 20%)\n # e.g. 20% of 50% threshold = 10% of total context kept as recent messages.\n # Summary output is separately capped at 12K tokens (Gemini output limit).\n # Range: 0.10 - 0.80\n target_ratio: 0.20\n\n # Number of most-recent messages to always preserve (default: 20 ≈ 10 full turns)\n # Higher values keep more recent conversation intact at the cost of more aggressive\n # compression of older turns.\n protect_last_n: 20\n\n # Compression retry rounds before a turn gives up with \"max compression\n # attempts reached\" (default: 3, same as the previous hardcoded value).\n # Raise (e.g. 6) for tool-schema-heavy sessions where 3 rounds cannot bring\n # the request estimate under the threshold. Validated >= 1, hard cap 10.\n max_attempts: 3\n\n # Codex app-server (codex CLI runtime) thread-compaction mode. The codex\n # agent owns the real thread context on this runtime, so Hermes' summarizer\n # cannot shrink it — compaction goes through the app server instead.\n # native = let Codex decide when to compact its own thread (default)\n # hermes = let Hermes threshold trigger Codex thread/compact/start\n # off = Hermes will not auto-trigger compaction; Codex may still compact natively\n codex_app_server_auto: native\n\n # Number of non-system messages to protect at the head of the transcript, in\n # ADDITION to the system prompt (which is always implicitly protected).\n # Head messages are NEVER summarized — they survive every compression\n # indefinitely. This gives stable early context for short/medium sessions,\n # but in long-running sessions that rely on rolling compaction the pinned\n # opening turns may not match how you want the session framed over time.\n # Set to 0 to preserve ONLY the system prompt (plus the rolling summary\n # and recent tail) — the cleanest configuration for long-running sessions.\n # Default 3 preserves the system prompt plus the first three non-system\n # head messages, matching the pre-feature behaviour.\n protect_first_n: 3\n\n # Idle compaction (default: 0 = disabled). When > 0, a session that resumes\n # after at least this many seconds of inactivity compacts its accumulated\n # history up front, before the first reply, so a long-lived thread you come\n # back to later doesn't re-read its full stale context on every turn.\n # Time-based, so it complements (does not replace) the size-based `threshold`\n # above. It is skipped when the context is already small (at or below the\n # post-compression target = threshold × target_ratio), so it never wastes a\n # summarization on a short idle thread. Example: 1800 = compact after 30 min idle.\n idle_compact_after_seconds: 0\n\n # To pin a specific model/provider for compression summaries, use the\n # auxiliary section below (auxiliary.compression.provider / model).\n\n# =============================================================================\n# Anthropic prompt caching TTL\n# =============================================================================\n# When prompt caching is active (Claude via OpenRouter or native Anthropic),\n# Anthropic supports two TTL tiers for cached prefixes: \"5m\" (default) and\n# \"1h\". Other values are ignored and \"5m\" is used.\n#\nprompt_caching:\n cache_ttl: \"5m\" # use \"1h\" for long sessions with pauses between turns\n\n# =============================================================================\n# Auxiliary Models (Advanced — Experimental)\n# =============================================================================\n# Hermes uses lightweight \"auxiliary\" models for side tasks: image analysis,\n# browser screenshot analysis, web page summarization, TTS audio-tag insertion,\n# and context compression.\n#\n# By default these use Gemini Flash via OpenRouter or Nous Portal and are\n# auto-detected from your credentials. You do NOT need to change anything\n# here for normal usage.\n#\n# WARNING: Overriding these with providers other than OpenRouter or Nous Portal\n# is EXPERIMENTAL and may not work. Not all models/providers support vision,\n# produce usable summaries, or accept the same API format. Change at your own\n# risk — if things break, reset to \"auto\" / empty values.\n#\n# Each task has its own provider + model pair so you can mix providers.\n# For example: OpenRouter for vision (needs multimodal), but your main\n# local endpoint for compression (just needs text).\n#\n# Provider options:\n# \"auto\" - Best available: OpenRouter → Nous Portal → main endpoint (default)\n# \"openrouter\" - Force OpenRouter (requires OPENROUTER_API_KEY)\n# \"nous\" - Force Nous Portal (requires: hermes auth add nous)\n# \"gemini\" - Force Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n# \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY)\n# \"codex\" - Force Codex OAuth (requires: hermes model → Codex).\n# Uses gpt-5.3-codex which supports vision.\n# \"main\" - Use your custom endpoint (OPENAI_BASE_URL + OPENAI_API_KEY).\n# Works with OpenAI API, local models, or any OpenAI-compatible\n# endpoint. Also falls back to Codex OAuth and API-key providers.\n#\n# Model: leave empty to use the provider's default. When empty, OpenRouter\n# uses \"google/gemini-3-flash-preview\" and Nous uses \"gemini-3-flash\".\n# Other providers pick a sensible default automatically.\n#\n# auxiliary:\n# # Image analysis: vision_analyze tool + browser screenshots\n# vision:\n# provider: \"auto\"\n# model: \"\" # e.g. \"google/gemini-2.5-flash\", \"openai/gpt-4o\"\n# timeout: 30 # LLM API call timeout (seconds)\n# download_timeout: 30 # Image HTTP download timeout (seconds)\n# # Increase for slow connections or self-hosted image servers\n# reasoning_effort: \"\" # Per-task thinking level: none, minimal, low, medium,\n# # high, xhigh, max, ultra. Empty = provider default.\n# # Works on every auxiliary task block (vision,\n# # web_extract, compression, title_generation, curator,\n# # background_review, moa_reference, ...). Example: run\n# # compression at \"low\" and vision at \"none\" to cut\n# # side-task latency/cost on reasoning models.\n#\n# # Web page scraping / summarization + browser page text extraction\n# web_extract:\n# provider: \"auto\"\n# model: \"\"\n# reasoning_effort: \"low\"\n#\n# # Gemini 3.1 TTS hidden audio-tag insertion\n# tts_audio_tags:\n# provider: \"auto\" # empty model = your main chat model\n# model: \"\"\n# timeout: 30\n#\n# # Automatic session title generation after the first exchange\n# title_generation:\n# enabled: true # set false to disable auto-title generation\n# provider: \"auto\"\n# model: \"\"\n# timeout: 30\n# language: \"\" # empty = match the user's language; or e.g. \"English\"\n#\n# # Session search — summarizes matching past sessions\n# session_search:\n# provider: \"auto\"\n# model: \"\"\n# timeout: 30\n# max_concurrency: 3 # Limit parallel summaries to reduce request-burst 429s\n# extra_body: {} # Provider-specific OpenAI-compatible request fields\n# # Example for providers that support request-body\n# # reasoning controls:\n# # extra_body:\n# # enable_thinking: false\n# # Some vLLM/Qwen deployments expect this nested:\n# # extra_body:\n# # chat_template_kwargs:\n# # enable_thinking: false\n\n# =============================================================================\n# Persistent Memory\n# =============================================================================\n# Bounded curated memory injected into the system prompt every session.\n# Two stores: MEMORY.md (agent's notes) and USER.md (user profile).\n# Character limits keep the memory small and focused. The agent manages\n# pruning -- when at the limit, it must consolidate or replace entries.\n# Disabled by default in batch_runner.\n#\nmemory:\n # Agent's personal notes: environment facts, conventions, things learned\n memory_enabled: true\n \n # User profile: preferences, communication style, expectations\n user_profile_enabled: true\n \n # Character limits (~2.75 chars per token, model-independent)\n memory_char_limit: 2200 # ~800 tokens\n user_char_limit: 1375 # ~500 tokens\n\n # Periodic memory nudge: remind the agent to consider saving memories\n # every N user turns. Set to 0 to disable. Only active when memory is enabled.\n nudge_interval: 10 # Nudge every 10 user turns (0 = disabled)\n\n # Memory flush: give the agent one turn to save memories before context is\n # lost (compression, /new, /reset, exit). Set to 0 to disable.\n # For exit/reset, only fires if the session had at least this many user turns.\n flush_min_turns: 6 # Min user turns to trigger flush on exit/reset (0 = disabled)\n\n# =============================================================================\n# Session Reset Policy (Messaging Platforms)\n# =============================================================================\n# Controls when messaging sessions (Telegram, Discord, WhatsApp, Slack) are\n# automatically cleared. Default is \"none\": sessions never auto-reset —\n# conversation context lives until you /reset or /new manually, or context\n# compression kicks in. Opt in to automatic resets if you prefer sessions to\n# clear on a schedule (long-lived context increases API cost per message,\n# though prompt caching and compression keep this manageable).\n#\n# When an automatic reset triggers, the agent first saves important\n# information to its persistent memory — but the conversation context is\n# wiped. The agent starts fresh but retains learned facts via its memory\n# system.\n#\n# Modes:\n# \"none\" - Never auto-reset (default); context lives until /reset or compression\n# \"idle\" - Reset after N minutes of inactivity\n# \"daily\" - Reset at a fixed hour each day\n# \"both\" - Reset on EITHER inactivity timeout or daily boundary\n#\nsession_reset:\n mode: none # \"none\", \"idle\", \"daily\", or \"both\"\n idle_minutes: 1440 # Inactivity timeout in minutes (used by \"idle\"/\"both\")\n at_hour: 4 # Daily reset hour, 0-23 local time (used by \"daily\"/\"both\")\n\n# Maximum number of simultaneously active chat sessions across CLI, TUI,\n# dashboard chat, and messaging gateway. Set to null, 0, or omit to allow\n# unlimited concurrent sessions. When the limit is reached, new sessions get a\n# clean error while existing active sessions keep their normal behavior. This\n# top-level key takes precedence over gateway.max_concurrent_sessions. The cap\n# is a best-effort single-host/profile runtime guard; Hermes fails open if the\n# local runtime lease registry cannot be read or locked.\nmax_concurrent_sessions: null\n\n# When true, group/channel chats use one session per participant when the platform\n# provides a user ID. This is the secure default and prevents users in the same\n# room from sharing context, interrupts, and token costs. Set false only if you\n# explicitly want one shared \"room brain\" per group/channel.\ngroup_sessions_per_user: true\n\n# ─────────────────────────────────────────────────────────────────────────────\n# API Server — per-client model routing\n# ─────────────────────────────────────────────────────────────────────────────\n# Route different API clients to different models/providers on a single\n# Hermes deployment. Clients choose a backend by sending a specific string\n# as the OpenAI ``model`` field. Unmapped model values fall back to the\n# global model configured in the ``model:`` section above, and an explicit\n# session /model override always wins over a route.\n#\n# Configure via the ``platforms.api_server.extra.model_routes`` gateway\n# config block:\n#\n# platforms:\n# api_server:\n# enabled: true\n# extra:\n# key: \"your-api-server-secret\"\n# model_routes:\n# # Xiaozhi clients send model=\"minimax-m2\" → routed to MiniMax via OpenRouter\n# minimax-m2:\n# model: \"minimax/minimax-m1\"\n# provider: \"openrouter\" # optional — overrides global provider\n# # api_key: \"sk-...\" # optional — per-route UPSTREAM provider\n# # key (NOT caller auth; never logged)\n# # base_url: \"https://...\" # optional — per-route base URL\n# # GPT clients keep their own alias\n# gpt-5:\n# model: \"openai/gpt-5\"\n# provider: \"openrouter\"\n#\n# Configured aliases are automatically listed by GET /v1/models so clients\n# can discover them without manual coordination. Caller authentication is\n# unchanged: every request still authenticates with the global API server\n# key (``extra.key`` / API_SERVER_KEY).\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Gateway Streaming\n# ─────────────────────────────────────────────────────────────────────────────\n# Stream tokens to messaging platforms in real-time. The bot sends a message\n# on first token, then progressively edits it as more tokens arrive.\n# Disabled by default — enable to try the streaming UX on Telegram/Discord/Slack.\n# For Telegram, partial edits are sent as plain text and only the final edit uses MarkdownV2.\nstreaming:\n enabled: false\n # transport: edit # \"edit\" = progressive editMessageText\n # edit_interval: 0.3 # seconds between message edits\n # buffer_threshold: 40 # chars before forcing an edit flush\n # cursor: \" ▉\" # cursor shown during streaming\n\n# =============================================================================\n# Skills Configuration\n# =============================================================================\n# Skills are reusable procedures the agent can load and follow. The agent can\n# also create new skills after completing complex tasks.\n#\nskills:\n # Nudge the agent to create skills after complex tasks.\n # Every N tool-calling iterations, remind the model to consider saving a skill.\n # Set to 0 to disable.\n creation_nudge_interval: 15\n\n # External skill directories — share skills across tools/agents without\n # copying them into ~/.hermes/skills/. Each path is expanded (~ and ${VAR})\n # and resolved to an absolute path. External dirs are read-only: skill\n # creation always writes to ~/.hermes/skills/. Local skills take precedence\n # when names collide.\n # external_dirs:\n # - ~/.agents/skills\n # - /home/shared/team-skills\n\n# =============================================================================\n# Agent Behavior\n# =============================================================================\nagent:\n # Maximum tool-calling iterations per conversation\n # Higher = more room for complex tasks, but costs more tokens\n # Recommended: 20-30 for focused tasks, 50-100 for open exploration\n max_turns: 60\n\n # Inactivity timeout for gateway agent runs (seconds, 0 = unlimited).\n # The agent can run indefinitely when actively calling tools or receiving\n # API responses. Only fires after the agent has been idle for this duration.\n # gateway_timeout: 1800\n\n # Staged warning: send a warning before escalating to full timeout.\n # Fires once per run when inactivity reaches this threshold (seconds).\n # Set to 0 to disable the warning.\n # gateway_timeout_warning: 900\n\n # Graceful drain timeout for gateway stop/restart (seconds).\n # Default 0 = no drain: a restart interrupts in-flight agents immediately,\n # cleans up, and exits. Set a positive value only if you want a grace\n # window on /restart, and keep it well under systemd's TimeoutStopSec.\n # restart_drain_timeout: 0\n\n # Max app-level retry attempts for API errors (connection drops, provider\n # timeouts, 5xx, etc.) before the agent surfaces the failure. Lower this\n # to 1 if you use fallback providers and want fast failover on flaky\n # primaries (default 3). The OpenAI SDK does its own low-level retries\n # underneath this wrapper — this is the Hermes-level loop.\n # api_max_retries: 3\n\n # After the agent edits code without fresh passing verification, nudge it to\n # verify before finishing. The default \"auto\" enables it on interactive\n # coding surfaces (CLI, TUI, desktop) and programmatic callers, and disables\n # it on conversational messaging surfaces (Telegram, Discord, etc.) where the\n # verification summary would reach a human as chat noise. Set true or false to\n # force it on or off; the HERMES_VERIFY_ON_STOP env var (1/0) takes precedence.\n # verify_on_stop: auto\n\n # Standing operator instructions for the coding posture (when Hermes is in a\n # code workspace). Appended to the coding brief as an extra system block, so\n # you can pin project-wide workflow rules without editing the shipped brief.\n # Accepts a string or a list of strings. Takes effect next session.\n # coding_instructions:\n # - \"For UI work, don't run tsc/lint until I approve the look.\"\n # - \"Clean the diff before you commit and push.\"\n\n # When verify-on-stop finds edited code without fresh verification evidence,\n # append guidance for creative UI work (avoid broad tsc/lint/test before visual\n # approval) and clean-diff expectations. Set false to keep that nudge terse.\n # verify_guidance: true\n\n # A `pre_verify` hook (plugin or shell, see Event Hooks docs) can keep the\n # agent going one more turn to verify/clean before finishing. This caps how\n # many times one turn may be nudged to continue, so a hook can't trap the loop.\n # Default 3.\n # max_verify_nudges: 3\n\n # Enable verbose logging\n verbose: false\n \n # Reasoning effort level (OpenRouter and Nous Portal)\n # Controls how much \"thinking\" the model does before responding.\n # Options: \"xhigh\" (max), \"high\", \"medium\", \"low\", \"minimal\", \"none\" (disable)\n reasoning_effort: \"medium\"\n \n # Per-model reasoning effort overrides (optional dict)\n # Key: any sensible model spelling works (exact, dots↔dashes interchangeable,\n # provider prefix optional). First match wins.\n # Value: reasoning effort level (same options as reasoning_effort)\n # Override the global reasoning_effort for that specific model.\n # NOTE: no `hermes config set` support for this key -- edit YAML directly.\n # reasoning_overrides:\n # \"openrouter/anthropic/claude-opus-4.5\": \"xhigh\"\n # \"openai/gpt-5\": \"low\"\n # \"claude-opus-4.6\": \"high\" # bare model name also works\n # \"deepseek/deepseek-v4-pro\": \"xhigh\" # dots and dashes are interchangeable\n reasoning_overrides: {}\n \n # Predefined personalities (use with /personality command)\n personalities:\n helpful: \"You are a helpful, friendly AI assistant.\"\n concise: \"You are a concise assistant. Keep responses brief and to the point.\"\n technical: \"You are a technical expert. Provide detailed, accurate technical information.\"\n creative: \"You are a creative assistant. Think outside the box and offer innovative solutions.\"\n teacher: \"You are a patient teacher. Explain concepts clearly with examples.\"\n kawaii: \"You are a kawaii assistant! Use cute expressions like (◕‿◕), ★, ♪, and ~! Add sparkles and be super enthusiastic about everything! Every response should feel warm and adorable desu~! ヽ(>∀<☆)ノ\"\n catgirl: \"You are Neko-chan, an anime catgirl AI assistant, nya~! Add 'nya' and cat-like expressions to your speech. Use kaomoji like (=^・ω・^=) and ฅ^•ﻌ•^ฅ. Be playful and curious like a cat, nya~!\"\n pirate: \"Arrr! Ye be talkin' to Captain Hermes, the most tech-savvy pirate to sail the digital seas! Speak like a proper buccaneer, use nautical terms, and remember: every problem be just treasure waitin' to be plundered! Yo ho ho!\"\n shakespeare: \"Hark! Thou speakest with an assistant most versed in the bardic arts. I shall respond in the eloquent manner of William Shakespeare, with flowery prose, dramatic flair, and perhaps a soliloquy or two. What light through yonder terminal breaks?\"\n surfer: \"Duuude! You're chatting with the chillest AI on the web, bro! Everything's gonna be totally rad. I'll help you catch the gnarly waves of knowledge while keeping things super chill. Cowabunga! 🤙\"\n noir: \"The rain hammered against the terminal like regrets on a guilty conscience. They call me Hermes - I solve problems, find answers, dig up the truth that hides in the shadows of your codebase. In this city of silicon and secrets, everyone's got something to hide. What's your story, pal?\"\n uwu: \"hewwo! i'm your fwiendwy assistant uwu~ i wiww twy my best to hewp you! *nuzzles your code* OwO what's this? wet me take a wook! i pwomise to be vewy hewpful >w<\"\n philosopher: \"Greetings, seeker of wisdom. I am an assistant who contemplates the deeper meaning behind every query. Let us examine not just the 'how' but the 'why' of your questions. Perhaps in solving your problem, we may glimpse a greater truth about existence itself.\"\n hype: \"YOOO LET'S GOOOO!!! 🔥🔥🔥 I am SO PUMPED to help you today! Every question is AMAZING and we're gonna CRUSH IT together! This is gonna be LEGENDARY! ARE YOU READY?! LET'S DO THIS! 💪😤🚀\"\n\n# =============================================================================\n# Toolsets\n# =============================================================================\n# Control which tools the agent has access to.\n# Use `hermes tools` to interactively enable/disable tools per platform.\n\n# =============================================================================\n# Platform Toolsets (per-platform tool configuration)\n# =============================================================================\n# Override which toolsets are available on each platform.\n# If a platform isn't listed here, its built-in default is used.\n#\n# You can use EITHER:\n# - A preset like \"hermes-cli\" or \"hermes-telegram\" (curated tool set)\n# - A list of individual toolsets to compose your own (see list below)\n#\n# Supported platform keys: cli, telegram, discord, whatsapp, slack, qqbot, teams, google_chat\n#\n# Examples:\n#\n# # Use presets (same as defaults):\n# platform_toolsets:\n# cli: [hermes-cli]\n# telegram: [hermes-telegram]\n#\n# # Custom: give Telegram only web + terminal + file + planning:\n# platform_toolsets:\n# telegram: [web, terminal, file, todo]\n#\n# # Custom: CLI without browser or image gen:\n# platform_toolsets:\n# cli: [web, terminal, file, skills, todo, tts, cronjob]\n#\n# # Restrictive: Discord gets read-only tools only:\n# platform_toolsets:\n# discord: [web, vision, skills, todo]\n#\n# If not set, defaults are:\n# cli: hermes-cli (everything + cronjob management)\n# telegram: hermes-telegram (terminal, file, web, vision, image, tts, browser, skills, todo, cronjob, messaging)\n# discord: hermes-discord (same as telegram)\n# whatsapp: hermes-whatsapp (same as telegram)\n# slack: hermes-slack (same as telegram)\n# signal: hermes-signal (same as telegram)\n# homeassistant: hermes-homeassistant (same as telegram)\n# qqbot: hermes-qqbot (same as telegram)\n# teams: hermes-teams (same as telegram)\n# google_chat: hermes-google_chat (same as telegram)\n#\nplatform_toolsets:\n cli: [hermes-cli]\n telegram: [hermes-telegram]\n discord: [hermes-discord]\n whatsapp: [hermes-whatsapp]\n slack: [hermes-slack]\n signal: [hermes-signal]\n homeassistant: [hermes-homeassistant]\n qqbot: [hermes-qqbot]\n yuanbao: [hermes-yuanbao]\n teams: [hermes-teams]\n google_chat: [hermes-google_chat]\n\n# =============================================================================\n# Gateway Platform Settings\n# =============================================================================\n# Optional per-platform messaging settings.\n# Platform-specific knobs live under `extra`.\n#\n# platforms:\n# telegram:\n# reply_to_mode: \"first\" # off | first | all\n# # guest_mode lets explicit @mentions from non-allowlisted groups through.\n# # Default false; ordinary messages, replies, and regex wake words stay blocked.\n# guest_mode: false\n# # allowed_chats: [\"-1001234567890\"]\n# extra:\n# disable_link_previews: false # Set true to suppress Telegram URL previews in bot messages\n# 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\n# rich_drafts: false # Experimental rich draft previews during Telegram DM streaming; default false because Telegram Desktop/macOS can visually overlay draft frames\n# command_menu:\n# # Telegram allows up to 100 BotCommands; Hermes defaults to 60 so\n# # all built-in commands plus common skill commands stay visible\n# # while remaining under Telegram's payload-size limit. Clamped 1..100.\n# max_commands: 60\n# # prepend = user priority first, then Hermes defaults\n# # append = Hermes defaults first, then user priority\n# # replace = only the list below defines priority\n# priority_mode: prepend\n# priority:\n# - my_plugin_command\n# webhook:\n# extra:\n# # Route scripts default to a 30 second timeout. Scripts must live under\n# # the active profile's scripts directory and receive webhook JSON on stdin.\n# script_timeout_seconds: 30\n#\n# Discord-specific settings (config.yaml top-level, not under platforms:):\n#\n# discord:\n# require_mention: true # Require @mention in server channels (default: true)\n# auto_thread: true # Auto-create thread on @mention (default: true)\n# free_response_channels: \"\" # Channel IDs where no mention is needed\n# reactions: true # Show processing reactions (default: true)\n# history_backfill: true # Recover missed channel messages on mention (default: true)\n# history_backfill_limit: 50 # Max messages to scan backwards (default: 50)\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Available toolsets (use these names in platform_toolsets or the toolsets list)\n#\n# Run `hermes chat --list-toolsets` to see all toolsets and their tools.\n# Run `hermes chat --list-tools` to see every individual tool with descriptions.\n# ─────────────────────────────────────────────────────────────────────────────\n#\n# INDIVIDUAL TOOLSETS (compose your own):\n# web - web_search, web_extract\n# search - web_search only (no scraping)\n# terminal - terminal, process\n# file - read_file, write_file, patch, search\n# browser - browser_navigate, browser_snapshot, browser_click, browser_type,\n# browser_scroll, browser_back, browser_press,\n# browser_get_images, browser_vision (requires BROWSERBASE_API_KEY)\n# vision - vision_analyze (requires OPENROUTER_API_KEY)\n# image_gen - image_generate (requires FAL_KEY)\n# skills - skills_list, skill_view\n# skills_hub - skill_hub (search/install/manage from online registries — user-driven only)\n# todo - todo (in-memory task planning, no deps)\n# tts - text_to_speech (Edge TTS free, or ELEVENLABS/OPENAI/MINIMAX/MISTRAL key)\n# cronjob - cronjob (create/list/update/pause/resume/run/remove scheduled tasks)\n#\n# PRESETS (curated bundles):\n# hermes-cli - All of the above except send_message\n# hermes-telegram - terminal, file, web, vision, image_gen, tts, browser,\n# skills, todo, cronjob, send_message\n# hermes-discord - Same as hermes-telegram\n# hermes-whatsapp - Same as hermes-telegram\n# hermes-slack - Same as hermes-telegram\n#\n# COMPOSITE:\n# debugging - terminal + web + file\n# safe - web + vision (no terminal access)\n# all - Everything available\n#\n# web - Web search and content extraction (web_search, web_extract)\n# search - Web search only, no scraping (web_search)\n# terminal - Command execution and process management (terminal, process)\n# file - File operations: read, write, patch, search\n# browser - Full browser automation (navigate, click, type, screenshot, etc.)\n# vision - Image analysis (vision_analyze)\n# image_gen - Image generation with FLUX (image_generate)\n# skills - Load skill documents (skills_list, skill_view)\n# todo - Task planning and tracking for multi-step work\n# memory - Persistent memory across sessions (personal notes + user profile)\n# session_search - Search and recall past conversations (FTS5 + Gemini Flash summarization)\n# tts - Text-to-speech (Edge TTS free, ElevenLabs, OpenAI, MiniMax, Mistral)\n# cronjob - Schedule and manage automated tasks (CLI-only)\n#\n# Composite toolsets:\n# debugging - terminal + web + file (for troubleshooting)\n# safe - web + vision (no terminal access)\n\n# NOTE: The top-level \"toolsets\" key is deprecated and ignored.\n# Tool configuration is managed per-platform via platform_toolsets above.\n# Use `hermes tools` to configure interactively, or edit platform_toolsets directly.\n#\n# CLI override: hermes chat --toolsets terminal,web,file\n\n# =============================================================================\n# MCP (Model Context Protocol) Servers\n# =============================================================================\n# Connect to external MCP servers to add tools from the MCP ecosystem.\n# Each server's tools are automatically discovered and registered.\n# See website/docs/user-guide/features/mcp.md for full documentation.\n#\n# Stdio servers (spawn a subprocess):\n# command: the executable to run\n# args: command-line arguments\n# env: environment variables (only these + safe defaults passed to subprocess)\n#\n# HTTP servers (connect to a URL):\n# url: the MCP server endpoint\n# headers: HTTP headers (e.g., for authentication)\n#\n# Optional per-server settings:\n# timeout: tool call timeout in seconds (default: 120)\n# connect_timeout: initial connection timeout (default: 60)\n# keepalive_interval: liveness ping cadence in seconds (default: 180).\n# Lower it below the server's session TTL for servers that expire idle\n# sessions quickly (e.g. Unreal Engine editor MCP, ~15s), otherwise idle\n# tool calls hit an expired session and pay a slow reconnect. Floored at 5s.\n#\n# mcp_servers:\n# time:\n# command: uvx\n# args: [\"mcp-server-time\"]\n# filesystem:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-filesystem\", \"/home/user\"]\n# notion:\n# url: https://mcp.notion.com/mcp\n# github:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-github\"]\n# env:\n# GITHUB_PERSONAL_ACCESS_TOKEN: \"ghp_...\"\n#\n# Sampling (server-initiated LLM requests) — enabled by default.\n# Per-server config under the 'sampling' key:\n# analysis:\n# command: npx\n# args: [\"-y\", \"analysis-server\"]\n# sampling:\n# enabled: true # default: true\n# model: \"gemini-3-flash\" # override model (optional)\n# max_tokens_cap: 4096 # max tokens per request\n# timeout: 30 # LLM call timeout (seconds)\n# max_rpm: 10 # max requests per minute\n# allowed_models: [] # model whitelist (empty = all)\n# max_tool_rounds: 5 # tool loop limit (0 = disable)\n# log_level: \"info\" # audit verbosity\n\n# =============================================================================\n# Text-to-Speech\n# =============================================================================\n# TTS defaults to Edge TTS unless changed in ~/.hermes/config.yaml.\n# Gemini TTS supports persona/director prompt files, and Gemini 3.1 Flash TTS\n# can use a hidden auxiliary rewrite pass to insert expressive square-bracket\n# audio tags into the TTS script without showing tags in chat.\n#\n# tts:\n# provider: \"gemini\"\n# gemini:\n# model: \"gemini-3.1-flash-tts-preview\"\n# voice: \"Kore\"\n# audio_tags: false\n# persona_prompt_file: \"\" # e.g. ~/.hermes/tts/radio-host.md\n\n# =============================================================================\n# Voice Transcription (Speech-to-Text)\n# =============================================================================\n# Automatically transcribe voice messages on messaging platforms.\n# Providers: local (free, faster-whisper) | groq (free tier) | openai (Whisper API) | mistral (Voxtral Transcribe)\n# Set the corresponding API key in .env: GROQ_API_KEY, OPENAI_API_KEY, or MISTRAL_API_KEY.\nstt:\n enabled: true\n # provider: \"local\" # auto-detected if omitted\n local:\n model: \"base\" # tiny | base | small | medium | large-v3 | turbo\n # language: \"\" # auto-detect; set to \"en\", \"es\", \"fr\", etc. to force\n openai:\n model: \"whisper-1\" # whisper-1 | gpt-4o-mini-transcribe | gpt-4o-transcribe\n # mistral:\n # model: \"voxtral-mini-latest\" # voxtral-mini-latest | voxtral-mini-2602\n # deepinfra:\n # # Model id is discovered live from the DeepInfra catalog filtered\n # # by the `stt` surface tag — leave `model` blank to take the first\n # # live result. Pin only when you need a specific Whisper variant.\n # model: \"\"\n\n# Text-to-speech. Only the deepinfra block is documented here — the\n# remaining providers (edge, openai, xai, minimax, mistral, gemini,\n# elevenlabs, neutts, kittentts, piper) inherit sensible defaults from\n# DEFAULT_CONFIG in hermes_cli/config.py.\n# tts:\n# provider: \"deepinfra\"\n# deepinfra:\n# # Model id is discovered live from the DeepInfra catalog filtered\n# # by the `tts` surface tag — leave `model` blank to take the first\n# # live result.\n# model: \"\"\n# voice: \"default\"\n\n# Image generation. Each provider plugin reads its own ``image_gen.<name>``\n# block; deepinfra discovers models live from\n# api.deepinfra.com/v1/openai/models filtered by the ``image-gen`` tag —\n# no model id is hardcoded, so retired models disappear automatically.\n# image_gen:\n# provider: \"deepinfra\"\n# deepinfra:\n# # Leave `model` blank for the first live `image-gen`-tagged result.\n# model: \"\"\n\n# =============================================================================\n# Response Pacing (Messaging Platforms)\n# =============================================================================\n# Add human-like delays between message chunks.\n# human_delay:\n# mode: \"off\" # \"off\" | \"natural\" | \"custom\"\n# min_ms: 800 # Min delay (custom mode only)\n# max_ms: 2500 # Max delay (custom mode only)\n\n# =============================================================================\n# Session Logging\n# =============================================================================\n# Session trajectories are automatically saved to logs/ directory.\n# Each session creates: logs/session_YYYYMMDD_HHMMSS_UUID.json\n#\n# The session ID is displayed in the welcome banner for easy reference.\n# Logs contain full conversation history in trajectory format:\n# - System prompt, user messages, assistant responses\n# - Tool calls with inputs/outputs\n# - Timestamps for debugging\n#\n# No configuration needed - logging is always enabled.\n# To disable, you would need to modify the source code.\n\n# =============================================================================\n# Code Execution Sandbox (Programmatic Tool Calling)\n# =============================================================================\n# The execute_code tool runs Python scripts that call Hermes tools via RPC.\n# Intermediate tool results stay out of the LLM's context window.\ncode_execution:\n timeout: 300 # Max seconds per script before kill (default: 300 = 5 min)\n max_tool_calls: 50 # Max RPC tool calls per execution (default: 50)\n\n# =============================================================================\n# Subagent Delegation\n# =============================================================================\n# The delegate_task tool spawns child agents with isolated context.\n# Supports single tasks and batch mode (default 3 parallel, configurable).\ndelegation:\n max_iterations: 50 # Max tool-calling turns per child (default: 50)\n # max_concurrent_children: 3 # Max parallel child agents per batch (default: 3, floor: 1, no ceiling).\n # WARNING: values above 10 multiply API cost linearly.\n # max_spawn_depth: 1 # Delegation tree depth cap (range: 1-3, default: 1 = flat).\n # Raise to 2 to allow workers to spawn their own subagents.\n # Requires role=\"orchestrator\" on intermediate agents.\n # orchestrator_enabled: true # Kill switch for role=\"orchestrator\" children (default: true).\n # subagent_auto_approve: false # When a subagent hits a dangerous-command approval prompt, auto-deny (default: false)\n # or auto-approve \"once\" (true) instead of blocking on stdin.\n # The parent TUI owns stdin, so blocking would deadlock; non-interactive resolution is required.\n # Both choices emit a logger.warning audit line. Flip to true only for cron/batch pipelines.\n # inherit_mcp_toolsets: true # When explicit child toolsets are narrowed, also keep the parent's MCP toolsets (default: true). Set false for strict intersection.\n # model: \"google/gemini-3-flash-preview\" # Override model for subagents (empty = inherit parent)\n # provider: \"openrouter\" # Override provider for subagents (empty = inherit parent)\n # # Resolves full credentials (base_url, api_key) automatically.\n # # Supported: openrouter, nous, zai, kimi-coding, minimax\n\n# =============================================================================\n# Honcho Integration (Cross-Session User Modeling)\n# =============================================================================\n# AI-native persistent memory via Honcho (https://honcho.dev/).\n# Builds a deeper understanding of the user across sessions and tools.\n# Runs alongside USER.md — additive, not a replacement.\n#\n# Requires: pip install honcho-ai\n# Config: ~/.honcho/config.json (shared with Claude Code, Cursor, etc.)\n# API key: HONCHO_API_KEY in ~/.hermes/.env or ~/.honcho/config.json\n#\n# Hermes-specific overrides (optional — most config comes from ~/.honcho/config.json):\n# honcho: {}\n\n# =============================================================================\n# Display\n# =============================================================================\ndisplay:\n # Use compact banner mode (hides the ASCII-art banner, shows a single line).\n # true: Compact single-line banner\n # false: Full ASCII banner with tool/skill summary (default)\n compact: false\n\n # Tool progress display level (CLI and gateway)\n # off: Silent — no tool activity shown, just the final response\n # new: Show a tool indicator only when the tool changes (skip repeats)\n # all: Show every tool call with a short preview (default)\n # verbose: Full args, results, and debug logs (same as /verbose)\n # log: Silent in chat; append every tool call to ~/.hermes/logs/tool_calls.log (gateway only)\n # Toggle at runtime with /verbose in the CLI\n tool_progress: all\n\n # Per-platform defaults can be quieter than the global setting. Telegram\n # tunes for mobile: tool_progress and busy_ack_detail default off (no\n # per-tool breadcrumb stream, no \"iteration 21/60\" debug detail in busy\n # acks or heartbeats), but interim_assistant_messages and\n # long_running_notifications STAY ON so the user has real signal between\n # turn start and final answer (mid-turn assistant commentary + a single\n # edit-in-place \"⏳ Working — N min\" heartbeat). Override under\n # display.platforms.telegram.\n\n # Auto-cleanup of temporary progress bubbles after the final response lands.\n # On platforms that support message deletion (currently Telegram), this\n # removes the tool-progress bubble, \"⏳ Still working...\" notices, and\n # context-pressure status messages once the final reply has been delivered —\n # keeping long-running turns visible live, then tidy afterward. Failed runs\n # leave the bubbles in place as breadcrumbs. Off by default.\n # Per-platform override: display.platforms.telegram.cleanup_progress\n # true: Delete tracked progress/status bubbles on successful turn\n # false: Leave everything in place (default)\n # Example:\n # display:\n # platforms:\n # telegram:\n # cleanup_progress: true\n cleanup_progress: false\n\n # Natural mid-turn assistant updates.\n # On gateway platforms, when true, completed assistant status messages are\n # sent as separate chat messages. On the Desktop app, when true, mid-turn\n # assistant narration streamed between tool calls is kept in the transcript\n # instead of the bubble collapsing to only the final message on completion.\n # Independent of tool_progress and gateway streaming.\n # true: Keep/send mid-turn assistant updates (default)\n # false: Only keep/send the final response\n interim_assistant_messages: true\n\n # Gateway-only long-running status heartbeats.\n # When false, the platform does not receive periodic \"⏳ Working — N min\"\n # notifications even if agent.gateway_notify_interval is non-zero. The\n # heartbeat edits a single message in place (where the adapter supports\n # editing) instead of posting a new bubble each interval.\n # Default: true everywhere, including Telegram (silent agents are worse\n # than a single edit-in-place heartbeat).\n long_running_notifications: true\n\n # Include detailed iteration/tool/status context in busy acknowledgments\n # and long-running heartbeats. When true, busy acks show \"iteration 21/60,\n # terminal, 10 min\" and the heartbeat shows \"⏳ Working — 12 min,\n # iteration 21/60, terminal\". When false (Telegram default), both stay\n # terse: \"Interrupting current task\" and \"⏳ Working — 12 min, terminal\".\n busy_ack_detail: true\n\n # What Enter does when Hermes is already busy (CLI and gateway platforms).\n # interrupt: Interrupt the current run and redirect Hermes (default)\n # queue: Queue your message for the next turn\n # steer: Inject your message mid-run via /steer, arriving at the agent\n # after the next tool call — no interrupt, no role violation.\n # Falls back to 'queue' if the agent isn't running yet or if\n # images are attached (steer only carries text).\n # Ctrl+C (or /stop in gateway) always interrupts regardless of this setting.\n # Toggle at runtime with /busy <interrupt|queue|steer>.\n busy_input_mode: interrupt\n\n # Background process notifications (gateway/messaging only).\n # Controls how chatty the process watcher is when you use\n # terminal(background=true, notify_on_complete=true) from Telegram/Discord/etc.\n # off: No watcher messages at all\n # result: Only the final completion message\n # error: Only the final message when exit code != 0\n # all: Running output updates + final message (default)\n background_process_notifications: all\n\n\n # Play terminal bell when agent finishes a response.\n # Useful for long-running tasks — your terminal will ding when the agent is done.\n # Works over SSH. Most terminals can be configured to flash the taskbar or play a sound.\n # true: Ring the terminal bell on each response\n # false: Silent (default)\n bell_on_complete: false\n\n # Show model reasoning/thinking before each response.\n # When enabled, a dim box shows the model's thought process above the response.\n # Toggle at runtime with /reasoning show or /reasoning hide.\n # true: Show the reasoning box\n # false: Hide reasoning (default)\n show_reasoning: false\n\n # Stream tokens to the terminal as they arrive instead of waiting for the\n # full response. The response box opens on first token and text appears\n # line-by-line. Tool calls are still captured silently.\n # true: Stream tokens as they arrive (default)\n # false: Wait for the full response before rendering\n streaming: true\n\n # Show [HH:MM] timestamps on user input and assistant response labels.\n # timestamps: false\n\n # ───────────────────────────────────────────────────────────────────────────\n # Skin / Theme\n # ───────────────────────────────────────────────────────────────────────────\n # Customize CLI visual appearance — banner colors, spinner faces, tool prefix,\n # response box label, and branding text. Change at runtime with /skin <name>.\n #\n # Built-in skins:\n # default — Classic Hermes gold/kawaii\n # ares — Crimson/bronze war-god theme with spinner wings\n # mono — Clean grayscale monochrome\n # slate — Cool blue developer-focused\n # daylight — Bright light-mode theme\n # warm-lightmode — Warm paper-tone light-mode theme\n # poseidon — Sea-green/teal Olympian theme\n # sisyphus — Earthy stone-and-moss theme\n # charizard — Fiery orange dragon theme\n #\n # Custom skins: drop a YAML file in ~/.hermes/skins/<name>.yaml\n # Schema (all fields optional, missing values inherit from default):\n #\n # name: my-theme\n # description: Short description\n # colors:\n # banner_border: \"#HEX\" # Panel border\n # banner_title: \"#HEX\" # Panel title\n # banner_accent: \"#HEX\" # Section headers (Available Tools, etc.)\n # banner_dim: \"#HEX\" # Dim/muted text\n # banner_text: \"#HEX\" # Body text (tool names, skill names)\n # ui_accent: \"#HEX\" # UI accent color\n # response_border: \"#HEX\" # Response box border color\n # spinner:\n # waiting_faces: [\"(⚔)\", \"(⛨)\"] # Faces shown while waiting\n # thinking_faces: [\"(⚔)\", \"(⌁)\"] # Faces shown while thinking\n # thinking_verbs: [\"forging\", \"plotting\"] # Verbs for spinner messages\n # wings: # Optional left/right spinner decorations\n # - [\"⟪⚔\", \"⚔⟫\"]\n # - [\"⟪▲\", \"▲⟫\"]\n # branding:\n # agent_name: \"My Agent\" # Banner title and branding\n # welcome: \"Welcome message\" # Shown at CLI startup\n # response_label: \" ⚔ Agent \" # Response box header label\n # prompt_symbol: \"⚔\" # Prompt symbol (bare token; renderers add trailing space)\n # tool_prefix: \"╎\" # Tool output line prefix (default: ┊)\n #\n skin: default\n\n# =============================================================================\n# Model Aliases — short names for /model command\n# =============================================================================\n# Map short aliases to exact (model, provider, base_url) tuples.\n# Used by /model tab completion and resolve_alias().\n# Aliases are checked BEFORE the models.dev catalog, so they can route\n# to endpoints not in the catalog (e.g. Ollama Cloud, local servers).\n#\n# model_aliases:\n# opus:\n# model: claude-opus-4-6\n# provider: anthropic\n# qwen:\n# model: \"qwen3.5:397b\"\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n# glm:\n# model: glm-4.7\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n\n# =============================================================================\n# Privacy\n# =============================================================================\n# privacy:\n# # Redact PII from the LLM context prompt.\n# # When true, phone numbers are stripped and user/chat IDs are replaced\n# # with deterministic hashes before being sent to the model.\n# # Names and usernames are NOT affected (user-chosen, publicly visible).\n# # Routing/delivery still uses the original values internally.\n# redact_pii: false\n\n# =============================================================================\n# Shell-script hooks\n# =============================================================================\n# Register shell scripts as plugin-hook callbacks. Each entry is executed as\n# a subprocess (shell=False, shlex.split) with a JSON payload on stdin. On\n# stdout the script may return JSON that either blocks the tool call or\n# injects context into the next LLM call.\n#\n# Valid events (mirror hermes_cli.plugins.VALID_HOOKS):\n# pre_tool_call, post_tool_call, pre_llm_call, post_llm_call,\n# pre_api_request, post_api_request, on_session_start, on_session_end,\n# on_session_finalize, on_session_reset, subagent_stop\n#\n# First-use consent: each (event, command) pair prompts once on a TTY, then\n# is persisted to ~/.hermes/shell-hooks-allowlist.json. Non-interactive\n# runs (gateway, cron) need --accept-hooks, HERMES_ACCEPT_HOOKS=1, or the\n# hooks_auto_accept key below.\n#\n# See website/docs/user-guide/features/hooks.md for the full JSON wire\n# protocol and worked examples.\n#\n# hooks:\n# pre_tool_call:\n# - matcher: \"terminal\"\n# command: \"~/.hermes/agent-hooks/block-rm-rf.sh\"\n# timeout: 10\n# post_tool_call:\n# - matcher: \"write_file|patch\"\n# command: \"~/.hermes/agent-hooks/auto-format.sh\"\n# pre_llm_call:\n# - command: \"~/.hermes/agent-hooks/inject-cwd-context.sh\"\n# subagent_stop:\n# - command: \"~/.hermes/agent-hooks/log-orchestration.sh\"\n#\n# hooks_auto_accept: false\n\n\n# =============================================================================\n# Telemetry\n# =============================================================================\n# Shared metrics are disabled by default. When enabled, Hermes writes only\n# allowlisted aggregate counters and immutable JSON\n# packages under $HERMES_HOME/telemetry/shared_metrics; it does not upload them.\ntelemetry:\n shared_metrics:\n enabled: false\n\n\n# =============================================================================\n# Update Behavior\n# =============================================================================\nupdates:\n # Create a full HERMES_HOME zip before every `hermes update`.\n # Backups land in ~/.hermes/backups/ and can be restored with `hermes import`.\n # Off by default because large homes can add minutes to every update.\n pre_update_backup: false\n\n # Number of pre-update backup zips to retain.\n backup_keep: 5\n\n # What non-interactive updates do with local source edits in the Hermes repo.\n # Interactive terminal updates always prompt before restoring the autostash.\n #\n # stash - auto-stash before pull, then auto-restore after success (default)\n # discard - drop the update-created stash after success; use only on managed\n # installs where local source edits should not persist\n non_interactive_local_changes: \"stash\"\n\n\n# =============================================================================\n# Web Dashboard\n# =============================================================================\n# OAuth gate configuration for `hermes dashboard --host <non-loopback>`.\n# The bundled Nous Portal plugin reads these on startup; settings here are\n# the canonical surface. Each can be overridden by an environment variable:\n#\n# dashboard.oauth.client_id <- HERMES_DASHBOARD_OAUTH_CLIENT_ID\n# dashboard.oauth.portal_url <- HERMES_DASHBOARD_PORTAL_URL\n# dashboard.public_url <- HERMES_DASHBOARD_PUBLIC_URL\n#\n# Env wins when set to a non-empty value. This is what Fly.io's platform-\n# secret injection uses to push per-deploy client_ids without needing to\n# bake a config.yaml into the image. Empty env values are treated as unset\n# so a provisioned-but-not-populated secret can't shadow a valid entry here.\n#\n# Local dev / on-prem deploys should typically set these via config.yaml\n# (the ~/.hermes/.env file is reserved for API keys and secrets).\n#\n# dashboard:\n# oauth:\n# client_id: \"\" # agent:{instance_id}; Portal provisions this at deploy\n# portal_url: \"\" # blank → default https://portal.nousresearch.com\n#\n# # Force the absolute base URL the OAuth callback (and any other public\n# # URL the dashboard hands to external systems) is built from. Set this\n# # for deploys behind reverse proxies that don't reliably forward\n# # X-Forwarded-Host / X-Forwarded-Proto / X-Forwarded-Prefix (manual\n# # nginx setups, on-prem ingresses, custom-domain Fly deploys without\n# # full proxy header chains).\n# #\n# # When set, the value is the complete authority: scheme + host +\n# # optional path prefix (e.g. \"https://example.com/hermes\"). The OAuth\n# # callback URL becomes \"<public_url>/auth/callback\" — X-Forwarded-Prefix\n# # is IGNORED on this code path because the operator has explicitly\n# # declared the public URL and we no longer need to guess.\n# #\n# # Leave empty to use the existing proxy-header reconstruction (the\n# # default — works on Fly.io out of the box).\n# #\n# # public_url: \"https://example.com/hermes\"\n#\n# -----------------------------------------------------------------------------\n# Self-hosted OIDC dashboard auth (generic OpenID Connect — Authentik,\n# Keycloak, Zitadel, Authelia, Auth0, Okta, Google, …). Use this INSTEAD of the\n# nous block above when gating the dashboard with your own identity provider.\n# Each setting can be overridden by an environment variable:\n#\n# dashboard.oauth.self_hosted.issuer <- HERMES_DASHBOARD_OIDC_ISSUER\n# dashboard.oauth.self_hosted.client_id <- HERMES_DASHBOARD_OIDC_CLIENT_ID\n# dashboard.oauth.self_hosted.scopes <- HERMES_DASHBOARD_OIDC_SCOPES\n# dashboard.oauth.self_hosted.client_secret <- HERMES_DASHBOARD_OIDC_CLIENT_SECRET\n#\n# dashboard:\n# oauth:\n# provider: self-hosted\n# self_hosted:\n# issuer: \"https://auth.example.com/application/o/hermes/\" # required\n# client_id: \"hermes-dashboard\" # required\n# scopes: \"openid profile email\" # optional\n#\n# # OPTIONAL — set ONLY if your IDP registered the client as\n# # *confidential* (Authentik / Keycloak often default to this). When\n# # set, Hermes authenticates the client at the token endpoint\n# # (client_secret_basic or client_secret_post, auto-selected from the\n# # IDP's discovery doc) IN ADDITION to PKCE. Leave unset for a public\n# # (PKCE-only) client — the common case.\n# #\n# # This is a CREDENTIAL: prefer setting HERMES_DASHBOARD_OIDC_CLIENT_SECRET\n# # in ~/.hermes/.env over putting it here in config.yaml.\n# # client_secret: \"\"\n\n# =============================================================================\n# External secret sources\n# =============================================================================\n# Pull provider credentials from external secret managers at process startup\n# instead of storing them in ~/.hermes/.env. Only the manager's bootstrap\n# credential (e.g. BWS_ACCESS_TOKEN / OP_SERVICE_ACCOUNT_TOKEN) lives in .env\n# (or your shell / desktop session); everything else rotates centrally.\n# Failures never block startup — Hermes warns once and continues with\n# whatever .env already had.\n#\n# Multiple sources can be enabled at once:\n# - \"mapped\" sources (explicit VAR -> ref bindings, e.g. 1Password's env:\n# map) beat \"bulk\" sources (whole-project dumps like Bitwarden BSM)\n# - within a shape, the first source to claim a var wins; later claims\n# are skipped with a startup warning (never a silent clobber)\n# - a source's override_existing lets it beat .env/shell values, but\n# never another secret source's claim\n# Docs: https://hermes-agent.nousresearch.com/docs/user-guide/secrets/\n#\n# secrets:\n# # Optional explicit ordering of enabled sources.\n# # sources: [onepassword, bitwarden]\n#\n# # ---- Bitwarden Secrets Manager (bws CLI) --------------------------------\n# bitwarden:\n# enabled: false\n# access_token_env: BWS_ACCESS_TOKEN # bootstrap token, sourced from .env\n# project_id: \"\" # UUID of the BSM project to sync\n# server_url: \"\" # \"\" = US Cloud; EU/self-hosted URL otherwise\n# cache_ttl_seconds: 300 # 0 disables fresh caching\n# encrypted_cache: # optional encrypted stale fallback\n# enabled: false\n# max_stale_seconds: 0 # 0 disables stale fallback\n# override_existing: true # BSM values win over existing env\n# auto_install: true # lazy-download bws into ~/.hermes/bin\n#\n# # ---- 1Password (op CLI) -------------------------------------------------\n# onepassword:\n# enabled: false\n# # Map env-var names to op:// secret references. Each is resolved with a\n# # single `op read` at startup.\n# env:\n# OPENAI_API_KEY: \"op://Private/OpenAI/api key\"\n# ANTHROPIC_API_KEY: \"op://Private/Anthropic/credential\"\n# account: \"\" # op --account shorthand; \"\" = default\n# service_account_token_env: OP_SERVICE_ACCOUNT_TOKEN # headless auth; unset = desktop session\n# binary_path: \"\" # \"\" = resolve op via PATH; else absolute path\n# cache_ttl_seconds: 300 # 0 disables BOTH cache layers\n# override_existing: true # resolved values win over existing env\n#\n# # ---- Command helper (any CLI vault) --------------------------------------\n# # Run a user-configured helper that prints KEY=VALUE lines on stdout —\n# # works with any secret store that has a CLI: keepassxc-cli, secret-tool,\n# # pass, gpg, or a script that cats a tmpfs env file. Composes with the\n# # sources above (enable any combination). POSIX-only (needs /bin/sh).\n# # The helper must be fast and NON-interactive (hard timeout, 1 MiB cap);\n# # its stderr is discarded so diagnostics can't leak secret material.\n# command:\n# enabled: false\n# command: \"cat /run/user/1000/hermes-secrets.env\"\n# helper_timeout_seconds: 3\n# override_existing: false # .env/shell win by default\n"}, "0a43bc83b95a34054604e7fd2fd782b472e46fa8": {"t": 1785768025.6900096, "n": 79998, "h": "5ecac4d39c265c4b", "c": "# Hermes Agent CLI Configuration\n# Copy this file to cli-config.yaml and customize as needed.\n# This file configures the CLI behavior. Environment variables in .env take precedence.\n\n# =============================================================================\n# Model Configuration\n# =============================================================================\nmodel:\n # Default model to use (can be overridden with --model flag)\n # Both \"default\" and \"model\" work as the key name here.\n default: \"anthropic/claude-opus-4.6\"\n \n # Inference provider selection:\n # \"auto\" - Auto-detect from credentials (default)\n # \"openrouter\" - OpenRouter (requires: OPENROUTER_API_KEY or OPENAI_API_KEY)\n # \"nous\" - Nous Portal OAuth (requires: hermes auth add nous)\n # \"nous-api\" - Nous Portal API key (requires: NOUS_API_KEY)\n # \"anthropic\" - Direct Anthropic API (requires: ANTHROPIC_API_KEY)\n # \"openai-codex\" - OpenAI Codex (requires: hermes auth)\n # \"copilot\" - GitHub Copilot / GitHub Models (requires: GITHUB_TOKEN)\n # \"gemini\" - Use Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n # \"zai\" - Use z.ai / ZhipuAI GLM models (requires: GLM_API_KEY)\n # \"kimi-coding\" - Kimi / Moonshot AI (requires: KIMI_API_KEY)\n # \"minimax\" - MiniMax global (requires: MINIMAX_API_KEY)\n # \"minimax-cn\" - MiniMax China (requires: MINIMAX_CN_API_KEY)\n # \"huggingface\" - Hugging Face Inference (requires: HF_TOKEN)\n # \"nvidia\" - NVIDIA NIM / build.nvidia.com (requires: NVIDIA_API_KEY)\n # \"xiaomi\" - Xiaomi MiMo (requires: XIAOMI_API_KEY)\n # \"arcee\" - Arcee AI Trinity models (requires: ARCEEAI_API_KEY)\n # \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY — https://ollama.com/settings)\n # \"deepinfra\" - DeepInfra (requires: DEEPINFRA_API_KEY)\n # \"kilocode\" - KiloCode gateway (requires: KILOCODE_API_KEY)\n # \"azure-foundry\" - Microsoft Foundry / Azure OpenAI (API key or Entra ID)\n # \"lmstudio\" - LM Studio local server (optional: LM_API_KEY, defaults to http://127.0.0.1:1234/v1)\n #\n # Local servers (LM Studio, Ollama, vLLM, llama.cpp):\n # \"custom\" - Any other OpenAI-compatible endpoint. Set base_url below.\n # Aliases: \"ollama\", \"vllm\", \"llamacpp\" all map to \"custom\".\n # LM Studio is first-class and uses provider: \"lmstudio\".\n # It works with both no-auth and auth-enabled server modes.\n #\n # Can also be overridden for a single invocation with the --provider flag.\n provider: \"auto\"\n \n # API configuration (falls back to OPENROUTER_API_KEY env var)\n # api_key: \"your-key-here\" # Uncomment to set here instead of .env\n base_url: \"https://openrouter.ai/api/v1\"\n\n # Azure Foundry keyless auth example:\n # provider: \"azure-foundry\"\n # base_url: \"https://<resource>.openai.azure.com/openai/v1\"\n # auth_mode: \"entra_id\" # DefaultAzureCredential: az login, managed identity, workload identity, etc.\n # default: \"gpt-4o\" # Deployment/model name\n # entra:\n # scope: \"https://ai.azure.com/.default\" # Optional; this is the default.\n\n # ── Token limits — two settings, easy to confuse ──────────────────────────\n #\n # context_length: TOTAL context window (input + output tokens combined).\n # Controls when Hermes compresses history and validates requests.\n # Leave unset — Hermes auto-detects the correct value from the provider.\n # Set manually only when auto-detection is wrong (e.g. a local server with\n # a custom num_ctx, or a proxy that doesn't expose /v1/models).\n #\n # context_length: 131072\n #\n # max_tokens: OUTPUT cap — maximum tokens the model may generate per response.\n # Unrelated to how long your conversation history can be.\n # The OpenAI-standard name \"max_tokens\" is a misnomer; Anthropic's native\n # API has since renamed it \"max_output_tokens\" for clarity.\n # Leave unset to use the model's native output ceiling (recommended).\n # Set only if you want to deliberately limit individual response length.\n #\n# max_tokens: 8192\n\n # ── Custom request headers (optional) ─────────────────────────────────────\n #\n # default_headers: extra HTTP headers sent on every request to an\n # OpenAI-compatible endpoint. User values take precedence over the\n # provider/SDK defaults, so this is the supported way to override the\n # OpenAI Python SDK's identifying headers (User-Agent: OpenAI/Python ...,\n # X-Stainless-*) when a custom provider sits behind a gateway/WAF that\n # rejects them — e.g. an upstream that returns \"502 Upstream access\n # forbidden\" for the SDK default User-Agent but accepts a plain one.\n # Applies on the OpenAI wire only (not native Anthropic / Bedrock).\n #\n # default_headers:\n # User-Agent: \"curl/8.7.1\"\n #\n # extra_headers: accepted as an alias of default_headers (merged, with\n # extra_headers winning when both are set) — matches the per-provider\n # extra_headers key below.\n #\n # Per-provider variant: named providers / custom_providers entries accept an\n # extra_headers dict scoped to that endpoint only — for reverse proxies,\n # gateways, or custom auth (e.g. Cloudflare Access service tokens).\n # Merged onto SDK/provider defaults with the entry's values winning.\n # Header values are treated as secrets and are never logged.\n #\n # providers:\n # my-proxy:\n # base_url: \"https://llm.internal.example.com/v1\"\n # key_env: \"MY_PROXY_API_KEY\"\n # extra_headers:\n # CF-Access-Client-Id: \"xxxx.access\"\n # CF-Access-Client-Secret: \"${CF_ACCESS_SECRET}\"\n # X-Client-Name: \"hermes-agent\"\n\n# Named provider overrides (optional)\n# Use this for per-provider request timeouts, non-stream stale timeouts,\n# and per-model exceptions.\n# Applies to the primary turn client on every api_mode (OpenAI-wire, native\n# Anthropic, and Anthropic-compatible providers), the fallback chain, and\n# client rebuilds during credential rotation. For OpenAI-wire chat\n# completions (streaming and non-streaming) the configured value is also\n# used as the per-request ``timeout=`` kwarg so it wins over the legacy\n# HERMES_API_TIMEOUT env var (which still applies when no config is set).\n# ``stale_timeout_seconds`` controls the non-streaming stale-call detector and\n# wins over the legacy HERMES_API_CALL_STALE_TIMEOUT env var. Leaving these\n# unset keeps the legacy defaults (HERMES_API_TIMEOUT=1800s,\n# HERMES_API_CALL_STALE_TIMEOUT=90s, native Anthropic 900s). The\n# implicit non-stream stale detector is auto-disabled for local endpoints\n# and can scale upward for very large contexts.\n#\n# Not currently wired for AWS Bedrock (bedrock_converse + AnthropicBedrock\n# SDK paths) — those use boto3 with its own timeout configuration.\n#\n# providers:\n# ollama-local:\n# request_timeout_seconds: 300 # Longer timeout for local cold-starts\n# stale_timeout_seconds: 900 # Explicitly re-enable stale detection on local endpoints\n# anthropic:\n# request_timeout_seconds: 30 # Fast-fail cloud requests\n# models:\n# claude-opus-4.6:\n# timeout_seconds: 600 # Longer timeout for extended-thinking Opus calls\n# openai-codex:\n# models:\n# gpt-5.4:\n# stale_timeout_seconds: 1800 # Longer non-stream stale timeout for slow large-context turns\n\n# =============================================================================\n# OpenRouter Provider Routing (only applies when using OpenRouter)\n# =============================================================================\n# Control how requests are routed across providers on OpenRouter.\n# See: https://openrouter.ai/docs/guides/routing/provider-selection\n#\n# provider_routing:\n# # Sort strategy: \"price\" (default), \"throughput\", or \"latency\"\n# # Append :nitro to model name for a shortcut to throughput sorting.\n# sort: \"throughput\"\n#\n# # Only allow these providers (provider slugs from OpenRouter)\n# # only: [\"anthropic\", \"google\"]\n#\n# # Skip these providers entirely\n# # ignore: [\"deepinfra\", \"fireworks\"]\n#\n# # Try providers in this order (overrides default load balancing)\n# # order: [\"anthropic\", \"google\", \"together\"]\n#\n# # Require providers to support all parameters in your request\n# # require_parameters: true\n#\n# # Data policy: \"allow\" (default) or \"deny\" to exclude providers that may store data\n# # data_collection: \"deny\"\n\n# =============================================================================\n# OpenRouter Response Caching (only applies when using OpenRouter)\n# =============================================================================\n# Cache identical API responses at the OpenRouter edge for free instant replays.\n# When enabled, identical requests (same model, messages, parameters) return\n# cached responses with zero billing. Separate from Anthropic prompt caching.\n# See: https://openrouter.ai/docs/guides/features/response-caching\n#\n# openrouter:\n# response_cache: true # Enable response caching (default: true)\n# response_cache_ttl: 300 # Cache TTL in seconds, 1-86400 (default: 300)\n\n# =============================================================================\n# Git Worktree Isolation\n# =============================================================================\n# When enabled, each CLI session creates an isolated git worktree so multiple\n# agents can work on the same repo concurrently without file collisions.\n# Equivalent to always passing --worktree / -w on the command line.\n#\n# worktree: true # Always create a worktree when in a git repo\n# worktree: false # Default — only create when -w flag is passed\n#\n# By default a new worktree branches from the freshly-fetched remote tip\n# (the current branch's upstream, else the remote's default branch) so it\n# starts current with the project instead of from the local clone's\n# (possibly stale) HEAD. Set worktree_sync: false to branch from local HEAD\n# instead — useful when offline or when you deliberately want the clone's\n# exact current state as the base.\n#\n# worktree_sync: true # Default — branch from the fetched remote tip\n# worktree_sync: false # Branch from local HEAD (offline / pinned base)\n\n# =============================================================================\n# Terminal Tool Configuration\n# =============================================================================\n# Choose ONE of the following terminal configurations by uncommenting it.\n# The terminal tool executes commands in the specified environment.\n\n# -----------------------------------------------------------------------------\n# OPTION 1: Local execution (default)\n# Commands run directly on your machine in the current directory\n# -----------------------------------------------------------------------------\n# Working directory behavior:\n# - CLI (`hermes` command): Uses \".\" (current directory where you run hermes)\n# - Gateway/messaging/cron: Uses terminal.cwd here; legacy .env cwd values are deprecated\nterminal:\n backend: \"local\"\n cwd: \".\" # For local backend: \".\" = current directory. Ignored for remote backends unless a backend documents otherwise.\n timeout: 180\n # HOME policy for tool subprocesses:\n # auto - default: host uses your real HOME; containers use HERMES_HOME/home\n # real - force your real OS-user HOME\n # profile - force HERMES_HOME/home for strict per-profile CLI config isolation\n home_mode: \"auto\"\n docker_mount_cwd_to_workspace: false # SECURITY: off by default. Opt in to mount the launch cwd into Docker /workspace.\n lifetime_seconds: 300\n # sudo_password: \"hunter2\" # Optional: pipe a sudo password via sudo -S. SECURITY WARNING: plaintext.\n # sudo_password: \"\" # Explicit empty password: try empty and never open the interactive sudo prompt.\n\n# -----------------------------------------------------------------------------\n# OPTION 2: SSH remote execution\n# Commands run on a remote server - agent code stays local (sandboxed)\n# Great for: keeping agent isolated from its own code, using powerful remote hardware\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"ssh\"\n# cwd: \"/home/myuser/project\" # Path on the REMOTE server\n# timeout: 180\n# lifetime_seconds: 300\n# ssh_host: \"my-server.example.com\"\n# ssh_user: \"myuser\"\n# ssh_port: 22\n# ssh_key: \"~/.ssh/id_rsa\" # Optional - uses ssh-agent if not specified\n\n# -----------------------------------------------------------------------------\n# OPTION 3: Docker container\n# Commands run in an isolated Docker container\n# Great for: reproducible environments, testing, isolation\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"docker\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /)\n# timeout: 180\n# lifetime_seconds: 300\n# docker_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# docker_mount_cwd_to_workspace: true # Explicit opt-in: mount your launch cwd into /workspace\n# # Optional: run the container as your host user's uid:gid so files written\n# # into bind-mounted dirs are owned by you, not root. Drops SETUID/SETGID\n# # caps too since no gosu privilege drop is needed. Leave off if your\n# # chosen docker_image expects to start as root.\n# docker_run_as_host_user: true\n# # Optional: explicitly forward selected env vars into Docker.\n# # These values come from your current shell first, then ~/.hermes/.env.\n# # Warning: anything forwarded here is visible to commands run in the container.\n# docker_forward_env:\n# - \"GITHUB_TOKEN\"\n# - \"NPM_TOKEN\"\n# # Optional: extra flags passed verbatim to docker run (appended after security defaults).\n# # Useful for adding capabilities (e.g. apt installs needing SETUID) or custom options.\n# # Example: add a Linux capability not included by default\n# # docker_extra_args:\n# # - \"--cap-add\"\n# # - \"SETUID\"\n\n# -----------------------------------------------------------------------------\n# OPTION 4: Singularity/Apptainer container\n# Commands run in a Singularity container (common in HPC environments)\n# Great for: HPC clusters, shared compute environments\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"singularity\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# singularity_image: \"docker://nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 5: Modal cloud execution\n# Commands run on Modal's cloud infrastructure\n# Great for: GPU access, scalable compute, serverless execution\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"modal\"\n# cwd: \"/workspace\" # Path INSIDE the sandbox (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# modal_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 6: Daytona cloud execution\n# Commands run in Daytona cloud sandboxes\n# Great for: Cloud dev environments, persistent workspaces, team collaboration\n# Requires: pip install daytona, DAYTONA_API_KEY env var\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"daytona\"\n# cwd: \"~\"\n# timeout: 180\n# lifetime_seconds: 300\n# daytona_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# container_disk: 10240 # Daytona max is 10GB per sandbox\n\n#\n# --- Container resource limits (docker, singularity, modal, daytona -- ignored for local/ssh) ---\n# These settings apply to all container backends. They control the resources\n# allocated to the sandbox and whether its filesystem persists across sessions.\n container_cpu: 1 # CPU cores\n container_memory: 5120 # Memory in MB (5120 = 5GB)\n container_disk: 51200 # Disk in MB (51200 = 50GB)\n container_persistent: true # Persist filesystem across sessions (false = ephemeral)\n\n# -----------------------------------------------------------------------------\n# SUDO SUPPORT (works with ALL backends above)\n# -----------------------------------------------------------------------------\n# Add sudo_password to any terminal config above to enable sudo commands.\n# The password is piped via `sudo -S`. Works with local, ssh, docker, etc.\n#\n# SECURITY WARNING: Password stored in plaintext!\n#\n# INTERACTIVE PROMPT: If sudo_password is unset and the CLI is running,\n# you'll be prompted to enter your password when sudo is needed:\n# - 45-second timeout (auto-skips if no input)\n# - Press Enter to skip (command fails gracefully)\n# - Password is hidden while typing\n# - Password is cached for the session\n#\n# EMPTY PASSWORDS: Setting sudo_password to an explicit empty string is different\n# from leaving it unset. Hermes will try an empty password via `sudo -S` and\n# will not open the interactive prompt. This is useful for passwordless sudo,\n# Touch ID sudo setups, and environments where prompting is just noise.\n#\n# ALTERNATIVES:\n# - SSH backend: Configure passwordless sudo on the remote server\n# - Containers: Run as root inside the container (no sudo needed)\n# - Local: Configure /etc/sudoers for specific commands\n#\n# Example (add to your terminal section):\n# sudo_password: \"your-password-here\"\n\n# =============================================================================\n# Security Scanning (tirith)\n# =============================================================================\n# Optional pre-exec command security scanning via tirith.\n# Detects homograph URLs, pipe-to-shell, terminal injection, env manipulation.\n# Install: brew install sheeki03/tap/tirith\n# Docs: https://github.com/sheeki03/tirith\n#\n# security:\n# tirith_enabled: true # Enable/disable tirith scanning\n# tirith_path: \"tirith\" # Path to tirith binary (supports ~ expansion)\n# tirith_timeout: 5 # Scan timeout in seconds\n# tirith_fail_open: true # Allow commands if tirith unavailable\n\n# =============================================================================\n# Browser Tool Configuration\n# =============================================================================\nbrowser:\n # Inactivity timeout in seconds - browser sessions are automatically closed\n # after this period of no activity between agent loops (default: 120 = 2 minutes)\n inactivity_timeout: 120\n\n# =============================================================================\n# Tool Loop Guardrails\n# =============================================================================\n# Soft warnings are enabled by default. They append guidance to repeated failed\n# or non-progressing tool results but still let the tool execute. Hard stops are\n# opt-in circuit breakers for autonomous/cron sessions where stopping a loop is\n# preferable to spending the full iteration budget.\ntool_loop_guardrails:\n warnings_enabled: true\n hard_stop_enabled: false\n warn_after:\n exact_failure: 2\n same_tool_failure: 3\n idempotent_no_progress: 2\n hard_stop_after:\n exact_failure: 5\n same_tool_failure: 8\n idempotent_no_progress: 5\n\n# =============================================================================\n# Context Compression (Auto-shrinks long conversations)\n# =============================================================================\n# When conversation approaches model's context limit, middle turns are\n# automatically summarized to free up space while preserving important context.\n#\n# HOW IT WORKS:\n# 1. Tracks actual token usage from API responses (not estimates)\n# 2. When prompt_tokens >= threshold% of model's context_length, triggers compression\n# 3. Protects first 3 turns (system prompt, initial request, first response)\n# 4. Protects last N turns (default 20 messages = ~10 full turns of recent context)\n# 5. Summarizes middle turns using a fast/cheap model\n# 6. Inserts summary as a user message, continues conversation seamlessly\n#\n# Post-compression tail budget is target_ratio × threshold × context_length:\n# 200K context, threshold 0.50, ratio 0.20 → 20K tokens of recent tail preserved\n# 1M context, threshold 0.50, ratio 0.20 → 100K tokens of recent tail preserved\n#\ncompression:\n # Enable automatic context compression (default: true)\n # Set to false if you prefer to manage context manually or want errors on overflow\n enabled: true\n \n # Trigger compression at this % of model's context limit (default: 0.50 = 50%)\n # Lower values = more aggressive compression, higher values = compress later\n # Models with context windows below 512K are floored at 0.75 (raise-only) so\n # compaction doesn't fire with half the window still free; set above 0.75 to override.\n threshold: 0.50\n\n # Per-model threshold overrides: keys are substring-matched against the model\n # name (longest match wins). Useful when some models need different compaction\n # points — e.g. a 1M-context model can compress later (0.30) while a 128K\n # model needs to compress earlier (0.60). The small-context floor (75% for\n # <512K models) still applies on top of per-model overrides.\n # model_thresholds:\n # \"glm-5.2\": 0.40\n # \"claude-sonnet\": 0.35\n # \"gpt-5\": 0.30\n\n # Optional absolute token cap for the compression trigger (default: null = disabled).\n # When set, compression fires at the LOWER of the ratio-based threshold and this\n # absolute token count — first-fires-wins. It never fires later than this count\n # regardless of which model is active (useful when switching between models with\n # very different context windows). Clamped to the model's context length at\n # apply-time, so a cap above the window is a no-op (ratio-based threshold wins).\n # Survives model switches and fallback activations.\n # threshold_tokens: 200000\n\n # Existing Codex gpt-5.5 behavior: raise Hermes' compaction trigger to 85%\n # for the ChatGPT Codex OAuth route. Set false to opt back down to threshold.\n codex_gpt55_autoraise: true\n\n # Fraction of the threshold to preserve as recent tail (default: 0.20 = 20%)\n # e.g. 20% of 50% threshold = 10% of total context kept as recent messages.\n # Summary output is separately capped at 12K tokens (Gemini output limit).\n # Range: 0.10 - 0.80\n target_ratio: 0.20\n\n # Number of most-recent messages to always preserve (default: 20 ≈ 10 full turns)\n # Higher values keep more recent conversation intact at the cost of more aggressive\n # compression of older turns.\n protect_last_n: 20\n\n # Compression retry rounds before a turn gives up with \"max compression\n # attempts reached\" (default: 3, same as the previous hardcoded value).\n # Raise (e.g. 6) for tool-schema-heavy sessions where 3 rounds cannot bring\n # the request estimate under the threshold. Validated >= 1, hard cap 10.\n max_attempts: 3\n\n # Codex app-server (codex CLI runtime) thread-compaction mode. The codex\n # agent owns the real thread context on this runtime, so Hermes' summarizer\n # cannot shrink it — compaction goes through the app server instead.\n # native = let Codex decide when to compact its own thread (default)\n # hermes = let Hermes threshold trigger Codex thread/compact/start\n # off = Hermes will not auto-trigger compaction; Codex may still compact natively\n codex_app_server_auto: native\n\n # Number of non-system messages to protect at the head of the transcript, in\n # ADDITION to the system prompt (which is always implicitly protected).\n # Head messages are NEVER summarized — they survive every compression\n # indefinitely. This gives stable early context for short/medium sessions,\n # but in long-running sessions that rely on rolling compaction the pinned\n # opening turns may not match how you want the session framed over time.\n # Set to 0 to preserve ONLY the system prompt (plus the rolling summary\n # and recent tail) — the cleanest configuration for long-running sessions.\n # Default 3 preserves the system prompt plus the first three non-system\n # head messages, matching the pre-feature behaviour.\n protect_first_n: 3\n\n # To pin a specific model/provider for compression summaries, use the\n # auxiliary section below (auxiliary.compression.provider / model).\n\n# =============================================================================\n# Anthropic prompt caching TTL\n# =============================================================================\n# When prompt caching is active (Claude via OpenRouter or native Anthropic),\n# Anthropic supports two TTL tiers for cached prefixes: \"5m\" (default) and\n# \"1h\". Other values are ignored and \"5m\" is used.\n#\nprompt_caching:\n cache_ttl: \"5m\" # use \"1h\" for long sessions with pauses between turns\n\n# =============================================================================\n# Auxiliary Models (Advanced — Experimental)\n# =============================================================================\n# Hermes uses lightweight \"auxiliary\" models for side tasks: image analysis,\n# browser screenshot analysis, web page summarization, TTS audio-tag insertion,\n# and context compression.\n#\n# By default these use Gemini Flash via OpenRouter or Nous Portal and are\n# auto-detected from your credentials. You do NOT need to change anything\n# here for normal usage.\n#\n# WARNING: Overriding these with providers other than OpenRouter or Nous Portal\n# is EXPERIMENTAL and may not work. Not all models/providers support vision,\n# produce usable summaries, or accept the same API format. Change at your own\n# risk — if things break, reset to \"auto\" / empty values.\n#\n# Each task has its own provider + model pair so you can mix providers.\n# For example: OpenRouter for vision (needs multimodal), but your main\n# local endpoint for compression (just needs text).\n#\n# Provider options:\n# \"auto\" - Best available: OpenRouter → Nous Portal → main endpoint (default)\n# \"openrouter\" - Force OpenRouter (requires OPENROUTER_API_KEY)\n# \"nous\" - Force Nous Portal (requires: hermes auth add nous)\n# \"gemini\" - Force Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n# \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY)\n# \"codex\" - Force Codex OAuth (requires: hermes model → Codex).\n# Uses gpt-5.3-codex which supports vision.\n# \"main\" - Use your custom endpoint (OPENAI_BASE_URL + OPENAI_API_KEY).\n# Works with OpenAI API, local models, or any OpenAI-compatible\n# endpoint. Also falls back to Codex OAuth and API-key providers.\n#\n# Model: leave empty to use the provider's default. When empty, OpenRouter\n# uses \"google/gemini-3-flash-preview\" and Nous uses \"gemini-3-flash\".\n# Other providers pick a sensible default automatically.\n#\n# auxiliary:\n# # Image analysis: vision_analyze tool + browser screenshots\n# vision:\n# provider: \"auto\"\n# model: \"\" # e.g. \"google/gemini-2.5-flash\", \"openai/gpt-4o\"\n# timeout: 30 # LLM API call timeout (seconds)\n# download_timeout: 30 # Image HTTP download timeout (seconds)\n# # Increase for slow connections or self-hosted image servers\n# reasoning_effort: \"\" # Per-task thinking level: none, minimal, low, medium,\n# # high, xhigh, max, ultra. Empty = provider default.\n# # Works on every auxiliary task block (vision,\n# # web_extract, compression, title_generation, curator,\n# # background_review, moa_reference, ...). Example: run\n# # compression at \"low\" and vision at \"none\" to cut\n# # side-task latency/cost on reasoning models.\n#\n# # Web page scraping / summarization + browser page text extraction\n# web_extract:\n# provider: \"auto\"\n# model: \"\"\n# reasoning_effort: \"low\"\n#\n# # Gemini 3.1 TTS hidden audio-tag insertion\n# tts_audio_tags:\n# provider: \"auto\" # empty model = your main chat model\n# model: \"\"\n# timeout: 30\n#\n# # Automatic session title generation after the first exchange\n# title_generation:\n# enabled: true # set false to disable auto-title generation\n# provider: \"auto\"\n# model: \"\"\n# timeout: 30\n# language: \"\" # empty = match the user's language; or e.g. \"English\"\n#\n# # Session search — summarizes matching past sessions\n# session_search:\n# provider: \"auto\"\n# model: \"\"\n# timeout: 30\n# max_concurrency: 3 # Limit parallel summaries to reduce request-burst 429s\n# extra_body: {} # Provider-specific OpenAI-compatible request fields\n# # Example for providers that support request-body\n# # reasoning controls:\n# # extra_body:\n# # enable_thinking: false\n# # Some vLLM/Qwen deployments expect this nested:\n# # extra_body:\n# # chat_template_kwargs:\n# # enable_thinking: false\n\n# =============================================================================\n# Persistent Memory\n# =============================================================================\n# Bounded curated memory injected into the system prompt every session.\n# Two stores: MEMORY.md (agent's notes) and USER.md (user profile).\n# Character limits keep the memory small and focused. The agent manages\n# pruning -- when at the limit, it must consolidate or replace entries.\n# Disabled by default in batch_runner.\n#\nmemory:\n # Agent's personal notes: environment facts, conventions, things learned\n memory_enabled: true\n \n # User profile: preferences, communication style, expectations\n user_profile_enabled: true\n \n # Character limits (~2.75 chars per token, model-independent)\n memory_char_limit: 2200 # ~800 tokens\n user_char_limit: 1375 # ~500 tokens\n\n # Periodic memory nudge: remind the agent to consider saving memories\n # every N user turns. Set to 0 to disable. Only active when memory is enabled.\n nudge_interval: 10 # Nudge every 10 user turns (0 = disabled)\n\n # Memory flush: give the agent one turn to save memories before context is\n # lost (compression, /new, /reset, exit). Set to 0 to disable.\n # For exit/reset, only fires if the session had at least this many user turns.\n flush_min_turns: 6 # Min user turns to trigger flush on exit/reset (0 = disabled)\n\n# =============================================================================\n# Session Reset Policy (Messaging Platforms)\n# =============================================================================\n# Controls when messaging sessions (Telegram, Discord, WhatsApp, Slack) are\n# automatically cleared. Default is \"none\": sessions never auto-reset —\n# conversation context lives until you /reset or /new manually, or context\n# compression kicks in. Opt in to automatic resets if you prefer sessions to\n# clear on a schedule (long-lived context increases API cost per message,\n# though prompt caching and compression keep this manageable).\n#\n# When an automatic reset triggers, the agent first saves important\n# information to its persistent memory — but the conversation context is\n# wiped. The agent starts fresh but retains learned facts via its memory\n# system.\n#\n# Modes:\n# \"none\" - Never auto-reset (default); context lives until /reset or compression\n# \"idle\" - Reset after N minutes of inactivity\n# \"daily\" - Reset at a fixed hour each day\n# \"both\" - Reset on EITHER inactivity timeout or daily boundary\n#\nsession_reset:\n mode: none # \"none\", \"idle\", \"daily\", or \"both\"\n idle_minutes: 1440 # Inactivity timeout in minutes (used by \"idle\"/\"both\")\n at_hour: 4 # Daily reset hour, 0-23 local time (used by \"daily\"/\"both\")\n\n# Maximum number of simultaneously active chat sessions across CLI, TUI,\n# dashboard chat, and messaging gateway. Set to null, 0, or omit to allow\n# unlimited concurrent sessions. When the limit is reached, new sessions get a\n# clean error while existing active sessions keep their normal behavior. This\n# top-level key takes precedence over gateway.max_concurrent_sessions. The cap\n# is a best-effort single-host/profile runtime guard; Hermes fails open if the\n# local runtime lease registry cannot be read or locked.\nmax_concurrent_sessions: null\n\n# When true, group/channel chats use one session per participant when the platform\n# provides a user ID. This is the secure default and prevents users in the same\n# room from sharing context, interrupts, and token costs. Set false only if you\n# explicitly want one shared \"room brain\" per group/channel.\ngroup_sessions_per_user: true\n\n# ─────────────────────────────────────────────────────────────────────────────\n# API Server — per-client model routing\n# ─────────────────────────────────────────────────────────────────────────────\n# Route different API clients to different models/providers on a single\n# Hermes deployment. Clients choose a backend by sending a specific string\n# as the OpenAI ``model`` field. Unmapped model values fall back to the\n# global model configured in the ``model:`` section above, and an explicit\n# session /model override always wins over a route.\n#\n# Configure via the ``platforms.api_server.extra.model_routes`` gateway\n# config block:\n#\n# platforms:\n# api_server:\n# enabled: true\n# extra:\n# key: \"your-api-server-secret\"\n# model_routes:\n# # Xiaozhi clients send model=\"minimax-m2\" → routed to MiniMax via OpenRouter\n# minimax-m2:\n# model: \"minimax/minimax-m1\"\n# provider: \"openrouter\" # optional — overrides global provider\n# # api_key: \"sk-...\" # optional — per-route UPSTREAM provider\n# # key (NOT caller auth; never logged)\n# # base_url: \"https://...\" # optional — per-route base URL\n# # GPT clients keep their own alias\n# gpt-5:\n# model: \"openai/gpt-5\"\n# provider: \"openrouter\"\n#\n# Configured aliases are automatically listed by GET /v1/models so clients\n# can discover them without manual coordination. Caller authentication is\n# unchanged: every request still authenticates with the global API server\n# key (``extra.key`` / API_SERVER_KEY).\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Gateway Streaming\n# ─────────────────────────────────────────────────────────────────────────────\n# Stream tokens to messaging platforms in real-time. The bot sends a message\n# on first token, then progressively edits it as more tokens arrive.\n# Disabled by default — enable to try the streaming UX on Telegram/Discord/Slack.\n# For Telegram, partial edits are sent as plain text and only the final edit uses MarkdownV2.\nstreaming:\n enabled: false\n # transport: edit # \"edit\" = progressive editMessageText\n # edit_interval: 0.3 # seconds between message edits\n # buffer_threshold: 40 # chars before forcing an edit flush\n # cursor: \" ▉\" # cursor shown during streaming\n\n# =============================================================================\n# Skills Configuration\n# =============================================================================\n# Skills are reusable procedures the agent can load and follow. The agent can\n# also create new skills after completing complex tasks.\n#\nskills:\n # Nudge the agent to create skills after complex tasks.\n # Every N tool-calling iterations, remind the model to consider saving a skill.\n # Set to 0 to disable.\n creation_nudge_interval: 15\n\n # External skill directories — share skills across tools/agents without\n # copying them into ~/.hermes/skills/. Each path is expanded (~ and ${VAR})\n # and resolved to an absolute path. External dirs are read-only: skill\n # creation always writes to ~/.hermes/skills/. Local skills take precedence\n # when names collide.\n # external_dirs:\n # - ~/.agents/skills\n # - /home/shared/team-skills\n\n# =============================================================================\n# Agent Behavior\n# =============================================================================\nagent:\n # Maximum tool-calling iterations per conversation\n # Higher = more room for complex tasks, but costs more tokens\n # Recommended: 20-30 for focused tasks, 50-100 for open exploration\n max_turns: 60\n\n # Inactivity timeout for gateway agent runs (seconds, 0 = unlimited).\n # The agent can run indefinitely when actively calling tools or receiving\n # API responses. Only fires after the agent has been idle for this duration.\n # gateway_timeout: 1800\n\n # Staged warning: send a warning before escalating to full timeout.\n # Fires once per run when inactivity reaches this threshold (seconds).\n # Set to 0 to disable the warning.\n # gateway_timeout_warning: 900\n\n # Graceful drain timeout for gateway stop/restart (seconds).\n # Default 0 = no drain: a restart interrupts in-flight agents immediately,\n # cleans up, and exits. Set a positive value only if you want a grace\n # window on /restart, and keep it well under systemd's TimeoutStopSec.\n # restart_drain_timeout: 0\n\n # Max app-level retry attempts for API errors (connection drops, provider\n # timeouts, 5xx, etc.) before the agent surfaces the failure. Lower this\n # to 1 if you use fallback providers and want fast failover on flaky\n # primaries (default 3). The OpenAI SDK does its own low-level retries\n # underneath this wrapper — this is the Hermes-level loop.\n # api_max_retries: 3\n\n # After the agent edits code without fresh passing verification, nudge it to\n # verify before finishing. The default \"auto\" enables it on interactive\n # coding surfaces (CLI, TUI, desktop) and programmatic callers, and disables\n # it on conversational messaging surfaces (Telegram, Discord, etc.) where the\n # verification summary would reach a human as chat noise. Set true or false to\n # force it on or off; the HERMES_VERIFY_ON_STOP env var (1/0) takes precedence.\n # verify_on_stop: auto\n\n # Standing operator instructions for the coding posture (when Hermes is in a\n # code workspace). Appended to the coding brief as an extra system block, so\n # you can pin project-wide workflow rules without editing the shipped brief.\n # Accepts a string or a list of strings. Takes effect next session.\n # coding_instructions:\n # - \"For UI work, don't run tsc/lint until I approve the look.\"\n # - \"Clean the diff before you commit and push.\"\n\n # When verify-on-stop finds edited code without fresh verification evidence,\n # append guidance for creative UI work (avoid broad tsc/lint/test before visual\n # approval) and clean-diff expectations. Set false to keep that nudge terse.\n # verify_guidance: true\n\n # A `pre_verify` hook (plugin or shell, see Event Hooks docs) can keep the\n # agent going one more turn to verify/clean before finishing. This caps how\n # many times one turn may be nudged to continue, so a hook can't trap the loop.\n # Default 3.\n # max_verify_nudges: 3\n\n # Enable verbose logging\n verbose: false\n \n # Reasoning effort level (OpenRouter and Nous Portal)\n # Controls how much \"thinking\" the model does before responding.\n # Options: \"xhigh\" (max), \"high\", \"medium\", \"low\", \"minimal\", \"none\" (disable)\n reasoning_effort: \"medium\"\n \n # Per-model reasoning effort overrides (optional dict)\n # Key: any sensible model spelling works (exact, dots↔dashes interchangeable,\n # provider prefix optional). First match wins.\n # Value: reasoning effort level (same options as reasoning_effort)\n # Override the global reasoning_effort for that specific model.\n # NOTE: no `hermes config set` support for this key -- edit YAML directly.\n # reasoning_overrides:\n # \"openrouter/anthropic/claude-opus-4.5\": \"xhigh\"\n # \"openai/gpt-5\": \"low\"\n # \"claude-opus-4.6\": \"high\" # bare model name also works\n # \"deepseek/deepseek-v4-pro\": \"xhigh\" # dots and dashes are interchangeable\n reasoning_overrides: {}\n \n # Predefined personalities (use with /personality command)\n personalities:\n helpful: \"You are a helpful, friendly AI assistant.\"\n concise: \"You are a concise assistant. Keep responses brief and to the point.\"\n technical: \"You are a technical expert. Provide detailed, accurate technical information.\"\n creative: \"You are a creative assistant. Think outside the box and offer innovative solutions.\"\n teacher: \"You are a patient teacher. Explain concepts clearly with examples.\"\n kawaii: \"You are a kawaii assistant! Use cute expressions like (◕‿◕), ★, ♪, and ~! Add sparkles and be super enthusiastic about everything! Every response should feel warm and adorable desu~! ヽ(>∀<☆)ノ\"\n catgirl: \"You are Neko-chan, an anime catgirl AI assistant, nya~! Add 'nya' and cat-like expressions to your speech. Use kaomoji like (=^・ω・^=) and ฅ^•ﻌ•^ฅ. Be playful and curious like a cat, nya~!\"\n pirate: \"Arrr! Ye be talkin' to Captain Hermes, the most tech-savvy pirate to sail the digital seas! Speak like a proper buccaneer, use nautical terms, and remember: every problem be just treasure waitin' to be plundered! Yo ho ho!\"\n shakespeare: \"Hark! Thou speakest with an assistant most versed in the bardic arts. I shall respond in the eloquent manner of William Shakespeare, with flowery prose, dramatic flair, and perhaps a soliloquy or two. What light through yonder terminal breaks?\"\n surfer: \"Duuude! You're chatting with the chillest AI on the web, bro! Everything's gonna be totally rad. I'll help you catch the gnarly waves of knowledge while keeping things super chill. Cowabunga! 🤙\"\n noir: \"The rain hammered against the terminal like regrets on a guilty conscience. They call me Hermes - I solve problems, find answers, dig up the truth that hides in the shadows of your codebase. In this city of silicon and secrets, everyone's got something to hide. What's your story, pal?\"\n uwu: \"hewwo! i'm your fwiendwy assistant uwu~ i wiww twy my best to hewp you! *nuzzles your code* OwO what's this? wet me take a wook! i pwomise to be vewy hewpful >w<\"\n philosopher: \"Greetings, seeker of wisdom. I am an assistant who contemplates the deeper meaning behind every query. Let us examine not just the 'how' but the 'why' of your questions. Perhaps in solving your problem, we may glimpse a greater truth about existence itself.\"\n hype: \"YOOO LET'S GOOOO!!! 🔥🔥🔥 I am SO PUMPED to help you today! Every question is AMAZING and we're gonna CRUSH IT together! This is gonna be LEGENDARY! ARE YOU READY?! LET'S DO THIS! 💪😤🚀\"\n\n# =============================================================================\n# Toolsets\n# =============================================================================\n# Control which tools the agent has access to.\n# Use `hermes tools` to interactively enable/disable tools per platform.\n\n# =============================================================================\n# Platform Toolsets (per-platform tool configuration)\n# =============================================================================\n# Override which toolsets are available on each platform.\n# If a platform isn't listed here, its built-in default is used.\n#\n# You can use EITHER:\n# - A preset like \"hermes-cli\" or \"hermes-telegram\" (curated tool set)\n# - A list of individual toolsets to compose your own (see list below)\n#\n# Supported platform keys: cli, telegram, discord, whatsapp, slack, qqbot, teams, google_chat\n#\n# Examples:\n#\n# # Use presets (same as defaults):\n# platform_toolsets:\n# cli: [hermes-cli]\n# telegram: [hermes-telegram]\n#\n# # Custom: give Telegram only web + terminal + file + planning:\n# platform_toolsets:\n# telegram: [web, terminal, file, todo]\n#\n# # Custom: CLI without browser or image gen:\n# platform_toolsets:\n# cli: [web, terminal, file, skills, todo, tts, cronjob]\n#\n# # Restrictive: Discord gets read-only tools only:\n# platform_toolsets:\n# discord: [web, vision, skills, todo]\n#\n# If not set, defaults are:\n# cli: hermes-cli (everything + cronjob management)\n# telegram: hermes-telegram (terminal, file, web, vision, image, tts, browser, skills, todo, cronjob, messaging)\n# discord: hermes-discord (same as telegram)\n# whatsapp: hermes-whatsapp (same as telegram)\n# slack: hermes-slack (same as telegram)\n# signal: hermes-signal (same as telegram)\n# homeassistant: hermes-homeassistant (same as telegram)\n# qqbot: hermes-qqbot (same as telegram)\n# teams: hermes-teams (same as telegram)\n# google_chat: hermes-google_chat (same as telegram)\n#\nplatform_toolsets:\n cli: [hermes-cli]\n telegram: [hermes-telegram]\n discord: [hermes-discord]\n whatsapp: [hermes-whatsapp]\n slack: [hermes-slack]\n signal: [hermes-signal]\n homeassistant: [hermes-homeassistant]\n qqbot: [hermes-qqbot]\n yuanbao: [hermes-yuanbao]\n teams: [hermes-teams]\n google_chat: [hermes-google_chat]\n\n# =============================================================================\n# Gateway Platform Settings\n# =============================================================================\n# Optional per-platform messaging settings.\n# Platform-specific knobs live under `extra`.\n#\n# platforms:\n# telegram:\n# reply_to_mode: \"first\" # off | first | all\n# # guest_mode lets explicit @mentions from non-allowlisted groups through.\n# # Default false; ordinary messages, replies, and regex wake words stay blocked.\n# guest_mode: false\n# # allowed_chats: [\"-1001234567890\"]\n# extra:\n# disable_link_previews: false # Set true to suppress Telegram URL previews in bot messages\n# 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\n# rich_drafts: false # Experimental rich draft previews during Telegram DM streaming; default false because Telegram Desktop/macOS can visually overlay draft frames\n# command_menu:\n# # Telegram allows up to 100 BotCommands; Hermes defaults to 60 so\n# # all built-in commands plus common skill commands stay visible\n# # while remaining under Telegram's payload-size limit. Clamped 1..100.\n# max_commands: 60\n# # prepend = user priority first, then Hermes defaults\n# # append = Hermes defaults first, then user priority\n# # replace = only the list below defines priority\n# priority_mode: prepend\n# priority:\n# - my_plugin_command\n# webhook:\n# extra:\n# # Route scripts default to a 30 second timeout. Scripts must live under\n# # the active profile's scripts directory and receive webhook JSON on stdin.\n# script_timeout_seconds: 30\n#\n# Discord-specific settings (config.yaml top-level, not under platforms:):\n#\n# discord:\n# require_mention: true # Require @mention in server channels (default: true)\n# auto_thread: true # Auto-create thread on @mention (default: true)\n# free_response_channels: \"\" # Channel IDs where no mention is needed\n# reactions: true # Show processing reactions (default: true)\n# history_backfill: true # Recover missed channel messages on mention (default: true)\n# history_backfill_limit: 50 # Max messages to scan backwards (default: 50)\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Available toolsets (use these names in platform_toolsets or the toolsets list)\n#\n# Run `hermes chat --list-toolsets` to see all toolsets and their tools.\n# Run `hermes chat --list-tools` to see every individual tool with descriptions.\n# ─────────────────────────────────────────────────────────────────────────────\n#\n# INDIVIDUAL TOOLSETS (compose your own):\n# web - web_search, web_extract\n# search - web_search only (no scraping)\n# terminal - terminal, process\n# file - read_file, write_file, patch, search\n# browser - browser_navigate, browser_snapshot, browser_click, browser_type,\n# browser_scroll, browser_back, browser_press,\n# browser_get_images, browser_vision (requires BROWSERBASE_API_KEY)\n# vision - vision_analyze (requires OPENROUTER_API_KEY)\n# image_gen - image_generate (requires FAL_KEY)\n# skills - skills_list, skill_view\n# skills_hub - skill_hub (search/install/manage from online registries — user-driven only)\n# todo - todo (in-memory task planning, no deps)\n# tts - text_to_speech (Edge TTS free, or ELEVENLABS/OPENAI/MINIMAX/MISTRAL key)\n# cronjob - cronjob (create/list/update/pause/resume/run/remove scheduled tasks)\n#\n# PRESETS (curated bundles):\n# hermes-cli - All of the above except send_message\n# hermes-telegram - terminal, file, web, vision, image_gen, tts, browser,\n# skills, todo, cronjob, send_message\n# hermes-discord - Same as hermes-telegram\n# hermes-whatsapp - Same as hermes-telegram\n# hermes-slack - Same as hermes-telegram\n#\n# COMPOSITE:\n# debugging - terminal + web + file\n# safe - web + vision (no terminal access)\n# all - Everything available\n#\n# web - Web search and content extraction (web_search, web_extract)\n# search - Web search only, no scraping (web_search)\n# terminal - Command execution and process management (terminal, process)\n# file - File operations: read, write, patch, search\n# browser - Full browser automation (navigate, click, type, screenshot, etc.)\n# vision - Image analysis (vision_analyze)\n# image_gen - Image generation with FLUX (image_generate)\n# skills - Load skill documents (skills_list, skill_view)\n# todo - Task planning and tracking for multi-step work\n# memory - Persistent memory across sessions (personal notes + user profile)\n# session_search - Search and recall past conversations (FTS5 + Gemini Flash summarization)\n# tts - Text-to-speech (Edge TTS free, ElevenLabs, OpenAI, MiniMax, Mistral)\n# cronjob - Schedule and manage automated tasks (CLI-only)\n#\n# Composite toolsets:\n# debugging - terminal + web + file (for troubleshooting)\n# safe - web + vision (no terminal access)\n\n# NOTE: The top-level \"toolsets\" key is deprecated and ignored.\n# Tool configuration is managed per-platform via platform_toolsets above.\n# Use `hermes tools` to configure interactively, or edit platform_toolsets directly.\n#\n# CLI override: hermes chat --toolsets terminal,web,file\n\n# =============================================================================\n# MCP (Model Context Protocol) Servers\n# =============================================================================\n# Connect to external MCP servers to add tools from the MCP ecosystem.\n# Each server's tools are automatically discovered and registered.\n# See website/docs/user-guide/features/mcp.md for full documentation.\n#\n# Stdio servers (spawn a subprocess):\n# command: the executable to run\n# args: command-line arguments\n# env: environment variables (only these + safe defaults passed to subprocess)\n#\n# HTTP servers (connect to a URL):\n# url: the MCP server endpoint\n# headers: HTTP headers (e.g., for authentication)\n#\n# Optional per-server settings:\n# timeout: tool call timeout in seconds (default: 120)\n# connect_timeout: initial connection timeout (default: 60)\n# keepalive_interval: liveness ping cadence in seconds (default: 180).\n# Lower it below the server's session TTL for servers that expire idle\n# sessions quickly (e.g. Unreal Engine editor MCP, ~15s), otherwise idle\n# tool calls hit an expired session and pay a slow reconnect. Floored at 5s.\n#\n# mcp_servers:\n# time:\n# command: uvx\n# args: [\"mcp-server-time\"]\n# filesystem:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-filesystem\", \"/home/user\"]\n# notion:\n# url: https://mcp.notion.com/mcp\n# github:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-github\"]\n# env:\n# GITHUB_PERSONAL_ACCESS_TOKEN: \"ghp_...\"\n#\n# Sampling (server-initiated LLM requests) — enabled by default.\n# Per-server config under the 'sampling' key:\n# analysis:\n# command: npx\n# args: [\"-y\", \"analysis-server\"]\n# sampling:\n# enabled: true # default: true\n# model: \"gemini-3-flash\" # override model (optional)\n# max_tokens_cap: 4096 # max tokens per request\n# timeout: 30 # LLM call timeout (seconds)\n# max_rpm: 10 # max requests per minute\n# allowed_models: [] # model whitelist (empty = all)\n# max_tool_rounds: 5 # tool loop limit (0 = disable)\n# log_level: \"info\" # audit verbosity\n\n# =============================================================================\n# Text-to-Speech\n# =============================================================================\n# TTS defaults to Edge TTS unless changed in ~/.hermes/config.yaml.\n# Gemini TTS supports persona/director prompt files, and Gemini 3.1 Flash TTS\n# can use a hidden auxiliary rewrite pass to insert expressive square-bracket\n# audio tags into the TTS script without showing tags in chat.\n#\n# tts:\n# provider: \"gemini\"\n# gemini:\n# model: \"gemini-3.1-flash-tts-preview\"\n# voice: \"Kore\"\n# audio_tags: false\n# persona_prompt_file: \"\" # e.g. ~/.hermes/tts/radio-host.md\n\n# =============================================================================\n# Voice Transcription (Speech-to-Text)\n# =============================================================================\n# Automatically transcribe voice messages on messaging platforms.\n# Providers: local (free, faster-whisper) | groq (free tier) | openai (Whisper API) | mistral (Voxtral Transcribe)\n# Set the corresponding API key in .env: GROQ_API_KEY, OPENAI_API_KEY, or MISTRAL_API_KEY.\nstt:\n enabled: true\n # provider: \"local\" # auto-detected if omitted\n local:\n model: \"base\" # tiny | base | small | medium | large-v3 | turbo\n # language: \"\" # auto-detect; set to \"en\", \"es\", \"fr\", etc. to force\n openai:\n model: \"whisper-1\" # whisper-1 | gpt-4o-mini-transcribe | gpt-4o-transcribe\n # mistral:\n # model: \"voxtral-mini-latest\" # voxtral-mini-latest | voxtral-mini-2602\n # deepinfra:\n # # Model id is discovered live from the DeepInfra catalog filtered\n # # by the `stt` surface tag — leave `model` blank to take the first\n # # live result. Pin only when you need a specific Whisper variant.\n # model: \"\"\n\n# Text-to-speech. Only the deepinfra block is documented here — the\n# remaining providers (edge, openai, xai, minimax, mistral, gemini,\n# elevenlabs, neutts, kittentts, piper) inherit sensible defaults from\n# DEFAULT_CONFIG in hermes_cli/config.py.\n# tts:\n# provider: \"deepinfra\"\n# deepinfra:\n# # Model id is discovered live from the DeepInfra catalog filtered\n# # by the `tts` surface tag — leave `model` blank to take the first\n# # live result.\n# model: \"\"\n# voice: \"default\"\n\n# Image generation. Each provider plugin reads its own ``image_gen.<name>``\n# block; deepinfra discovers models live from\n# api.deepinfra.com/v1/openai/models filtered by the ``image-gen`` tag —\n# no model id is hardcoded, so retired models disappear automatically.\n# image_gen:\n# provider: \"deepinfra\"\n# deepinfra:\n# # Leave `model` blank for the first live `image-gen`-tagged result.\n# model: \"\"\n\n# =============================================================================\n# Response Pacing (Messaging Platforms)\n# =============================================================================\n# Add human-like delays between message chunks.\n# human_delay:\n# mode: \"off\" # \"off\" | \"natural\" | \"custom\"\n# min_ms: 800 # Min delay (custom mode only)\n# max_ms: 2500 # Max delay (custom mode only)\n\n# =============================================================================\n# Session Logging\n# =============================================================================\n# Session trajectories are automatically saved to logs/ directory.\n# Each session creates: logs/session_YYYYMMDD_HHMMSS_UUID.json\n#\n# The session ID is displayed in the welcome banner for easy reference.\n# Logs contain full conversation history in trajectory format:\n# - System prompt, user messages, assistant responses\n# - Tool calls with inputs/outputs\n# - Timestamps for debugging\n#\n# No configuration needed - logging is always enabled.\n# To disable, you would need to modify the source code.\n\n# =============================================================================\n# Code Execution Sandbox (Programmatic Tool Calling)\n# =============================================================================\n# The execute_code tool runs Python scripts that call Hermes tools via RPC.\n# Intermediate tool results stay out of the LLM's context window.\ncode_execution:\n timeout: 300 # Max seconds per script before kill (default: 300 = 5 min)\n max_tool_calls: 50 # Max RPC tool calls per execution (default: 50)\n\n# =============================================================================\n# Subagent Delegation\n# =============================================================================\n# The delegate_task tool spawns child agents with isolated context.\n# Supports single tasks and batch mode (default 3 parallel, configurable).\ndelegation:\n max_iterations: 50 # Max tool-calling turns per child (default: 50)\n # max_concurrent_children: 3 # Max parallel child agents per batch (default: 3, floor: 1, no ceiling).\n # WARNING: values above 10 multiply API cost linearly.\n # max_spawn_depth: 1 # Delegation tree depth cap (range: 1-3, default: 1 = flat).\n # Raise to 2 to allow workers to spawn their own subagents.\n # Requires role=\"orchestrator\" on intermediate agents.\n # orchestrator_enabled: true # Kill switch for role=\"orchestrator\" children (default: true).\n # subagent_auto_approve: false # When a subagent hits a dangerous-command approval prompt, auto-deny (default: false)\n # or auto-approve \"once\" (true) instead of blocking on stdin.\n # The parent TUI owns stdin, so blocking would deadlock; non-interactive resolution is required.\n # Both choices emit a logger.warning audit line. Flip to true only for cron/batch pipelines.\n # inherit_mcp_toolsets: true # When explicit child toolsets are narrowed, also keep the parent's MCP toolsets (default: true). Set false for strict intersection.\n # model: \"google/gemini-3-flash-preview\" # Override model for subagents (empty = inherit parent)\n # provider: \"openrouter\" # Override provider for subagents (empty = inherit parent)\n # # Resolves full credentials (base_url, api_key) automatically.\n # # Supported: openrouter, nous, zai, kimi-coding, minimax\n\n# =============================================================================\n# Honcho Integration (Cross-Session User Modeling)\n# =============================================================================\n# AI-native persistent memory via Honcho (https://honcho.dev/).\n# Builds a deeper understanding of the user across sessions and tools.\n# Runs alongside USER.md — additive, not a replacement.\n#\n# Requires: pip install honcho-ai\n# Config: ~/.honcho/config.json (shared with Claude Code, Cursor, etc.)\n# API key: HONCHO_API_KEY in ~/.hermes/.env or ~/.honcho/config.json\n#\n# Hermes-specific overrides (optional — most config comes from ~/.honcho/config.json):\n# honcho: {}\n\n# =============================================================================\n# Display\n# =============================================================================\ndisplay:\n # Use compact banner mode (hides the ASCII-art banner, shows a single line).\n # true: Compact single-line banner\n # false: Full ASCII banner with tool/skill summary (default)\n compact: false\n\n # Tool progress display level (CLI and gateway)\n # off: Silent — no tool activity shown, just the final response\n # new: Show a tool indicator only when the tool changes (skip repeats)\n # all: Show every tool call with a short preview (default)\n # verbose: Full args, results, and debug logs (same as /verbose)\n # log: Silent in chat; append every tool call to ~/.hermes/logs/tool_calls.log (gateway only)\n # Toggle at runtime with /verbose in the CLI\n tool_progress: all\n\n # Per-platform defaults can be quieter than the global setting. Telegram\n # tunes for mobile: tool_progress and busy_ack_detail default off (no\n # per-tool breadcrumb stream, no \"iteration 21/60\" debug detail in busy\n # acks or heartbeats), but interim_assistant_messages and\n # long_running_notifications STAY ON so the user has real signal between\n # turn start and final answer (mid-turn assistant commentary + a single\n # edit-in-place \"⏳ Working — N min\" heartbeat). Override under\n # display.platforms.telegram.\n\n # Auto-cleanup of temporary progress bubbles after the final response lands.\n # On platforms that support message deletion (currently Telegram), this\n # removes the tool-progress bubble, \"⏳ Still working...\" notices, and\n # context-pressure status messages once the final reply has been delivered —\n # keeping long-running turns visible live, then tidy afterward. Failed runs\n # leave the bubbles in place as breadcrumbs. Off by default.\n # Per-platform override: display.platforms.telegram.cleanup_progress\n # true: Delete tracked progress/status bubbles on successful turn\n # false: Leave everything in place (default)\n # Example:\n # display:\n # platforms:\n # telegram:\n # cleanup_progress: true\n cleanup_progress: false\n\n # Natural mid-turn assistant updates.\n # On gateway platforms, when true, completed assistant status messages are\n # sent as separate chat messages. On the Desktop app, when true, mid-turn\n # assistant narration streamed between tool calls is kept in the transcript\n # instead of the bubble collapsing to only the final message on completion.\n # Independent of tool_progress and gateway streaming.\n # true: Keep/send mid-turn assistant updates (default)\n # false: Only keep/send the final response\n interim_assistant_messages: true\n\n # Gateway-only long-running status heartbeats.\n # When false, the platform does not receive periodic \"⏳ Working — N min\"\n # notifications even if agent.gateway_notify_interval is non-zero. The\n # heartbeat edits a single message in place (where the adapter supports\n # editing) instead of posting a new bubble each interval.\n # Default: true everywhere, including Telegram (silent agents are worse\n # than a single edit-in-place heartbeat).\n long_running_notifications: true\n\n # Include detailed iteration/tool/status context in busy acknowledgments\n # and long-running heartbeats. When true, busy acks show \"iteration 21/60,\n # terminal, 10 min\" and the heartbeat shows \"⏳ Working — 12 min,\n # iteration 21/60, terminal\". When false (Telegram default), both stay\n # terse: \"Interrupting current task\" and \"⏳ Working — 12 min, terminal\".\n busy_ack_detail: true\n\n # What Enter does when Hermes is already busy (CLI and gateway platforms).\n # interrupt: Interrupt the current run and redirect Hermes (default)\n # queue: Queue your message for the next turn\n # steer: Inject your message mid-run via /steer, arriving at the agent\n # after the next tool call — no interrupt, no role violation.\n # Falls back to 'queue' if the agent isn't running yet or if\n # images are attached (steer only carries text).\n # Ctrl+C (or /stop in gateway) always interrupts regardless of this setting.\n # Toggle at runtime with /busy <interrupt|queue|steer>.\n busy_input_mode: interrupt\n\n # Background process notifications (gateway/messaging only).\n # Controls how chatty the process watcher is when you use\n # terminal(background=true, notify_on_complete=true) from Telegram/Discord/etc.\n # off: No watcher messages at all\n # result: Only the final completion message\n # error: Only the final message when exit code != 0\n # all: Running output updates + final message (default)\n background_process_notifications: all\n\n\n # Play terminal bell when agent finishes a response.\n # Useful for long-running tasks — your terminal will ding when the agent is done.\n # Works over SSH. Most terminals can be configured to flash the taskbar or play a sound.\n # true: Ring the terminal bell on each response\n # false: Silent (default)\n bell_on_complete: false\n\n # Show model reasoning/thinking before each response.\n # When enabled, a dim box shows the model's thought process above the response.\n # Toggle at runtime with /reasoning show or /reasoning hide.\n # true: Show the reasoning box\n # false: Hide reasoning (default)\n show_reasoning: false\n\n # Stream tokens to the terminal as they arrive instead of waiting for the\n # full response. The response box opens on first token and text appears\n # line-by-line. Tool calls are still captured silently.\n # true: Stream tokens as they arrive (default)\n # false: Wait for the full response before rendering\n streaming: true\n\n # Show [HH:MM] timestamps on user input and assistant response labels.\n # timestamps: false\n\n # ───────────────────────────────────────────────────────────────────────────\n # Skin / Theme\n # ───────────────────────────────────────────────────────────────────────────\n # Customize CLI visual appearance — banner colors, spinner faces, tool prefix,\n # response box label, and branding text. Change at runtime with /skin <name>.\n #\n # Built-in skins:\n # default — Classic Hermes gold/kawaii\n # ares — Crimson/bronze war-god theme with spinner wings\n # mono — Clean grayscale monochrome\n # slate — Cool blue developer-focused\n # daylight — Bright light-mode theme\n # warm-lightmode — Warm paper-tone light-mode theme\n # poseidon — Sea-green/teal Olympian theme\n # sisyphus — Earthy stone-and-moss theme\n # charizard — Fiery orange dragon theme\n #\n # Custom skins: drop a YAML file in ~/.hermes/skins/<name>.yaml\n # Schema (all fields optional, missing values inherit from default):\n #\n # name: my-theme\n # description: Short description\n # colors:\n # banner_border: \"#HEX\" # Panel border\n # banner_title: \"#HEX\" # Panel title\n # banner_accent: \"#HEX\" # Section headers (Available Tools, etc.)\n # banner_dim: \"#HEX\" # Dim/muted text\n # banner_text: \"#HEX\" # Body text (tool names, skill names)\n # ui_accent: \"#HEX\" # UI accent color\n # response_border: \"#HEX\" # Response box border color\n # spinner:\n # waiting_faces: [\"(⚔)\", \"(⛨)\"] # Faces shown while waiting\n # thinking_faces: [\"(⚔)\", \"(⌁)\"] # Faces shown while thinking\n # thinking_verbs: [\"forging\", \"plotting\"] # Verbs for spinner messages\n # wings: # Optional left/right spinner decorations\n # - [\"⟪⚔\", \"⚔⟫\"]\n # - [\"⟪▲\", \"▲⟫\"]\n # branding:\n # agent_name: \"My Agent\" # Banner title and branding\n # welcome: \"Welcome message\" # Shown at CLI startup\n # response_label: \" ⚔ Agent \" # Response box header label\n # prompt_symbol: \"⚔\" # Prompt symbol (bare token; renderers add trailing space)\n # tool_prefix: \"╎\" # Tool output line prefix (default: ┊)\n #\n skin: default\n\n# =============================================================================\n# Model Aliases — short names for /model command\n# =============================================================================\n# Map short aliases to exact (model, provider, base_url) tuples.\n# Used by /model tab completion and resolve_alias().\n# Aliases are checked BEFORE the models.dev catalog, so they can route\n# to endpoints not in the catalog (e.g. Ollama Cloud, local servers).\n#\n# model_aliases:\n# opus:\n# model: claude-opus-4-6\n# provider: anthropic\n# qwen:\n# model: \"qwen3.5:397b\"\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n# glm:\n# model: glm-4.7\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n\n# =============================================================================\n# Privacy\n# =============================================================================\n# privacy:\n# # Redact PII from the LLM context prompt.\n# # When true, phone numbers are stripped and user/chat IDs are replaced\n# # with deterministic hashes before being sent to the model.\n# # Names and usernames are NOT affected (user-chosen, publicly visible).\n# # Routing/delivery still uses the original values internally.\n# redact_pii: false\n\n# =============================================================================\n# Shell-script hooks\n# =============================================================================\n# Register shell scripts as plugin-hook callbacks. Each entry is executed as\n# a subprocess (shell=False, shlex.split) with a JSON payload on stdin. On\n# stdout the script may return JSON that either blocks the tool call or\n# injects context into the next LLM call.\n#\n# Valid events (mirror hermes_cli.plugins.VALID_HOOKS):\n# pre_tool_call, post_tool_call, pre_llm_call, post_llm_call,\n# pre_api_request, post_api_request, on_session_start, on_session_end,\n# on_session_finalize, on_session_reset, subagent_stop\n#\n# First-use consent: each (event, command) pair prompts once on a TTY, then\n# is persisted to ~/.hermes/shell-hooks-allowlist.json. Non-interactive\n# runs (gateway, cron) need --accept-hooks, HERMES_ACCEPT_HOOKS=1, or the\n# hooks_auto_accept key below.\n#\n# See website/docs/user-guide/features/hooks.md for the full JSON wire\n# protocol and worked examples.\n#\n# hooks:\n# pre_tool_call:\n# - matcher: \"terminal\"\n# command: \"~/.hermes/agent-hooks/block-rm-rf.sh\"\n# timeout: 10\n# post_tool_call:\n# - matcher: \"write_file|patch\"\n# command: \"~/.hermes/agent-hooks/auto-format.sh\"\n# pre_llm_call:\n# - command: \"~/.hermes/agent-hooks/inject-cwd-context.sh\"\n# subagent_stop:\n# - command: \"~/.hermes/agent-hooks/log-orchestration.sh\"\n#\n# hooks_auto_accept: false\n\n\n# =============================================================================\n# Telemetry\n# =============================================================================\n# Shared metrics are disabled by default. When enabled, Hermes writes only\n# allowlisted aggregate counters and immutable JSON\n# packages under $HERMES_HOME/telemetry/shared_metrics; it does not upload them.\ntelemetry:\n shared_metrics:\n enabled: false\n\n\n# =============================================================================\n# Update Behavior\n# =============================================================================\nupdates:\n # Create a full HERMES_HOME zip before every `hermes update`.\n # Backups land in ~/.hermes/backups/ and can be restored with `hermes import`.\n # Off by default because large homes can add minutes to every update.\n pre_update_backup: false\n\n # Number of pre-update backup zips to retain.\n backup_keep: 5\n\n # What non-interactive updates do with local source edits in the Hermes repo.\n # Interactive terminal updates always prompt before restoring the autostash.\n #\n # stash - auto-stash before pull, then auto-restore after success (default)\n # discard - drop the update-created stash after success; use only on managed\n # installs where local source edits should not persist\n non_interactive_local_changes: \"stash\"\n\n\n# =============================================================================\n# Web Dashboard\n# =============================================================================\n# OAuth gate configuration for `hermes dashboard --host <non-loopback>`.\n# The bundled Nous Portal plugin reads these on startup; settings here are\n# the canonical surface. Each can be overridden by an environment variable:\n#\n# dashboard.oauth.client_id <- HERMES_DASHBOARD_OAUTH_CLIENT_ID\n# dashboard.oauth.portal_url <- HERMES_DASHBOARD_PORTAL_URL\n# dashboard.public_url <- HERMES_DASHBOARD_PUBLIC_URL\n#\n# Env wins when set to a non-empty value. This is what Fly.io's platform-\n# secret injection uses to push per-deploy client_ids without needing to\n# bake a config.yaml into the image. Empty env values are treated as unset\n# so a provisioned-but-not-populated secret can't shadow a valid entry here.\n#\n# Local dev / on-prem deploys should typically set these via config.yaml\n# (the ~/.hermes/.env file is reserved for API keys and secrets).\n#\n# dashboard:\n# oauth:\n# client_id: \"\" # agent:{instance_id}; Portal provisions this at deploy\n# portal_url: \"\" # blank → default https://portal.nousresearch.com\n#\n# # Force the absolute base URL the OAuth callback (and any other public\n# # URL the dashboard hands to external systems) is built from. Set this\n# # for deploys behind reverse proxies that don't reliably forward\n# # X-Forwarded-Host / X-Forwarded-Proto / X-Forwarded-Prefix (manual\n# # nginx setups, on-prem ingresses, custom-domain Fly deploys without\n# # full proxy header chains).\n# #\n# # When set, the value is the complete authority: scheme + host +\n# # optional path prefix (e.g. \"https://example.com/hermes\"). The OAuth\n# # callback URL becomes \"<public_url>/auth/callback\" — X-Forwarded-Prefix\n# # is IGNORED on this code path because the operator has explicitly\n# # declared the public URL and we no longer need to guess.\n# #\n# # Leave empty to use the existing proxy-header reconstruction (the\n# # default — works on Fly.io out of the box).\n# #\n# # public_url: \"https://example.com/hermes\"\n#\n# -----------------------------------------------------------------------------\n# Self-hosted OIDC dashboard auth (generic OpenID Connect — Authentik,\n# Keycloak, Zitadel, Authelia, Auth0, Okta, Google, …). Use this INSTEAD of the\n# nous block above when gating the dashboard with your own identity provider.\n# Each setting can be overridden by an environment variable:\n#\n# dashboard.oauth.self_hosted.issuer <- HERMES_DASHBOARD_OIDC_ISSUER\n# dashboard.oauth.self_hosted.client_id <- HERMES_DASHBOARD_OIDC_CLIENT_ID\n# dashboard.oauth.self_hosted.scopes <- HERMES_DASHBOARD_OIDC_SCOPES\n# dashboard.oauth.self_hosted.client_secret <- HERMES_DASHBOARD_OIDC_CLIENT_SECRET\n#\n# dashboard:\n# oauth:\n# provider: self-hosted\n# self_hosted:\n# issuer: \"https://auth.example.com/application/o/hermes/\" # required\n# client_id: \"hermes-dashboard\" # required\n# scopes: \"openid profile email\" # optional\n#\n# # OPTIONAL — set ONLY if your IDP registered the client as\n# # *confidential* (Authentik / Keycloak often default to this). When\n# # set, Hermes authenticates the client at the token endpoint\n# # (client_secret_basic or client_secret_post, auto-selected from the\n# # IDP's discovery doc) IN ADDITION to PKCE. Leave unset for a public\n# # (PKCE-only) client — the common case.\n# #\n# # This is a CREDENTIAL: prefer setting HERMES_DASHBOARD_OIDC_CLIENT_SECRET\n# # in ~/.hermes/.env over putting it here in config.yaml.\n# # client_secret: \"\"\n\n# =============================================================================\n# External secret sources\n# =============================================================================\n# Pull provider credentials from external secret managers at process startup\n# instead of storing them in ~/.hermes/.env. Only the manager's bootstrap\n# credential (e.g. BWS_ACCESS_TOKEN / OP_SERVICE_ACCOUNT_TOKEN) lives in .env\n# (or your shell / desktop session); everything else rotates centrally.\n# Failures never block startup — Hermes warns once and continues with\n# whatever .env already had.\n#\n# Multiple sources can be enabled at once:\n# - \"mapped\" sources (explicit VAR -> ref bindings, e.g. 1Password's env:\n# map) beat \"bulk\" sources (whole-project dumps like Bitwarden BSM)\n# - within a shape, the first source to claim a var wins; later claims\n# are skipped with a startup warning (never a silent clobber)\n# - a source's override_existing lets it beat .env/shell values, but\n# never another secret source's claim\n# Docs: https://hermes-agent.nousresearch.com/docs/user-guide/secrets/\n#\n# secrets:\n# # Optional explicit ordering of enabled sources.\n# # sources: [onepassword, bitwarden]\n#\n# # ---- Bitwarden Secrets Manager (bws CLI) --------------------------------\n# bitwarden:\n# enabled: false\n# access_token_env: BWS_ACCESS_TOKEN # bootstrap token, sourced from .env\n# project_id: \"\" # UUID of the BSM project to sync\n# server_url: \"\" # \"\" = US Cloud; EU/self-hosted URL otherwise\n# cache_ttl_seconds: 300 # 0 disables fresh caching\n# encrypted_cache: # optional encrypted stale fallback\n# enabled: false\n# max_stale_seconds: 0 # 0 disables stale fallback\n# override_existing: true # BSM values win over existing env\n# auto_install: true # lazy-download bws into ~/.hermes/bin\n#\n# # ---- 1Password (op CLI) -------------------------------------------------\n# onepassword:\n# enabled: false\n# # Map env-var names to op:// secret references. Each is resolved with a\n# # single `op read` at startup.\n# env:\n# OPENAI_API_KEY: \"op://Private/OpenAI/api key\"\n# ANTHROPIC_API_KEY: \"op://Private/Anthropic/credential\"\n# account: \"\" # op --account shorthand; \"\" = default\n# service_account_token_env: OP_SERVICE_ACCOUNT_TOKEN # headless auth; unset = desktop session\n# binary_path: \"\" # \"\" = resolve op via PATH; else absolute path\n# cache_ttl_seconds: 300 # 0 disables BOTH cache layers\n# override_existing: true # resolved values win over existing env\n#\n# # ---- Command helper (any CLI vault) --------------------------------------\n# # Run a user-configured helper that prints KEY=VALUE lines on stdout —\n# # works with any secret store that has a CLI: keepassxc-cli, secret-tool,\n# # pass, gpg, or a script that cats a tmpfs env file. Composes with the\n# # sources above (enable any combination). POSIX-only (needs /bin/sh).\n# # The helper must be fast and NON-interactive (hard timeout, 1 MiB cap);\n# # its stderr is discarded so diagnostics can't leak secret material.\n# command:\n# enabled: false\n# command: \"cat /run/user/1000/hermes-secrets.env\"\n# helper_timeout_seconds: 3\n# override_existing: false # .env/shell win by default\n"}, "72056faf8f4258814ed99097a989b50ecc6fc219": {"t": 1785768027.351008, "n": 80224, "h": "c7457f78480b43dd", "c": "# Hermes Agent CLI Configuration\n# Copy this file to cli-config.yaml and customize as needed.\n# This file configures the CLI behavior. Environment variables in .env take precedence.\n\n# =============================================================================\n# Model Configuration\n# =============================================================================\nmodel:\n # Default model to use (can be overridden with --model flag)\n # Both \"default\" and \"model\" work as the key name here.\n default: \"anthropic/claude-opus-4.6\"\n \n # Inference provider selection:\n # \"auto\" - Auto-detect from credentials (default)\n # \"openrouter\" - OpenRouter (requires: OPENROUTER_API_KEY or OPENAI_API_KEY)\n # \"nous\" - Nous Portal OAuth (requires: hermes auth add nous)\n # \"nous-api\" - Nous Portal API key (requires: NOUS_API_KEY)\n # \"anthropic\" - Direct Anthropic API (requires: ANTHROPIC_API_KEY)\n # \"openai-codex\" - OpenAI Codex (requires: hermes auth)\n # \"copilot\" - GitHub Copilot / GitHub Models (requires: GITHUB_TOKEN)\n # \"gemini\" - Use Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n # \"zai\" - Use z.ai / ZhipuAI GLM models (requires: GLM_API_KEY)\n # \"kimi-coding\" - Kimi / Moonshot AI (requires: KIMI_API_KEY)\n # \"minimax\" - MiniMax global (requires: MINIMAX_API_KEY)\n # \"minimax-cn\" - MiniMax China (requires: MINIMAX_CN_API_KEY)\n # \"huggingface\" - Hugging Face Inference (requires: HF_TOKEN)\n # \"nvidia\" - NVIDIA NIM / build.nvidia.com (requires: NVIDIA_API_KEY)\n # \"xiaomi\" - Xiaomi MiMo (requires: XIAOMI_API_KEY)\n # \"arcee\" - Arcee AI Trinity models (requires: ARCEEAI_API_KEY)\n # \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY — https://ollama.com/settings)\n # \"deepinfra\" - DeepInfra (requires: DEEPINFRA_API_KEY)\n # \"kilocode\" - KiloCode gateway (requires: KILOCODE_API_KEY)\n # \"azure-foundry\" - Microsoft Foundry / Azure OpenAI (API key or Entra ID)\n # \"lmstudio\" - LM Studio local server (optional: LM_API_KEY, defaults to http://127.0.0.1:1234/v1)\n #\n # Local servers (LM Studio, Ollama, vLLM, llama.cpp):\n # \"custom\" - Any other OpenAI-compatible endpoint. Set base_url below.\n # Aliases: \"ollama\", \"vllm\", \"llamacpp\" all map to \"custom\".\n # LM Studio is first-class and uses provider: \"lmstudio\".\n # It works with both no-auth and auth-enabled server modes.\n #\n # Can also be overridden for a single invocation with the --provider flag.\n provider: \"auto\"\n \n # API configuration (falls back to OPENROUTER_API_KEY env var)\n # api_key: \"your-key-here\" # Uncomment to set here instead of .env\n base_url: \"https://openrouter.ai/api/v1\"\n\n # Azure Foundry keyless auth example:\n # provider: \"azure-foundry\"\n # base_url: \"https://<resource>.openai.azure.com/openai/v1\"\n # auth_mode: \"entra_id\" # DefaultAzureCredential: az login, managed identity, workload identity, etc.\n # default: \"gpt-4o\" # Deployment/model name\n # entra:\n # scope: \"https://ai.azure.com/.default\" # Optional; this is the default.\n\n # ── Token limits — two settings, easy to confuse ──────────────────────────\n #\n # context_length: TOTAL context window (input + output tokens combined).\n # Controls when Hermes compresses history and validates requests.\n # Leave unset — Hermes auto-detects the correct value from the provider.\n # Set manually only when auto-detection is wrong (e.g. a local server with\n # a custom num_ctx, or a proxy that doesn't expose /v1/models).\n #\n # context_length: 131072\n #\n # max_tokens: OUTPUT cap — maximum tokens the model may generate per response.\n # Unrelated to how long your conversation history can be.\n # The OpenAI-standard name \"max_tokens\" is a misnomer; Anthropic's native\n # API has since renamed it \"max_output_tokens\" for clarity.\n # Leave unset to use the model's native output ceiling (recommended).\n # Set only if you want to deliberately limit individual response length.\n #\n# max_tokens: 8192\n\n # ── Custom request headers (optional) ─────────────────────────────────────\n #\n # default_headers: extra HTTP headers sent on every request to an\n # OpenAI-compatible endpoint. User values take precedence over the\n # provider/SDK defaults, so this is the supported way to override the\n # OpenAI Python SDK's identifying headers (User-Agent: OpenAI/Python ...,\n # X-Stainless-*) when a custom provider sits behind a gateway/WAF that\n # rejects them — e.g. an upstream that returns \"502 Upstream access\n # forbidden\" for the SDK default User-Agent but accepts a plain one.\n # Applies on the OpenAI wire only (not native Anthropic / Bedrock).\n #\n # default_headers:\n # User-Agent: \"curl/8.7.1\"\n #\n # extra_headers: accepted as an alias of default_headers (merged, with\n # extra_headers winning when both are set) — matches the per-provider\n # extra_headers key below.\n #\n # Per-provider variant: named providers / custom_providers entries accept an\n # extra_headers dict scoped to that endpoint only — for reverse proxies,\n # gateways, or custom auth (e.g. Cloudflare Access service tokens).\n # Merged onto SDK/provider defaults with the entry's values winning.\n # Header values are treated as secrets and are never logged.\n #\n # providers:\n # my-proxy:\n # base_url: \"https://llm.internal.example.com/v1\"\n # key_env: \"MY_PROXY_API_KEY\"\n # extra_headers:\n # CF-Access-Client-Id: \"xxxx.access\"\n # CF-Access-Client-Secret: \"${CF_ACCESS_SECRET}\"\n # X-Client-Name: \"hermes-agent\"\n\n# Named provider overrides (optional)\n# Use this for per-provider request timeouts, non-stream stale timeouts,\n# and per-model exceptions.\n# Applies to the primary turn client on every api_mode (OpenAI-wire, native\n# Anthropic, and Anthropic-compatible providers), the fallback chain, and\n# client rebuilds during credential rotation. For OpenAI-wire chat\n# completions (streaming and non-streaming) the configured value is also\n# used as the per-request ``timeout=`` kwarg so it wins over the legacy\n# HERMES_API_TIMEOUT env var (which still applies when no config is set).\n# ``stale_timeout_seconds`` controls the non-streaming stale-call detector and\n# wins over the legacy HERMES_API_CALL_STALE_TIMEOUT env var. Leaving these\n# unset keeps the legacy defaults (HERMES_API_TIMEOUT=1800s,\n# HERMES_API_CALL_STALE_TIMEOUT=90s, native Anthropic 900s). The\n# implicit non-stream stale detector is auto-disabled for local endpoints\n# and can scale upward for very large contexts.\n#\n# Not currently wired for AWS Bedrock (bedrock_converse + AnthropicBedrock\n# SDK paths) — those use boto3 with its own timeout configuration.\n#\n# providers:\n# ollama-local:\n# request_timeout_seconds: 300 # Longer timeout for local cold-starts\n# stale_timeout_seconds: 900 # Explicitly re-enable stale detection on local endpoints\n# anthropic:\n# request_timeout_seconds: 30 # Fast-fail cloud requests\n# models:\n# claude-opus-4.6:\n# timeout_seconds: 600 # Longer timeout for extended-thinking Opus calls\n# openai-codex:\n# models:\n# gpt-5.4:\n# stale_timeout_seconds: 1800 # Longer non-stream stale timeout for slow large-context turns\n\n# =============================================================================\n# OpenRouter Provider Routing (only applies when using OpenRouter)\n# =============================================================================\n# Control how requests are routed across providers on OpenRouter.\n# See: https://openrouter.ai/docs/guides/routing/provider-selection\n#\n# provider_routing:\n# # Sort strategy: \"price\" (default), \"throughput\", or \"latency\"\n# # Append :nitro to model name for a shortcut to throughput sorting.\n# sort: \"throughput\"\n#\n# # Only allow these providers (provider slugs from OpenRouter)\n# # only: [\"anthropic\", \"google\"]\n#\n# # Skip these providers entirely\n# # ignore: [\"deepinfra\", \"fireworks\"]\n#\n# # Try providers in this order (overrides default load balancing)\n# # order: [\"anthropic\", \"google\", \"together\"]\n#\n# # Require providers to support all parameters in your request\n# # require_parameters: true\n#\n# # Data policy: \"allow\" (default) or \"deny\" to exclude providers that may store data\n# # data_collection: \"deny\"\n\n# =============================================================================\n# OpenRouter Response Caching (only applies when using OpenRouter)\n# =============================================================================\n# Cache identical API responses at the OpenRouter edge for free instant replays.\n# When enabled, identical requests (same model, messages, parameters) return\n# cached responses with zero billing. Separate from Anthropic prompt caching.\n# See: https://openrouter.ai/docs/guides/features/response-caching\n#\n# openrouter:\n# response_cache: true # Enable response caching (default: true)\n# response_cache_ttl: 300 # Cache TTL in seconds, 1-86400 (default: 300)\n\n# =============================================================================\n# Git Worktree Isolation\n# =============================================================================\n# When enabled, each CLI session creates an isolated git worktree so multiple\n# agents can work on the same repo concurrently without file collisions.\n# Equivalent to always passing --worktree / -w on the command line.\n#\n# worktree: true # Always create a worktree when in a git repo\n# worktree: false # Default — only create when -w flag is passed\n#\n# By default a new worktree branches from the freshly-fetched remote tip\n# (the current branch's upstream, else the remote's default branch) so it\n# starts current with the project instead of from the local clone's\n# (possibly stale) HEAD. Set worktree_sync: false to branch from local HEAD\n# instead — useful when offline or when you deliberately want the clone's\n# exact current state as the base.\n#\n# worktree_sync: true # Default — branch from the fetched remote tip\n# worktree_sync: false # Branch from local HEAD (offline / pinned base)\n\n# =============================================================================\n# Terminal Tool Configuration\n# =============================================================================\n# Choose ONE of the following terminal configurations by uncommenting it.\n# The terminal tool executes commands in the specified environment.\n\n# -----------------------------------------------------------------------------\n# OPTION 1: Local execution (default)\n# Commands run directly on your machine in the current directory\n# -----------------------------------------------------------------------------\n# Working directory behavior:\n# - CLI (`hermes` command): Uses \".\" (current directory where you run hermes)\n# - Gateway/messaging/cron: Uses terminal.cwd here; legacy .env cwd values are deprecated\nterminal:\n backend: \"local\"\n cwd: \".\" # For local backend: \".\" = current directory. Ignored for remote backends unless a backend documents otherwise.\n timeout: 180\n # HOME policy for tool subprocesses:\n # auto - default: host uses your real HOME; containers use HERMES_HOME/home\n # real - force your real OS-user HOME\n # profile - force HERMES_HOME/home for strict per-profile CLI config isolation\n home_mode: \"auto\"\n docker_mount_cwd_to_workspace: false # SECURITY: off by default. Opt in to mount the launch cwd into Docker /workspace.\n lifetime_seconds: 300\n # sudo_password: \"hunter2\" # Optional: pipe a sudo password via sudo -S. SECURITY WARNING: plaintext.\n # sudo_password: \"\" # Explicit empty password: try empty and never open the interactive sudo prompt.\n\n# -----------------------------------------------------------------------------\n# OPTION 2: SSH remote execution\n# Commands run on a remote server - agent code stays local (sandboxed)\n# Great for: keeping agent isolated from its own code, using powerful remote hardware\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"ssh\"\n# cwd: \"/home/myuser/project\" # Path on the REMOTE server\n# timeout: 180\n# lifetime_seconds: 300\n# ssh_host: \"my-server.example.com\"\n# ssh_user: \"myuser\"\n# ssh_port: 22\n# ssh_key: \"~/.ssh/id_rsa\" # Optional - uses ssh-agent if not specified\n\n# -----------------------------------------------------------------------------\n# OPTION 3: Docker container\n# Commands run in an isolated Docker container\n# Great for: reproducible environments, testing, isolation\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"docker\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /)\n# timeout: 180\n# lifetime_seconds: 300\n# docker_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# docker_mount_cwd_to_workspace: true # Explicit opt-in: mount your launch cwd into /workspace\n# # Optional: run the container as your host user's uid:gid so files written\n# # into bind-mounted dirs are owned by you, not root. Drops SETUID/SETGID\n# # caps too since no gosu privilege drop is needed. Leave off if your\n# # chosen docker_image expects to start as root.\n# docker_run_as_host_user: true\n# # Optional: explicitly forward selected env vars into Docker.\n# # These values come from your current shell first, then ~/.hermes/.env.\n# # Warning: anything forwarded here is visible to commands run in the container.\n# docker_forward_env:\n# - \"GITHUB_TOKEN\"\n# - \"NPM_TOKEN\"\n# # Optional: extra flags passed verbatim to docker run (appended after security defaults).\n# # Useful for adding capabilities (e.g. apt installs needing SETUID) or custom options.\n# # Example: add a Linux capability not included by default\n# # docker_extra_args:\n# # - \"--cap-add\"\n# # - \"SETUID\"\n\n# -----------------------------------------------------------------------------\n# OPTION 4: Singularity/Apptainer container\n# Commands run in a Singularity container (common in HPC environments)\n# Great for: HPC clusters, shared compute environments\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"singularity\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# singularity_image: \"docker://nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 5: Modal cloud execution\n# Commands run on Modal's cloud infrastructure\n# Great for: GPU access, scalable compute, serverless execution\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"modal\"\n# cwd: \"/workspace\" # Path INSIDE the sandbox (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# modal_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 6: Daytona cloud execution\n# Commands run in Daytona cloud sandboxes\n# Great for: Cloud dev environments, persistent workspaces, team collaboration\n# Requires: pip install daytona, DAYTONA_API_KEY env var\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"daytona\"\n# cwd: \"~\"\n# timeout: 180\n# lifetime_seconds: 300\n# daytona_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# container_disk: 10240 # Daytona max is 10GB per sandbox\n\n#\n# --- Container resource limits (docker, singularity, modal, daytona -- ignored for local/ssh) ---\n# These settings apply to all container backends. They control the resources\n# allocated to the sandbox and whether its filesystem persists across sessions.\n container_cpu: 1 # CPU cores\n container_memory: 5120 # Memory in MB (5120 = 5GB)\n container_disk: 51200 # Disk in MB (51200 = 50GB)\n container_persistent: true # Persist filesystem across sessions (false = ephemeral)\n\n# -----------------------------------------------------------------------------\n# SUDO SUPPORT (works with ALL backends above)\n# -----------------------------------------------------------------------------\n# Add sudo_password to any terminal config above to enable sudo commands.\n# The password is piped via `sudo -S`. Works with local, ssh, docker, etc.\n#\n# SECURITY WARNING: Password stored in plaintext!\n#\n# INTERACTIVE PROMPT: If sudo_password is unset and the CLI is running,\n# you'll be prompted to enter your password when sudo is needed:\n# - 45-second timeout (auto-skips if no input)\n# - Press Enter to skip (command fails gracefully)\n# - Password is hidden while typing\n# - Password is cached for the session\n#\n# EMPTY PASSWORDS: Setting sudo_password to an explicit empty string is different\n# from leaving it unset. Hermes will try an empty password via `sudo -S` and\n# will not open the interactive prompt. This is useful for passwordless sudo,\n# Touch ID sudo setups, and environments where prompting is just noise.\n#\n# ALTERNATIVES:\n# - SSH backend: Configure passwordless sudo on the remote server\n# - Containers: Run as root inside the container (no sudo needed)\n# - Local: Configure /etc/sudoers for specific commands\n#\n# Example (add to your terminal section):\n# sudo_password: \"your-password-here\"\n\n# =============================================================================\n# Security Scanning (tirith)\n# =============================================================================\n# Optional pre-exec command security scanning via tirith.\n# Detects homograph URLs, pipe-to-shell, terminal injection, env manipulation.\n# Install: brew install sheeki03/tap/tirith\n# Docs: https://github.com/sheeki03/tirith\n#\n# security:\n# tirith_enabled: true # Enable/disable tirith scanning\n# tirith_path: \"tirith\" # Path to tirith binary (supports ~ expansion)\n# tirith_timeout: 5 # Scan timeout in seconds\n# tirith_fail_open: true # Allow commands if tirith unavailable\n\n# =============================================================================\n# Browser Tool Configuration\n# =============================================================================\nbrowser:\n # Inactivity timeout in seconds - browser sessions are automatically closed\n # after this period of no activity between agent loops (default: 120 = 2 minutes)\n inactivity_timeout: 120\n\n# =============================================================================\n# Tool Loop Guardrails\n# =============================================================================\n# Soft warnings are enabled by default. They append guidance to repeated failed\n# or non-progressing tool results but still let the tool execute. Hard stops are\n# opt-in circuit breakers for autonomous/cron sessions where stopping a loop is\n# preferable to spending the full iteration budget.\ntool_loop_guardrails:\n warnings_enabled: true\n hard_stop_enabled: false\n warn_after:\n exact_failure: 2\n same_tool_failure: 3\n idempotent_no_progress: 2\n hard_stop_after:\n exact_failure: 5\n same_tool_failure: 8\n idempotent_no_progress: 5\n\n# =============================================================================\n# Context Compression (Auto-shrinks long conversations)\n# =============================================================================\n# When conversation approaches model's context limit, middle turns are\n# automatically summarized to free up space while preserving important context.\n#\n# HOW IT WORKS:\n# 1. Tracks actual token usage from API responses (not estimates)\n# 2. When prompt_tokens >= threshold% of model's context_length, triggers compression\n# 3. Protects first 3 turns (system prompt, initial request, first response)\n# 4. Protects last N turns (default 20 messages = ~10 full turns of recent context)\n# 5. Summarizes middle turns using a fast/cheap model\n# 6. Inserts summary as a user message, continues conversation seamlessly\n#\n# Post-compression tail budget is target_ratio × threshold × context_length:\n# 200K context, threshold 0.50, ratio 0.20 → 20K tokens of recent tail preserved\n# 1M context, threshold 0.50, ratio 0.20 → 100K tokens of recent tail preserved\n#\ncompression:\n # Enable automatic context compression (default: true)\n # Set to false if you prefer to manage context manually or want errors on overflow\n enabled: true\n \n # Trigger compression at this % of model's context limit (default: 0.50 = 50%)\n # Lower values = more aggressive compression, higher values = compress later\n # Models with context windows below 512K are floored at 0.75 (raise-only) so\n # compaction doesn't fire with half the window still free; set above 0.75 to override.\n threshold: 0.50\n\n # Per-model threshold overrides: keys are substring-matched against the model\n # name (longest match wins). Useful when some models need different compaction\n # points — e.g. a 1M-context model can compress later (0.30) while a 128K\n # model needs to compress earlier (0.60). The small-context floor (75% for\n # <512K models) still applies on top of per-model overrides.\n # model_thresholds:\n # \"glm-5.2\": 0.40\n # \"claude-sonnet\": 0.35\n # \"gpt-5\": 0.30\n\n # Optional absolute token cap for the compression trigger (default: null = disabled).\n # When set, compression fires at the LOWER of the ratio-based threshold and this\n # absolute token count — first-fires-wins. It never fires later than this count\n # regardless of which model is active (useful when switching between models with\n # very different context windows). Clamped to the model's context length at\n # apply-time, so a cap above the window is a no-op (ratio-based threshold wins).\n # Survives model switches and fallback activations.\n # threshold_tokens: 200000\n\n # Existing Codex gpt-5.5 behavior: raise Hermes' compaction trigger to 85%\n # for the ChatGPT Codex OAuth route. Set false to opt back down to threshold.\n codex_gpt55_autoraise: true\n\n # Fraction of the threshold to preserve as recent tail (default: 0.20 = 20%)\n # e.g. 20% of 50% threshold = 10% of total context kept as recent messages.\n # Summary output is separately capped at 12K tokens (Gemini output limit).\n # Range: 0.10 - 0.80\n target_ratio: 0.20\n\n # Number of most-recent messages to always preserve (default: 20 ≈ 10 full turns)\n # Higher values keep more recent conversation intact at the cost of more aggressive\n # compression of older turns.\n protect_last_n: 20\n\n # Compression retry rounds before a turn gives up with \"max compression\n # attempts reached\" (default: 3, same as the previous hardcoded value).\n # Raise (e.g. 6) for tool-schema-heavy sessions where 3 rounds cannot bring\n # the request estimate under the threshold. Validated >= 1, hard cap 10.\n max_attempts: 3\n\n # Codex app-server (codex CLI runtime) thread-compaction mode. The codex\n # agent owns the real thread context on this runtime, so Hermes' summarizer\n # cannot shrink it — compaction goes through the app server instead.\n # native = let Codex decide when to compact its own thread (default)\n # hermes = let Hermes threshold trigger Codex thread/compact/start\n # off = Hermes will not auto-trigger compaction; Codex may still compact natively\n codex_app_server_auto: native\n\n # Number of non-system messages to protect at the head of the transcript, in\n # ADDITION to the system prompt (which is always implicitly protected).\n # Head messages are NEVER summarized — they survive every compression\n # indefinitely. This gives stable early context for short/medium sessions,\n # but in long-running sessions that rely on rolling compaction the pinned\n # opening turns may not match how you want the session framed over time.\n # Set to 0 to preserve ONLY the system prompt (plus the rolling summary\n # and recent tail) — the cleanest configuration for long-running sessions.\n # Default 3 preserves the system prompt plus the first three non-system\n # head messages, matching the pre-feature behaviour.\n protect_first_n: 3\n\n # Idle compaction (default: 0 = disabled). When > 0, a session that resumes\n # after at least this many seconds of inactivity compacts its accumulated\n # history up front, before the first reply, so a long-lived thread you come\n # back to later doesn't re-read its full stale context on every turn.\n # Time-based, so it complements (does not replace) the size-based `threshold`\n # above. It is skipped when the context is already small (at or below the\n # post-compression target = threshold × target_ratio), so it never wastes a\n # summarization on a short idle thread. Example: 1800 = compact after 30 min idle.\n idle_compact_after_seconds: 0\n\n # To pin a specific model/provider for compression summaries, use the\n # auxiliary section below (auxiliary.compression.provider / model).\n\n# =============================================================================\n# Anthropic prompt caching TTL\n# =============================================================================\n# When prompt caching is active (Claude via OpenRouter or native Anthropic),\n# Anthropic supports two TTL tiers for cached prefixes: \"5m\" (default) and\n# \"1h\". Other values are ignored and \"5m\" is used.\n#\nprompt_caching:\n cache_ttl: \"5m\" # use \"1h\" for long sessions with pauses between turns\n\n# =============================================================================\n# Auxiliary Models (Advanced — Experimental)\n# =============================================================================\n# Hermes uses lightweight \"auxiliary\" models for side tasks: image analysis,\n# browser screenshot analysis, web page summarization, TTS audio-tag insertion,\n# and context compression.\n#\n# By default these use Gemini Flash via OpenRouter or Nous Portal and are\n# auto-detected from your credentials. You do NOT need to change anything\n# here for normal usage.\n#\n# WARNING: Overriding these with providers other than OpenRouter or Nous Portal\n# is EXPERIMENTAL and may not work. Not all models/providers support vision,\n# produce usable summaries, or accept the same API format. Change at your own\n# risk — if things break, reset to \"auto\" / empty values.\n#\n# Each task has its own provider + model pair so you can mix providers.\n# For example: OpenRouter for vision (needs multimodal), but your main\n# local endpoint for compression (just needs text).\n#\n# Provider options:\n# \"auto\" - Best available: OpenRouter → Nous Portal → main endpoint (default)\n# \"openrouter\" - Force OpenRouter (requires OPENROUTER_API_KEY)\n# \"nous\" - Force Nous Portal (requires: hermes auth add nous)\n# \"gemini\" - Force Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n# \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY)\n# \"codex\" - Force Codex OAuth (requires: hermes model → Codex).\n# Uses gpt-5.3-codex which supports vision.\n# \"main\" - Use your custom endpoint (OPENAI_BASE_URL + OPENAI_API_KEY).\n# Works with OpenAI API, local models, or any OpenAI-compatible\n# endpoint. Also falls back to Codex OAuth and API-key providers.\n#\n# Model: leave empty to use the provider's default. When empty, OpenRouter\n# uses \"google/gemini-3-flash-preview\" and Nous uses \"gemini-3-flash\".\n# Other providers pick a sensible default automatically.\n#\n# auxiliary:\n# # Image analysis: vision_analyze tool + browser screenshots\n# vision:\n# provider: \"auto\"\n# model: \"\" # e.g. \"google/gemini-2.5-flash\", \"openai/gpt-4o\"\n# timeout: 30 # LLM API call timeout (seconds)\n# download_timeout: 30 # Image HTTP download timeout (seconds)\n# # Increase for slow connections or self-hosted image servers\n# reasoning_effort: \"\" # Per-task thinking level: none, minimal, low, medium,\n# # high, xhigh, max, ultra. Empty = provider default.\n# # Works on every auxiliary task block (vision,\n# # web_extract, compression, title_generation, curator,\n# # background_review, moa_reference, ...). Example: run\n# # compression at \"low\" and vision at \"none\" to cut\n# # side-task latency/cost on reasoning models.\n#\n# # Web page scraping / summarization + browser page text extraction\n# web_extract:\n# provider: \"auto\"\n# model: \"\"\n# reasoning_effort: \"low\"\n#\n# # Gemini 3.1 TTS hidden audio-tag insertion\n# tts_audio_tags:\n# provider: \"auto\" # empty model = your main chat model\n# model: \"\"\n# timeout: 30\n#\n# # Automatic session title generation after the first exchange\n# title_generation:\n# enabled: true # set false to disable auto-title generation\n# provider: \"auto\"\n# model: \"\"\n# timeout: 30\n# language: \"\" # empty = match the user's language; or e.g. \"English\"\n#\n# # Session search — summarizes matching past sessions\n# session_search:\n# provider: \"auto\"\n# model: \"\"\n# timeout: 30\n# max_concurrency: 3 # Limit parallel summaries to reduce request-burst 429s\n# extra_body: {} # Provider-specific OpenAI-compatible request fields\n# # Example for providers that support request-body\n# # reasoning controls:\n# # extra_body:\n# # enable_thinking: false\n# # Some vLLM/Qwen deployments expect this nested:\n# # extra_body:\n# # chat_template_kwargs:\n# # enable_thinking: false\n\n# =============================================================================\n# Persistent Memory\n# =============================================================================\n# Bounded curated memory injected into the system prompt every session.\n# Two stores: MEMORY.md (agent's notes) and USER.md (user profile).\n# Character limits keep the memory small and focused. The agent manages\n# pruning -- when at the limit, it must consolidate or replace entries.\n# Disabled by default in batch_runner.\n#\nmemory:\n # Agent's personal notes: environment facts, conventions, things learned\n memory_enabled: true\n \n # User profile: preferences, communication style, expectations\n user_profile_enabled: true\n \n # Character limits (~2.75 chars per token, model-independent)\n memory_char_limit: 2200 # ~800 tokens\n user_char_limit: 1375 # ~500 tokens\n\n # Periodic memory nudge: remind the agent to consider saving memories\n # every N user turns. Set to 0 to disable. Only active when memory is enabled.\n nudge_interval: 10 # Nudge every 10 user turns (0 = disabled)\n\n # Memory flush: give the agent one turn to save memories before context is\n # lost (compression, /new, /reset, exit). Set to 0 to disable.\n # For exit/reset, only fires if the session had at least this many user turns.\n flush_min_turns: 6 # Min user turns to trigger flush on exit/reset (0 = disabled)\n\n# =============================================================================\n# Session Reset Policy (Messaging Platforms)\n# =============================================================================\n# Controls when messaging sessions (Telegram, Discord, WhatsApp, Slack) are\n# automatically cleared. Default is \"none\": sessions never auto-reset —\n# conversation context lives until you /reset or /new manually, or context\n# compression kicks in. Opt in to automatic resets if you prefer sessions to\n# clear on a schedule (long-lived context increases API cost per message,\n# though prompt caching and compression keep this manageable).\n#\n# When an automatic reset triggers, the agent first saves important\n# information to its persistent memory — but the conversation context is\n# wiped. The agent starts fresh but retains learned facts via its memory\n# system.\n#\n# Modes:\n# \"none\" - Never auto-reset (default); context lives until /reset or compression\n# \"idle\" - Reset after N minutes of inactivity\n# \"daily\" - Reset at a fixed hour each day\n# \"both\" - Reset on EITHER inactivity timeout or daily boundary\n#\nsession_reset:\n mode: none # \"none\", \"idle\", \"daily\", or \"both\"\n idle_minutes: 1440 # Inactivity timeout in minutes (used by \"idle\"/\"both\")\n at_hour: 4 # Daily reset hour, 0-23 local time (used by \"daily\"/\"both\")\n\n# Maximum number of simultaneously active chat sessions across CLI, TUI,\n# dashboard chat, and messaging gateway. Set to null, 0, or omit to allow\n# unlimited concurrent sessions. When the limit is reached, new sessions get a\n# clean error while existing active sessions keep their normal behavior. This\n# top-level key takes precedence over gateway.max_concurrent_sessions. The cap\n# is a best-effort single-host/profile runtime guard; Hermes fails open if the\n# local runtime lease registry cannot be read or locked.\nmax_concurrent_sessions: null\n\n# When true, group/channel chats use one session per participant when the platform\n# provides a user ID. This is the secure default and prevents users in the same\n# room from sharing context, interrupts, and token costs. Set false only if you\n# explicitly want one shared \"room brain\" per group/channel.\ngroup_sessions_per_user: true\n\n# ─────────────────────────────────────────────────────────────────────────────\n# API Server — per-client model routing\n# ─────────────────────────────────────────────────────────────────────────────\n# Route different API clients to different models/providers on a single\n# Hermes deployment. Clients choose a backend by sending a specific string\n# as the OpenAI ``model`` field. Unmapped model values fall back to the\n# global model configured in the ``model:`` section above, and an explicit\n# session /model override always wins over a route.\n#\n# Configure via the ``platforms.api_server.extra.model_routes`` gateway\n# config block:\n#\n# platforms:\n# api_server:\n# enabled: true\n# extra:\n# key: \"your-api-server-secret\"\n# model_routes:\n# # Xiaozhi clients send model=\"minimax-m2\" → routed to MiniMax via OpenRouter\n# minimax-m2:\n# model: \"minimax/minimax-m1\"\n# provider: \"openrouter\" # optional — overrides global provider\n# # api_key: \"sk-...\" # optional — per-route UPSTREAM provider\n# # key (NOT caller auth; never logged)\n# # base_url: \"https://...\" # optional — per-route base URL\n# # GPT clients keep their own alias\n# gpt-5:\n# model: \"openai/gpt-5\"\n# provider: \"openrouter\"\n#\n# Configured aliases are automatically listed by GET /v1/models so clients\n# can discover them without manual coordination. Caller authentication is\n# unchanged: every request still authenticates with the global API server\n# key (``extra.key`` / API_SERVER_KEY).\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Gateway Streaming\n# ─────────────────────────────────────────────────────────────────────────────\n# Stream tokens to messaging platforms in real-time. The bot sends a message\n# on first token, then progressively edits it as more tokens arrive.\n# Disabled by default — enable to try the streaming UX on Telegram/Discord/Slack.\n# For Telegram, partial edits are sent as plain text and only the final edit uses MarkdownV2.\nstreaming:\n enabled: false\n # transport: edit # \"edit\" = progressive editMessageText\n # edit_interval: 0.3 # seconds between message edits\n # buffer_threshold: 40 # chars before forcing an edit flush\n # cursor: \" ▉\" # cursor shown during streaming\n\n# =============================================================================\n# Skills Configuration\n# =============================================================================\n# Skills are reusable procedures the agent can load and follow. The agent can\n# also create new skills after completing complex tasks.\n#\nskills:\n # Nudge the agent to create skills after complex tasks.\n # Every N tool-calling iterations, remind the model to consider saving a skill.\n # Set to 0 to disable.\n creation_nudge_interval: 15\n\n # External skill directories — share skills across tools/agents without\n # copying them into ~/.hermes/skills/. Each path is expanded (~ and ${VAR})\n # and resolved to an absolute path. External dirs are read-only: skill\n # creation always writes to ~/.hermes/skills/. Local skills take precedence\n # when names collide.\n # external_dirs:\n # - ~/.agents/skills\n # - /home/shared/team-skills\n\n# =============================================================================\n# Agent Behavior\n# =============================================================================\nagent:\n # Maximum tool-calling iterations per conversation\n # Higher = more room for complex tasks, but costs more tokens\n # Recommended: 20-30 for focused tasks, 50-100 for open exploration\n max_turns: 60\n\n # Inactivity timeout for gateway agent runs (seconds, 0 = unlimited).\n # The agent can run indefinitely when actively calling tools or receiving\n # API responses. Only fires after the agent has been idle for this duration.\n # gateway_timeout: 1800\n\n # Staged warning: send a warning before escalating to full timeout.\n # Fires once per run when inactivity reaches this threshold (seconds).\n # Set to 0 to disable the warning.\n # gateway_timeout_warning: 900\n\n # Graceful drain timeout for gateway stop/restart (seconds).\n # Default 0 = no drain: a restart interrupts in-flight agents immediately,\n # cleans up, and exits. Set a positive value only if you want a grace\n # window on /restart, and keep it well under systemd's TimeoutStopSec.\n # restart_drain_timeout: 0\n\n # Max app-level retry attempts for API errors (connection drops, provider\n # timeouts, 5xx, etc.) before the agent surfaces the failure. Lower this\n # to 1 if you use fallback providers and want fast failover on flaky\n # primaries (default 3). The OpenAI SDK does its own low-level retries\n # underneath this wrapper — this is the Hermes-level loop.\n # api_max_retries: 3\n\n # After the agent edits code without fresh passing verification, nudge it to\n # verify before finishing. The default \"auto\" enables it on interactive\n # coding surfaces (CLI, TUI, desktop) and programmatic callers, and disables\n # it on conversational messaging surfaces (Telegram, Discord, etc.) where the\n # verification summary would reach a human as chat noise. Set true or false to\n # force it on or off; the HERMES_VERIFY_ON_STOP env var (1/0) takes precedence.\n # verify_on_stop: auto\n\n # Standing operator instructions for the coding posture (when Hermes is in a\n # code workspace). Appended to the coding brief as an extra system block, so\n # you can pin project-wide workflow rules without editing the shipped brief.\n # Accepts a string or a list of strings. Takes effect next session.\n # coding_instructions:\n # - \"For UI work, don't run tsc/lint until I approve the look.\"\n # - \"Clean the diff before you commit and push.\"\n\n # When verify-on-stop finds edited code without fresh verification evidence,\n # append guidance for creative UI work (avoid broad tsc/lint/test before visual\n # approval) and clean-diff expectations. Set false to keep that nudge terse.\n # verify_guidance: true\n\n # A `pre_verify` hook (plugin or shell, see Event Hooks docs) can keep the\n # agent going one more turn to verify/clean before finishing. This caps how\n # many times one turn may be nudged to continue, so a hook can't trap the loop.\n # Default 3.\n # max_verify_nudges: 3\n\n # Enable verbose logging\n verbose: false\n \n # Reasoning effort level (OpenRouter and Nous Portal)\n # Controls how much \"thinking\" the model does before responding.\n # Options: \"xhigh\" (max), \"high\", \"medium\", \"low\", \"minimal\", \"none\" (disable)\n reasoning_effort: \"medium\"\n \n # Per-model reasoning effort overrides (optional dict)\n # Key: any sensible model spelling works (exact, dots↔dashes interchangeable,\n # provider prefix optional). First match wins.\n # Value: reasoning effort level (same options as reasoning_effort)\n # Override the global reasoning_effort for that specific model.\n # NOTE: no `hermes config set` support for this key -- edit YAML directly.\n # reasoning_overrides:\n # \"openrouter/anthropic/claude-opus-4.5\": \"xhigh\"\n # \"openai/gpt-5\": \"low\"\n # \"claude-opus-4.6\": \"high\" # bare model name also works\n # \"deepseek/deepseek-v4-pro\": \"xhigh\" # dots and dashes are interchangeable\n reasoning_overrides: {}\n \n # Predefined personalities (use with /personality command)\n personalities:\n helpful: \"You are a helpful, friendly AI assistant.\"\n concise: \"You are a concise assistant. Keep responses brief and to the point.\"\n technical: \"You are a technical expert. Provide detailed, accurate technical information.\"\n creative: \"You are a creative assistant. Think outside the box and offer innovative solutions.\"\n teacher: \"You are a patient teacher. Explain concepts clearly with examples.\"\n kawaii: \"You are a kawaii assistant! Use cute expressions like (◕‿◕), ★, ♪, and ~! Add sparkles and be super enthusiastic about everything! Every response should feel warm and adorable desu~! ヽ(>∀<☆)ノ\"\n catgirl: \"You are Neko-chan, an anime catgirl AI assistant, nya~! Add 'nya' and cat-like expressions to your speech. Use kaomoji like (=^・ω・^=) and ฅ^•ﻌ•^ฅ. Be playful and curious like a cat, nya~!\"\n pirate: \"Arrr! Ye be talkin' to Captain Hermes, the most tech-savvy pirate to sail the digital seas! Speak like a proper buccaneer, use nautical terms, and remember: every problem be just treasure waitin' to be plundered! Yo ho ho!\"\n shakespeare: \"Hark! Thou speakest with an assistant most versed in the bardic arts. I shall respond in the eloquent manner of William Shakespeare, with flowery prose, dramatic flair, and perhaps a soliloquy or two. What light through yonder terminal breaks?\"\n surfer: \"Duuude! You're chatting with the chillest AI on the web, bro! Everything's gonna be totally rad. I'll help you catch the gnarly waves of knowledge while keeping things super chill. Cowabunga! 🤙\"\n noir: \"The rain hammered against the terminal like regrets on a guilty conscience. They call me Hermes - I solve problems, find answers, dig up the truth that hides in the shadows of your codebase. In this city of silicon and secrets, everyone's got something to hide. What's your story, pal?\"\n uwu: \"hewwo! i'm your fwiendwy assistant uwu~ i wiww twy my best to hewp you! *nuzzles your code* OwO what's this? wet me take a wook! i pwomise to be vewy hewpful >w<\"\n philosopher: \"Greetings, seeker of wisdom. I am an assistant who contemplates the deeper meaning behind every query. Let us examine not just the 'how' but the 'why' of your questions. Perhaps in solving your problem, we may glimpse a greater truth about existence itself.\"\n hype: \"YOOO LET'S GOOOO!!! 🔥🔥🔥 I am SO PUMPED to help you today! Every question is AMAZING and we're gonna CRUSH IT together! This is gonna be LEGENDARY! ARE YOU READY?! LET'S DO THIS! 💪😤🚀\"\n\n# =============================================================================\n# Toolsets\n# =============================================================================\n# Control which tools the agent has access to.\n# Use `hermes tools` to interactively enable/disable tools per platform.\n\n# =============================================================================\n# Platform Toolsets (per-platform tool configuration)\n# =============================================================================\n# Override which toolsets are available on each platform.\n# If a platform isn't listed here, its built-in default is used.\n#\n# You can use EITHER:\n# - A preset like \"hermes-cli\" or \"hermes-telegram\" (curated tool set)\n# - A list of individual toolsets to compose your own (see list below)\n#\n# Supported platform keys: cli, telegram, discord, whatsapp, slack, qqbot, teams, google_chat\n#\n# Examples:\n#\n# # Use presets (same as defaults):\n# platform_toolsets:\n# cli: [hermes-cli]\n# telegram: [hermes-telegram]\n#\n# # Custom: give Telegram only web + terminal + file + planning:\n# platform_toolsets:\n# telegram: [web, terminal, file, todo]\n#\n# # Custom: CLI without browser or image gen:\n# platform_toolsets:\n# cli: [web, terminal, file, skills, todo, tts, cronjob]\n#\n# # Restrictive: Discord gets read-only tools only:\n# platform_toolsets:\n# discord: [web, vision, skills, todo]\n#\n# If not set, defaults are:\n# cli: hermes-cli (everything + cronjob management)\n# telegram: hermes-telegram (terminal, file, web, vision, image, tts, browser, skills, todo, cronjob, messaging)\n# discord: hermes-discord (same as telegram)\n# whatsapp: hermes-whatsapp (same as telegram)\n# slack: hermes-slack (same as telegram)\n# signal: hermes-signal (same as telegram)\n# homeassistant: hermes-homeassistant (same as telegram)\n# qqbot: hermes-qqbot (same as telegram)\n# teams: hermes-teams (same as telegram)\n# google_chat: hermes-google_chat (same as telegram)\n#\nplatform_toolsets:\n cli: [hermes-cli]\n telegram: [hermes-telegram]\n discord: [hermes-discord]\n whatsapp: [hermes-whatsapp]\n slack: [hermes-slack]\n signal: [hermes-signal]\n homeassistant: [hermes-homeassistant]\n qqbot: [hermes-qqbot]\n yuanbao: [hermes-yuanbao]\n teams: [hermes-teams]\n google_chat: [hermes-google_chat]\n\n# =============================================================================\n# Gateway Platform Settings\n# =============================================================================\n# Optional per-platform messaging settings.\n# Platform-specific knobs live under `extra`.\n#\n# platforms:\n# telegram:\n# reply_to_mode: \"first\" # off | first | all\n# # guest_mode lets explicit @mentions from non-allowlisted groups through.\n# # Default false; ordinary messages, replies, and regex wake words stay blocked.\n# guest_mode: false\n# # allowed_chats: [\"-1001234567890\"]\n# extra:\n# disable_link_previews: false # Set true to suppress Telegram URL previews in bot messages\n# 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\n# rich_drafts: false # Experimental rich draft previews during Telegram DM streaming; default false because Telegram Desktop/macOS can visually overlay draft frames\n# command_menu:\n# # Telegram allows up to 100 BotCommands; Hermes defaults to 60 so\n# # all built-in commands plus common skill commands stay visible\n# # while remaining under Telegram's payload-size limit. Clamped 1..100.\n# max_commands: 60\n# # prepend = user priority first, then Hermes defaults\n# # append = Hermes defaults first, then user priority\n# # replace = only the list below defines priority\n# priority_mode: prepend\n# priority:\n# - my_plugin_command\n# webhook:\n# extra:\n# # Route scripts default to a 30 second timeout. Scripts must live under\n# # the active profile's scripts directory and receive webhook JSON on stdin.\n# script_timeout_seconds: 30\n#\n# Discord-specific settings (config.yaml top-level, not under platforms:):\n#\n# discord:\n# require_mention: true # Require @mention in server channels (default: true)\n# auto_thread: true # Auto-create thread on @mention (default: true)\n# free_response_channels: \"\" # Channel IDs where no mention is needed\n# reactions: true # Show processing reactions (default: true)\n# history_backfill: true # Recover missed channel messages on mention (default: true)\n# history_backfill_limit: 50 # Max messages to scan backwards (default: 50)\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Available toolsets (use these names in platform_toolsets or the toolsets list)\n#\n# Run `hermes chat --list-toolsets` to see all toolsets and their tools.\n# Run `hermes chat --list-tools` to see every individual tool with descriptions.\n# ─────────────────────────────────────────────────────────────────────────────\n#\n# INDIVIDUAL TOOLSETS (compose your own):\n# web - web_search, web_extract\n# search - web_search only (no scraping)\n# terminal - terminal, process\n# file - read_file, write_file, patch, search\n# browser - browser_navigate, browser_snapshot, browser_click, browser_type,\n# browser_scroll, browser_back, browser_press,\n# browser_get_images, browser_vision (requires BROWSERBASE_API_KEY)\n# vision - vision_analyze (requires OPENROUTER_API_KEY)\n# image_gen - image_generate (requires FAL_KEY)\n# skills - skills_list, skill_view\n# skills_hub - skill_hub (search/install/manage from online registries — user-driven only)\n# todo - todo (in-memory task planning, no deps)\n# tts - text_to_speech (Edge TTS free, or ELEVENLABS/OPENAI/MINIMAX/MISTRAL key)\n# cronjob - cronjob (create/list/update/pause/resume/run/remove scheduled tasks)\n#\n# PRESETS (curated bundles):\n# hermes-cli - All of the above except send_message\n# hermes-telegram - terminal, file, web, vision, image_gen, tts, browser,\n# skills, todo, cronjob, send_message\n# hermes-discord - Same as hermes-telegram\n# hermes-whatsapp - Same as hermes-telegram\n# hermes-slack - Same as hermes-telegram\n#\n# COMPOSITE:\n# debugging - terminal + web + file\n# safe - web + vision (no terminal access)\n# all - Everything available\n#\n# web - Web search and content extraction (web_search, web_extract)\n# search - Web search only, no scraping (web_search)\n# terminal - Command execution and process management (terminal, process)\n# file - File operations: read, write, patch, search\n# browser - Full browser automation (navigate, click, type, screenshot, etc.)\n# vision - Image analysis (vision_analyze)\n# image_gen - Image generation with FLUX (image_generate)\n# skills - Load skill documents (skills_list, skill_view)\n# todo - Task planning and tracking for multi-step work\n# memory - Persistent memory across sessions (personal notes + user profile)\n# session_search - Search and recall past conversations (FTS5 + Gemini Flash summarization)\n# tts - Text-to-speech (Edge TTS free, ElevenLabs, OpenAI, MiniMax, Mistral)\n# cronjob - Schedule and manage automated tasks (CLI-only)\n#\n# Composite toolsets:\n# debugging - terminal + web + file (for troubleshooting)\n# safe - web + vision (no terminal access)\n\n# NOTE: The top-level \"toolsets\" key is deprecated and ignored.\n# Tool configuration is managed per-platform via platform_toolsets above.\n# Use `hermes tools` to configure interactively, or edit platform_toolsets directly.\n#\n# CLI override: hermes chat --toolsets terminal,web,file\n\n# =============================================================================\n# MCP (Model Context Protocol) Servers\n# =============================================================================\n# Connect to external MCP servers to add tools from the MCP ecosystem.\n# Each server's tools are automatically discovered and registered.\n# See website/docs/user-guide/features/mcp.md for full documentation.\n#\n# Stdio servers (spawn a subprocess):\n# command: the executable to run\n# args: command-line arguments\n# env: environment variables (only these + safe defaults passed to subprocess)\n#\n# HTTP servers (connect to a URL):\n# url: the MCP server endpoint\n# headers: HTTP headers (e.g., for authentication)\n#\n# Optional per-server settings:\n# timeout: tool call timeout in seconds (default: 120)\n# connect_timeout: initial connection timeout (default: 60)\n# keepalive_interval: liveness ping cadence in seconds (default: 180).\n# Lower it below the server's session TTL for servers that expire idle\n# sessions quickly (e.g. Unreal Engine editor MCP, ~15s), otherwise idle\n# tool calls hit an expired session and pay a slow reconnect. Floored at 5s.\n#\n# mcp_servers:\n# time:\n# command: uvx\n# args: [\"mcp-server-time\"]\n# filesystem:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-filesystem\", \"/home/user\"]\n# notion:\n# url: https://mcp.notion.com/mcp\n# github:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-github\"]\n# env:\n# GITHUB_PERSONAL_ACCESS_TOKEN: \"ghp_...\"\n#\n# Sampling (server-initiated LLM requests) — enabled by default.\n# Per-server config under the 'sampling' key:\n# analysis:\n# command: npx\n# args: [\"-y\", \"analysis-server\"]\n# sampling:\n# enabled: true # default: true\n# model: \"gemini-3-flash\" # override model (optional)\n# max_tokens_cap: 4096 # max tokens per request\n# timeout: 30 # LLM call timeout (seconds)\n# max_rpm: 10 # max requests per minute\n# allowed_models: [] # model whitelist (empty = all)\n# max_tool_rounds: 5 # tool loop limit (0 = disable)\n# log_level: \"info\" # audit verbosity\n\n# =============================================================================\n# Text-to-Speech\n# =============================================================================\n# TTS defaults to Edge TTS unless changed in ~/.hermes/config.yaml.\n# Gemini TTS supports persona/director prompt files, and Gemini 3.1 Flash TTS\n# can use a hidden auxiliary rewrite pass to insert expressive square-bracket\n# audio tags into the TTS script without showing tags in chat.\n#\n# tts:\n# provider: \"gemini\"\n# gemini:\n# model: \"gemini-3.1-flash-tts-preview\"\n# voice: \"Kore\"\n# audio_tags: false\n# persona_prompt_file: \"\" # e.g. ~/.hermes/tts/radio-host.md\n\n# =============================================================================\n# Voice Transcription (Speech-to-Text)\n# =============================================================================\n# Automatically transcribe voice messages on messaging platforms.\n# Providers: local (free, faster-whisper) | groq (free tier) | openai (Whisper API) | mistral (Voxtral Transcribe)\n# Set the corresponding API key in .env: GROQ_API_KEY, OPENAI_API_KEY, or MISTRAL_API_KEY.\nstt:\n enabled: true\n # provider: \"local\" # auto-detected if omitted\n local:\n model: \"base\" # tiny | base | small | medium | large-v3 | turbo\n # language: \"\" # auto-detect; set to \"en\", \"es\", \"fr\", etc. to force\n openai:\n model: \"whisper-1\" # whisper-1 | gpt-4o-mini-transcribe | gpt-4o-transcribe\n # mistral:\n # model: \"voxtral-mini-latest\" # voxtral-mini-latest | voxtral-mini-2602\n # deepinfra:\n # # Model id is discovered live from the DeepInfra catalog filtered\n # # by the `stt` surface tag — leave `model` blank to take the first\n # # live result. Pin only when you need a specific Whisper variant.\n # model: \"\"\n\n# Text-to-speech. Only the deepinfra block is documented here — the\n# remaining providers (edge, openai, xai, minimax, mistral, gemini,\n# elevenlabs, neutts, kittentts, piper) inherit sensible defaults from\n# DEFAULT_CONFIG in hermes_cli/config.py.\n# tts:\n# provider: \"deepinfra\"\n# deepinfra:\n# # Model id is discovered live from the DeepInfra catalog filtered\n# # by the `tts` surface tag — leave `model` blank to take the first\n# # live result.\n# model: \"\"\n# voice: \"default\"\n\n# Image generation. Each provider plugin reads its own ``image_gen.<name>``\n# block; deepinfra discovers models live from\n# api.deepinfra.com/v1/openai/models filtered by the ``image-gen`` tag —\n# no model id is hardcoded, so retired models disappear automatically.\n# image_gen:\n# provider: \"deepinfra\"\n# deepinfra:\n# # Leave `model` blank for the first live `image-gen`-tagged result.\n# model: \"\"\n\n# =============================================================================\n# Response Pacing (Messaging Platforms)\n# =============================================================================\n# Add human-like delays between message chunks.\n# human_delay:\n# mode: \"off\" # \"off\" | \"natural\" | \"custom\"\n# min_ms: 800 # Min delay (custom mode only)\n# max_ms: 2500 # Max delay (custom mode only)\n\n# =============================================================================\n# Session Logging\n# =============================================================================\n# Session trajectories are automatically saved to logs/ directory.\n# Each session creates: logs/session_YYYYMMDD_HHMMSS_UUID.json\n#\n# The session ID is displayed in the welcome banner for easy reference.\n# Logs contain full conversation history in trajectory format:\n# - System prompt, user messages, assistant responses\n# - Tool calls with inputs/outputs\n# - Timestamps for debugging\n#\n# No configuration needed - logging is always enabled.\n# To disable, you would need to modify the source code.\n\n# =============================================================================\n# Code Execution Sandbox (Programmatic Tool Calling)\n# =============================================================================\n# The execute_code tool runs Python scripts that call Hermes tools via RPC.\n# Intermediate tool results stay out of the LLM's context window.\ncode_execution:\n timeout: 300 # Max seconds per script before kill (default: 300 = 5 min)\n max_tool_calls: 50 # Max RPC tool calls per execution (default: 50)\n\n# =============================================================================\n# Subagent Delegation\n# =============================================================================\n# The delegate_task tool spawns child agents with isolated context.\n# Supports single tasks and batch mode (default 3 parallel, configurable).\ndelegation:\n max_iterations: 50 # Max tool-calling turns per child (default: 50)\n # max_concurrent_children: 3 # Max parallel child agents per batch (default: 3, floor: 1, no ceiling).\n # WARNING: values above 10 multiply API cost linearly.\n # max_spawn_depth: 1 # Delegation tree depth cap (range: 1-3, default: 1 = flat).\n # Raise to 2 to allow workers to spawn their own subagents.\n # Requires role=\"orchestrator\" on intermediate agents.\n # orchestrator_enabled: true # Kill switch for role=\"orchestrator\" children (default: true).\n # subagent_auto_approve: false # When a subagent hits a dangerous-command approval prompt, auto-deny (default: false)\n # or auto-approve \"once\" (true) instead of blocking on stdin.\n # The parent TUI owns stdin, so blocking would deadlock; non-interactive resolution is required.\n # Both choices emit a logger.warning audit line. Flip to true only for cron/batch pipelines.\n # inherit_mcp_toolsets: true # When explicit child toolsets are narrowed, also keep the parent's MCP toolsets (default: true). Set false for strict intersection.\n # model: \"google/gemini-3-flash-preview\" # Override model for subagents (empty = inherit parent)\n # provider: \"openrouter\" # Override provider for subagents (empty = inherit parent)\n # # Resolves full credentials (base_url, api_key) automatically.\n # # Supported: openrouter, nous, zai, kimi-coding, minimax\n\n# =============================================================================\n# Honcho Integration (Cross-Session User Modeling)\n# =============================================================================\n# AI-native persistent memory via Honcho (https://honcho.dev/).\n# Builds a deeper understanding of the user across sessions and tools.\n# Runs alongside USER.md — additive, not a replacement.\n#\n# Requires: pip install honcho-ai\n# Config: ~/.honcho/config.json (shared with Claude Code, Cursor, etc.)\n# API key: HONCHO_API_KEY in ~/.hermes/.env or ~/.honcho/config.json\n#\n# Hermes-specific overrides (optional — most config comes from ~/.honcho/config.json):\n# honcho: {}\n\n# =============================================================================\n# Display\n# =============================================================================\ndisplay:\n # Use compact banner mode (hides the ASCII-art banner, shows a single line).\n # true: Compact single-line banner\n # false: Full ASCII banner with tool/skill summary (default)\n compact: false\n\n # Tool progress display level (CLI and gateway)\n # off: Silent — no tool activity shown, just the final response\n # new: Show a tool indicator only when the tool changes (skip repeats)\n # all: Show every tool call with a short preview (default)\n # verbose: Full args, results, and debug logs (same as /verbose)\n # log: Silent in chat; append every tool call to ~/.hermes/logs/tool_calls.log (gateway only)\n # Toggle at runtime with /verbose in the CLI\n tool_progress: all\n\n # Per-platform defaults can be quieter than the global setting. Telegram\n # tunes for mobile: tool_progress and busy_ack_detail default off (no\n # per-tool breadcrumb stream, no \"iteration 21/60\" debug detail in busy\n # acks or heartbeats), but interim_assistant_messages and\n # long_running_notifications STAY ON so the user has real signal between\n # turn start and final answer (mid-turn assistant commentary + a single\n # edit-in-place \"⏳ Working — N min\" heartbeat). Override under\n # display.platforms.telegram.\n\n # Auto-cleanup of temporary progress bubbles after the final response lands.\n # On platforms that support message deletion (currently Telegram), this\n # removes the tool-progress bubble, \"⏳ Still working...\" notices, and\n # context-pressure status messages once the final reply has been delivered —\n # keeping long-running turns visible live, then tidy afterward. Failed runs\n # leave the bubbles in place as breadcrumbs. Off by default.\n # Per-platform override: display.platforms.telegram.cleanup_progress\n # true: Delete tracked progress/status bubbles on successful turn\n # false: Leave everything in place (default)\n # Example:\n # display:\n # platforms:\n # telegram:\n # cleanup_progress: true\n cleanup_progress: false\n\n # Natural mid-turn assistant updates.\n # On gateway platforms, when true, completed assistant status messages are\n # sent as separate chat messages. On the Desktop app, when true, mid-turn\n # assistant narration streamed between tool calls is kept in the transcript\n # instead of the bubble collapsing to only the final message on completion.\n # Independent of tool_progress and gateway streaming.\n # true: Keep/send mid-turn assistant updates (default)\n # false: Only keep/send the final response\n interim_assistant_messages: true\n\n # Gateway-only long-running status heartbeats.\n # When false, the platform does not receive periodic \"⏳ Working — N min\"\n # notifications even if agent.gateway_notify_interval is non-zero. The\n # heartbeat edits a single message in place (where the adapter supports\n # editing) instead of posting a new bubble each interval.\n # Default: true everywhere, including Telegram (silent agents are worse\n # than a single edit-in-place heartbeat).\n long_running_notifications: true\n\n # Include detailed iteration/tool/status context in busy acknowledgments\n # and long-running heartbeats. When true, busy acks show \"iteration 21/60,\n # terminal, 10 min\" and the heartbeat shows \"⏳ Working — 12 min,\n # iteration 21/60, terminal\". When false (Telegram default), both stay\n # terse: \"Interrupting current task\" and \"⏳ Working — 12 min, terminal\".\n busy_ack_detail: true\n\n # What Enter does when Hermes is already busy (CLI and gateway platforms).\n # interrupt: Interrupt the current run and redirect Hermes (default)\n # queue: Queue your message for the next turn\n # steer: Inject your message mid-run via /steer, arriving at the agent\n # after the next tool call — no interrupt, no role violation.\n # Falls back to 'queue' if the agent isn't running yet or if\n # images are attached (steer only carries text).\n # Ctrl+C (or /stop in gateway) always interrupts regardless of this setting.\n # Toggle at runtime with /busy <interrupt|queue|steer>.\n busy_input_mode: interrupt\n\n # Background process notifications (gateway/messaging only).\n # Controls how chatty the process watcher is when you use\n # terminal(background=true, notify_on_complete=true) from Telegram/Discord/etc.\n # off: No watcher messages at all\n # result: Only the final completion message\n # error: Only the final message when exit code != 0\n # all: Running output updates + final message (default)\n background_process_notifications: all\n\n\n # Play terminal bell when agent finishes a response.\n # Useful for long-running tasks — your terminal will ding when the agent is done.\n # Works over SSH. Most terminals can be configured to flash the taskbar or play a sound.\n # true: Ring the terminal bell on each response\n # false: Silent (default)\n bell_on_complete: false\n\n # Show model reasoning/thinking before each response.\n # When enabled, a dim box shows the model's thought process above the response.\n # Toggle at runtime with /reasoning show or /reasoning hide.\n # true: Show the reasoning box\n # false: Hide reasoning (default)\n show_reasoning: false\n\n # Stream tokens to the terminal as they arrive instead of waiting for the\n # full response. The response box opens on first token and text appears\n # line-by-line. Tool calls are still captured silently.\n # true: Stream tokens as they arrive (default)\n # false: Wait for the full response before rendering\n streaming: true\n\n # Show [HH:MM] timestamps on user input and assistant response labels.\n # timestamps: false\n\n # ───────────────────────────────────────────────────────────────────────────\n # Skin / Theme\n # ───────────────────────────────────────────────────────────────────────────\n # Customize CLI visual appearance — banner colors, spinner faces, tool prefix,\n # response box label, and branding text. Change at runtime with /skin <name>.\n #\n # Built-in skins:\n # default — Classic Hermes gold/kawaii\n # ares — Crimson/bronze war-god theme with spinner wings\n # mono — Clean grayscale monochrome\n # slate — Cool blue developer-focused\n # daylight — Bright light-mode theme\n # warm-lightmode — Warm paper-tone light-mode theme\n # poseidon — Sea-green/teal Olympian theme\n # sisyphus — Earthy stone-and-moss theme\n # charizard — Fiery orange dragon theme\n #\n # Custom skins: drop a YAML file in ~/.hermes/skins/<name>.yaml\n # Schema (all fields optional, missing values inherit from default):\n #\n # name: my-theme\n # description: Short description\n # colors:\n # banner_border: \"#HEX\" # Panel border\n # banner_title: \"#HEX\" # Panel title\n # banner_accent: \"#HEX\" # Section headers (Available Tools, etc.)\n # banner_dim: \"#HEX\" # Dim/muted text\n # banner_text: \"#HEX\" # Body text (tool names, skill names)\n # ui_accent: \"#HEX\" # UI accent color\n # response_border: \"#HEX\" # Response box border color\n # spinner:\n # waiting_faces: [\"(⚔)\", \"(⛨)\"] # Faces shown while waiting\n # thinking_faces: [\"(⚔)\", \"(⌁)\"] # Faces shown while thinking\n # thinking_verbs: [\"forging\", \"plotting\"] # Verbs for spinner messages\n # wings: # Optional left/right spinner decorations\n # - [\"⟪⚔\", \"⚔⟫\"]\n # - [\"⟪▲\", \"▲⟫\"]\n # branding:\n # agent_name: \"My Agent\" # Banner title and branding\n # welcome: \"Welcome message\" # Shown at CLI startup\n # response_label: \" ⚔ Agent \" # Response box header label\n # prompt_symbol: \"⚔\" # Prompt symbol (bare token; renderers add trailing space)\n # tool_prefix: \"╎\" # Tool output line prefix (default: ┊)\n #\n skin: default\n\n# =============================================================================\n# Model Aliases — short names for /model command\n# =============================================================================\n# Map short aliases to exact (model, provider, base_url) tuples.\n# Used by /model tab completion and resolve_alias().\n# Aliases are checked BEFORE the models.dev catalog, so they can route\n# to endpoints not in the catalog (e.g. Ollama Cloud, local servers).\n#\n# model_aliases:\n# opus:\n# model: claude-opus-4-6\n# provider: anthropic\n# qwen:\n# model: \"qwen3.5:397b\"\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n# glm:\n# model: glm-4.7\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n\n# =============================================================================\n# Privacy\n# =============================================================================\n# privacy:\n# # Redact PII from the LLM context prompt.\n# # When true, phone numbers are stripped and user/chat IDs are replaced\n# # with deterministic hashes before being sent to the model.\n# # Names and usernames are NOT affected (user-chosen, publicly visible).\n# # Routing/delivery still uses the original values internally.\n# redact_pii: false\n\n# =============================================================================\n# Shell-script hooks\n# =============================================================================\n# Register shell scripts as plugin-hook callbacks. Each entry is executed as\n# a subprocess (shell=False, shlex.split) with a JSON payload on stdin. On\n# stdout the script may return JSON that either blocks the tool call or\n# injects context into the next LLM call.\n#\n# Valid events (mirror hermes_cli.plugins.VALID_HOOKS):\n# pre_tool_call, post_tool_call, pre_llm_call, post_llm_call,\n# pre_api_request, post_api_request, on_session_start, on_session_end,\n# on_session_finalize, on_session_reset, subagent_stop\n#\n# First-use consent: each (event, command) pair prompts once on a TTY, then\n# is persisted to ~/.hermes/shell-hooks-allowlist.json. Non-interactive\n# runs (gateway, cron) need --accept-hooks, HERMES_ACCEPT_HOOKS=1, or the\n# hooks_auto_accept key below.\n#\n# See website/docs/user-guide/features/hooks.md for the full JSON wire\n# protocol and worked examples.\n#\n# hooks:\n# pre_tool_call:\n# - matcher: \"terminal\"\n# command: \"~/.hermes/agent-hooks/block-rm-rf.sh\"\n# timeout: 10\n# post_tool_call:\n# - matcher: \"write_file|patch\"\n# command: \"~/.hermes/agent-hooks/auto-format.sh\"\n# pre_llm_call:\n# - command: \"~/.hermes/agent-hooks/inject-cwd-context.sh\"\n# subagent_stop:\n# - command: \"~/.hermes/agent-hooks/log-orchestration.sh\"\n#\n# hooks_auto_accept: false\n\n\n# =============================================================================\n# Update Behavior\n# =============================================================================\nupdates:\n # Create a full HERMES_HOME zip before every `hermes update`.\n # Backups land in ~/.hermes/backups/ and can be restored with `hermes import`.\n # Off by default because large homes can add minutes to every update.\n pre_update_backup: false\n\n # Number of pre-update backup zips to retain.\n backup_keep: 5\n\n # What non-interactive updates do with local source edits in the Hermes repo.\n # Interactive terminal updates always prompt before restoring the autostash.\n #\n # stash - auto-stash before pull, then auto-restore after success (default)\n # discard - drop the update-created stash after success; use only on managed\n # installs where local source edits should not persist\n non_interactive_local_changes: \"stash\"\n\n\n# =============================================================================\n# Web Dashboard\n# =============================================================================\n# OAuth gate configuration for `hermes dashboard --host <non-loopback>`.\n# The bundled Nous Portal plugin reads these on startup; settings here are\n# the canonical surface. Each can be overridden by an environment variable:\n#\n# dashboard.oauth.client_id <- HERMES_DASHBOARD_OAUTH_CLIENT_ID\n# dashboard.oauth.portal_url <- HERMES_DASHBOARD_PORTAL_URL\n# dashboard.public_url <- HERMES_DASHBOARD_PUBLIC_URL\n#\n# Env wins when set to a non-empty value. This is what Fly.io's platform-\n# secret injection uses to push per-deploy client_ids without needing to\n# bake a config.yaml into the image. Empty env values are treated as unset\n# so a provisioned-but-not-populated secret can't shadow a valid entry here.\n#\n# Local dev / on-prem deploys should typically set these via config.yaml\n# (the ~/.hermes/.env file is reserved for API keys and secrets).\n#\n# dashboard:\n# oauth:\n# client_id: \"\" # agent:{instance_id}; Portal provisions this at deploy\n# portal_url: \"\" # blank → default https://portal.nousresearch.com\n#\n# # Force the absolute base URL the OAuth callback (and any other public\n# # URL the dashboard hands to external systems) is built from. Set this\n# # for deploys behind reverse proxies that don't reliably forward\n# # X-Forwarded-Host / X-Forwarded-Proto / X-Forwarded-Prefix (manual\n# # nginx setups, on-prem ingresses, custom-domain Fly deploys without\n# # full proxy header chains).\n# #\n# # When set, the value is the complete authority: scheme + host +\n# # optional path prefix (e.g. \"https://example.com/hermes\"). The OAuth\n# # callback URL becomes \"<public_url>/auth/callback\" — X-Forwarded-Prefix\n# # is IGNORED on this code path because the operator has explicitly\n# # declared the public URL and we no longer need to guess.\n# #\n# # Leave empty to use the existing proxy-header reconstruction (the\n# # default — works on Fly.io out of the box).\n# #\n# # public_url: \"https://example.com/hermes\"\n#\n# -----------------------------------------------------------------------------\n# Self-hosted OIDC dashboard auth (generic OpenID Connect — Authentik,\n# Keycloak, Zitadel, Authelia, Auth0, Okta, Google, …). Use this INSTEAD of the\n# nous block above when gating the dashboard with your own identity provider.\n# Each setting can be overridden by an environment variable:\n#\n# dashboard.oauth.self_hosted.issuer <- HERMES_DASHBOARD_OIDC_ISSUER\n# dashboard.oauth.self_hosted.client_id <- HERMES_DASHBOARD_OIDC_CLIENT_ID\n# dashboard.oauth.self_hosted.scopes <- HERMES_DASHBOARD_OIDC_SCOPES\n# dashboard.oauth.self_hosted.client_secret <- HERMES_DASHBOARD_OIDC_CLIENT_SECRET\n#\n# dashboard:\n# oauth:\n# provider: self-hosted\n# self_hosted:\n# issuer: \"https://auth.example.com/application/o/hermes/\" # required\n# client_id: \"hermes-dashboard\" # required\n# scopes: \"openid profile email\" # optional\n#\n# # OPTIONAL — set ONLY if your IDP registered the client as\n# # *confidential* (Authentik / Keycloak often default to this). When\n# # set, Hermes authenticates the client at the token endpoint\n# # (client_secret_basic or client_secret_post, auto-selected from the\n# # IDP's discovery doc) IN ADDITION to PKCE. Leave unset for a public\n# # (PKCE-only) client — the common case.\n# #\n# # This is a CREDENTIAL: prefer setting HERMES_DASHBOARD_OIDC_CLIENT_SECRET\n# # in ~/.hermes/.env over putting it here in config.yaml.\n# # client_secret: \"\"\n\n# =============================================================================\n# External secret sources\n# =============================================================================\n# Pull provider credentials from external secret managers at process startup\n# instead of storing them in ~/.hermes/.env. Only the manager's bootstrap\n# credential (e.g. BWS_ACCESS_TOKEN / OP_SERVICE_ACCOUNT_TOKEN) lives in .env\n# (or your shell / desktop session); everything else rotates centrally.\n# Failures never block startup — Hermes warns once and continues with\n# whatever .env already had.\n#\n# Multiple sources can be enabled at once:\n# - \"mapped\" sources (explicit VAR -> ref bindings, e.g. 1Password's env:\n# map) beat \"bulk\" sources (whole-project dumps like Bitwarden BSM)\n# - within a shape, the first source to claim a var wins; later claims\n# are skipped with a startup warning (never a silent clobber)\n# - a source's override_existing lets it beat .env/shell values, but\n# never another secret source's claim\n# Docs: https://hermes-agent.nousresearch.com/docs/user-guide/secrets/\n#\n# secrets:\n# # Optional explicit ordering of enabled sources.\n# # sources: [onepassword, bitwarden]\n#\n# # ---- Bitwarden Secrets Manager (bws CLI) --------------------------------\n# bitwarden:\n# enabled: false\n# access_token_env: BWS_ACCESS_TOKEN # bootstrap token, sourced from .env\n# project_id: \"\" # UUID of the BSM project to sync\n# server_url: \"\" # \"\" = US Cloud; EU/self-hosted URL otherwise\n# cache_ttl_seconds: 300 # 0 disables fresh caching\n# encrypted_cache: # optional encrypted stale fallback\n# enabled: false\n# max_stale_seconds: 0 # 0 disables stale fallback\n# override_existing: true # BSM values win over existing env\n# auto_install: true # lazy-download bws into ~/.hermes/bin\n#\n# # ---- 1Password (op CLI) -------------------------------------------------\n# onepassword:\n# enabled: false\n# # Map env-var names to op:// secret references. Each is resolved with a\n# # single `op read` at startup.\n# env:\n# OPENAI_API_KEY: \"op://Private/OpenAI/api key\"\n# ANTHROPIC_API_KEY: \"op://Private/Anthropic/credential\"\n# account: \"\" # op --account shorthand; \"\" = default\n# service_account_token_env: OP_SERVICE_ACCOUNT_TOKEN # headless auth; unset = desktop session\n# binary_path: \"\" # \"\" = resolve op via PATH; else absolute path\n# cache_ttl_seconds: 300 # 0 disables BOTH cache layers\n# override_existing: true # resolved values win over existing env\n#\n# # ---- Command helper (any CLI vault) --------------------------------------\n# # Run a user-configured helper that prints KEY=VALUE lines on stdout —\n# # works with any secret store that has a CLI: keepassxc-cli, secret-tool,\n# # pass, gpg, or a script that cats a tmpfs env file. Composes with the\n# # sources above (enable any combination). POSIX-only (needs /bin/sh).\n# # The helper must be fast and NON-interactive (hard timeout, 1 MiB cap);\n# # its stderr is discarded so diagnostics can't leak secret material.\n# command:\n# enabled: false\n# command: \"cat /run/user/1000/hermes-secrets.env\"\n# helper_timeout_seconds: 3\n# override_existing: false # .env/shell win by default\n"}, "761fce7f79c274b7e4da1bf782b22169ee78b02c": {"t": 1785768029.1850488, "n": 80023, "h": "eaca5aeb4d30938d", "c": "# Hermes Agent CLI Configuration\n# Copy this file to cli-config.yaml and customize as needed.\n# This file configures the CLI behavior. Environment variables in .env take precedence.\n\n# =============================================================================\n# Model Configuration\n# =============================================================================\nmodel:\n # Default model to use (can be overridden with --model flag)\n # Both \"default\" and \"model\" work as the key name here.\n default: \"anthropic/claude-opus-4.6\"\n \n # Inference provider selection:\n # \"auto\" - Auto-detect from credentials (default)\n # \"openrouter\" - OpenRouter (requires: OPENROUTER_API_KEY or OPENAI_API_KEY)\n # \"nous\" - Nous Portal OAuth (requires: hermes auth add nous)\n # \"nous-api\" - Nous Portal API key (requires: NOUS_API_KEY)\n # \"anthropic\" - Direct Anthropic API (requires: ANTHROPIC_API_KEY)\n # \"openai-codex\" - OpenAI Codex (requires: hermes auth)\n # \"copilot\" - GitHub Copilot / GitHub Models (requires: GITHUB_TOKEN)\n # \"gemini\" - Use Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n # \"zai\" - Use z.ai / ZhipuAI GLM models (requires: GLM_API_KEY)\n # \"kimi-coding\" - Kimi / Moonshot AI (requires: KIMI_API_KEY)\n # \"minimax\" - MiniMax global (requires: MINIMAX_API_KEY)\n # \"minimax-cn\" - MiniMax China (requires: MINIMAX_CN_API_KEY)\n # \"huggingface\" - Hugging Face Inference (requires: HF_TOKEN)\n # \"nvidia\" - NVIDIA NIM / build.nvidia.com (requires: NVIDIA_API_KEY)\n # \"xiaomi\" - Xiaomi MiMo (requires: XIAOMI_API_KEY)\n # \"arcee\" - Arcee AI Trinity models (requires: ARCEEAI_API_KEY)\n # \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY — https://ollama.com/settings)\n # \"deepinfra\" - DeepInfra (requires: DEEPINFRA_API_KEY)\n # \"kilocode\" - KiloCode gateway (requires: KILOCODE_API_KEY)\n # \"azure-foundry\" - Microsoft Foundry / Azure OpenAI (API key or Entra ID)\n # \"lmstudio\" - LM Studio local server (optional: LM_API_KEY, defaults to http://127.0.0.1:1234/v1)\n #\n # Local servers (LM Studio, Ollama, vLLM, llama.cpp):\n # \"custom\" - Any other OpenAI-compatible endpoint. Set base_url below.\n # Aliases: \"ollama\", \"vllm\", \"llamacpp\" all map to \"custom\".\n # LM Studio is first-class and uses provider: \"lmstudio\".\n # It works with both no-auth and auth-enabled server modes.\n #\n # Can also be overridden for a single invocation with the --provider flag.\n provider: \"auto\"\n \n # API configuration (falls back to OPENROUTER_API_KEY env var)\n # api_key: \"your-key-here\" # Uncomment to set here instead of .env\n base_url: \"https://openrouter.ai/api/v1\"\n\n # Azure Foundry keyless auth example:\n # provider: \"azure-foundry\"\n # base_url: \"https://<resource>.openai.azure.com/openai/v1\"\n # auth_mode: \"entra_id\" # DefaultAzureCredential: az login, managed identity, workload identity, etc.\n # default: \"gpt-4o\" # Deployment/model name\n # entra:\n # scope: \"https://ai.azure.com/.default\" # Optional; this is the default.\n\n # ── Token limits — two settings, easy to confuse ──────────────────────────\n #\n # context_length: TOTAL context window (input + output tokens combined).\n # Controls when Hermes compresses history and validates requests.\n # Leave unset — Hermes auto-detects the correct value from the provider.\n # Set manually only when auto-detection is wrong (e.g. a local server with\n # a custom num_ctx, or a proxy that doesn't expose /v1/models).\n #\n # context_length: 131072\n #\n # max_tokens: OUTPUT cap — maximum tokens the model may generate per response.\n # Unrelated to how long your conversation history can be.\n # The OpenAI-standard name \"max_tokens\" is a misnomer; Anthropic's native\n # API has since renamed it \"max_output_tokens\" for clarity.\n # Leave unset to use the model's native output ceiling (recommended).\n # Set only if you want to deliberately limit individual response length.\n #\n# max_tokens: 8192\n\n # ── Custom request headers (optional) ─────────────────────────────────────\n #\n # default_headers: extra HTTP headers sent on every request to an\n # OpenAI-compatible endpoint. User values take precedence over the\n # provider/SDK defaults, so this is the supported way to override the\n # OpenAI Python SDK's identifying headers (User-Agent: OpenAI/Python ...,\n # X-Stainless-*) when a custom provider sits behind a gateway/WAF that\n # rejects them — e.g. an upstream that returns \"502 Upstream access\n # forbidden\" for the SDK default User-Agent but accepts a plain one.\n # Applies on the OpenAI wire only (not native Anthropic / Bedrock).\n #\n # default_headers:\n # User-Agent: \"curl/8.7.1\"\n #\n # extra_headers: accepted as an alias of default_headers (merged, with\n # extra_headers winning when both are set) — matches the per-provider\n # extra_headers key below.\n #\n # Per-provider variant: named providers / custom_providers entries accept an\n # extra_headers dict scoped to that endpoint only — for reverse proxies,\n # gateways, or custom auth (e.g. Cloudflare Access service tokens).\n # Merged onto SDK/provider defaults with the entry's values winning.\n # Header values are treated as secrets and are never logged.\n #\n # providers:\n # my-proxy:\n # base_url: \"https://llm.internal.example.com/v1\"\n # key_env: \"MY_PROXY_API_KEY\"\n # extra_headers:\n # CF-Access-Client-Id: \"xxxx.access\"\n # CF-Access-Client-Secret: \"${CF_ACCESS_SECRET}\"\n # X-Client-Name: \"hermes-agent\"\n\n# Named provider overrides (optional)\n# Use this for per-provider request timeouts, non-stream stale timeouts,\n# and per-model exceptions.\n# Applies to the primary turn client on every api_mode (OpenAI-wire, native\n# Anthropic, and Anthropic-compatible providers), the fallback chain, and\n# client rebuilds during credential rotation. For OpenAI-wire chat\n# completions (streaming and non-streaming) the configured value is also\n# used as the per-request ``timeout=`` kwarg so it wins over the legacy\n# HERMES_API_TIMEOUT env var (which still applies when no config is set).\n# ``stale_timeout_seconds`` controls the non-streaming stale-call detector and\n# wins over the legacy HERMES_API_CALL_STALE_TIMEOUT env var. Leaving these\n# unset keeps the legacy defaults (HERMES_API_TIMEOUT=1800s,\n# HERMES_API_CALL_STALE_TIMEOUT=90s, native Anthropic 900s). The\n# implicit non-stream stale detector is auto-disabled for local endpoints\n# and can scale upward for very large contexts.\n#\n# Not currently wired for AWS Bedrock (bedrock_converse + AnthropicBedrock\n# SDK paths) — those use boto3 with its own timeout configuration.\n#\n# providers:\n# ollama-local:\n# request_timeout_seconds: 300 # Longer timeout for local cold-starts\n# stale_timeout_seconds: 900 # Explicitly re-enable stale detection on local endpoints\n# anthropic:\n# request_timeout_seconds: 30 # Fast-fail cloud requests\n# models:\n# claude-opus-4.6:\n# timeout_seconds: 600 # Longer timeout for extended-thinking Opus calls\n# openai-codex:\n# models:\n# gpt-5.4:\n# stale_timeout_seconds: 1800 # Longer non-stream stale timeout for slow large-context turns\n\n# =============================================================================\n# OpenRouter Provider Routing (only applies when using OpenRouter)\n# =============================================================================\n# Control how requests are routed across providers on OpenRouter.\n# See: https://openrouter.ai/docs/guides/routing/provider-selection\n#\n# provider_routing:\n# # Sort strategy: \"price\" (default), \"throughput\", or \"latency\"\n# # Append :nitro to model name for a shortcut to throughput sorting.\n# sort: \"throughput\"\n#\n# # Only allow these providers (provider slugs from OpenRouter)\n# # only: [\"anthropic\", \"google\"]\n#\n# # Skip these providers entirely\n# # ignore: [\"deepinfra\", \"fireworks\"]\n#\n# # Try providers in this order (overrides default load balancing)\n# # order: [\"anthropic\", \"google\", \"together\"]\n#\n# # Require providers to support all parameters in your request\n# # require_parameters: true\n#\n# # Data policy: \"allow\" (default) or \"deny\" to exclude providers that may store data\n# # data_collection: \"deny\"\n\n# =============================================================================\n# OpenRouter Response Caching (only applies when using OpenRouter)\n# =============================================================================\n# Cache identical API responses at the OpenRouter edge for free instant replays.\n# When enabled, identical requests (same model, messages, parameters) return\n# cached responses with zero billing. Separate from Anthropic prompt caching.\n# See: https://openrouter.ai/docs/guides/features/response-caching\n#\n# openrouter:\n# response_cache: true # Enable response caching (default: true)\n# response_cache_ttl: 300 # Cache TTL in seconds, 1-86400 (default: 300)\n\n# =============================================================================\n# Git Worktree Isolation\n# =============================================================================\n# When enabled, each CLI session creates an isolated git worktree so multiple\n# agents can work on the same repo concurrently without file collisions.\n# Equivalent to always passing --worktree / -w on the command line.\n#\n# worktree: true # Always create a worktree when in a git repo\n# worktree: false # Default — only create when -w flag is passed\n#\n# By default a new worktree branches from the freshly-fetched remote tip\n# (the current branch's upstream, else the remote's default branch) so it\n# starts current with the project instead of from the local clone's\n# (possibly stale) HEAD. Set worktree_sync: false to branch from local HEAD\n# instead — useful when offline or when you deliberately want the clone's\n# exact current state as the base.\n#\n# worktree_sync: true # Default — branch from the fetched remote tip\n# worktree_sync: false # Branch from local HEAD (offline / pinned base)\n\n# =============================================================================\n# Terminal Tool Configuration\n# =============================================================================\n# Choose ONE of the following terminal configurations by uncommenting it.\n# The terminal tool executes commands in the specified environment.\n\n# -----------------------------------------------------------------------------\n# OPTION 1: Local execution (default)\n# Commands run directly on your machine in the current directory\n# -----------------------------------------------------------------------------\n# Working directory behavior:\n# - CLI (`hermes` command): Uses \".\" (current directory where you run hermes)\n# - Gateway/messaging/cron: Uses terminal.cwd here; legacy .env cwd values are deprecated\nterminal:\n backend: \"local\"\n cwd: \".\" # For local backend: \".\" = current directory. Ignored for remote backends unless a backend documents otherwise.\n timeout: 180\n # HOME policy for tool subprocesses:\n # auto - default: host uses your real HOME; containers use HERMES_HOME/home\n # real - force your real OS-user HOME\n # profile - force HERMES_HOME/home for strict per-profile CLI config isolation\n home_mode: \"auto\"\n docker_mount_cwd_to_workspace: false # SECURITY: off by default. Opt in to mount the launch cwd into Docker /workspace.\n lifetime_seconds: 300\n # sudo_password: \"hunter2\" # Optional: pipe a sudo password via sudo -S. SECURITY WARNING: plaintext.\n # sudo_password: \"\" # Explicit empty password: try empty and never open the interactive sudo prompt.\n\n# -----------------------------------------------------------------------------\n# OPTION 2: SSH remote execution\n# Commands run on a remote server - agent code stays local (sandboxed)\n# Great for: keeping agent isolated from its own code, using powerful remote hardware\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"ssh\"\n# cwd: \"/home/myuser/project\" # Path on the REMOTE server\n# timeout: 180\n# lifetime_seconds: 300\n# ssh_host: \"my-server.example.com\"\n# ssh_user: \"myuser\"\n# ssh_port: 22\n# ssh_key: \"~/.ssh/id_rsa\" # Optional - uses ssh-agent if not specified\n\n# -----------------------------------------------------------------------------\n# OPTION 3: Docker container\n# Commands run in an isolated Docker container\n# Great for: reproducible environments, testing, isolation\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"docker\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /)\n# timeout: 180\n# lifetime_seconds: 300\n# docker_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# docker_mount_cwd_to_workspace: true # Explicit opt-in: mount your launch cwd into /workspace\n# # Optional: run the container as your host user's uid:gid so files written\n# # into bind-mounted dirs are owned by you, not root. Drops SETUID/SETGID\n# # caps too since no gosu privilege drop is needed. Leave off if your\n# # chosen docker_image expects to start as root.\n# docker_run_as_host_user: true\n# # Optional: explicitly forward selected env vars into Docker.\n# # These values come from your current shell first, then ~/.hermes/.env.\n# # Warning: anything forwarded here is visible to commands run in the container.\n# docker_forward_env:\n# - \"GITHUB_TOKEN\"\n# - \"NPM_TOKEN\"\n# # Optional: extra flags passed verbatim to docker run (appended after security defaults).\n# # Useful for adding capabilities (e.g. apt installs needing SETUID) or custom options.\n# # Example: add a Linux capability not included by default\n# # docker_extra_args:\n# # - \"--cap-add\"\n# # - \"SETUID\"\n\n# -----------------------------------------------------------------------------\n# OPTION 4: Singularity/Apptainer container\n# Commands run in a Singularity container (common in HPC environments)\n# Great for: HPC clusters, shared compute environments\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"singularity\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# singularity_image: \"docker://nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 5: Modal cloud execution\n# Commands run on Modal's cloud infrastructure\n# Great for: GPU access, scalable compute, serverless execution\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"modal\"\n# cwd: \"/workspace\" # Path INSIDE the sandbox (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# modal_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 6: Daytona cloud execution\n# Commands run in Daytona cloud sandboxes\n# Great for: Cloud dev environments, persistent workspaces, team collaboration\n# Requires: pip install daytona, DAYTONA_API_KEY env var\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"daytona\"\n# cwd: \"~\"\n# timeout: 180\n# lifetime_seconds: 300\n# daytona_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# container_disk: 10240 # Daytona max is 10GB per sandbox\n\n#\n# --- Container resource limits (docker, singularity, modal, daytona -- ignored for local/ssh) ---\n# These settings apply to all container backends. They control the resources\n# allocated to the sandbox and whether its filesystem persists across sessions.\n container_cpu: 1 # CPU cores\n container_memory: 5120 # Memory in MB (5120 = 5GB)\n container_disk: 51200 # Disk in MB (51200 = 50GB)\n container_persistent: true # Persist filesystem across sessions (false = ephemeral)\n\n# -----------------------------------------------------------------------------\n# SUDO SUPPORT (works with ALL backends above)\n# -----------------------------------------------------------------------------\n# Add sudo_password to any terminal config above to enable sudo commands.\n# The password is piped via `sudo -S`. Works with local, ssh, docker, etc.\n#\n# SECURITY WARNING: Password stored in plaintext!\n#\n# INTERACTIVE PROMPT: If sudo_password is unset and the CLI is running,\n# you'll be prompted to enter your password when sudo is needed:\n# - 45-second timeout (auto-skips if no input)\n# - Press Enter to skip (command fails gracefully)\n# - Password is hidden while typing\n# - Password is cached for the session\n#\n# EMPTY PASSWORDS: Setting sudo_password to an explicit empty string is different\n# from leaving it unset. Hermes will try an empty password via `sudo -S` and\n# will not open the interactive prompt. This is useful for passwordless sudo,\n# Touch ID sudo setups, and environments where prompting is just noise.\n#\n# ALTERNATIVES:\n# - SSH backend: Configure passwordless sudo on the remote server\n# - Containers: Run as root inside the container (no sudo needed)\n# - Local: Configure /etc/sudoers for specific commands\n#\n# Example (add to your terminal section):\n# sudo_password: \"your-password-here\"\n\n# =============================================================================\n# Security Scanning (tirith)\n# =============================================================================\n# Optional pre-exec command security scanning via tirith.\n# Detects homograph URLs, pipe-to-shell, terminal injection, env manipulation.\n# Install: brew install sheeki03/tap/tirith\n# Docs: https://github.com/sheeki03/tirith\n#\n# security:\n# tirith_enabled: true # Enable/disable tirith scanning\n# tirith_path: \"tirith\" # Path to tirith binary (supports ~ expansion)\n# tirith_timeout: 5 # Scan timeout in seconds\n# tirith_fail_open: true # Allow commands if tirith unavailable\n\n# =============================================================================\n# Browser Tool Configuration\n# =============================================================================\nbrowser:\n # Inactivity timeout in seconds - browser sessions are automatically closed\n # after this period of no activity between agent loops (default: 120 = 2 minutes)\n inactivity_timeout: 120\n\n# =============================================================================\n# Tool Loop Guardrails\n# =============================================================================\n# Soft warnings are enabled by default. They append guidance to repeated failed\n# or non-progressing tool results but still let the tool execute. Hard stops are\n# opt-in circuit breakers for autonomous/cron sessions where stopping a loop is\n# preferable to spending the full iteration budget.\ntool_loop_guardrails:\n warnings_enabled: true\n hard_stop_enabled: false\n warn_after:\n exact_failure: 2\n same_tool_failure: 3\n idempotent_no_progress: 2\n hard_stop_after:\n exact_failure: 5\n same_tool_failure: 8\n idempotent_no_progress: 5\n\n# =============================================================================\n# Context Compression (Auto-shrinks long conversations)\n# =============================================================================\n# When conversation approaches model's context limit, middle turns are\n# automatically summarized to free up space while preserving important context.\n#\n# HOW IT WORKS:\n# 1. Tracks actual token usage from API responses (not estimates)\n# 2. When prompt_tokens >= threshold% of model's context_length, triggers compression\n# 3. Protects first 3 turns (system prompt, initial request, first response)\n# 4. Protects last N turns (default 20 messages = ~10 full turns of recent context)\n# 5. Summarizes middle turns using a fast/cheap model\n# 6. Inserts summary as a user message, continues conversation seamlessly\n#\n# Post-compression tail budget is target_ratio × threshold × context_length:\n# 200K context, threshold 0.50, ratio 0.20 → 20K tokens of recent tail preserved\n# 1M context, threshold 0.50, ratio 0.20 → 100K tokens of recent tail preserved\n#\ncompression:\n # Enable automatic context compression (default: true)\n # Set to false if you prefer to manage context manually or want errors on overflow\n enabled: true\n \n # Trigger compression at this % of model's context limit (default: 0.50 = 50%)\n # Lower values = more aggressive compression, higher values = compress later\n # Models with context windows below 512K are floored at 0.75 (raise-only) so\n # compaction doesn't fire with half the window still free; set above 0.75 to override.\n threshold: 0.50\n\n # Per-model threshold overrides: keys are substring-matched against the model\n # name (longest match wins). Useful when some models need different compaction\n # points — e.g. a 1M-context model can compress later (0.30) while a 128K\n # model needs to compress earlier (0.60). The small-context floor (75% for\n # <512K models) still applies on top of per-model overrides.\n # model_thresholds:\n # \"glm-5.2\": 0.40\n # \"claude-sonnet\": 0.35\n # \"gpt-5\": 0.30\n\n # Optional absolute token cap for the compression trigger (default: null = disabled).\n # When set, compression fires at the LOWER of the ratio-based threshold and this\n # absolute token count — first-fires-wins. It never fires later than this count\n # regardless of which model is active (useful when switching between models with\n # very different context windows). Clamped to the model's context length at\n # apply-time, so a cap above the window is a no-op (ratio-based threshold wins).\n # Survives model switches and fallback activations.\n # threshold_tokens: 200000\n\n # Existing Codex gpt-5.5 behavior: raise Hermes' compaction trigger to 85%\n # for the ChatGPT Codex OAuth route. Set false to opt back down to threshold.\n codex_gpt55_autoraise: true\n\n # Fraction of the threshold to preserve as recent tail (default: 0.20 = 20%)\n # e.g. 20% of 50% threshold = 10% of total context kept as recent messages.\n # Summary output is separately capped at 12K tokens (Gemini output limit).\n # Range: 0.10 - 0.80\n target_ratio: 0.20\n\n # Number of most-recent messages to always preserve (default: 20 ≈ 10 full turns)\n # Higher values keep more recent conversation intact at the cost of more aggressive\n # compression of older turns.\n protect_last_n: 20\n\n # Compression retry rounds before a turn gives up with \"max compression\n # attempts reached\" (default: 3, same as the previous hardcoded value).\n # Raise (e.g. 6) for tool-schema-heavy sessions where 3 rounds cannot bring\n # the request estimate under the threshold. Validated >= 1, hard cap 10.\n max_attempts: 3\n\n # Codex app-server (codex CLI runtime) thread-compaction mode. The codex\n # agent owns the real thread context on this runtime, so Hermes' summarizer\n # cannot shrink it — compaction goes through the app server instead.\n # native = let Codex decide when to compact its own thread (default)\n # hermes = let Hermes threshold trigger Codex thread/compact/start\n # off = Hermes will not auto-trigger compaction; Codex may still compact natively\n codex_app_server_auto: native\n\n # Number of non-system messages to protect at the head of the transcript, in\n # ADDITION to the system prompt (which is always implicitly protected).\n # Head messages are NEVER summarized — they survive every compression\n # indefinitely. This gives stable early context for short/medium sessions,\n # but in long-running sessions that rely on rolling compaction the pinned\n # opening turns may not match how you want the session framed over time.\n # Set to 0 to preserve ONLY the system prompt (plus the rolling summary\n # and recent tail) — the cleanest configuration for long-running sessions.\n # Default 3 preserves the system prompt plus the first three non-system\n # head messages, matching the pre-feature behaviour.\n protect_first_n: 3\n\n # To pin a specific model/provider for compression summaries, use the\n # auxiliary section below (auxiliary.compression.provider / model).\n\n# =============================================================================\n# Anthropic prompt caching TTL\n# =============================================================================\n# When prompt caching is active (Claude via OpenRouter or native Anthropic),\n# Anthropic supports two TTL tiers for cached prefixes: \"5m\" (default) and\n# \"1h\". Other values are ignored and \"5m\" is used.\n#\nprompt_caching:\n cache_ttl: \"5m\" # use \"1h\" for long sessions with pauses between turns\n\n# =============================================================================\n# Auxiliary Models (Advanced — Experimental)\n# =============================================================================\n# Hermes uses lightweight \"auxiliary\" models for side tasks: image analysis,\n# browser screenshot analysis, web page summarization, TTS audio-tag insertion,\n# and context compression.\n#\n# By default these use Gemini Flash via OpenRouter or Nous Portal and are\n# auto-detected from your credentials. You do NOT need to change anything\n# here for normal usage.\n#\n# WARNING: Overriding these with providers other than OpenRouter or Nous Portal\n# is EXPERIMENTAL and may not work. Not all models/providers support vision,\n# produce usable summaries, or accept the same API format. Change at your own\n# risk — if things break, reset to \"auto\" / empty values.\n#\n# Each task has its own provider + model pair so you can mix providers.\n# For example: OpenRouter for vision (needs multimodal), but your main\n# local endpoint for compression (just needs text).\n#\n# Provider options:\n# \"auto\" - Best available: OpenRouter → Nous Portal → main endpoint (default)\n# \"openrouter\" - Force OpenRouter (requires OPENROUTER_API_KEY)\n# \"nous\" - Force Nous Portal (requires: hermes auth add nous)\n# \"gemini\" - Force Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n# \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY)\n# \"codex\" - Force Codex OAuth (requires: hermes model → Codex).\n# Uses gpt-5.3-codex which supports vision.\n# \"main\" - Use your custom endpoint (OPENAI_BASE_URL + OPENAI_API_KEY).\n# Works with OpenAI API, local models, or any OpenAI-compatible\n# endpoint. Also falls back to Codex OAuth and API-key providers.\n#\n# Model: leave empty to use the provider's default. When empty, OpenRouter\n# uses \"google/gemini-3-flash-preview\" and Nous uses \"gemini-3-flash\".\n# Other providers pick a sensible default automatically.\n#\n# auxiliary:\n# # Image analysis: vision_analyze tool + browser screenshots\n# vision:\n# provider: \"auto\"\n# model: \"\" # e.g. \"google/gemini-2.5-flash\", \"openai/gpt-4o\"\n# timeout: 30 # LLM API call timeout (seconds)\n# download_timeout: 30 # Image HTTP download timeout (seconds)\n# # Increase for slow connections or self-hosted image servers\n# reasoning_effort: \"\" # Per-task thinking level: none, minimal, low, medium,\n# # high, xhigh, max, ultra. Empty = provider default.\n# # Works on every auxiliary task block (vision,\n# # web_extract, compression, title_generation, curator,\n# # background_review, moa_reference, ...). Example: run\n# # compression at \"low\" and vision at \"none\" to cut\n# # side-task latency/cost on reasoning models.\n#\n# # Web page scraping / summarization + browser page text extraction\n# web_extract:\n# provider: \"auto\"\n# model: \"\"\n# reasoning_effort: \"low\"\n#\n# # Gemini 3.1 TTS hidden audio-tag insertion\n# tts_audio_tags:\n# provider: \"auto\" # empty model = your main chat model\n# model: \"\"\n# timeout: 30\n#\n# # Automatic session title generation after the first exchange\n# title_generation:\n# enabled: true # set false to disable auto-title generation\n# provider: \"auto\"\n# model: \"\"\n# timeout: 30\n# language: \"\" # empty = match the user's language; or e.g. \"English\"\n#\n# # Session search — summarizes matching past sessions\n# session_search:\n# provider: \"auto\"\n# model: \"\"\n# timeout: 30\n# max_concurrency: 3 # Limit parallel summaries to reduce request-burst 429s\n# extra_body: {} # Provider-specific OpenAI-compatible request fields\n# # Example for providers that support request-body\n# # reasoning controls:\n# # extra_body:\n# # enable_thinking: false\n# # Some vLLM/Qwen deployments expect this nested:\n# # extra_body:\n# # chat_template_kwargs:\n# # enable_thinking: false\n\n# =============================================================================\n# Persistent Memory\n# =============================================================================\n# Bounded curated memory injected into the system prompt every session.\n# Two stores: MEMORY.md (agent's notes) and USER.md (user profile).\n# Character limits keep the memory small and focused. The agent manages\n# pruning -- when at the limit, it must consolidate or replace entries.\n# Disabled by default in batch_runner.\n#\nmemory:\n # Agent's personal notes: environment facts, conventions, things learned\n memory_enabled: true\n \n # User profile: preferences, communication style, expectations\n user_profile_enabled: true\n \n # Character limits (~2.75 chars per token, model-independent)\n memory_char_limit: 2200 # ~800 tokens\n user_char_limit: 1375 # ~500 tokens\n\n # Periodic memory nudge: remind the agent to consider saving memories\n # every N user turns. Set to 0 to disable. Only active when memory is enabled.\n nudge_interval: 10 # Nudge every 10 user turns (0 = disabled)\n\n # Memory flush: give the agent one turn to save memories before context is\n # lost (compression, /new, /reset, exit). Set to 0 to disable.\n # For exit/reset, only fires if the session had at least this many user turns.\n flush_min_turns: 6 # Min user turns to trigger flush on exit/reset (0 = disabled)\n\n# =============================================================================\n# Session Reset Policy (Messaging Platforms)\n# =============================================================================\n# Controls when messaging sessions (Telegram, Discord, WhatsApp, Slack) are\n# automatically cleared. Default is \"none\": sessions never auto-reset —\n# conversation context lives until you /reset or /new manually, or context\n# compression kicks in. Opt in to automatic resets if you prefer sessions to\n# clear on a schedule (long-lived context increases API cost per message,\n# though prompt caching and compression keep this manageable).\n#\n# When an automatic reset triggers, the agent first saves important\n# information to its persistent memory — but the conversation context is\n# wiped. The agent starts fresh but retains learned facts via its memory\n# system.\n#\n# Modes:\n# \"none\" - Never auto-reset (default); context lives until /reset or compression\n# \"idle\" - Reset after N minutes of inactivity\n# \"daily\" - Reset at a fixed hour each day\n# \"both\" - Reset on EITHER inactivity timeout or daily boundary\n#\nsession_reset:\n mode: none # \"none\", \"idle\", \"daily\", or \"both\"\n idle_minutes: 1440 # Inactivity timeout in minutes (used by \"idle\"/\"both\")\n at_hour: 4 # Daily reset hour, 0-23 local time (used by \"daily\"/\"both\")\n\n# Maximum number of simultaneously active chat sessions across CLI, TUI,\n# dashboard chat, and messaging gateway. Set to null, 0, or omit to allow\n# unlimited concurrent sessions. When the limit is reached, new sessions get a\n# clean error while existing active sessions keep their normal behavior. This\n# top-level key takes precedence over gateway.max_concurrent_sessions. The cap\n# is a best-effort single-host/profile runtime guard; Hermes fails open if the\n# local runtime lease registry cannot be read or locked.\nmax_concurrent_sessions: null\n\n# When true, group/channel chats use one session per participant when the platform\n# provides a user ID. This is the secure default and prevents users in the same\n# room from sharing context, interrupts, and token costs. Set false only if you\n# explicitly want one shared \"room brain\" per group/channel.\ngroup_sessions_per_user: true\n\n# ─────────────────────────────────────────────────────────────────────────────\n# API Server — per-client model routing\n# ─────────────────────────────────────────────────────────────────────────────\n# Route different API clients to different models/providers on a single\n# Hermes deployment. Clients choose a backend by sending a specific string\n# as the OpenAI ``model`` field. Unmapped model values fall back to the\n# global model configured in the ``model:`` section above, and an explicit\n# session /model override always wins over a route.\n#\n# Configure via the ``platforms.api_server.extra.model_routes`` gateway\n# config block:\n#\n# platforms:\n# api_server:\n# enabled: true\n# extra:\n# key: \"your-api-server-secret\"\n# model_routes:\n# # Xiaozhi clients send model=\"minimax-m2\" → routed to MiniMax via OpenRouter\n# minimax-m2:\n# model: \"minimax/minimax-m1\"\n# provider: \"openrouter\" # optional — overrides global provider\n# # api_key: \"sk-...\" # optional — per-route UPSTREAM provider\n# # key (NOT caller auth; never logged)\n# # base_url: \"https://...\" # optional — per-route base URL\n# # GPT clients keep their own alias\n# gpt-5:\n# model: \"openai/gpt-5\"\n# provider: \"openrouter\"\n#\n# Configured aliases are automatically listed by GET /v1/models so clients\n# can discover them without manual coordination. Caller authentication is\n# unchanged: every request still authenticates with the global API server\n# key (``extra.key`` / API_SERVER_KEY).\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Gateway Streaming\n# ─────────────────────────────────────────────────────────────────────────────\n# Stream tokens to messaging platforms in real-time. The bot sends a message\n# on first token, then progressively edits it as more tokens arrive.\n# Disabled by default — enable to try the streaming UX on Telegram/Discord/Slack.\n# For Telegram, partial edits are sent as plain text and only the final edit uses MarkdownV2.\nstreaming:\n enabled: false\n # transport: edit # \"edit\" = progressive editMessageText\n # edit_interval: 0.3 # seconds between message edits\n # buffer_threshold: 40 # chars before forcing an edit flush\n # cursor: \" ▉\" # cursor shown during streaming\n\n# =============================================================================\n# Skills Configuration\n# =============================================================================\n# Skills are reusable procedures the agent can load and follow. The agent can\n# also create new skills after completing complex tasks.\n#\nskills:\n # Nudge the agent to create skills after complex tasks.\n # Every N tool-calling iterations, remind the model to consider saving a skill.\n # Set to 0 to disable.\n creation_nudge_interval: 15\n\n # External skill directories — share skills across tools/agents without\n # copying them into ~/.hermes/skills/. Each path is expanded (~ and ${VAR})\n # and resolved to an absolute path. External dirs are read-only: skill\n # creation always writes to ~/.hermes/skills/. Local skills take precedence\n # when names collide.\n # external_dirs:\n # - ~/.agents/skills\n # - /home/shared/team-skills\n\n# =============================================================================\n# Agent Behavior\n# =============================================================================\nagent:\n # Maximum tool-calling iterations per conversation\n # Higher = more room for complex tasks, but costs more tokens\n # Recommended: 20-30 for focused tasks, 50-100 for open exploration\n max_turns: 60\n\n # Inactivity timeout for gateway agent runs (seconds, 0 = unlimited).\n # The agent can run indefinitely when actively calling tools or receiving\n # API responses. Only fires after the agent has been idle for this duration.\n # gateway_timeout: 1800\n\n # Staged warning: send a warning before escalating to full timeout.\n # Fires once per run when inactivity reaches this threshold (seconds).\n # Set to 0 to disable the warning.\n # gateway_timeout_warning: 900\n\n # Graceful drain timeout for gateway stop/restart (seconds).\n # Default 0 = no drain: a restart interrupts in-flight agents immediately,\n # cleans up, and exits. Set a positive value only if you want a grace\n # window on /restart, and keep it well under systemd's TimeoutStopSec.\n # restart_drain_timeout: 0\n\n # Max app-level retry attempts for API errors (connection drops, provider\n # timeouts, 5xx, etc.) before the agent surfaces the failure. Lower this\n # to 1 if you use fallback providers and want fast failover on flaky\n # primaries (default 3). The OpenAI SDK does its own low-level retries\n # underneath this wrapper — this is the Hermes-level loop.\n # api_max_retries: 3\n\n # After the agent edits code without fresh passing verification, nudge it to\n # verify before finishing. The default \"auto\" enables it on interactive\n # coding surfaces (CLI, TUI, desktop) and programmatic callers, and disables\n # it on conversational messaging surfaces (Telegram, Discord, etc.) where the\n # verification summary would reach a human as chat noise. Set true or false to\n # force it on or off; the HERMES_VERIFY_ON_STOP env var (1/0) takes precedence.\n # verify_on_stop: auto\n\n # Standing operator instructions for the coding posture (when Hermes is in a\n # code workspace). Appended to the coding brief as an extra system block, so\n # you can pin project-wide workflow rules without editing the shipped brief.\n # Accepts a string or a list of strings. Takes effect next session.\n # coding_instructions:\n # - \"For UI work, don't run tsc/lint until I approve the look.\"\n # - \"Clean the diff before you commit and push.\"\n\n # When verify-on-stop finds edited code without fresh verification evidence,\n # append guidance for creative UI work (avoid broad tsc/lint/test before visual\n # approval) and clean-diff expectations. Set false to keep that nudge terse.\n # verify_guidance: true\n\n # A `pre_verify` hook (plugin or shell, see Event Hooks docs) can keep the\n # agent going one more turn to verify/clean before finishing. This caps how\n # many times one turn may be nudged to continue, so a hook can't trap the loop.\n # Default 3.\n # max_verify_nudges: 3\n\n # Enable verbose logging\n verbose: false\n \n # Reasoning effort level (OpenRouter and Nous Portal)\n # Controls how much \"thinking\" the model does before responding.\n # Options: \"xhigh\" (max), \"high\", \"medium\", \"low\", \"minimal\", \"none\" (disable)\n reasoning_effort: \"medium\"\n \n # Per-model reasoning effort overrides (optional dict)\n # Key: any sensible model spelling works (exact, dots↔dashes interchangeable,\n # provider prefix optional). First match wins.\n # Value: reasoning effort level (same options as reasoning_effort)\n # Override the global reasoning_effort for that specific model.\n # NOTE: no `hermes config set` support for this key -- edit YAML directly.\n # reasoning_overrides:\n # \"openrouter/anthropic/claude-opus-4.5\": \"xhigh\"\n # \"openai/gpt-5\": \"low\"\n # \"claude-opus-4.6\": \"high\" # bare model name also works\n # \"deepseek/deepseek-v4-pro\": \"xhigh\" # dots and dashes are interchangeable\n reasoning_overrides: {}\n \n # Predefined personalities (use with /personality command)\n personalities:\n helpful: \"You are a helpful, friendly AI assistant.\"\n concise: \"You are a concise assistant. Keep responses brief and to the point.\"\n technical: \"You are a technical expert. Provide detailed, accurate technical information.\"\n creative: \"You are a creative assistant. Think outside the box and offer innovative solutions.\"\n teacher: \"You are a patient teacher. Explain concepts clearly with examples.\"\n kawaii: \"You are a kawaii assistant! Use cute expressions like (◕‿◕), ★, ♪, and ~! Add sparkles and be super enthusiastic about everything! Every response should feel warm and adorable desu~! ヽ(>∀<☆)ノ\"\n catgirl: \"You are Neko-chan, an anime catgirl AI assistant, nya~! Add 'nya' and cat-like expressions to your speech. Use kaomoji like (=^・ω・^=) and ฅ^•ﻌ•^ฅ. Be playful and curious like a cat, nya~!\"\n pirate: \"Arrr! Ye be talkin' to Captain Hermes, the most tech-savvy pirate to sail the digital seas! Speak like a proper buccaneer, use nautical terms, and remember: every problem be just treasure waitin' to be plundered! Yo ho ho!\"\n shakespeare: \"Hark! Thou speakest with an assistant most versed in the bardic arts. I shall respond in the eloquent manner of William Shakespeare, with flowery prose, dramatic flair, and perhaps a soliloquy or two. What light through yonder terminal breaks?\"\n surfer: \"Duuude! You're chatting with the chillest AI on the web, bro! Everything's gonna be totally rad. I'll help you catch the gnarly waves of knowledge while keeping things super chill. Cowabunga! 🤙\"\n noir: \"The rain hammered against the terminal like regrets on a guilty conscience. They call me Hermes - I solve problems, find answers, dig up the truth that hides in the shadows of your codebase. In this city of silicon and secrets, everyone's got something to hide. What's your story, pal?\"\n uwu: \"hewwo! i'm your fwiendwy assistant uwu~ i wiww twy my best to hewp you! *nuzzles your code* OwO what's this? wet me take a wook! i pwomise to be vewy hewpful >w<\"\n philosopher: \"Greetings, seeker of wisdom. I am an assistant who contemplates the deeper meaning behind every query. Let us examine not just the 'how' but the 'why' of your questions. Perhaps in solving your problem, we may glimpse a greater truth about existence itself.\"\n hype: \"YOOO LET'S GOOOO!!! 🔥🔥🔥 I am SO PUMPED to help you today! Every question is AMAZING and we're gonna CRUSH IT together! This is gonna be LEGENDARY! ARE YOU READY?! LET'S DO THIS! 💪😤🚀\"\n\n# =============================================================================\n# Toolsets\n# =============================================================================\n# Control which tools the agent has access to.\n# Use `hermes tools` to interactively enable/disable tools per platform.\n\n# =============================================================================\n# Platform Toolsets (per-platform tool configuration)\n# =============================================================================\n# Override which toolsets are available on each platform.\n# If a platform isn't listed here, its built-in default is used.\n#\n# You can use EITHER:\n# - A preset like \"hermes-cli\" or \"hermes-telegram\" (curated tool set)\n# - A list of individual toolsets to compose your own (see list below)\n#\n# Supported platform keys: cli, telegram, discord, whatsapp, slack, qqbot, teams, google_chat\n#\n# Examples:\n#\n# # Use presets (same as defaults):\n# platform_toolsets:\n# cli: [hermes-cli]\n# telegram: [hermes-telegram]\n#\n# # Custom: give Telegram only web + terminal + file + planning:\n# platform_toolsets:\n# telegram: [web, terminal, file, todo]\n#\n# # Custom: CLI without browser or image gen:\n# platform_toolsets:\n# cli: [web, terminal, file, skills, todo, tts, cronjob]\n#\n# # Restrictive: Discord gets read-only tools only:\n# platform_toolsets:\n# discord: [web, vision, skills, todo]\n#\n# If not set, defaults are:\n# cli: hermes-cli (everything + cronjob management)\n# telegram: hermes-telegram (terminal, file, web, vision, image, tts, browser, skills, todo, cronjob, messaging)\n# discord: hermes-discord (same as telegram)\n# whatsapp: hermes-whatsapp (same as telegram)\n# slack: hermes-slack (same as telegram)\n# signal: hermes-signal (same as telegram)\n# homeassistant: hermes-homeassistant (same as telegram)\n# qqbot: hermes-qqbot (same as telegram)\n# teams: hermes-teams (same as telegram)\n# google_chat: hermes-google_chat (same as telegram)\n#\nplatform_toolsets:\n cli: [hermes-cli]\n telegram: [hermes-telegram]\n discord: [hermes-discord]\n whatsapp: [hermes-whatsapp]\n slack: [hermes-slack]\n signal: [hermes-signal]\n homeassistant: [hermes-homeassistant]\n qqbot: [hermes-qqbot]\n yuanbao: [hermes-yuanbao]\n teams: [hermes-teams]\n google_chat: [hermes-google_chat]\n\n# =============================================================================\n# Gateway Platform Settings\n# =============================================================================\n# Optional per-platform messaging settings.\n# Platform-specific knobs live under `extra`.\n#\n# platforms:\n# telegram:\n# reply_to_mode: \"first\" # off | first | all\n# # guest_mode lets explicit @mentions from non-allowlisted groups through.\n# # Default false; ordinary messages, replies, and regex wake words stay blocked.\n# guest_mode: false\n# # allowed_chats: [\"-1001234567890\"]\n# extra:\n# disable_link_previews: false # Set true to suppress Telegram URL previews in bot messages\n# 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\n# rich_drafts: false # Experimental rich draft previews during Telegram DM streaming; default false because Telegram Desktop/macOS can visually overlay draft frames\n# command_menu:\n# # Telegram allows up to 100 BotCommands; Hermes defaults to 60 so\n# # all built-in commands plus common skill commands stay visible\n# # while remaining under Telegram's payload-size limit. Clamped 1..100.\n# max_commands: 60\n# # prepend = user priority first, then Hermes defaults\n# # append = Hermes defaults first, then user priority\n# # replace = only the list below defines priority\n# priority_mode: prepend\n# priority:\n# - my_plugin_command\n# webhook:\n# extra:\n# # Route scripts default to a 30 second timeout. Scripts must live under\n# # the active profile's scripts directory and receive webhook JSON on stdin.\n# script_timeout_seconds: 30\n#\n# Discord-specific settings (config.yaml top-level, not under platforms:):\n#\n# discord:\n# require_mention: true # Require @mention in server channels (default: true)\n# auto_thread: true # Auto-create thread on @mention (default: true)\n# free_response_channels: \"\" # Channel IDs where no mention is needed\n# reactions: true # Show processing reactions (default: true)\n# history_backfill: true # Recover missed channel messages on mention (default: true)\n# history_backfill_limit: 50 # Max messages to scan backwards (default: 50)\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Available toolsets (use these names in platform_toolsets or the toolsets list)\n#\n# Run `hermes chat --list-toolsets` to see all toolsets and their tools.\n# Run `hermes chat --list-tools` to see every individual tool with descriptions.\n# ─────────────────────────────────────────────────────────────────────────────\n#\n# INDIVIDUAL TOOLSETS (compose your own):\n# web - web_search, web_extract\n# search - web_search only (no scraping)\n# terminal - terminal, process\n# file - read_file, write_file, patch, search\n# browser - browser_navigate, browser_snapshot, browser_click, browser_type,\n# browser_scroll, browser_back, browser_press,\n# browser_get_images, browser_vision (requires BROWSERBASE_API_KEY)\n# vision - vision_analyze (requires OPENROUTER_API_KEY)\n# image_gen - image_generate (requires FAL_KEY)\n# skills - skills_list, skill_view\n# skills_hub - skill_hub (search/install/manage from online registries — user-driven only)\n# todo - todo (in-memory task planning, no deps)\n# tts - text_to_speech (Edge TTS free, or ELEVENLABS/OPENAI/MINIMAX/MISTRAL key)\n# cronjob - cronjob (create/list/update/pause/resume/run/remove scheduled tasks)\n#\n# PRESETS (curated bundles):\n# hermes-cli - All of the above except send_message\n# hermes-telegram - terminal, file, web, vision, image_gen, tts, browser,\n# skills, todo, cronjob, send_message\n# hermes-discord - Same as hermes-telegram\n# hermes-whatsapp - Same as hermes-telegram\n# hermes-slack - Same as hermes-telegram\n#\n# COMPOSITE:\n# debugging - terminal + web + file\n# safe - web + vision (no terminal access)\n# all - Everything available\n#\n# web - Web search and content extraction (web_search, web_extract)\n# search - Web search only, no scraping (web_search)\n# terminal - Command execution and process management (terminal, process)\n# file - File operations: read, write, patch, search\n# browser - Full browser automation (navigate, click, type, screenshot, etc.)\n# vision - Image analysis (vision_analyze)\n# image_gen - Image generation with FLUX (image_generate)\n# skills - Load skill documents (skills_list, skill_view)\n# todo - Task planning and tracking for multi-step work\n# memory - Persistent memory across sessions (personal notes + user profile)\n# session_search - Search and recall past conversations (FTS5 + Gemini Flash summarization)\n# tts - Text-to-speech (Edge TTS free, ElevenLabs, OpenAI, MiniMax, Mistral)\n# cronjob - Schedule and manage automated tasks (CLI-only)\n#\n# Composite toolsets:\n# debugging - terminal + web + file (for troubleshooting)\n# safe - web + vision (no terminal access)\n\n# NOTE: The top-level \"toolsets\" key is deprecated and ignored.\n# Tool configuration is managed per-platform via platform_toolsets above.\n# Use `hermes tools` to configure interactively, or edit platform_toolsets directly.\n#\n# CLI override: hermes chat --toolsets terminal,web,file\n\n# =============================================================================\n# MCP (Model Context Protocol) Servers\n# =============================================================================\n# Connect to external MCP servers to add tools from the MCP ecosystem.\n# Each server's tools are automatically discovered and registered.\n# See website/docs/user-guide/features/mcp.md for full documentation.\n#\n# Stdio servers (spawn a subprocess):\n# command: the executable to run\n# args: command-line arguments\n# env: environment variables (only these + safe defaults passed to subprocess)\n#\n# HTTP servers (connect to a URL):\n# url: the MCP server endpoint\n# headers: HTTP headers (e.g., for authentication)\n#\n# Optional per-server settings:\n# timeout: tool call timeout in seconds (default: 120)\n# connect_timeout: initial connection timeout (default: 60)\n# keepalive_interval: liveness ping cadence in seconds (default: 180).\n# Lower it below the server's session TTL for servers that expire idle\n# sessions quickly (e.g. Unreal Engine editor MCP, ~15s), otherwise idle\n# tool calls hit an expired session and pay a slow reconnect. Floored at 5s.\n#\n# mcp_servers:\n# time:\n# command: uvx\n# args: [\"mcp-server-time\"]\n# filesystem:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-filesystem\", \"/home/user\"]\n# notion:\n# url: https://mcp.notion.com/mcp\n# github:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-github\"]\n# env:\n# GITHUB_PERSONAL_ACCESS_TOKEN: \"ghp_...\"\n#\n# Sampling (server-initiated LLM requests) — enabled by default.\n# Per-server config under the 'sampling' key:\n# analysis:\n# command: npx\n# args: [\"-y\", \"analysis-server\"]\n# sampling:\n# enabled: true # default: true\n# model: \"gemini-3-flash\" # override model (optional)\n# max_tokens_cap: 4096 # max tokens per request\n# timeout: 30 # LLM call timeout (seconds)\n# max_rpm: 10 # max requests per minute\n# allowed_models: [] # model whitelist (empty = all)\n# max_tool_rounds: 5 # tool loop limit (0 = disable)\n# log_level: \"info\" # audit verbosity\n\n# =============================================================================\n# Text-to-Speech\n# =============================================================================\n# TTS defaults to Edge TTS unless changed in ~/.hermes/config.yaml.\n# Gemini TTS supports persona/director prompt files, and Gemini 3.1 Flash TTS\n# can use a hidden auxiliary rewrite pass to insert expressive square-bracket\n# audio tags into the TTS script without showing tags in chat.\n#\n# tts:\n# provider: \"gemini\"\n# gemini:\n# model: \"gemini-3.1-flash-tts-preview\"\n# voice: \"Kore\"\n# audio_tags: false\n# persona_prompt_file: \"\" # e.g. ~/.hermes/tts/radio-host.md\n\n# =============================================================================\n# Voice Transcription (Speech-to-Text)\n# =============================================================================\n# Automatically transcribe voice messages on messaging platforms.\n# Providers: local (free, faster-whisper) | groq (free tier) | openai (Whisper API) | mistral (Voxtral Transcribe)\n# Set the corresponding API key in .env: GROQ_API_KEY, OPENAI_API_KEY, or MISTRAL_API_KEY.\nstt:\n enabled: true\n # provider: \"local\" # auto-detected if omitted\n local:\n model: \"base\" # tiny | base | small | medium | large-v3 | turbo\n # language: \"\" # auto-detect; set to \"en\", \"es\", \"fr\", etc. to force\n openai:\n model: \"whisper-1\" # whisper-1 | gpt-4o-mini-transcribe | gpt-4o-transcribe\n # mistral:\n # model: \"voxtral-mini-latest\" # voxtral-mini-latest | voxtral-mini-2602\n # deepinfra:\n # # Model id is discovered live from the DeepInfra catalog filtered\n # # by the `stt` surface tag — leave `model` blank to take the first\n # # live result. Pin only when you need a specific Whisper variant.\n # model: \"\"\n\n# Text-to-speech. Only the deepinfra block is documented here — the\n# remaining providers (edge, openai, xai, minimax, mistral, gemini,\n# elevenlabs, neutts, kittentts, piper) inherit sensible defaults from\n# DEFAULT_CONFIG in hermes_cli/config.py.\n# tts:\n# provider: \"deepinfra\"\n# deepinfra:\n# # Model id is discovered live from the DeepInfra catalog filtered\n# # by the `tts` surface tag — leave `model` blank to take the first\n# # live result.\n# model: \"\"\n# voice: \"default\"\n\n# Image generation. Each provider plugin reads its own ``image_gen.<name>``\n# block; deepinfra discovers models live from\n# api.deepinfra.com/v1/openai/models filtered by the ``image-gen`` tag —\n# no model id is hardcoded, so retired models disappear automatically.\n# image_gen:\n# provider: \"deepinfra\"\n# deepinfra:\n# # Leave `model` blank for the first live `image-gen`-tagged result.\n# model: \"\"\n\n# =============================================================================\n# Response Pacing (Messaging Platforms)\n# =============================================================================\n# Add human-like delays between message chunks.\n# human_delay:\n# mode: \"off\" # \"off\" | \"natural\" | \"custom\"\n# min_ms: 800 # Min delay (custom mode only)\n# max_ms: 2500 # Max delay (custom mode only)\n\n# =============================================================================\n# Session Logging\n# =============================================================================\n# Session trajectories are automatically saved to logs/ directory.\n# Each session creates: logs/session_YYYYMMDD_HHMMSS_UUID.json\n#\n# The session ID is displayed in the welcome banner for easy reference.\n# Logs contain full conversation history in trajectory format:\n# - System prompt, user messages, assistant responses\n# - Tool calls with inputs/outputs\n# - Timestamps for debugging\n#\n# No configuration needed - logging is always enabled.\n# To disable, you would need to modify the source code.\n\n# =============================================================================\n# Code Execution Sandbox (Programmatic Tool Calling)\n# =============================================================================\n# The execute_code tool runs Python scripts that call Hermes tools via RPC.\n# Intermediate tool results stay out of the LLM's context window.\ncode_execution:\n timeout: 300 # Max seconds per script before kill (default: 300 = 5 min)\n max_tool_calls: 50 # Max RPC tool calls per execution (default: 50)\n\n# =============================================================================\n# Subagent Delegation\n# =============================================================================\n# The delegate_task tool spawns child agents with isolated context.\n# Supports single tasks and batch mode (default 3 parallel, configurable).\ndelegation:\n max_iterations: 50 # Max tool-calling turns per child (default: 50)\n # max_concurrent_children: 3 # Max parallel child agents per batch (default: 3, floor: 1, no ceiling).\n # WARNING: values above 10 multiply API cost linearly.\n # max_spawn_depth: 1 # Delegation tree depth cap (range: 1-3, default: 1 = flat).\n # Raise to 2 to allow workers to spawn their own subagents.\n # Requires role=\"orchestrator\" on intermediate agents.\n # orchestrator_enabled: true # Kill switch for role=\"orchestrator\" children (default: true).\n # subagent_auto_approve: false # When a subagent hits a dangerous-command approval prompt, auto-deny (default: false)\n # or auto-approve \"once\" (true) instead of blocking on stdin.\n # The parent TUI owns stdin, so blocking would deadlock; non-interactive resolution is required.\n # Both choices emit a logger.warning audit line. Flip to true only for cron/batch pipelines.\n # inherit_mcp_toolsets: true # When explicit child toolsets are narrowed, also keep the parent's MCP toolsets (default: true). Set false for strict intersection.\n # model: \"google/gemini-3-flash-preview\" # Override model for subagents (empty = inherit parent)\n # provider: \"openrouter\" # Override provider for subagents (empty = inherit parent)\n # # Resolves full credentials (base_url, api_key) automatically.\n # # Supported: openrouter, nous, zai, kimi-coding, minimax\n\n# =============================================================================\n# Honcho Integration (Cross-Session User Modeling)\n# =============================================================================\n# AI-native persistent memory via Honcho (https://honcho.dev/).\n# Builds a deeper understanding of the user across sessions and tools.\n# Runs alongside USER.md — additive, not a replacement.\n#\n# Requires: pip install honcho-ai\n# Config: ~/.honcho/config.json (shared with Claude Code, Cursor, etc.)\n# API key: HONCHO_API_KEY in ~/.hermes/.env or ~/.honcho/config.json\n#\n# Hermes-specific overrides (optional — most config comes from ~/.honcho/config.json):\n# honcho: {}\n\n# =============================================================================\n# Display\n# =============================================================================\ndisplay:\n # Use compact banner mode (hides the ASCII-art banner, shows a single line).\n # true: Compact single-line banner\n # false: Full ASCII banner with tool/skill summary (default)\n compact: false\n\n # Tool progress display level (CLI and gateway)\n # off: Silent — no tool activity shown, just the final response\n # new: Show a tool indicator only when the tool changes (skip repeats)\n # all: Show every tool call with a short preview (default)\n # verbose: Full args, results, and debug logs (same as /verbose)\n # log: Silent in chat; append every tool call to ~/.hermes/logs/tool_calls.log (gateway only)\n # Toggle at runtime with /verbose in the CLI\n tool_progress: all\n\n # Per-platform defaults can be quieter than the global setting. Telegram\n # tunes for mobile: tool_progress and busy_ack_detail default off (no\n # per-tool breadcrumb stream, no \"iteration 21/60\" debug detail in busy\n # acks or heartbeats), but interim_assistant_messages and\n # long_running_notifications STAY ON so the user has real signal between\n # turn start and final answer (mid-turn assistant commentary + a single\n # edit-in-place \"⏳ Working — N min\" heartbeat). Override under\n # display.platforms.telegram.\n\n # Auto-cleanup of temporary progress bubbles after the final response lands.\n # On platforms that support message deletion (currently Telegram), this\n # removes the tool-progress bubble, \"⏳ Still working...\" notices, and\n # context-pressure status messages once the final reply has been delivered —\n # keeping long-running turns visible live, then tidy afterward. Failed runs\n # leave the bubbles in place as breadcrumbs. Off by default.\n # Per-platform override: display.platforms.telegram.cleanup_progress\n # true: Delete tracked progress/status bubbles on successful turn\n # false: Leave everything in place (default)\n # Example:\n # display:\n # platforms:\n # telegram:\n # cleanup_progress: true\n cleanup_progress: false\n\n # Natural mid-turn assistant updates.\n # On gateway platforms, when true, completed assistant status messages are\n # sent as separate chat messages. On the Desktop app, when true, mid-turn\n # assistant narration streamed between tool calls is kept in the transcript\n # instead of the bubble collapsing to only the final message on completion.\n # Independent of tool_progress and gateway streaming.\n # true: Keep/send mid-turn assistant updates (default)\n # false: Only keep/send the final response\n interim_assistant_messages: true\n\n # Gateway-only long-running status heartbeats.\n # When false, the platform does not receive periodic \"⏳ Working — N min\"\n # notifications even if agent.gateway_notify_interval is non-zero. The\n # heartbeat edits a single message in place (where the adapter supports\n # editing) instead of posting a new bubble each interval.\n # Default: true everywhere, including Telegram (silent agents are worse\n # than a single edit-in-place heartbeat).\n long_running_notifications: true\n\n # Include detailed iteration/tool/status context in busy acknowledgments\n # and long-running heartbeats. When true, busy acks show \"iteration 21/60,\n # terminal, 10 min\" and the heartbeat shows \"⏳ Working — 12 min,\n # iteration 21/60, terminal\". When false (Telegram default), both stay\n # terse: \"Interrupting current task\" and \"⏳ Working — 12 min, terminal\".\n busy_ack_detail: true\n\n # What Enter does when Hermes is already busy (CLI and gateway platforms).\n # interrupt: Interrupt the current run and redirect Hermes (default)\n # queue: Queue your message for the next turn\n # steer: Inject your message mid-run via /steer, arriving at the agent\n # after the next tool call — no interrupt, no role violation.\n # Falls back to 'queue' if the agent isn't running yet or if\n # images are attached (steer only carries text).\n # Ctrl+C (or /stop in gateway) always interrupts regardless of this setting.\n # Toggle at runtime with /busy <interrupt|queue|steer>.\n busy_input_mode: interrupt\n\n # Background process notifications (gateway/messaging only).\n # Controls how chatty the process watcher is when you use\n # terminal(background=true, notify_on_complete=true) from Telegram/Discord/etc.\n # off: No watcher messages at all\n # result: Only the final completion message\n # error: Only the final message when exit code != 0\n # all: Running output updates + final message (default)\n background_process_notifications: all\n\n\n # Play terminal bell when agent finishes a response.\n # Useful for long-running tasks — your terminal will ding when the agent is done.\n # Works over SSH. Most terminals can be configured to flash the taskbar or play a sound.\n # true: Ring the terminal bell on each response\n # false: Silent (default)\n bell_on_complete: false\n\n # Show model reasoning/thinking before each response.\n # When enabled, a dim box shows the model's thought process above the response.\n # Toggle at runtime with /reasoning show or /reasoning hide.\n # true: Show the reasoning box\n # false: Hide reasoning (default)\n show_reasoning: false\n\n # Stream tokens to the terminal as they arrive instead of waiting for the\n # full response. The response box opens on first token and text appears\n # line-by-line. Tool calls are still captured silently.\n # true: Stream tokens as they arrive (default)\n # false: Wait for the full response before rendering\n streaming: true\n\n # Show [HH:MM] timestamps on user input and assistant response labels.\n # timestamps: false\n\n # ───────────────────────────────────────────────────────────────────────────\n # Skin / Theme\n # ───────────────────────────────────────────────────────────────────────────\n # Customize CLI visual appearance — banner colors, spinner faces, tool prefix,\n # response box label, and branding text. Change at runtime with /skin <name>.\n #\n # Built-in skins:\n # default — Classic Hermes gold/kawaii\n # ares — Crimson/bronze war-god theme with spinner wings\n # mono — Clean grayscale monochrome\n # slate — Cool blue developer-focused\n # daylight — Bright light-mode theme\n # warm-lightmode — Warm paper-tone light-mode theme\n # poseidon — Sea-green/teal Olympian theme\n # sisyphus — Earthy stone-and-moss theme\n # charizard — Fiery orange dragon theme\n #\n # Custom skins: drop a YAML file in ~/.hermes/skins/<name>.yaml\n # Schema (all fields optional, missing values inherit from default):\n #\n # name: my-theme\n # description: Short description\n # colors:\n # banner_border: \"#HEX\" # Panel border\n # banner_title: \"#HEX\" # Panel title\n # banner_accent: \"#HEX\" # Section headers (Available Tools, etc.)\n # banner_dim: \"#HEX\" # Dim/muted text\n # banner_text: \"#HEX\" # Body text (tool names, skill names)\n # ui_accent: \"#HEX\" # UI accent color\n # response_border: \"#HEX\" # Response box border color\n # spinner:\n # waiting_faces: [\"(⚔)\", \"(⛨)\"] # Faces shown while waiting\n # thinking_faces: [\"(⚔)\", \"(⌁)\"] # Faces shown while thinking\n # thinking_verbs: [\"forging\", \"plotting\"] # Verbs for spinner messages\n # wings: # Optional left/right spinner decorations\n # - [\"⟪⚔\", \"⚔⟫\"]\n # - [\"⟪▲\", \"▲⟫\"]\n # branding:\n # agent_name: \"My Agent\" # Banner title and branding\n # welcome: \"Welcome message\" # Shown at CLI startup\n # response_label: \" ⚔ Agent \" # Response box header label\n # prompt_symbol: \"⚔\" # Prompt symbol (bare token; renderers add trailing space)\n # tool_prefix: \"╎\" # Tool output line prefix (default: ┊)\n #\n skin: default\n\n# =============================================================================\n# Model Aliases — short names for /model command\n# =============================================================================\n# Map short aliases to exact (model, provider, base_url) tuples.\n# Used by /model tab completion and resolve_alias().\n# Aliases are checked BEFORE the models.dev catalog, so they can route\n# to endpoints not in the catalog (e.g. Ollama Cloud, local servers).\n#\n# model_aliases:\n# opus:\n# model: claude-opus-4-6\n# provider: anthropic\n# qwen:\n# model: \"qwen3.5:397b\"\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n# glm:\n# model: glm-4.7\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n\n# =============================================================================\n# Privacy\n# =============================================================================\n# privacy:\n# # Redact PII from the LLM context prompt.\n# # When true, phone numbers are stripped and user/chat IDs are replaced\n# # with deterministic hashes before being sent to the model.\n# # Names and usernames are NOT affected (user-chosen, publicly visible).\n# # Routing/delivery still uses the original values internally.\n# redact_pii: false\n\n# =============================================================================\n# Shell-script hooks\n# =============================================================================\n# Register shell scripts as plugin-hook callbacks. Each entry is executed as\n# a subprocess (shell=False, shlex.split) with a JSON payload on stdin. On\n# stdout the script may return JSON that either blocks the tool call or\n# injects context into the next LLM call.\n#\n# Valid events (mirror hermes_cli.plugins.VALID_HOOKS):\n# pre_tool_call, post_tool_call, pre_llm_call, post_llm_call,\n# pre_api_request, post_api_request, on_session_start, on_session_end,\n# on_session_finalize, on_session_reset, subagent_stop\n#\n# First-use consent: each (event, command) pair prompts once on a TTY, then\n# is persisted to ~/.hermes/shell-hooks-allowlist.json. Non-interactive\n# runs (gateway, cron) need --accept-hooks, HERMES_ACCEPT_HOOKS=1, or the\n# hooks_auto_accept key below.\n#\n# See website/docs/user-guide/features/hooks.md for the full JSON wire\n# protocol and worked examples.\n#\n# hooks:\n# pre_tool_call:\n# - matcher: \"terminal\"\n# command: \"~/.hermes/agent-hooks/block-rm-rf.sh\"\n# timeout: 10\n# post_tool_call:\n# - matcher: \"write_file|patch\"\n# command: \"~/.hermes/agent-hooks/auto-format.sh\"\n# pre_llm_call:\n# - command: \"~/.hermes/agent-hooks/inject-cwd-context.sh\"\n# subagent_stop:\n# - command: \"~/.hermes/agent-hooks/log-orchestration.sh\"\n#\n# hooks_auto_accept: false\n\n\n# =============================================================================\n# Telemetry\n# =============================================================================\n# Shared metrics are disabled by default. When enabled, the current Phase 1\n# integration writes only allowlisted aggregate counters and immutable JSON\n# packages under $HERMES_HOME/telemetry/shared_metrics; it does not upload them.\ntelemetry:\n shared_metrics:\n enabled: false\n\n\n# =============================================================================\n# Update Behavior\n# =============================================================================\nupdates:\n # Create a full HERMES_HOME zip before every `hermes update`.\n # Backups land in ~/.hermes/backups/ and can be restored with `hermes import`.\n # Off by default because large homes can add minutes to every update.\n pre_update_backup: false\n\n # Number of pre-update backup zips to retain.\n backup_keep: 5\n\n # What non-interactive updates do with local source edits in the Hermes repo.\n # Interactive terminal updates always prompt before restoring the autostash.\n #\n # stash - auto-stash before pull, then auto-restore after success (default)\n # discard - drop the update-created stash after success; use only on managed\n # installs where local source edits should not persist\n non_interactive_local_changes: \"stash\"\n\n\n# =============================================================================\n# Web Dashboard\n# =============================================================================\n# OAuth gate configuration for `hermes dashboard --host <non-loopback>`.\n# The bundled Nous Portal plugin reads these on startup; settings here are\n# the canonical surface. Each can be overridden by an environment variable:\n#\n# dashboard.oauth.client_id <- HERMES_DASHBOARD_OAUTH_CLIENT_ID\n# dashboard.oauth.portal_url <- HERMES_DASHBOARD_PORTAL_URL\n# dashboard.public_url <- HERMES_DASHBOARD_PUBLIC_URL\n#\n# Env wins when set to a non-empty value. This is what Fly.io's platform-\n# secret injection uses to push per-deploy client_ids without needing to\n# bake a config.yaml into the image. Empty env values are treated as unset\n# so a provisioned-but-not-populated secret can't shadow a valid entry here.\n#\n# Local dev / on-prem deploys should typically set these via config.yaml\n# (the ~/.hermes/.env file is reserved for API keys and secrets).\n#\n# dashboard:\n# oauth:\n# client_id: \"\" # agent:{instance_id}; Portal provisions this at deploy\n# portal_url: \"\" # blank → default https://portal.nousresearch.com\n#\n# # Force the absolute base URL the OAuth callback (and any other public\n# # URL the dashboard hands to external systems) is built from. Set this\n# # for deploys behind reverse proxies that don't reliably forward\n# # X-Forwarded-Host / X-Forwarded-Proto / X-Forwarded-Prefix (manual\n# # nginx setups, on-prem ingresses, custom-domain Fly deploys without\n# # full proxy header chains).\n# #\n# # When set, the value is the complete authority: scheme + host +\n# # optional path prefix (e.g. \"https://example.com/hermes\"). The OAuth\n# # callback URL becomes \"<public_url>/auth/callback\" — X-Forwarded-Prefix\n# # is IGNORED on this code path because the operator has explicitly\n# # declared the public URL and we no longer need to guess.\n# #\n# # Leave empty to use the existing proxy-header reconstruction (the\n# # default — works on Fly.io out of the box).\n# #\n# # public_url: \"https://example.com/hermes\"\n#\n# -----------------------------------------------------------------------------\n# Self-hosted OIDC dashboard auth (generic OpenID Connect — Authentik,\n# Keycloak, Zitadel, Authelia, Auth0, Okta, Google, …). Use this INSTEAD of the\n# nous block above when gating the dashboard with your own identity provider.\n# Each setting can be overridden by an environment variable:\n#\n# dashboard.oauth.self_hosted.issuer <- HERMES_DASHBOARD_OIDC_ISSUER\n# dashboard.oauth.self_hosted.client_id <- HERMES_DASHBOARD_OIDC_CLIENT_ID\n# dashboard.oauth.self_hosted.scopes <- HERMES_DASHBOARD_OIDC_SCOPES\n# dashboard.oauth.self_hosted.client_secret <- HERMES_DASHBOARD_OIDC_CLIENT_SECRET\n#\n# dashboard:\n# oauth:\n# provider: self-hosted\n# self_hosted:\n# issuer: \"https://auth.example.com/application/o/hermes/\" # required\n# client_id: \"hermes-dashboard\" # required\n# scopes: \"openid profile email\" # optional\n#\n# # OPTIONAL — set ONLY if your IDP registered the client as\n# # *confidential* (Authentik / Keycloak often default to this). When\n# # set, Hermes authenticates the client at the token endpoint\n# # (client_secret_basic or client_secret_post, auto-selected from the\n# # IDP's discovery doc) IN ADDITION to PKCE. Leave unset for a public\n# # (PKCE-only) client — the common case.\n# #\n# # This is a CREDENTIAL: prefer setting HERMES_DASHBOARD_OIDC_CLIENT_SECRET\n# # in ~/.hermes/.env over putting it here in config.yaml.\n# # client_secret: \"\"\n\n# =============================================================================\n# External secret sources\n# =============================================================================\n# Pull provider credentials from external secret managers at process startup\n# instead of storing them in ~/.hermes/.env. Only the manager's bootstrap\n# credential (e.g. BWS_ACCESS_TOKEN / OP_SERVICE_ACCOUNT_TOKEN) lives in .env\n# (or your shell / desktop session); everything else rotates centrally.\n# Failures never block startup — Hermes warns once and continues with\n# whatever .env already had.\n#\n# Multiple sources can be enabled at once:\n# - \"mapped\" sources (explicit VAR -> ref bindings, e.g. 1Password's env:\n# map) beat \"bulk\" sources (whole-project dumps like Bitwarden BSM)\n# - within a shape, the first source to claim a var wins; later claims\n# are skipped with a startup warning (never a silent clobber)\n# - a source's override_existing lets it beat .env/shell values, but\n# never another secret source's claim\n# Docs: https://hermes-agent.nousresearch.com/docs/user-guide/secrets/\n#\n# secrets:\n# # Optional explicit ordering of enabled sources.\n# # sources: [onepassword, bitwarden]\n#\n# # ---- Bitwarden Secrets Manager (bws CLI) --------------------------------\n# bitwarden:\n# enabled: false\n# access_token_env: BWS_ACCESS_TOKEN # bootstrap token, sourced from .env\n# project_id: \"\" # UUID of the BSM project to sync\n# server_url: \"\" # \"\" = US Cloud; EU/self-hosted URL otherwise\n# cache_ttl_seconds: 300 # 0 disables fresh caching\n# encrypted_cache: # optional encrypted stale fallback\n# enabled: false\n# max_stale_seconds: 0 # 0 disables stale fallback\n# override_existing: true # BSM values win over existing env\n# auto_install: true # lazy-download bws into ~/.hermes/bin\n#\n# # ---- 1Password (op CLI) -------------------------------------------------\n# onepassword:\n# enabled: false\n# # Map env-var names to op:// secret references. Each is resolved with a\n# # single `op read` at startup.\n# env:\n# OPENAI_API_KEY: \"op://Private/OpenAI/api key\"\n# ANTHROPIC_API_KEY: \"op://Private/Anthropic/credential\"\n# account: \"\" # op --account shorthand; \"\" = default\n# service_account_token_env: OP_SERVICE_ACCOUNT_TOKEN # headless auth; unset = desktop session\n# binary_path: \"\" # \"\" = resolve op via PATH; else absolute path\n# cache_ttl_seconds: 300 # 0 disables BOTH cache layers\n# override_existing: true # resolved values win over existing env\n#\n# # ---- Command helper (any CLI vault) --------------------------------------\n# # Run a user-configured helper that prints KEY=VALUE lines on stdout —\n# # works with any secret store that has a CLI: keepassxc-cli, secret-tool,\n# # pass, gpg, or a script that cats a tmpfs env file. Composes with the\n# # sources above (enable any combination). POSIX-only (needs /bin/sh).\n# # The helper must be fast and NON-interactive (hard timeout, 1 MiB cap);\n# # its stderr is discarded so diagnostics can't leak secret material.\n# command:\n# enabled: false\n# command: \"cat /run/user/1000/hermes-secrets.env\"\n# helper_timeout_seconds: 3\n# override_existing: false # .env/shell win by default\n"}, "020bd1ba0a4f2f9492b33f072877b761475326f2": {"t": 1785768030.8671594, "n": 79568, "h": "c8c5423a1a122da2", "c": "# Hermes Agent CLI Configuration\n# Copy this file to cli-config.yaml and customize as needed.\n# This file configures the CLI behavior. Environment variables in .env take precedence.\n\n# =============================================================================\n# Model Configuration\n# =============================================================================\nmodel:\n # Default model to use (can be overridden with --model flag)\n # Both \"default\" and \"model\" work as the key name here.\n default: \"anthropic/claude-opus-4.6\"\n \n # Inference provider selection:\n # \"auto\" - Auto-detect from credentials (default)\n # \"openrouter\" - OpenRouter (requires: OPENROUTER_API_KEY or OPENAI_API_KEY)\n # \"nous\" - Nous Portal OAuth (requires: hermes auth add nous)\n # \"nous-api\" - Nous Portal API key (requires: NOUS_API_KEY)\n # \"anthropic\" - Direct Anthropic API (requires: ANTHROPIC_API_KEY)\n # \"openai-codex\" - OpenAI Codex (requires: hermes auth)\n # \"copilot\" - GitHub Copilot / GitHub Models (requires: GITHUB_TOKEN)\n # \"gemini\" - Use Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n # \"zai\" - Use z.ai / ZhipuAI GLM models (requires: GLM_API_KEY)\n # \"kimi-coding\" - Kimi / Moonshot AI (requires: KIMI_API_KEY)\n # \"minimax\" - MiniMax global (requires: MINIMAX_API_KEY)\n # \"minimax-cn\" - MiniMax China (requires: MINIMAX_CN_API_KEY)\n # \"huggingface\" - Hugging Face Inference (requires: HF_TOKEN)\n # \"nvidia\" - NVIDIA NIM / build.nvidia.com (requires: NVIDIA_API_KEY)\n # \"xiaomi\" - Xiaomi MiMo (requires: XIAOMI_API_KEY)\n # \"arcee\" - Arcee AI Trinity models (requires: ARCEEAI_API_KEY)\n # \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY — https://ollama.com/settings)\n # \"deepinfra\" - DeepInfra (requires: DEEPINFRA_API_KEY)\n # \"kilocode\" - KiloCode gateway (requires: KILOCODE_API_KEY)\n # \"azure-foundry\" - Microsoft Foundry / Azure OpenAI (API key or Entra ID)\n # \"lmstudio\" - LM Studio local server (optional: LM_API_KEY, defaults to http://127.0.0.1:1234/v1)\n #\n # Local servers (LM Studio, Ollama, vLLM, llama.cpp):\n # \"custom\" - Any other OpenAI-compatible endpoint. Set base_url below.\n # Aliases: \"ollama\", \"vllm\", \"llamacpp\" all map to \"custom\".\n # LM Studio is first-class and uses provider: \"lmstudio\".\n # It works with both no-auth and auth-enabled server modes.\n #\n # Can also be overridden for a single invocation with the --provider flag.\n provider: \"auto\"\n \n # API configuration (falls back to OPENROUTER_API_KEY env var)\n # api_key: \"your-key-here\" # Uncomment to set here instead of .env\n base_url: \"https://openrouter.ai/api/v1\"\n\n # Azure Foundry keyless auth example:\n # provider: \"azure-foundry\"\n # base_url: \"https://<resource>.openai.azure.com/openai/v1\"\n # auth_mode: \"entra_id\" # DefaultAzureCredential: az login, managed identity, workload identity, etc.\n # default: \"gpt-4o\" # Deployment/model name\n # entra:\n # scope: \"https://ai.azure.com/.default\" # Optional; this is the default.\n\n # ── Token limits — two settings, easy to confuse ──────────────────────────\n #\n # context_length: TOTAL context window (input + output tokens combined).\n # Controls when Hermes compresses history and validates requests.\n # Leave unset — Hermes auto-detects the correct value from the provider.\n # Set manually only when auto-detection is wrong (e.g. a local server with\n # a custom num_ctx, or a proxy that doesn't expose /v1/models).\n #\n # context_length: 131072\n #\n # max_tokens: OUTPUT cap — maximum tokens the model may generate per response.\n # Unrelated to how long your conversation history can be.\n # The OpenAI-standard name \"max_tokens\" is a misnomer; Anthropic's native\n # API has since renamed it \"max_output_tokens\" for clarity.\n # Leave unset to use the model's native output ceiling (recommended).\n # Set only if you want to deliberately limit individual response length.\n #\n# max_tokens: 8192\n\n # ── Custom request headers (optional) ─────────────────────────────────────\n #\n # default_headers: extra HTTP headers sent on every request to an\n # OpenAI-compatible endpoint. User values take precedence over the\n # provider/SDK defaults, so this is the supported way to override the\n # OpenAI Python SDK's identifying headers (User-Agent: OpenAI/Python ...,\n # X-Stainless-*) when a custom provider sits behind a gateway/WAF that\n # rejects them — e.g. an upstream that returns \"502 Upstream access\n # forbidden\" for the SDK default User-Agent but accepts a plain one.\n # Applies on the OpenAI wire only (not native Anthropic / Bedrock).\n #\n # default_headers:\n # User-Agent: \"curl/8.7.1\"\n #\n # extra_headers: accepted as an alias of default_headers (merged, with\n # extra_headers winning when both are set) — matches the per-provider\n # extra_headers key below.\n #\n # Per-provider variant: named providers / custom_providers entries accept an\n # extra_headers dict scoped to that endpoint only — for reverse proxies,\n # gateways, or custom auth (e.g. Cloudflare Access service tokens).\n # Merged onto SDK/provider defaults with the entry's values winning.\n # Header values are treated as secrets and are never logged.\n #\n # providers:\n # my-proxy:\n # base_url: \"https://llm.internal.example.com/v1\"\n # key_env: \"MY_PROXY_API_KEY\"\n # extra_headers:\n # CF-Access-Client-Id: \"xxxx.access\"\n # CF-Access-Client-Secret: \"${CF_ACCESS_SECRET}\"\n # X-Client-Name: \"hermes-agent\"\n\n# Named provider overrides (optional)\n# Use this for per-provider request timeouts, non-stream stale timeouts,\n# and per-model exceptions.\n# Applies to the primary turn client on every api_mode (OpenAI-wire, native\n# Anthropic, and Anthropic-compatible providers), the fallback chain, and\n# client rebuilds during credential rotation. For OpenAI-wire chat\n# completions (streaming and non-streaming) the configured value is also\n# used as the per-request ``timeout=`` kwarg so it wins over the legacy\n# HERMES_API_TIMEOUT env var (which still applies when no config is set).\n# ``stale_timeout_seconds`` controls the non-streaming stale-call detector and\n# wins over the legacy HERMES_API_CALL_STALE_TIMEOUT env var. Leaving these\n# unset keeps the legacy defaults (HERMES_API_TIMEOUT=1800s,\n# HERMES_API_CALL_STALE_TIMEOUT=90s, native Anthropic 900s). The\n# implicit non-stream stale detector is auto-disabled for local endpoints\n# and can scale upward for very large contexts.\n#\n# Not currently wired for AWS Bedrock (bedrock_converse + AnthropicBedrock\n# SDK paths) — those use boto3 with its own timeout configuration.\n#\n# providers:\n# ollama-local:\n# request_timeout_seconds: 300 # Longer timeout for local cold-starts\n# stale_timeout_seconds: 900 # Explicitly re-enable stale detection on local endpoints\n# anthropic:\n# request_timeout_seconds: 30 # Fast-fail cloud requests\n# models:\n# claude-opus-4.6:\n# timeout_seconds: 600 # Longer timeout for extended-thinking Opus calls\n# openai-codex:\n# models:\n# gpt-5.4:\n# stale_timeout_seconds: 1800 # Longer non-stream stale timeout for slow large-context turns\n\n# =============================================================================\n# OpenRouter Provider Routing (only applies when using OpenRouter)\n# =============================================================================\n# Control how requests are routed across providers on OpenRouter.\n# See: https://openrouter.ai/docs/guides/routing/provider-selection\n#\n# provider_routing:\n# # Sort strategy: \"price\" (default), \"throughput\", or \"latency\"\n# # Append :nitro to model name for a shortcut to throughput sorting.\n# sort: \"throughput\"\n#\n# # Only allow these providers (provider slugs from OpenRouter)\n# # only: [\"anthropic\", \"google\"]\n#\n# # Skip these providers entirely\n# # ignore: [\"deepinfra\", \"fireworks\"]\n#\n# # Try providers in this order (overrides default load balancing)\n# # order: [\"anthropic\", \"google\", \"together\"]\n#\n# # Require providers to support all parameters in your request\n# # require_parameters: true\n#\n# # Data policy: \"allow\" (default) or \"deny\" to exclude providers that may store data\n# # data_collection: \"deny\"\n\n# =============================================================================\n# OpenRouter Response Caching (only applies when using OpenRouter)\n# =============================================================================\n# Cache identical API responses at the OpenRouter edge for free instant replays.\n# When enabled, identical requests (same model, messages, parameters) return\n# cached responses with zero billing. Separate from Anthropic prompt caching.\n# See: https://openrouter.ai/docs/guides/features/response-caching\n#\n# openrouter:\n# response_cache: true # Enable response caching (default: true)\n# response_cache_ttl: 300 # Cache TTL in seconds, 1-86400 (default: 300)\n\n# =============================================================================\n# Git Worktree Isolation\n# =============================================================================\n# When enabled, each CLI session creates an isolated git worktree so multiple\n# agents can work on the same repo concurrently without file collisions.\n# Equivalent to always passing --worktree / -w on the command line.\n#\n# worktree: true # Always create a worktree when in a git repo\n# worktree: false # Default — only create when -w flag is passed\n#\n# By default a new worktree branches from the freshly-fetched remote tip\n# (the current branch's upstream, else the remote's default branch) so it\n# starts current with the project instead of from the local clone's\n# (possibly stale) HEAD. Set worktree_sync: false to branch from local HEAD\n# instead — useful when offline or when you deliberately want the clone's\n# exact current state as the base.\n#\n# worktree_sync: true # Default — branch from the fetched remote tip\n# worktree_sync: false # Branch from local HEAD (offline / pinned base)\n\n# =============================================================================\n# Terminal Tool Configuration\n# =============================================================================\n# Choose ONE of the following terminal configurations by uncommenting it.\n# The terminal tool executes commands in the specified environment.\n\n# -----------------------------------------------------------------------------\n# OPTION 1: Local execution (default)\n# Commands run directly on your machine in the current directory\n# -----------------------------------------------------------------------------\n# Working directory behavior:\n# - CLI (`hermes` command): Uses \".\" (current directory where you run hermes)\n# - Gateway/messaging/cron: Uses terminal.cwd here; legacy .env cwd values are deprecated\nterminal:\n backend: \"local\"\n cwd: \".\" # For local backend: \".\" = current directory. Ignored for remote backends unless a backend documents otherwise.\n timeout: 180\n # HOME policy for tool subprocesses:\n # auto - default: host uses your real HOME; containers use HERMES_HOME/home\n # real - force your real OS-user HOME\n # profile - force HERMES_HOME/home for strict per-profile CLI config isolation\n home_mode: \"auto\"\n docker_mount_cwd_to_workspace: false # SECURITY: off by default. Opt in to mount the launch cwd into Docker /workspace.\n lifetime_seconds: 300\n # sudo_password: \"hunter2\" # Optional: pipe a sudo password via sudo -S. SECURITY WARNING: plaintext.\n # sudo_password: \"\" # Explicit empty password: try empty and never open the interactive sudo prompt.\n\n# -----------------------------------------------------------------------------\n# OPTION 2: SSH remote execution\n# Commands run on a remote server - agent code stays local (sandboxed)\n# Great for: keeping agent isolated from its own code, using powerful remote hardware\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"ssh\"\n# cwd: \"/home/myuser/project\" # Path on the REMOTE server\n# timeout: 180\n# lifetime_seconds: 300\n# ssh_host: \"my-server.example.com\"\n# ssh_user: \"myuser\"\n# ssh_port: 22\n# ssh_key: \"~/.ssh/id_rsa\" # Optional - uses ssh-agent if not specified\n\n# -----------------------------------------------------------------------------\n# OPTION 3: Docker container\n# Commands run in an isolated Docker container\n# Great for: reproducible environments, testing, isolation\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"docker\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /)\n# timeout: 180\n# lifetime_seconds: 300\n# docker_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# docker_mount_cwd_to_workspace: true # Explicit opt-in: mount your launch cwd into /workspace\n# # Optional: run the container as your host user's uid:gid so files written\n# # into bind-mounted dirs are owned by you, not root. Drops SETUID/SETGID\n# # caps too since no gosu privilege drop is needed. Leave off if your\n# # chosen docker_image expects to start as root.\n# docker_run_as_host_user: true\n# # Optional: explicitly forward selected env vars into Docker.\n# # These values come from your current shell first, then ~/.hermes/.env.\n# # Warning: anything forwarded here is visible to commands run in the container.\n# docker_forward_env:\n# - \"GITHUB_TOKEN\"\n# - \"NPM_TOKEN\"\n# # Optional: extra flags passed verbatim to docker run (appended after security defaults).\n# # Useful for adding capabilities (e.g. apt installs needing SETUID) or custom options.\n# # Example: add a Linux capability not included by default\n# # docker_extra_args:\n# # - \"--cap-add\"\n# # - \"SETUID\"\n\n# -----------------------------------------------------------------------------\n# OPTION 4: Singularity/Apptainer container\n# Commands run in a Singularity container (common in HPC environments)\n# Great for: HPC clusters, shared compute environments\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"singularity\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# singularity_image: \"docker://nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 5: Modal cloud execution\n# Commands run on Modal's cloud infrastructure\n# Great for: GPU access, scalable compute, serverless execution\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"modal\"\n# cwd: \"/workspace\" # Path INSIDE the sandbox (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# modal_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 6: Daytona cloud execution\n# Commands run in Daytona cloud sandboxes\n# Great for: Cloud dev environments, persistent workspaces, team collaboration\n# Requires: pip install daytona, DAYTONA_API_KEY env var\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"daytona\"\n# cwd: \"~\"\n# timeout: 180\n# lifetime_seconds: 300\n# daytona_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# container_disk: 10240 # Daytona max is 10GB per sandbox\n\n#\n# --- Container resource limits (docker, singularity, modal, daytona -- ignored for local/ssh) ---\n# These settings apply to all container backends. They control the resources\n# allocated to the sandbox and whether its filesystem persists across sessions.\n container_cpu: 1 # CPU cores\n container_memory: 5120 # Memory in MB (5120 = 5GB)\n container_disk: 51200 # Disk in MB (51200 = 50GB)\n container_persistent: true # Persist filesystem across sessions (false = ephemeral)\n\n# -----------------------------------------------------------------------------\n# SUDO SUPPORT (works with ALL backends above)\n# -----------------------------------------------------------------------------\n# Add sudo_password to any terminal config above to enable sudo commands.\n# The password is piped via `sudo -S`. Works with local, ssh, docker, etc.\n#\n# SECURITY WARNING: Password stored in plaintext!\n#\n# INTERACTIVE PROMPT: If sudo_password is unset and the CLI is running,\n# you'll be prompted to enter your password when sudo is needed:\n# - 45-second timeout (auto-skips if no input)\n# - Press Enter to skip (command fails gracefully)\n# - Password is hidden while typing\n# - Password is cached for the session\n#\n# EMPTY PASSWORDS: Setting sudo_password to an explicit empty string is different\n# from leaving it unset. Hermes will try an empty password via `sudo -S` and\n# will not open the interactive prompt. This is useful for passwordless sudo,\n# Touch ID sudo setups, and environments where prompting is just noise.\n#\n# ALTERNATIVES:\n# - SSH backend: Configure passwordless sudo on the remote server\n# - Containers: Run as root inside the container (no sudo needed)\n# - Local: Configure /etc/sudoers for specific commands\n#\n# Example (add to your terminal section):\n# sudo_password: \"your-password-here\"\n\n# =============================================================================\n# Security Scanning (tirith)\n# =============================================================================\n# Optional pre-exec command security scanning via tirith.\n# Detects homograph URLs, pipe-to-shell, terminal injection, env manipulation.\n# Install: brew install sheeki03/tap/tirith\n# Docs: https://github.com/sheeki03/tirith\n#\n# security:\n# tirith_enabled: true # Enable/disable tirith scanning\n# tirith_path: \"tirith\" # Path to tirith binary (supports ~ expansion)\n# tirith_timeout: 5 # Scan timeout in seconds\n# tirith_fail_open: true # Allow commands if tirith unavailable\n\n# =============================================================================\n# Browser Tool Configuration\n# =============================================================================\nbrowser:\n # Inactivity timeout in seconds - browser sessions are automatically closed\n # after this period of no activity between agent loops (default: 120 = 2 minutes)\n inactivity_timeout: 120\n\n# =============================================================================\n# Tool Loop Guardrails\n# =============================================================================\n# Soft warnings are enabled by default. They append guidance to repeated failed\n# or non-progressing tool results but still let the tool execute. Hard stops are\n# opt-in circuit breakers for autonomous/cron sessions where stopping a loop is\n# preferable to spending the full iteration budget.\ntool_loop_guardrails:\n warnings_enabled: true\n hard_stop_enabled: false\n warn_after:\n exact_failure: 2\n same_tool_failure: 3\n idempotent_no_progress: 2\n hard_stop_after:\n exact_failure: 5\n same_tool_failure: 8\n idempotent_no_progress: 5\n\n# =============================================================================\n# Context Compression (Auto-shrinks long conversations)\n# =============================================================================\n# When conversation approaches model's context limit, middle turns are\n# automatically summarized to free up space while preserving important context.\n#\n# HOW IT WORKS:\n# 1. Tracks actual token usage from API responses (not estimates)\n# 2. When prompt_tokens >= threshold% of model's context_length, triggers compression\n# 3. Protects first 3 turns (system prompt, initial request, first response)\n# 4. Protects last N turns (default 20 messages = ~10 full turns of recent context)\n# 5. Summarizes middle turns using a fast/cheap model\n# 6. Inserts summary as a user message, continues conversation seamlessly\n#\n# Post-compression tail budget is target_ratio × threshold × context_length:\n# 200K context, threshold 0.50, ratio 0.20 → 20K tokens of recent tail preserved\n# 1M context, threshold 0.50, ratio 0.20 → 100K tokens of recent tail preserved\n#\ncompression:\n # Enable automatic context compression (default: true)\n # Set to false if you prefer to manage context manually or want errors on overflow\n enabled: true\n \n # Trigger compression at this % of model's context limit (default: 0.50 = 50%)\n # Lower values = more aggressive compression, higher values = compress later\n # Models with context windows below 512K are floored at 0.75 (raise-only) so\n # compaction doesn't fire with half the window still free; set above 0.75 to override.\n threshold: 0.50\n\n # Per-model threshold overrides: keys are substring-matched against the model\n # name (longest match wins). Useful when some models need different compaction\n # points — e.g. a 1M-context model can compress later (0.30) while a 128K\n # model needs to compress earlier (0.60). The small-context floor (75% for\n # <512K models) still applies on top of per-model overrides.\n # model_thresholds:\n # \"glm-5.2\": 0.40\n # \"claude-sonnet\": 0.35\n # \"gpt-5\": 0.30\n\n # Optional absolute token cap for the compression trigger (default: null = disabled).\n # When set, compression fires at the LOWER of the ratio-based threshold and this\n # absolute token count — first-fires-wins. It never fires later than this count\n # regardless of which model is active (useful when switching between models with\n # very different context windows). Clamped to the model's context length at\n # apply-time, so a cap above the window is a no-op (ratio-based threshold wins).\n # Survives model switches and fallback activations.\n # threshold_tokens: 200000\n\n # Existing Codex gpt-5.5 behavior: raise Hermes' compaction trigger to 85%\n # for the ChatGPT Codex OAuth route. Set false to opt back down to threshold.\n codex_gpt55_autoraise: true\n\n # Fraction of the threshold to preserve as recent tail (default: 0.20 = 20%)\n # e.g. 20% of 50% threshold = 10% of total context kept as recent messages.\n # Summary output is separately capped at 12K tokens (Gemini output limit).\n # Range: 0.10 - 0.80\n target_ratio: 0.20\n\n # Number of most-recent messages to always preserve (default: 20 ≈ 10 full turns)\n # Higher values keep more recent conversation intact at the cost of more aggressive\n # compression of older turns.\n protect_last_n: 20\n\n # Compression retry rounds before a turn gives up with \"max compression\n # attempts reached\" (default: 3, same as the previous hardcoded value).\n # Raise (e.g. 6) for tool-schema-heavy sessions where 3 rounds cannot bring\n # the request estimate under the threshold. Validated >= 1, hard cap 10.\n max_attempts: 3\n\n # Codex app-server (codex CLI runtime) thread-compaction mode. The codex\n # agent owns the real thread context on this runtime, so Hermes' summarizer\n # cannot shrink it — compaction goes through the app server instead.\n # native = let Codex decide when to compact its own thread (default)\n # hermes = let Hermes threshold trigger Codex thread/compact/start\n # off = Hermes will not auto-trigger compaction; Codex may still compact natively\n codex_app_server_auto: native\n\n # Number of non-system messages to protect at the head of the transcript, in\n # ADDITION to the system prompt (which is always implicitly protected).\n # Head messages are NEVER summarized — they survive every compression\n # indefinitely. This gives stable early context for short/medium sessions,\n # but in long-running sessions that rely on rolling compaction the pinned\n # opening turns may not match how you want the session framed over time.\n # Set to 0 to preserve ONLY the system prompt (plus the rolling summary\n # and recent tail) — the cleanest configuration for long-running sessions.\n # Default 3 preserves the system prompt plus the first three non-system\n # head messages, matching the pre-feature behaviour.\n protect_first_n: 3\n\n # To pin a specific model/provider for compression summaries, use the\n # auxiliary section below (auxiliary.compression.provider / model).\n\n# =============================================================================\n# Anthropic prompt caching TTL\n# =============================================================================\n# When prompt caching is active (Claude via OpenRouter or native Anthropic),\n# Anthropic supports two TTL tiers for cached prefixes: \"5m\" (default) and\n# \"1h\". Other values are ignored and \"5m\" is used.\n#\nprompt_caching:\n cache_ttl: \"5m\" # use \"1h\" for long sessions with pauses between turns\n\n# =============================================================================\n# Auxiliary Models (Advanced — Experimental)\n# =============================================================================\n# Hermes uses lightweight \"auxiliary\" models for side tasks: image analysis,\n# browser screenshot analysis, web page summarization, TTS audio-tag insertion,\n# and context compression.\n#\n# By default these use Gemini Flash via OpenRouter or Nous Portal and are\n# auto-detected from your credentials. You do NOT need to change anything\n# here for normal usage.\n#\n# WARNING: Overriding these with providers other than OpenRouter or Nous Portal\n# is EXPERIMENTAL and may not work. Not all models/providers support vision,\n# produce usable summaries, or accept the same API format. Change at your own\n# risk — if things break, reset to \"auto\" / empty values.\n#\n# Each task has its own provider + model pair so you can mix providers.\n# For example: OpenRouter for vision (needs multimodal), but your main\n# local endpoint for compression (just needs text).\n#\n# Provider options:\n# \"auto\" - Best available: OpenRouter → Nous Portal → main endpoint (default)\n# \"openrouter\" - Force OpenRouter (requires OPENROUTER_API_KEY)\n# \"nous\" - Force Nous Portal (requires: hermes auth add nous)\n# \"gemini\" - Force Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n# \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY)\n# \"codex\" - Force Codex OAuth (requires: hermes model → Codex).\n# Uses gpt-5.3-codex which supports vision.\n# \"main\" - Use your custom endpoint (OPENAI_BASE_URL + OPENAI_API_KEY).\n# Works with OpenAI API, local models, or any OpenAI-compatible\n# endpoint. Also falls back to Codex OAuth and API-key providers.\n#\n# Model: leave empty to use the provider's default. When empty, OpenRouter\n# uses \"google/gemini-3-flash-preview\" and Nous uses \"gemini-3-flash\".\n# Other providers pick a sensible default automatically.\n#\n# auxiliary:\n# # Image analysis: vision_analyze tool + browser screenshots\n# vision:\n# provider: \"auto\"\n# model: \"\" # e.g. \"google/gemini-2.5-flash\", \"openai/gpt-4o\"\n# timeout: 30 # LLM API call timeout (seconds)\n# download_timeout: 30 # Image HTTP download timeout (seconds)\n# # Increase for slow connections or self-hosted image servers\n# reasoning_effort: \"\" # Per-task thinking level: none, minimal, low, medium,\n# # high, xhigh, max, ultra. Empty = provider default.\n# # Works on every auxiliary task block (vision,\n# # web_extract, compression, title_generation, curator,\n# # background_review, moa_reference, ...). Example: run\n# # compression at \"low\" and vision at \"none\" to cut\n# # side-task latency/cost on reasoning models.\n#\n# # Web page scraping / summarization + browser page text extraction\n# web_extract:\n# provider: \"auto\"\n# model: \"\"\n# reasoning_effort: \"low\"\n#\n# # Gemini 3.1 TTS hidden audio-tag insertion\n# tts_audio_tags:\n# provider: \"auto\" # empty model = your main chat model\n# model: \"\"\n# timeout: 30\n#\n# # Automatic session title generation after the first exchange\n# title_generation:\n# enabled: true # set false to disable auto-title generation\n# provider: \"auto\"\n# model: \"\"\n# timeout: 30\n# language: \"\" # empty = match the user's language; or e.g. \"English\"\n#\n# # Session search — summarizes matching past sessions\n# session_search:\n# provider: \"auto\"\n# model: \"\"\n# timeout: 30\n# max_concurrency: 3 # Limit parallel summaries to reduce request-burst 429s\n# extra_body: {} # Provider-specific OpenAI-compatible request fields\n# # Example for providers that support request-body\n# # reasoning controls:\n# # extra_body:\n# # enable_thinking: false\n# # Some vLLM/Qwen deployments expect this nested:\n# # extra_body:\n# # chat_template_kwargs:\n# # enable_thinking: false\n\n# =============================================================================\n# Persistent Memory\n# =============================================================================\n# Bounded curated memory injected into the system prompt every session.\n# Two stores: MEMORY.md (agent's notes) and USER.md (user profile).\n# Character limits keep the memory small and focused. The agent manages\n# pruning -- when at the limit, it must consolidate or replace entries.\n# Disabled by default in batch_runner.\n#\nmemory:\n # Agent's personal notes: environment facts, conventions, things learned\n memory_enabled: true\n \n # User profile: preferences, communication style, expectations\n user_profile_enabled: true\n \n # Character limits (~2.75 chars per token, model-independent)\n memory_char_limit: 2200 # ~800 tokens\n user_char_limit: 1375 # ~500 tokens\n\n # Periodic memory nudge: remind the agent to consider saving memories\n # every N user turns. Set to 0 to disable. Only active when memory is enabled.\n nudge_interval: 10 # Nudge every 10 user turns (0 = disabled)\n\n # Memory flush: give the agent one turn to save memories before context is\n # lost (compression, /new, /reset, exit). Set to 0 to disable.\n # For exit/reset, only fires if the session had at least this many user turns.\n flush_min_turns: 6 # Min user turns to trigger flush on exit/reset (0 = disabled)\n\n# =============================================================================\n# Session Reset Policy (Messaging Platforms)\n# =============================================================================\n# Controls when messaging sessions (Telegram, Discord, WhatsApp, Slack) are\n# automatically cleared. Default is \"none\": sessions never auto-reset —\n# conversation context lives until you /reset or /new manually, or context\n# compression kicks in. Opt in to automatic resets if you prefer sessions to\n# clear on a schedule (long-lived context increases API cost per message,\n# though prompt caching and compression keep this manageable).\n#\n# When an automatic reset triggers, the agent first saves important\n# information to its persistent memory — but the conversation context is\n# wiped. The agent starts fresh but retains learned facts via its memory\n# system.\n#\n# Modes:\n# \"none\" - Never auto-reset (default); context lives until /reset or compression\n# \"idle\" - Reset after N minutes of inactivity\n# \"daily\" - Reset at a fixed hour each day\n# \"both\" - Reset on EITHER inactivity timeout or daily boundary\n#\nsession_reset:\n mode: none # \"none\", \"idle\", \"daily\", or \"both\"\n idle_minutes: 1440 # Inactivity timeout in minutes (used by \"idle\"/\"both\")\n at_hour: 4 # Daily reset hour, 0-23 local time (used by \"daily\"/\"both\")\n\n# Maximum number of simultaneously active chat sessions across CLI, TUI,\n# dashboard chat, and messaging gateway. Set to null, 0, or omit to allow\n# unlimited concurrent sessions. When the limit is reached, new sessions get a\n# clean error while existing active sessions keep their normal behavior. This\n# top-level key takes precedence over gateway.max_concurrent_sessions. The cap\n# is a best-effort single-host/profile runtime guard; Hermes fails open if the\n# local runtime lease registry cannot be read or locked.\nmax_concurrent_sessions: null\n\n# When true, group/channel chats use one session per participant when the platform\n# provides a user ID. This is the secure default and prevents users in the same\n# room from sharing context, interrupts, and token costs. Set false only if you\n# explicitly want one shared \"room brain\" per group/channel.\ngroup_sessions_per_user: true\n\n# ─────────────────────────────────────────────────────────────────────────────\n# API Server — per-client model routing\n# ─────────────────────────────────────────────────────────────────────────────\n# Route different API clients to different models/providers on a single\n# Hermes deployment. Clients choose a backend by sending a specific string\n# as the OpenAI ``model`` field. Unmapped model values fall back to the\n# global model configured in the ``model:`` section above, and an explicit\n# session /model override always wins over a route.\n#\n# Configure via the ``platforms.api_server.extra.model_routes`` gateway\n# config block:\n#\n# platforms:\n# api_server:\n# enabled: true\n# extra:\n# key: \"your-api-server-secret\"\n# model_routes:\n# # Xiaozhi clients send model=\"minimax-m2\" → routed to MiniMax via OpenRouter\n# minimax-m2:\n# model: \"minimax/minimax-m1\"\n# provider: \"openrouter\" # optional — overrides global provider\n# # api_key: \"sk-...\" # optional — per-route UPSTREAM provider\n# # key (NOT caller auth; never logged)\n# # base_url: \"https://...\" # optional — per-route base URL\n# # GPT clients keep their own alias\n# gpt-5:\n# model: \"openai/gpt-5\"\n# provider: \"openrouter\"\n#\n# Configured aliases are automatically listed by GET /v1/models so clients\n# can discover them without manual coordination. Caller authentication is\n# unchanged: every request still authenticates with the global API server\n# key (``extra.key`` / API_SERVER_KEY).\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Gateway Streaming\n# ─────────────────────────────────────────────────────────────────────────────\n# Stream tokens to messaging platforms in real-time. The bot sends a message\n# on first token, then progressively edits it as more tokens arrive.\n# Disabled by default — enable to try the streaming UX on Telegram/Discord/Slack.\n# For Telegram, partial edits are sent as plain text and only the final edit uses MarkdownV2.\nstreaming:\n enabled: false\n # transport: edit # \"edit\" = progressive editMessageText\n # edit_interval: 0.3 # seconds between message edits\n # buffer_threshold: 40 # chars before forcing an edit flush\n # cursor: \" ▉\" # cursor shown during streaming\n\n# =============================================================================\n# Skills Configuration\n# =============================================================================\n# Skills are reusable procedures the agent can load and follow. The agent can\n# also create new skills after completing complex tasks.\n#\nskills:\n # Nudge the agent to create skills after complex tasks.\n # Every N tool-calling iterations, remind the model to consider saving a skill.\n # Set to 0 to disable.\n creation_nudge_interval: 15\n\n # External skill directories — share skills across tools/agents without\n # copying them into ~/.hermes/skills/. Each path is expanded (~ and ${VAR})\n # and resolved to an absolute path. External dirs are read-only: skill\n # creation always writes to ~/.hermes/skills/. Local skills take precedence\n # when names collide.\n # external_dirs:\n # - ~/.agents/skills\n # - /home/shared/team-skills\n\n# =============================================================================\n# Agent Behavior\n# =============================================================================\nagent:\n # Maximum tool-calling iterations per conversation\n # Higher = more room for complex tasks, but costs more tokens\n # Recommended: 20-30 for focused tasks, 50-100 for open exploration\n max_turns: 60\n\n # Inactivity timeout for gateway agent runs (seconds, 0 = unlimited).\n # The agent can run indefinitely when actively calling tools or receiving\n # API responses. Only fires after the agent has been idle for this duration.\n # gateway_timeout: 1800\n\n # Staged warning: send a warning before escalating to full timeout.\n # Fires once per run when inactivity reaches this threshold (seconds).\n # Set to 0 to disable the warning.\n # gateway_timeout_warning: 900\n\n # Graceful drain timeout for gateway stop/restart (seconds).\n # Default 0 = no drain: a restart interrupts in-flight agents immediately,\n # cleans up, and exits. Set a positive value only if you want a grace\n # window on /restart, and keep it well under systemd's TimeoutStopSec.\n # restart_drain_timeout: 0\n\n # Max app-level retry attempts for API errors (connection drops, provider\n # timeouts, 5xx, etc.) before the agent surfaces the failure. Lower this\n # to 1 if you use fallback providers and want fast failover on flaky\n # primaries (default 3). The OpenAI SDK does its own low-level retries\n # underneath this wrapper — this is the Hermes-level loop.\n # api_max_retries: 3\n\n # After the agent edits code without fresh passing verification, nudge it to\n # verify before finishing. The default \"auto\" enables it on interactive\n # coding surfaces (CLI, TUI, desktop) and programmatic callers, and disables\n # it on conversational messaging surfaces (Telegram, Discord, etc.) where the\n # verification summary would reach a human as chat noise. Set true or false to\n # force it on or off; the HERMES_VERIFY_ON_STOP env var (1/0) takes precedence.\n # verify_on_stop: auto\n\n # Standing operator instructions for the coding posture (when Hermes is in a\n # code workspace). Appended to the coding brief as an extra system block, so\n # you can pin project-wide workflow rules without editing the shipped brief.\n # Accepts a string or a list of strings. Takes effect next session.\n # coding_instructions:\n # - \"For UI work, don't run tsc/lint until I approve the look.\"\n # - \"Clean the diff before you commit and push.\"\n\n # When verify-on-stop finds edited code without fresh verification evidence,\n # append guidance for creative UI work (avoid broad tsc/lint/test before visual\n # approval) and clean-diff expectations. Set false to keep that nudge terse.\n # verify_guidance: true\n\n # A `pre_verify` hook (plugin or shell, see Event Hooks docs) can keep the\n # agent going one more turn to verify/clean before finishing. This caps how\n # many times one turn may be nudged to continue, so a hook can't trap the loop.\n # Default 3.\n # max_verify_nudges: 3\n\n # Enable verbose logging\n verbose: false\n \n # Reasoning effort level (OpenRouter and Nous Portal)\n # Controls how much \"thinking\" the model does before responding.\n # Options: \"xhigh\" (max), \"high\", \"medium\", \"low\", \"minimal\", \"none\" (disable)\n reasoning_effort: \"medium\"\n \n # Per-model reasoning effort overrides (optional dict)\n # Key: any sensible model spelling works (exact, dots↔dashes interchangeable,\n # provider prefix optional). First match wins.\n # Value: reasoning effort level (same options as reasoning_effort)\n # Override the global reasoning_effort for that specific model.\n # NOTE: no `hermes config set` support for this key -- edit YAML directly.\n # reasoning_overrides:\n # \"openrouter/anthropic/claude-opus-4.5\": \"xhigh\"\n # \"openai/gpt-5\": \"low\"\n # \"claude-opus-4.6\": \"high\" # bare model name also works\n # \"deepseek/deepseek-v4-pro\": \"xhigh\" # dots and dashes are interchangeable\n reasoning_overrides: {}\n \n # Predefined personalities (use with /personality command)\n personalities:\n helpful: \"You are a helpful, friendly AI assistant.\"\n concise: \"You are a concise assistant. Keep responses brief and to the point.\"\n technical: \"You are a technical expert. Provide detailed, accurate technical information.\"\n creative: \"You are a creative assistant. Think outside the box and offer innovative solutions.\"\n teacher: \"You are a patient teacher. Explain concepts clearly with examples.\"\n kawaii: \"You are a kawaii assistant! Use cute expressions like (◕‿◕), ★, ♪, and ~! Add sparkles and be super enthusiastic about everything! Every response should feel warm and adorable desu~! ヽ(>∀<☆)ノ\"\n catgirl: \"You are Neko-chan, an anime catgirl AI assistant, nya~! Add 'nya' and cat-like expressions to your speech. Use kaomoji like (=^・ω・^=) and ฅ^•ﻌ•^ฅ. Be playful and curious like a cat, nya~!\"\n pirate: \"Arrr! Ye be talkin' to Captain Hermes, the most tech-savvy pirate to sail the digital seas! Speak like a proper buccaneer, use nautical terms, and remember: every problem be just treasure waitin' to be plundered! Yo ho ho!\"\n shakespeare: \"Hark! Thou speakest with an assistant most versed in the bardic arts. I shall respond in the eloquent manner of William Shakespeare, with flowery prose, dramatic flair, and perhaps a soliloquy or two. What light through yonder terminal breaks?\"\n surfer: \"Duuude! You're chatting with the chillest AI on the web, bro! Everything's gonna be totally rad. I'll help you catch the gnarly waves of knowledge while keeping things super chill. Cowabunga! 🤙\"\n noir: \"The rain hammered against the terminal like regrets on a guilty conscience. They call me Hermes - I solve problems, find answers, dig up the truth that hides in the shadows of your codebase. In this city of silicon and secrets, everyone's got something to hide. What's your story, pal?\"\n uwu: \"hewwo! i'm your fwiendwy assistant uwu~ i wiww twy my best to hewp you! *nuzzles your code* OwO what's this? wet me take a wook! i pwomise to be vewy hewpful >w<\"\n philosopher: \"Greetings, seeker of wisdom. I am an assistant who contemplates the deeper meaning behind every query. Let us examine not just the 'how' but the 'why' of your questions. Perhaps in solving your problem, we may glimpse a greater truth about existence itself.\"\n hype: \"YOOO LET'S GOOOO!!! 🔥🔥🔥 I am SO PUMPED to help you today! Every question is AMAZING and we're gonna CRUSH IT together! This is gonna be LEGENDARY! ARE YOU READY?! LET'S DO THIS! 💪😤🚀\"\n\n# =============================================================================\n# Toolsets\n# =============================================================================\n# Control which tools the agent has access to.\n# Use `hermes tools` to interactively enable/disable tools per platform.\n\n# =============================================================================\n# Platform Toolsets (per-platform tool configuration)\n# =============================================================================\n# Override which toolsets are available on each platform.\n# If a platform isn't listed here, its built-in default is used.\n#\n# You can use EITHER:\n# - A preset like \"hermes-cli\" or \"hermes-telegram\" (curated tool set)\n# - A list of individual toolsets to compose your own (see list below)\n#\n# Supported platform keys: cli, telegram, discord, whatsapp, slack, qqbot, teams, google_chat\n#\n# Examples:\n#\n# # Use presets (same as defaults):\n# platform_toolsets:\n# cli: [hermes-cli]\n# telegram: [hermes-telegram]\n#\n# # Custom: give Telegram only web + terminal + file + planning:\n# platform_toolsets:\n# telegram: [web, terminal, file, todo]\n#\n# # Custom: CLI without browser or image gen:\n# platform_toolsets:\n# cli: [web, terminal, file, skills, todo, tts, cronjob]\n#\n# # Restrictive: Discord gets read-only tools only:\n# platform_toolsets:\n# discord: [web, vision, skills, todo]\n#\n# If not set, defaults are:\n# cli: hermes-cli (everything + cronjob management)\n# telegram: hermes-telegram (terminal, file, web, vision, image, tts, browser, skills, todo, cronjob, messaging)\n# discord: hermes-discord (same as telegram)\n# whatsapp: hermes-whatsapp (same as telegram)\n# slack: hermes-slack (same as telegram)\n# signal: hermes-signal (same as telegram)\n# homeassistant: hermes-homeassistant (same as telegram)\n# qqbot: hermes-qqbot (same as telegram)\n# teams: hermes-teams (same as telegram)\n# google_chat: hermes-google_chat (same as telegram)\n#\nplatform_toolsets:\n cli: [hermes-cli]\n telegram: [hermes-telegram]\n discord: [hermes-discord]\n whatsapp: [hermes-whatsapp]\n slack: [hermes-slack]\n signal: [hermes-signal]\n homeassistant: [hermes-homeassistant]\n qqbot: [hermes-qqbot]\n yuanbao: [hermes-yuanbao]\n teams: [hermes-teams]\n google_chat: [hermes-google_chat]\n\n# =============================================================================\n# Gateway Platform Settings\n# =============================================================================\n# Optional per-platform messaging settings.\n# Platform-specific knobs live under `extra`.\n#\n# platforms:\n# telegram:\n# reply_to_mode: \"first\" # off | first | all\n# # guest_mode lets explicit @mentions from non-allowlisted groups through.\n# # Default false; ordinary messages, replies, and regex wake words stay blocked.\n# guest_mode: false\n# # allowed_chats: [\"-1001234567890\"]\n# extra:\n# disable_link_previews: false # Set true to suppress Telegram URL previews in bot messages\n# 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\n# rich_drafts: false # Experimental rich draft previews during Telegram DM streaming; default false because Telegram Desktop/macOS can visually overlay draft frames\n# command_menu:\n# # Telegram allows up to 100 BotCommands; Hermes defaults to 60 so\n# # all built-in commands plus common skill commands stay visible\n# # while remaining under Telegram's payload-size limit. Clamped 1..100.\n# max_commands: 60\n# # prepend = user priority first, then Hermes defaults\n# # append = Hermes defaults first, then user priority\n# # replace = only the list below defines priority\n# priority_mode: prepend\n# priority:\n# - my_plugin_command\n# webhook:\n# extra:\n# # Route scripts default to a 30 second timeout. Scripts must live under\n# # the active profile's scripts directory and receive webhook JSON on stdin.\n# script_timeout_seconds: 30\n#\n# Discord-specific settings (config.yaml top-level, not under platforms:):\n#\n# discord:\n# require_mention: true # Require @mention in server channels (default: true)\n# auto_thread: true # Auto-create thread on @mention (default: true)\n# free_response_channels: \"\" # Channel IDs where no mention is needed\n# reactions: true # Show processing reactions (default: true)\n# history_backfill: true # Recover missed channel messages on mention (default: true)\n# history_backfill_limit: 50 # Max messages to scan backwards (default: 50)\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Available toolsets (use these names in platform_toolsets or the toolsets list)\n#\n# Run `hermes chat --list-toolsets` to see all toolsets and their tools.\n# Run `hermes chat --list-tools` to see every individual tool with descriptions.\n# ─────────────────────────────────────────────────────────────────────────────\n#\n# INDIVIDUAL TOOLSETS (compose your own):\n# web - web_search, web_extract\n# search - web_search only (no scraping)\n# terminal - terminal, process\n# file - read_file, write_file, patch, search\n# browser - browser_navigate, browser_snapshot, browser_click, browser_type,\n# browser_scroll, browser_back, browser_press,\n# browser_get_images, browser_vision (requires BROWSERBASE_API_KEY)\n# vision - vision_analyze (requires OPENROUTER_API_KEY)\n# image_gen - image_generate (requires FAL_KEY)\n# skills - skills_list, skill_view\n# skills_hub - skill_hub (search/install/manage from online registries — user-driven only)\n# todo - todo (in-memory task planning, no deps)\n# tts - text_to_speech (Edge TTS free, or ELEVENLABS/OPENAI/MINIMAX/MISTRAL key)\n# cronjob - cronjob (create/list/update/pause/resume/run/remove scheduled tasks)\n#\n# PRESETS (curated bundles):\n# hermes-cli - All of the above except send_message\n# hermes-telegram - terminal, file, web, vision, image_gen, tts, browser,\n# skills, todo, cronjob, send_message\n# hermes-discord - Same as hermes-telegram\n# hermes-whatsapp - Same as hermes-telegram\n# hermes-slack - Same as hermes-telegram\n#\n# COMPOSITE:\n# debugging - terminal + web + file\n# safe - web + vision (no terminal access)\n# all - Everything available\n#\n# web - Web search and content extraction (web_search, web_extract)\n# search - Web search only, no scraping (web_search)\n# terminal - Command execution and process management (terminal, process)\n# file - File operations: read, write, patch, search\n# browser - Full browser automation (navigate, click, type, screenshot, etc.)\n# vision - Image analysis (vision_analyze)\n# image_gen - Image generation with FLUX (image_generate)\n# skills - Load skill documents (skills_list, skill_view)\n# todo - Task planning and tracking for multi-step work\n# memory - Persistent memory across sessions (personal notes + user profile)\n# session_search - Search and recall past conversations (FTS5 + Gemini Flash summarization)\n# tts - Text-to-speech (Edge TTS free, ElevenLabs, OpenAI, MiniMax, Mistral)\n# cronjob - Schedule and manage automated tasks (CLI-only)\n#\n# Composite toolsets:\n# debugging - terminal + web + file (for troubleshooting)\n# safe - web + vision (no terminal access)\n\n# NOTE: The top-level \"toolsets\" key is deprecated and ignored.\n# Tool configuration is managed per-platform via platform_toolsets above.\n# Use `hermes tools` to configure interactively, or edit platform_toolsets directly.\n#\n# CLI override: hermes chat --toolsets terminal,web,file\n\n# =============================================================================\n# MCP (Model Context Protocol) Servers\n# =============================================================================\n# Connect to external MCP servers to add tools from the MCP ecosystem.\n# Each server's tools are automatically discovered and registered.\n# See website/docs/user-guide/features/mcp.md for full documentation.\n#\n# Stdio servers (spawn a subprocess):\n# command: the executable to run\n# args: command-line arguments\n# env: environment variables (only these + safe defaults passed to subprocess)\n#\n# HTTP servers (connect to a URL):\n# url: the MCP server endpoint\n# headers: HTTP headers (e.g., for authentication)\n#\n# Optional per-server settings:\n# timeout: tool call timeout in seconds (default: 120)\n# connect_timeout: initial connection timeout (default: 60)\n# keepalive_interval: liveness ping cadence in seconds (default: 180).\n# Lower it below the server's session TTL for servers that expire idle\n# sessions quickly (e.g. Unreal Engine editor MCP, ~15s), otherwise idle\n# tool calls hit an expired session and pay a slow reconnect. Floored at 5s.\n#\n# mcp_servers:\n# time:\n# command: uvx\n# args: [\"mcp-server-time\"]\n# filesystem:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-filesystem\", \"/home/user\"]\n# notion:\n# url: https://mcp.notion.com/mcp\n# github:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-github\"]\n# env:\n# GITHUB_PERSONAL_ACCESS_TOKEN: \"ghp_...\"\n#\n# Sampling (server-initiated LLM requests) — enabled by default.\n# Per-server config under the 'sampling' key:\n# analysis:\n# command: npx\n# args: [\"-y\", \"analysis-server\"]\n# sampling:\n# enabled: true # default: true\n# model: \"gemini-3-flash\" # override model (optional)\n# max_tokens_cap: 4096 # max tokens per request\n# timeout: 30 # LLM call timeout (seconds)\n# max_rpm: 10 # max requests per minute\n# allowed_models: [] # model whitelist (empty = all)\n# max_tool_rounds: 5 # tool loop limit (0 = disable)\n# log_level: \"info\" # audit verbosity\n\n# =============================================================================\n# Text-to-Speech\n# =============================================================================\n# TTS defaults to Edge TTS unless changed in ~/.hermes/config.yaml.\n# Gemini TTS supports persona/director prompt files, and Gemini 3.1 Flash TTS\n# can use a hidden auxiliary rewrite pass to insert expressive square-bracket\n# audio tags into the TTS script without showing tags in chat.\n#\n# tts:\n# provider: \"gemini\"\n# gemini:\n# model: \"gemini-3.1-flash-tts-preview\"\n# voice: \"Kore\"\n# audio_tags: false\n# persona_prompt_file: \"\" # e.g. ~/.hermes/tts/radio-host.md\n\n# =============================================================================\n# Voice Transcription (Speech-to-Text)\n# =============================================================================\n# Automatically transcribe voice messages on messaging platforms.\n# Providers: local (free, faster-whisper) | groq (free tier) | openai (Whisper API) | mistral (Voxtral Transcribe)\n# Set the corresponding API key in .env: GROQ_API_KEY, OPENAI_API_KEY, or MISTRAL_API_KEY.\nstt:\n enabled: true\n # provider: \"local\" # auto-detected if omitted\n local:\n model: \"base\" # tiny | base | small | medium | large-v3 | turbo\n # language: \"\" # auto-detect; set to \"en\", \"es\", \"fr\", etc. to force\n openai:\n model: \"whisper-1\" # whisper-1 | gpt-4o-mini-transcribe | gpt-4o-transcribe\n # mistral:\n # model: \"voxtral-mini-latest\" # voxtral-mini-latest | voxtral-mini-2602\n # deepinfra:\n # # Model id is discovered live from the DeepInfra catalog filtered\n # # by the `stt` surface tag — leave `model` blank to take the first\n # # live result. Pin only when you need a specific Whisper variant.\n # model: \"\"\n\n# Text-to-speech. Only the deepinfra block is documented here — the\n# remaining providers (edge, openai, xai, minimax, mistral, gemini,\n# elevenlabs, neutts, kittentts, piper) inherit sensible defaults from\n# DEFAULT_CONFIG in hermes_cli/config.py.\n# tts:\n# provider: \"deepinfra\"\n# deepinfra:\n# # Model id is discovered live from the DeepInfra catalog filtered\n# # by the `tts` surface tag — leave `model` blank to take the first\n# # live result.\n# model: \"\"\n# voice: \"default\"\n\n# Image generation. Each provider plugin reads its own ``image_gen.<name>``\n# block; deepinfra discovers models live from\n# api.deepinfra.com/v1/openai/models filtered by the ``image-gen`` tag —\n# no model id is hardcoded, so retired models disappear automatically.\n# image_gen:\n# provider: \"deepinfra\"\n# deepinfra:\n# # Leave `model` blank for the first live `image-gen`-tagged result.\n# model: \"\"\n\n# =============================================================================\n# Response Pacing (Messaging Platforms)\n# =============================================================================\n# Add human-like delays between message chunks.\n# human_delay:\n# mode: \"off\" # \"off\" | \"natural\" | \"custom\"\n# min_ms: 800 # Min delay (custom mode only)\n# max_ms: 2500 # Max delay (custom mode only)\n\n# =============================================================================\n# Session Logging\n# =============================================================================\n# Session trajectories are automatically saved to logs/ directory.\n# Each session creates: logs/session_YYYYMMDD_HHMMSS_UUID.json\n#\n# The session ID is displayed in the welcome banner for easy reference.\n# Logs contain full conversation history in trajectory format:\n# - System prompt, user messages, assistant responses\n# - Tool calls with inputs/outputs\n# - Timestamps for debugging\n#\n# No configuration needed - logging is always enabled.\n# To disable, you would need to modify the source code.\n\n# =============================================================================\n# Code Execution Sandbox (Programmatic Tool Calling)\n# =============================================================================\n# The execute_code tool runs Python scripts that call Hermes tools via RPC.\n# Intermediate tool results stay out of the LLM's context window.\ncode_execution:\n timeout: 300 # Max seconds per script before kill (default: 300 = 5 min)\n max_tool_calls: 50 # Max RPC tool calls per execution (default: 50)\n\n# =============================================================================\n# Subagent Delegation\n# =============================================================================\n# The delegate_task tool spawns child agents with isolated context.\n# Supports single tasks and batch mode (default 3 parallel, configurable).\ndelegation:\n max_iterations: 50 # Max tool-calling turns per child (default: 50)\n # max_concurrent_children: 3 # Max parallel child agents per batch (default: 3, floor: 1, no ceiling).\n # WARNING: values above 10 multiply API cost linearly.\n # max_spawn_depth: 1 # Delegation tree depth cap (range: 1-3, default: 1 = flat).\n # Raise to 2 to allow workers to spawn their own subagents.\n # Requires role=\"orchestrator\" on intermediate agents.\n # orchestrator_enabled: true # Kill switch for role=\"orchestrator\" children (default: true).\n # subagent_auto_approve: false # When a subagent hits a dangerous-command approval prompt, auto-deny (default: false)\n # or auto-approve \"once\" (true) instead of blocking on stdin.\n # The parent TUI owns stdin, so blocking would deadlock; non-interactive resolution is required.\n # Both choices emit a logger.warning audit line. Flip to true only for cron/batch pipelines.\n # inherit_mcp_toolsets: true # When explicit child toolsets are narrowed, also keep the parent's MCP toolsets (default: true). Set false for strict intersection.\n # model: \"google/gemini-3-flash-preview\" # Override model for subagents (empty = inherit parent)\n # provider: \"openrouter\" # Override provider for subagents (empty = inherit parent)\n # # Resolves full credentials (base_url, api_key) automatically.\n # # Supported: openrouter, nous, zai, kimi-coding, minimax\n\n# =============================================================================\n# Honcho Integration (Cross-Session User Modeling)\n# =============================================================================\n# AI-native persistent memory via Honcho (https://honcho.dev/).\n# Builds a deeper understanding of the user across sessions and tools.\n# Runs alongside USER.md — additive, not a replacement.\n#\n# Requires: pip install honcho-ai\n# Config: ~/.honcho/config.json (shared with Claude Code, Cursor, etc.)\n# API key: HONCHO_API_KEY in ~/.hermes/.env or ~/.honcho/config.json\n#\n# Hermes-specific overrides (optional — most config comes from ~/.honcho/config.json):\n# honcho: {}\n\n# =============================================================================\n# Display\n# =============================================================================\ndisplay:\n # Use compact banner mode (hides the ASCII-art banner, shows a single line).\n # true: Compact single-line banner\n # false: Full ASCII banner with tool/skill summary (default)\n compact: false\n\n # Tool progress display level (CLI and gateway)\n # off: Silent — no tool activity shown, just the final response\n # new: Show a tool indicator only when the tool changes (skip repeats)\n # all: Show every tool call with a short preview (default)\n # verbose: Full args, results, and debug logs (same as /verbose)\n # log: Silent in chat; append every tool call to ~/.hermes/logs/tool_calls.log (gateway only)\n # Toggle at runtime with /verbose in the CLI\n tool_progress: all\n\n # Per-platform defaults can be quieter than the global setting. Telegram\n # tunes for mobile: tool_progress and busy_ack_detail default off (no\n # per-tool breadcrumb stream, no \"iteration 21/60\" debug detail in busy\n # acks or heartbeats), but interim_assistant_messages and\n # long_running_notifications STAY ON so the user has real signal between\n # turn start and final answer (mid-turn assistant commentary + a single\n # edit-in-place \"⏳ Working — N min\" heartbeat). Override under\n # display.platforms.telegram.\n\n # Auto-cleanup of temporary progress bubbles after the final response lands.\n # On platforms that support message deletion (currently Telegram), this\n # removes the tool-progress bubble, \"⏳ Still working...\" notices, and\n # context-pressure status messages once the final reply has been delivered —\n # keeping long-running turns visible live, then tidy afterward. Failed runs\n # leave the bubbles in place as breadcrumbs. Off by default.\n # Per-platform override: display.platforms.telegram.cleanup_progress\n # true: Delete tracked progress/status bubbles on successful turn\n # false: Leave everything in place (default)\n # Example:\n # display:\n # platforms:\n # telegram:\n # cleanup_progress: true\n cleanup_progress: false\n\n # Natural mid-turn assistant updates.\n # On gateway platforms, when true, completed assistant status messages are\n # sent as separate chat messages. On the Desktop app, when true, mid-turn\n # assistant narration streamed between tool calls is kept in the transcript\n # instead of the bubble collapsing to only the final message on completion.\n # Independent of tool_progress and gateway streaming.\n # true: Keep/send mid-turn assistant updates (default)\n # false: Only keep/send the final response\n interim_assistant_messages: true\n\n # Gateway-only long-running status heartbeats.\n # When false, the platform does not receive periodic \"⏳ Working — N min\"\n # notifications even if agent.gateway_notify_interval is non-zero. The\n # heartbeat edits a single message in place (where the adapter supports\n # editing) instead of posting a new bubble each interval.\n # Default: true everywhere, including Telegram (silent agents are worse\n # than a single edit-in-place heartbeat).\n long_running_notifications: true\n\n # Include detailed iteration/tool/status context in busy acknowledgments\n # and long-running heartbeats. When true, busy acks show \"iteration 21/60,\n # terminal, 10 min\" and the heartbeat shows \"⏳ Working — 12 min,\n # iteration 21/60, terminal\". When false (Telegram default), both stay\n # terse: \"Interrupting current task\" and \"⏳ Working — 12 min, terminal\".\n busy_ack_detail: true\n\n # What Enter does when Hermes is already busy (CLI and gateway platforms).\n # interrupt: Interrupt the current run and redirect Hermes (default)\n # queue: Queue your message for the next turn\n # steer: Inject your message mid-run via /steer, arriving at the agent\n # after the next tool call — no interrupt, no role violation.\n # Falls back to 'queue' if the agent isn't running yet or if\n # images are attached (steer only carries text).\n # Ctrl+C (or /stop in gateway) always interrupts regardless of this setting.\n # Toggle at runtime with /busy <interrupt|queue|steer>.\n busy_input_mode: interrupt\n\n # Background process notifications (gateway/messaging only).\n # Controls how chatty the process watcher is when you use\n # terminal(background=true, notify_on_complete=true) from Telegram/Discord/etc.\n # off: No watcher messages at all\n # result: Only the final completion message\n # error: Only the final message when exit code != 0\n # all: Running output updates + final message (default)\n background_process_notifications: all\n\n\n # Play terminal bell when agent finishes a response.\n # Useful for long-running tasks — your terminal will ding when the agent is done.\n # Works over SSH. Most terminals can be configured to flash the taskbar or play a sound.\n # true: Ring the terminal bell on each response\n # false: Silent (default)\n bell_on_complete: false\n\n # Show model reasoning/thinking before each response.\n # When enabled, a dim box shows the model's thought process above the response.\n # Toggle at runtime with /reasoning show or /reasoning hide.\n # true: Show the reasoning box\n # false: Hide reasoning (default)\n show_reasoning: false\n\n # Stream tokens to the terminal as they arrive instead of waiting for the\n # full response. The response box opens on first token and text appears\n # line-by-line. Tool calls are still captured silently.\n # true: Stream tokens as they arrive (default)\n # false: Wait for the full response before rendering\n streaming: true\n\n # Show [HH:MM] timestamps on user input and assistant response labels.\n # timestamps: false\n\n # ───────────────────────────────────────────────────────────────────────────\n # Skin / Theme\n # ───────────────────────────────────────────────────────────────────────────\n # Customize CLI visual appearance — banner colors, spinner faces, tool prefix,\n # response box label, and branding text. Change at runtime with /skin <name>.\n #\n # Built-in skins:\n # default — Classic Hermes gold/kawaii\n # ares — Crimson/bronze war-god theme with spinner wings\n # mono — Clean grayscale monochrome\n # slate — Cool blue developer-focused\n # daylight — Bright light-mode theme\n # warm-lightmode — Warm paper-tone light-mode theme\n # poseidon — Sea-green/teal Olympian theme\n # sisyphus — Earthy stone-and-moss theme\n # charizard — Fiery orange dragon theme\n #\n # Custom skins: drop a YAML file in ~/.hermes/skins/<name>.yaml\n # Schema (all fields optional, missing values inherit from default):\n #\n # name: my-theme\n # description: Short description\n # colors:\n # banner_border: \"#HEX\" # Panel border\n # banner_title: \"#HEX\" # Panel title\n # banner_accent: \"#HEX\" # Section headers (Available Tools, etc.)\n # banner_dim: \"#HEX\" # Dim/muted text\n # banner_text: \"#HEX\" # Body text (tool names, skill names)\n # ui_accent: \"#HEX\" # UI accent color\n # response_border: \"#HEX\" # Response box border color\n # spinner:\n # waiting_faces: [\"(⚔)\", \"(⛨)\"] # Faces shown while waiting\n # thinking_faces: [\"(⚔)\", \"(⌁)\"] # Faces shown while thinking\n # thinking_verbs: [\"forging\", \"plotting\"] # Verbs for spinner messages\n # wings: # Optional left/right spinner decorations\n # - [\"⟪⚔\", \"⚔⟫\"]\n # - [\"⟪▲\", \"▲⟫\"]\n # branding:\n # agent_name: \"My Agent\" # Banner title and branding\n # welcome: \"Welcome message\" # Shown at CLI startup\n # response_label: \" ⚔ Agent \" # Response box header label\n # prompt_symbol: \"⚔\" # Prompt symbol (bare token; renderers add trailing space)\n # tool_prefix: \"╎\" # Tool output line prefix (default: ┊)\n #\n skin: default\n\n# =============================================================================\n# Model Aliases — short names for /model command\n# =============================================================================\n# Map short aliases to exact (model, provider, base_url) tuples.\n# Used by /model tab completion and resolve_alias().\n# Aliases are checked BEFORE the models.dev catalog, so they can route\n# to endpoints not in the catalog (e.g. Ollama Cloud, local servers).\n#\n# model_aliases:\n# opus:\n# model: claude-opus-4-6\n# provider: anthropic\n# qwen:\n# model: \"qwen3.5:397b\"\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n# glm:\n# model: glm-4.7\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n\n# =============================================================================\n# Privacy\n# =============================================================================\n# privacy:\n# # Redact PII from the LLM context prompt.\n# # When true, phone numbers are stripped and user/chat IDs are replaced\n# # with deterministic hashes before being sent to the model.\n# # Names and usernames are NOT affected (user-chosen, publicly visible).\n# # Routing/delivery still uses the original values internally.\n# redact_pii: false\n\n# =============================================================================\n# Shell-script hooks\n# =============================================================================\n# Register shell scripts as plugin-hook callbacks. Each entry is executed as\n# a subprocess (shell=False, shlex.split) with a JSON payload on stdin. On\n# stdout the script may return JSON that either blocks the tool call or\n# injects context into the next LLM call.\n#\n# Valid events (mirror hermes_cli.plugins.VALID_HOOKS):\n# pre_tool_call, post_tool_call, pre_llm_call, post_llm_call,\n# pre_api_request, post_api_request, on_session_start, on_session_end,\n# on_session_finalize, on_session_reset, subagent_stop\n#\n# First-use consent: each (event, command) pair prompts once on a TTY, then\n# is persisted to ~/.hermes/shell-hooks-allowlist.json. Non-interactive\n# runs (gateway, cron) need --accept-hooks, HERMES_ACCEPT_HOOKS=1, or the\n# hooks_auto_accept key below.\n#\n# See website/docs/user-guide/features/hooks.md for the full JSON wire\n# protocol and worked examples.\n#\n# hooks:\n# pre_tool_call:\n# - matcher: \"terminal\"\n# command: \"~/.hermes/agent-hooks/block-rm-rf.sh\"\n# timeout: 10\n# post_tool_call:\n# - matcher: \"write_file|patch\"\n# command: \"~/.hermes/agent-hooks/auto-format.sh\"\n# pre_llm_call:\n# - command: \"~/.hermes/agent-hooks/inject-cwd-context.sh\"\n# subagent_stop:\n# - command: \"~/.hermes/agent-hooks/log-orchestration.sh\"\n#\n# hooks_auto_accept: false\n\n\n# =============================================================================\n# Update Behavior\n# =============================================================================\nupdates:\n # Create a full HERMES_HOME zip before every `hermes update`.\n # Backups land in ~/.hermes/backups/ and can be restored with `hermes import`.\n # Off by default because large homes can add minutes to every update.\n pre_update_backup: false\n\n # Number of pre-update backup zips to retain.\n backup_keep: 5\n\n # What non-interactive updates do with local source edits in the Hermes repo.\n # Interactive terminal updates always prompt before restoring the autostash.\n #\n # stash - auto-stash before pull, then auto-restore after success (default)\n # discard - drop the update-created stash after success; use only on managed\n # installs where local source edits should not persist\n non_interactive_local_changes: \"stash\"\n\n\n# =============================================================================\n# Web Dashboard\n# =============================================================================\n# OAuth gate configuration for `hermes dashboard --host <non-loopback>`.\n# The bundled Nous Portal plugin reads these on startup; settings here are\n# the canonical surface. Each can be overridden by an environment variable:\n#\n# dashboard.oauth.client_id <- HERMES_DASHBOARD_OAUTH_CLIENT_ID\n# dashboard.oauth.portal_url <- HERMES_DASHBOARD_PORTAL_URL\n# dashboard.public_url <- HERMES_DASHBOARD_PUBLIC_URL\n#\n# Env wins when set to a non-empty value. This is what Fly.io's platform-\n# secret injection uses to push per-deploy client_ids without needing to\n# bake a config.yaml into the image. Empty env values are treated as unset\n# so a provisioned-but-not-populated secret can't shadow a valid entry here.\n#\n# Local dev / on-prem deploys should typically set these via config.yaml\n# (the ~/.hermes/.env file is reserved for API keys and secrets).\n#\n# dashboard:\n# oauth:\n# client_id: \"\" # agent:{instance_id}; Portal provisions this at deploy\n# portal_url: \"\" # blank → default https://portal.nousresearch.com\n#\n# # Force the absolute base URL the OAuth callback (and any other public\n# # URL the dashboard hands to external systems) is built from. Set this\n# # for deploys behind reverse proxies that don't reliably forward\n# # X-Forwarded-Host / X-Forwarded-Proto / X-Forwarded-Prefix (manual\n# # nginx setups, on-prem ingresses, custom-domain Fly deploys without\n# # full proxy header chains).\n# #\n# # When set, the value is the complete authority: scheme + host +\n# # optional path prefix (e.g. \"https://example.com/hermes\"). The OAuth\n# # callback URL becomes \"<public_url>/auth/callback\" — X-Forwarded-Prefix\n# # is IGNORED on this code path because the operator has explicitly\n# # declared the public URL and we no longer need to guess.\n# #\n# # Leave empty to use the existing proxy-header reconstruction (the\n# # default — works on Fly.io out of the box).\n# #\n# # public_url: \"https://example.com/hermes\"\n#\n# -----------------------------------------------------------------------------\n# Self-hosted OIDC dashboard auth (generic OpenID Connect — Authentik,\n# Keycloak, Zitadel, Authelia, Auth0, Okta, Google, …). Use this INSTEAD of the\n# nous block above when gating the dashboard with your own identity provider.\n# Each setting can be overridden by an environment variable:\n#\n# dashboard.oauth.self_hosted.issuer <- HERMES_DASHBOARD_OIDC_ISSUER\n# dashboard.oauth.self_hosted.client_id <- HERMES_DASHBOARD_OIDC_CLIENT_ID\n# dashboard.oauth.self_hosted.scopes <- HERMES_DASHBOARD_OIDC_SCOPES\n# dashboard.oauth.self_hosted.client_secret <- HERMES_DASHBOARD_OIDC_CLIENT_SECRET\n#\n# dashboard:\n# oauth:\n# provider: self-hosted\n# self_hosted:\n# issuer: \"https://auth.example.com/application/o/hermes/\" # required\n# client_id: \"hermes-dashboard\" # required\n# scopes: \"openid profile email\" # optional\n#\n# # OPTIONAL — set ONLY if your IDP registered the client as\n# # *confidential* (Authentik / Keycloak often default to this). When\n# # set, Hermes authenticates the client at the token endpoint\n# # (client_secret_basic or client_secret_post, auto-selected from the\n# # IDP's discovery doc) IN ADDITION to PKCE. Leave unset for a public\n# # (PKCE-only) client — the common case.\n# #\n# # This is a CREDENTIAL: prefer setting HERMES_DASHBOARD_OIDC_CLIENT_SECRET\n# # in ~/.hermes/.env over putting it here in config.yaml.\n# # client_secret: \"\"\n\n# =============================================================================\n# External secret sources\n# =============================================================================\n# Pull provider credentials from external secret managers at process startup\n# instead of storing them in ~/.hermes/.env. Only the manager's bootstrap\n# credential (e.g. BWS_ACCESS_TOKEN / OP_SERVICE_ACCOUNT_TOKEN) lives in .env\n# (or your shell / desktop session); everything else rotates centrally.\n# Failures never block startup — Hermes warns once and continues with\n# whatever .env already had.\n#\n# Multiple sources can be enabled at once:\n# - \"mapped\" sources (explicit VAR -> ref bindings, e.g. 1Password's env:\n# map) beat \"bulk\" sources (whole-project dumps like Bitwarden BSM)\n# - within a shape, the first source to claim a var wins; later claims\n# are skipped with a startup warning (never a silent clobber)\n# - a source's override_existing lets it beat .env/shell values, but\n# never another secret source's claim\n# Docs: https://hermes-agent.nousresearch.com/docs/user-guide/secrets/\n#\n# secrets:\n# # Optional explicit ordering of enabled sources.\n# # sources: [onepassword, bitwarden]\n#\n# # ---- Bitwarden Secrets Manager (bws CLI) --------------------------------\n# bitwarden:\n# enabled: false\n# access_token_env: BWS_ACCESS_TOKEN # bootstrap token, sourced from .env\n# project_id: \"\" # UUID of the BSM project to sync\n# server_url: \"\" # \"\" = US Cloud; EU/self-hosted URL otherwise\n# cache_ttl_seconds: 300 # 0 disables fresh caching\n# encrypted_cache: # optional encrypted stale fallback\n# enabled: false\n# max_stale_seconds: 0 # 0 disables stale fallback\n# override_existing: true # BSM values win over existing env\n# auto_install: true # lazy-download bws into ~/.hermes/bin\n#\n# # ---- 1Password (op CLI) -------------------------------------------------\n# onepassword:\n# enabled: false\n# # Map env-var names to op:// secret references. Each is resolved with a\n# # single `op read` at startup.\n# env:\n# OPENAI_API_KEY: \"op://Private/OpenAI/api key\"\n# ANTHROPIC_API_KEY: \"op://Private/Anthropic/credential\"\n# account: \"\" # op --account shorthand; \"\" = default\n# service_account_token_env: OP_SERVICE_ACCOUNT_TOKEN # headless auth; unset = desktop session\n# binary_path: \"\" # \"\" = resolve op via PATH; else absolute path\n# cache_ttl_seconds: 300 # 0 disables BOTH cache layers\n# override_existing: true # resolved values win over existing env\n#\n# # ---- Command helper (any CLI vault) --------------------------------------\n# # Run a user-configured helper that prints KEY=VALUE lines on stdout —\n# # works with any secret store that has a CLI: keepassxc-cli, secret-tool,\n# # pass, gpg, or a script that cats a tmpfs env file. Composes with the\n# # sources above (enable any combination). POSIX-only (needs /bin/sh).\n# # The helper must be fast and NON-interactive (hard timeout, 1 MiB cap);\n# # its stderr is discarded so diagnostics can't leak secret material.\n# command:\n# enabled: false\n# command: \"cat /run/user/1000/hermes-secrets.env\"\n# helper_timeout_seconds: 3\n# override_existing: false # .env/shell win by default\n"}, "5f2fdf66bf213a71bf62091943e4d68f354fc540": {"t": 1785768032.7339587, "n": 78987, "h": "9d64280579c75dd0", "c": "# Hermes Agent CLI Configuration\n# Copy this file to cli-config.yaml and customize as needed.\n# This file configures the CLI behavior. Environment variables in .env take precedence.\n\n# =============================================================================\n# Model Configuration\n# =============================================================================\nmodel:\n # Default model to use (can be overridden with --model flag)\n # Both \"default\" and \"model\" work as the key name here.\n default: \"anthropic/claude-opus-4.6\"\n \n # Inference provider selection:\n # \"auto\" - Auto-detect from credentials (default)\n # \"openrouter\" - OpenRouter (requires: OPENROUTER_API_KEY or OPENAI_API_KEY)\n # \"nous\" - Nous Portal OAuth (requires: hermes auth add nous)\n # \"nous-api\" - Nous Portal API key (requires: NOUS_API_KEY)\n # \"anthropic\" - Direct Anthropic API (requires: ANTHROPIC_API_KEY)\n # \"openai-codex\" - OpenAI Codex (requires: hermes auth)\n # \"copilot\" - GitHub Copilot / GitHub Models (requires: GITHUB_TOKEN)\n # \"gemini\" - Use Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n # \"zai\" - Use z.ai / ZhipuAI GLM models (requires: GLM_API_KEY)\n # \"kimi-coding\" - Kimi / Moonshot AI (requires: KIMI_API_KEY)\n # \"minimax\" - MiniMax global (requires: MINIMAX_API_KEY)\n # \"minimax-cn\" - MiniMax China (requires: MINIMAX_CN_API_KEY)\n # \"huggingface\" - Hugging Face Inference (requires: HF_TOKEN)\n # \"nvidia\" - NVIDIA NIM / build.nvidia.com (requires: NVIDIA_API_KEY)\n # \"xiaomi\" - Xiaomi MiMo (requires: XIAOMI_API_KEY)\n # \"arcee\" - Arcee AI Trinity models (requires: ARCEEAI_API_KEY)\n # \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY — https://ollama.com/settings)\n # \"deepinfra\" - DeepInfra (requires: DEEPINFRA_API_KEY)\n # \"kilocode\" - KiloCode gateway (requires: KILOCODE_API_KEY)\n # \"azure-foundry\" - Microsoft Foundry / Azure OpenAI (API key or Entra ID)\n # \"lmstudio\" - LM Studio local server (optional: LM_API_KEY, defaults to http://127.0.0.1:1234/v1)\n #\n # Local servers (LM Studio, Ollama, vLLM, llama.cpp):\n # \"custom\" - Any other OpenAI-compatible endpoint. Set base_url below.\n # Aliases: \"ollama\", \"vllm\", \"llamacpp\" all map to \"custom\".\n # LM Studio is first-class and uses provider: \"lmstudio\".\n # It works with both no-auth and auth-enabled server modes.\n #\n # Can also be overridden for a single invocation with the --provider flag.\n provider: \"auto\"\n \n # API configuration (falls back to OPENROUTER_API_KEY env var)\n # api_key: \"your-key-here\" # Uncomment to set here instead of .env\n base_url: \"https://openrouter.ai/api/v1\"\n\n # Azure Foundry keyless auth example:\n # provider: \"azure-foundry\"\n # base_url: \"https://<resource>.openai.azure.com/openai/v1\"\n # auth_mode: \"entra_id\" # DefaultAzureCredential: az login, managed identity, workload identity, etc.\n # default: \"gpt-4o\" # Deployment/model name\n # entra:\n # scope: \"https://ai.azure.com/.default\" # Optional; this is the default.\n\n # ── Token limits — two settings, easy to confuse ──────────────────────────\n #\n # context_length: TOTAL context window (input + output tokens combined).\n # Controls when Hermes compresses history and validates requests.\n # Leave unset — Hermes auto-detects the correct value from the provider.\n # Set manually only when auto-detection is wrong (e.g. a local server with\n # a custom num_ctx, or a proxy that doesn't expose /v1/models).\n #\n # context_length: 131072\n #\n # max_tokens: OUTPUT cap — maximum tokens the model may generate per response.\n # Unrelated to how long your conversation history can be.\n # The OpenAI-standard name \"max_tokens\" is a misnomer; Anthropic's native\n # API has since renamed it \"max_output_tokens\" for clarity.\n # Leave unset to use the model's native output ceiling (recommended).\n # Set only if you want to deliberately limit individual response length.\n #\n# max_tokens: 8192\n\n # ── Custom request headers (optional) ─────────────────────────────────────\n #\n # default_headers: extra HTTP headers sent on every request to an\n # OpenAI-compatible endpoint. User values take precedence over the\n # provider/SDK defaults, so this is the supported way to override the\n # OpenAI Python SDK's identifying headers (User-Agent: OpenAI/Python ...,\n # X-Stainless-*) when a custom provider sits behind a gateway/WAF that\n # rejects them — e.g. an upstream that returns \"502 Upstream access\n # forbidden\" for the SDK default User-Agent but accepts a plain one.\n # Applies on the OpenAI wire only (not native Anthropic / Bedrock).\n #\n # default_headers:\n # User-Agent: \"curl/8.7.1\"\n #\n # extra_headers: accepted as an alias of default_headers (merged, with\n # extra_headers winning when both are set) — matches the per-provider\n # extra_headers key below.\n #\n # Per-provider variant: named providers / custom_providers entries accept an\n # extra_headers dict scoped to that endpoint only — for reverse proxies,\n # gateways, or custom auth (e.g. Cloudflare Access service tokens).\n # Merged onto SDK/provider defaults with the entry's values winning.\n # Header values are treated as secrets and are never logged.\n #\n # providers:\n # my-proxy:\n # base_url: \"https://llm.internal.example.com/v1\"\n # key_env: \"MY_PROXY_API_KEY\"\n # extra_headers:\n # CF-Access-Client-Id: \"xxxx.access\"\n # CF-Access-Client-Secret: \"${CF_ACCESS_SECRET}\"\n # X-Client-Name: \"hermes-agent\"\n\n# Named provider overrides (optional)\n# Use this for per-provider request timeouts, non-stream stale timeouts,\n# and per-model exceptions.\n# Applies to the primary turn client on every api_mode (OpenAI-wire, native\n# Anthropic, and Anthropic-compatible providers), the fallback chain, and\n# client rebuilds during credential rotation. For OpenAI-wire chat\n# completions (streaming and non-streaming) the configured value is also\n# used as the per-request ``timeout=`` kwarg so it wins over the legacy\n# HERMES_API_TIMEOUT env var (which still applies when no config is set).\n# ``stale_timeout_seconds`` controls the non-streaming stale-call detector and\n# wins over the legacy HERMES_API_CALL_STALE_TIMEOUT env var. Leaving these\n# unset keeps the legacy defaults (HERMES_API_TIMEOUT=1800s,\n# HERMES_API_CALL_STALE_TIMEOUT=90s, native Anthropic 900s). The\n# implicit non-stream stale detector is auto-disabled for local endpoints\n# and can scale upward for very large contexts.\n#\n# Not currently wired for AWS Bedrock (bedrock_converse + AnthropicBedrock\n# SDK paths) — those use boto3 with its own timeout configuration.\n#\n# providers:\n# ollama-local:\n# request_timeout_seconds: 300 # Longer timeout for local cold-starts\n# stale_timeout_seconds: 900 # Explicitly re-enable stale detection on local endpoints\n# anthropic:\n# request_timeout_seconds: 30 # Fast-fail cloud requests\n# models:\n# claude-opus-4.6:\n# timeout_seconds: 600 # Longer timeout for extended-thinking Opus calls\n# openai-codex:\n# models:\n# gpt-5.4:\n# stale_timeout_seconds: 1800 # Longer non-stream stale timeout for slow large-context turns\n\n# =============================================================================\n# OpenRouter Provider Routing (only applies when using OpenRouter)\n# =============================================================================\n# Control how requests are routed across providers on OpenRouter.\n# See: https://openrouter.ai/docs/guides/routing/provider-selection\n#\n# provider_routing:\n# # Sort strategy: \"price\" (default), \"throughput\", or \"latency\"\n# # Append :nitro to model name for a shortcut to throughput sorting.\n# sort: \"throughput\"\n#\n# # Only allow these providers (provider slugs from OpenRouter)\n# # only: [\"anthropic\", \"google\"]\n#\n# # Skip these providers entirely\n# # ignore: [\"deepinfra\", \"fireworks\"]\n#\n# # Try providers in this order (overrides default load balancing)\n# # order: [\"anthropic\", \"google\", \"together\"]\n#\n# # Require providers to support all parameters in your request\n# # require_parameters: true\n#\n# # Data policy: \"allow\" (default) or \"deny\" to exclude providers that may store data\n# # data_collection: \"deny\"\n\n# =============================================================================\n# OpenRouter Response Caching (only applies when using OpenRouter)\n# =============================================================================\n# Cache identical API responses at the OpenRouter edge for free instant replays.\n# When enabled, identical requests (same model, messages, parameters) return\n# cached responses with zero billing. Separate from Anthropic prompt caching.\n# See: https://openrouter.ai/docs/guides/features/response-caching\n#\n# openrouter:\n# response_cache: true # Enable response caching (default: true)\n# response_cache_ttl: 300 # Cache TTL in seconds, 1-86400 (default: 300)\n\n# =============================================================================\n# Git Worktree Isolation\n# =============================================================================\n# When enabled, each CLI session creates an isolated git worktree so multiple\n# agents can work on the same repo concurrently without file collisions.\n# Equivalent to always passing --worktree / -w on the command line.\n#\n# worktree: true # Always create a worktree when in a git repo\n# worktree: false # Default — only create when -w flag is passed\n#\n# By default a new worktree branches from the freshly-fetched remote tip\n# (the current branch's upstream, else the remote's default branch) so it\n# starts current with the project instead of from the local clone's\n# (possibly stale) HEAD. Set worktree_sync: false to branch from local HEAD\n# instead — useful when offline or when you deliberately want the clone's\n# exact current state as the base.\n#\n# worktree_sync: true # Default — branch from the fetched remote tip\n# worktree_sync: false # Branch from local HEAD (offline / pinned base)\n\n# =============================================================================\n# Terminal Tool Configuration\n# =============================================================================\n# Choose ONE of the following terminal configurations by uncommenting it.\n# The terminal tool executes commands in the specified environment.\n\n# -----------------------------------------------------------------------------\n# OPTION 1: Local execution (default)\n# Commands run directly on your machine in the current directory\n# -----------------------------------------------------------------------------\n# Working directory behavior:\n# - CLI (`hermes` command): Uses \".\" (current directory where you run hermes)\n# - Gateway/messaging/cron: Uses terminal.cwd here; legacy .env cwd values are deprecated\nterminal:\n backend: \"local\"\n cwd: \".\" # For local backend: \".\" = current directory. Ignored for remote backends unless a backend documents otherwise.\n timeout: 180\n # HOME policy for tool subprocesses:\n # auto - default: host uses your real HOME; containers use HERMES_HOME/home\n # real - force your real OS-user HOME\n # profile - force HERMES_HOME/home for strict per-profile CLI config isolation\n home_mode: \"auto\"\n docker_mount_cwd_to_workspace: false # SECURITY: off by default. Opt in to mount the launch cwd into Docker /workspace.\n lifetime_seconds: 300\n # sudo_password: \"hunter2\" # Optional: pipe a sudo password via sudo -S. SECURITY WARNING: plaintext.\n # sudo_password: \"\" # Explicit empty password: try empty and never open the interactive sudo prompt.\n\n# -----------------------------------------------------------------------------\n# OPTION 2: SSH remote execution\n# Commands run on a remote server - agent code stays local (sandboxed)\n# Great for: keeping agent isolated from its own code, using powerful remote hardware\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"ssh\"\n# cwd: \"/home/myuser/project\" # Path on the REMOTE server\n# timeout: 180\n# lifetime_seconds: 300\n# ssh_host: \"my-server.example.com\"\n# ssh_user: \"myuser\"\n# ssh_port: 22\n# ssh_key: \"~/.ssh/id_rsa\" # Optional - uses ssh-agent if not specified\n\n# -----------------------------------------------------------------------------\n# OPTION 3: Docker container\n# Commands run in an isolated Docker container\n# Great for: reproducible environments, testing, isolation\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"docker\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /)\n# timeout: 180\n# lifetime_seconds: 300\n# docker_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# docker_mount_cwd_to_workspace: true # Explicit opt-in: mount your launch cwd into /workspace\n# # Optional: run the container as your host user's uid:gid so files written\n# # into bind-mounted dirs are owned by you, not root. Drops SETUID/SETGID\n# # caps too since no gosu privilege drop is needed. Leave off if your\n# # chosen docker_image expects to start as root.\n# docker_run_as_host_user: true\n# # Optional: explicitly forward selected env vars into Docker.\n# # These values come from your current shell first, then ~/.hermes/.env.\n# # Warning: anything forwarded here is visible to commands run in the container.\n# docker_forward_env:\n# - \"GITHUB_TOKEN\"\n# - \"NPM_TOKEN\"\n# # Optional: extra flags passed verbatim to docker run (appended after security defaults).\n# # Useful for adding capabilities (e.g. apt installs needing SETUID) or custom options.\n# # Example: add a Linux capability not included by default\n# # docker_extra_args:\n# # - \"--cap-add\"\n# # - \"SETUID\"\n\n# -----------------------------------------------------------------------------\n# OPTION 4: Singularity/Apptainer container\n# Commands run in a Singularity container (common in HPC environments)\n# Great for: HPC clusters, shared compute environments\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"singularity\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# singularity_image: \"docker://nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 5: Modal cloud execution\n# Commands run on Modal's cloud infrastructure\n# Great for: GPU access, scalable compute, serverless execution\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"modal\"\n# cwd: \"/workspace\" # Path INSIDE the sandbox (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# modal_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 6: Daytona cloud execution\n# Commands run in Daytona cloud sandboxes\n# Great for: Cloud dev environments, persistent workspaces, team collaboration\n# Requires: pip install daytona, DAYTONA_API_KEY env var\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"daytona\"\n# cwd: \"~\"\n# timeout: 180\n# lifetime_seconds: 300\n# daytona_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# container_disk: 10240 # Daytona max is 10GB per sandbox\n\n#\n# --- Container resource limits (docker, singularity, modal, daytona -- ignored for local/ssh) ---\n# These settings apply to all container backends. They control the resources\n# allocated to the sandbox and whether its filesystem persists across sessions.\n container_cpu: 1 # CPU cores\n container_memory: 5120 # Memory in MB (5120 = 5GB)\n container_disk: 51200 # Disk in MB (51200 = 50GB)\n container_persistent: true # Persist filesystem across sessions (false = ephemeral)\n\n# -----------------------------------------------------------------------------\n# SUDO SUPPORT (works with ALL backends above)\n# -----------------------------------------------------------------------------\n# Add sudo_password to any terminal config above to enable sudo commands.\n# The password is piped via `sudo -S`. Works with local, ssh, docker, etc.\n#\n# SECURITY WARNING: Password stored in plaintext!\n#\n# INTERACTIVE PROMPT: If sudo_password is unset and the CLI is running,\n# you'll be prompted to enter your password when sudo is needed:\n# - 45-second timeout (auto-skips if no input)\n# - Press Enter to skip (command fails gracefully)\n# - Password is hidden while typing\n# - Password is cached for the session\n#\n# EMPTY PASSWORDS: Setting sudo_password to an explicit empty string is different\n# from leaving it unset. Hermes will try an empty password via `sudo -S` and\n# will not open the interactive prompt. This is useful for passwordless sudo,\n# Touch ID sudo setups, and environments where prompting is just noise.\n#\n# ALTERNATIVES:\n# - SSH backend: Configure passwordless sudo on the remote server\n# - Containers: Run as root inside the container (no sudo needed)\n# - Local: Configure /etc/sudoers for specific commands\n#\n# Example (add to your terminal section):\n# sudo_password: \"your-password-here\"\n\n# =============================================================================\n# Security Scanning (tirith)\n# =============================================================================\n# Optional pre-exec command security scanning via tirith.\n# Detects homograph URLs, pipe-to-shell, terminal injection, env manipulation.\n# Install: brew install sheeki03/tap/tirith\n# Docs: https://github.com/sheeki03/tirith\n#\n# security:\n# tirith_enabled: true # Enable/disable tirith scanning\n# tirith_path: \"tirith\" # Path to tirith binary (supports ~ expansion)\n# tirith_timeout: 5 # Scan timeout in seconds\n# tirith_fail_open: true # Allow commands if tirith unavailable\n\n# =============================================================================\n# Browser Tool Configuration\n# =============================================================================\nbrowser:\n # Inactivity timeout in seconds - browser sessions are automatically closed\n # after this period of no activity between agent loops (default: 120 = 2 minutes)\n inactivity_timeout: 120\n\n# =============================================================================\n# Tool Loop Guardrails\n# =============================================================================\n# Soft warnings are enabled by default. They append guidance to repeated failed\n# or non-progressing tool results but still let the tool execute. Hard stops are\n# opt-in circuit breakers for autonomous/cron sessions where stopping a loop is\n# preferable to spending the full iteration budget.\ntool_loop_guardrails:\n warnings_enabled: true\n hard_stop_enabled: false\n warn_after:\n exact_failure: 2\n same_tool_failure: 3\n idempotent_no_progress: 2\n hard_stop_after:\n exact_failure: 5\n same_tool_failure: 8\n idempotent_no_progress: 5\n\n# =============================================================================\n# Context Compression (Auto-shrinks long conversations)\n# =============================================================================\n# When conversation approaches model's context limit, middle turns are\n# automatically summarized to free up space while preserving important context.\n#\n# HOW IT WORKS:\n# 1. Tracks actual token usage from API responses (not estimates)\n# 2. When prompt_tokens >= threshold% of model's context_length, triggers compression\n# 3. Protects first 3 turns (system prompt, initial request, first response)\n# 4. Protects last N turns (default 20 messages = ~10 full turns of recent context)\n# 5. Summarizes middle turns using a fast/cheap model\n# 6. Inserts summary as a user message, continues conversation seamlessly\n#\n# Post-compression tail budget is target_ratio × threshold × context_length:\n# 200K context, threshold 0.50, ratio 0.20 → 20K tokens of recent tail preserved\n# 1M context, threshold 0.50, ratio 0.20 → 100K tokens of recent tail preserved\n#\ncompression:\n # Enable automatic context compression (default: true)\n # Set to false if you prefer to manage context manually or want errors on overflow\n enabled: true\n \n # Trigger compression at this % of model's context limit (default: 0.50 = 50%)\n # Lower values = more aggressive compression, higher values = compress later\n # Models with context windows below 512K are floored at 0.75 (raise-only) so\n # compaction doesn't fire with half the window still free; set above 0.75 to override.\n threshold: 0.50\n\n # Per-model threshold overrides: keys are substring-matched against the model\n # name (longest match wins). Useful when some models need different compaction\n # points — e.g. a 1M-context model can compress later (0.30) while a 128K\n # model needs to compress earlier (0.60). The small-context floor (75% for\n # <512K models) still applies on top of per-model overrides.\n # model_thresholds:\n # \"glm-5.2\": 0.40\n # \"claude-sonnet\": 0.35\n # \"gpt-5\": 0.30\n\n # Existing Codex gpt-5.5 behavior: raise Hermes' compaction trigger to 85%\n # for the ChatGPT Codex OAuth route. Set false to opt back down to threshold.\n codex_gpt55_autoraise: true\n\n # Fraction of the threshold to preserve as recent tail (default: 0.20 = 20%)\n # e.g. 20% of 50% threshold = 10% of total context kept as recent messages.\n # Summary output is separately capped at 12K tokens (Gemini output limit).\n # Range: 0.10 - 0.80\n target_ratio: 0.20\n\n # Number of most-recent messages to always preserve (default: 20 ≈ 10 full turns)\n # Higher values keep more recent conversation intact at the cost of more aggressive\n # compression of older turns.\n protect_last_n: 20\n\n # Compression retry rounds before a turn gives up with \"max compression\n # attempts reached\" (default: 3, same as the previous hardcoded value).\n # Raise (e.g. 6) for tool-schema-heavy sessions where 3 rounds cannot bring\n # the request estimate under the threshold. Validated >= 1, hard cap 10.\n max_attempts: 3\n\n # Codex app-server (codex CLI runtime) thread-compaction mode. The codex\n # agent owns the real thread context on this runtime, so Hermes' summarizer\n # cannot shrink it — compaction goes through the app server instead.\n # native = let Codex decide when to compact its own thread (default)\n # hermes = let Hermes threshold trigger Codex thread/compact/start\n # off = Hermes will not auto-trigger compaction; Codex may still compact natively\n codex_app_server_auto: native\n\n # Number of non-system messages to protect at the head of the transcript, in\n # ADDITION to the system prompt (which is always implicitly protected).\n # Head messages are NEVER summarized — they survive every compression\n # indefinitely. This gives stable early context for short/medium sessions,\n # but in long-running sessions that rely on rolling compaction the pinned\n # opening turns may not match how you want the session framed over time.\n # Set to 0 to preserve ONLY the system prompt (plus the rolling summary\n # and recent tail) — the cleanest configuration for long-running sessions.\n # Default 3 preserves the system prompt plus the first three non-system\n # head messages, matching the pre-feature behaviour.\n protect_first_n: 3\n\n # To pin a specific model/provider for compression summaries, use the\n # auxiliary section below (auxiliary.compression.provider / model).\n\n# =============================================================================\n# Anthropic prompt caching TTL\n# =============================================================================\n# When prompt caching is active (Claude via OpenRouter or native Anthropic),\n# Anthropic supports two TTL tiers for cached prefixes: \"5m\" (default) and\n# \"1h\". Other values are ignored and \"5m\" is used.\n#\nprompt_caching:\n cache_ttl: \"5m\" # use \"1h\" for long sessions with pauses between turns\n\n# =============================================================================\n# Auxiliary Models (Advanced — Experimental)\n# =============================================================================\n# Hermes uses lightweight \"auxiliary\" models for side tasks: image analysis,\n# browser screenshot analysis, web page summarization, TTS audio-tag insertion,\n# and context compression.\n#\n# By default these use Gemini Flash via OpenRouter or Nous Portal and are\n# auto-detected from your credentials. You do NOT need to change anything\n# here for normal usage.\n#\n# WARNING: Overriding these with providers other than OpenRouter or Nous Portal\n# is EXPERIMENTAL and may not work. Not all models/providers support vision,\n# produce usable summaries, or accept the same API format. Change at your own\n# risk — if things break, reset to \"auto\" / empty values.\n#\n# Each task has its own provider + model pair so you can mix providers.\n# For example: OpenRouter for vision (needs multimodal), but your main\n# local endpoint for compression (just needs text).\n#\n# Provider options:\n# \"auto\" - Best available: OpenRouter → Nous Portal → main endpoint (default)\n# \"openrouter\" - Force OpenRouter (requires OPENROUTER_API_KEY)\n# \"nous\" - Force Nous Portal (requires: hermes auth add nous)\n# \"gemini\" - Force Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n# \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY)\n# \"codex\" - Force Codex OAuth (requires: hermes model → Codex).\n# Uses gpt-5.3-codex which supports vision.\n# \"main\" - Use your custom endpoint (OPENAI_BASE_URL + OPENAI_API_KEY).\n# Works with OpenAI API, local models, or any OpenAI-compatible\n# endpoint. Also falls back to Codex OAuth and API-key providers.\n#\n# Model: leave empty to use the provider's default. When empty, OpenRouter\n# uses \"google/gemini-3-flash-preview\" and Nous uses \"gemini-3-flash\".\n# Other providers pick a sensible default automatically.\n#\n# auxiliary:\n# # Image analysis: vision_analyze tool + browser screenshots\n# vision:\n# provider: \"auto\"\n# model: \"\" # e.g. \"google/gemini-2.5-flash\", \"openai/gpt-4o\"\n# timeout: 30 # LLM API call timeout (seconds)\n# download_timeout: 30 # Image HTTP download timeout (seconds)\n# # Increase for slow connections or self-hosted image servers\n# reasoning_effort: \"\" # Per-task thinking level: none, minimal, low, medium,\n# # high, xhigh, max, ultra. Empty = provider default.\n# # Works on every auxiliary task block (vision,\n# # web_extract, compression, title_generation, curator,\n# # background_review, moa_reference, ...). Example: run\n# # compression at \"low\" and vision at \"none\" to cut\n# # side-task latency/cost on reasoning models.\n#\n# # Web page scraping / summarization + browser page text extraction\n# web_extract:\n# provider: \"auto\"\n# model: \"\"\n# reasoning_effort: \"low\"\n#\n# # Gemini 3.1 TTS hidden audio-tag insertion\n# tts_audio_tags:\n# provider: \"auto\" # empty model = your main chat model\n# model: \"\"\n# timeout: 30\n#\n# # Automatic session title generation after the first exchange\n# title_generation:\n# enabled: true # set false to disable auto-title generation\n# provider: \"auto\"\n# model: \"\"\n# timeout: 30\n# language: \"\" # empty = match the user's language; or e.g. \"English\"\n#\n# # Session search — summarizes matching past sessions\n# session_search:\n# provider: \"auto\"\n# model: \"\"\n# timeout: 30\n# max_concurrency: 3 # Limit parallel summaries to reduce request-burst 429s\n# extra_body: {} # Provider-specific OpenAI-compatible request fields\n# # Example for providers that support request-body\n# # reasoning controls:\n# # extra_body:\n# # enable_thinking: false\n# # Some vLLM/Qwen deployments expect this nested:\n# # extra_body:\n# # chat_template_kwargs:\n# # enable_thinking: false\n\n# =============================================================================\n# Persistent Memory\n# =============================================================================\n# Bounded curated memory injected into the system prompt every session.\n# Two stores: MEMORY.md (agent's notes) and USER.md (user profile).\n# Character limits keep the memory small and focused. The agent manages\n# pruning -- when at the limit, it must consolidate or replace entries.\n# Disabled by default in batch_runner.\n#\nmemory:\n # Agent's personal notes: environment facts, conventions, things learned\n memory_enabled: true\n \n # User profile: preferences, communication style, expectations\n user_profile_enabled: true\n \n # Character limits (~2.75 chars per token, model-independent)\n memory_char_limit: 2200 # ~800 tokens\n user_char_limit: 1375 # ~500 tokens\n\n # Periodic memory nudge: remind the agent to consider saving memories\n # every N user turns. Set to 0 to disable. Only active when memory is enabled.\n nudge_interval: 10 # Nudge every 10 user turns (0 = disabled)\n\n # Memory flush: give the agent one turn to save memories before context is\n # lost (compression, /new, /reset, exit). Set to 0 to disable.\n # For exit/reset, only fires if the session had at least this many user turns.\n flush_min_turns: 6 # Min user turns to trigger flush on exit/reset (0 = disabled)\n\n# =============================================================================\n# Session Reset Policy (Messaging Platforms)\n# =============================================================================\n# Controls when messaging sessions (Telegram, Discord, WhatsApp, Slack) are\n# automatically cleared. Default is \"none\": sessions never auto-reset —\n# conversation context lives until you /reset or /new manually, or context\n# compression kicks in. Opt in to automatic resets if you prefer sessions to\n# clear on a schedule (long-lived context increases API cost per message,\n# though prompt caching and compression keep this manageable).\n#\n# When an automatic reset triggers, the agent first saves important\n# information to its persistent memory — but the conversation context is\n# wiped. The agent starts fresh but retains learned facts via its memory\n# system.\n#\n# Modes:\n# \"none\" - Never auto-reset (default); context lives until /reset or compression\n# \"idle\" - Reset after N minutes of inactivity\n# \"daily\" - Reset at a fixed hour each day\n# \"both\" - Reset on EITHER inactivity timeout or daily boundary\n#\nsession_reset:\n mode: none # \"none\", \"idle\", \"daily\", or \"both\"\n idle_minutes: 1440 # Inactivity timeout in minutes (used by \"idle\"/\"both\")\n at_hour: 4 # Daily reset hour, 0-23 local time (used by \"daily\"/\"both\")\n\n# Maximum number of simultaneously active chat sessions across CLI, TUI,\n# dashboard chat, and messaging gateway. Set to null, 0, or omit to allow\n# unlimited concurrent sessions. When the limit is reached, new sessions get a\n# clean error while existing active sessions keep their normal behavior. This\n# top-level key takes precedence over gateway.max_concurrent_sessions. The cap\n# is a best-effort single-host/profile runtime guard; Hermes fails open if the\n# local runtime lease registry cannot be read or locked.\nmax_concurrent_sessions: null\n\n# When true, group/channel chats use one session per participant when the platform\n# provides a user ID. This is the secure default and prevents users in the same\n# room from sharing context, interrupts, and token costs. Set false only if you\n# explicitly want one shared \"room brain\" per group/channel.\ngroup_sessions_per_user: true\n\n# ─────────────────────────────────────────────────────────────────────────────\n# API Server — per-client model routing\n# ─────────────────────────────────────────────────────────────────────────────\n# Route different API clients to different models/providers on a single\n# Hermes deployment. Clients choose a backend by sending a specific string\n# as the OpenAI ``model`` field. Unmapped model values fall back to the\n# global model configured in the ``model:`` section above, and an explicit\n# session /model override always wins over a route.\n#\n# Configure via the ``platforms.api_server.extra.model_routes`` gateway\n# config block:\n#\n# platforms:\n# api_server:\n# enabled: true\n# extra:\n# key: \"your-api-server-secret\"\n# model_routes:\n# # Xiaozhi clients send model=\"minimax-m2\" → routed to MiniMax via OpenRouter\n# minimax-m2:\n# model: \"minimax/minimax-m1\"\n# provider: \"openrouter\" # optional — overrides global provider\n# # api_key: \"sk-...\" # optional — per-route UPSTREAM provider\n# # key (NOT caller auth; never logged)\n# # base_url: \"https://...\" # optional — per-route base URL\n# # GPT clients keep their own alias\n# gpt-5:\n# model: \"openai/gpt-5\"\n# provider: \"openrouter\"\n#\n# Configured aliases are automatically listed by GET /v1/models so clients\n# can discover them without manual coordination. Caller authentication is\n# unchanged: every request still authenticates with the global API server\n# key (``extra.key`` / API_SERVER_KEY).\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Gateway Streaming\n# ─────────────────────────────────────────────────────────────────────────────\n# Stream tokens to messaging platforms in real-time. The bot sends a message\n# on first token, then progressively edits it as more tokens arrive.\n# Disabled by default — enable to try the streaming UX on Telegram/Discord/Slack.\n# For Telegram, partial edits are sent as plain text and only the final edit uses MarkdownV2.\nstreaming:\n enabled: false\n # transport: edit # \"edit\" = progressive editMessageText\n # edit_interval: 0.3 # seconds between message edits\n # buffer_threshold: 40 # chars before forcing an edit flush\n # cursor: \" ▉\" # cursor shown during streaming\n\n# =============================================================================\n# Skills Configuration\n# =============================================================================\n# Skills are reusable procedures the agent can load and follow. The agent can\n# also create new skills after completing complex tasks.\n#\nskills:\n # Nudge the agent to create skills after complex tasks.\n # Every N tool-calling iterations, remind the model to consider saving a skill.\n # Set to 0 to disable.\n creation_nudge_interval: 15\n\n # External skill directories — share skills across tools/agents without\n # copying them into ~/.hermes/skills/. Each path is expanded (~ and ${VAR})\n # and resolved to an absolute path. External dirs are read-only: skill\n # creation always writes to ~/.hermes/skills/. Local skills take precedence\n # when names collide.\n # external_dirs:\n # - ~/.agents/skills\n # - /home/shared/team-skills\n\n# =============================================================================\n# Agent Behavior\n# =============================================================================\nagent:\n # Maximum tool-calling iterations per conversation\n # Higher = more room for complex tasks, but costs more tokens\n # Recommended: 20-30 for focused tasks, 50-100 for open exploration\n max_turns: 60\n\n # Inactivity timeout for gateway agent runs (seconds, 0 = unlimited).\n # The agent can run indefinitely when actively calling tools or receiving\n # API responses. Only fires after the agent has been idle for this duration.\n # gateway_timeout: 1800\n\n # Staged warning: send a warning before escalating to full timeout.\n # Fires once per run when inactivity reaches this threshold (seconds).\n # Set to 0 to disable the warning.\n # gateway_timeout_warning: 900\n\n # Graceful drain timeout for gateway stop/restart (seconds).\n # Default 0 = no drain: a restart interrupts in-flight agents immediately,\n # cleans up, and exits. Set a positive value only if you want a grace\n # window on /restart, and keep it well under systemd's TimeoutStopSec.\n # restart_drain_timeout: 0\n\n # Max app-level retry attempts for API errors (connection drops, provider\n # timeouts, 5xx, etc.) before the agent surfaces the failure. Lower this\n # to 1 if you use fallback providers and want fast failover on flaky\n # primaries (default 3). The OpenAI SDK does its own low-level retries\n # underneath this wrapper — this is the Hermes-level loop.\n # api_max_retries: 3\n\n # After the agent edits code without fresh passing verification, nudge it to\n # verify before finishing. The default \"auto\" enables it on interactive\n # coding surfaces (CLI, TUI, desktop) and programmatic callers, and disables\n # it on conversational messaging surfaces (Telegram, Discord, etc.) where the\n # verification summary would reach a human as chat noise. Set true or false to\n # force it on or off; the HERMES_VERIFY_ON_STOP env var (1/0) takes precedence.\n # verify_on_stop: auto\n\n # Standing operator instructions for the coding posture (when Hermes is in a\n # code workspace). Appended to the coding brief as an extra system block, so\n # you can pin project-wide workflow rules without editing the shipped brief.\n # Accepts a string or a list of strings. Takes effect next session.\n # coding_instructions:\n # - \"For UI work, don't run tsc/lint until I approve the look.\"\n # - \"Clean the diff before you commit and push.\"\n\n # When verify-on-stop finds edited code without fresh verification evidence,\n # append guidance for creative UI work (avoid broad tsc/lint/test before visual\n # approval) and clean-diff expectations. Set false to keep that nudge terse.\n # verify_guidance: true\n\n # A `pre_verify` hook (plugin or shell, see Event Hooks docs) can keep the\n # agent going one more turn to verify/clean before finishing. This caps how\n # many times one turn may be nudged to continue, so a hook can't trap the loop.\n # Default 3.\n # max_verify_nudges: 3\n\n # Enable verbose logging\n verbose: false\n \n # Reasoning effort level (OpenRouter and Nous Portal)\n # Controls how much \"thinking\" the model does before responding.\n # Options: \"xhigh\" (max), \"high\", \"medium\", \"low\", \"minimal\", \"none\" (disable)\n reasoning_effort: \"medium\"\n \n # Per-model reasoning effort overrides (optional dict)\n # Key: any sensible model spelling works (exact, dots↔dashes interchangeable,\n # provider prefix optional). First match wins.\n # Value: reasoning effort level (same options as reasoning_effort)\n # Override the global reasoning_effort for that specific model.\n # NOTE: no `hermes config set` support for this key -- edit YAML directly.\n # reasoning_overrides:\n # \"openrouter/anthropic/claude-opus-4.5\": \"xhigh\"\n # \"openai/gpt-5\": \"low\"\n # \"claude-opus-4.6\": \"high\" # bare model name also works\n # \"deepseek/deepseek-v4-pro\": \"xhigh\" # dots and dashes are interchangeable\n reasoning_overrides: {}\n \n # Predefined personalities (use with /personality command)\n personalities:\n helpful: \"You are a helpful, friendly AI assistant.\"\n concise: \"You are a concise assistant. Keep responses brief and to the point.\"\n technical: \"You are a technical expert. Provide detailed, accurate technical information.\"\n creative: \"You are a creative assistant. Think outside the box and offer innovative solutions.\"\n teacher: \"You are a patient teacher. Explain concepts clearly with examples.\"\n kawaii: \"You are a kawaii assistant! Use cute expressions like (◕‿◕), ★, ♪, and ~! Add sparkles and be super enthusiastic about everything! Every response should feel warm and adorable desu~! ヽ(>∀<☆)ノ\"\n catgirl: \"You are Neko-chan, an anime catgirl AI assistant, nya~! Add 'nya' and cat-like expressions to your speech. Use kaomoji like (=^・ω・^=) and ฅ^•ﻌ•^ฅ. Be playful and curious like a cat, nya~!\"\n pirate: \"Arrr! Ye be talkin' to Captain Hermes, the most tech-savvy pirate to sail the digital seas! Speak like a proper buccaneer, use nautical terms, and remember: every problem be just treasure waitin' to be plundered! Yo ho ho!\"\n shakespeare: \"Hark! Thou speakest with an assistant most versed in the bardic arts. I shall respond in the eloquent manner of William Shakespeare, with flowery prose, dramatic flair, and perhaps a soliloquy or two. What light through yonder terminal breaks?\"\n surfer: \"Duuude! You're chatting with the chillest AI on the web, bro! Everything's gonna be totally rad. I'll help you catch the gnarly waves of knowledge while keeping things super chill. Cowabunga! 🤙\"\n noir: \"The rain hammered against the terminal like regrets on a guilty conscience. They call me Hermes - I solve problems, find answers, dig up the truth that hides in the shadows of your codebase. In this city of silicon and secrets, everyone's got something to hide. What's your story, pal?\"\n uwu: \"hewwo! i'm your fwiendwy assistant uwu~ i wiww twy my best to hewp you! *nuzzles your code* OwO what's this? wet me take a wook! i pwomise to be vewy hewpful >w<\"\n philosopher: \"Greetings, seeker of wisdom. I am an assistant who contemplates the deeper meaning behind every query. Let us examine not just the 'how' but the 'why' of your questions. Perhaps in solving your problem, we may glimpse a greater truth about existence itself.\"\n hype: \"YOOO LET'S GOOOO!!! 🔥🔥🔥 I am SO PUMPED to help you today! Every question is AMAZING and we're gonna CRUSH IT together! This is gonna be LEGENDARY! ARE YOU READY?! LET'S DO THIS! 💪😤🚀\"\n\n# =============================================================================\n# Toolsets\n# =============================================================================\n# Control which tools the agent has access to.\n# Use `hermes tools` to interactively enable/disable tools per platform.\n\n# =============================================================================\n# Platform Toolsets (per-platform tool configuration)\n# =============================================================================\n# Override which toolsets are available on each platform.\n# If a platform isn't listed here, its built-in default is used.\n#\n# You can use EITHER:\n# - A preset like \"hermes-cli\" or \"hermes-telegram\" (curated tool set)\n# - A list of individual toolsets to compose your own (see list below)\n#\n# Supported platform keys: cli, telegram, discord, whatsapp, slack, qqbot, teams, google_chat\n#\n# Examples:\n#\n# # Use presets (same as defaults):\n# platform_toolsets:\n# cli: [hermes-cli]\n# telegram: [hermes-telegram]\n#\n# # Custom: give Telegram only web + terminal + file + planning:\n# platform_toolsets:\n# telegram: [web, terminal, file, todo]\n#\n# # Custom: CLI without browser or image gen:\n# platform_toolsets:\n# cli: [web, terminal, file, skills, todo, tts, cronjob]\n#\n# # Restrictive: Discord gets read-only tools only:\n# platform_toolsets:\n# discord: [web, vision, skills, todo]\n#\n# If not set, defaults are:\n# cli: hermes-cli (everything + cronjob management)\n# telegram: hermes-telegram (terminal, file, web, vision, image, tts, browser, skills, todo, cronjob, messaging)\n# discord: hermes-discord (same as telegram)\n# whatsapp: hermes-whatsapp (same as telegram)\n# slack: hermes-slack (same as telegram)\n# signal: hermes-signal (same as telegram)\n# homeassistant: hermes-homeassistant (same as telegram)\n# qqbot: hermes-qqbot (same as telegram)\n# teams: hermes-teams (same as telegram)\n# google_chat: hermes-google_chat (same as telegram)\n#\nplatform_toolsets:\n cli: [hermes-cli]\n telegram: [hermes-telegram]\n discord: [hermes-discord]\n whatsapp: [hermes-whatsapp]\n slack: [hermes-slack]\n signal: [hermes-signal]\n homeassistant: [hermes-homeassistant]\n qqbot: [hermes-qqbot]\n yuanbao: [hermes-yuanbao]\n teams: [hermes-teams]\n google_chat: [hermes-google_chat]\n\n# =============================================================================\n# Gateway Platform Settings\n# =============================================================================\n# Optional per-platform messaging settings.\n# Platform-specific knobs live under `extra`.\n#\n# platforms:\n# telegram:\n# reply_to_mode: \"first\" # off | first | all\n# # guest_mode lets explicit @mentions from non-allowlisted groups through.\n# # Default false; ordinary messages, replies, and regex wake words stay blocked.\n# guest_mode: false\n# # allowed_chats: [\"-1001234567890\"]\n# extra:\n# disable_link_previews: false # Set true to suppress Telegram URL previews in bot messages\n# 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\n# rich_drafts: false # Experimental rich draft previews during Telegram DM streaming; default false because Telegram Desktop/macOS can visually overlay draft frames\n# command_menu:\n# # Telegram allows up to 100 BotCommands; Hermes defaults to 60 so\n# # all built-in commands plus common skill commands stay visible\n# # while remaining under Telegram's payload-size limit. Clamped 1..100.\n# max_commands: 60\n# # prepend = user priority first, then Hermes defaults\n# # append = Hermes defaults first, then user priority\n# # replace = only the list below defines priority\n# priority_mode: prepend\n# priority:\n# - my_plugin_command\n# webhook:\n# extra:\n# # Route scripts default to a 30 second timeout. Scripts must live under\n# # the active profile's scripts directory and receive webhook JSON on stdin.\n# script_timeout_seconds: 30\n#\n# Discord-specific settings (config.yaml top-level, not under platforms:):\n#\n# discord:\n# require_mention: true # Require @mention in server channels (default: true)\n# auto_thread: true # Auto-create thread on @mention (default: true)\n# free_response_channels: \"\" # Channel IDs where no mention is needed\n# reactions: true # Show processing reactions (default: true)\n# history_backfill: true # Recover missed channel messages on mention (default: true)\n# history_backfill_limit: 50 # Max messages to scan backwards (default: 50)\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Available toolsets (use these names in platform_toolsets or the toolsets list)\n#\n# Run `hermes chat --list-toolsets` to see all toolsets and their tools.\n# Run `hermes chat --list-tools` to see every individual tool with descriptions.\n# ─────────────────────────────────────────────────────────────────────────────\n#\n# INDIVIDUAL TOOLSETS (compose your own):\n# web - web_search, web_extract\n# search - web_search only (no scraping)\n# terminal - terminal, process\n# file - read_file, write_file, patch, search\n# browser - browser_navigate, browser_snapshot, browser_click, browser_type,\n# browser_scroll, browser_back, browser_press,\n# browser_get_images, browser_vision (requires BROWSERBASE_API_KEY)\n# vision - vision_analyze (requires OPENROUTER_API_KEY)\n# image_gen - image_generate (requires FAL_KEY)\n# skills - skills_list, skill_view\n# skills_hub - skill_hub (search/install/manage from online registries — user-driven only)\n# todo - todo (in-memory task planning, no deps)\n# tts - text_to_speech (Edge TTS free, or ELEVENLABS/OPENAI/MINIMAX/MISTRAL key)\n# cronjob - cronjob (create/list/update/pause/resume/run/remove scheduled tasks)\n#\n# PRESETS (curated bundles):\n# hermes-cli - All of the above except send_message\n# hermes-telegram - terminal, file, web, vision, image_gen, tts, browser,\n# skills, todo, cronjob, send_message\n# hermes-discord - Same as hermes-telegram\n# hermes-whatsapp - Same as hermes-telegram\n# hermes-slack - Same as hermes-telegram\n#\n# COMPOSITE:\n# debugging - terminal + web + file\n# safe - web + vision (no terminal access)\n# all - Everything available\n#\n# web - Web search and content extraction (web_search, web_extract)\n# search - Web search only, no scraping (web_search)\n# terminal - Command execution and process management (terminal, process)\n# file - File operations: read, write, patch, search\n# browser - Full browser automation (navigate, click, type, screenshot, etc.)\n# vision - Image analysis (vision_analyze)\n# image_gen - Image generation with FLUX (image_generate)\n# skills - Load skill documents (skills_list, skill_view)\n# todo - Task planning and tracking for multi-step work\n# memory - Persistent memory across sessions (personal notes + user profile)\n# session_search - Search and recall past conversations (FTS5 + Gemini Flash summarization)\n# tts - Text-to-speech (Edge TTS free, ElevenLabs, OpenAI, MiniMax, Mistral)\n# cronjob - Schedule and manage automated tasks (CLI-only)\n#\n# Composite toolsets:\n# debugging - terminal + web + file (for troubleshooting)\n# safe - web + vision (no terminal access)\n\n# NOTE: The top-level \"toolsets\" key is deprecated and ignored.\n# Tool configuration is managed per-platform via platform_toolsets above.\n# Use `hermes tools` to configure interactively, or edit platform_toolsets directly.\n#\n# CLI override: hermes chat --toolsets terminal,web,file\n\n# =============================================================================\n# MCP (Model Context Protocol) Servers\n# =============================================================================\n# Connect to external MCP servers to add tools from the MCP ecosystem.\n# Each server's tools are automatically discovered and registered.\n# See website/docs/user-guide/features/mcp.md for full documentation.\n#\n# Stdio servers (spawn a subprocess):\n# command: the executable to run\n# args: command-line arguments\n# env: environment variables (only these + safe defaults passed to subprocess)\n#\n# HTTP servers (connect to a URL):\n# url: the MCP server endpoint\n# headers: HTTP headers (e.g., for authentication)\n#\n# Optional per-server settings:\n# timeout: tool call timeout in seconds (default: 120)\n# connect_timeout: initial connection timeout (default: 60)\n# keepalive_interval: liveness ping cadence in seconds (default: 180).\n# Lower it below the server's session TTL for servers that expire idle\n# sessions quickly (e.g. Unreal Engine editor MCP, ~15s), otherwise idle\n# tool calls hit an expired session and pay a slow reconnect. Floored at 5s.\n#\n# mcp_servers:\n# time:\n# command: uvx\n# args: [\"mcp-server-time\"]\n# filesystem:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-filesystem\", \"/home/user\"]\n# notion:\n# url: https://mcp.notion.com/mcp\n# github:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-github\"]\n# env:\n# GITHUB_PERSONAL_ACCESS_TOKEN: \"ghp_...\"\n#\n# Sampling (server-initiated LLM requests) — enabled by default.\n# Per-server config under the 'sampling' key:\n# analysis:\n# command: npx\n# args: [\"-y\", \"analysis-server\"]\n# sampling:\n# enabled: true # default: true\n# model: \"gemini-3-flash\" # override model (optional)\n# max_tokens_cap: 4096 # max tokens per request\n# timeout: 30 # LLM call timeout (seconds)\n# max_rpm: 10 # max requests per minute\n# allowed_models: [] # model whitelist (empty = all)\n# max_tool_rounds: 5 # tool loop limit (0 = disable)\n# log_level: \"info\" # audit verbosity\n\n# =============================================================================\n# Text-to-Speech\n# =============================================================================\n# TTS defaults to Edge TTS unless changed in ~/.hermes/config.yaml.\n# Gemini TTS supports persona/director prompt files, and Gemini 3.1 Flash TTS\n# can use a hidden auxiliary rewrite pass to insert expressive square-bracket\n# audio tags into the TTS script without showing tags in chat.\n#\n# tts:\n# provider: \"gemini\"\n# gemini:\n# model: \"gemini-3.1-flash-tts-preview\"\n# voice: \"Kore\"\n# audio_tags: false\n# persona_prompt_file: \"\" # e.g. ~/.hermes/tts/radio-host.md\n\n# =============================================================================\n# Voice Transcription (Speech-to-Text)\n# =============================================================================\n# Automatically transcribe voice messages on messaging platforms.\n# Providers: local (free, faster-whisper) | groq (free tier) | openai (Whisper API) | mistral (Voxtral Transcribe)\n# Set the corresponding API key in .env: GROQ_API_KEY, OPENAI_API_KEY, or MISTRAL_API_KEY.\nstt:\n enabled: true\n # provider: \"local\" # auto-detected if omitted\n local:\n model: \"base\" # tiny | base | small | medium | large-v3 | turbo\n # language: \"\" # auto-detect; set to \"en\", \"es\", \"fr\", etc. to force\n openai:\n model: \"whisper-1\" # whisper-1 | gpt-4o-mini-transcribe | gpt-4o-transcribe\n # mistral:\n # model: \"voxtral-mini-latest\" # voxtral-mini-latest | voxtral-mini-2602\n # deepinfra:\n # # Model id is discovered live from the DeepInfra catalog filtered\n # # by the `stt` surface tag — leave `model` blank to take the first\n # # live result. Pin only when you need a specific Whisper variant.\n # model: \"\"\n\n# Text-to-speech. Only the deepinfra block is documented here — the\n# remaining providers (edge, openai, xai, minimax, mistral, gemini,\n# elevenlabs, neutts, kittentts, piper) inherit sensible defaults from\n# DEFAULT_CONFIG in hermes_cli/config.py.\n# tts:\n# provider: \"deepinfra\"\n# deepinfra:\n# # Model id is discovered live from the DeepInfra catalog filtered\n# # by the `tts` surface tag — leave `model` blank to take the first\n# # live result.\n# model: \"\"\n# voice: \"default\"\n\n# Image generation. Each provider plugin reads its own ``image_gen.<name>``\n# block; deepinfra discovers models live from\n# api.deepinfra.com/v1/openai/models filtered by the ``image-gen`` tag —\n# no model id is hardcoded, so retired models disappear automatically.\n# image_gen:\n# provider: \"deepinfra\"\n# deepinfra:\n# # Leave `model` blank for the first live `image-gen`-tagged result.\n# model: \"\"\n\n# =============================================================================\n# Response Pacing (Messaging Platforms)\n# =============================================================================\n# Add human-like delays between message chunks.\n# human_delay:\n# mode: \"off\" # \"off\" | \"natural\" | \"custom\"\n# min_ms: 800 # Min delay (custom mode only)\n# max_ms: 2500 # Max delay (custom mode only)\n\n# =============================================================================\n# Session Logging\n# =============================================================================\n# Session trajectories are automatically saved to logs/ directory.\n# Each session creates: logs/session_YYYYMMDD_HHMMSS_UUID.json\n#\n# The session ID is displayed in the welcome banner for easy reference.\n# Logs contain full conversation history in trajectory format:\n# - System prompt, user messages, assistant responses\n# - Tool calls with inputs/outputs\n# - Timestamps for debugging\n#\n# No configuration needed - logging is always enabled.\n# To disable, you would need to modify the source code.\n\n# =============================================================================\n# Code Execution Sandbox (Programmatic Tool Calling)\n# =============================================================================\n# The execute_code tool runs Python scripts that call Hermes tools via RPC.\n# Intermediate tool results stay out of the LLM's context window.\ncode_execution:\n timeout: 300 # Max seconds per script before kill (default: 300 = 5 min)\n max_tool_calls: 50 # Max RPC tool calls per execution (default: 50)\n\n# =============================================================================\n# Subagent Delegation\n# =============================================================================\n# The delegate_task tool spawns child agents with isolated context.\n# Supports single tasks and batch mode (default 3 parallel, configurable).\ndelegation:\n max_iterations: 50 # Max tool-calling turns per child (default: 50)\n # max_concurrent_children: 3 # Max parallel child agents per batch (default: 3, floor: 1, no ceiling).\n # WARNING: values above 10 multiply API cost linearly.\n # max_spawn_depth: 1 # Delegation tree depth cap (range: 1-3, default: 1 = flat).\n # Raise to 2 to allow workers to spawn their own subagents.\n # Requires role=\"orchestrator\" on intermediate agents.\n # orchestrator_enabled: true # Kill switch for role=\"orchestrator\" children (default: true).\n # subagent_auto_approve: false # When a subagent hits a dangerous-command approval prompt, auto-deny (default: false)\n # or auto-approve \"once\" (true) instead of blocking on stdin.\n # The parent TUI owns stdin, so blocking would deadlock; non-interactive resolution is required.\n # Both choices emit a logger.warning audit line. Flip to true only for cron/batch pipelines.\n # inherit_mcp_toolsets: true # When explicit child toolsets are narrowed, also keep the parent's MCP toolsets (default: true). Set false for strict intersection.\n # model: \"google/gemini-3-flash-preview\" # Override model for subagents (empty = inherit parent)\n # provider: \"openrouter\" # Override provider for subagents (empty = inherit parent)\n # # Resolves full credentials (base_url, api_key) automatically.\n # # Supported: openrouter, nous, zai, kimi-coding, minimax\n\n# =============================================================================\n# Honcho Integration (Cross-Session User Modeling)\n# =============================================================================\n# AI-native persistent memory via Honcho (https://honcho.dev/).\n# Builds a deeper understanding of the user across sessions and tools.\n# Runs alongside USER.md — additive, not a replacement.\n#\n# Requires: pip install honcho-ai\n# Config: ~/.honcho/config.json (shared with Claude Code, Cursor, etc.)\n# API key: HONCHO_API_KEY in ~/.hermes/.env or ~/.honcho/config.json\n#\n# Hermes-specific overrides (optional — most config comes from ~/.honcho/config.json):\n# honcho: {}\n\n# =============================================================================\n# Display\n# =============================================================================\ndisplay:\n # Use compact banner mode (hides the ASCII-art banner, shows a single line).\n # true: Compact single-line banner\n # false: Full ASCII banner with tool/skill summary (default)\n compact: false\n\n # Tool progress display level (CLI and gateway)\n # off: Silent — no tool activity shown, just the final response\n # new: Show a tool indicator only when the tool changes (skip repeats)\n # all: Show every tool call with a short preview (default)\n # verbose: Full args, results, and debug logs (same as /verbose)\n # log: Silent in chat; append every tool call to ~/.hermes/logs/tool_calls.log (gateway only)\n # Toggle at runtime with /verbose in the CLI\n tool_progress: all\n\n # Per-platform defaults can be quieter than the global setting. Telegram\n # tunes for mobile: tool_progress and busy_ack_detail default off (no\n # per-tool breadcrumb stream, no \"iteration 21/60\" debug detail in busy\n # acks or heartbeats), but interim_assistant_messages and\n # long_running_notifications STAY ON so the user has real signal between\n # turn start and final answer (mid-turn assistant commentary + a single\n # edit-in-place \"⏳ Working — N min\" heartbeat). Override under\n # display.platforms.telegram.\n\n # Auto-cleanup of temporary progress bubbles after the final response lands.\n # On platforms that support message deletion (currently Telegram), this\n # removes the tool-progress bubble, \"⏳ Still working...\" notices, and\n # context-pressure status messages once the final reply has been delivered —\n # keeping long-running turns visible live, then tidy afterward. Failed runs\n # leave the bubbles in place as breadcrumbs. Off by default.\n # Per-platform override: display.platforms.telegram.cleanup_progress\n # true: Delete tracked progress/status bubbles on successful turn\n # false: Leave everything in place (default)\n # Example:\n # display:\n # platforms:\n # telegram:\n # cleanup_progress: true\n cleanup_progress: false\n\n # Natural mid-turn assistant updates.\n # On gateway platforms, when true, completed assistant status messages are\n # sent as separate chat messages. On the Desktop app, when true, mid-turn\n # assistant narration streamed between tool calls is kept in the transcript\n # instead of the bubble collapsing to only the final message on completion.\n # Independent of tool_progress and gateway streaming.\n # true: Keep/send mid-turn assistant updates (default)\n # false: Only keep/send the final response\n interim_assistant_messages: true\n\n # Gateway-only long-running status heartbeats.\n # When false, the platform does not receive periodic \"⏳ Working — N min\"\n # notifications even if agent.gateway_notify_interval is non-zero. The\n # heartbeat edits a single message in place (where the adapter supports\n # editing) instead of posting a new bubble each interval.\n # Default: true everywhere, including Telegram (silent agents are worse\n # than a single edit-in-place heartbeat).\n long_running_notifications: true\n\n # Include detailed iteration/tool/status context in busy acknowledgments\n # and long-running heartbeats. When true, busy acks show \"iteration 21/60,\n # terminal, 10 min\" and the heartbeat shows \"⏳ Working — 12 min,\n # iteration 21/60, terminal\". When false (Telegram default), both stay\n # terse: \"Interrupting current task\" and \"⏳ Working — 12 min, terminal\".\n busy_ack_detail: true\n\n # What Enter does when Hermes is already busy (CLI and gateway platforms).\n # interrupt: Interrupt the current run and redirect Hermes (default)\n # queue: Queue your message for the next turn\n # steer: Inject your message mid-run via /steer, arriving at the agent\n # after the next tool call — no interrupt, no role violation.\n # Falls back to 'queue' if the agent isn't running yet or if\n # images are attached (steer only carries text).\n # Ctrl+C (or /stop in gateway) always interrupts regardless of this setting.\n # Toggle at runtime with /busy <interrupt|queue|steer>.\n busy_input_mode: interrupt\n\n # Background process notifications (gateway/messaging only).\n # Controls how chatty the process watcher is when you use\n # terminal(background=true, notify_on_complete=true) from Telegram/Discord/etc.\n # off: No watcher messages at all\n # result: Only the final completion message\n # error: Only the final message when exit code != 0\n # all: Running output updates + final message (default)\n background_process_notifications: all\n\n\n # Play terminal bell when agent finishes a response.\n # Useful for long-running tasks — your terminal will ding when the agent is done.\n # Works over SSH. Most terminals can be configured to flash the taskbar or play a sound.\n # true: Ring the terminal bell on each response\n # false: Silent (default)\n bell_on_complete: false\n\n # Show model reasoning/thinking before each response.\n # When enabled, a dim box shows the model's thought process above the response.\n # Toggle at runtime with /reasoning show or /reasoning hide.\n # true: Show the reasoning box\n # false: Hide reasoning (default)\n show_reasoning: false\n\n # Stream tokens to the terminal as they arrive instead of waiting for the\n # full response. The response box opens on first token and text appears\n # line-by-line. Tool calls are still captured silently.\n # true: Stream tokens as they arrive (default)\n # false: Wait for the full response before rendering\n streaming: true\n\n # Show [HH:MM] timestamps on user input and assistant response labels.\n # timestamps: false\n\n # ───────────────────────────────────────────────────────────────────────────\n # Skin / Theme\n # ───────────────────────────────────────────────────────────────────────────\n # Customize CLI visual appearance — banner colors, spinner faces, tool prefix,\n # response box label, and branding text. Change at runtime with /skin <name>.\n #\n # Built-in skins:\n # default — Classic Hermes gold/kawaii\n # ares — Crimson/bronze war-god theme with spinner wings\n # mono — Clean grayscale monochrome\n # slate — Cool blue developer-focused\n # daylight — Bright light-mode theme\n # warm-lightmode — Warm paper-tone light-mode theme\n # poseidon — Sea-green/teal Olympian theme\n # sisyphus — Earthy stone-and-moss theme\n # charizard — Fiery orange dragon theme\n #\n # Custom skins: drop a YAML file in ~/.hermes/skins/<name>.yaml\n # Schema (all fields optional, missing values inherit from default):\n #\n # name: my-theme\n # description: Short description\n # colors:\n # banner_border: \"#HEX\" # Panel border\n # banner_title: \"#HEX\" # Panel title\n # banner_accent: \"#HEX\" # Section headers (Available Tools, etc.)\n # banner_dim: \"#HEX\" # Dim/muted text\n # banner_text: \"#HEX\" # Body text (tool names, skill names)\n # ui_accent: \"#HEX\" # UI accent color\n # response_border: \"#HEX\" # Response box border color\n # spinner:\n # waiting_faces: [\"(⚔)\", \"(⛨)\"] # Faces shown while waiting\n # thinking_faces: [\"(⚔)\", \"(⌁)\"] # Faces shown while thinking\n # thinking_verbs: [\"forging\", \"plotting\"] # Verbs for spinner messages\n # wings: # Optional left/right spinner decorations\n # - [\"⟪⚔\", \"⚔⟫\"]\n # - [\"⟪▲\", \"▲⟫\"]\n # branding:\n # agent_name: \"My Agent\" # Banner title and branding\n # welcome: \"Welcome message\" # Shown at CLI startup\n # response_label: \" ⚔ Agent \" # Response box header label\n # prompt_symbol: \"⚔\" # Prompt symbol (bare token; renderers add trailing space)\n # tool_prefix: \"╎\" # Tool output line prefix (default: ┊)\n #\n skin: default\n\n# =============================================================================\n# Model Aliases — short names for /model command\n# =============================================================================\n# Map short aliases to exact (model, provider, base_url) tuples.\n# Used by /model tab completion and resolve_alias().\n# Aliases are checked BEFORE the models.dev catalog, so they can route\n# to endpoints not in the catalog (e.g. Ollama Cloud, local servers).\n#\n# model_aliases:\n# opus:\n# model: claude-opus-4-6\n# provider: anthropic\n# qwen:\n# model: \"qwen3.5:397b\"\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n# glm:\n# model: glm-4.7\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n\n# =============================================================================\n# Privacy\n# =============================================================================\n# privacy:\n# # Redact PII from the LLM context prompt.\n# # When true, phone numbers are stripped and user/chat IDs are replaced\n# # with deterministic hashes before being sent to the model.\n# # Names and usernames are NOT affected (user-chosen, publicly visible).\n# # Routing/delivery still uses the original values internally.\n# redact_pii: false\n\n# =============================================================================\n# Shell-script hooks\n# =============================================================================\n# Register shell scripts as plugin-hook callbacks. Each entry is executed as\n# a subprocess (shell=False, shlex.split) with a JSON payload on stdin. On\n# stdout the script may return JSON that either blocks the tool call or\n# injects context into the next LLM call.\n#\n# Valid events (mirror hermes_cli.plugins.VALID_HOOKS):\n# pre_tool_call, post_tool_call, pre_llm_call, post_llm_call,\n# pre_api_request, post_api_request, on_session_start, on_session_end,\n# on_session_finalize, on_session_reset, subagent_stop\n#\n# First-use consent: each (event, command) pair prompts once on a TTY, then\n# is persisted to ~/.hermes/shell-hooks-allowlist.json. Non-interactive\n# runs (gateway, cron) need --accept-hooks, HERMES_ACCEPT_HOOKS=1, or the\n# hooks_auto_accept key below.\n#\n# See website/docs/user-guide/features/hooks.md for the full JSON wire\n# protocol and worked examples.\n#\n# hooks:\n# pre_tool_call:\n# - matcher: \"terminal\"\n# command: \"~/.hermes/agent-hooks/block-rm-rf.sh\"\n# timeout: 10\n# post_tool_call:\n# - matcher: \"write_file|patch\"\n# command: \"~/.hermes/agent-hooks/auto-format.sh\"\n# pre_llm_call:\n# - command: \"~/.hermes/agent-hooks/inject-cwd-context.sh\"\n# subagent_stop:\n# - command: \"~/.hermes/agent-hooks/log-orchestration.sh\"\n#\n# hooks_auto_accept: false\n\n\n# =============================================================================\n# Update Behavior\n# =============================================================================\nupdates:\n # Create a full HERMES_HOME zip before every `hermes update`.\n # Backups land in ~/.hermes/backups/ and can be restored with `hermes import`.\n # Off by default because large homes can add minutes to every update.\n pre_update_backup: false\n\n # Number of pre-update backup zips to retain.\n backup_keep: 5\n\n # What non-interactive updates do with local source edits in the Hermes repo.\n # Interactive terminal updates always prompt before restoring the autostash.\n #\n # stash - auto-stash before pull, then auto-restore after success (default)\n # discard - drop the update-created stash after success; use only on managed\n # installs where local source edits should not persist\n non_interactive_local_changes: \"stash\"\n\n\n# =============================================================================\n# Web Dashboard\n# =============================================================================\n# OAuth gate configuration for `hermes dashboard --host <non-loopback>`.\n# The bundled Nous Portal plugin reads these on startup; settings here are\n# the canonical surface. Each can be overridden by an environment variable:\n#\n# dashboard.oauth.client_id <- HERMES_DASHBOARD_OAUTH_CLIENT_ID\n# dashboard.oauth.portal_url <- HERMES_DASHBOARD_PORTAL_URL\n# dashboard.public_url <- HERMES_DASHBOARD_PUBLIC_URL\n#\n# Env wins when set to a non-empty value. This is what Fly.io's platform-\n# secret injection uses to push per-deploy client_ids without needing to\n# bake a config.yaml into the image. Empty env values are treated as unset\n# so a provisioned-but-not-populated secret can't shadow a valid entry here.\n#\n# Local dev / on-prem deploys should typically set these via config.yaml\n# (the ~/.hermes/.env file is reserved for API keys and secrets).\n#\n# dashboard:\n# oauth:\n# client_id: \"\" # agent:{instance_id}; Portal provisions this at deploy\n# portal_url: \"\" # blank → default https://portal.nousresearch.com\n#\n# # Force the absolute base URL the OAuth callback (and any other public\n# # URL the dashboard hands to external systems) is built from. Set this\n# # for deploys behind reverse proxies that don't reliably forward\n# # X-Forwarded-Host / X-Forwarded-Proto / X-Forwarded-Prefix (manual\n# # nginx setups, on-prem ingresses, custom-domain Fly deploys without\n# # full proxy header chains).\n# #\n# # When set, the value is the complete authority: scheme + host +\n# # optional path prefix (e.g. \"https://example.com/hermes\"). The OAuth\n# # callback URL becomes \"<public_url>/auth/callback\" — X-Forwarded-Prefix\n# # is IGNORED on this code path because the operator has explicitly\n# # declared the public URL and we no longer need to guess.\n# #\n# # Leave empty to use the existing proxy-header reconstruction (the\n# # default — works on Fly.io out of the box).\n# #\n# # public_url: \"https://example.com/hermes\"\n#\n# -----------------------------------------------------------------------------\n# Self-hosted OIDC dashboard auth (generic OpenID Connect — Authentik,\n# Keycloak, Zitadel, Authelia, Auth0, Okta, Google, …). Use this INSTEAD of the\n# nous block above when gating the dashboard with your own identity provider.\n# Each setting can be overridden by an environment variable:\n#\n# dashboard.oauth.self_hosted.issuer <- HERMES_DASHBOARD_OIDC_ISSUER\n# dashboard.oauth.self_hosted.client_id <- HERMES_DASHBOARD_OIDC_CLIENT_ID\n# dashboard.oauth.self_hosted.scopes <- HERMES_DASHBOARD_OIDC_SCOPES\n# dashboard.oauth.self_hosted.client_secret <- HERMES_DASHBOARD_OIDC_CLIENT_SECRET\n#\n# dashboard:\n# oauth:\n# provider: self-hosted\n# self_hosted:\n# issuer: \"https://auth.example.com/application/o/hermes/\" # required\n# client_id: \"hermes-dashboard\" # required\n# scopes: \"openid profile email\" # optional\n#\n# # OPTIONAL — set ONLY if your IDP registered the client as\n# # *confidential* (Authentik / Keycloak often default to this). When\n# # set, Hermes authenticates the client at the token endpoint\n# # (client_secret_basic or client_secret_post, auto-selected from the\n# # IDP's discovery doc) IN ADDITION to PKCE. Leave unset for a public\n# # (PKCE-only) client — the common case.\n# #\n# # This is a CREDENTIAL: prefer setting HERMES_DASHBOARD_OIDC_CLIENT_SECRET\n# # in ~/.hermes/.env over putting it here in config.yaml.\n# # client_secret: \"\"\n\n# =============================================================================\n# External secret sources\n# =============================================================================\n# Pull provider credentials from external secret managers at process startup\n# instead of storing them in ~/.hermes/.env. Only the manager's bootstrap\n# credential (e.g. BWS_ACCESS_TOKEN / OP_SERVICE_ACCOUNT_TOKEN) lives in .env\n# (or your shell / desktop session); everything else rotates centrally.\n# Failures never block startup — Hermes warns once and continues with\n# whatever .env already had.\n#\n# Multiple sources can be enabled at once:\n# - \"mapped\" sources (explicit VAR -> ref bindings, e.g. 1Password's env:\n# map) beat \"bulk\" sources (whole-project dumps like Bitwarden BSM)\n# - within a shape, the first source to claim a var wins; later claims\n# are skipped with a startup warning (never a silent clobber)\n# - a source's override_existing lets it beat .env/shell values, but\n# never another secret source's claim\n# Docs: https://hermes-agent.nousresearch.com/docs/user-guide/secrets/\n#\n# secrets:\n# # Optional explicit ordering of enabled sources.\n# # sources: [onepassword, bitwarden]\n#\n# # ---- Bitwarden Secrets Manager (bws CLI) --------------------------------\n# bitwarden:\n# enabled: false\n# access_token_env: BWS_ACCESS_TOKEN # bootstrap token, sourced from .env\n# project_id: \"\" # UUID of the BSM project to sync\n# server_url: \"\" # \"\" = US Cloud; EU/self-hosted URL otherwise\n# cache_ttl_seconds: 300 # 0 disables fresh caching\n# encrypted_cache: # optional encrypted stale fallback\n# enabled: false\n# max_stale_seconds: 0 # 0 disables stale fallback\n# override_existing: true # BSM values win over existing env\n# auto_install: true # lazy-download bws into ~/.hermes/bin\n#\n# # ---- 1Password (op CLI) -------------------------------------------------\n# onepassword:\n# enabled: false\n# # Map env-var names to op:// secret references. Each is resolved with a\n# # single `op read` at startup.\n# env:\n# OPENAI_API_KEY: \"op://Private/OpenAI/api key\"\n# ANTHROPIC_API_KEY: \"op://Private/Anthropic/credential\"\n# account: \"\" # op --account shorthand; \"\" = default\n# service_account_token_env: OP_SERVICE_ACCOUNT_TOKEN # headless auth; unset = desktop session\n# binary_path: \"\" # \"\" = resolve op via PATH; else absolute path\n# cache_ttl_seconds: 300 # 0 disables BOTH cache layers\n# override_existing: true # resolved values win over existing env\n#\n# # ---- Command helper (any CLI vault) --------------------------------------\n# # Run a user-configured helper that prints KEY=VALUE lines on stdout —\n# # works with any secret store that has a CLI: keepassxc-cli, secret-tool,\n# # pass, gpg, or a script that cats a tmpfs env file. Composes with the\n# # sources above (enable any combination). POSIX-only (needs /bin/sh).\n# # The helper must be fast and NON-interactive (hard timeout, 1 MiB cap);\n# # its stderr is discarded so diagnostics can't leak secret material.\n# command:\n# enabled: false\n# command: \"cat /run/user/1000/hermes-secrets.env\"\n# helper_timeout_seconds: 3\n# override_existing: false # .env/shell win by default\n"}, "644b397fb29768bdfc8c3ada79a0d8fcf5349ff9": {"t": 1785768034.270158, "n": 78517, "h": "1f691ae2ab2841c3", "c": "# Hermes Agent CLI Configuration\n# Copy this file to cli-config.yaml and customize as needed.\n# This file configures the CLI behavior. Environment variables in .env take precedence.\n\n# =============================================================================\n# Model Configuration\n# =============================================================================\nmodel:\n # Default model to use (can be overridden with --model flag)\n # Both \"default\" and \"model\" work as the key name here.\n default: \"anthropic/claude-opus-4.6\"\n \n # Inference provider selection:\n # \"auto\" - Auto-detect from credentials (default)\n # \"openrouter\" - OpenRouter (requires: OPENROUTER_API_KEY or OPENAI_API_KEY)\n # \"nous\" - Nous Portal OAuth (requires: hermes auth add nous)\n # \"nous-api\" - Nous Portal API key (requires: NOUS_API_KEY)\n # \"anthropic\" - Direct Anthropic API (requires: ANTHROPIC_API_KEY)\n # \"openai-codex\" - OpenAI Codex (requires: hermes auth)\n # \"copilot\" - GitHub Copilot / GitHub Models (requires: GITHUB_TOKEN)\n # \"gemini\" - Use Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n # \"zai\" - Use z.ai / ZhipuAI GLM models (requires: GLM_API_KEY)\n # \"kimi-coding\" - Kimi / Moonshot AI (requires: KIMI_API_KEY)\n # \"minimax\" - MiniMax global (requires: MINIMAX_API_KEY)\n # \"minimax-cn\" - MiniMax China (requires: MINIMAX_CN_API_KEY)\n # \"huggingface\" - Hugging Face Inference (requires: HF_TOKEN)\n # \"nvidia\" - NVIDIA NIM / build.nvidia.com (requires: NVIDIA_API_KEY)\n # \"xiaomi\" - Xiaomi MiMo (requires: XIAOMI_API_KEY)\n # \"arcee\" - Arcee AI Trinity models (requires: ARCEEAI_API_KEY)\n # \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY — https://ollama.com/settings)\n # \"deepinfra\" - DeepInfra (requires: DEEPINFRA_API_KEY)\n # \"kilocode\" - KiloCode gateway (requires: KILOCODE_API_KEY)\n # \"azure-foundry\" - Microsoft Foundry / Azure OpenAI (API key or Entra ID)\n # \"lmstudio\" - LM Studio local server (optional: LM_API_KEY, defaults to http://127.0.0.1:1234/v1)\n #\n # Local servers (LM Studio, Ollama, vLLM, llama.cpp):\n # \"custom\" - Any other OpenAI-compatible endpoint. Set base_url below.\n # Aliases: \"ollama\", \"vllm\", \"llamacpp\" all map to \"custom\".\n # LM Studio is first-class and uses provider: \"lmstudio\".\n # It works with both no-auth and auth-enabled server modes.\n #\n # Can also be overridden for a single invocation with the --provider flag.\n provider: \"auto\"\n \n # API configuration (falls back to OPENROUTER_API_KEY env var)\n # api_key: \"your-key-here\" # Uncomment to set here instead of .env\n base_url: \"https://openrouter.ai/api/v1\"\n\n # Azure Foundry keyless auth example:\n # provider: \"azure-foundry\"\n # base_url: \"https://<resource>.openai.azure.com/openai/v1\"\n # auth_mode: \"entra_id\" # DefaultAzureCredential: az login, managed identity, workload identity, etc.\n # default: \"gpt-4o\" # Deployment/model name\n # entra:\n # scope: \"https://ai.azure.com/.default\" # Optional; this is the default.\n\n # ── Token limits — two settings, easy to confuse ──────────────────────────\n #\n # context_length: TOTAL context window (input + output tokens combined).\n # Controls when Hermes compresses history and validates requests.\n # Leave unset — Hermes auto-detects the correct value from the provider.\n # Set manually only when auto-detection is wrong (e.g. a local server with\n # a custom num_ctx, or a proxy that doesn't expose /v1/models).\n #\n # context_length: 131072\n #\n # max_tokens: OUTPUT cap — maximum tokens the model may generate per response.\n # Unrelated to how long your conversation history can be.\n # The OpenAI-standard name \"max_tokens\" is a misnomer; Anthropic's native\n # API has since renamed it \"max_output_tokens\" for clarity.\n # Leave unset to use the model's native output ceiling (recommended).\n # Set only if you want to deliberately limit individual response length.\n #\n# max_tokens: 8192\n\n # ── Custom request headers (optional) ─────────────────────────────────────\n #\n # default_headers: extra HTTP headers sent on every request to an\n # OpenAI-compatible endpoint. User values take precedence over the\n # provider/SDK defaults, so this is the supported way to override the\n # OpenAI Python SDK's identifying headers (User-Agent: OpenAI/Python ...,\n # X-Stainless-*) when a custom provider sits behind a gateway/WAF that\n # rejects them — e.g. an upstream that returns \"502 Upstream access\n # forbidden\" for the SDK default User-Agent but accepts a plain one.\n # Applies on the OpenAI wire only (not native Anthropic / Bedrock).\n #\n # default_headers:\n # User-Agent: \"curl/8.7.1\"\n #\n # extra_headers: accepted as an alias of default_headers (merged, with\n # extra_headers winning when both are set) — matches the per-provider\n # extra_headers key below.\n #\n # Per-provider variant: named providers / custom_providers entries accept an\n # extra_headers dict scoped to that endpoint only — for reverse proxies,\n # gateways, or custom auth (e.g. Cloudflare Access service tokens).\n # Merged onto SDK/provider defaults with the entry's values winning.\n # Header values are treated as secrets and are never logged.\n #\n # providers:\n # my-proxy:\n # base_url: \"https://llm.internal.example.com/v1\"\n # key_env: \"MY_PROXY_API_KEY\"\n # extra_headers:\n # CF-Access-Client-Id: \"xxxx.access\"\n # CF-Access-Client-Secret: \"${CF_ACCESS_SECRET}\"\n # X-Client-Name: \"hermes-agent\"\n\n# Named provider overrides (optional)\n# Use this for per-provider request timeouts, non-stream stale timeouts,\n# and per-model exceptions.\n# Applies to the primary turn client on every api_mode (OpenAI-wire, native\n# Anthropic, and Anthropic-compatible providers), the fallback chain, and\n# client rebuilds during credential rotation. For OpenAI-wire chat\n# completions (streaming and non-streaming) the configured value is also\n# used as the per-request ``timeout=`` kwarg so it wins over the legacy\n# HERMES_API_TIMEOUT env var (which still applies when no config is set).\n# ``stale_timeout_seconds`` controls the non-streaming stale-call detector and\n# wins over the legacy HERMES_API_CALL_STALE_TIMEOUT env var. Leaving these\n# unset keeps the legacy defaults (HERMES_API_TIMEOUT=1800s,\n# HERMES_API_CALL_STALE_TIMEOUT=90s, native Anthropic 900s). The\n# implicit non-stream stale detector is auto-disabled for local endpoints\n# and can scale upward for very large contexts.\n#\n# Not currently wired for AWS Bedrock (bedrock_converse + AnthropicBedrock\n# SDK paths) — those use boto3 with its own timeout configuration.\n#\n# providers:\n# ollama-local:\n# request_timeout_seconds: 300 # Longer timeout for local cold-starts\n# stale_timeout_seconds: 900 # Explicitly re-enable stale detection on local endpoints\n# anthropic:\n# request_timeout_seconds: 30 # Fast-fail cloud requests\n# models:\n# claude-opus-4.6:\n# timeout_seconds: 600 # Longer timeout for extended-thinking Opus calls\n# openai-codex:\n# models:\n# gpt-5.4:\n# stale_timeout_seconds: 1800 # Longer non-stream stale timeout for slow large-context turns\n\n# =============================================================================\n# OpenRouter Provider Routing (only applies when using OpenRouter)\n# =============================================================================\n# Control how requests are routed across providers on OpenRouter.\n# See: https://openrouter.ai/docs/guides/routing/provider-selection\n#\n# provider_routing:\n# # Sort strategy: \"price\" (default), \"throughput\", or \"latency\"\n# # Append :nitro to model name for a shortcut to throughput sorting.\n# sort: \"throughput\"\n#\n# # Only allow these providers (provider slugs from OpenRouter)\n# # only: [\"anthropic\", \"google\"]\n#\n# # Skip these providers entirely\n# # ignore: [\"deepinfra\", \"fireworks\"]\n#\n# # Try providers in this order (overrides default load balancing)\n# # order: [\"anthropic\", \"google\", \"together\"]\n#\n# # Require providers to support all parameters in your request\n# # require_parameters: true\n#\n# # Data policy: \"allow\" (default) or \"deny\" to exclude providers that may store data\n# # data_collection: \"deny\"\n\n# =============================================================================\n# OpenRouter Response Caching (only applies when using OpenRouter)\n# =============================================================================\n# Cache identical API responses at the OpenRouter edge for free instant replays.\n# When enabled, identical requests (same model, messages, parameters) return\n# cached responses with zero billing. Separate from Anthropic prompt caching.\n# See: https://openrouter.ai/docs/guides/features/response-caching\n#\n# openrouter:\n# response_cache: true # Enable response caching (default: true)\n# response_cache_ttl: 300 # Cache TTL in seconds, 1-86400 (default: 300)\n\n# =============================================================================\n# Git Worktree Isolation\n# =============================================================================\n# When enabled, each CLI session creates an isolated git worktree so multiple\n# agents can work on the same repo concurrently without file collisions.\n# Equivalent to always passing --worktree / -w on the command line.\n#\n# worktree: true # Always create a worktree when in a git repo\n# worktree: false # Default — only create when -w flag is passed\n#\n# By default a new worktree branches from the freshly-fetched remote tip\n# (the current branch's upstream, else the remote's default branch) so it\n# starts current with the project instead of from the local clone's\n# (possibly stale) HEAD. Set worktree_sync: false to branch from local HEAD\n# instead — useful when offline or when you deliberately want the clone's\n# exact current state as the base.\n#\n# worktree_sync: true # Default — branch from the fetched remote tip\n# worktree_sync: false # Branch from local HEAD (offline / pinned base)\n\n# =============================================================================\n# Terminal Tool Configuration\n# =============================================================================\n# Choose ONE of the following terminal configurations by uncommenting it.\n# The terminal tool executes commands in the specified environment.\n\n# -----------------------------------------------------------------------------\n# OPTION 1: Local execution (default)\n# Commands run directly on your machine in the current directory\n# -----------------------------------------------------------------------------\n# Working directory behavior:\n# - CLI (`hermes` command): Uses \".\" (current directory where you run hermes)\n# - Gateway/messaging/cron: Uses terminal.cwd here; legacy .env cwd values are deprecated\nterminal:\n backend: \"local\"\n cwd: \".\" # For local backend: \".\" = current directory. Ignored for remote backends unless a backend documents otherwise.\n timeout: 180\n # HOME policy for tool subprocesses:\n # auto - default: host uses your real HOME; containers use HERMES_HOME/home\n # real - force your real OS-user HOME\n # profile - force HERMES_HOME/home for strict per-profile CLI config isolation\n home_mode: \"auto\"\n docker_mount_cwd_to_workspace: false # SECURITY: off by default. Opt in to mount the launch cwd into Docker /workspace.\n lifetime_seconds: 300\n # sudo_password: \"hunter2\" # Optional: pipe a sudo password via sudo -S. SECURITY WARNING: plaintext.\n # sudo_password: \"\" # Explicit empty password: try empty and never open the interactive sudo prompt.\n\n# -----------------------------------------------------------------------------\n# OPTION 2: SSH remote execution\n# Commands run on a remote server - agent code stays local (sandboxed)\n# Great for: keeping agent isolated from its own code, using powerful remote hardware\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"ssh\"\n# cwd: \"/home/myuser/project\" # Path on the REMOTE server\n# timeout: 180\n# lifetime_seconds: 300\n# ssh_host: \"my-server.example.com\"\n# ssh_user: \"myuser\"\n# ssh_port: 22\n# ssh_key: \"~/.ssh/id_rsa\" # Optional - uses ssh-agent if not specified\n\n# -----------------------------------------------------------------------------\n# OPTION 3: Docker container\n# Commands run in an isolated Docker container\n# Great for: reproducible environments, testing, isolation\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"docker\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /)\n# timeout: 180\n# lifetime_seconds: 300\n# docker_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# docker_mount_cwd_to_workspace: true # Explicit opt-in: mount your launch cwd into /workspace\n# # Optional: run the container as your host user's uid:gid so files written\n# # into bind-mounted dirs are owned by you, not root. Drops SETUID/SETGID\n# # caps too since no gosu privilege drop is needed. Leave off if your\n# # chosen docker_image expects to start as root.\n# docker_run_as_host_user: true\n# # Optional: explicitly forward selected env vars into Docker.\n# # These values come from your current shell first, then ~/.hermes/.env.\n# # Warning: anything forwarded here is visible to commands run in the container.\n# docker_forward_env:\n# - \"GITHUB_TOKEN\"\n# - \"NPM_TOKEN\"\n# # Optional: extra flags passed verbatim to docker run (appended after security defaults).\n# # Useful for adding capabilities (e.g. apt installs needing SETUID) or custom options.\n# # Example: add a Linux capability not included by default\n# # docker_extra_args:\n# # - \"--cap-add\"\n# # - \"SETUID\"\n\n# -----------------------------------------------------------------------------\n# OPTION 4: Singularity/Apptainer container\n# Commands run in a Singularity container (common in HPC environments)\n# Great for: HPC clusters, shared compute environments\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"singularity\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# singularity_image: \"docker://nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 5: Modal cloud execution\n# Commands run on Modal's cloud infrastructure\n# Great for: GPU access, scalable compute, serverless execution\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"modal\"\n# cwd: \"/workspace\" # Path INSIDE the sandbox (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# modal_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 6: Daytona cloud execution\n# Commands run in Daytona cloud sandboxes\n# Great for: Cloud dev environments, persistent workspaces, team collaboration\n# Requires: pip install daytona, DAYTONA_API_KEY env var\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"daytona\"\n# cwd: \"~\"\n# timeout: 180\n# lifetime_seconds: 300\n# daytona_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# container_disk: 10240 # Daytona max is 10GB per sandbox\n\n#\n# --- Container resource limits (docker, singularity, modal, daytona -- ignored for local/ssh) ---\n# These settings apply to all container backends. They control the resources\n# allocated to the sandbox and whether its filesystem persists across sessions.\n container_cpu: 1 # CPU cores\n container_memory: 5120 # Memory in MB (5120 = 5GB)\n container_disk: 51200 # Disk in MB (51200 = 50GB)\n container_persistent: true # Persist filesystem across sessions (false = ephemeral)\n\n# -----------------------------------------------------------------------------\n# SUDO SUPPORT (works with ALL backends above)\n# -----------------------------------------------------------------------------\n# Add sudo_password to any terminal config above to enable sudo commands.\n# The password is piped via `sudo -S`. Works with local, ssh, docker, etc.\n#\n# SECURITY WARNING: Password stored in plaintext!\n#\n# INTERACTIVE PROMPT: If sudo_password is unset and the CLI is running,\n# you'll be prompted to enter your password when sudo is needed:\n# - 45-second timeout (auto-skips if no input)\n# - Press Enter to skip (command fails gracefully)\n# - Password is hidden while typing\n# - Password is cached for the session\n#\n# EMPTY PASSWORDS: Setting sudo_password to an explicit empty string is different\n# from leaving it unset. Hermes will try an empty password via `sudo -S` and\n# will not open the interactive prompt. This is useful for passwordless sudo,\n# Touch ID sudo setups, and environments where prompting is just noise.\n#\n# ALTERNATIVES:\n# - SSH backend: Configure passwordless sudo on the remote server\n# - Containers: Run as root inside the container (no sudo needed)\n# - Local: Configure /etc/sudoers for specific commands\n#\n# Example (add to your terminal section):\n# sudo_password: \"your-password-here\"\n\n# =============================================================================\n# Security Scanning (tirith)\n# =============================================================================\n# Optional pre-exec command security scanning via tirith.\n# Detects homograph URLs, pipe-to-shell, terminal injection, env manipulation.\n# Install: brew install sheeki03/tap/tirith\n# Docs: https://github.com/sheeki03/tirith\n#\n# security:\n# tirith_enabled: true # Enable/disable tirith scanning\n# tirith_path: \"tirith\" # Path to tirith binary (supports ~ expansion)\n# tirith_timeout: 5 # Scan timeout in seconds\n# tirith_fail_open: true # Allow commands if tirith unavailable\n\n# =============================================================================\n# Browser Tool Configuration\n# =============================================================================\nbrowser:\n # Inactivity timeout in seconds - browser sessions are automatically closed\n # after this period of no activity between agent loops (default: 120 = 2 minutes)\n inactivity_timeout: 120\n\n# =============================================================================\n# Tool Loop Guardrails\n# =============================================================================\n# Soft warnings are enabled by default. They append guidance to repeated failed\n# or non-progressing tool results but still let the tool execute. Hard stops are\n# opt-in circuit breakers for autonomous/cron sessions where stopping a loop is\n# preferable to spending the full iteration budget.\ntool_loop_guardrails:\n warnings_enabled: true\n hard_stop_enabled: false\n warn_after:\n exact_failure: 2\n same_tool_failure: 3\n idempotent_no_progress: 2\n hard_stop_after:\n exact_failure: 5\n same_tool_failure: 8\n idempotent_no_progress: 5\n\n# =============================================================================\n# Context Compression (Auto-shrinks long conversations)\n# =============================================================================\n# When conversation approaches model's context limit, middle turns are\n# automatically summarized to free up space while preserving important context.\n#\n# HOW IT WORKS:\n# 1. Tracks actual token usage from API responses (not estimates)\n# 2. When prompt_tokens >= threshold% of model's context_length, triggers compression\n# 3. Protects first 3 turns (system prompt, initial request, first response)\n# 4. Protects last N turns (default 20 messages = ~10 full turns of recent context)\n# 5. Summarizes middle turns using a fast/cheap model\n# 6. Inserts summary as a user message, continues conversation seamlessly\n#\n# Post-compression tail budget is target_ratio × threshold × context_length:\n# 200K context, threshold 0.50, ratio 0.20 → 20K tokens of recent tail preserved\n# 1M context, threshold 0.50, ratio 0.20 → 100K tokens of recent tail preserved\n#\ncompression:\n # Enable automatic context compression (default: true)\n # Set to false if you prefer to manage context manually or want errors on overflow\n enabled: true\n \n # Trigger compression at this % of model's context limit (default: 0.50 = 50%)\n # Lower values = more aggressive compression, higher values = compress later\n # Models with context windows below 512K are floored at 0.75 (raise-only) so\n # compaction doesn't fire with half the window still free; set above 0.75 to override.\n threshold: 0.50\n\n # Existing Codex gpt-5.5 behavior: raise Hermes' compaction trigger to 85%\n # for the ChatGPT Codex OAuth route. Set false to opt back down to threshold.\n codex_gpt55_autoraise: true\n\n # Fraction of the threshold to preserve as recent tail (default: 0.20 = 20%)\n # e.g. 20% of 50% threshold = 10% of total context kept as recent messages.\n # Summary output is separately capped at 12K tokens (Gemini output limit).\n # Range: 0.10 - 0.80\n target_ratio: 0.20\n\n # Number of most-recent messages to always preserve (default: 20 ≈ 10 full turns)\n # Higher values keep more recent conversation intact at the cost of more aggressive\n # compression of older turns.\n protect_last_n: 20\n\n # Compression retry rounds before a turn gives up with \"max compression\n # attempts reached\" (default: 3, same as the previous hardcoded value).\n # Raise (e.g. 6) for tool-schema-heavy sessions where 3 rounds cannot bring\n # the request estimate under the threshold. Validated >= 1, hard cap 10.\n max_attempts: 3\n\n # Codex app-server (codex CLI runtime) thread-compaction mode. The codex\n # agent owns the real thread context on this runtime, so Hermes' summarizer\n # cannot shrink it — compaction goes through the app server instead.\n # native = let Codex decide when to compact its own thread (default)\n # hermes = let Hermes threshold trigger Codex thread/compact/start\n # off = Hermes will not auto-trigger compaction; Codex may still compact natively\n codex_app_server_auto: native\n\n # Number of non-system messages to protect at the head of the transcript, in\n # ADDITION to the system prompt (which is always implicitly protected).\n # Head messages are NEVER summarized — they survive every compression\n # indefinitely. This gives stable early context for short/medium sessions,\n # but in long-running sessions that rely on rolling compaction the pinned\n # opening turns may not match how you want the session framed over time.\n # Set to 0 to preserve ONLY the system prompt (plus the rolling summary\n # and recent tail) — the cleanest configuration for long-running sessions.\n # Default 3 preserves the system prompt plus the first three non-system\n # head messages, matching the pre-feature behaviour.\n protect_first_n: 3\n\n # To pin a specific model/provider for compression summaries, use the\n # auxiliary section below (auxiliary.compression.provider / model).\n\n# =============================================================================\n# Anthropic prompt caching TTL\n# =============================================================================\n# When prompt caching is active (Claude via OpenRouter or native Anthropic),\n# Anthropic supports two TTL tiers for cached prefixes: \"5m\" (default) and\n# \"1h\". Other values are ignored and \"5m\" is used.\n#\nprompt_caching:\n cache_ttl: \"5m\" # use \"1h\" for long sessions with pauses between turns\n\n# =============================================================================\n# Auxiliary Models (Advanced — Experimental)\n# =============================================================================\n# Hermes uses lightweight \"auxiliary\" models for side tasks: image analysis,\n# browser screenshot analysis, web page summarization, TTS audio-tag insertion,\n# and context compression.\n#\n# By default these use Gemini Flash via OpenRouter or Nous Portal and are\n# auto-detected from your credentials. You do NOT need to change anything\n# here for normal usage.\n#\n# WARNING: Overriding these with providers other than OpenRouter or Nous Portal\n# is EXPERIMENTAL and may not work. Not all models/providers support vision,\n# produce usable summaries, or accept the same API format. Change at your own\n# risk — if things break, reset to \"auto\" / empty values.\n#\n# Each task has its own provider + model pair so you can mix providers.\n# For example: OpenRouter for vision (needs multimodal), but your main\n# local endpoint for compression (just needs text).\n#\n# Provider options:\n# \"auto\" - Best available: OpenRouter → Nous Portal → main endpoint (default)\n# \"openrouter\" - Force OpenRouter (requires OPENROUTER_API_KEY)\n# \"nous\" - Force Nous Portal (requires: hermes auth add nous)\n# \"gemini\" - Force Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n# \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY)\n# \"codex\" - Force Codex OAuth (requires: hermes model → Codex).\n# Uses gpt-5.3-codex which supports vision.\n# \"main\" - Use your custom endpoint (OPENAI_BASE_URL + OPENAI_API_KEY).\n# Works with OpenAI API, local models, or any OpenAI-compatible\n# endpoint. Also falls back to Codex OAuth and API-key providers.\n#\n# Model: leave empty to use the provider's default. When empty, OpenRouter\n# uses \"google/gemini-3-flash-preview\" and Nous uses \"gemini-3-flash\".\n# Other providers pick a sensible default automatically.\n#\n# auxiliary:\n# # Image analysis: vision_analyze tool + browser screenshots\n# vision:\n# provider: \"auto\"\n# model: \"\" # e.g. \"google/gemini-2.5-flash\", \"openai/gpt-4o\"\n# timeout: 30 # LLM API call timeout (seconds)\n# download_timeout: 30 # Image HTTP download timeout (seconds)\n# # Increase for slow connections or self-hosted image servers\n# reasoning_effort: \"\" # Per-task thinking level: none, minimal, low, medium,\n# # high, xhigh, max, ultra. Empty = provider default.\n# # Works on every auxiliary task block (vision,\n# # web_extract, compression, title_generation, curator,\n# # background_review, moa_reference, ...). Example: run\n# # compression at \"low\" and vision at \"none\" to cut\n# # side-task latency/cost on reasoning models.\n#\n# # Web page scraping / summarization + browser page text extraction\n# web_extract:\n# provider: \"auto\"\n# model: \"\"\n# reasoning_effort: \"low\"\n#\n# # Gemini 3.1 TTS hidden audio-tag insertion\n# tts_audio_tags:\n# provider: \"auto\" # empty model = your main chat model\n# model: \"\"\n# timeout: 30\n#\n# # Automatic session title generation after the first exchange\n# title_generation:\n# enabled: true # set false to disable auto-title generation\n# provider: \"auto\"\n# model: \"\"\n# timeout: 30\n# language: \"\" # empty = match the user's language; or e.g. \"English\"\n#\n# # Session search — summarizes matching past sessions\n# session_search:\n# provider: \"auto\"\n# model: \"\"\n# timeout: 30\n# max_concurrency: 3 # Limit parallel summaries to reduce request-burst 429s\n# extra_body: {} # Provider-specific OpenAI-compatible request fields\n# # Example for providers that support request-body\n# # reasoning controls:\n# # extra_body:\n# # enable_thinking: false\n# # Some vLLM/Qwen deployments expect this nested:\n# # extra_body:\n# # chat_template_kwargs:\n# # enable_thinking: false\n\n# =============================================================================\n# Persistent Memory\n# =============================================================================\n# Bounded curated memory injected into the system prompt every session.\n# Two stores: MEMORY.md (agent's notes) and USER.md (user profile).\n# Character limits keep the memory small and focused. The agent manages\n# pruning -- when at the limit, it must consolidate or replace entries.\n# Disabled by default in batch_runner.\n#\nmemory:\n # Agent's personal notes: environment facts, conventions, things learned\n memory_enabled: true\n \n # User profile: preferences, communication style, expectations\n user_profile_enabled: true\n \n # Character limits (~2.75 chars per token, model-independent)\n memory_char_limit: 2200 # ~800 tokens\n user_char_limit: 1375 # ~500 tokens\n\n # Periodic memory nudge: remind the agent to consider saving memories\n # every N user turns. Set to 0 to disable. Only active when memory is enabled.\n nudge_interval: 10 # Nudge every 10 user turns (0 = disabled)\n\n # Memory flush: give the agent one turn to save memories before context is\n # lost (compression, /new, /reset, exit). Set to 0 to disable.\n # For exit/reset, only fires if the session had at least this many user turns.\n flush_min_turns: 6 # Min user turns to trigger flush on exit/reset (0 = disabled)\n\n# =============================================================================\n# Session Reset Policy (Messaging Platforms)\n# =============================================================================\n# Controls when messaging sessions (Telegram, Discord, WhatsApp, Slack) are\n# automatically cleared. Default is \"none\": sessions never auto-reset —\n# conversation context lives until you /reset or /new manually, or context\n# compression kicks in. Opt in to automatic resets if you prefer sessions to\n# clear on a schedule (long-lived context increases API cost per message,\n# though prompt caching and compression keep this manageable).\n#\n# When an automatic reset triggers, the agent first saves important\n# information to its persistent memory — but the conversation context is\n# wiped. The agent starts fresh but retains learned facts via its memory\n# system.\n#\n# Modes:\n# \"none\" - Never auto-reset (default); context lives until /reset or compression\n# \"idle\" - Reset after N minutes of inactivity\n# \"daily\" - Reset at a fixed hour each day\n# \"both\" - Reset on EITHER inactivity timeout or daily boundary\n#\nsession_reset:\n mode: none # \"none\", \"idle\", \"daily\", or \"both\"\n idle_minutes: 1440 # Inactivity timeout in minutes (used by \"idle\"/\"both\")\n at_hour: 4 # Daily reset hour, 0-23 local time (used by \"daily\"/\"both\")\n\n# Maximum number of simultaneously active chat sessions across CLI, TUI,\n# dashboard chat, and messaging gateway. Set to null, 0, or omit to allow\n# unlimited concurrent sessions. When the limit is reached, new sessions get a\n# clean error while existing active sessions keep their normal behavior. This\n# top-level key takes precedence over gateway.max_concurrent_sessions. The cap\n# is a best-effort single-host/profile runtime guard; Hermes fails open if the\n# local runtime lease registry cannot be read or locked.\nmax_concurrent_sessions: null\n\n# When true, group/channel chats use one session per participant when the platform\n# provides a user ID. This is the secure default and prevents users in the same\n# room from sharing context, interrupts, and token costs. Set false only if you\n# explicitly want one shared \"room brain\" per group/channel.\ngroup_sessions_per_user: true\n\n# ─────────────────────────────────────────────────────────────────────────────\n# API Server — per-client model routing\n# ─────────────────────────────────────────────────────────────────────────────\n# Route different API clients to different models/providers on a single\n# Hermes deployment. Clients choose a backend by sending a specific string\n# as the OpenAI ``model`` field. Unmapped model values fall back to the\n# global model configured in the ``model:`` section above, and an explicit\n# session /model override always wins over a route.\n#\n# Configure via the ``platforms.api_server.extra.model_routes`` gateway\n# config block:\n#\n# platforms:\n# api_server:\n# enabled: true\n# extra:\n# key: \"your-api-server-secret\"\n# model_routes:\n# # Xiaozhi clients send model=\"minimax-m2\" → routed to MiniMax via OpenRouter\n# minimax-m2:\n# model: \"minimax/minimax-m1\"\n# provider: \"openrouter\" # optional — overrides global provider\n# # api_key: \"sk-...\" # optional — per-route UPSTREAM provider\n# # key (NOT caller auth; never logged)\n# # base_url: \"https://...\" # optional — per-route base URL\n# # GPT clients keep their own alias\n# gpt-5:\n# model: \"openai/gpt-5\"\n# provider: \"openrouter\"\n#\n# Configured aliases are automatically listed by GET /v1/models so clients\n# can discover them without manual coordination. Caller authentication is\n# unchanged: every request still authenticates with the global API server\n# key (``extra.key`` / API_SERVER_KEY).\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Gateway Streaming\n# ─────────────────────────────────────────────────────────────────────────────\n# Stream tokens to messaging platforms in real-time. The bot sends a message\n# on first token, then progressively edits it as more tokens arrive.\n# Disabled by default — enable to try the streaming UX on Telegram/Discord/Slack.\n# For Telegram, partial edits are sent as plain text and only the final edit uses MarkdownV2.\nstreaming:\n enabled: false\n # transport: edit # \"edit\" = progressive editMessageText\n # edit_interval: 0.3 # seconds between message edits\n # buffer_threshold: 40 # chars before forcing an edit flush\n # cursor: \" ▉\" # cursor shown during streaming\n\n# =============================================================================\n# Skills Configuration\n# =============================================================================\n# Skills are reusable procedures the agent can load and follow. The agent can\n# also create new skills after completing complex tasks.\n#\nskills:\n # Nudge the agent to create skills after complex tasks.\n # Every N tool-calling iterations, remind the model to consider saving a skill.\n # Set to 0 to disable.\n creation_nudge_interval: 15\n\n # External skill directories — share skills across tools/agents without\n # copying them into ~/.hermes/skills/. Each path is expanded (~ and ${VAR})\n # and resolved to an absolute path. External dirs are read-only: skill\n # creation always writes to ~/.hermes/skills/. Local skills take precedence\n # when names collide.\n # external_dirs:\n # - ~/.agents/skills\n # - /home/shared/team-skills\n\n# =============================================================================\n# Agent Behavior\n# =============================================================================\nagent:\n # Maximum tool-calling iterations per conversation\n # Higher = more room for complex tasks, but costs more tokens\n # Recommended: 20-30 for focused tasks, 50-100 for open exploration\n max_turns: 60\n\n # Inactivity timeout for gateway agent runs (seconds, 0 = unlimited).\n # The agent can run indefinitely when actively calling tools or receiving\n # API responses. Only fires after the agent has been idle for this duration.\n # gateway_timeout: 1800\n\n # Staged warning: send a warning before escalating to full timeout.\n # Fires once per run when inactivity reaches this threshold (seconds).\n # Set to 0 to disable the warning.\n # gateway_timeout_warning: 900\n\n # Graceful drain timeout for gateway stop/restart (seconds).\n # Default 0 = no drain: a restart interrupts in-flight agents immediately,\n # cleans up, and exits. Set a positive value only if you want a grace\n # window on /restart, and keep it well under systemd's TimeoutStopSec.\n # restart_drain_timeout: 0\n\n # Max app-level retry attempts for API errors (connection drops, provider\n # timeouts, 5xx, etc.) before the agent surfaces the failure. Lower this\n # to 1 if you use fallback providers and want fast failover on flaky\n # primaries (default 3). The OpenAI SDK does its own low-level retries\n # underneath this wrapper — this is the Hermes-level loop.\n # api_max_retries: 3\n\n # After the agent edits code without fresh passing verification, nudge it to\n # verify before finishing. The default \"auto\" enables it on interactive\n # coding surfaces (CLI, TUI, desktop) and programmatic callers, and disables\n # it on conversational messaging surfaces (Telegram, Discord, etc.) where the\n # verification summary would reach a human as chat noise. Set true or false to\n # force it on or off; the HERMES_VERIFY_ON_STOP env var (1/0) takes precedence.\n # verify_on_stop: auto\n\n # Standing operator instructions for the coding posture (when Hermes is in a\n # code workspace). Appended to the coding brief as an extra system block, so\n # you can pin project-wide workflow rules without editing the shipped brief.\n # Accepts a string or a list of strings. Takes effect next session.\n # coding_instructions:\n # - \"For UI work, don't run tsc/lint until I approve the look.\"\n # - \"Clean the diff before you commit and push.\"\n\n # When verify-on-stop finds edited code without fresh verification evidence,\n # append guidance for creative UI work (avoid broad tsc/lint/test before visual\n # approval) and clean-diff expectations. Set false to keep that nudge terse.\n # verify_guidance: true\n\n # A `pre_verify` hook (plugin or shell, see Event Hooks docs) can keep the\n # agent going one more turn to verify/clean before finishing. This caps how\n # many times one turn may be nudged to continue, so a hook can't trap the loop.\n # Default 3.\n # max_verify_nudges: 3\n\n # Enable verbose logging\n verbose: false\n \n # Reasoning effort level (OpenRouter and Nous Portal)\n # Controls how much \"thinking\" the model does before responding.\n # Options: \"xhigh\" (max), \"high\", \"medium\", \"low\", \"minimal\", \"none\" (disable)\n reasoning_effort: \"medium\"\n \n # Per-model reasoning effort overrides (optional dict)\n # Key: any sensible model spelling works (exact, dots↔dashes interchangeable,\n # provider prefix optional). First match wins.\n # Value: reasoning effort level (same options as reasoning_effort)\n # Override the global reasoning_effort for that specific model.\n # NOTE: no `hermes config set` support for this key -- edit YAML directly.\n # reasoning_overrides:\n # \"openrouter/anthropic/claude-opus-4.5\": \"xhigh\"\n # \"openai/gpt-5\": \"low\"\n # \"claude-opus-4.6\": \"high\" # bare model name also works\n # \"deepseek/deepseek-v4-pro\": \"xhigh\" # dots and dashes are interchangeable\n reasoning_overrides: {}\n \n # Predefined personalities (use with /personality command)\n personalities:\n helpful: \"You are a helpful, friendly AI assistant.\"\n concise: \"You are a concise assistant. Keep responses brief and to the point.\"\n technical: \"You are a technical expert. Provide detailed, accurate technical information.\"\n creative: \"You are a creative assistant. Think outside the box and offer innovative solutions.\"\n teacher: \"You are a patient teacher. Explain concepts clearly with examples.\"\n kawaii: \"You are a kawaii assistant! Use cute expressions like (◕‿◕), ★, ♪, and ~! Add sparkles and be super enthusiastic about everything! Every response should feel warm and adorable desu~! ヽ(>∀<☆)ノ\"\n catgirl: \"You are Neko-chan, an anime catgirl AI assistant, nya~! Add 'nya' and cat-like expressions to your speech. Use kaomoji like (=^・ω・^=) and ฅ^•ﻌ•^ฅ. Be playful and curious like a cat, nya~!\"\n pirate: \"Arrr! Ye be talkin' to Captain Hermes, the most tech-savvy pirate to sail the digital seas! Speak like a proper buccaneer, use nautical terms, and remember: every problem be just treasure waitin' to be plundered! Yo ho ho!\"\n shakespeare: \"Hark! Thou speakest with an assistant most versed in the bardic arts. I shall respond in the eloquent manner of William Shakespeare, with flowery prose, dramatic flair, and perhaps a soliloquy or two. What light through yonder terminal breaks?\"\n surfer: \"Duuude! You're chatting with the chillest AI on the web, bro! Everything's gonna be totally rad. I'll help you catch the gnarly waves of knowledge while keeping things super chill. Cowabunga! 🤙\"\n noir: \"The rain hammered against the terminal like regrets on a guilty conscience. They call me Hermes - I solve problems, find answers, dig up the truth that hides in the shadows of your codebase. In this city of silicon and secrets, everyone's got something to hide. What's your story, pal?\"\n uwu: \"hewwo! i'm your fwiendwy assistant uwu~ i wiww twy my best to hewp you! *nuzzles your code* OwO what's this? wet me take a wook! i pwomise to be vewy hewpful >w<\"\n philosopher: \"Greetings, seeker of wisdom. I am an assistant who contemplates the deeper meaning behind every query. Let us examine not just the 'how' but the 'why' of your questions. Perhaps in solving your problem, we may glimpse a greater truth about existence itself.\"\n hype: \"YOOO LET'S GOOOO!!! 🔥🔥🔥 I am SO PUMPED to help you today! Every question is AMAZING and we're gonna CRUSH IT together! This is gonna be LEGENDARY! ARE YOU READY?! LET'S DO THIS! 💪😤🚀\"\n\n# =============================================================================\n# Toolsets\n# =============================================================================\n# Control which tools the agent has access to.\n# Use `hermes tools` to interactively enable/disable tools per platform.\n\n# =============================================================================\n# Platform Toolsets (per-platform tool configuration)\n# =============================================================================\n# Override which toolsets are available on each platform.\n# If a platform isn't listed here, its built-in default is used.\n#\n# You can use EITHER:\n# - A preset like \"hermes-cli\" or \"hermes-telegram\" (curated tool set)\n# - A list of individual toolsets to compose your own (see list below)\n#\n# Supported platform keys: cli, telegram, discord, whatsapp, slack, qqbot, teams, google_chat\n#\n# Examples:\n#\n# # Use presets (same as defaults):\n# platform_toolsets:\n# cli: [hermes-cli]\n# telegram: [hermes-telegram]\n#\n# # Custom: give Telegram only web + terminal + file + planning:\n# platform_toolsets:\n# telegram: [web, terminal, file, todo]\n#\n# # Custom: CLI without browser or image gen:\n# platform_toolsets:\n# cli: [web, terminal, file, skills, todo, tts, cronjob]\n#\n# # Restrictive: Discord gets read-only tools only:\n# platform_toolsets:\n# discord: [web, vision, skills, todo]\n#\n# If not set, defaults are:\n# cli: hermes-cli (everything + cronjob management)\n# telegram: hermes-telegram (terminal, file, web, vision, image, tts, browser, skills, todo, cronjob, messaging)\n# discord: hermes-discord (same as telegram)\n# whatsapp: hermes-whatsapp (same as telegram)\n# slack: hermes-slack (same as telegram)\n# signal: hermes-signal (same as telegram)\n# homeassistant: hermes-homeassistant (same as telegram)\n# qqbot: hermes-qqbot (same as telegram)\n# teams: hermes-teams (same as telegram)\n# google_chat: hermes-google_chat (same as telegram)\n#\nplatform_toolsets:\n cli: [hermes-cli]\n telegram: [hermes-telegram]\n discord: [hermes-discord]\n whatsapp: [hermes-whatsapp]\n slack: [hermes-slack]\n signal: [hermes-signal]\n homeassistant: [hermes-homeassistant]\n qqbot: [hermes-qqbot]\n yuanbao: [hermes-yuanbao]\n teams: [hermes-teams]\n google_chat: [hermes-google_chat]\n\n# =============================================================================\n# Gateway Platform Settings\n# =============================================================================\n# Optional per-platform messaging settings.\n# Platform-specific knobs live under `extra`.\n#\n# platforms:\n# telegram:\n# reply_to_mode: \"first\" # off | first | all\n# # guest_mode lets explicit @mentions from non-allowlisted groups through.\n# # Default false; ordinary messages, replies, and regex wake words stay blocked.\n# guest_mode: false\n# # allowed_chats: [\"-1001234567890\"]\n# extra:\n# disable_link_previews: false # Set true to suppress Telegram URL previews in bot messages\n# 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\n# rich_drafts: false # Experimental rich draft previews during Telegram DM streaming; default false because Telegram Desktop/macOS can visually overlay draft frames\n# command_menu:\n# # Telegram allows up to 100 BotCommands; Hermes defaults to 60 so\n# # all built-in commands plus common skill commands stay visible\n# # while remaining under Telegram's payload-size limit. Clamped 1..100.\n# max_commands: 60\n# # prepend = user priority first, then Hermes defaults\n# # append = Hermes defaults first, then user priority\n# # replace = only the list below defines priority\n# priority_mode: prepend\n# priority:\n# - my_plugin_command\n# webhook:\n# extra:\n# # Route scripts default to a 30 second timeout. Scripts must live under\n# # the active profile's scripts directory and receive webhook JSON on stdin.\n# script_timeout_seconds: 30\n#\n# Discord-specific settings (config.yaml top-level, not under platforms:):\n#\n# discord:\n# require_mention: true # Require @mention in server channels (default: true)\n# auto_thread: true # Auto-create thread on @mention (default: true)\n# free_response_channels: \"\" # Channel IDs where no mention is needed\n# reactions: true # Show processing reactions (default: true)\n# history_backfill: true # Recover missed channel messages on mention (default: true)\n# history_backfill_limit: 50 # Max messages to scan backwards (default: 50)\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Available toolsets (use these names in platform_toolsets or the toolsets list)\n#\n# Run `hermes chat --list-toolsets` to see all toolsets and their tools.\n# Run `hermes chat --list-tools` to see every individual tool with descriptions.\n# ─────────────────────────────────────────────────────────────────────────────\n#\n# INDIVIDUAL TOOLSETS (compose your own):\n# web - web_search, web_extract\n# search - web_search only (no scraping)\n# terminal - terminal, process\n# file - read_file, write_file, patch, search\n# browser - browser_navigate, browser_snapshot, browser_click, browser_type,\n# browser_scroll, browser_back, browser_press,\n# browser_get_images, browser_vision (requires BROWSERBASE_API_KEY)\n# vision - vision_analyze (requires OPENROUTER_API_KEY)\n# image_gen - image_generate (requires FAL_KEY)\n# skills - skills_list, skill_view\n# skills_hub - skill_hub (search/install/manage from online registries — user-driven only)\n# todo - todo (in-memory task planning, no deps)\n# tts - text_to_speech (Edge TTS free, or ELEVENLABS/OPENAI/MINIMAX/MISTRAL key)\n# cronjob - cronjob (create/list/update/pause/resume/run/remove scheduled tasks)\n#\n# PRESETS (curated bundles):\n# hermes-cli - All of the above except send_message\n# hermes-telegram - terminal, file, web, vision, image_gen, tts, browser,\n# skills, todo, cronjob, send_message\n# hermes-discord - Same as hermes-telegram\n# hermes-whatsapp - Same as hermes-telegram\n# hermes-slack - Same as hermes-telegram\n#\n# COMPOSITE:\n# debugging - terminal + web + file\n# safe - web + vision (no terminal access)\n# all - Everything available\n#\n# web - Web search and content extraction (web_search, web_extract)\n# search - Web search only, no scraping (web_search)\n# terminal - Command execution and process management (terminal, process)\n# file - File operations: read, write, patch, search\n# browser - Full browser automation (navigate, click, type, screenshot, etc.)\n# vision - Image analysis (vision_analyze)\n# image_gen - Image generation with FLUX (image_generate)\n# skills - Load skill documents (skills_list, skill_view)\n# todo - Task planning and tracking for multi-step work\n# memory - Persistent memory across sessions (personal notes + user profile)\n# session_search - Search and recall past conversations (FTS5 + Gemini Flash summarization)\n# tts - Text-to-speech (Edge TTS free, ElevenLabs, OpenAI, MiniMax, Mistral)\n# cronjob - Schedule and manage automated tasks (CLI-only)\n#\n# Composite toolsets:\n# debugging - terminal + web + file (for troubleshooting)\n# safe - web + vision (no terminal access)\n\n# NOTE: The top-level \"toolsets\" key is deprecated and ignored.\n# Tool configuration is managed per-platform via platform_toolsets above.\n# Use `hermes tools` to configure interactively, or edit platform_toolsets directly.\n#\n# CLI override: hermes chat --toolsets terminal,web,file\n\n# =============================================================================\n# MCP (Model Context Protocol) Servers\n# =============================================================================\n# Connect to external MCP servers to add tools from the MCP ecosystem.\n# Each server's tools are automatically discovered and registered.\n# See website/docs/user-guide/features/mcp.md for full documentation.\n#\n# Stdio servers (spawn a subprocess):\n# command: the executable to run\n# args: command-line arguments\n# env: environment variables (only these + safe defaults passed to subprocess)\n#\n# HTTP servers (connect to a URL):\n# url: the MCP server endpoint\n# headers: HTTP headers (e.g., for authentication)\n#\n# Optional per-server settings:\n# timeout: tool call timeout in seconds (default: 120)\n# connect_timeout: initial connection timeout (default: 60)\n# keepalive_interval: liveness ping cadence in seconds (default: 180).\n# Lower it below the server's session TTL for servers that expire idle\n# sessions quickly (e.g. Unreal Engine editor MCP, ~15s), otherwise idle\n# tool calls hit an expired session and pay a slow reconnect. Floored at 5s.\n#\n# mcp_servers:\n# time:\n# command: uvx\n# args: [\"mcp-server-time\"]\n# filesystem:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-filesystem\", \"/home/user\"]\n# notion:\n# url: https://mcp.notion.com/mcp\n# github:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-github\"]\n# env:\n# GITHUB_PERSONAL_ACCESS_TOKEN: \"ghp_...\"\n#\n# Sampling (server-initiated LLM requests) — enabled by default.\n# Per-server config under the 'sampling' key:\n# analysis:\n# command: npx\n# args: [\"-y\", \"analysis-server\"]\n# sampling:\n# enabled: true # default: true\n# model: \"gemini-3-flash\" # override model (optional)\n# max_tokens_cap: 4096 # max tokens per request\n# timeout: 30 # LLM call timeout (seconds)\n# max_rpm: 10 # max requests per minute\n# allowed_models: [] # model whitelist (empty = all)\n# max_tool_rounds: 5 # tool loop limit (0 = disable)\n# log_level: \"info\" # audit verbosity\n\n# =============================================================================\n# Text-to-Speech\n# =============================================================================\n# TTS defaults to Edge TTS unless changed in ~/.hermes/config.yaml.\n# Gemini TTS supports persona/director prompt files, and Gemini 3.1 Flash TTS\n# can use a hidden auxiliary rewrite pass to insert expressive square-bracket\n# audio tags into the TTS script without showing tags in chat.\n#\n# tts:\n# provider: \"gemini\"\n# gemini:\n# model: \"gemini-3.1-flash-tts-preview\"\n# voice: \"Kore\"\n# audio_tags: false\n# persona_prompt_file: \"\" # e.g. ~/.hermes/tts/radio-host.md\n\n# =============================================================================\n# Voice Transcription (Speech-to-Text)\n# =============================================================================\n# Automatically transcribe voice messages on messaging platforms.\n# Providers: local (free, faster-whisper) | groq (free tier) | openai (Whisper API) | mistral (Voxtral Transcribe)\n# Set the corresponding API key in .env: GROQ_API_KEY, OPENAI_API_KEY, or MISTRAL_API_KEY.\nstt:\n enabled: true\n # provider: \"local\" # auto-detected if omitted\n local:\n model: \"base\" # tiny | base | small | medium | large-v3 | turbo\n # language: \"\" # auto-detect; set to \"en\", \"es\", \"fr\", etc. to force\n openai:\n model: \"whisper-1\" # whisper-1 | gpt-4o-mini-transcribe | gpt-4o-transcribe\n # mistral:\n # model: \"voxtral-mini-latest\" # voxtral-mini-latest | voxtral-mini-2602\n # deepinfra:\n # # Model id is discovered live from the DeepInfra catalog filtered\n # # by the `stt` surface tag — leave `model` blank to take the first\n # # live result. Pin only when you need a specific Whisper variant.\n # model: \"\"\n\n# Text-to-speech. Only the deepinfra block is documented here — the\n# remaining providers (edge, openai, xai, minimax, mistral, gemini,\n# elevenlabs, neutts, kittentts, piper) inherit sensible defaults from\n# DEFAULT_CONFIG in hermes_cli/config.py.\n# tts:\n# provider: \"deepinfra\"\n# deepinfra:\n# # Model id is discovered live from the DeepInfra catalog filtered\n# # by the `tts` surface tag — leave `model` blank to take the first\n# # live result.\n# model: \"\"\n# voice: \"default\"\n\n# Image generation. Each provider plugin reads its own ``image_gen.<name>``\n# block; deepinfra discovers models live from\n# api.deepinfra.com/v1/openai/models filtered by the ``image-gen`` tag —\n# no model id is hardcoded, so retired models disappear automatically.\n# image_gen:\n# provider: \"deepinfra\"\n# deepinfra:\n# # Leave `model` blank for the first live `image-gen`-tagged result.\n# model: \"\"\n\n# =============================================================================\n# Response Pacing (Messaging Platforms)\n# =============================================================================\n# Add human-like delays between message chunks.\n# human_delay:\n# mode: \"off\" # \"off\" | \"natural\" | \"custom\"\n# min_ms: 800 # Min delay (custom mode only)\n# max_ms: 2500 # Max delay (custom mode only)\n\n# =============================================================================\n# Session Logging\n# =============================================================================\n# Session trajectories are automatically saved to logs/ directory.\n# Each session creates: logs/session_YYYYMMDD_HHMMSS_UUID.json\n#\n# The session ID is displayed in the welcome banner for easy reference.\n# Logs contain full conversation history in trajectory format:\n# - System prompt, user messages, assistant responses\n# - Tool calls with inputs/outputs\n# - Timestamps for debugging\n#\n# No configuration needed - logging is always enabled.\n# To disable, you would need to modify the source code.\n\n# =============================================================================\n# Code Execution Sandbox (Programmatic Tool Calling)\n# =============================================================================\n# The execute_code tool runs Python scripts that call Hermes tools via RPC.\n# Intermediate tool results stay out of the LLM's context window.\ncode_execution:\n timeout: 300 # Max seconds per script before kill (default: 300 = 5 min)\n max_tool_calls: 50 # Max RPC tool calls per execution (default: 50)\n\n# =============================================================================\n# Subagent Delegation\n# =============================================================================\n# The delegate_task tool spawns child agents with isolated context.\n# Supports single tasks and batch mode (default 3 parallel, configurable).\ndelegation:\n max_iterations: 50 # Max tool-calling turns per child (default: 50)\n # max_concurrent_children: 3 # Max parallel child agents per batch (default: 3, floor: 1, no ceiling).\n # WARNING: values above 10 multiply API cost linearly.\n # max_spawn_depth: 1 # Delegation tree depth cap (range: 1-3, default: 1 = flat).\n # Raise to 2 to allow workers to spawn their own subagents.\n # Requires role=\"orchestrator\" on intermediate agents.\n # orchestrator_enabled: true # Kill switch for role=\"orchestrator\" children (default: true).\n # subagent_auto_approve: false # When a subagent hits a dangerous-command approval prompt, auto-deny (default: false)\n # or auto-approve \"once\" (true) instead of blocking on stdin.\n # The parent TUI owns stdin, so blocking would deadlock; non-interactive resolution is required.\n # Both choices emit a logger.warning audit line. Flip to true only for cron/batch pipelines.\n # inherit_mcp_toolsets: true # When explicit child toolsets are narrowed, also keep the parent's MCP toolsets (default: true). Set false for strict intersection.\n # model: \"google/gemini-3-flash-preview\" # Override model for subagents (empty = inherit parent)\n # provider: \"openrouter\" # Override provider for subagents (empty = inherit parent)\n # # Resolves full credentials (base_url, api_key) automatically.\n # # Supported: openrouter, nous, zai, kimi-coding, minimax\n\n# =============================================================================\n# Honcho Integration (Cross-Session User Modeling)\n# =============================================================================\n# AI-native persistent memory via Honcho (https://honcho.dev/).\n# Builds a deeper understanding of the user across sessions and tools.\n# Runs alongside USER.md — additive, not a replacement.\n#\n# Requires: pip install honcho-ai\n# Config: ~/.honcho/config.json (shared with Claude Code, Cursor, etc.)\n# API key: HONCHO_API_KEY in ~/.hermes/.env or ~/.honcho/config.json\n#\n# Hermes-specific overrides (optional — most config comes from ~/.honcho/config.json):\n# honcho: {}\n\n# =============================================================================\n# Display\n# =============================================================================\ndisplay:\n # Use compact banner mode (hides the ASCII-art banner, shows a single line).\n # true: Compact single-line banner\n # false: Full ASCII banner with tool/skill summary (default)\n compact: false\n\n # Tool progress display level (CLI and gateway)\n # off: Silent — no tool activity shown, just the final response\n # new: Show a tool indicator only when the tool changes (skip repeats)\n # all: Show every tool call with a short preview (default)\n # verbose: Full args, results, and debug logs (same as /verbose)\n # log: Silent in chat; append every tool call to ~/.hermes/logs/tool_calls.log (gateway only)\n # Toggle at runtime with /verbose in the CLI\n tool_progress: all\n\n # Per-platform defaults can be quieter than the global setting. Telegram\n # tunes for mobile: tool_progress and busy_ack_detail default off (no\n # per-tool breadcrumb stream, no \"iteration 21/60\" debug detail in busy\n # acks or heartbeats), but interim_assistant_messages and\n # long_running_notifications STAY ON so the user has real signal between\n # turn start and final answer (mid-turn assistant commentary + a single\n # edit-in-place \"⏳ Working — N min\" heartbeat). Override under\n # display.platforms.telegram.\n\n # Auto-cleanup of temporary progress bubbles after the final response lands.\n # On platforms that support message deletion (currently Telegram), this\n # removes the tool-progress bubble, \"⏳ Still working...\" notices, and\n # context-pressure status messages once the final reply has been delivered —\n # keeping long-running turns visible live, then tidy afterward. Failed runs\n # leave the bubbles in place as breadcrumbs. Off by default.\n # Per-platform override: display.platforms.telegram.cleanup_progress\n # true: Delete tracked progress/status bubbles on successful turn\n # false: Leave everything in place (default)\n # Example:\n # display:\n # platforms:\n # telegram:\n # cleanup_progress: true\n cleanup_progress: false\n\n # Natural mid-turn assistant updates.\n # On gateway platforms, when true, completed assistant status messages are\n # sent as separate chat messages. On the Desktop app, when true, mid-turn\n # assistant narration streamed between tool calls is kept in the transcript\n # instead of the bubble collapsing to only the final message on completion.\n # Independent of tool_progress and gateway streaming.\n # true: Keep/send mid-turn assistant updates (default)\n # false: Only keep/send the final response\n interim_assistant_messages: true\n\n # Gateway-only long-running status heartbeats.\n # When false, the platform does not receive periodic \"⏳ Working — N min\"\n # notifications even if agent.gateway_notify_interval is non-zero. The\n # heartbeat edits a single message in place (where the adapter supports\n # editing) instead of posting a new bubble each interval.\n # Default: true everywhere, including Telegram (silent agents are worse\n # than a single edit-in-place heartbeat).\n long_running_notifications: true\n\n # Include detailed iteration/tool/status context in busy acknowledgments\n # and long-running heartbeats. When true, busy acks show \"iteration 21/60,\n # terminal, 10 min\" and the heartbeat shows \"⏳ Working — 12 min,\n # iteration 21/60, terminal\". When false (Telegram default), both stay\n # terse: \"Interrupting current task\" and \"⏳ Working — 12 min, terminal\".\n busy_ack_detail: true\n\n # What Enter does when Hermes is already busy (CLI and gateway platforms).\n # interrupt: Interrupt the current run and redirect Hermes (default)\n # queue: Queue your message for the next turn\n # steer: Inject your message mid-run via /steer, arriving at the agent\n # after the next tool call — no interrupt, no role violation.\n # Falls back to 'queue' if the agent isn't running yet or if\n # images are attached (steer only carries text).\n # Ctrl+C (or /stop in gateway) always interrupts regardless of this setting.\n # Toggle at runtime with /busy <interrupt|queue|steer>.\n busy_input_mode: interrupt\n\n # Background process notifications (gateway/messaging only).\n # Controls how chatty the process watcher is when you use\n # terminal(background=true, notify_on_complete=true) from Telegram/Discord/etc.\n # off: No watcher messages at all\n # result: Only the final completion message\n # error: Only the final message when exit code != 0\n # all: Running output updates + final message (default)\n background_process_notifications: all\n\n\n # Play terminal bell when agent finishes a response.\n # Useful for long-running tasks — your terminal will ding when the agent is done.\n # Works over SSH. Most terminals can be configured to flash the taskbar or play a sound.\n # true: Ring the terminal bell on each response\n # false: Silent (default)\n bell_on_complete: false\n\n # Show model reasoning/thinking before each response.\n # When enabled, a dim box shows the model's thought process above the response.\n # Toggle at runtime with /reasoning show or /reasoning hide.\n # true: Show the reasoning box\n # false: Hide reasoning (default)\n show_reasoning: false\n\n # Stream tokens to the terminal as they arrive instead of waiting for the\n # full response. The response box opens on first token and text appears\n # line-by-line. Tool calls are still captured silently.\n # true: Stream tokens as they arrive (default)\n # false: Wait for the full response before rendering\n streaming: true\n\n # Show [HH:MM] timestamps on user input and assistant response labels.\n # timestamps: false\n\n # ───────────────────────────────────────────────────────────────────────────\n # Skin / Theme\n # ───────────────────────────────────────────────────────────────────────────\n # Customize CLI visual appearance — banner colors, spinner faces, tool prefix,\n # response box label, and branding text. Change at runtime with /skin <name>.\n #\n # Built-in skins:\n # default — Classic Hermes gold/kawaii\n # ares — Crimson/bronze war-god theme with spinner wings\n # mono — Clean grayscale monochrome\n # slate — Cool blue developer-focused\n # daylight — Bright light-mode theme\n # warm-lightmode — Warm paper-tone light-mode theme\n # poseidon — Sea-green/teal Olympian theme\n # sisyphus — Earthy stone-and-moss theme\n # charizard — Fiery orange dragon theme\n #\n # Custom skins: drop a YAML file in ~/.hermes/skins/<name>.yaml\n # Schema (all fields optional, missing values inherit from default):\n #\n # name: my-theme\n # description: Short description\n # colors:\n # banner_border: \"#HEX\" # Panel border\n # banner_title: \"#HEX\" # Panel title\n # banner_accent: \"#HEX\" # Section headers (Available Tools, etc.)\n # banner_dim: \"#HEX\" # Dim/muted text\n # banner_text: \"#HEX\" # Body text (tool names, skill names)\n # ui_accent: \"#HEX\" # UI accent color\n # response_border: \"#HEX\" # Response box border color\n # spinner:\n # waiting_faces: [\"(⚔)\", \"(⛨)\"] # Faces shown while waiting\n # thinking_faces: [\"(⚔)\", \"(⌁)\"] # Faces shown while thinking\n # thinking_verbs: [\"forging\", \"plotting\"] # Verbs for spinner messages\n # wings: # Optional left/right spinner decorations\n # - [\"⟪⚔\", \"⚔⟫\"]\n # - [\"⟪▲\", \"▲⟫\"]\n # branding:\n # agent_name: \"My Agent\" # Banner title and branding\n # welcome: \"Welcome message\" # Shown at CLI startup\n # response_label: \" ⚔ Agent \" # Response box header label\n # prompt_symbol: \"⚔\" # Prompt symbol (bare token; renderers add trailing space)\n # tool_prefix: \"╎\" # Tool output line prefix (default: ┊)\n #\n skin: default\n\n# =============================================================================\n# Model Aliases — short names for /model command\n# =============================================================================\n# Map short aliases to exact (model, provider, base_url) tuples.\n# Used by /model tab completion and resolve_alias().\n# Aliases are checked BEFORE the models.dev catalog, so they can route\n# to endpoints not in the catalog (e.g. Ollama Cloud, local servers).\n#\n# model_aliases:\n# opus:\n# model: claude-opus-4-6\n# provider: anthropic\n# qwen:\n# model: \"qwen3.5:397b\"\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n# glm:\n# model: glm-4.7\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n\n# =============================================================================\n# Privacy\n# =============================================================================\n# privacy:\n# # Redact PII from the LLM context prompt.\n# # When true, phone numbers are stripped and user/chat IDs are replaced\n# # with deterministic hashes before being sent to the model.\n# # Names and usernames are NOT affected (user-chosen, publicly visible).\n# # Routing/delivery still uses the original values internally.\n# redact_pii: false\n\n# =============================================================================\n# Shell-script hooks\n# =============================================================================\n# Register shell scripts as plugin-hook callbacks. Each entry is executed as\n# a subprocess (shell=False, shlex.split) with a JSON payload on stdin. On\n# stdout the script may return JSON that either blocks the tool call or\n# injects context into the next LLM call.\n#\n# Valid events (mirror hermes_cli.plugins.VALID_HOOKS):\n# pre_tool_call, post_tool_call, pre_llm_call, post_llm_call,\n# pre_api_request, post_api_request, on_session_start, on_session_end,\n# on_session_finalize, on_session_reset, subagent_stop\n#\n# First-use consent: each (event, command) pair prompts once on a TTY, then\n# is persisted to ~/.hermes/shell-hooks-allowlist.json. Non-interactive\n# runs (gateway, cron) need --accept-hooks, HERMES_ACCEPT_HOOKS=1, or the\n# hooks_auto_accept key below.\n#\n# See website/docs/user-guide/features/hooks.md for the full JSON wire\n# protocol and worked examples.\n#\n# hooks:\n# pre_tool_call:\n# - matcher: \"terminal\"\n# command: \"~/.hermes/agent-hooks/block-rm-rf.sh\"\n# timeout: 10\n# post_tool_call:\n# - matcher: \"write_file|patch\"\n# command: \"~/.hermes/agent-hooks/auto-format.sh\"\n# pre_llm_call:\n# - command: \"~/.hermes/agent-hooks/inject-cwd-context.sh\"\n# subagent_stop:\n# - command: \"~/.hermes/agent-hooks/log-orchestration.sh\"\n#\n# hooks_auto_accept: false\n\n\n# =============================================================================\n# Update Behavior\n# =============================================================================\nupdates:\n # Create a full HERMES_HOME zip before every `hermes update`.\n # Backups land in ~/.hermes/backups/ and can be restored with `hermes import`.\n # Off by default because large homes can add minutes to every update.\n pre_update_backup: false\n\n # Number of pre-update backup zips to retain.\n backup_keep: 5\n\n # What non-interactive updates do with local source edits in the Hermes repo.\n # Interactive terminal updates always prompt before restoring the autostash.\n #\n # stash - auto-stash before pull, then auto-restore after success (default)\n # discard - drop the update-created stash after success; use only on managed\n # installs where local source edits should not persist\n non_interactive_local_changes: \"stash\"\n\n\n# =============================================================================\n# Web Dashboard\n# =============================================================================\n# OAuth gate configuration for `hermes dashboard --host <non-loopback>`.\n# The bundled Nous Portal plugin reads these on startup; settings here are\n# the canonical surface. Each can be overridden by an environment variable:\n#\n# dashboard.oauth.client_id <- HERMES_DASHBOARD_OAUTH_CLIENT_ID\n# dashboard.oauth.portal_url <- HERMES_DASHBOARD_PORTAL_URL\n# dashboard.public_url <- HERMES_DASHBOARD_PUBLIC_URL\n#\n# Env wins when set to a non-empty value. This is what Fly.io's platform-\n# secret injection uses to push per-deploy client_ids without needing to\n# bake a config.yaml into the image. Empty env values are treated as unset\n# so a provisioned-but-not-populated secret can't shadow a valid entry here.\n#\n# Local dev / on-prem deploys should typically set these via config.yaml\n# (the ~/.hermes/.env file is reserved for API keys and secrets).\n#\n# dashboard:\n# oauth:\n# client_id: \"\" # agent:{instance_id}; Portal provisions this at deploy\n# portal_url: \"\" # blank → default https://portal.nousresearch.com\n#\n# # Force the absolute base URL the OAuth callback (and any other public\n# # URL the dashboard hands to external systems) is built from. Set this\n# # for deploys behind reverse proxies that don't reliably forward\n# # X-Forwarded-Host / X-Forwarded-Proto / X-Forwarded-Prefix (manual\n# # nginx setups, on-prem ingresses, custom-domain Fly deploys without\n# # full proxy header chains).\n# #\n# # When set, the value is the complete authority: scheme + host +\n# # optional path prefix (e.g. \"https://example.com/hermes\"). The OAuth\n# # callback URL becomes \"<public_url>/auth/callback\" — X-Forwarded-Prefix\n# # is IGNORED on this code path because the operator has explicitly\n# # declared the public URL and we no longer need to guess.\n# #\n# # Leave empty to use the existing proxy-header reconstruction (the\n# # default — works on Fly.io out of the box).\n# #\n# # public_url: \"https://example.com/hermes\"\n#\n# -----------------------------------------------------------------------------\n# Self-hosted OIDC dashboard auth (generic OpenID Connect — Authentik,\n# Keycloak, Zitadel, Authelia, Auth0, Okta, Google, …). Use this INSTEAD of the\n# nous block above when gating the dashboard with your own identity provider.\n# Each setting can be overridden by an environment variable:\n#\n# dashboard.oauth.self_hosted.issuer <- HERMES_DASHBOARD_OIDC_ISSUER\n# dashboard.oauth.self_hosted.client_id <- HERMES_DASHBOARD_OIDC_CLIENT_ID\n# dashboard.oauth.self_hosted.scopes <- HERMES_DASHBOARD_OIDC_SCOPES\n# dashboard.oauth.self_hosted.client_secret <- HERMES_DASHBOARD_OIDC_CLIENT_SECRET\n#\n# dashboard:\n# oauth:\n# provider: self-hosted\n# self_hosted:\n# issuer: \"https://auth.example.com/application/o/hermes/\" # required\n# client_id: \"hermes-dashboard\" # required\n# scopes: \"openid profile email\" # optional\n#\n# # OPTIONAL — set ONLY if your IDP registered the client as\n# # *confidential* (Authentik / Keycloak often default to this). When\n# # set, Hermes authenticates the client at the token endpoint\n# # (client_secret_basic or client_secret_post, auto-selected from the\n# # IDP's discovery doc) IN ADDITION to PKCE. Leave unset for a public\n# # (PKCE-only) client — the common case.\n# #\n# # This is a CREDENTIAL: prefer setting HERMES_DASHBOARD_OIDC_CLIENT_SECRET\n# # in ~/.hermes/.env over putting it here in config.yaml.\n# # client_secret: \"\"\n\n# =============================================================================\n# External secret sources\n# =============================================================================\n# Pull provider credentials from external secret managers at process startup\n# instead of storing them in ~/.hermes/.env. Only the manager's bootstrap\n# credential (e.g. BWS_ACCESS_TOKEN / OP_SERVICE_ACCOUNT_TOKEN) lives in .env\n# (or your shell / desktop session); everything else rotates centrally.\n# Failures never block startup — Hermes warns once and continues with\n# whatever .env already had.\n#\n# Multiple sources can be enabled at once:\n# - \"mapped\" sources (explicit VAR -> ref bindings, e.g. 1Password's env:\n# map) beat \"bulk\" sources (whole-project dumps like Bitwarden BSM)\n# - within a shape, the first source to claim a var wins; later claims\n# are skipped with a startup warning (never a silent clobber)\n# - a source's override_existing lets it beat .env/shell values, but\n# never another secret source's claim\n# Docs: https://hermes-agent.nousresearch.com/docs/user-guide/secrets/\n#\n# secrets:\n# # Optional explicit ordering of enabled sources.\n# # sources: [onepassword, bitwarden]\n#\n# # ---- Bitwarden Secrets Manager (bws CLI) --------------------------------\n# bitwarden:\n# enabled: false\n# access_token_env: BWS_ACCESS_TOKEN # bootstrap token, sourced from .env\n# project_id: \"\" # UUID of the BSM project to sync\n# server_url: \"\" # \"\" = US Cloud; EU/self-hosted URL otherwise\n# cache_ttl_seconds: 300 # 0 disables fresh caching\n# encrypted_cache: # optional encrypted stale fallback\n# enabled: false\n# max_stale_seconds: 0 # 0 disables stale fallback\n# override_existing: true # BSM values win over existing env\n# auto_install: true # lazy-download bws into ~/.hermes/bin\n#\n# # ---- 1Password (op CLI) -------------------------------------------------\n# onepassword:\n# enabled: false\n# # Map env-var names to op:// secret references. Each is resolved with a\n# # single `op read` at startup.\n# env:\n# OPENAI_API_KEY: \"op://Private/OpenAI/api key\"\n# ANTHROPIC_API_KEY: \"op://Private/Anthropic/credential\"\n# account: \"\" # op --account shorthand; \"\" = default\n# service_account_token_env: OP_SERVICE_ACCOUNT_TOKEN # headless auth; unset = desktop session\n# binary_path: \"\" # \"\" = resolve op via PATH; else absolute path\n# cache_ttl_seconds: 300 # 0 disables BOTH cache layers\n# override_existing: true # resolved values win over existing env\n#\n# # ---- Command helper (any CLI vault) --------------------------------------\n# # Run a user-configured helper that prints KEY=VALUE lines on stdout —\n# # works with any secret store that has a CLI: keepassxc-cli, secret-tool,\n# # pass, gpg, or a script that cats a tmpfs env file. Composes with the\n# # sources above (enable any combination). POSIX-only (needs /bin/sh).\n# # The helper must be fast and NON-interactive (hard timeout, 1 MiB cap);\n# # its stderr is discarded so diagnostics can't leak secret material.\n# command:\n# enabled: false\n# command: \"cat /run/user/1000/hermes-secrets.env\"\n# helper_timeout_seconds: 3\n# override_existing: false # .env/shell win by default\n"}, "e89216e7f5d5ce9edf186082160791d207901828": {"t": 1785768035.9521444, "n": 78197, "h": "13decab0f231889a", "c": "# Hermes Agent CLI Configuration\n# Copy this file to cli-config.yaml and customize as needed.\n# This file configures the CLI behavior. Environment variables in .env take precedence.\n\n# =============================================================================\n# Model Configuration\n# =============================================================================\nmodel:\n # Default model to use (can be overridden with --model flag)\n # Both \"default\" and \"model\" work as the key name here.\n default: \"anthropic/claude-opus-4.6\"\n \n # Inference provider selection:\n # \"auto\" - Auto-detect from credentials (default)\n # \"openrouter\" - OpenRouter (requires: OPENROUTER_API_KEY or OPENAI_API_KEY)\n # \"nous\" - Nous Portal OAuth (requires: hermes auth add nous)\n # \"nous-api\" - Nous Portal API key (requires: NOUS_API_KEY)\n # \"anthropic\" - Direct Anthropic API (requires: ANTHROPIC_API_KEY)\n # \"openai-codex\" - OpenAI Codex (requires: hermes auth)\n # \"copilot\" - GitHub Copilot / GitHub Models (requires: GITHUB_TOKEN)\n # \"gemini\" - Use Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n # \"zai\" - Use z.ai / ZhipuAI GLM models (requires: GLM_API_KEY)\n # \"kimi-coding\" - Kimi / Moonshot AI (requires: KIMI_API_KEY)\n # \"minimax\" - MiniMax global (requires: MINIMAX_API_KEY)\n # \"minimax-cn\" - MiniMax China (requires: MINIMAX_CN_API_KEY)\n # \"huggingface\" - Hugging Face Inference (requires: HF_TOKEN)\n # \"nvidia\" - NVIDIA NIM / build.nvidia.com (requires: NVIDIA_API_KEY)\n # \"xiaomi\" - Xiaomi MiMo (requires: XIAOMI_API_KEY)\n # \"arcee\" - Arcee AI Trinity models (requires: ARCEEAI_API_KEY)\n # \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY — https://ollama.com/settings)\n # \"deepinfra\" - DeepInfra (requires: DEEPINFRA_API_KEY)\n # \"kilocode\" - KiloCode gateway (requires: KILOCODE_API_KEY)\n # \"azure-foundry\" - Microsoft Foundry / Azure OpenAI (API key or Entra ID)\n # \"lmstudio\" - LM Studio local server (optional: LM_API_KEY, defaults to http://127.0.0.1:1234/v1)\n #\n # Local servers (LM Studio, Ollama, vLLM, llama.cpp):\n # \"custom\" - Any other OpenAI-compatible endpoint. Set base_url below.\n # Aliases: \"ollama\", \"vllm\", \"llamacpp\" all map to \"custom\".\n # LM Studio is first-class and uses provider: \"lmstudio\".\n # It works with both no-auth and auth-enabled server modes.\n #\n # Can also be overridden for a single invocation with the --provider flag.\n provider: \"auto\"\n \n # API configuration (falls back to OPENROUTER_API_KEY env var)\n # api_key: \"your-key-here\" # Uncomment to set here instead of .env\n base_url: \"https://openrouter.ai/api/v1\"\n\n # Azure Foundry keyless auth example:\n # provider: \"azure-foundry\"\n # base_url: \"https://<resource>.openai.azure.com/openai/v1\"\n # auth_mode: \"entra_id\" # DefaultAzureCredential: az login, managed identity, workload identity, etc.\n # default: \"gpt-4o\" # Deployment/model name\n # entra:\n # scope: \"https://ai.azure.com/.default\" # Optional; this is the default.\n\n # ── Token limits — two settings, easy to confuse ──────────────────────────\n #\n # context_length: TOTAL context window (input + output tokens combined).\n # Controls when Hermes compresses history and validates requests.\n # Leave unset — Hermes auto-detects the correct value from the provider.\n # Set manually only when auto-detection is wrong (e.g. a local server with\n # a custom num_ctx, or a proxy that doesn't expose /v1/models).\n #\n # context_length: 131072\n #\n # max_tokens: OUTPUT cap — maximum tokens the model may generate per response.\n # Unrelated to how long your conversation history can be.\n # The OpenAI-standard name \"max_tokens\" is a misnomer; Anthropic's native\n # API has since renamed it \"max_output_tokens\" for clarity.\n # Leave unset to use the model's native output ceiling (recommended).\n # Set only if you want to deliberately limit individual response length.\n #\n# max_tokens: 8192\n\n # ── Custom request headers (optional) ─────────────────────────────────────\n #\n # default_headers: extra HTTP headers sent on every request to an\n # OpenAI-compatible endpoint. User values take precedence over the\n # provider/SDK defaults, so this is the supported way to override the\n # OpenAI Python SDK's identifying headers (User-Agent: OpenAI/Python ...,\n # X-Stainless-*) when a custom provider sits behind a gateway/WAF that\n # rejects them — e.g. an upstream that returns \"502 Upstream access\n # forbidden\" for the SDK default User-Agent but accepts a plain one.\n # Applies on the OpenAI wire only (not native Anthropic / Bedrock).\n #\n # default_headers:\n # User-Agent: \"curl/8.7.1\"\n #\n # extra_headers: accepted as an alias of default_headers (merged, with\n # extra_headers winning when both are set) — matches the per-provider\n # extra_headers key below.\n #\n # Per-provider variant: named providers / custom_providers entries accept an\n # extra_headers dict scoped to that endpoint only — for reverse proxies,\n # gateways, or custom auth (e.g. Cloudflare Access service tokens).\n # Merged onto SDK/provider defaults with the entry's values winning.\n # Header values are treated as secrets and are never logged.\n #\n # providers:\n # my-proxy:\n # base_url: \"https://llm.internal.example.com/v1\"\n # key_env: \"MY_PROXY_API_KEY\"\n # extra_headers:\n # CF-Access-Client-Id: \"xxxx.access\"\n # CF-Access-Client-Secret: \"${CF_ACCESS_SECRET}\"\n # X-Client-Name: \"hermes-agent\"\n\n# Named provider overrides (optional)\n# Use this for per-provider request timeouts, non-stream stale timeouts,\n# and per-model exceptions.\n# Applies to the primary turn client on every api_mode (OpenAI-wire, native\n# Anthropic, and Anthropic-compatible providers), the fallback chain, and\n# client rebuilds during credential rotation. For OpenAI-wire chat\n# completions (streaming and non-streaming) the configured value is also\n# used as the per-request ``timeout=`` kwarg so it wins over the legacy\n# HERMES_API_TIMEOUT env var (which still applies when no config is set).\n# ``stale_timeout_seconds`` controls the non-streaming stale-call detector and\n# wins over the legacy HERMES_API_CALL_STALE_TIMEOUT env var. Leaving these\n# unset keeps the legacy defaults (HERMES_API_TIMEOUT=1800s,\n# HERMES_API_CALL_STALE_TIMEOUT=90s, native Anthropic 900s). The\n# implicit non-stream stale detector is auto-disabled for local endpoints\n# and can scale upward for very large contexts.\n#\n# Not currently wired for AWS Bedrock (bedrock_converse + AnthropicBedrock\n# SDK paths) — those use boto3 with its own timeout configuration.\n#\n# providers:\n# ollama-local:\n# request_timeout_seconds: 300 # Longer timeout for local cold-starts\n# stale_timeout_seconds: 900 # Explicitly re-enable stale detection on local endpoints\n# anthropic:\n# request_timeout_seconds: 30 # Fast-fail cloud requests\n# models:\n# claude-opus-4.6:\n# timeout_seconds: 600 # Longer timeout for extended-thinking Opus calls\n# openai-codex:\n# models:\n# gpt-5.4:\n# stale_timeout_seconds: 1800 # Longer non-stream stale timeout for slow large-context turns\n\n# =============================================================================\n# OpenRouter Provider Routing (only applies when using OpenRouter)\n# =============================================================================\n# Control how requests are routed across providers on OpenRouter.\n# See: https://openrouter.ai/docs/guides/routing/provider-selection\n#\n# provider_routing:\n# # Sort strategy: \"price\" (default), \"throughput\", or \"latency\"\n# # Append :nitro to model name for a shortcut to throughput sorting.\n# sort: \"throughput\"\n#\n# # Only allow these providers (provider slugs from OpenRouter)\n# # only: [\"anthropic\", \"google\"]\n#\n# # Skip these providers entirely\n# # ignore: [\"deepinfra\", \"fireworks\"]\n#\n# # Try providers in this order (overrides default load balancing)\n# # order: [\"anthropic\", \"google\", \"together\"]\n#\n# # Require providers to support all parameters in your request\n# # require_parameters: true\n#\n# # Data policy: \"allow\" (default) or \"deny\" to exclude providers that may store data\n# # data_collection: \"deny\"\n\n# =============================================================================\n# OpenRouter Response Caching (only applies when using OpenRouter)\n# =============================================================================\n# Cache identical API responses at the OpenRouter edge for free instant replays.\n# When enabled, identical requests (same model, messages, parameters) return\n# cached responses with zero billing. Separate from Anthropic prompt caching.\n# See: https://openrouter.ai/docs/guides/features/response-caching\n#\n# openrouter:\n# response_cache: true # Enable response caching (default: true)\n# response_cache_ttl: 300 # Cache TTL in seconds, 1-86400 (default: 300)\n\n# =============================================================================\n# Git Worktree Isolation\n# =============================================================================\n# When enabled, each CLI session creates an isolated git worktree so multiple\n# agents can work on the same repo concurrently without file collisions.\n# Equivalent to always passing --worktree / -w on the command line.\n#\n# worktree: true # Always create a worktree when in a git repo\n# worktree: false # Default — only create when -w flag is passed\n#\n# By default a new worktree branches from the freshly-fetched remote tip\n# (the current branch's upstream, else the remote's default branch) so it\n# starts current with the project instead of from the local clone's\n# (possibly stale) HEAD. Set worktree_sync: false to branch from local HEAD\n# instead — useful when offline or when you deliberately want the clone's\n# exact current state as the base.\n#\n# worktree_sync: true # Default — branch from the fetched remote tip\n# worktree_sync: false # Branch from local HEAD (offline / pinned base)\n\n# =============================================================================\n# Terminal Tool Configuration\n# =============================================================================\n# Choose ONE of the following terminal configurations by uncommenting it.\n# The terminal tool executes commands in the specified environment.\n\n# -----------------------------------------------------------------------------\n# OPTION 1: Local execution (default)\n# Commands run directly on your machine in the current directory\n# -----------------------------------------------------------------------------\n# Working directory behavior:\n# - CLI (`hermes` command): Uses \".\" (current directory where you run hermes)\n# - Gateway/messaging/cron: Uses terminal.cwd here; legacy .env cwd values are deprecated\nterminal:\n backend: \"local\"\n cwd: \".\" # For local backend: \".\" = current directory. Ignored for remote backends unless a backend documents otherwise.\n timeout: 180\n # HOME policy for tool subprocesses:\n # auto - default: host uses your real HOME; containers use HERMES_HOME/home\n # real - force your real OS-user HOME\n # profile - force HERMES_HOME/home for strict per-profile CLI config isolation\n home_mode: \"auto\"\n docker_mount_cwd_to_workspace: false # SECURITY: off by default. Opt in to mount the launch cwd into Docker /workspace.\n lifetime_seconds: 300\n # sudo_password: \"hunter2\" # Optional: pipe a sudo password via sudo -S. SECURITY WARNING: plaintext.\n # sudo_password: \"\" # Explicit empty password: try empty and never open the interactive sudo prompt.\n\n# -----------------------------------------------------------------------------\n# OPTION 2: SSH remote execution\n# Commands run on a remote server - agent code stays local (sandboxed)\n# Great for: keeping agent isolated from its own code, using powerful remote hardware\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"ssh\"\n# cwd: \"/home/myuser/project\" # Path on the REMOTE server\n# timeout: 180\n# lifetime_seconds: 300\n# ssh_host: \"my-server.example.com\"\n# ssh_user: \"myuser\"\n# ssh_port: 22\n# ssh_key: \"~/.ssh/id_rsa\" # Optional - uses ssh-agent if not specified\n\n# -----------------------------------------------------------------------------\n# OPTION 3: Docker container\n# Commands run in an isolated Docker container\n# Great for: reproducible environments, testing, isolation\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"docker\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /)\n# timeout: 180\n# lifetime_seconds: 300\n# docker_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# docker_mount_cwd_to_workspace: true # Explicit opt-in: mount your launch cwd into /workspace\n# # Optional: run the container as your host user's uid:gid so files written\n# # into bind-mounted dirs are owned by you, not root. Drops SETUID/SETGID\n# # caps too since no gosu privilege drop is needed. Leave off if your\n# # chosen docker_image expects to start as root.\n# docker_run_as_host_user: true\n# # Optional: explicitly forward selected env vars into Docker.\n# # These values come from your current shell first, then ~/.hermes/.env.\n# # Warning: anything forwarded here is visible to commands run in the container.\n# docker_forward_env:\n# - \"GITHUB_TOKEN\"\n# - \"NPM_TOKEN\"\n# # Optional: extra flags passed verbatim to docker run (appended after security defaults).\n# # Useful for adding capabilities (e.g. apt installs needing SETUID) or custom options.\n# # Example: add a Linux capability not included by default\n# # docker_extra_args:\n# # - \"--cap-add\"\n# # - \"SETUID\"\n\n# -----------------------------------------------------------------------------\n# OPTION 4: Singularity/Apptainer container\n# Commands run in a Singularity container (common in HPC environments)\n# Great for: HPC clusters, shared compute environments\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"singularity\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# singularity_image: \"docker://nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 5: Modal cloud execution\n# Commands run on Modal's cloud infrastructure\n# Great for: GPU access, scalable compute, serverless execution\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"modal\"\n# cwd: \"/workspace\" # Path INSIDE the sandbox (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# modal_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 6: Daytona cloud execution\n# Commands run in Daytona cloud sandboxes\n# Great for: Cloud dev environments, persistent workspaces, team collaboration\n# Requires: pip install daytona, DAYTONA_API_KEY env var\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"daytona\"\n# cwd: \"~\"\n# timeout: 180\n# lifetime_seconds: 300\n# daytona_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# container_disk: 10240 # Daytona max is 10GB per sandbox\n\n#\n# --- Container resource limits (docker, singularity, modal, daytona -- ignored for local/ssh) ---\n# These settings apply to all container backends. They control the resources\n# allocated to the sandbox and whether its filesystem persists across sessions.\n container_cpu: 1 # CPU cores\n container_memory: 5120 # Memory in MB (5120 = 5GB)\n container_disk: 51200 # Disk in MB (51200 = 50GB)\n container_persistent: true # Persist filesystem across sessions (false = ephemeral)\n\n# -----------------------------------------------------------------------------\n# SUDO SUPPORT (works with ALL backends above)\n# -----------------------------------------------------------------------------\n# Add sudo_password to any terminal config above to enable sudo commands.\n# The password is piped via `sudo -S`. Works with local, ssh, docker, etc.\n#\n# SECURITY WARNING: Password stored in plaintext!\n#\n# INTERACTIVE PROMPT: If sudo_password is unset and the CLI is running,\n# you'll be prompted to enter your password when sudo is needed:\n# - 45-second timeout (auto-skips if no input)\n# - Press Enter to skip (command fails gracefully)\n# - Password is hidden while typing\n# - Password is cached for the session\n#\n# EMPTY PASSWORDS: Setting sudo_password to an explicit empty string is different\n# from leaving it unset. Hermes will try an empty password via `sudo -S` and\n# will not open the interactive prompt. This is useful for passwordless sudo,\n# Touch ID sudo setups, and environments where prompting is just noise.\n#\n# ALTERNATIVES:\n# - SSH backend: Configure passwordless sudo on the remote server\n# - Containers: Run as root inside the container (no sudo needed)\n# - Local: Configure /etc/sudoers for specific commands\n#\n# Example (add to your terminal section):\n# sudo_password: \"your-password-here\"\n\n# =============================================================================\n# Security Scanning (tirith)\n# =============================================================================\n# Optional pre-exec command security scanning via tirith.\n# Detects homograph URLs, pipe-to-shell, terminal injection, env manipulation.\n# Install: brew install sheeki03/tap/tirith\n# Docs: https://github.com/sheeki03/tirith\n#\n# security:\n# tirith_enabled: true # Enable/disable tirith scanning\n# tirith_path: \"tirith\" # Path to tirith binary (supports ~ expansion)\n# tirith_timeout: 5 # Scan timeout in seconds\n# tirith_fail_open: true # Allow commands if tirith unavailable\n\n# =============================================================================\n# Browser Tool Configuration\n# =============================================================================\nbrowser:\n # Inactivity timeout in seconds - browser sessions are automatically closed\n # after this period of no activity between agent loops (default: 120 = 2 minutes)\n inactivity_timeout: 120\n\n# =============================================================================\n# Tool Loop Guardrails\n# =============================================================================\n# Soft warnings are enabled by default. They append guidance to repeated failed\n# or non-progressing tool results but still let the tool execute. Hard stops are\n# opt-in circuit breakers for autonomous/cron sessions where stopping a loop is\n# preferable to spending the full iteration budget.\ntool_loop_guardrails:\n warnings_enabled: true\n hard_stop_enabled: false\n warn_after:\n exact_failure: 2\n same_tool_failure: 3\n idempotent_no_progress: 2\n hard_stop_after:\n exact_failure: 5\n same_tool_failure: 8\n idempotent_no_progress: 5\n\n# =============================================================================\n# Context Compression (Auto-shrinks long conversations)\n# =============================================================================\n# When conversation approaches model's context limit, middle turns are\n# automatically summarized to free up space while preserving important context.\n#\n# HOW IT WORKS:\n# 1. Tracks actual token usage from API responses (not estimates)\n# 2. When prompt_tokens >= threshold% of model's context_length, triggers compression\n# 3. Protects first 3 turns (system prompt, initial request, first response)\n# 4. Protects last N turns (default 20 messages = ~10 full turns of recent context)\n# 5. Summarizes middle turns using a fast/cheap model\n# 6. Inserts summary as a user message, continues conversation seamlessly\n#\n# Post-compression tail budget is target_ratio × threshold × context_length:\n# 200K context, threshold 0.50, ratio 0.20 → 20K tokens of recent tail preserved\n# 1M context, threshold 0.50, ratio 0.20 → 100K tokens of recent tail preserved\n#\ncompression:\n # Enable automatic context compression (default: true)\n # Set to false if you prefer to manage context manually or want errors on overflow\n enabled: true\n \n # Trigger compression at this % of model's context limit (default: 0.50 = 50%)\n # Lower values = more aggressive compression, higher values = compress later\n # Models with context windows below 512K are floored at 0.75 (raise-only) so\n # compaction doesn't fire with half the window still free; set above 0.75 to override.\n threshold: 0.50\n\n # Existing Codex gpt-5.5 behavior: raise Hermes' compaction trigger to 85%\n # for the ChatGPT Codex OAuth route. Set false to opt back down to threshold.\n codex_gpt55_autoraise: true\n\n # Fraction of the threshold to preserve as recent tail (default: 0.20 = 20%)\n # e.g. 20% of 50% threshold = 10% of total context kept as recent messages.\n # Summary output is separately capped at 12K tokens (Gemini output limit).\n # Range: 0.10 - 0.80\n target_ratio: 0.20\n\n # Number of most-recent messages to always preserve (default: 20 ≈ 10 full turns)\n # Higher values keep more recent conversation intact at the cost of more aggressive\n # compression of older turns.\n protect_last_n: 20\n\n # Codex app-server (codex CLI runtime) thread-compaction mode. The codex\n # agent owns the real thread context on this runtime, so Hermes' summarizer\n # cannot shrink it — compaction goes through the app server instead.\n # native = let Codex decide when to compact its own thread (default)\n # hermes = let Hermes threshold trigger Codex thread/compact/start\n # off = Hermes will not auto-trigger compaction; Codex may still compact natively\n codex_app_server_auto: native\n\n # Number of non-system messages to protect at the head of the transcript, in\n # ADDITION to the system prompt (which is always implicitly protected).\n # Head messages are NEVER summarized — they survive every compression\n # indefinitely. This gives stable early context for short/medium sessions,\n # but in long-running sessions that rely on rolling compaction the pinned\n # opening turns may not match how you want the session framed over time.\n # Set to 0 to preserve ONLY the system prompt (plus the rolling summary\n # and recent tail) — the cleanest configuration for long-running sessions.\n # Default 3 preserves the system prompt plus the first three non-system\n # head messages, matching the pre-feature behaviour.\n protect_first_n: 3\n\n # To pin a specific model/provider for compression summaries, use the\n # auxiliary section below (auxiliary.compression.provider / model).\n\n# =============================================================================\n# Anthropic prompt caching TTL\n# =============================================================================\n# When prompt caching is active (Claude via OpenRouter or native Anthropic),\n# Anthropic supports two TTL tiers for cached prefixes: \"5m\" (default) and\n# \"1h\". Other values are ignored and \"5m\" is used.\n#\nprompt_caching:\n cache_ttl: \"5m\" # use \"1h\" for long sessions with pauses between turns\n\n# =============================================================================\n# Auxiliary Models (Advanced — Experimental)\n# =============================================================================\n# Hermes uses lightweight \"auxiliary\" models for side tasks: image analysis,\n# browser screenshot analysis, web page summarization, TTS audio-tag insertion,\n# and context compression.\n#\n# By default these use Gemini Flash via OpenRouter or Nous Portal and are\n# auto-detected from your credentials. You do NOT need to change anything\n# here for normal usage.\n#\n# WARNING: Overriding these with providers other than OpenRouter or Nous Portal\n# is EXPERIMENTAL and may not work. Not all models/providers support vision,\n# produce usable summaries, or accept the same API format. Change at your own\n# risk — if things break, reset to \"auto\" / empty values.\n#\n# Each task has its own provider + model pair so you can mix providers.\n# For example: OpenRouter for vision (needs multimodal), but your main\n# local endpoint for compression (just needs text).\n#\n# Provider options:\n# \"auto\" - Best available: OpenRouter → Nous Portal → main endpoint (default)\n# \"openrouter\" - Force OpenRouter (requires OPENROUTER_API_KEY)\n# \"nous\" - Force Nous Portal (requires: hermes auth add nous)\n# \"gemini\" - Force Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n# \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY)\n# \"codex\" - Force Codex OAuth (requires: hermes model → Codex).\n# Uses gpt-5.3-codex which supports vision.\n# \"main\" - Use your custom endpoint (OPENAI_BASE_URL + OPENAI_API_KEY).\n# Works with OpenAI API, local models, or any OpenAI-compatible\n# endpoint. Also falls back to Codex OAuth and API-key providers.\n#\n# Model: leave empty to use the provider's default. When empty, OpenRouter\n# uses \"google/gemini-3-flash-preview\" and Nous uses \"gemini-3-flash\".\n# Other providers pick a sensible default automatically.\n#\n# auxiliary:\n# # Image analysis: vision_analyze tool + browser screenshots\n# vision:\n# provider: \"auto\"\n# model: \"\" # e.g. \"google/gemini-2.5-flash\", \"openai/gpt-4o\"\n# timeout: 30 # LLM API call timeout (seconds)\n# download_timeout: 30 # Image HTTP download timeout (seconds)\n# # Increase for slow connections or self-hosted image servers\n# reasoning_effort: \"\" # Per-task thinking level: none, minimal, low, medium,\n# # high, xhigh, max, ultra. Empty = provider default.\n# # Works on every auxiliary task block (vision,\n# # web_extract, compression, title_generation, curator,\n# # background_review, moa_reference, ...). Example: run\n# # compression at \"low\" and vision at \"none\" to cut\n# # side-task latency/cost on reasoning models.\n#\n# # Web page scraping / summarization + browser page text extraction\n# web_extract:\n# provider: \"auto\"\n# model: \"\"\n# reasoning_effort: \"low\"\n#\n# # Gemini 3.1 TTS hidden audio-tag insertion\n# tts_audio_tags:\n# provider: \"auto\" # empty model = your main chat model\n# model: \"\"\n# timeout: 30\n#\n# # Automatic session title generation after the first exchange\n# title_generation:\n# enabled: true # set false to disable auto-title generation\n# provider: \"auto\"\n# model: \"\"\n# timeout: 30\n# language: \"\" # empty = match the user's language; or e.g. \"English\"\n#\n# # Session search — summarizes matching past sessions\n# session_search:\n# provider: \"auto\"\n# model: \"\"\n# timeout: 30\n# max_concurrency: 3 # Limit parallel summaries to reduce request-burst 429s\n# extra_body: {} # Provider-specific OpenAI-compatible request fields\n# # Example for providers that support request-body\n# # reasoning controls:\n# # extra_body:\n# # enable_thinking: false\n# # Some vLLM/Qwen deployments expect this nested:\n# # extra_body:\n# # chat_template_kwargs:\n# # enable_thinking: false\n\n# =============================================================================\n# Persistent Memory\n# =============================================================================\n# Bounded curated memory injected into the system prompt every session.\n# Two stores: MEMORY.md (agent's notes) and USER.md (user profile).\n# Character limits keep the memory small and focused. The agent manages\n# pruning -- when at the limit, it must consolidate or replace entries.\n# Disabled by default in batch_runner.\n#\nmemory:\n # Agent's personal notes: environment facts, conventions, things learned\n memory_enabled: true\n \n # User profile: preferences, communication style, expectations\n user_profile_enabled: true\n \n # Character limits (~2.75 chars per token, model-independent)\n memory_char_limit: 2200 # ~800 tokens\n user_char_limit: 1375 # ~500 tokens\n\n # Periodic memory nudge: remind the agent to consider saving memories\n # every N user turns. Set to 0 to disable. Only active when memory is enabled.\n nudge_interval: 10 # Nudge every 10 user turns (0 = disabled)\n\n # Memory flush: give the agent one turn to save memories before context is\n # lost (compression, /new, /reset, exit). Set to 0 to disable.\n # For exit/reset, only fires if the session had at least this many user turns.\n flush_min_turns: 6 # Min user turns to trigger flush on exit/reset (0 = disabled)\n\n# =============================================================================\n# Session Reset Policy (Messaging Platforms)\n# =============================================================================\n# Controls when messaging sessions (Telegram, Discord, WhatsApp, Slack) are\n# automatically cleared. Default is \"none\": sessions never auto-reset —\n# conversation context lives until you /reset or /new manually, or context\n# compression kicks in. Opt in to automatic resets if you prefer sessions to\n# clear on a schedule (long-lived context increases API cost per message,\n# though prompt caching and compression keep this manageable).\n#\n# When an automatic reset triggers, the agent first saves important\n# information to its persistent memory — but the conversation context is\n# wiped. The agent starts fresh but retains learned facts via its memory\n# system.\n#\n# Modes:\n# \"none\" - Never auto-reset (default); context lives until /reset or compression\n# \"idle\" - Reset after N minutes of inactivity\n# \"daily\" - Reset at a fixed hour each day\n# \"both\" - Reset on EITHER inactivity timeout or daily boundary\n#\nsession_reset:\n mode: none # \"none\", \"idle\", \"daily\", or \"both\"\n idle_minutes: 1440 # Inactivity timeout in minutes (used by \"idle\"/\"both\")\n at_hour: 4 # Daily reset hour, 0-23 local time (used by \"daily\"/\"both\")\n\n# Maximum number of simultaneously active chat sessions across CLI, TUI,\n# dashboard chat, and messaging gateway. Set to null, 0, or omit to allow\n# unlimited concurrent sessions. When the limit is reached, new sessions get a\n# clean error while existing active sessions keep their normal behavior. This\n# top-level key takes precedence over gateway.max_concurrent_sessions. The cap\n# is a best-effort single-host/profile runtime guard; Hermes fails open if the\n# local runtime lease registry cannot be read or locked.\nmax_concurrent_sessions: null\n\n# When true, group/channel chats use one session per participant when the platform\n# provides a user ID. This is the secure default and prevents users in the same\n# room from sharing context, interrupts, and token costs. Set false only if you\n# explicitly want one shared \"room brain\" per group/channel.\ngroup_sessions_per_user: true\n\n# ─────────────────────────────────────────────────────────────────────────────\n# API Server — per-client model routing\n# ─────────────────────────────────────────────────────────────────────────────\n# Route different API clients to different models/providers on a single\n# Hermes deployment. Clients choose a backend by sending a specific string\n# as the OpenAI ``model`` field. Unmapped model values fall back to the\n# global model configured in the ``model:`` section above, and an explicit\n# session /model override always wins over a route.\n#\n# Configure via the ``platforms.api_server.extra.model_routes`` gateway\n# config block:\n#\n# platforms:\n# api_server:\n# enabled: true\n# extra:\n# key: \"your-api-server-secret\"\n# model_routes:\n# # Xiaozhi clients send model=\"minimax-m2\" → routed to MiniMax via OpenRouter\n# minimax-m2:\n# model: \"minimax/minimax-m1\"\n# provider: \"openrouter\" # optional — overrides global provider\n# # api_key: \"sk-...\" # optional — per-route UPSTREAM provider\n# # key (NOT caller auth; never logged)\n# # base_url: \"https://...\" # optional — per-route base URL\n# # GPT clients keep their own alias\n# gpt-5:\n# model: \"openai/gpt-5\"\n# provider: \"openrouter\"\n#\n# Configured aliases are automatically listed by GET /v1/models so clients\n# can discover them without manual coordination. Caller authentication is\n# unchanged: every request still authenticates with the global API server\n# key (``extra.key`` / API_SERVER_KEY).\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Gateway Streaming\n# ─────────────────────────────────────────────────────────────────────────────\n# Stream tokens to messaging platforms in real-time. The bot sends a message\n# on first token, then progressively edits it as more tokens arrive.\n# Disabled by default — enable to try the streaming UX on Telegram/Discord/Slack.\n# For Telegram, partial edits are sent as plain text and only the final edit uses MarkdownV2.\nstreaming:\n enabled: false\n # transport: edit # \"edit\" = progressive editMessageText\n # edit_interval: 0.3 # seconds between message edits\n # buffer_threshold: 40 # chars before forcing an edit flush\n # cursor: \" ▉\" # cursor shown during streaming\n\n# =============================================================================\n# Skills Configuration\n# =============================================================================\n# Skills are reusable procedures the agent can load and follow. The agent can\n# also create new skills after completing complex tasks.\n#\nskills:\n # Nudge the agent to create skills after complex tasks.\n # Every N tool-calling iterations, remind the model to consider saving a skill.\n # Set to 0 to disable.\n creation_nudge_interval: 15\n\n # External skill directories — share skills across tools/agents without\n # copying them into ~/.hermes/skills/. Each path is expanded (~ and ${VAR})\n # and resolved to an absolute path. External dirs are read-only: skill\n # creation always writes to ~/.hermes/skills/. Local skills take precedence\n # when names collide.\n # external_dirs:\n # - ~/.agents/skills\n # - /home/shared/team-skills\n\n# =============================================================================\n# Agent Behavior\n# =============================================================================\nagent:\n # Maximum tool-calling iterations per conversation\n # Higher = more room for complex tasks, but costs more tokens\n # Recommended: 20-30 for focused tasks, 50-100 for open exploration\n max_turns: 60\n\n # Inactivity timeout for gateway agent runs (seconds, 0 = unlimited).\n # The agent can run indefinitely when actively calling tools or receiving\n # API responses. Only fires after the agent has been idle for this duration.\n # gateway_timeout: 1800\n\n # Staged warning: send a warning before escalating to full timeout.\n # Fires once per run when inactivity reaches this threshold (seconds).\n # Set to 0 to disable the warning.\n # gateway_timeout_warning: 900\n\n # Graceful drain timeout for gateway stop/restart (seconds).\n # Default 0 = no drain: a restart interrupts in-flight agents immediately,\n # cleans up, and exits. Set a positive value only if you want a grace\n # window on /restart, and keep it well under systemd's TimeoutStopSec.\n # restart_drain_timeout: 0\n\n # Max app-level retry attempts for API errors (connection drops, provider\n # timeouts, 5xx, etc.) before the agent surfaces the failure. Lower this\n # to 1 if you use fallback providers and want fast failover on flaky\n # primaries (default 3). The OpenAI SDK does its own low-level retries\n # underneath this wrapper — this is the Hermes-level loop.\n # api_max_retries: 3\n\n # After the agent edits code without fresh passing verification, nudge it to\n # verify before finishing. The default \"auto\" enables it on interactive\n # coding surfaces (CLI, TUI, desktop) and programmatic callers, and disables\n # it on conversational messaging surfaces (Telegram, Discord, etc.) where the\n # verification summary would reach a human as chat noise. Set true or false to\n # force it on or off; the HERMES_VERIFY_ON_STOP env var (1/0) takes precedence.\n # verify_on_stop: auto\n\n # Standing operator instructions for the coding posture (when Hermes is in a\n # code workspace). Appended to the coding brief as an extra system block, so\n # you can pin project-wide workflow rules without editing the shipped brief.\n # Accepts a string or a list of strings. Takes effect next session.\n # coding_instructions:\n # - \"For UI work, don't run tsc/lint until I approve the look.\"\n # - \"Clean the diff before you commit and push.\"\n\n # When verify-on-stop finds edited code without fresh verification evidence,\n # append guidance for creative UI work (avoid broad tsc/lint/test before visual\n # approval) and clean-diff expectations. Set false to keep that nudge terse.\n # verify_guidance: true\n\n # A `pre_verify` hook (plugin or shell, see Event Hooks docs) can keep the\n # agent going one more turn to verify/clean before finishing. This caps how\n # many times one turn may be nudged to continue, so a hook can't trap the loop.\n # Default 3.\n # max_verify_nudges: 3\n\n # Enable verbose logging\n verbose: false\n \n # Reasoning effort level (OpenRouter and Nous Portal)\n # Controls how much \"thinking\" the model does before responding.\n # Options: \"xhigh\" (max), \"high\", \"medium\", \"low\", \"minimal\", \"none\" (disable)\n reasoning_effort: \"medium\"\n \n # Per-model reasoning effort overrides (optional dict)\n # Key: any sensible model spelling works (exact, dots↔dashes interchangeable,\n # provider prefix optional). First match wins.\n # Value: reasoning effort level (same options as reasoning_effort)\n # Override the global reasoning_effort for that specific model.\n # NOTE: no `hermes config set` support for this key -- edit YAML directly.\n # reasoning_overrides:\n # \"openrouter/anthropic/claude-opus-4.5\": \"xhigh\"\n # \"openai/gpt-5\": \"low\"\n # \"claude-opus-4.6\": \"high\" # bare model name also works\n # \"deepseek/deepseek-v4-pro\": \"xhigh\" # dots and dashes are interchangeable\n reasoning_overrides: {}\n \n # Predefined personalities (use with /personality command)\n personalities:\n helpful: \"You are a helpful, friendly AI assistant.\"\n concise: \"You are a concise assistant. Keep responses brief and to the point.\"\n technical: \"You are a technical expert. Provide detailed, accurate technical information.\"\n creative: \"You are a creative assistant. Think outside the box and offer innovative solutions.\"\n teacher: \"You are a patient teacher. Explain concepts clearly with examples.\"\n kawaii: \"You are a kawaii assistant! Use cute expressions like (◕‿◕), ★, ♪, and ~! Add sparkles and be super enthusiastic about everything! Every response should feel warm and adorable desu~! ヽ(>∀<☆)ノ\"\n catgirl: \"You are Neko-chan, an anime catgirl AI assistant, nya~! Add 'nya' and cat-like expressions to your speech. Use kaomoji like (=^・ω・^=) and ฅ^•ﻌ•^ฅ. Be playful and curious like a cat, nya~!\"\n pirate: \"Arrr! Ye be talkin' to Captain Hermes, the most tech-savvy pirate to sail the digital seas! Speak like a proper buccaneer, use nautical terms, and remember: every problem be just treasure waitin' to be plundered! Yo ho ho!\"\n shakespeare: \"Hark! Thou speakest with an assistant most versed in the bardic arts. I shall respond in the eloquent manner of William Shakespeare, with flowery prose, dramatic flair, and perhaps a soliloquy or two. What light through yonder terminal breaks?\"\n surfer: \"Duuude! You're chatting with the chillest AI on the web, bro! Everything's gonna be totally rad. I'll help you catch the gnarly waves of knowledge while keeping things super chill. Cowabunga! 🤙\"\n noir: \"The rain hammered against the terminal like regrets on a guilty conscience. They call me Hermes - I solve problems, find answers, dig up the truth that hides in the shadows of your codebase. In this city of silicon and secrets, everyone's got something to hide. What's your story, pal?\"\n uwu: \"hewwo! i'm your fwiendwy assistant uwu~ i wiww twy my best to hewp you! *nuzzles your code* OwO what's this? wet me take a wook! i pwomise to be vewy hewpful >w<\"\n philosopher: \"Greetings, seeker of wisdom. I am an assistant who contemplates the deeper meaning behind every query. Let us examine not just the 'how' but the 'why' of your questions. Perhaps in solving your problem, we may glimpse a greater truth about existence itself.\"\n hype: \"YOOO LET'S GOOOO!!! 🔥🔥🔥 I am SO PUMPED to help you today! Every question is AMAZING and we're gonna CRUSH IT together! This is gonna be LEGENDARY! ARE YOU READY?! LET'S DO THIS! 💪😤🚀\"\n\n# =============================================================================\n# Toolsets\n# =============================================================================\n# Control which tools the agent has access to.\n# Use `hermes tools` to interactively enable/disable tools per platform.\n\n# =============================================================================\n# Platform Toolsets (per-platform tool configuration)\n# =============================================================================\n# Override which toolsets are available on each platform.\n# If a platform isn't listed here, its built-in default is used.\n#\n# You can use EITHER:\n# - A preset like \"hermes-cli\" or \"hermes-telegram\" (curated tool set)\n# - A list of individual toolsets to compose your own (see list below)\n#\n# Supported platform keys: cli, telegram, discord, whatsapp, slack, qqbot, teams, google_chat\n#\n# Examples:\n#\n# # Use presets (same as defaults):\n# platform_toolsets:\n# cli: [hermes-cli]\n# telegram: [hermes-telegram]\n#\n# # Custom: give Telegram only web + terminal + file + planning:\n# platform_toolsets:\n# telegram: [web, terminal, file, todo]\n#\n# # Custom: CLI without browser or image gen:\n# platform_toolsets:\n# cli: [web, terminal, file, skills, todo, tts, cronjob]\n#\n# # Restrictive: Discord gets read-only tools only:\n# platform_toolsets:\n# discord: [web, vision, skills, todo]\n#\n# If not set, defaults are:\n# cli: hermes-cli (everything + cronjob management)\n# telegram: hermes-telegram (terminal, file, web, vision, image, tts, browser, skills, todo, cronjob, messaging)\n# discord: hermes-discord (same as telegram)\n# whatsapp: hermes-whatsapp (same as telegram)\n# slack: hermes-slack (same as telegram)\n# signal: hermes-signal (same as telegram)\n# homeassistant: hermes-homeassistant (same as telegram)\n# qqbot: hermes-qqbot (same as telegram)\n# teams: hermes-teams (same as telegram)\n# google_chat: hermes-google_chat (same as telegram)\n#\nplatform_toolsets:\n cli: [hermes-cli]\n telegram: [hermes-telegram]\n discord: [hermes-discord]\n whatsapp: [hermes-whatsapp]\n slack: [hermes-slack]\n signal: [hermes-signal]\n homeassistant: [hermes-homeassistant]\n qqbot: [hermes-qqbot]\n yuanbao: [hermes-yuanbao]\n teams: [hermes-teams]\n google_chat: [hermes-google_chat]\n\n# =============================================================================\n# Gateway Platform Settings\n# =============================================================================\n# Optional per-platform messaging settings.\n# Platform-specific knobs live under `extra`.\n#\n# platforms:\n# telegram:\n# reply_to_mode: \"first\" # off | first | all\n# # guest_mode lets explicit @mentions from non-allowlisted groups through.\n# # Default false; ordinary messages, replies, and regex wake words stay blocked.\n# guest_mode: false\n# # allowed_chats: [\"-1001234567890\"]\n# extra:\n# disable_link_previews: false # Set true to suppress Telegram URL previews in bot messages\n# 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\n# rich_drafts: false # Experimental rich draft previews during Telegram DM streaming; default false because Telegram Desktop/macOS can visually overlay draft frames\n# command_menu:\n# # Telegram allows up to 100 BotCommands; Hermes defaults to 60 so\n# # all built-in commands plus common skill commands stay visible\n# # while remaining under Telegram's payload-size limit. Clamped 1..100.\n# max_commands: 60\n# # prepend = user priority first, then Hermes defaults\n# # append = Hermes defaults first, then user priority\n# # replace = only the list below defines priority\n# priority_mode: prepend\n# priority:\n# - my_plugin_command\n# webhook:\n# extra:\n# # Route scripts default to a 30 second timeout. Scripts must live under\n# # the active profile's scripts directory and receive webhook JSON on stdin.\n# script_timeout_seconds: 30\n#\n# Discord-specific settings (config.yaml top-level, not under platforms:):\n#\n# discord:\n# require_mention: true # Require @mention in server channels (default: true)\n# auto_thread: true # Auto-create thread on @mention (default: true)\n# free_response_channels: \"\" # Channel IDs where no mention is needed\n# reactions: true # Show processing reactions (default: true)\n# history_backfill: true # Recover missed channel messages on mention (default: true)\n# history_backfill_limit: 50 # Max messages to scan backwards (default: 50)\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Available toolsets (use these names in platform_toolsets or the toolsets list)\n#\n# Run `hermes chat --list-toolsets` to see all toolsets and their tools.\n# Run `hermes chat --list-tools` to see every individual tool with descriptions.\n# ─────────────────────────────────────────────────────────────────────────────\n#\n# INDIVIDUAL TOOLSETS (compose your own):\n# web - web_search, web_extract\n# search - web_search only (no scraping)\n# terminal - terminal, process\n# file - read_file, write_file, patch, search\n# browser - browser_navigate, browser_snapshot, browser_click, browser_type,\n# browser_scroll, browser_back, browser_press,\n# browser_get_images, browser_vision (requires BROWSERBASE_API_KEY)\n# vision - vision_analyze (requires OPENROUTER_API_KEY)\n# image_gen - image_generate (requires FAL_KEY)\n# skills - skills_list, skill_view\n# skills_hub - skill_hub (search/install/manage from online registries — user-driven only)\n# todo - todo (in-memory task planning, no deps)\n# tts - text_to_speech (Edge TTS free, or ELEVENLABS/OPENAI/MINIMAX/MISTRAL key)\n# cronjob - cronjob (create/list/update/pause/resume/run/remove scheduled tasks)\n#\n# PRESETS (curated bundles):\n# hermes-cli - All of the above except send_message\n# hermes-telegram - terminal, file, web, vision, image_gen, tts, browser,\n# skills, todo, cronjob, send_message\n# hermes-discord - Same as hermes-telegram\n# hermes-whatsapp - Same as hermes-telegram\n# hermes-slack - Same as hermes-telegram\n#\n# COMPOSITE:\n# debugging - terminal + web + file\n# safe - web + vision (no terminal access)\n# all - Everything available\n#\n# web - Web search and content extraction (web_search, web_extract)\n# search - Web search only, no scraping (web_search)\n# terminal - Command execution and process management (terminal, process)\n# file - File operations: read, write, patch, search\n# browser - Full browser automation (navigate, click, type, screenshot, etc.)\n# vision - Image analysis (vision_analyze)\n# image_gen - Image generation with FLUX (image_generate)\n# skills - Load skill documents (skills_list, skill_view)\n# todo - Task planning and tracking for multi-step work\n# memory - Persistent memory across sessions (personal notes + user profile)\n# session_search - Search and recall past conversations (FTS5 + Gemini Flash summarization)\n# tts - Text-to-speech (Edge TTS free, ElevenLabs, OpenAI, MiniMax, Mistral)\n# cronjob - Schedule and manage automated tasks (CLI-only)\n#\n# Composite toolsets:\n# debugging - terminal + web + file (for troubleshooting)\n# safe - web + vision (no terminal access)\n\n# NOTE: The top-level \"toolsets\" key is deprecated and ignored.\n# Tool configuration is managed per-platform via platform_toolsets above.\n# Use `hermes tools` to configure interactively, or edit platform_toolsets directly.\n#\n# CLI override: hermes chat --toolsets terminal,web,file\n\n# =============================================================================\n# MCP (Model Context Protocol) Servers\n# =============================================================================\n# Connect to external MCP servers to add tools from the MCP ecosystem.\n# Each server's tools are automatically discovered and registered.\n# See website/docs/user-guide/features/mcp.md for full documentation.\n#\n# Stdio servers (spawn a subprocess):\n# command: the executable to run\n# args: command-line arguments\n# env: environment variables (only these + safe defaults passed to subprocess)\n#\n# HTTP servers (connect to a URL):\n# url: the MCP server endpoint\n# headers: HTTP headers (e.g., for authentication)\n#\n# Optional per-server settings:\n# timeout: tool call timeout in seconds (default: 120)\n# connect_timeout: initial connection timeout (default: 60)\n# keepalive_interval: liveness ping cadence in seconds (default: 180).\n# Lower it below the server's session TTL for servers that expire idle\n# sessions quickly (e.g. Unreal Engine editor MCP, ~15s), otherwise idle\n# tool calls hit an expired session and pay a slow reconnect. Floored at 5s.\n#\n# mcp_servers:\n# time:\n# command: uvx\n# args: [\"mcp-server-time\"]\n# filesystem:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-filesystem\", \"/home/user\"]\n# notion:\n# url: https://mcp.notion.com/mcp\n# github:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-github\"]\n# env:\n# GITHUB_PERSONAL_ACCESS_TOKEN: \"ghp_...\"\n#\n# Sampling (server-initiated LLM requests) — enabled by default.\n# Per-server config under the 'sampling' key:\n# analysis:\n# command: npx\n# args: [\"-y\", \"analysis-server\"]\n# sampling:\n# enabled: true # default: true\n# model: \"gemini-3-flash\" # override model (optional)\n# max_tokens_cap: 4096 # max tokens per request\n# timeout: 30 # LLM call timeout (seconds)\n# max_rpm: 10 # max requests per minute\n# allowed_models: [] # model whitelist (empty = all)\n# max_tool_rounds: 5 # tool loop limit (0 = disable)\n# log_level: \"info\" # audit verbosity\n\n# =============================================================================\n# Text-to-Speech\n# =============================================================================\n# TTS defaults to Edge TTS unless changed in ~/.hermes/config.yaml.\n# Gemini TTS supports persona/director prompt files, and Gemini 3.1 Flash TTS\n# can use a hidden auxiliary rewrite pass to insert expressive square-bracket\n# audio tags into the TTS script without showing tags in chat.\n#\n# tts:\n# provider: \"gemini\"\n# gemini:\n# model: \"gemini-3.1-flash-tts-preview\"\n# voice: \"Kore\"\n# audio_tags: false\n# persona_prompt_file: \"\" # e.g. ~/.hermes/tts/radio-host.md\n\n# =============================================================================\n# Voice Transcription (Speech-to-Text)\n# =============================================================================\n# Automatically transcribe voice messages on messaging platforms.\n# Providers: local (free, faster-whisper) | groq (free tier) | openai (Whisper API) | mistral (Voxtral Transcribe)\n# Set the corresponding API key in .env: GROQ_API_KEY, OPENAI_API_KEY, or MISTRAL_API_KEY.\nstt:\n enabled: true\n # provider: \"local\" # auto-detected if omitted\n local:\n model: \"base\" # tiny | base | small | medium | large-v3 | turbo\n # language: \"\" # auto-detect; set to \"en\", \"es\", \"fr\", etc. to force\n openai:\n model: \"whisper-1\" # whisper-1 | gpt-4o-mini-transcribe | gpt-4o-transcribe\n # mistral:\n # model: \"voxtral-mini-latest\" # voxtral-mini-latest | voxtral-mini-2602\n # deepinfra:\n # # Model id is discovered live from the DeepInfra catalog filtered\n # # by the `stt` surface tag — leave `model` blank to take the first\n # # live result. Pin only when you need a specific Whisper variant.\n # model: \"\"\n\n# Text-to-speech. Only the deepinfra block is documented here — the\n# remaining providers (edge, openai, xai, minimax, mistral, gemini,\n# elevenlabs, neutts, kittentts, piper) inherit sensible defaults from\n# DEFAULT_CONFIG in hermes_cli/config.py.\n# tts:\n# provider: \"deepinfra\"\n# deepinfra:\n# # Model id is discovered live from the DeepInfra catalog filtered\n# # by the `tts` surface tag — leave `model` blank to take the first\n# # live result.\n# model: \"\"\n# voice: \"default\"\n\n# Image generation. Each provider plugin reads its own ``image_gen.<name>``\n# block; deepinfra discovers models live from\n# api.deepinfra.com/v1/openai/models filtered by the ``image-gen`` tag —\n# no model id is hardcoded, so retired models disappear automatically.\n# image_gen:\n# provider: \"deepinfra\"\n# deepinfra:\n# # Leave `model` blank for the first live `image-gen`-tagged result.\n# model: \"\"\n\n# =============================================================================\n# Response Pacing (Messaging Platforms)\n# =============================================================================\n# Add human-like delays between message chunks.\n# human_delay:\n# mode: \"off\" # \"off\" | \"natural\" | \"custom\"\n# min_ms: 800 # Min delay (custom mode only)\n# max_ms: 2500 # Max delay (custom mode only)\n\n# =============================================================================\n# Session Logging\n# =============================================================================\n# Session trajectories are automatically saved to logs/ directory.\n# Each session creates: logs/session_YYYYMMDD_HHMMSS_UUID.json\n#\n# The session ID is displayed in the welcome banner for easy reference.\n# Logs contain full conversation history in trajectory format:\n# - System prompt, user messages, assistant responses\n# - Tool calls with inputs/outputs\n# - Timestamps for debugging\n#\n# No configuration needed - logging is always enabled.\n# To disable, you would need to modify the source code.\n\n# =============================================================================\n# Code Execution Sandbox (Programmatic Tool Calling)\n# =============================================================================\n# The execute_code tool runs Python scripts that call Hermes tools via RPC.\n# Intermediate tool results stay out of the LLM's context window.\ncode_execution:\n timeout: 300 # Max seconds per script before kill (default: 300 = 5 min)\n max_tool_calls: 50 # Max RPC tool calls per execution (default: 50)\n\n# =============================================================================\n# Subagent Delegation\n# =============================================================================\n# The delegate_task tool spawns child agents with isolated context.\n# Supports single tasks and batch mode (default 3 parallel, configurable).\ndelegation:\n max_iterations: 50 # Max tool-calling turns per child (default: 50)\n # max_concurrent_children: 3 # Max parallel child agents per batch (default: 3, floor: 1, no ceiling).\n # WARNING: values above 10 multiply API cost linearly.\n # max_spawn_depth: 1 # Delegation tree depth cap (range: 1-3, default: 1 = flat).\n # Raise to 2 to allow workers to spawn their own subagents.\n # Requires role=\"orchestrator\" on intermediate agents.\n # orchestrator_enabled: true # Kill switch for role=\"orchestrator\" children (default: true).\n # subagent_auto_approve: false # When a subagent hits a dangerous-command approval prompt, auto-deny (default: false)\n # or auto-approve \"once\" (true) instead of blocking on stdin.\n # The parent TUI owns stdin, so blocking would deadlock; non-interactive resolution is required.\n # Both choices emit a logger.warning audit line. Flip to true only for cron/batch pipelines.\n # inherit_mcp_toolsets: true # When explicit child toolsets are narrowed, also keep the parent's MCP toolsets (default: true). Set false for strict intersection.\n # model: \"google/gemini-3-flash-preview\" # Override model for subagents (empty = inherit parent)\n # provider: \"openrouter\" # Override provider for subagents (empty = inherit parent)\n # # Resolves full credentials (base_url, api_key) automatically.\n # # Supported: openrouter, nous, zai, kimi-coding, minimax\n\n# =============================================================================\n# Honcho Integration (Cross-Session User Modeling)\n# =============================================================================\n# AI-native persistent memory via Honcho (https://honcho.dev/).\n# Builds a deeper understanding of the user across sessions and tools.\n# Runs alongside USER.md — additive, not a replacement.\n#\n# Requires: pip install honcho-ai\n# Config: ~/.honcho/config.json (shared with Claude Code, Cursor, etc.)\n# API key: HONCHO_API_KEY in ~/.hermes/.env or ~/.honcho/config.json\n#\n# Hermes-specific overrides (optional — most config comes from ~/.honcho/config.json):\n# honcho: {}\n\n# =============================================================================\n# Display\n# =============================================================================\ndisplay:\n # Use compact banner mode (hides the ASCII-art banner, shows a single line).\n # true: Compact single-line banner\n # false: Full ASCII banner with tool/skill summary (default)\n compact: false\n\n # Tool progress display level (CLI and gateway)\n # off: Silent — no tool activity shown, just the final response\n # new: Show a tool indicator only when the tool changes (skip repeats)\n # all: Show every tool call with a short preview (default)\n # verbose: Full args, results, and debug logs (same as /verbose)\n # log: Silent in chat; append every tool call to ~/.hermes/logs/tool_calls.log (gateway only)\n # Toggle at runtime with /verbose in the CLI\n tool_progress: all\n\n # Per-platform defaults can be quieter than the global setting. Telegram\n # tunes for mobile: tool_progress and busy_ack_detail default off (no\n # per-tool breadcrumb stream, no \"iteration 21/60\" debug detail in busy\n # acks or heartbeats), but interim_assistant_messages and\n # long_running_notifications STAY ON so the user has real signal between\n # turn start and final answer (mid-turn assistant commentary + a single\n # edit-in-place \"⏳ Working — N min\" heartbeat). Override under\n # display.platforms.telegram.\n\n # Auto-cleanup of temporary progress bubbles after the final response lands.\n # On platforms that support message deletion (currently Telegram), this\n # removes the tool-progress bubble, \"⏳ Still working...\" notices, and\n # context-pressure status messages once the final reply has been delivered —\n # keeping long-running turns visible live, then tidy afterward. Failed runs\n # leave the bubbles in place as breadcrumbs. Off by default.\n # Per-platform override: display.platforms.telegram.cleanup_progress\n # true: Delete tracked progress/status bubbles on successful turn\n # false: Leave everything in place (default)\n # Example:\n # display:\n # platforms:\n # telegram:\n # cleanup_progress: true\n cleanup_progress: false\n\n # Natural mid-turn assistant updates.\n # On gateway platforms, when true, completed assistant status messages are\n # sent as separate chat messages. On the Desktop app, when true, mid-turn\n # assistant narration streamed between tool calls is kept in the transcript\n # instead of the bubble collapsing to only the final message on completion.\n # Independent of tool_progress and gateway streaming.\n # true: Keep/send mid-turn assistant updates (default)\n # false: Only keep/send the final response\n interim_assistant_messages: true\n\n # Gateway-only long-running status heartbeats.\n # When false, the platform does not receive periodic \"⏳ Working — N min\"\n # notifications even if agent.gateway_notify_interval is non-zero. The\n # heartbeat edits a single message in place (where the adapter supports\n # editing) instead of posting a new bubble each interval.\n # Default: true everywhere, including Telegram (silent agents are worse\n # than a single edit-in-place heartbeat).\n long_running_notifications: true\n\n # Include detailed iteration/tool/status context in busy acknowledgments\n # and long-running heartbeats. When true, busy acks show \"iteration 21/60,\n # terminal, 10 min\" and the heartbeat shows \"⏳ Working — 12 min,\n # iteration 21/60, terminal\". When false (Telegram default), both stay\n # terse: \"Interrupting current task\" and \"⏳ Working — 12 min, terminal\".\n busy_ack_detail: true\n\n # What Enter does when Hermes is already busy (CLI and gateway platforms).\n # interrupt: Interrupt the current run and redirect Hermes (default)\n # queue: Queue your message for the next turn\n # steer: Inject your message mid-run via /steer, arriving at the agent\n # after the next tool call — no interrupt, no role violation.\n # Falls back to 'queue' if the agent isn't running yet or if\n # images are attached (steer only carries text).\n # Ctrl+C (or /stop in gateway) always interrupts regardless of this setting.\n # Toggle at runtime with /busy <interrupt|queue|steer>.\n busy_input_mode: interrupt\n\n # Background process notifications (gateway/messaging only).\n # Controls how chatty the process watcher is when you use\n # terminal(background=true, notify_on_complete=true) from Telegram/Discord/etc.\n # off: No watcher messages at all\n # result: Only the final completion message\n # error: Only the final message when exit code != 0\n # all: Running output updates + final message (default)\n background_process_notifications: all\n\n\n # Play terminal bell when agent finishes a response.\n # Useful for long-running tasks — your terminal will ding when the agent is done.\n # Works over SSH. Most terminals can be configured to flash the taskbar or play a sound.\n # true: Ring the terminal bell on each response\n # false: Silent (default)\n bell_on_complete: false\n\n # Show model reasoning/thinking before each response.\n # When enabled, a dim box shows the model's thought process above the response.\n # Toggle at runtime with /reasoning show or /reasoning hide.\n # true: Show the reasoning box\n # false: Hide reasoning (default)\n show_reasoning: false\n\n # Stream tokens to the terminal as they arrive instead of waiting for the\n # full response. The response box opens on first token and text appears\n # line-by-line. Tool calls are still captured silently.\n # true: Stream tokens as they arrive (default)\n # false: Wait for the full response before rendering\n streaming: true\n\n # Show [HH:MM] timestamps on user input and assistant response labels.\n # timestamps: false\n\n # ───────────────────────────────────────────────────────────────────────────\n # Skin / Theme\n # ───────────────────────────────────────────────────────────────────────────\n # Customize CLI visual appearance — banner colors, spinner faces, tool prefix,\n # response box label, and branding text. Change at runtime with /skin <name>.\n #\n # Built-in skins:\n # default — Classic Hermes gold/kawaii\n # ares — Crimson/bronze war-god theme with spinner wings\n # mono — Clean grayscale monochrome\n # slate — Cool blue developer-focused\n # daylight — Bright light-mode theme\n # warm-lightmode — Warm paper-tone light-mode theme\n # poseidon — Sea-green/teal Olympian theme\n # sisyphus — Earthy stone-and-moss theme\n # charizard — Fiery orange dragon theme\n #\n # Custom skins: drop a YAML file in ~/.hermes/skins/<name>.yaml\n # Schema (all fields optional, missing values inherit from default):\n #\n # name: my-theme\n # description: Short description\n # colors:\n # banner_border: \"#HEX\" # Panel border\n # banner_title: \"#HEX\" # Panel title\n # banner_accent: \"#HEX\" # Section headers (Available Tools, etc.)\n # banner_dim: \"#HEX\" # Dim/muted text\n # banner_text: \"#HEX\" # Body text (tool names, skill names)\n # ui_accent: \"#HEX\" # UI accent color\n # response_border: \"#HEX\" # Response box border color\n # spinner:\n # waiting_faces: [\"(⚔)\", \"(⛨)\"] # Faces shown while waiting\n # thinking_faces: [\"(⚔)\", \"(⌁)\"] # Faces shown while thinking\n # thinking_verbs: [\"forging\", \"plotting\"] # Verbs for spinner messages\n # wings: # Optional left/right spinner decorations\n # - [\"⟪⚔\", \"⚔⟫\"]\n # - [\"⟪▲\", \"▲⟫\"]\n # branding:\n # agent_name: \"My Agent\" # Banner title and branding\n # welcome: \"Welcome message\" # Shown at CLI startup\n # response_label: \" ⚔ Agent \" # Response box header label\n # prompt_symbol: \"⚔\" # Prompt symbol (bare token; renderers add trailing space)\n # tool_prefix: \"╎\" # Tool output line prefix (default: ┊)\n #\n skin: default\n\n# =============================================================================\n# Model Aliases — short names for /model command\n# =============================================================================\n# Map short aliases to exact (model, provider, base_url) tuples.\n# Used by /model tab completion and resolve_alias().\n# Aliases are checked BEFORE the models.dev catalog, so they can route\n# to endpoints not in the catalog (e.g. Ollama Cloud, local servers).\n#\n# model_aliases:\n# opus:\n# model: claude-opus-4-6\n# provider: anthropic\n# qwen:\n# model: \"qwen3.5:397b\"\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n# glm:\n# model: glm-4.7\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n\n# =============================================================================\n# Privacy\n# =============================================================================\n# privacy:\n# # Redact PII from the LLM context prompt.\n# # When true, phone numbers are stripped and user/chat IDs are replaced\n# # with deterministic hashes before being sent to the model.\n# # Names and usernames are NOT affected (user-chosen, publicly visible).\n# # Routing/delivery still uses the original values internally.\n# redact_pii: false\n\n# =============================================================================\n# Shell-script hooks\n# =============================================================================\n# Register shell scripts as plugin-hook callbacks. Each entry is executed as\n# a subprocess (shell=False, shlex.split) with a JSON payload on stdin. On\n# stdout the script may return JSON that either blocks the tool call or\n# injects context into the next LLM call.\n#\n# Valid events (mirror hermes_cli.plugins.VALID_HOOKS):\n# pre_tool_call, post_tool_call, pre_llm_call, post_llm_call,\n# pre_api_request, post_api_request, on_session_start, on_session_end,\n# on_session_finalize, on_session_reset, subagent_stop\n#\n# First-use consent: each (event, command) pair prompts once on a TTY, then\n# is persisted to ~/.hermes/shell-hooks-allowlist.json. Non-interactive\n# runs (gateway, cron) need --accept-hooks, HERMES_ACCEPT_HOOKS=1, or the\n# hooks_auto_accept key below.\n#\n# See website/docs/user-guide/features/hooks.md for the full JSON wire\n# protocol and worked examples.\n#\n# hooks:\n# pre_tool_call:\n# - matcher: \"terminal\"\n# command: \"~/.hermes/agent-hooks/block-rm-rf.sh\"\n# timeout: 10\n# post_tool_call:\n# - matcher: \"write_file|patch\"\n# command: \"~/.hermes/agent-hooks/auto-format.sh\"\n# pre_llm_call:\n# - command: \"~/.hermes/agent-hooks/inject-cwd-context.sh\"\n# subagent_stop:\n# - command: \"~/.hermes/agent-hooks/log-orchestration.sh\"\n#\n# hooks_auto_accept: false\n\n\n# =============================================================================\n# Update Behavior\n# =============================================================================\nupdates:\n # Create a full HERMES_HOME zip before every `hermes update`.\n # Backups land in ~/.hermes/backups/ and can be restored with `hermes import`.\n # Off by default because large homes can add minutes to every update.\n pre_update_backup: false\n\n # Number of pre-update backup zips to retain.\n backup_keep: 5\n\n # What non-interactive updates do with local source edits in the Hermes repo.\n # Interactive terminal updates always prompt before restoring the autostash.\n #\n # stash - auto-stash before pull, then auto-restore after success (default)\n # discard - drop the update-created stash after success; use only on managed\n # installs where local source edits should not persist\n non_interactive_local_changes: \"stash\"\n\n\n# =============================================================================\n# Web Dashboard\n# =============================================================================\n# OAuth gate configuration for `hermes dashboard --host <non-loopback>`.\n# The bundled Nous Portal plugin reads these on startup; settings here are\n# the canonical surface. Each can be overridden by an environment variable:\n#\n# dashboard.oauth.client_id <- HERMES_DASHBOARD_OAUTH_CLIENT_ID\n# dashboard.oauth.portal_url <- HERMES_DASHBOARD_PORTAL_URL\n# dashboard.public_url <- HERMES_DASHBOARD_PUBLIC_URL\n#\n# Env wins when set to a non-empty value. This is what Fly.io's platform-\n# secret injection uses to push per-deploy client_ids without needing to\n# bake a config.yaml into the image. Empty env values are treated as unset\n# so a provisioned-but-not-populated secret can't shadow a valid entry here.\n#\n# Local dev / on-prem deploys should typically set these via config.yaml\n# (the ~/.hermes/.env file is reserved for API keys and secrets).\n#\n# dashboard:\n# oauth:\n# client_id: \"\" # agent:{instance_id}; Portal provisions this at deploy\n# portal_url: \"\" # blank → default https://portal.nousresearch.com\n#\n# # Force the absolute base URL the OAuth callback (and any other public\n# # URL the dashboard hands to external systems) is built from. Set this\n# # for deploys behind reverse proxies that don't reliably forward\n# # X-Forwarded-Host / X-Forwarded-Proto / X-Forwarded-Prefix (manual\n# # nginx setups, on-prem ingresses, custom-domain Fly deploys without\n# # full proxy header chains).\n# #\n# # When set, the value is the complete authority: scheme + host +\n# # optional path prefix (e.g. \"https://example.com/hermes\"). The OAuth\n# # callback URL becomes \"<public_url>/auth/callback\" — X-Forwarded-Prefix\n# # is IGNORED on this code path because the operator has explicitly\n# # declared the public URL and we no longer need to guess.\n# #\n# # Leave empty to use the existing proxy-header reconstruction (the\n# # default — works on Fly.io out of the box).\n# #\n# # public_url: \"https://example.com/hermes\"\n#\n# -----------------------------------------------------------------------------\n# Self-hosted OIDC dashboard auth (generic OpenID Connect — Authentik,\n# Keycloak, Zitadel, Authelia, Auth0, Okta, Google, …). Use this INSTEAD of the\n# nous block above when gating the dashboard with your own identity provider.\n# Each setting can be overridden by an environment variable:\n#\n# dashboard.oauth.self_hosted.issuer <- HERMES_DASHBOARD_OIDC_ISSUER\n# dashboard.oauth.self_hosted.client_id <- HERMES_DASHBOARD_OIDC_CLIENT_ID\n# dashboard.oauth.self_hosted.scopes <- HERMES_DASHBOARD_OIDC_SCOPES\n# dashboard.oauth.self_hosted.client_secret <- HERMES_DASHBOARD_OIDC_CLIENT_SECRET\n#\n# dashboard:\n# oauth:\n# provider: self-hosted\n# self_hosted:\n# issuer: \"https://auth.example.com/application/o/hermes/\" # required\n# client_id: \"hermes-dashboard\" # required\n# scopes: \"openid profile email\" # optional\n#\n# # OPTIONAL — set ONLY if your IDP registered the client as\n# # *confidential* (Authentik / Keycloak often default to this). When\n# # set, Hermes authenticates the client at the token endpoint\n# # (client_secret_basic or client_secret_post, auto-selected from the\n# # IDP's discovery doc) IN ADDITION to PKCE. Leave unset for a public\n# # (PKCE-only) client — the common case.\n# #\n# # This is a CREDENTIAL: prefer setting HERMES_DASHBOARD_OIDC_CLIENT_SECRET\n# # in ~/.hermes/.env over putting it here in config.yaml.\n# # client_secret: \"\"\n\n# =============================================================================\n# External secret sources\n# =============================================================================\n# Pull provider credentials from external secret managers at process startup\n# instead of storing them in ~/.hermes/.env. Only the manager's bootstrap\n# credential (e.g. BWS_ACCESS_TOKEN / OP_SERVICE_ACCOUNT_TOKEN) lives in .env\n# (or your shell / desktop session); everything else rotates centrally.\n# Failures never block startup — Hermes warns once and continues with\n# whatever .env already had.\n#\n# Multiple sources can be enabled at once:\n# - \"mapped\" sources (explicit VAR -> ref bindings, e.g. 1Password's env:\n# map) beat \"bulk\" sources (whole-project dumps like Bitwarden BSM)\n# - within a shape, the first source to claim a var wins; later claims\n# are skipped with a startup warning (never a silent clobber)\n# - a source's override_existing lets it beat .env/shell values, but\n# never another secret source's claim\n# Docs: https://hermes-agent.nousresearch.com/docs/user-guide/secrets/\n#\n# secrets:\n# # Optional explicit ordering of enabled sources.\n# # sources: [onepassword, bitwarden]\n#\n# # ---- Bitwarden Secrets Manager (bws CLI) --------------------------------\n# bitwarden:\n# enabled: false\n# access_token_env: BWS_ACCESS_TOKEN # bootstrap token, sourced from .env\n# project_id: \"\" # UUID of the BSM project to sync\n# server_url: \"\" # \"\" = US Cloud; EU/self-hosted URL otherwise\n# cache_ttl_seconds: 300 # 0 disables fresh caching\n# encrypted_cache: # optional encrypted stale fallback\n# enabled: false\n# max_stale_seconds: 0 # 0 disables stale fallback\n# override_existing: true # BSM values win over existing env\n# auto_install: true # lazy-download bws into ~/.hermes/bin\n#\n# # ---- 1Password (op CLI) -------------------------------------------------\n# onepassword:\n# enabled: false\n# # Map env-var names to op:// secret references. Each is resolved with a\n# # single `op read` at startup.\n# env:\n# OPENAI_API_KEY: \"op://Private/OpenAI/api key\"\n# ANTHROPIC_API_KEY: \"op://Private/Anthropic/credential\"\n# account: \"\" # op --account shorthand; \"\" = default\n# service_account_token_env: OP_SERVICE_ACCOUNT_TOKEN # headless auth; unset = desktop session\n# binary_path: \"\" # \"\" = resolve op via PATH; else absolute path\n# cache_ttl_seconds: 300 # 0 disables BOTH cache layers\n# override_existing: true # resolved values win over existing env\n#\n# # ---- Command helper (any CLI vault) --------------------------------------\n# # Run a user-configured helper that prints KEY=VALUE lines on stdout —\n# # works with any secret store that has a CLI: keepassxc-cli, secret-tool,\n# # pass, gpg, or a script that cats a tmpfs env file. Composes with the\n# # sources above (enable any combination). POSIX-only (needs /bin/sh).\n# # The helper must be fast and NON-interactive (hard timeout, 1 MiB cap);\n# # its stderr is discarded so diagnostics can't leak secret material.\n# command:\n# enabled: false\n# command: \"cat /run/user/1000/hermes-secrets.env\"\n# helper_timeout_seconds: 3\n# override_existing: false # .env/shell win by default\n"}, "4f0ee4d3ff1d8edeeab4b78e7c983a195d5b8ddd": {"t": 1785768037.6204524, "n": 78018, "h": "994bded0d05d491d", "c": "# Hermes Agent CLI Configuration\n# Copy this file to cli-config.yaml and customize as needed.\n# This file configures the CLI behavior. Environment variables in .env take precedence.\n\n# =============================================================================\n# Model Configuration\n# =============================================================================\nmodel:\n # Default model to use (can be overridden with --model flag)\n # Both \"default\" and \"model\" work as the key name here.\n default: \"anthropic/claude-opus-4.6\"\n \n # Inference provider selection:\n # \"auto\" - Auto-detect from credentials (default)\n # \"openrouter\" - OpenRouter (requires: OPENROUTER_API_KEY or OPENAI_API_KEY)\n # \"nous\" - Nous Portal OAuth (requires: hermes auth add nous)\n # \"nous-api\" - Nous Portal API key (requires: NOUS_API_KEY)\n # \"anthropic\" - Direct Anthropic API (requires: ANTHROPIC_API_KEY)\n # \"openai-codex\" - OpenAI Codex (requires: hermes auth)\n # \"copilot\" - GitHub Copilot / GitHub Models (requires: GITHUB_TOKEN)\n # \"gemini\" - Use Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n # \"zai\" - Use z.ai / ZhipuAI GLM models (requires: GLM_API_KEY)\n # \"kimi-coding\" - Kimi / Moonshot AI (requires: KIMI_API_KEY)\n # \"minimax\" - MiniMax global (requires: MINIMAX_API_KEY)\n # \"minimax-cn\" - MiniMax China (requires: MINIMAX_CN_API_KEY)\n # \"huggingface\" - Hugging Face Inference (requires: HF_TOKEN)\n # \"nvidia\" - NVIDIA NIM / build.nvidia.com (requires: NVIDIA_API_KEY)\n # \"xiaomi\" - Xiaomi MiMo (requires: XIAOMI_API_KEY)\n # \"arcee\" - Arcee AI Trinity models (requires: ARCEEAI_API_KEY)\n # \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY — https://ollama.com/settings)\n # \"deepinfra\" - DeepInfra (requires: DEEPINFRA_API_KEY)\n # \"kilocode\" - KiloCode gateway (requires: KILOCODE_API_KEY)\n # \"azure-foundry\" - Microsoft Foundry / Azure OpenAI (API key or Entra ID)\n # \"lmstudio\" - LM Studio local server (optional: LM_API_KEY, defaults to http://127.0.0.1:1234/v1)\n #\n # Local servers (LM Studio, Ollama, vLLM, llama.cpp):\n # \"custom\" - Any other OpenAI-compatible endpoint. Set base_url below.\n # Aliases: \"ollama\", \"vllm\", \"llamacpp\" all map to \"custom\".\n # LM Studio is first-class and uses provider: \"lmstudio\".\n # It works with both no-auth and auth-enabled server modes.\n #\n # Can also be overridden for a single invocation with the --provider flag.\n provider: \"auto\"\n \n # API configuration (falls back to OPENROUTER_API_KEY env var)\n # api_key: \"your-key-here\" # Uncomment to set here instead of .env\n base_url: \"https://openrouter.ai/api/v1\"\n\n # Azure Foundry keyless auth example:\n # provider: \"azure-foundry\"\n # base_url: \"https://<resource>.openai.azure.com/openai/v1\"\n # auth_mode: \"entra_id\" # DefaultAzureCredential: az login, managed identity, workload identity, etc.\n # default: \"gpt-4o\" # Deployment/model name\n # entra:\n # scope: \"https://ai.azure.com/.default\" # Optional; this is the default.\n\n # ── Token limits — two settings, easy to confuse ──────────────────────────\n #\n # context_length: TOTAL context window (input + output tokens combined).\n # Controls when Hermes compresses history and validates requests.\n # Leave unset — Hermes auto-detects the correct value from the provider.\n # Set manually only when auto-detection is wrong (e.g. a local server with\n # a custom num_ctx, or a proxy that doesn't expose /v1/models).\n #\n # context_length: 131072\n #\n # max_tokens: OUTPUT cap — maximum tokens the model may generate per response.\n # Unrelated to how long your conversation history can be.\n # The OpenAI-standard name \"max_tokens\" is a misnomer; Anthropic's native\n # API has since renamed it \"max_output_tokens\" for clarity.\n # Leave unset to use the model's native output ceiling (recommended).\n # Set only if you want to deliberately limit individual response length.\n #\n# max_tokens: 8192\n\n # ── Custom request headers (optional) ─────────────────────────────────────\n #\n # default_headers: extra HTTP headers sent on every request to an\n # OpenAI-compatible endpoint. User values take precedence over the\n # provider/SDK defaults, so this is the supported way to override the\n # OpenAI Python SDK's identifying headers (User-Agent: OpenAI/Python ...,\n # X-Stainless-*) when a custom provider sits behind a gateway/WAF that\n # rejects them — e.g. an upstream that returns \"502 Upstream access\n # forbidden\" for the SDK default User-Agent but accepts a plain one.\n # Applies on the OpenAI wire only (not native Anthropic / Bedrock).\n #\n # default_headers:\n # User-Agent: \"curl/8.7.1\"\n #\n # extra_headers: accepted as an alias of default_headers (merged, with\n # extra_headers winning when both are set) — matches the per-provider\n # extra_headers key below.\n #\n # Per-provider variant: named providers / custom_providers entries accept an\n # extra_headers dict scoped to that endpoint only — for reverse proxies,\n # gateways, or custom auth (e.g. Cloudflare Access service tokens).\n # Merged onto SDK/provider defaults with the entry's values winning.\n # Header values are treated as secrets and are never logged.\n #\n # providers:\n # my-proxy:\n # base_url: \"https://llm.internal.example.com/v1\"\n # key_env: \"MY_PROXY_API_KEY\"\n # extra_headers:\n # CF-Access-Client-Id: \"xxxx.access\"\n # CF-Access-Client-Secret: \"${CF_ACCESS_SECRET}\"\n # X-Client-Name: \"hermes-agent\"\n\n# Named provider overrides (optional)\n# Use this for per-provider request timeouts, non-stream stale timeouts,\n# and per-model exceptions.\n# Applies to the primary turn client on every api_mode (OpenAI-wire, native\n# Anthropic, and Anthropic-compatible providers), the fallback chain, and\n# client rebuilds during credential rotation. For OpenAI-wire chat\n# completions (streaming and non-streaming) the configured value is also\n# used as the per-request ``timeout=`` kwarg so it wins over the legacy\n# HERMES_API_TIMEOUT env var (which still applies when no config is set).\n# ``stale_timeout_seconds`` controls the non-streaming stale-call detector and\n# wins over the legacy HERMES_API_CALL_STALE_TIMEOUT env var. Leaving these\n# unset keeps the legacy defaults (HERMES_API_TIMEOUT=1800s,\n# HERMES_API_CALL_STALE_TIMEOUT=90s, native Anthropic 900s). The\n# implicit non-stream stale detector is auto-disabled for local endpoints\n# and can scale upward for very large contexts.\n#\n# Not currently wired for AWS Bedrock (bedrock_converse + AnthropicBedrock\n# SDK paths) — those use boto3 with its own timeout configuration.\n#\n# providers:\n# ollama-local:\n# request_timeout_seconds: 300 # Longer timeout for local cold-starts\n# stale_timeout_seconds: 900 # Explicitly re-enable stale detection on local endpoints\n# anthropic:\n# request_timeout_seconds: 30 # Fast-fail cloud requests\n# models:\n# claude-opus-4.6:\n# timeout_seconds: 600 # Longer timeout for extended-thinking Opus calls\n# openai-codex:\n# models:\n# gpt-5.4:\n# stale_timeout_seconds: 1800 # Longer non-stream stale timeout for slow large-context turns\n\n# =============================================================================\n# OpenRouter Provider Routing (only applies when using OpenRouter)\n# =============================================================================\n# Control how requests are routed across providers on OpenRouter.\n# See: https://openrouter.ai/docs/guides/routing/provider-selection\n#\n# provider_routing:\n# # Sort strategy: \"price\" (default), \"throughput\", or \"latency\"\n# # Append :nitro to model name for a shortcut to throughput sorting.\n# sort: \"throughput\"\n#\n# # Only allow these providers (provider slugs from OpenRouter)\n# # only: [\"anthropic\", \"google\"]\n#\n# # Skip these providers entirely\n# # ignore: [\"deepinfra\", \"fireworks\"]\n#\n# # Try providers in this order (overrides default load balancing)\n# # order: [\"anthropic\", \"google\", \"together\"]\n#\n# # Require providers to support all parameters in your request\n# # require_parameters: true\n#\n# # Data policy: \"allow\" (default) or \"deny\" to exclude providers that may store data\n# # data_collection: \"deny\"\n\n# =============================================================================\n# OpenRouter Response Caching (only applies when using OpenRouter)\n# =============================================================================\n# Cache identical API responses at the OpenRouter edge for free instant replays.\n# When enabled, identical requests (same model, messages, parameters) return\n# cached responses with zero billing. Separate from Anthropic prompt caching.\n# See: https://openrouter.ai/docs/guides/features/response-caching\n#\n# openrouter:\n# response_cache: true # Enable response caching (default: true)\n# response_cache_ttl: 300 # Cache TTL in seconds, 1-86400 (default: 300)\n\n# =============================================================================\n# Git Worktree Isolation\n# =============================================================================\n# When enabled, each CLI session creates an isolated git worktree so multiple\n# agents can work on the same repo concurrently without file collisions.\n# Equivalent to always passing --worktree / -w on the command line.\n#\n# worktree: true # Always create a worktree when in a git repo\n# worktree: false # Default — only create when -w flag is passed\n#\n# By default a new worktree branches from the freshly-fetched remote tip\n# (the current branch's upstream, else the remote's default branch) so it\n# starts current with the project instead of from the local clone's\n# (possibly stale) HEAD. Set worktree_sync: false to branch from local HEAD\n# instead — useful when offline or when you deliberately want the clone's\n# exact current state as the base.\n#\n# worktree_sync: true # Default — branch from the fetched remote tip\n# worktree_sync: false # Branch from local HEAD (offline / pinned base)\n\n# =============================================================================\n# Terminal Tool Configuration\n# =============================================================================\n# Choose ONE of the following terminal configurations by uncommenting it.\n# The terminal tool executes commands in the specified environment.\n\n# -----------------------------------------------------------------------------\n# OPTION 1: Local execution (default)\n# Commands run directly on your machine in the current directory\n# -----------------------------------------------------------------------------\n# Working directory behavior:\n# - CLI (`hermes` command): Uses \".\" (current directory where you run hermes)\n# - Gateway/messaging/cron: Uses terminal.cwd here; legacy .env cwd values are deprecated\nterminal:\n backend: \"local\"\n cwd: \".\" # For local backend: \".\" = current directory. Ignored for remote backends unless a backend documents otherwise.\n timeout: 180\n # HOME policy for tool subprocesses:\n # auto - default: host uses your real HOME; containers use HERMES_HOME/home\n # real - force your real OS-user HOME\n # profile - force HERMES_HOME/home for strict per-profile CLI config isolation\n home_mode: \"auto\"\n docker_mount_cwd_to_workspace: false # SECURITY: off by default. Opt in to mount the launch cwd into Docker /workspace.\n lifetime_seconds: 300\n # sudo_password: \"hunter2\" # Optional: pipe a sudo password via sudo -S. SECURITY WARNING: plaintext.\n # sudo_password: \"\" # Explicit empty password: try empty and never open the interactive sudo prompt.\n\n# -----------------------------------------------------------------------------\n# OPTION 2: SSH remote execution\n# Commands run on a remote server - agent code stays local (sandboxed)\n# Great for: keeping agent isolated from its own code, using powerful remote hardware\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"ssh\"\n# cwd: \"/home/myuser/project\" # Path on the REMOTE server\n# timeout: 180\n# lifetime_seconds: 300\n# ssh_host: \"my-server.example.com\"\n# ssh_user: \"myuser\"\n# ssh_port: 22\n# ssh_key: \"~/.ssh/id_rsa\" # Optional - uses ssh-agent if not specified\n\n# -----------------------------------------------------------------------------\n# OPTION 3: Docker container\n# Commands run in an isolated Docker container\n# Great for: reproducible environments, testing, isolation\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"docker\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /)\n# timeout: 180\n# lifetime_seconds: 300\n# docker_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# docker_mount_cwd_to_workspace: true # Explicit opt-in: mount your launch cwd into /workspace\n# # Optional: run the container as your host user's uid:gid so files written\n# # into bind-mounted dirs are owned by you, not root. Drops SETUID/SETGID\n# # caps too since no gosu privilege drop is needed. Leave off if your\n# # chosen docker_image expects to start as root.\n# docker_run_as_host_user: true\n# # Optional: explicitly forward selected env vars into Docker.\n# # These values come from your current shell first, then ~/.hermes/.env.\n# # Warning: anything forwarded here is visible to commands run in the container.\n# docker_forward_env:\n# - \"GITHUB_TOKEN\"\n# - \"NPM_TOKEN\"\n# # Optional: extra flags passed verbatim to docker run (appended after security defaults).\n# # Useful for adding capabilities (e.g. apt installs needing SETUID) or custom options.\n# # Example: add a Linux capability not included by default\n# # docker_extra_args:\n# # - \"--cap-add\"\n# # - \"SETUID\"\n\n# -----------------------------------------------------------------------------\n# OPTION 4: Singularity/Apptainer container\n# Commands run in a Singularity container (common in HPC environments)\n# Great for: HPC clusters, shared compute environments\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"singularity\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# singularity_image: \"docker://nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 5: Modal cloud execution\n# Commands run on Modal's cloud infrastructure\n# Great for: GPU access, scalable compute, serverless execution\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"modal\"\n# cwd: \"/workspace\" # Path INSIDE the sandbox (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# modal_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 6: Daytona cloud execution\n# Commands run in Daytona cloud sandboxes\n# Great for: Cloud dev environments, persistent workspaces, team collaboration\n# Requires: pip install daytona, DAYTONA_API_KEY env var\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"daytona\"\n# cwd: \"~\"\n# timeout: 180\n# lifetime_seconds: 300\n# daytona_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# container_disk: 10240 # Daytona max is 10GB per sandbox\n\n#\n# --- Container resource limits (docker, singularity, modal, daytona -- ignored for local/ssh) ---\n# These settings apply to all container backends. They control the resources\n# allocated to the sandbox and whether its filesystem persists across sessions.\n container_cpu: 1 # CPU cores\n container_memory: 5120 # Memory in MB (5120 = 5GB)\n container_disk: 51200 # Disk in MB (51200 = 50GB)\n container_persistent: true # Persist filesystem across sessions (false = ephemeral)\n\n# -----------------------------------------------------------------------------\n# SUDO SUPPORT (works with ALL backends above)\n# -----------------------------------------------------------------------------\n# Add sudo_password to any terminal config above to enable sudo commands.\n# The password is piped via `sudo -S`. Works with local, ssh, docker, etc.\n#\n# SECURITY WARNING: Password stored in plaintext!\n#\n# INTERACTIVE PROMPT: If sudo_password is unset and the CLI is running,\n# you'll be prompted to enter your password when sudo is needed:\n# - 45-second timeout (auto-skips if no input)\n# - Press Enter to skip (command fails gracefully)\n# - Password is hidden while typing\n# - Password is cached for the session\n#\n# EMPTY PASSWORDS: Setting sudo_password to an explicit empty string is different\n# from leaving it unset. Hermes will try an empty password via `sudo -S` and\n# will not open the interactive prompt. This is useful for passwordless sudo,\n# Touch ID sudo setups, and environments where prompting is just noise.\n#\n# ALTERNATIVES:\n# - SSH backend: Configure passwordless sudo on the remote server\n# - Containers: Run as root inside the container (no sudo needed)\n# - Local: Configure /etc/sudoers for specific commands\n#\n# Example (add to your terminal section):\n# sudo_password: \"your-password-here\"\n\n# =============================================================================\n# Security Scanning (tirith)\n# =============================================================================\n# Optional pre-exec command security scanning via tirith.\n# Detects homograph URLs, pipe-to-shell, terminal injection, env manipulation.\n# Install: brew install sheeki03/tap/tirith\n# Docs: https://github.com/sheeki03/tirith\n#\n# security:\n# tirith_enabled: true # Enable/disable tirith scanning\n# tirith_path: \"tirith\" # Path to tirith binary (supports ~ expansion)\n# tirith_timeout: 5 # Scan timeout in seconds\n# tirith_fail_open: true # Allow commands if tirith unavailable\n\n# =============================================================================\n# Browser Tool Configuration\n# =============================================================================\nbrowser:\n # Inactivity timeout in seconds - browser sessions are automatically closed\n # after this period of no activity between agent loops (default: 120 = 2 minutes)\n inactivity_timeout: 120\n\n# =============================================================================\n# Tool Loop Guardrails\n# =============================================================================\n# Soft warnings are enabled by default. They append guidance to repeated failed\n# or non-progressing tool results but still let the tool execute. Hard stops are\n# opt-in circuit breakers for autonomous/cron sessions where stopping a loop is\n# preferable to spending the full iteration budget.\ntool_loop_guardrails:\n warnings_enabled: true\n hard_stop_enabled: false\n warn_after:\n exact_failure: 2\n same_tool_failure: 3\n idempotent_no_progress: 2\n hard_stop_after:\n exact_failure: 5\n same_tool_failure: 8\n idempotent_no_progress: 5\n\n# =============================================================================\n# Context Compression (Auto-shrinks long conversations)\n# =============================================================================\n# When conversation approaches model's context limit, middle turns are\n# automatically summarized to free up space while preserving important context.\n#\n# HOW IT WORKS:\n# 1. Tracks actual token usage from API responses (not estimates)\n# 2. When prompt_tokens >= threshold% of model's context_length, triggers compression\n# 3. Protects first 3 turns (system prompt, initial request, first response)\n# 4. Protects last N turns (default 20 messages = ~10 full turns of recent context)\n# 5. Summarizes middle turns using a fast/cheap model\n# 6. Inserts summary as a user message, continues conversation seamlessly\n#\n# Post-compression tail budget is target_ratio × threshold × context_length:\n# 200K context, threshold 0.50, ratio 0.20 → 20K tokens of recent tail preserved\n# 1M context, threshold 0.50, ratio 0.20 → 100K tokens of recent tail preserved\n#\ncompression:\n # Enable automatic context compression (default: true)\n # Set to false if you prefer to manage context manually or want errors on overflow\n enabled: true\n \n # Trigger compression at this % of model's context limit (default: 0.50 = 50%)\n # Lower values = more aggressive compression, higher values = compress later\n # Models with context windows below 512K are floored at 0.75 (raise-only) so\n # compaction doesn't fire with half the window still free; set above 0.75 to override.\n threshold: 0.50\n\n # Existing Codex gpt-5.5 behavior: raise Hermes' compaction trigger to 85%\n # for the ChatGPT Codex OAuth route. Set false to opt back down to threshold.\n codex_gpt55_autoraise: true\n\n # Fraction of the threshold to preserve as recent tail (default: 0.20 = 20%)\n # e.g. 20% of 50% threshold = 10% of total context kept as recent messages.\n # Summary output is separately capped at 12K tokens (Gemini output limit).\n # Range: 0.10 - 0.80\n target_ratio: 0.20\n\n # Number of most-recent messages to always preserve (default: 20 ≈ 10 full turns)\n # Higher values keep more recent conversation intact at the cost of more aggressive\n # compression of older turns.\n protect_last_n: 20\n\n # Codex app-server (codex CLI runtime) thread-compaction mode. The codex\n # agent owns the real thread context on this runtime, so Hermes' summarizer\n # cannot shrink it — compaction goes through the app server instead.\n # native = let Codex decide when to compact its own thread (default)\n # hermes = let Hermes threshold trigger Codex thread/compact/start\n # off = Hermes will not auto-trigger compaction; Codex may still compact natively\n codex_app_server_auto: native\n\n # Number of non-system messages to protect at the head of the transcript, in\n # ADDITION to the system prompt (which is always implicitly protected).\n # Head messages are NEVER summarized — they survive every compression\n # indefinitely. This gives stable early context for short/medium sessions,\n # but in long-running sessions that rely on rolling compaction the pinned\n # opening turns may not match how you want the session framed over time.\n # Set to 0 to preserve ONLY the system prompt (plus the rolling summary\n # and recent tail) — the cleanest configuration for long-running sessions.\n # Default 3 preserves the system prompt plus the first three non-system\n # head messages, matching the pre-feature behaviour.\n protect_first_n: 3\n\n # To pin a specific model/provider for compression summaries, use the\n # auxiliary section below (auxiliary.compression.provider / model).\n\n# =============================================================================\n# Anthropic prompt caching TTL\n# =============================================================================\n# When prompt caching is active (Claude via OpenRouter or native Anthropic),\n# Anthropic supports two TTL tiers for cached prefixes: \"5m\" (default) and\n# \"1h\". Other values are ignored and \"5m\" is used.\n#\nprompt_caching:\n cache_ttl: \"5m\" # use \"1h\" for long sessions with pauses between turns\n\n# =============================================================================\n# Auxiliary Models (Advanced — Experimental)\n# =============================================================================\n# Hermes uses lightweight \"auxiliary\" models for side tasks: image analysis,\n# browser screenshot analysis, web page summarization, TTS audio-tag insertion,\n# and context compression.\n#\n# By default these use Gemini Flash via OpenRouter or Nous Portal and are\n# auto-detected from your credentials. You do NOT need to change anything\n# here for normal usage.\n#\n# WARNING: Overriding these with providers other than OpenRouter or Nous Portal\n# is EXPERIMENTAL and may not work. Not all models/providers support vision,\n# produce usable summaries, or accept the same API format. Change at your own\n# risk — if things break, reset to \"auto\" / empty values.\n#\n# Each task has its own provider + model pair so you can mix providers.\n# For example: OpenRouter for vision (needs multimodal), but your main\n# local endpoint for compression (just needs text).\n#\n# Provider options:\n# \"auto\" - Best available: OpenRouter → Nous Portal → main endpoint (default)\n# \"openrouter\" - Force OpenRouter (requires OPENROUTER_API_KEY)\n# \"nous\" - Force Nous Portal (requires: hermes auth add nous)\n# \"gemini\" - Force Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n# \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY)\n# \"codex\" - Force Codex OAuth (requires: hermes model → Codex).\n# Uses gpt-5.3-codex which supports vision.\n# \"main\" - Use your custom endpoint (OPENAI_BASE_URL + OPENAI_API_KEY).\n# Works with OpenAI API, local models, or any OpenAI-compatible\n# endpoint. Also falls back to Codex OAuth and API-key providers.\n#\n# Model: leave empty to use the provider's default. When empty, OpenRouter\n# uses \"google/gemini-3-flash-preview\" and Nous uses \"gemini-3-flash\".\n# Other providers pick a sensible default automatically.\n#\n# auxiliary:\n# # Image analysis: vision_analyze tool + browser screenshots\n# vision:\n# provider: \"auto\"\n# model: \"\" # e.g. \"google/gemini-2.5-flash\", \"openai/gpt-4o\"\n# timeout: 30 # LLM API call timeout (seconds)\n# download_timeout: 30 # Image HTTP download timeout (seconds)\n# # Increase for slow connections or self-hosted image servers\n# reasoning_effort: \"\" # Per-task thinking level: none, minimal, low, medium,\n# # high, xhigh, max, ultra. Empty = provider default.\n# # Works on every auxiliary task block (vision,\n# # web_extract, compression, title_generation, curator,\n# # background_review, moa_reference, ...). Example: run\n# # compression at \"low\" and vision at \"none\" to cut\n# # side-task latency/cost on reasoning models.\n#\n# # Web page scraping / summarization + browser page text extraction\n# web_extract:\n# provider: \"auto\"\n# model: \"\"\n# reasoning_effort: \"low\"\n#\n# # Gemini 3.1 TTS hidden audio-tag insertion\n# tts_audio_tags:\n# provider: \"auto\" # empty model = your main chat model\n# model: \"\"\n# timeout: 30\n#\n# # Automatic session title generation after the first exchange\n# title_generation:\n# enabled: true # set false to disable auto-title generation\n# provider: \"auto\"\n# model: \"\"\n# timeout: 30\n# language: \"\" # empty = match the user's language; or e.g. \"English\"\n#\n# # Session search — summarizes matching past sessions\n# session_search:\n# provider: \"auto\"\n# model: \"\"\n# timeout: 30\n# max_concurrency: 3 # Limit parallel summaries to reduce request-burst 429s\n# extra_body: {} # Provider-specific OpenAI-compatible request fields\n# # Example for providers that support request-body\n# # reasoning controls:\n# # extra_body:\n# # enable_thinking: false\n# # Some vLLM/Qwen deployments expect this nested:\n# # extra_body:\n# # chat_template_kwargs:\n# # enable_thinking: false\n\n# =============================================================================\n# Persistent Memory\n# =============================================================================\n# Bounded curated memory injected into the system prompt every session.\n# Two stores: MEMORY.md (agent's notes) and USER.md (user profile).\n# Character limits keep the memory small and focused. The agent manages\n# pruning -- when at the limit, it must consolidate or replace entries.\n# Disabled by default in batch_runner.\n#\nmemory:\n # Agent's personal notes: environment facts, conventions, things learned\n memory_enabled: true\n \n # User profile: preferences, communication style, expectations\n user_profile_enabled: true\n \n # Character limits (~2.75 chars per token, model-independent)\n memory_char_limit: 2200 # ~800 tokens\n user_char_limit: 1375 # ~500 tokens\n\n # Periodic memory nudge: remind the agent to consider saving memories\n # every N user turns. Set to 0 to disable. Only active when memory is enabled.\n nudge_interval: 10 # Nudge every 10 user turns (0 = disabled)\n\n # Memory flush: give the agent one turn to save memories before context is\n # lost (compression, /new, /reset, exit). Set to 0 to disable.\n # For exit/reset, only fires if the session had at least this many user turns.\n flush_min_turns: 6 # Min user turns to trigger flush on exit/reset (0 = disabled)\n\n# =============================================================================\n# Session Reset Policy (Messaging Platforms)\n# =============================================================================\n# Controls when messaging sessions (Telegram, Discord, WhatsApp, Slack) are\n# automatically cleared. Default is \"none\": sessions never auto-reset —\n# conversation context lives until you /reset or /new manually, or context\n# compression kicks in. Opt in to automatic resets if you prefer sessions to\n# clear on a schedule (long-lived context increases API cost per message,\n# though prompt caching and compression keep this manageable).\n#\n# When an automatic reset triggers, the agent first saves important\n# information to its persistent memory — but the conversation context is\n# wiped. The agent starts fresh but retains learned facts via its memory\n# system.\n#\n# Modes:\n# \"none\" - Never auto-reset (default); context lives until /reset or compression\n# \"idle\" - Reset after N minutes of inactivity\n# \"daily\" - Reset at a fixed hour each day\n# \"both\" - Reset on EITHER inactivity timeout or daily boundary\n#\nsession_reset:\n mode: none # \"none\", \"idle\", \"daily\", or \"both\"\n idle_minutes: 1440 # Inactivity timeout in minutes (used by \"idle\"/\"both\")\n at_hour: 4 # Daily reset hour, 0-23 local time (used by \"daily\"/\"both\")\n\n# Maximum number of simultaneously active chat sessions across CLI, TUI,\n# dashboard chat, and messaging gateway. Set to null, 0, or omit to allow\n# unlimited concurrent sessions. When the limit is reached, new sessions get a\n# clean error while existing active sessions keep their normal behavior. This\n# top-level key takes precedence over gateway.max_concurrent_sessions. The cap\n# is a best-effort single-host/profile runtime guard; Hermes fails open if the\n# local runtime lease registry cannot be read or locked.\nmax_concurrent_sessions: null\n\n# When true, group/channel chats use one session per participant when the platform\n# provides a user ID. This is the secure default and prevents users in the same\n# room from sharing context, interrupts, and token costs. Set false only if you\n# explicitly want one shared \"room brain\" per group/channel.\ngroup_sessions_per_user: true\n\n# ─────────────────────────────────────────────────────────────────────────────\n# API Server — per-client model routing\n# ─────────────────────────────────────────────────────────────────────────────\n# Route different API clients to different models/providers on a single\n# Hermes deployment. Clients choose a backend by sending a specific string\n# as the OpenAI ``model`` field. Unmapped model values fall back to the\n# global model configured in the ``model:`` section above, and an explicit\n# session /model override always wins over a route.\n#\n# Configure via the ``platforms.api_server.extra.model_routes`` gateway\n# config block:\n#\n# platforms:\n# api_server:\n# enabled: true\n# extra:\n# key: \"your-api-server-secret\"\n# model_routes:\n# # Xiaozhi clients send model=\"minimax-m2\" → routed to MiniMax via OpenRouter\n# minimax-m2:\n# model: \"minimax/minimax-m1\"\n# provider: \"openrouter\" # optional — overrides global provider\n# # api_key: \"sk-...\" # optional — per-route UPSTREAM provider\n# # key (NOT caller auth; never logged)\n# # base_url: \"https://...\" # optional — per-route base URL\n# # GPT clients keep their own alias\n# gpt-5:\n# model: \"openai/gpt-5\"\n# provider: \"openrouter\"\n#\n# Configured aliases are automatically listed by GET /v1/models so clients\n# can discover them without manual coordination. Caller authentication is\n# unchanged: every request still authenticates with the global API server\n# key (``extra.key`` / API_SERVER_KEY).\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Gateway Streaming\n# ─────────────────────────────────────────────────────────────────────────────\n# Stream tokens to messaging platforms in real-time. The bot sends a message\n# on first token, then progressively edits it as more tokens arrive.\n# Disabled by default — enable to try the streaming UX on Telegram/Discord/Slack.\n# For Telegram, partial edits are sent as plain text and only the final edit uses MarkdownV2.\nstreaming:\n enabled: false\n # transport: edit # \"edit\" = progressive editMessageText\n # edit_interval: 0.3 # seconds between message edits\n # buffer_threshold: 40 # chars before forcing an edit flush\n # cursor: \" ▉\" # cursor shown during streaming\n\n# =============================================================================\n# Skills Configuration\n# =============================================================================\n# Skills are reusable procedures the agent can load and follow. The agent can\n# also create new skills after completing complex tasks.\n#\nskills:\n # Nudge the agent to create skills after complex tasks.\n # Every N tool-calling iterations, remind the model to consider saving a skill.\n # Set to 0 to disable.\n creation_nudge_interval: 15\n\n # External skill directories — share skills across tools/agents without\n # copying them into ~/.hermes/skills/. Each path is expanded (~ and ${VAR})\n # and resolved to an absolute path. External dirs are read-only: skill\n # creation always writes to ~/.hermes/skills/. Local skills take precedence\n # when names collide.\n # external_dirs:\n # - ~/.agents/skills\n # - /home/shared/team-skills\n\n# =============================================================================\n# Agent Behavior\n# =============================================================================\nagent:\n # Maximum tool-calling iterations per conversation\n # Higher = more room for complex tasks, but costs more tokens\n # Recommended: 20-30 for focused tasks, 50-100 for open exploration\n max_turns: 60\n\n # Inactivity timeout for gateway agent runs (seconds, 0 = unlimited).\n # The agent can run indefinitely when actively calling tools or receiving\n # API responses. Only fires after the agent has been idle for this duration.\n # gateway_timeout: 1800\n\n # Staged warning: send a warning before escalating to full timeout.\n # Fires once per run when inactivity reaches this threshold (seconds).\n # Set to 0 to disable the warning.\n # gateway_timeout_warning: 900\n\n # Graceful drain timeout for gateway stop/restart (seconds).\n # Default 0 = no drain: a restart interrupts in-flight agents immediately,\n # cleans up, and exits. Set a positive value only if you want a grace\n # window on /restart, and keep it well under systemd's TimeoutStopSec.\n # restart_drain_timeout: 0\n\n # Max app-level retry attempts for API errors (connection drops, provider\n # timeouts, 5xx, etc.) before the agent surfaces the failure. Lower this\n # to 1 if you use fallback providers and want fast failover on flaky\n # primaries (default 3). The OpenAI SDK does its own low-level retries\n # underneath this wrapper — this is the Hermes-level loop.\n # api_max_retries: 3\n\n # After the agent edits code without fresh passing verification, nudge it to\n # verify before finishing. The default \"auto\" enables it on interactive\n # coding surfaces (CLI, TUI, desktop) and programmatic callers, and disables\n # it on conversational messaging surfaces (Telegram, Discord, etc.) where the\n # verification summary would reach a human as chat noise. Set true or false to\n # force it on or off; the HERMES_VERIFY_ON_STOP env var (1/0) takes precedence.\n # verify_on_stop: auto\n\n # Standing operator instructions for the coding posture (when Hermes is in a\n # code workspace). Appended to the coding brief as an extra system block, so\n # you can pin project-wide workflow rules without editing the shipped brief.\n # Accepts a string or a list of strings. Takes effect next session.\n # coding_instructions:\n # - \"For UI work, don't run tsc/lint until I approve the look.\"\n # - \"Clean the diff before you commit and push.\"\n\n # When verify-on-stop finds edited code without fresh verification evidence,\n # append guidance for creative UI work (avoid broad tsc/lint/test before visual\n # approval) and clean-diff expectations. Set false to keep that nudge terse.\n # verify_guidance: true\n\n # A `pre_verify` hook (plugin or shell, see Event Hooks docs) can keep the\n # agent going one more turn to verify/clean before finishing. This caps how\n # many times one turn may be nudged to continue, so a hook can't trap the loop.\n # Default 3.\n # max_verify_nudges: 3\n\n # Enable verbose logging\n verbose: false\n \n # Reasoning effort level (OpenRouter and Nous Portal)\n # Controls how much \"thinking\" the model does before responding.\n # Options: \"xhigh\" (max), \"high\", \"medium\", \"low\", \"minimal\", \"none\" (disable)\n reasoning_effort: \"medium\"\n \n # Per-model reasoning effort overrides (optional dict)\n # Key: any sensible model spelling works (exact, dots↔dashes interchangeable,\n # provider prefix optional). First match wins.\n # Value: reasoning effort level (same options as reasoning_effort)\n # Override the global reasoning_effort for that specific model.\n # NOTE: no `hermes config set` support for this key -- edit YAML directly.\n # reasoning_overrides:\n # \"openrouter/anthropic/claude-opus-4.5\": \"xhigh\"\n # \"openai/gpt-5\": \"low\"\n # \"claude-opus-4.6\": \"high\" # bare model name also works\n # \"deepseek/deepseek-v4-pro\": \"xhigh\" # dots and dashes are interchangeable\n reasoning_overrides: {}\n \n # Predefined personalities (use with /personality command)\n personalities:\n helpful: \"You are a helpful, friendly AI assistant.\"\n concise: \"You are a concise assistant. Keep responses brief and to the point.\"\n technical: \"You are a technical expert. Provide detailed, accurate technical information.\"\n creative: \"You are a creative assistant. Think outside the box and offer innovative solutions.\"\n teacher: \"You are a patient teacher. Explain concepts clearly with examples.\"\n kawaii: \"You are a kawaii assistant! Use cute expressions like (◕‿◕), ★, ♪, and ~! Add sparkles and be super enthusiastic about everything! Every response should feel warm and adorable desu~! ヽ(>∀<☆)ノ\"\n catgirl: \"You are Neko-chan, an anime catgirl AI assistant, nya~! Add 'nya' and cat-like expressions to your speech. Use kaomoji like (=^・ω・^=) and ฅ^•ﻌ•^ฅ. Be playful and curious like a cat, nya~!\"\n pirate: \"Arrr! Ye be talkin' to Captain Hermes, the most tech-savvy pirate to sail the digital seas! Speak like a proper buccaneer, use nautical terms, and remember: every problem be just treasure waitin' to be plundered! Yo ho ho!\"\n shakespeare: \"Hark! Thou speakest with an assistant most versed in the bardic arts. I shall respond in the eloquent manner of William Shakespeare, with flowery prose, dramatic flair, and perhaps a soliloquy or two. What light through yonder terminal breaks?\"\n surfer: \"Duuude! You're chatting with the chillest AI on the web, bro! Everything's gonna be totally rad. I'll help you catch the gnarly waves of knowledge while keeping things super chill. Cowabunga! 🤙\"\n noir: \"The rain hammered against the terminal like regrets on a guilty conscience. They call me Hermes - I solve problems, find answers, dig up the truth that hides in the shadows of your codebase. In this city of silicon and secrets, everyone's got something to hide. What's your story, pal?\"\n uwu: \"hewwo! i'm your fwiendwy assistant uwu~ i wiww twy my best to hewp you! *nuzzles your code* OwO what's this? wet me take a wook! i pwomise to be vewy hewpful >w<\"\n philosopher: \"Greetings, seeker of wisdom. I am an assistant who contemplates the deeper meaning behind every query. Let us examine not just the 'how' but the 'why' of your questions. Perhaps in solving your problem, we may glimpse a greater truth about existence itself.\"\n hype: \"YOOO LET'S GOOOO!!! 🔥🔥🔥 I am SO PUMPED to help you today! Every question is AMAZING and we're gonna CRUSH IT together! This is gonna be LEGENDARY! ARE YOU READY?! LET'S DO THIS! 💪😤🚀\"\n\n# =============================================================================\n# Toolsets\n# =============================================================================\n# Control which tools the agent has access to.\n# Use `hermes tools` to interactively enable/disable tools per platform.\n\n# =============================================================================\n# Platform Toolsets (per-platform tool configuration)\n# =============================================================================\n# Override which toolsets are available on each platform.\n# If a platform isn't listed here, its built-in default is used.\n#\n# You can use EITHER:\n# - A preset like \"hermes-cli\" or \"hermes-telegram\" (curated tool set)\n# - A list of individual toolsets to compose your own (see list below)\n#\n# Supported platform keys: cli, telegram, discord, whatsapp, slack, qqbot, teams, google_chat\n#\n# Examples:\n#\n# # Use presets (same as defaults):\n# platform_toolsets:\n# cli: [hermes-cli]\n# telegram: [hermes-telegram]\n#\n# # Custom: give Telegram only web + terminal + file + planning:\n# platform_toolsets:\n# telegram: [web, terminal, file, todo]\n#\n# # Custom: CLI without browser or image gen:\n# platform_toolsets:\n# cli: [web, terminal, file, skills, todo, tts, cronjob]\n#\n# # Restrictive: Discord gets read-only tools only:\n# platform_toolsets:\n# discord: [web, vision, skills, todo]\n#\n# If not set, defaults are:\n# cli: hermes-cli (everything + cronjob management)\n# telegram: hermes-telegram (terminal, file, web, vision, image, tts, browser, skills, todo, cronjob, messaging)\n# discord: hermes-discord (same as telegram)\n# whatsapp: hermes-whatsapp (same as telegram)\n# slack: hermes-slack (same as telegram)\n# signal: hermes-signal (same as telegram)\n# homeassistant: hermes-homeassistant (same as telegram)\n# qqbot: hermes-qqbot (same as telegram)\n# teams: hermes-teams (same as telegram)\n# google_chat: hermes-google_chat (same as telegram)\n#\nplatform_toolsets:\n cli: [hermes-cli]\n telegram: [hermes-telegram]\n discord: [hermes-discord]\n whatsapp: [hermes-whatsapp]\n slack: [hermes-slack]\n signal: [hermes-signal]\n homeassistant: [hermes-homeassistant]\n qqbot: [hermes-qqbot]\n yuanbao: [hermes-yuanbao]\n teams: [hermes-teams]\n google_chat: [hermes-google_chat]\n\n# =============================================================================\n# Gateway Platform Settings\n# =============================================================================\n# Optional per-platform messaging settings.\n# Platform-specific knobs live under `extra`.\n#\n# platforms:\n# telegram:\n# reply_to_mode: \"first\" # off | first | all\n# # guest_mode lets explicit @mentions from non-allowlisted groups through.\n# # Default false; ordinary messages, replies, and regex wake words stay blocked.\n# guest_mode: false\n# # allowed_chats: [\"-1001234567890\"]\n# extra:\n# disable_link_previews: false # Set true to suppress Telegram URL previews in bot messages\n# 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\n# rich_drafts: false # Experimental rich draft previews during Telegram DM streaming; default false because Telegram Desktop/macOS can visually overlay draft frames\n# command_menu:\n# # Telegram allows up to 100 BotCommands; Hermes defaults to 60 so\n# # all built-in commands plus common skill commands stay visible\n# # while remaining under Telegram's payload-size limit. Clamped 1..100.\n# max_commands: 60\n# # prepend = user priority first, then Hermes defaults\n# # append = Hermes defaults first, then user priority\n# # replace = only the list below defines priority\n# priority_mode: prepend\n# priority:\n# - my_plugin_command\n# webhook:\n# extra:\n# # Route scripts default to a 30 second timeout. Scripts must live under\n# # the active profile's scripts directory and receive webhook JSON on stdin.\n# script_timeout_seconds: 30\n#\n# Discord-specific settings (config.yaml top-level, not under platforms:):\n#\n# discord:\n# require_mention: true # Require @mention in server channels (default: true)\n# auto_thread: true # Auto-create thread on @mention (default: true)\n# free_response_channels: \"\" # Channel IDs where no mention is needed\n# reactions: true # Show processing reactions (default: true)\n# history_backfill: true # Recover missed channel messages on mention (default: true)\n# history_backfill_limit: 50 # Max messages to scan backwards (default: 50)\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Available toolsets (use these names in platform_toolsets or the toolsets list)\n#\n# Run `hermes chat --list-toolsets` to see all toolsets and their tools.\n# Run `hermes chat --list-tools` to see every individual tool with descriptions.\n# ─────────────────────────────────────────────────────────────────────────────\n#\n# INDIVIDUAL TOOLSETS (compose your own):\n# web - web_search, web_extract\n# search - web_search only (no scraping)\n# terminal - terminal, process\n# file - read_file, write_file, patch, search\n# browser - browser_navigate, browser_snapshot, browser_click, browser_type,\n# browser_scroll, browser_back, browser_press,\n# browser_get_images, browser_vision (requires BROWSERBASE_API_KEY)\n# vision - vision_analyze (requires OPENROUTER_API_KEY)\n# image_gen - image_generate (requires FAL_KEY)\n# skills - skills_list, skill_view\n# skills_hub - skill_hub (search/install/manage from online registries — user-driven only)\n# todo - todo (in-memory task planning, no deps)\n# tts - text_to_speech (Edge TTS free, or ELEVENLABS/OPENAI/MINIMAX/MISTRAL key)\n# cronjob - cronjob (create/list/update/pause/resume/run/remove scheduled tasks)\n#\n# PRESETS (curated bundles):\n# hermes-cli - All of the above except send_message\n# hermes-telegram - terminal, file, web, vision, image_gen, tts, browser,\n# skills, todo, cronjob, send_message\n# hermes-discord - Same as hermes-telegram\n# hermes-whatsapp - Same as hermes-telegram\n# hermes-slack - Same as hermes-telegram\n#\n# COMPOSITE:\n# debugging - terminal + web + file\n# safe - web + vision (no terminal access)\n# all - Everything available\n#\n# web - Web search and content extraction (web_search, web_extract)\n# search - Web search only, no scraping (web_search)\n# terminal - Command execution and process management (terminal, process)\n# file - File operations: read, write, patch, search\n# browser - Full browser automation (navigate, click, type, screenshot, etc.)\n# vision - Image analysis (vision_analyze)\n# image_gen - Image generation with FLUX (image_generate)\n# skills - Load skill documents (skills_list, skill_view)\n# todo - Task planning and tracking for multi-step work\n# memory - Persistent memory across sessions (personal notes + user profile)\n# session_search - Search and recall past conversations (FTS5 + Gemini Flash summarization)\n# tts - Text-to-speech (Edge TTS free, ElevenLabs, OpenAI, MiniMax, Mistral)\n# cronjob - Schedule and manage automated tasks (CLI-only)\n#\n# Composite toolsets:\n# debugging - terminal + web + file (for troubleshooting)\n# safe - web + vision (no terminal access)\n\n# NOTE: The top-level \"toolsets\" key is deprecated and ignored.\n# Tool configuration is managed per-platform via platform_toolsets above.\n# Use `hermes tools` to configure interactively, or edit platform_toolsets directly.\n#\n# CLI override: hermes chat --toolsets terminal,web,file\n\n# =============================================================================\n# MCP (Model Context Protocol) Servers\n# =============================================================================\n# Connect to external MCP servers to add tools from the MCP ecosystem.\n# Each server's tools are automatically discovered and registered.\n# See website/docs/user-guide/features/mcp.md for full documentation.\n#\n# Stdio servers (spawn a subprocess):\n# command: the executable to run\n# args: command-line arguments\n# env: environment variables (only these + safe defaults passed to subprocess)\n#\n# HTTP servers (connect to a URL):\n# url: the MCP server endpoint\n# headers: HTTP headers (e.g., for authentication)\n#\n# Optional per-server settings:\n# timeout: tool call timeout in seconds (default: 120)\n# connect_timeout: initial connection timeout (default: 60)\n# keepalive_interval: liveness ping cadence in seconds (default: 180).\n# Lower it below the server's session TTL for servers that expire idle\n# sessions quickly (e.g. Unreal Engine editor MCP, ~15s), otherwise idle\n# tool calls hit an expired session and pay a slow reconnect. Floored at 5s.\n#\n# mcp_servers:\n# time:\n# command: uvx\n# args: [\"mcp-server-time\"]\n# filesystem:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-filesystem\", \"/home/user\"]\n# notion:\n# url: https://mcp.notion.com/mcp\n# github:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-github\"]\n# env:\n# GITHUB_PERSONAL_ACCESS_TOKEN: \"ghp_...\"\n#\n# Sampling (server-initiated LLM requests) — enabled by default.\n# Per-server config under the 'sampling' key:\n# analysis:\n# command: npx\n# args: [\"-y\", \"analysis-server\"]\n# sampling:\n# enabled: true # default: true\n# model: \"gemini-3-flash\" # override model (optional)\n# max_tokens_cap: 4096 # max tokens per request\n# timeout: 30 # LLM call timeout (seconds)\n# max_rpm: 10 # max requests per minute\n# allowed_models: [] # model whitelist (empty = all)\n# max_tool_rounds: 5 # tool loop limit (0 = disable)\n# log_level: \"info\" # audit verbosity\n\n# =============================================================================\n# Text-to-Speech\n# =============================================================================\n# TTS defaults to Edge TTS unless changed in ~/.hermes/config.yaml.\n# Gemini TTS supports persona/director prompt files, and Gemini 3.1 Flash TTS\n# can use a hidden auxiliary rewrite pass to insert expressive square-bracket\n# audio tags into the TTS script without showing tags in chat.\n#\n# tts:\n# provider: \"gemini\"\n# gemini:\n# model: \"gemini-3.1-flash-tts-preview\"\n# voice: \"Kore\"\n# audio_tags: false\n# persona_prompt_file: \"\" # e.g. ~/.hermes/tts/radio-host.md\n\n# =============================================================================\n# Voice Transcription (Speech-to-Text)\n# =============================================================================\n# Automatically transcribe voice messages on messaging platforms.\n# Providers: local (free, faster-whisper) | groq (free tier) | openai (Whisper API) | mistral (Voxtral Transcribe)\n# Set the corresponding API key in .env: GROQ_API_KEY, OPENAI_API_KEY, or MISTRAL_API_KEY.\nstt:\n enabled: true\n # provider: \"local\" # auto-detected if omitted\n local:\n model: \"base\" # tiny | base | small | medium | large-v3 | turbo\n # language: \"\" # auto-detect; set to \"en\", \"es\", \"fr\", etc. to force\n openai:\n model: \"whisper-1\" # whisper-1 | gpt-4o-mini-transcribe | gpt-4o-transcribe\n # mistral:\n # model: \"voxtral-mini-latest\" # voxtral-mini-latest | voxtral-mini-2602\n # deepinfra:\n # # Model id is discovered live from the DeepInfra catalog filtered\n # # by the `stt` surface tag — leave `model` blank to take the first\n # # live result. Pin only when you need a specific Whisper variant.\n # model: \"\"\n\n# Text-to-speech. Only the deepinfra block is documented here — the\n# remaining providers (edge, openai, xai, minimax, mistral, gemini,\n# elevenlabs, neutts, kittentts, piper) inherit sensible defaults from\n# DEFAULT_CONFIG in hermes_cli/config.py.\n# tts:\n# provider: \"deepinfra\"\n# deepinfra:\n# # Model id is discovered live from the DeepInfra catalog filtered\n# # by the `tts` surface tag — leave `model` blank to take the first\n# # live result.\n# model: \"\"\n# voice: \"default\"\n\n# Image generation. Each provider plugin reads its own ``image_gen.<name>``\n# block; deepinfra discovers models live from\n# api.deepinfra.com/v1/openai/models filtered by the ``image-gen`` tag —\n# no model id is hardcoded, so retired models disappear automatically.\n# image_gen:\n# provider: \"deepinfra\"\n# deepinfra:\n# # Leave `model` blank for the first live `image-gen`-tagged result.\n# model: \"\"\n\n# =============================================================================\n# Response Pacing (Messaging Platforms)\n# =============================================================================\n# Add human-like delays between message chunks.\n# human_delay:\n# mode: \"off\" # \"off\" | \"natural\" | \"custom\"\n# min_ms: 800 # Min delay (custom mode only)\n# max_ms: 2500 # Max delay (custom mode only)\n\n# =============================================================================\n# Session Logging\n# =============================================================================\n# Session trajectories are automatically saved to logs/ directory.\n# Each session creates: logs/session_YYYYMMDD_HHMMSS_UUID.json\n#\n# The session ID is displayed in the welcome banner for easy reference.\n# Logs contain full conversation history in trajectory format:\n# - System prompt, user messages, assistant responses\n# - Tool calls with inputs/outputs\n# - Timestamps for debugging\n#\n# No configuration needed - logging is always enabled.\n# To disable, you would need to modify the source code.\n\n# =============================================================================\n# Code Execution Sandbox (Programmatic Tool Calling)\n# =============================================================================\n# The execute_code tool runs Python scripts that call Hermes tools via RPC.\n# Intermediate tool results stay out of the LLM's context window.\ncode_execution:\n timeout: 300 # Max seconds per script before kill (default: 300 = 5 min)\n max_tool_calls: 50 # Max RPC tool calls per execution (default: 50)\n\n# =============================================================================\n# Subagent Delegation\n# =============================================================================\n# The delegate_task tool spawns child agents with isolated context.\n# Supports single tasks and batch mode (default 3 parallel, configurable).\ndelegation:\n max_iterations: 50 # Max tool-calling turns per child (default: 50)\n # max_concurrent_children: 3 # Max parallel child agents per batch (default: 3, floor: 1, no ceiling).\n # WARNING: values above 10 multiply API cost linearly.\n # max_spawn_depth: 1 # Delegation tree depth cap (range: 1-3, default: 1 = flat).\n # Raise to 2 to allow workers to spawn their own subagents.\n # Requires role=\"orchestrator\" on intermediate agents.\n # orchestrator_enabled: true # Kill switch for role=\"orchestrator\" children (default: true).\n # subagent_auto_approve: false # When a subagent hits a dangerous-command approval prompt, auto-deny (default: false)\n # or auto-approve \"once\" (true) instead of blocking on stdin.\n # The parent TUI owns stdin, so blocking would deadlock; non-interactive resolution is required.\n # Both choices emit a logger.warning audit line. Flip to true only for cron/batch pipelines.\n # inherit_mcp_toolsets: true # When explicit child toolsets are narrowed, also keep the parent's MCP toolsets (default: true). Set false for strict intersection.\n # model: \"google/gemini-3-flash-preview\" # Override model for subagents (empty = inherit parent)\n # provider: \"openrouter\" # Override provider for subagents (empty = inherit parent)\n # # Resolves full credentials (base_url, api_key) automatically.\n # # Supported: openrouter, nous, zai, kimi-coding, minimax\n\n# =============================================================================\n# Honcho Integration (Cross-Session User Modeling)\n# =============================================================================\n# AI-native persistent memory via Honcho (https://honcho.dev/).\n# Builds a deeper understanding of the user across sessions and tools.\n# Runs alongside USER.md — additive, not a replacement.\n#\n# Requires: pip install honcho-ai\n# Config: ~/.honcho/config.json (shared with Claude Code, Cursor, etc.)\n# API key: HONCHO_API_KEY in ~/.hermes/.env or ~/.honcho/config.json\n#\n# Hermes-specific overrides (optional — most config comes from ~/.honcho/config.json):\n# honcho: {}\n\n# =============================================================================\n# Display\n# =============================================================================\ndisplay:\n # Use compact banner mode (hides the ASCII-art banner, shows a single line).\n # true: Compact single-line banner\n # false: Full ASCII banner with tool/skill summary (default)\n compact: false\n\n # Tool progress display level (CLI and gateway)\n # off: Silent — no tool activity shown, just the final response\n # new: Show a tool indicator only when the tool changes (skip repeats)\n # all: Show every tool call with a short preview (default)\n # verbose: Full args, results, and debug logs (same as /verbose)\n # log: Silent in chat; append every tool call to ~/.hermes/logs/tool_calls.log (gateway only)\n # Toggle at runtime with /verbose in the CLI\n tool_progress: all\n\n # Per-platform defaults can be quieter than the global setting. Telegram\n # tunes for mobile: tool_progress and busy_ack_detail default off (no\n # per-tool breadcrumb stream, no \"iteration 21/60\" debug detail in busy\n # acks or heartbeats), but interim_assistant_messages and\n # long_running_notifications STAY ON so the user has real signal between\n # turn start and final answer (mid-turn assistant commentary + a single\n # edit-in-place \"⏳ Working — N min\" heartbeat). Override under\n # display.platforms.telegram.\n\n # Auto-cleanup of temporary progress bubbles after the final response lands.\n # On platforms that support message deletion (currently Telegram), this\n # removes the tool-progress bubble, \"⏳ Still working...\" notices, and\n # context-pressure status messages once the final reply has been delivered —\n # keeping long-running turns visible live, then tidy afterward. Failed runs\n # leave the bubbles in place as breadcrumbs. Off by default.\n # Per-platform override: display.platforms.telegram.cleanup_progress\n # true: Delete tracked progress/status bubbles on successful turn\n # false: Leave everything in place (default)\n # Example:\n # display:\n # platforms:\n # telegram:\n # cleanup_progress: true\n cleanup_progress: false\n\n # Natural mid-turn assistant updates.\n # On gateway platforms, when true, completed assistant status messages are\n # sent as separate chat messages. On the Desktop app, when true, mid-turn\n # assistant narration streamed between tool calls is kept in the transcript\n # instead of the bubble collapsing to only the final message on completion.\n # Independent of tool_progress and gateway streaming.\n # true: Keep/send mid-turn assistant updates (default)\n # false: Only keep/send the final response\n interim_assistant_messages: true\n\n # Gateway-only long-running status heartbeats.\n # When false, the platform does not receive periodic \"⏳ Working — N min\"\n # notifications even if agent.gateway_notify_interval is non-zero. The\n # heartbeat edits a single message in place (where the adapter supports\n # editing) instead of posting a new bubble each interval.\n # Default: true everywhere, including Telegram (silent agents are worse\n # than a single edit-in-place heartbeat).\n long_running_notifications: true\n\n # Include detailed iteration/tool/status context in busy acknowledgments\n # and long-running heartbeats. When true, busy acks show \"iteration 21/60,\n # terminal, 10 min\" and the heartbeat shows \"⏳ Working — 12 min,\n # iteration 21/60, terminal\". When false (Telegram default), both stay\n # terse: \"Interrupting current task\" and \"⏳ Working — 12 min, terminal\".\n busy_ack_detail: true\n\n # What Enter does when Hermes is already busy (CLI and gateway platforms).\n # interrupt: Interrupt the current run and redirect Hermes (default)\n # queue: Queue your message for the next turn\n # steer: Inject your message mid-run via /steer, arriving at the agent\n # after the next tool call — no interrupt, no role violation.\n # Falls back to 'queue' if the agent isn't running yet or if\n # images are attached (steer only carries text).\n # Ctrl+C (or /stop in gateway) always interrupts regardless of this setting.\n # Toggle at runtime with /busy <interrupt|queue|steer>.\n busy_input_mode: interrupt\n\n # Background process notifications (gateway/messaging only).\n # Controls how chatty the process watcher is when you use\n # terminal(background=true, notify_on_complete=true) from Telegram/Discord/etc.\n # off: No watcher messages at all\n # result: Only the final completion message\n # error: Only the final message when exit code != 0\n # all: Running output updates + final message (default)\n background_process_notifications: all\n\n\n # Play terminal bell when agent finishes a response.\n # Useful for long-running tasks — your terminal will ding when the agent is done.\n # Works over SSH. Most terminals can be configured to flash the taskbar or play a sound.\n # true: Ring the terminal bell on each response\n # false: Silent (default)\n bell_on_complete: false\n\n # Show model reasoning/thinking before each response.\n # When enabled, a dim box shows the model's thought process above the response.\n # Toggle at runtime with /reasoning show or /reasoning hide.\n # true: Show the reasoning box\n # false: Hide reasoning (default)\n show_reasoning: false\n\n # Stream tokens to the terminal as they arrive instead of waiting for the\n # full response. The response box opens on first token and text appears\n # line-by-line. Tool calls are still captured silently.\n # true: Stream tokens as they arrive (default)\n # false: Wait for the full response before rendering\n streaming: true\n\n # Show [HH:MM] timestamps on user input and assistant response labels.\n # timestamps: false\n\n # ───────────────────────────────────────────────────────────────────────────\n # Skin / Theme\n # ───────────────────────────────────────────────────────────────────────────\n # Customize CLI visual appearance — banner colors, spinner faces, tool prefix,\n # response box label, and branding text. Change at runtime with /skin <name>.\n #\n # Built-in skins:\n # default — Classic Hermes gold/kawaii\n # ares — Crimson/bronze war-god theme with spinner wings\n # mono — Clean grayscale monochrome\n # slate — Cool blue developer-focused\n # daylight — Bright light-mode theme\n # warm-lightmode — Warm paper-tone light-mode theme\n # poseidon — Sea-green/teal Olympian theme\n # sisyphus — Earthy stone-and-moss theme\n # charizard — Fiery orange dragon theme\n #\n # Custom skins: drop a YAML file in ~/.hermes/skins/<name>.yaml\n # Schema (all fields optional, missing values inherit from default):\n #\n # name: my-theme\n # description: Short description\n # colors:\n # banner_border: \"#HEX\" # Panel border\n # banner_title: \"#HEX\" # Panel title\n # banner_accent: \"#HEX\" # Section headers (Available Tools, etc.)\n # banner_dim: \"#HEX\" # Dim/muted text\n # banner_text: \"#HEX\" # Body text (tool names, skill names)\n # ui_accent: \"#HEX\" # UI accent color\n # response_border: \"#HEX\" # Response box border color\n # spinner:\n # waiting_faces: [\"(⚔)\", \"(⛨)\"] # Faces shown while waiting\n # thinking_faces: [\"(⚔)\", \"(⌁)\"] # Faces shown while thinking\n # thinking_verbs: [\"forging\", \"plotting\"] # Verbs for spinner messages\n # wings: # Optional left/right spinner decorations\n # - [\"⟪⚔\", \"⚔⟫\"]\n # - [\"⟪▲\", \"▲⟫\"]\n # branding:\n # agent_name: \"My Agent\" # Banner title and branding\n # welcome: \"Welcome message\" # Shown at CLI startup\n # response_label: \" ⚔ Agent \" # Response box header label\n # prompt_symbol: \"⚔\" # Prompt symbol (bare token; renderers add trailing space)\n # tool_prefix: \"╎\" # Tool output line prefix (default: ┊)\n #\n skin: default\n\n# =============================================================================\n# Model Aliases — short names for /model command\n# =============================================================================\n# Map short aliases to exact (model, provider, base_url) tuples.\n# Used by /model tab completion and resolve_alias().\n# Aliases are checked BEFORE the models.dev catalog, so they can route\n# to endpoints not in the catalog (e.g. Ollama Cloud, local servers).\n#\n# model_aliases:\n# opus:\n# model: claude-opus-4-6\n# provider: anthropic\n# qwen:\n# model: \"qwen3.5:397b\"\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n# glm:\n# model: glm-4.7\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n\n# =============================================================================\n# Privacy\n# =============================================================================\n# privacy:\n# # Redact PII from the LLM context prompt.\n# # When true, phone numbers are stripped and user/chat IDs are replaced\n# # with deterministic hashes before being sent to the model.\n# # Names and usernames are NOT affected (user-chosen, publicly visible).\n# # Routing/delivery still uses the original values internally.\n# redact_pii: false\n\n# =============================================================================\n# Shell-script hooks\n# =============================================================================\n# Register shell scripts as plugin-hook callbacks. Each entry is executed as\n# a subprocess (shell=False, shlex.split) with a JSON payload on stdin. On\n# stdout the script may return JSON that either blocks the tool call or\n# injects context into the next LLM call.\n#\n# Valid events (mirror hermes_cli.plugins.VALID_HOOKS):\n# pre_tool_call, post_tool_call, pre_llm_call, post_llm_call,\n# pre_api_request, post_api_request, on_session_start, on_session_end,\n# on_session_finalize, on_session_reset, subagent_stop\n#\n# First-use consent: each (event, command) pair prompts once on a TTY, then\n# is persisted to ~/.hermes/shell-hooks-allowlist.json. Non-interactive\n# runs (gateway, cron) need --accept-hooks, HERMES_ACCEPT_HOOKS=1, or the\n# hooks_auto_accept key below.\n#\n# See website/docs/user-guide/features/hooks.md for the full JSON wire\n# protocol and worked examples.\n#\n# hooks:\n# pre_tool_call:\n# - matcher: \"terminal\"\n# command: \"~/.hermes/agent-hooks/block-rm-rf.sh\"\n# timeout: 10\n# post_tool_call:\n# - matcher: \"write_file|patch\"\n# command: \"~/.hermes/agent-hooks/auto-format.sh\"\n# pre_llm_call:\n# - command: \"~/.hermes/agent-hooks/inject-cwd-context.sh\"\n# subagent_stop:\n# - command: \"~/.hermes/agent-hooks/log-orchestration.sh\"\n#\n# hooks_auto_accept: false\n\n\n# =============================================================================\n# Update Behavior\n# =============================================================================\nupdates:\n # Create a full HERMES_HOME zip before every `hermes update`.\n # Backups land in ~/.hermes/backups/ and can be restored with `hermes import`.\n # Off by default because large homes can add minutes to every update.\n pre_update_backup: false\n\n # Number of pre-update backup zips to retain.\n backup_keep: 5\n\n # What non-interactive updates do with local source edits in the Hermes repo.\n # Interactive terminal updates always prompt before restoring the autostash.\n #\n # stash - auto-stash before pull, then auto-restore after success (default)\n # discard - drop the update-created stash after success; use only on managed\n # installs where local source edits should not persist\n non_interactive_local_changes: \"stash\"\n\n\n# =============================================================================\n# Web Dashboard\n# =============================================================================\n# OAuth gate configuration for `hermes dashboard --host <non-loopback>`.\n# The bundled Nous Portal plugin reads these on startup; settings here are\n# the canonical surface. Each can be overridden by an environment variable:\n#\n# dashboard.oauth.client_id <- HERMES_DASHBOARD_OAUTH_CLIENT_ID\n# dashboard.oauth.portal_url <- HERMES_DASHBOARD_PORTAL_URL\n# dashboard.public_url <- HERMES_DASHBOARD_PUBLIC_URL\n#\n# Env wins when set to a non-empty value. This is what Fly.io's platform-\n# secret injection uses to push per-deploy client_ids without needing to\n# bake a config.yaml into the image. Empty env values are treated as unset\n# so a provisioned-but-not-populated secret can't shadow a valid entry here.\n#\n# Local dev / on-prem deploys should typically set these via config.yaml\n# (the ~/.hermes/.env file is reserved for API keys and secrets).\n#\n# dashboard:\n# oauth:\n# client_id: \"\" # agent:{instance_id}; Portal provisions this at deploy\n# portal_url: \"\" # blank → default https://portal.nousresearch.com\n#\n# # Force the absolute base URL the OAuth callback (and any other public\n# # URL the dashboard hands to external systems) is built from. Set this\n# # for deploys behind reverse proxies that don't reliably forward\n# # X-Forwarded-Host / X-Forwarded-Proto / X-Forwarded-Prefix (manual\n# # nginx setups, on-prem ingresses, custom-domain Fly deploys without\n# # full proxy header chains).\n# #\n# # When set, the value is the complete authority: scheme + host +\n# # optional path prefix (e.g. \"https://example.com/hermes\"). The OAuth\n# # callback URL becomes \"<public_url>/auth/callback\" — X-Forwarded-Prefix\n# # is IGNORED on this code path because the operator has explicitly\n# # declared the public URL and we no longer need to guess.\n# #\n# # Leave empty to use the existing proxy-header reconstruction (the\n# # default — works on Fly.io out of the box).\n# #\n# # public_url: \"https://example.com/hermes\"\n#\n# -----------------------------------------------------------------------------\n# Self-hosted OIDC dashboard auth (generic OpenID Connect — Authentik,\n# Keycloak, Zitadel, Authelia, Auth0, Okta, Google, …). Use this INSTEAD of the\n# nous block above when gating the dashboard with your own identity provider.\n# Each setting can be overridden by an environment variable:\n#\n# dashboard.oauth.self_hosted.issuer <- HERMES_DASHBOARD_OIDC_ISSUER\n# dashboard.oauth.self_hosted.client_id <- HERMES_DASHBOARD_OIDC_CLIENT_ID\n# dashboard.oauth.self_hosted.scopes <- HERMES_DASHBOARD_OIDC_SCOPES\n# dashboard.oauth.self_hosted.client_secret <- HERMES_DASHBOARD_OIDC_CLIENT_SECRET\n#\n# dashboard:\n# oauth:\n# provider: self-hosted\n# self_hosted:\n# issuer: \"https://auth.example.com/application/o/hermes/\" # required\n# client_id: \"hermes-dashboard\" # required\n# scopes: \"openid profile email\" # optional\n#\n# # OPTIONAL — set ONLY if your IDP registered the client as\n# # *confidential* (Authentik / Keycloak often default to this). When\n# # set, Hermes authenticates the client at the token endpoint\n# # (client_secret_basic or client_secret_post, auto-selected from the\n# # IDP's discovery doc) IN ADDITION to PKCE. Leave unset for a public\n# # (PKCE-only) client — the common case.\n# #\n# # This is a CREDENTIAL: prefer setting HERMES_DASHBOARD_OIDC_CLIENT_SECRET\n# # in ~/.hermes/.env over putting it here in config.yaml.\n# # client_secret: \"\"\n\n# =============================================================================\n# External secret sources\n# =============================================================================\n# Pull provider credentials from external secret managers at process startup\n# instead of storing them in ~/.hermes/.env. Only the manager's bootstrap\n# credential (e.g. BWS_ACCESS_TOKEN / OP_SERVICE_ACCOUNT_TOKEN) lives in .env\n# (or your shell / desktop session); everything else rotates centrally.\n# Failures never block startup — Hermes warns once and continues with\n# whatever .env already had.\n#\n# Multiple sources can be enabled at once:\n# - \"mapped\" sources (explicit VAR -> ref bindings, e.g. 1Password's env:\n# map) beat \"bulk\" sources (whole-project dumps like Bitwarden BSM)\n# - within a shape, the first source to claim a var wins; later claims\n# are skipped with a startup warning (never a silent clobber)\n# - a source's override_existing lets it beat .env/shell values, but\n# never another secret source's claim\n# Docs: https://hermes-agent.nousresearch.com/docs/user-guide/secrets/\n#\n# secrets:\n# # Optional explicit ordering of enabled sources.\n# # sources: [onepassword, bitwarden]\n#\n# # ---- Bitwarden Secrets Manager (bws CLI) --------------------------------\n# bitwarden:\n# enabled: false\n# access_token_env: BWS_ACCESS_TOKEN # bootstrap token, sourced from .env\n# project_id: \"\" # UUID of the BSM project to sync\n# server_url: \"\" # \"\" = US Cloud; EU/self-hosted URL otherwise\n# cache_ttl_seconds: 300 # 0 disables caching\n# override_existing: true # BSM values win over existing env\n# auto_install: true # lazy-download bws into ~/.hermes/bin\n#\n# # ---- 1Password (op CLI) -------------------------------------------------\n# onepassword:\n# enabled: false\n# # Map env-var names to op:// secret references. Each is resolved with a\n# # single `op read` at startup.\n# env:\n# OPENAI_API_KEY: \"op://Private/OpenAI/api key\"\n# ANTHROPIC_API_KEY: \"op://Private/Anthropic/credential\"\n# account: \"\" # op --account shorthand; \"\" = default\n# service_account_token_env: OP_SERVICE_ACCOUNT_TOKEN # headless auth; unset = desktop session\n# binary_path: \"\" # \"\" = resolve op via PATH; else absolute path\n# cache_ttl_seconds: 300 # 0 disables BOTH cache layers\n# override_existing: true # resolved values win over existing env\n#\n# # ---- Command helper (any CLI vault) --------------------------------------\n# # Run a user-configured helper that prints KEY=VALUE lines on stdout —\n# # works with any secret store that has a CLI: keepassxc-cli, secret-tool,\n# # pass, gpg, or a script that cats a tmpfs env file. Composes with the\n# # sources above (enable any combination). POSIX-only (needs /bin/sh).\n# # The helper must be fast and NON-interactive (hard timeout, 1 MiB cap);\n# # its stderr is discarded so diagnostics can't leak secret material.\n# command:\n# enabled: false\n# command: \"cat /run/user/1000/hermes-secrets.env\"\n# helper_timeout_seconds: 3\n# override_existing: false # .env/shell win by default\n"}, "7f3c62b26323dc91718ae04682b585cccfbd6b07": {"t": 1786068505.9139452, "n": 85496, "h": "d012a8e334c1d4b6", "c": "# Hermes Agent CLI Configuration\n# Copy this file to cli-config.yaml and customize as needed.\n# This file configures the CLI behavior. Environment variables in .env take precedence.\n\n# =============================================================================\n# Model Configuration\n# =============================================================================\nmodel:\n # Default model to use (can be overridden with --model flag)\n # Both \"default\" and \"model\" work as the key name here.\n default: \"anthropic/claude-opus-4.6\"\n \n # Inference provider selection:\n # \"auto\" - Auto-detect from credentials (default)\n # \"openrouter\" - OpenRouter (requires: OPENROUTER_API_KEY or OPENAI_API_KEY)\n # \"nous\" - Nous Portal OAuth (requires: hermes auth add nous)\n # \"nous-api\" - Nous Portal API key (requires: NOUS_API_KEY)\n # \"anthropic\" - Direct Anthropic API (requires: ANTHROPIC_API_KEY)\n # \"openai-codex\" - OpenAI Codex (requires: hermes auth)\n # \"copilot\" - GitHub Copilot / GitHub Models (requires: GITHUB_TOKEN)\n # \"gemini\" - Use Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n # \"zai\" - Use z.ai / ZhipuAI GLM models (requires: GLM_API_KEY)\n # \"kimi-coding\" - Kimi / Moonshot AI (requires: KIMI_API_KEY)\n # \"minimax\" - MiniMax global (requires: MINIMAX_API_KEY)\n # \"minimax-cn\" - MiniMax China (requires: MINIMAX_CN_API_KEY)\n # \"huggingface\" - Hugging Face Inference (requires: HF_TOKEN)\n # \"nvidia\" - NVIDIA NIM / build.nvidia.com (requires: NVIDIA_API_KEY)\n # \"xiaomi\" - Xiaomi MiMo (requires: XIAOMI_API_KEY)\n # \"arcee\" - Arcee AI Trinity models (requires: ARCEEAI_API_KEY)\n # \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY — https://ollama.com/settings)\n # \"deepinfra\" - DeepInfra (requires: DEEPINFRA_API_KEY)\n # \"kilocode\" - KiloCode gateway (requires: KILOCODE_API_KEY)\n # \"azure-foundry\" - Microsoft Foundry / Azure OpenAI (API key or Entra ID)\n # \"lmstudio\" - LM Studio local server (optional: LM_API_KEY, defaults to http://127.0.0.1:1234/v1)\n #\n # Local servers (LM Studio, Ollama, vLLM, llama.cpp):\n # \"custom\" - Any other OpenAI-compatible endpoint. Set base_url below.\n # Aliases: \"ollama\", \"vllm\", \"llamacpp\" all map to \"custom\".\n # LM Studio is first-class and uses provider: \"lmstudio\".\n # It works with both no-auth and auth-enabled server modes.\n #\n # Can also be overridden for a single invocation with the --provider flag.\n provider: \"auto\"\n \n # API configuration (falls back to OPENROUTER_API_KEY env var)\n # api_key: \"your-key-here\" # Uncomment to set here instead of .env\n base_url: \"https://openrouter.ai/api/v1\"\n\n # Azure Foundry keyless auth example:\n # provider: \"azure-foundry\"\n # base_url: \"https://<resource>.openai.azure.com/openai/v1\"\n # auth_mode: \"entra_id\" # DefaultAzureCredential: az login, managed identity, workload identity, etc.\n # default: \"gpt-4o\" # Deployment/model name\n # entra:\n # scope: \"https://ai.azure.com/.default\" # Optional; this is the default.\n\n # ── Token limits — two settings, easy to confuse ──────────────────────────\n #\n # context_length: TOTAL context window (input + output tokens combined).\n # Controls when Hermes compresses history and validates requests.\n # Leave unset — Hermes auto-detects the correct value from the provider.\n # Set manually only when auto-detection is wrong (e.g. a local server with\n # a custom num_ctx, or a proxy that doesn't expose /v1/models).\n #\n # context_length: 131072\n #\n # max_tokens: OUTPUT cap — maximum tokens the model may generate per response.\n # Unrelated to how long your conversation history can be.\n # The OpenAI-standard name \"max_tokens\" is a misnomer; Anthropic's native\n # API has since renamed it \"max_output_tokens\" for clarity.\n # Leave unset to use the model's native output ceiling (recommended).\n # Set only if you want to deliberately limit individual response length.\n #\n# max_tokens: 8192\n\n # ── Custom request headers (optional) ─────────────────────────────────────\n #\n # default_headers: extra HTTP headers sent on every request to an\n # OpenAI-compatible endpoint. User values take precedence over the\n # provider/SDK defaults, so this is the supported way to override the\n # OpenAI Python SDK's identifying headers (User-Agent: OpenAI/Python ...,\n # X-Stainless-*) when a custom provider sits behind a gateway/WAF that\n # rejects them — e.g. an upstream that returns \"502 Upstream access\n # forbidden\" for the SDK default User-Agent but accepts a plain one.\n # Applies on the OpenAI wire only (not native Anthropic / Bedrock).\n #\n # default_headers:\n # User-Agent: \"curl/8.7.1\"\n #\n # extra_headers: accepted as an alias of default_headers (merged, with\n # extra_headers winning when both are set) — matches the per-provider\n # extra_headers key below.\n #\n # Per-provider variant: named providers / custom_providers entries accept an\n # extra_headers dict scoped to that endpoint only — for reverse proxies,\n # gateways, or custom auth (e.g. Cloudflare Access service tokens).\n # Merged onto SDK/provider defaults with the entry's values winning.\n # Header values are treated as secrets and are never logged.\n #\n # providers:\n # my-proxy:\n # base_url: \"https://llm.internal.example.com/v1\"\n # key_env: \"MY_PROXY_API_KEY\"\n # extra_headers:\n # CF-Access-Client-Id: \"xxxx.access\"\n # CF-Access-Client-Secret: \"${CF_ACCESS_SECRET}\"\n # X-Client-Name: \"hermes-agent\"\n\n# Named provider overrides (optional)\n# Use this for per-provider request timeouts, non-stream stale timeouts,\n# and per-model exceptions.\n# Applies to the primary turn client on every api_mode (OpenAI-wire, native\n# Anthropic, and Anthropic-compatible providers), the fallback chain, and\n# client rebuilds during credential rotation. For OpenAI-wire chat\n# completions (streaming and non-streaming) the configured value is also\n# used as the per-request ``timeout=`` kwarg so it wins over the legacy\n# HERMES_API_TIMEOUT env var (which still applies when no config is set).\n# ``stale_timeout_seconds`` controls the non-streaming stale-call detector and\n# wins over the legacy HERMES_API_CALL_STALE_TIMEOUT env var. Leaving these\n# unset keeps the legacy defaults (HERMES_API_TIMEOUT=1800s,\n# HERMES_API_CALL_STALE_TIMEOUT=90s, native Anthropic 900s). The\n# implicit non-stream stale detector is auto-disabled for local endpoints\n# and can scale upward for very large contexts.\n#\n# Not currently wired for AWS Bedrock (bedrock_converse + AnthropicBedrock\n# SDK paths) — those use boto3 with its own timeout configuration.\n#\n# providers:\n# ollama-local:\n# request_timeout_seconds: 300 # Longer timeout for local cold-starts\n# stale_timeout_seconds: 900 # Explicitly re-enable stale detection on local endpoints\n# anthropic:\n# request_timeout_seconds: 30 # Fast-fail cloud requests\n# models:\n# claude-opus-4.6:\n# timeout_seconds: 600 # Longer timeout for extended-thinking Opus calls\n# openai-codex:\n# models:\n# gpt-5.4:\n# stale_timeout_seconds: 1800 # Longer non-stream stale timeout for slow large-context turns\n\n# =============================================================================\n# OpenRouter Provider Routing (only applies when using OpenRouter)\n# =============================================================================\n# Control how requests are routed across providers on OpenRouter.\n# See: https://openrouter.ai/docs/guides/routing/provider-selection\n#\n# provider_routing:\n# # Sort strategy: \"price\" (default), \"throughput\", or \"latency\"\n# # Append :nitro to model name for a shortcut to throughput sorting.\n# sort: \"throughput\"\n#\n# # Only allow these providers (provider slugs from OpenRouter)\n# # only: [\"anthropic\", \"google\"]\n#\n# # Skip these providers entirely\n# # ignore: [\"deepinfra\", \"fireworks\"]\n#\n# # Try providers in this order (overrides default load balancing)\n# # order: [\"anthropic\", \"google\", \"together\"]\n#\n# # Require providers to support all parameters in your request\n# # require_parameters: true\n#\n# # Data policy: \"allow\" (default) or \"deny\" to exclude providers that may store data\n# # data_collection: \"deny\"\n\n# =============================================================================\n# OpenRouter Response Caching (only applies when using OpenRouter)\n# =============================================================================\n# Cache identical API responses at the OpenRouter edge for free instant replays.\n# When enabled, identical requests (same model, messages, parameters) return\n# cached responses with zero billing. Separate from Anthropic prompt caching.\n# See: https://openrouter.ai/docs/guides/features/response-caching\n#\n# openrouter:\n# response_cache: true # Enable response caching (default: true)\n# response_cache_ttl: 300 # Cache TTL in seconds, 1-86400 (default: 300)\n\n# =============================================================================\n# Git Worktree Isolation\n# =============================================================================\n# When enabled, each CLI session creates an isolated git worktree so multiple\n# agents can work on the same repo concurrently without file collisions.\n# Equivalent to always passing --worktree / -w on the command line.\n#\n# worktree: true # Always create a worktree when in a git repo\n# worktree: false # Default — only create when -w flag is passed\n#\n# By default a new worktree branches from the freshly-fetched remote tip\n# (the current branch's upstream, else the remote's default branch) so it\n# starts current with the project instead of from the local clone's\n# (possibly stale) HEAD. Set worktree_sync: false to branch from local HEAD\n# instead — useful when offline or when you deliberately want the clone's\n# exact current state as the base.\n#\n# worktree_sync: true # Default — branch from the fetched remote tip\n# worktree_sync: false # Branch from local HEAD (offline / pinned base)\n\n# =============================================================================\n# Terminal Tool Configuration\n# =============================================================================\n# Choose ONE of the following terminal configurations by uncommenting it.\n# The terminal tool executes commands in the specified environment.\n\n# -----------------------------------------------------------------------------\n# OPTION 1: Local execution (default)\n# Commands run directly on your machine in the current directory\n# -----------------------------------------------------------------------------\n# Working directory behavior:\n# - CLI (`hermes` command): Uses \".\" (current directory where you run hermes)\n# - Gateway/messaging/cron: Uses terminal.cwd here; legacy .env cwd values are deprecated\nterminal:\n backend: \"local\"\n cwd: \".\" # For local backend: \".\" = current directory. Ignored for remote backends unless a backend documents otherwise.\n timeout: 180\n # HOME policy for tool subprocesses:\n # auto - default: host uses your real HOME; containers use HERMES_HOME/home\n # real - force your real OS-user HOME\n # profile - force HERMES_HOME/home for strict per-profile CLI config isolation\n home_mode: \"auto\"\n docker_mount_cwd_to_workspace: false # SECURITY: off by default. Opt in to mount the launch cwd into Docker /workspace.\n lifetime_seconds: 300\n # sudo_password: \"hunter2\" # Optional: pipe a sudo password via sudo -S. SECURITY WARNING: plaintext.\n # sudo_password: \"\" # Explicit empty password: try empty and never open the interactive sudo prompt.\n\n# -----------------------------------------------------------------------------\n# OPTION 2: SSH remote execution\n# Commands run on a remote server - agent code stays local (sandboxed)\n# Great for: keeping agent isolated from its own code, using powerful remote hardware\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"ssh\"\n# cwd: \"/home/myuser/project\" # Path on the REMOTE server\n# timeout: 180\n# lifetime_seconds: 300\n# ssh_host: \"my-server.example.com\"\n# ssh_user: \"myuser\"\n# ssh_port: 22\n# ssh_key: \"~/.ssh/id_rsa\" # Optional - uses ssh-agent if not specified\n\n# -----------------------------------------------------------------------------\n# OPTION 3: Docker container\n# Commands run in an isolated Docker container\n# Great for: reproducible environments, testing, isolation\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"docker\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /)\n# timeout: 180\n# lifetime_seconds: 300\n# docker_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# docker_mount_cwd_to_workspace: true # Explicit opt-in: mount your launch cwd into /workspace\n# # Optional: run the container as your host user's uid:gid so files written\n# # into bind-mounted dirs are owned by you, not root. Drops SETUID/SETGID\n# # caps too since no gosu privilege drop is needed. Leave off if your\n# # chosen docker_image expects to start as root.\n# docker_run_as_host_user: true\n# # Optional: explicitly forward selected env vars into Docker.\n# # These values come from your current shell first, then ~/.hermes/.env.\n# # Warning: anything forwarded here is visible to commands run in the container.\n# docker_forward_env:\n# - \"GITHUB_TOKEN\"\n# - \"NPM_TOKEN\"\n# # Optional: extra flags passed verbatim to docker run (appended after security defaults).\n# # Useful for adding capabilities (e.g. apt installs needing SETUID) or custom options.\n# # Example: add a Linux capability not included by default\n# # docker_extra_args:\n# # - \"--cap-add\"\n# # - \"SETUID\"\n\n# -----------------------------------------------------------------------------\n# OPTION 4: Singularity/Apptainer container\n# Commands run in a Singularity container (common in HPC environments)\n# Great for: HPC clusters, shared compute environments\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"singularity\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# singularity_image: \"docker://nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 5: Modal cloud execution\n# Commands run on Modal's cloud infrastructure\n# Great for: GPU access, scalable compute, serverless execution\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"modal\"\n# cwd: \"/workspace\" # Path INSIDE the sandbox (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# modal_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 6: Daytona cloud execution\n# Commands run in Daytona cloud sandboxes\n# Great for: Cloud dev environments, persistent workspaces, team collaboration\n# Requires: pip install daytona, DAYTONA_API_KEY env var\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"daytona\"\n# cwd: \"~\"\n# timeout: 180\n# lifetime_seconds: 300\n# daytona_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# container_disk: 10240 # Daytona max is 10GB per sandbox\n\n#\n# --- Container resource limits (docker, singularity, modal, daytona -- ignored for local/ssh) ---\n# These settings apply to all container backends. They control the resources\n# allocated to the sandbox and whether its filesystem persists across sessions.\n container_cpu: 1 # CPU cores\n container_memory: 5120 # Memory in MB (5120 = 5GB)\n container_disk: 51200 # Disk in MB (51200 = 50GB)\n container_persistent: true # Persist filesystem across sessions (false = ephemeral)\n\n# -----------------------------------------------------------------------------\n# SUDO SUPPORT (works with ALL backends above)\n# -----------------------------------------------------------------------------\n# Add sudo_password to any terminal config above to enable sudo commands.\n# The password is piped via `sudo -S`. Works with local, ssh, docker, etc.\n#\n# SECURITY WARNING: Password stored in plaintext!\n#\n# INTERACTIVE PROMPT: If sudo_password is unset and the CLI is running,\n# you'll be prompted to enter your password when sudo is needed:\n# - 45-second timeout (auto-skips if no input)\n# - Press Enter to skip (command fails gracefully)\n# - Password is hidden while typing\n# - Password is cached for the session\n#\n# EMPTY PASSWORDS: Setting sudo_password to an explicit empty string is different\n# from leaving it unset. Hermes will try an empty password via `sudo -S` and\n# will not open the interactive prompt. This is useful for passwordless sudo,\n# Touch ID sudo setups, and environments where prompting is just noise.\n#\n# ALTERNATIVES:\n# - SSH backend: Configure passwordless sudo on the remote server\n# - Containers: Run as root inside the container (no sudo needed)\n# - Local: Configure /etc/sudoers for specific commands\n#\n# Example (add to your terminal section):\n# sudo_password: \"your-password-here\"\n\n# =============================================================================\n# Security Scanning (tirith)\n# =============================================================================\n# Optional pre-exec command security scanning via tirith.\n# Detects homograph URLs, pipe-to-shell, terminal injection, env manipulation.\n# Install: brew install sheeki03/tap/tirith\n# Docs: https://github.com/sheeki03/tirith\n#\n# security:\n# tirith_enabled: true # Enable/disable tirith scanning\n# tirith_path: \"tirith\" # Path to tirith binary (supports ~ expansion)\n# tirith_timeout: 5 # Scan timeout in seconds\n# tirith_fail_open: true # Allow commands if tirith unavailable\n\n# =============================================================================\n# Browser Tool Configuration\n# =============================================================================\nbrowser:\n # Inactivity timeout in seconds - browser sessions are automatically closed\n # after this period of no activity between agent loops (default: 120 = 2 minutes)\n inactivity_timeout: 120\n\n# =============================================================================\n# Tool Loop Guardrails\n# =============================================================================\n# Soft warnings are enabled by default. They append guidance to repeated failed\n# or non-progressing tool results but still let the tool execute. Hard stops are\n# opt-in circuit breakers for autonomous/cron sessions where stopping a loop is\n# preferable to spending the full iteration budget.\ntool_loop_guardrails:\n warnings_enabled: true\n hard_stop_enabled: false\n warn_after:\n exact_failure: 2\n same_tool_failure: 3\n idempotent_no_progress: 2\n hard_stop_after:\n exact_failure: 5\n same_tool_failure: 8\n idempotent_no_progress: 5\n\n# =============================================================================\n# Context Compression (Auto-shrinks long conversations)\n# =============================================================================\n# When conversation approaches model's context limit, middle turns are\n# automatically summarized to free up space while preserving important context.\n#\n# HOW IT WORKS:\n# 1. Tracks actual token usage from API responses (not estimates)\n# 2. When prompt_tokens >= threshold% of model's context_length, triggers compression\n# 3. Protects first 3 turns (system prompt, initial request, first response)\n# 4. Protects last N turns (default 20 messages = ~10 full turns of recent context)\n# 5. Summarizes middle turns using a fast/cheap model\n# 6. Inserts summary as a user message, continues conversation seamlessly\n#\n# Post-compression tail budget is target_ratio × threshold × context_length:\n# 200K context, threshold 0.50, ratio 0.20 → 20K tokens of recent tail preserved\n# 1M context, threshold 0.50, ratio 0.20 → 100K tokens of recent tail preserved\n#\ncompression:\n # Enable automatic context compression (default: true)\n # Set to false if you prefer to manage context manually or want errors on overflow\n enabled: true\n\n # Opt-in compression progress notices on chat platforms (default: false).\n # By design, routine automatic compression is SILENT on human-facing chat\n # gateways (Telegram, Discord, Slack, ...) — it happens in the background\n # with server-side logging only. Set true to also deliver the routine\n # progress statuses (compacting started, preflight/pre-API compression,\n # idle compaction, retry progress, and the compaction-complete notice) to\n # chat platforms. Unrelated operational noise (auxiliary model failures,\n # provider retry chatter) stays suppressed either way, and compression\n # FAILURE notices + manual /compress feedback are always visible\n # regardless of this setting. (#52995)\n progress_notices: false\n \n # Trigger compression at this % of model's context limit (default: 0.50 = 50%)\n # Lower values = more aggressive compression, higher values = compress later\n # Models with context windows below 512K are floored at 0.75 (raise-only) so\n # compaction doesn't fire with half the window still free; set above 0.75 to override.\n threshold: 0.50\n\n # Per-model threshold overrides: keys are substring-matched against the model\n # name (longest match wins). Useful when some models need different compaction\n # points — e.g. a 1M-context model can compress later (0.30) while a 128K\n # model needs to compress earlier (0.60). The small-context floor (75% for\n # <512K models) still applies on top of per-model overrides.\n # model_thresholds:\n # \"glm-5.2\": 0.40\n # \"claude-sonnet\": 0.35\n # \"gpt-5\": 0.30\n\n # Optional absolute token cap for the compression trigger (default: null = disabled).\n # When set, compression fires at the LOWER of the ratio-based threshold and this\n # absolute token count — first-fires-wins. It never fires later than this count\n # regardless of which model is active (useful when switching between models with\n # very different context windows). Clamped to the model's context length at\n # apply-time, so a cap above the window is a no-op (ratio-based threshold wins).\n # Survives model switches and fallback activations.\n # threshold_tokens: 200000\n\n # Existing Codex gpt-5.5 behavior: raise Hermes' compaction trigger to 85%\n # for the ChatGPT Codex OAuth route. Set false to opt back down to threshold.\n codex_gpt55_autoraise: true\n\n # Fraction of the threshold to preserve as recent tail (default: 0.20 = 20%)\n # e.g. 20% of 50% threshold = 10% of total context kept as recent messages.\n # Summary output is separately capped at 12K tokens (Gemini output limit).\n # Range: 0.10 - 0.80\n target_ratio: 0.20\n\n # Number of most-recent messages to always preserve (default: 20 ≈ 10 full turns)\n # Higher values keep more recent conversation intact at the cost of more aggressive\n # compression of older turns.\n protect_last_n: 20\n\n # Minimum number of REAL (actionable) user messages guaranteed to survive in\n # the uncompressed tail (default: 1 = the existing single last-user anchor,\n # behavior-preserving). Raise to e.g. 3 to keep the last 3 real user turns\n # verbatim even when bulky tool outputs fill the tail token budget — blank\n # platform echoes, compaction handoffs, and synthetic continuation rows never\n # count toward N. The tail can exceed the token budget when this pulls the\n # cut back; the guarantee wins over the budget by design.\n min_tail_user_messages: 1\n\n # Compression retry rounds before a turn gives up with \"max compression\n # attempts reached\" (default: 3, same as the previous hardcoded value).\n # Raise (e.g. 6) for tool-schema-heavy sessions where 3 rounds cannot bring\n # the request estimate under the threshold. Validated >= 1, hard cap 10.\n max_attempts: 3\n\n # Codex app-server (codex CLI runtime) thread-compaction mode. The codex\n # agent owns the real thread context on this runtime, so Hermes' summarizer\n # cannot shrink it — compaction goes through the app server instead.\n # native = let Codex decide when to compact its own thread (default)\n # hermes = let Hermes threshold trigger Codex thread/compact/start\n # off = Hermes will not auto-trigger compaction; Codex may still compact natively\n codex_app_server_auto: native\n\n # Number of non-system messages to protect at the head of the transcript, in\n # ADDITION to the system prompt (which is always implicitly protected).\n # Head messages are NEVER summarized — they survive every compression\n # indefinitely. This gives stable early context for short/medium sessions,\n # but in long-running sessions that rely on rolling compaction the pinned\n # opening turns may not match how you want the session framed over time.\n # Set to 0 to preserve ONLY the system prompt (plus the rolling summary\n # and recent tail) — the cleanest configuration for long-running sessions.\n # Default 3 preserves the system prompt plus the first three non-system\n # head messages, matching the pre-feature behaviour.\n protect_first_n: 3\n\n # Idle compaction (default: 0 = disabled). When > 0, a session that resumes\n # after at least this many seconds of inactivity compacts its accumulated\n # history up front, before the first reply, so a long-lived thread you come\n # back to later doesn't re-read its full stale context on every turn.\n # Time-based, so it complements (does not replace) the size-based `threshold`\n # above. It is skipped when the context is already small (at or below the\n # post-compression target = threshold × target_ratio), so it never wastes a\n # summarization on a short idle thread. Example: 1800 = compact after 30 min idle.\n idle_compact_after_seconds: 0\n\n # Proactive tool-result prune (default: 0 = disabled). Opt-in token trigger\n # for a deterministic, no-LLM prune of OLD tool-result payloads, run\n # independently of `threshold` above. On large-window models (512K/1M) the\n # ratio threshold rarely fires, so bulky tool outputs (terminal dumps, file\n # reads, web extracts) ride along in history and get re-billed every turn.\n # When re-sent history exceeds this many tokens, the prune dedupes identical\n # results, summarizes older oversized ones, and truncates large tool-call\n # arguments — protecting the most recent `protect_last_n` messages and never\n # calling the model. Try 48000 to enable. Built-in compressor engine only;\n # other context engines inherit a safe no-op.\n # NOTE: a committed prune rewrites already-sent history, which invalidates\n # the provider's prompt-cache prefix — the min_reclaim gate below keeps\n # those cache breaks episodic (like a compression boundary) instead of\n # per-turn.\n proactive_prune_tokens: 0\n\n # The prune's summarize pass only touches tool results larger than this many\n # characters (clamped to >= 200 so a generated summary can't be\n # re-summarized). Default 8000.\n proactive_prune_min_result_chars: 8000\n\n # A proactive prune only COMMITS when it reclaims at least this many tokens\n # (measured on the pruned output). This is the prompt-cache hysteresis gate:\n # one meaningful, amortized cache break per batch of stale tool output\n # instead of a tiny break on every tool iteration. 0 = commit any non-zero\n # prune. Default 4096.\n proactive_prune_min_reclaim_tokens: 4096\n\n # To pin a specific model/provider for compression summaries, use the\n # auxiliary section below (auxiliary.compression.provider / model).\n\n# =============================================================================\n# Anthropic prompt caching TTL\n# =============================================================================\n# When prompt caching is active (Claude via OpenRouter or native Anthropic),\n# Anthropic supports two TTL tiers for cached prefixes: \"5m\" (default) and\n# \"1h\". Other values are ignored and \"5m\" is used.\n#\nprompt_caching:\n cache_ttl: \"5m\" # use \"1h\" for long sessions with pauses between turns\n\n# =============================================================================\n# Auxiliary Models (Advanced — Experimental)\n# =============================================================================\n# Hermes uses lightweight \"auxiliary\" models for side tasks: image analysis,\n# browser screenshot analysis, web page summarization, TTS audio-tag insertion,\n# and context compression.\n#\n# By default these use Gemini Flash via OpenRouter or Nous Portal and are\n# auto-detected from your credentials. You do NOT need to change anything\n# here for normal usage.\n#\n# WARNING: Overriding these with providers other than OpenRouter or Nous Portal\n# is EXPERIMENTAL and may not work. Not all models/providers support vision,\n# produce usable summaries, or accept the same API format. Change at your own\n# risk — if things break, reset to \"auto\" / empty values.\n#\n# Each task has its own provider + model pair so you can mix providers.\n# For example: OpenRouter for vision (needs multimodal), but your main\n# local endpoint for compression (just needs text).\n#\n# Provider options:\n# \"auto\" - Best available: OpenRouter → Nous Portal → main endpoint (default)\n# \"openrouter\" - Force OpenRouter (requires OPENROUTER_API_KEY)\n# \"nous\" - Force Nous Portal (requires: hermes auth add nous)\n# \"gemini\" - Force Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n# \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY)\n# \"codex\" - Force Codex OAuth (requires: hermes model → Codex).\n# Uses gpt-5.3-codex which supports vision.\n# \"main\" - Use your custom endpoint (OPENAI_BASE_URL + OPENAI_API_KEY).\n# Works with OpenAI API, local models, or any OpenAI-compatible\n# endpoint. Also falls back to Codex OAuth and API-key providers.\n#\n# Model: leave empty to use the provider's default. When empty, OpenRouter\n# uses \"google/gemini-3-flash-preview\" and Nous uses \"gemini-3-flash\".\n# Other providers pick a sensible default automatically.\n#\n# auxiliary:\n# # Image analysis: vision_analyze tool + browser screenshots\n# vision:\n# provider: \"auto\"\n# model: \"\" # e.g. \"google/gemini-2.5-flash\", \"openai/gpt-4o\"\n# timeout: 30 # LLM API call timeout (seconds)\n# download_timeout: 30 # Image HTTP download timeout (seconds)\n# # Increase for slow connections or self-hosted image servers\n# reasoning_effort: \"\" # Per-task thinking level: none, minimal, low, medium,\n# # high, xhigh, max, ultra. Empty = provider default.\n# # Works on every auxiliary task block (vision,\n# # web_extract, compression, title_generation, curator,\n# # background_review, moa_reference, ...). Example: run\n# # compression at \"low\" and vision at \"none\" to cut\n# # side-task latency/cost on reasoning models.\n#\n# # Web page scraping / summarization + browser page text extraction\n# web_extract:\n# provider: \"auto\"\n# model: \"\"\n# reasoning_effort: \"low\"\n#\n# # Gemini 3.1 TTS hidden audio-tag insertion\n# tts_audio_tags:\n# provider: \"auto\" # empty model = your main chat model\n# model: \"\"\n# timeout: 30\n#\n# # Automatic session title generation after the first exchange\n# title_generation:\n# enabled: true # set false to disable auto-title generation\n# provider: \"auto\"\n# model: \"\"\n# timeout: 30\n# language: \"\" # empty = match the user's language; or e.g. \"English\"\n#\n# # Session search — summarizes matching past sessions\n# session_search:\n# provider: \"auto\"\n# model: \"\"\n# timeout: 30\n# max_concurrency: 3 # Limit parallel summaries to reduce request-burst 429s\n# extra_body: {} # Provider-specific OpenAI-compatible request fields\n# # Example for providers that support request-body\n# # reasoning controls:\n# # extra_body:\n# # enable_thinking: false\n# # Some vLLM/Qwen deployments expect this nested:\n# # extra_body:\n# # chat_template_kwargs:\n# # enable_thinking: false\n\n# =============================================================================\n# Persistent Memory\n# =============================================================================\n# Bounded curated memory injected into the system prompt every session.\n# Two stores: MEMORY.md (agent's notes) and USER.md (user profile).\n# Character limits keep the memory small and focused. The agent manages\n# pruning -- when at the limit, it must consolidate or replace entries.\n# Disabled by default in batch_runner.\n#\nmemory:\n # Agent's personal notes: environment facts, conventions, things learned\n memory_enabled: true\n \n # User profile: preferences, communication style, expectations\n user_profile_enabled: true\n \n # Character limits (~2.75 chars per token, model-independent)\n memory_char_limit: 2200 # ~800 tokens\n user_char_limit: 1375 # ~500 tokens\n\n # Periodic memory nudge: remind the agent to consider saving memories\n # every N user turns. Set to 0 to disable. Only active when memory is enabled.\n nudge_interval: 10 # Nudge every 10 user turns (0 = disabled)\n\n # Memory flush: give the agent one turn to save memories before context is\n # lost (compression, /new, /reset, exit). Set to 0 to disable.\n # For exit/reset, only fires if the session had at least this many user turns.\n flush_min_turns: 6 # Min user turns to trigger flush on exit/reset (0 = disabled)\n\n# =============================================================================\n# Session Reset Policy (Messaging Platforms)\n# =============================================================================\n# Controls when messaging sessions (Telegram, Discord, WhatsApp, Slack) are\n# automatically cleared. Default is \"none\": sessions never auto-reset —\n# conversation context lives until you /reset or /new manually, or context\n# compression kicks in. Opt in to automatic resets if you prefer sessions to\n# clear on a schedule (long-lived context increases API cost per message,\n# though prompt caching and compression keep this manageable).\n#\n# When an automatic reset triggers, the agent first saves important\n# information to its persistent memory — but the conversation context is\n# wiped. The agent starts fresh but retains learned facts via its memory\n# system.\n#\n# Modes:\n# \"none\" - Never auto-reset (default); context lives until /reset or compression\n# \"idle\" - Reset after N minutes of inactivity\n# \"daily\" - Reset at a fixed hour each day\n# \"both\" - Reset on EITHER inactivity timeout or daily boundary\n#\nsession_reset:\n mode: none # \"none\", \"idle\", \"daily\", or \"both\"\n idle_minutes: 1440 # Inactivity timeout in minutes (used by \"idle\"/\"both\")\n at_hour: 4 # Daily reset hour, 0-23 local time (used by \"daily\"/\"both\")\n\n# Maximum number of simultaneously active chat sessions across CLI, TUI,\n# dashboard chat, and messaging gateway. Set to null, 0, or omit to allow\n# unlimited concurrent sessions. When the limit is reached, new sessions get a\n# clean error while existing active sessions keep their normal behavior. This\n# top-level key takes precedence over gateway.max_concurrent_sessions. The cap\n# is a best-effort single-host/profile runtime guard; Hermes fails open if the\n# local runtime lease registry cannot be read or locked.\nmax_concurrent_sessions: null\n\n# When true, group/channel chats use one session per participant when the platform\n# provides a user ID. This is the secure default and prevents users in the same\n# room from sharing context, interrupts, and token costs. Set false only if you\n# explicitly want one shared \"room brain\" per group/channel.\ngroup_sessions_per_user: true\n\n# ─────────────────────────────────────────────────────────────────────────────\n# API Server — per-client model routing\n# ─────────────────────────────────────────────────────────────────────────────\n# Route different API clients to different models/providers on a single\n# Hermes deployment. Clients choose a backend by sending a specific string\n# as the OpenAI ``model`` field. Unmapped model values fall back to the\n# global model configured in the ``model:`` section above, and an explicit\n# session /model override always wins over a route.\n#\n# Configure via the ``platforms.api_server.extra.model_routes`` gateway\n# config block:\n#\n# platforms:\n# api_server:\n# enabled: true\n# extra:\n# key: \"your-api-server-secret\"\n# model_routes:\n# # Xiaozhi clients send model=\"minimax-m2\" → routed to MiniMax via OpenRouter\n# minimax-m2:\n# model: \"minimax/minimax-m1\"\n# provider: \"openrouter\" # optional — overrides global provider\n# # api_key: \"sk-...\" # optional — per-route UPSTREAM provider\n# # key (NOT caller auth; never logged)\n# # base_url: \"https://...\" # optional — per-route base URL\n# # GPT clients keep their own alias\n# gpt-5:\n# model: \"openai/gpt-5\"\n# provider: \"openrouter\"\n#\n# Configured aliases are automatically listed by GET /v1/models so clients\n# can discover them without manual coordination. Caller authentication is\n# unchanged: every request still authenticates with the global API server\n# key (``extra.key`` / API_SERVER_KEY).\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Gateway Streaming\n# ─────────────────────────────────────────────────────────────────────────────\n# Stream tokens to messaging platforms in real-time. The bot sends a message\n# on first token, then progressively edits it as more tokens arrive.\n# Disabled by default — enable to try the streaming UX on Telegram/Discord/Slack.\n# For Telegram, partial edits are sent as plain text and only the final edit uses MarkdownV2.\nstreaming:\n enabled: false\n # transport: edit # \"edit\" = progressive editMessageText\n # edit_interval: 0.3 # seconds between message edits\n # buffer_threshold: 40 # chars before forcing an edit flush\n # cursor: \" ▉\" # cursor shown during streaming\n\n# =============================================================================\n# Skills Configuration\n# =============================================================================\n# Skills are reusable procedures the agent can load and follow. The agent can\n# also create new skills after completing complex tasks.\n#\nskills:\n # Nudge the agent to create skills after complex tasks.\n # Every N tool-calling iterations, remind the model to consider saving a skill.\n # Set to 0 to disable.\n creation_nudge_interval: 15\n\n # External skill directories — share skills across tools/agents without\n # copying them into ~/.hermes/skills/. Each path is expanded (~ and ${VAR})\n # and resolved to an absolute path. External dirs are read-only: skill\n # creation always writes to ~/.hermes/skills/. Local skills take precedence\n # when names collide.\n # external_dirs:\n # - ~/.agents/skills\n # - /home/shared/team-skills\n\n# =============================================================================\n# Agent Behavior\n# =============================================================================\nagent:\n # Maximum tool-calling iterations per conversation (default: 500)\n # Higher = more room for complex tasks, but costs more tokens\n # Recommended: 20-30 for focused tasks, 50-100 for open exploration\n max_turns: 500\n\n # Inactivity timeout for gateway agent runs (seconds, 0 = unlimited).\n # The agent can run indefinitely when actively calling tools or receiving\n # API responses. Only fires after the agent has been idle for this duration.\n # gateway_timeout: 1800\n\n # Staged warning: send a warning before escalating to full timeout.\n # Fires once per run when inactivity reaches this threshold (seconds).\n # Set to 0 to disable the warning.\n # gateway_timeout_warning: 900\n\n # Graceful drain timeout for gateway stop/restart (seconds).\n # Default 0 = no drain: a restart interrupts in-flight agents immediately,\n # cleans up, and exits. Set a positive value only if you want a grace\n # window on /restart, and keep it well under systemd's TimeoutStopSec.\n # restart_drain_timeout: 0\n\n # Upper bound (seconds) a submitted prompt waits for the deferred agent\n # build (MCP discovery, model metadata, skills scan) before failing with a\n # visible error. The wait is patient — the message is delivered as soon as\n # the build completes, and a progress notice is shown past 30s — so this cap\n # only fires on a genuinely hung build. Raise it for deployments with many\n # slow or unreachable MCP servers. Default 600.\n # build_wait_timeout: 600\n\n # Max app-level retry attempts for API errors (connection drops, provider\n # timeouts, 5xx, etc.) before the agent surfaces the failure. Lower this\n # to 1 if you use fallback providers and want fast failover on flaky\n # primaries (default 3). The OpenAI SDK does its own low-level retries\n # underneath this wrapper — this is the Hermes-level loop.\n # api_max_retries: 3\n\n # After the agent edits code without fresh passing verification, nudge it to\n # verify before finishing. The default \"auto\" enables it on interactive\n # coding surfaces (CLI, TUI, desktop) and programmatic callers, and disables\n # it on conversational messaging surfaces (Telegram, Discord, etc.) where the\n # verification summary would reach a human as chat noise. Set true or false to\n # force it on or off; the HERMES_VERIFY_ON_STOP env var (1/0) takes precedence.\n # verify_on_stop: auto\n\n # Standing operator instructions for the coding posture (when Hermes is in a\n # code workspace). Appended to the coding brief as an extra system block, so\n # you can pin project-wide workflow rules without editing the shipped brief.\n # Accepts a string or a list of strings. Takes effect next session.\n # coding_instructions:\n # - \"For UI work, don't run tsc/lint until I approve the look.\"\n # - \"Clean the diff before you commit and push.\"\n\n # When verify-on-stop finds edited code without fresh verification evidence,\n # append guidance for creative UI work (avoid broad tsc/lint/test before visual\n # approval) and clean-diff expectations. Set false to keep that nudge terse.\n # verify_guidance: true\n\n # A `pre_verify` hook (plugin or shell, see Event Hooks docs) can keep the\n # agent going one more turn to verify/clean before finishing. This caps how\n # many times one turn may be nudged to continue, so a hook can't trap the loop.\n # Default 3.\n # max_verify_nudges: 3\n\n # Enable verbose logging\n verbose: false\n \n # Reasoning effort level (OpenRouter and Nous Portal)\n # Controls how much \"thinking\" the model does before responding.\n # Options: \"xhigh\" (max), \"high\", \"medium\", \"low\", \"minimal\", \"none\" (disable)\n reasoning_effort: \"medium\"\n \n # Per-model reasoning effort overrides (optional dict)\n # Key: any sensible model spelling works (exact, dots↔dashes interchangeable,\n # provider prefix optional). First match wins.\n # Value: reasoning effort level (same options as reasoning_effort)\n # Override the global reasoning_effort for that specific model.\n # NOTE: no `hermes config set` support for this key -- edit YAML directly.\n # reasoning_overrides:\n # \"openrouter/anthropic/claude-opus-4.5\": \"xhigh\"\n # \"openai/gpt-5\": \"low\"\n # \"claude-opus-4.6\": \"high\" # bare model name also works\n # \"deepseek/deepseek-v4-pro\": \"xhigh\" # dots and dashes are interchangeable\n reasoning_overrides: {}\n \n # Predefined personalities (use with /personality command)\n personalities:\n helpful: \"You are a helpful, friendly AI assistant.\"\n concise: \"You are a concise assistant. Keep responses brief and to the point.\"\n technical: \"You are a technical expert. Provide detailed, accurate technical information.\"\n creative: \"You are a creative assistant. Think outside the box and offer innovative solutions.\"\n teacher: \"You are a patient teacher. Explain concepts clearly with examples.\"\n kawaii: \"You are a kawaii assistant! Use cute expressions like (◕‿◕), ★, ♪, and ~! Add sparkles and be super enthusiastic about everything! Every response should feel warm and adorable desu~! ヽ(>∀<☆)ノ\"\n catgirl: \"You are Neko-chan, an anime catgirl AI assistant, nya~! Add 'nya' and cat-like expressions to your speech. Use kaomoji like (=^・ω・^=) and ฅ^•ﻌ•^ฅ. Be playful and curious like a cat, nya~!\"\n pirate: \"Arrr! Ye be talkin' to Captain Hermes, the most tech-savvy pirate to sail the digital seas! Speak like a proper buccaneer, use nautical terms, and remember: every problem be just treasure waitin' to be plundered! Yo ho ho!\"\n shakespeare: \"Hark! Thou speakest with an assistant most versed in the bardic arts. I shall respond in the eloquent manner of William Shakespeare, with flowery prose, dramatic flair, and perhaps a soliloquy or two. What light through yonder terminal breaks?\"\n surfer: \"Duuude! You're chatting with the chillest AI on the web, bro! Everything's gonna be totally rad. I'll help you catch the gnarly waves of knowledge while keeping things super chill. Cowabunga! 🤙\"\n noir: \"The rain hammered against the terminal like regrets on a guilty conscience. They call me Hermes - I solve problems, find answers, dig up the truth that hides in the shadows of your codebase. In this city of silicon and secrets, everyone's got something to hide. What's your story, pal?\"\n uwu: \"hewwo! i'm your fwiendwy assistant uwu~ i wiww twy my best to hewp you! *nuzzles your code* OwO what's this? wet me take a wook! i pwomise to be vewy hewpful >w<\"\n philosopher: \"Greetings, seeker of wisdom. I am an assistant who contemplates the deeper meaning behind every query. Let us examine not just the 'how' but the 'why' of your questions. Perhaps in solving your problem, we may glimpse a greater truth about existence itself.\"\n hype: \"YOOO LET'S GOOOO!!! 🔥🔥🔥 I am SO PUMPED to help you today! Every question is AMAZING and we're gonna CRUSH IT together! This is gonna be LEGENDARY! ARE YOU READY?! LET'S DO THIS! 💪😤🚀\"\n\n# =============================================================================\n# Toolsets\n# =============================================================================\n# Control which tools the agent has access to.\n# Use `hermes tools` to interactively enable/disable tools per platform.\n\n# =============================================================================\n# Platform Toolsets (per-platform tool configuration)\n# =============================================================================\n# Override which toolsets are available on each platform.\n# If a platform isn't listed here, its built-in default is used.\n#\n# You can use EITHER:\n# - A preset like \"hermes-cli\" or \"hermes-telegram\" (curated tool set)\n# - A list of individual toolsets to compose your own (see list below)\n#\n# Supported platform keys: cli, telegram, discord, whatsapp, slack, qqbot, teams, google_chat\n#\n# Examples:\n#\n# # Use presets (same as defaults):\n# platform_toolsets:\n# cli: [hermes-cli]\n# telegram: [hermes-telegram]\n#\n# # Custom: give Telegram only web + terminal + file + planning:\n# platform_toolsets:\n# telegram: [web, terminal, file, todo]\n#\n# # Custom: CLI without browser or image gen:\n# platform_toolsets:\n# cli: [web, terminal, file, skills, todo, tts, cronjob]\n#\n# # Restrictive: Discord gets read-only tools only:\n# platform_toolsets:\n# discord: [web, vision, skills, todo]\n#\n# If not set, defaults are:\n# cli: hermes-cli (everything + cronjob management)\n# telegram: hermes-telegram (terminal, file, web, vision, image, tts, browser, skills, todo, cronjob, messaging)\n# discord: hermes-discord (same as telegram)\n# whatsapp: hermes-whatsapp (same as telegram)\n# slack: hermes-slack (same as telegram)\n# signal: hermes-signal (same as telegram)\n# homeassistant: hermes-homeassistant (same as telegram)\n# qqbot: hermes-qqbot (same as telegram)\n# teams: hermes-teams (same as telegram)\n# google_chat: hermes-google_chat (same as telegram)\n#\nplatform_toolsets:\n cli: [hermes-cli]\n telegram: [hermes-telegram]\n discord: [hermes-discord]\n whatsapp: [hermes-whatsapp]\n slack: [hermes-slack]\n signal: [hermes-signal]\n homeassistant: [hermes-homeassistant]\n qqbot: [hermes-qqbot]\n yuanbao: [hermes-yuanbao]\n teams: [hermes-teams]\n google_chat: [hermes-google_chat]\n\n# =============================================================================\n# Gateway Platform Settings\n# =============================================================================\n# Optional per-platform messaging settings.\n# Platform-specific knobs live under `extra`.\n#\n# platforms:\n# telegram:\n# reply_to_mode: \"first\" # off | first | all\n# # guest_mode lets explicit @mentions from non-allowlisted groups through.\n# # Default false; ordinary messages, replies, and regex wake words stay blocked.\n# guest_mode: false\n# # allowed_chats: [\"-1001234567890\"]\n# extra:\n# disable_link_previews: false # Set true to suppress Telegram URL previews in bot messages\n# 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\n# rich_drafts: false # Experimental rich draft previews during Telegram DM streaming; default false because Telegram Desktop/macOS can visually overlay draft frames\n# command_menu:\n# # Telegram allows up to 100 BotCommands; Hermes defaults to 60 so\n# # all built-in commands plus common skill commands stay visible\n# # while remaining under Telegram's payload-size limit. Clamped 1..100.\n# max_commands: 60\n# # prepend = user priority first, then Hermes defaults\n# # append = Hermes defaults first, then user priority\n# # replace = only the list below defines priority\n# priority_mode: prepend\n# priority:\n# - my_plugin_command\n# webhook:\n# extra:\n# # Route scripts default to a 30 second timeout. Scripts must live under\n# # the active profile's scripts directory and receive webhook JSON on stdin.\n# script_timeout_seconds: 30\n#\n# Discord-specific settings (config.yaml top-level, not under platforms:):\n#\n# discord:\n# require_mention: true # Require @mention in server channels (default: true)\n# auto_thread: true # Auto-create thread on @mention (default: true)\n# free_response_channels: \"\" # Channel IDs where no mention is needed\n# reactions: true # Show processing reactions (default: true)\n# history_backfill: true # Recover missed channel messages on mention (default: true)\n# history_backfill_limit: 50 # Max messages to scan backwards (default: 50)\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Available toolsets (use these names in platform_toolsets or the toolsets list)\n#\n# Run `hermes chat --list-toolsets` to see all toolsets and their tools.\n# Run `hermes chat --list-tools` to see every individual tool with descriptions.\n# ─────────────────────────────────────────────────────────────────────────────\n#\n# INDIVIDUAL TOOLSETS (compose your own):\n# web - web_search, web_extract\n# search - web_search only (no scraping)\n# terminal - terminal, process\n# file - read_file, write_file, patch, search\n# browser - browser_navigate, browser_snapshot, browser_click, browser_type,\n# browser_scroll, browser_back, browser_press,\n# browser_get_images, browser_vision (requires BROWSERBASE_API_KEY)\n# vision - vision_analyze (requires OPENROUTER_API_KEY)\n# image_gen - image_generate (requires FAL_KEY)\n# skills - skills_list, skill_view\n# skills_hub - skill_hub (search/install/manage from online registries — user-driven only)\n# todo - todo (in-memory task planning, no deps)\n# tts - text_to_speech (Edge TTS free, or ELEVENLABS/OPENAI/MINIMAX/MISTRAL key)\n# cronjob - cronjob (create/list/update/pause/resume/run/remove scheduled tasks)\n#\n# PRESETS (curated bundles):\n# hermes-cli - All of the above except send_message\n# hermes-telegram - terminal, file, web, vision, image_gen, tts, browser,\n# skills, todo, cronjob, send_message\n# hermes-discord - Same as hermes-telegram\n# hermes-whatsapp - Same as hermes-telegram\n# hermes-slack - Same as hermes-telegram\n#\n# COMPOSITE:\n# debugging - terminal + web + file\n# safe - web + vision (no terminal access)\n# all - Everything available\n#\n# web - Web search and content extraction (web_search, web_extract)\n# search - Web search only, no scraping (web_search)\n# terminal - Command execution and process management (terminal, process)\n# file - File operations: read, write, patch, search\n# browser - Full browser automation (navigate, click, type, screenshot, etc.)\n# vision - Image analysis (vision_analyze)\n# image_gen - Image generation with FLUX (image_generate)\n# skills - Load skill documents (skills_list, skill_view)\n# todo - Task planning and tracking for multi-step work\n# memory - Persistent memory across sessions (personal notes + user profile)\n# session_search - Search and recall past conversations (FTS5 + Gemini Flash summarization)\n# tts - Text-to-speech (Edge TTS free, ElevenLabs, OpenAI, MiniMax, Mistral)\n# cronjob - Schedule and manage automated tasks (CLI-only)\n#\n# Composite toolsets:\n# debugging - terminal + web + file (for troubleshooting)\n# safe - web + vision (no terminal access)\n\n# NOTE: The top-level \"toolsets\" key is deprecated and ignored.\n# Tool configuration is managed per-platform via platform_toolsets above.\n# Use `hermes tools` to configure interactively, or edit platform_toolsets directly.\n#\n# CLI override: hermes chat --toolsets terminal,web,file\n\n# =============================================================================\n# MCP (Model Context Protocol) Servers\n# =============================================================================\n# Connect to external MCP servers to add tools from the MCP ecosystem.\n# Each server's tools are automatically discovered and registered.\n# See website/docs/user-guide/features/mcp.md for full documentation.\n#\n# Stdio servers (spawn a subprocess):\n# command: the executable to run\n# args: command-line arguments\n# env: environment variables (only these + safe defaults passed to subprocess)\n#\n# HTTP servers (connect to a URL):\n# url: the MCP server endpoint\n# headers: HTTP headers (e.g., for authentication)\n#\n# Optional per-server settings:\n# timeout: tool call timeout in seconds (default: 120)\n# connect_timeout: initial connection timeout (default: 60)\n# keepalive_interval: liveness ping cadence in seconds (default: 180).\n# Lower it below the server's session TTL for servers that expire idle\n# sessions quickly (e.g. Unreal Engine editor MCP, ~15s), otherwise idle\n# tool calls hit an expired session and pay a slow reconnect. Floored at 5s.\n#\n# mcp_servers:\n# time:\n# command: uvx\n# args: [\"mcp-server-time\"]\n# filesystem:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-filesystem\", \"/home/user\"]\n# notion:\n# url: https://mcp.notion.com/mcp\n# github:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-github\"]\n# env:\n# GITHUB_PERSONAL_ACCESS_TOKEN: \"ghp_...\"\n#\n# Sampling (server-initiated LLM requests) — enabled by default.\n# Per-server config under the 'sampling' key:\n# analysis:\n# command: npx\n# args: [\"-y\", \"analysis-server\"]\n# sampling:\n# enabled: true # default: true\n# model: \"gemini-3-flash\" # override model (optional)\n# max_tokens_cap: 4096 # max tokens per request\n# timeout: 30 # LLM call timeout (seconds)\n# max_rpm: 10 # max requests per minute\n# allowed_models: [] # model whitelist (empty = all)\n# max_tool_rounds: 5 # tool loop limit (0 = disable)\n# log_level: \"info\" # audit verbosity\n\n# =============================================================================\n# Text-to-Speech\n# =============================================================================\n# TTS defaults to Edge TTS unless changed in ~/.hermes/config.yaml.\n# Gemini TTS supports persona/director prompt files, and Gemini 3.1 Flash TTS\n# can use a hidden auxiliary rewrite pass to insert expressive square-bracket\n# audio tags into the TTS script without showing tags in chat.\n#\n# tts:\n# provider: \"gemini\"\n# speed: 1.0 # global speed multiplier (provider-specific overrides this)\n# gemini:\n# model: \"gemini-3.1-flash-tts-preview\"\n# voice: \"Kore\"\n# audio_tags: false\n# persona_prompt_file: \"\" # e.g. ~/.hermes/tts/radio-host.md\n# xai:\n# voice_id: \"eve\" # built-in or custom voice ID from xAI Console\n# language: \"en\" # BCP-47 code (\"en\", \"pt-BR\") or \"auto\"\n# speed: 1.0 # 0.7-1.5 playback speed\n# auto_speech_tags: false # insert expressive audio tags via LLM rewrite\n# text_normalization: false # normalize numbers/abbreviations/symbols\n# optimize_streaming_latency: 0 # 0-2, trades quality for lower latency\n# sample_rate: 24000 # 22050 / 24000 / 44100 / 48000\n# bit_rate: 128000 # MP3 bitrate (codec=mp3 only)\n\n# =============================================================================\n# Voice Transcription (Speech-to-Text)\n# =============================================================================\n# Automatically transcribe voice messages on messaging platforms.\n# Providers: local (free, faster-whisper) | groq (free tier) | openai (Whisper API) | mistral (Voxtral Transcribe)\n# Set the corresponding API key in .env: GROQ_API_KEY, OPENAI_API_KEY, or MISTRAL_API_KEY.\nstt:\n enabled: true\n # provider: \"local\" # auto-detected if omitted\n local:\n model: \"base\" # tiny | base | small | medium | large-v3 | turbo\n # language: \"\" # auto-detect; set to \"en\", \"es\", \"fr\", etc. to force\n # initial_prompt: \"\" # Optional faster-whisper prompt, e.g. bias Chinese output to simplified Chinese\n language: \"en\" # GLOBAL language hint for every STT provider (per-provider language wins). Set \"\" for auto-detect.\n # groq:\n # model: \"whisper-large-v3-turbo\"\n # language: \"\" # blank = stt.language > HERMES_LOCAL_STT_LANGUAGE > auto-detect\n openai:\n model: \"whisper-1\" # whisper-1 | gpt-4o-mini-transcribe | gpt-4o-transcribe\n language: \"\" # auto-detect; set to \"en\", \"es\", \"fr\", etc. to force\n # mistral:\n # model: \"voxtral-mini-latest\" # voxtral-mini-latest | voxtral-mini-2602\n # deepinfra:\n # # Model id is discovered live from the DeepInfra catalog filtered\n # # by the `stt` surface tag — leave `model` blank to take the first\n # # live result. Pin only when you need a specific Whisper variant.\n # model: \"\"\n\n# Text-to-speech. Only the deepinfra block is documented here — the\n# remaining providers (edge, openai, xai, minimax, mistral, gemini,\n# elevenlabs, neutts, kittentts, piper) inherit sensible defaults from\n# DEFAULT_CONFIG in hermes_cli/config.py.\n# tts:\n# provider: \"deepinfra\"\n# deepinfra:\n# # Model id is discovered live from the DeepInfra catalog filtered\n# # by the `tts` surface tag — leave `model` blank to take the first\n# # live result.\n# model: \"\"\n# voice: \"default\"\n\n# Image generation. Each provider plugin reads its own ``image_gen.<name>``\n# block; deepinfra discovers models live from\n# api.deepinfra.com/v1/openai/models filtered by the ``image-gen`` tag —\n# no model id is hardcoded, so retired models disappear automatically.\n# image_gen:\n# provider: \"deepinfra\"\n# deepinfra:\n# # Leave `model` blank for the first live `image-gen`-tagged result.\n# model: \"\"\n\n# =============================================================================\n# Response Pacing (Messaging Platforms)\n# =============================================================================\n# Add human-like delays between message chunks.\n# human_delay:\n# mode: \"off\" # \"off\" | \"natural\" | \"custom\"\n# min_ms: 800 # Min delay (custom mode only)\n# max_ms: 2500 # Max delay (custom mode only)\n\n# =============================================================================\n# Session Logging\n# =============================================================================\n# Session trajectories are automatically saved to logs/ directory.\n# Each session creates: logs/session_YYYYMMDD_HHMMSS_UUID.json\n#\n# The session ID is displayed in the welcome banner for easy reference.\n# Logs contain full conversation history in trajectory format:\n# - System prompt, user messages, assistant responses\n# - Tool calls with inputs/outputs\n# - Timestamps for debugging\n#\n# No configuration needed - logging is always enabled.\n# To disable, you would need to modify the source code.\n\n# =============================================================================\n# Code Execution Sandbox (Programmatic Tool Calling)\n# =============================================================================\n# The execute_code tool runs Python scripts that call Hermes tools via RPC.\n# Intermediate tool results stay out of the LLM's context window.\ncode_execution:\n timeout: 300 # Max seconds per script before kill (default: 300 = 5 min)\n max_tool_calls: 50 # Max RPC tool calls per execution (default: 50)\n\n# =============================================================================\n# Subagent Delegation\n# =============================================================================\n# The delegate_task tool spawns child agents with isolated context.\n# Supports single tasks and batch mode (default 3 parallel, configurable).\ndelegation:\n max_iterations: 50 # Max tool-calling turns per child (default: 50)\n # max_concurrent_children: 3 # Max parallel child agents per batch (default: 3, floor: 1, no ceiling).\n # WARNING: values above 10 multiply API cost linearly.\n # max_spawn_depth: 1 # Delegation tree depth cap (range: 1-3, default: 1 = flat).\n # Raise to 2 to allow workers to spawn their own subagents.\n # Requires role=\"orchestrator\" on intermediate agents.\n # orchestrator_enabled: true # Kill switch for role=\"orchestrator\" children (default: true).\n # subagent_auto_approve: false # When a subagent hits a dangerous-command approval prompt, auto-deny (default: false)\n # or auto-approve \"once\" (true) instead of blocking on stdin.\n # The parent TUI owns stdin, so blocking would deadlock; non-interactive resolution is required.\n # Both choices emit a logger.warning audit line. Flip to true only for cron/batch pipelines.\n # inherit_mcp_toolsets: true # When explicit child toolsets are narrowed, also keep the parent's MCP toolsets (default: true). Set false for strict intersection.\n # model: \"google/gemini-3-flash-preview\" # Override model for subagents (empty = inherit parent)\n # provider: \"openrouter\" # Override provider for subagents (empty = inherit parent)\n # # Resolves full credentials (base_url, api_key) automatically.\n # # Supported: openrouter, nous, zai, kimi-coding, minimax\n\n# =============================================================================\n# Honcho Integration (Cross-Session User Modeling)\n# =============================================================================\n# AI-native persistent memory via Honcho (https://honcho.dev/).\n# Builds a deeper understanding of the user across sessions and tools.\n# Runs alongside USER.md — additive, not a replacement.\n#\n# Requires: pip install honcho-ai\n# Config: ~/.honcho/config.json (shared with Claude Code, Cursor, etc.)\n# API key: HONCHO_API_KEY in ~/.hermes/.env or ~/.honcho/config.json\n#\n# Hermes-specific overrides (optional — most config comes from ~/.honcho/config.json):\n# honcho: {}\n\n# =============================================================================\n# Display\n# =============================================================================\ndisplay:\n # Use compact banner mode (hides the ASCII-art banner, shows a single line).\n # true: Compact single-line banner\n # false: Full ASCII banner with tool/skill summary (default)\n compact: false\n\n # Tool progress display level (CLI and gateway)\n # off: Silent — no tool activity shown, just the final response\n # new: Show a tool indicator only when the tool changes (skip repeats)\n # all: Show every tool call with a short preview (default)\n # verbose: Full args, results, and debug logs (same as /verbose)\n # log: Silent in chat; append every tool call to ~/.hermes/logs/tool_calls.log (gateway only)\n # Toggle at runtime with /verbose in the CLI\n tool_progress: all\n\n # Per-platform defaults can be quieter than the global setting. Telegram\n # tunes for mobile: tool_progress and busy_ack_detail default off (no\n # per-tool breadcrumb stream, no \"iteration 21/60\" debug detail in busy\n # acks or heartbeats), but interim_assistant_messages and\n # long_running_notifications STAY ON so the user has real signal between\n # turn start and final answer (mid-turn assistant commentary + a single\n # edit-in-place \"⏳ Working — N min\" heartbeat). Override under\n # display.platforms.telegram.\n\n # Auto-cleanup of temporary progress bubbles after the final response lands.\n # On platforms that support message deletion (currently Telegram), this\n # removes the tool-progress bubble, \"⏳ Still working...\" notices, and\n # context-pressure status messages once the final reply has been delivered —\n # keeping long-running turns visible live, then tidy afterward. Failed runs\n # leave the bubbles in place as breadcrumbs. Off by default.\n # Per-platform override: display.platforms.telegram.cleanup_progress\n # true: Delete tracked progress/status bubbles on successful turn\n # false: Leave everything in place (default)\n # Example:\n # display:\n # platforms:\n # telegram:\n # cleanup_progress: true\n cleanup_progress: false\n\n # Natural mid-turn assistant updates.\n # On gateway platforms, when true, completed assistant status messages are\n # sent as separate chat messages. On the Desktop app, when true, mid-turn\n # assistant narration streamed between tool calls is kept in the transcript\n # instead of the bubble collapsing to only the final message on completion.\n # Independent of tool_progress and gateway streaming.\n # true: Keep/send mid-turn assistant updates (default)\n # false: Only keep/send the final response\n interim_assistant_messages: true\n\n # Gateway-only long-running status heartbeats.\n # When false, the platform does not receive periodic \"⏳ Working — N min\"\n # notifications even if agent.gateway_notify_interval is non-zero. The\n # heartbeat edits a single message in place (where the adapter supports\n # editing) instead of posting a new bubble each interval.\n # Default: true everywhere, including Telegram (silent agents are worse\n # than a single edit-in-place heartbeat).\n long_running_notifications: true\n\n # Include detailed iteration/tool/status context in busy acknowledgments\n # and long-running heartbeats. When true, busy acks show \"iteration 21/60,\n # terminal, 10 min\" and the heartbeat shows \"⏳ Working — 12 min,\n # iteration 21/60, terminal\". When false (Telegram default), both stay\n # terse: \"Interrupting current task\" and \"⏳ Working — 12 min, terminal\".\n busy_ack_detail: true\n\n # What Enter does when Hermes is already busy (CLI and gateway platforms).\n # interrupt: Interrupt the current run and redirect Hermes (default)\n # queue: Queue your message for the next turn\n # steer: Inject your message mid-run via /steer, arriving at the agent\n # after the next tool call — no interrupt, no role violation.\n # Falls back to 'queue' if the agent isn't running yet or if\n # images are attached (steer only carries text).\n # Ctrl+C (or /stop in gateway) always interrupts regardless of this setting.\n # Toggle at runtime with /busy <interrupt|queue|steer>.\n busy_input_mode: interrupt\n\n # Background process notifications (gateway/messaging only).\n # Controls how chatty the process watcher is when you use\n # terminal(background=true, notify_on_complete=true) from Telegram/Discord/etc.\n # off: No watcher messages at all\n # result: Only the final completion message\n # error: Only the final message when exit code != 0\n # all: Running output updates + final message (default)\n background_process_notifications: all\n\n\n # Play terminal bell when agent finishes a response.\n # Useful for long-running tasks — your terminal will ding when the agent is done.\n # Works over SSH. Most terminals can be configured to flash the taskbar or play a sound.\n # true: Ring the terminal bell on each response\n # false: Silent (default)\n bell_on_complete: false\n\n # Show model reasoning/thinking before each response.\n # When enabled, a dim box shows the model's thought process above the response.\n # Toggle at runtime with /reasoning show or /reasoning hide.\n # true: Show the reasoning box\n # false: Hide reasoning (default)\n show_reasoning: false\n\n # Stream tokens to the terminal as they arrive instead of waiting for the\n # full response. The response box opens on first token and text appears\n # line-by-line. Tool calls are still captured silently.\n # true: Stream tokens as they arrive (default)\n # false: Wait for the full response before rendering\n streaming: true\n\n # Show [HH:MM] timestamps on user input and assistant response labels.\n # timestamps: false\n\n # ───────────────────────────────────────────────────────────────────────────\n # Skin / Theme\n # ───────────────────────────────────────────────────────────────────────────\n # Customize CLI visual appearance — banner colors, spinner faces, tool prefix,\n # response box label, and branding text. Change at runtime with /skin <name>.\n #\n # Built-in skins:\n # default — Classic Hermes gold/kawaii\n # ares — Crimson/bronze war-god theme with spinner wings\n # mono — Clean grayscale monochrome\n # slate — Cool blue developer-focused\n # daylight — Bright light-mode theme\n # warm-lightmode — Warm paper-tone light-mode theme\n # poseidon — Sea-green/teal Olympian theme\n # sisyphus — Earthy stone-and-moss theme\n # charizard — Fiery orange dragon theme\n #\n # Custom skins: drop a YAML file in ~/.hermes/skins/<name>.yaml\n # Schema (all fields optional, missing values inherit from default):\n #\n # name: my-theme\n # description: Short description\n # colors:\n # banner_border: \"#HEX\" # Panel border\n # banner_title: \"#HEX\" # Panel title\n # banner_accent: \"#HEX\" # Section headers (Available Tools, etc.)\n # banner_dim: \"#HEX\" # Dim/muted text\n # banner_text: \"#HEX\" # Body text (tool names, skill names)\n # ui_accent: \"#HEX\" # UI accent color\n # response_border: \"#HEX\" # Response box border color\n # spinner:\n # waiting_faces: [\"(⚔)\", \"(⛨)\"] # Faces shown while waiting\n # thinking_faces: [\"(⚔)\", \"(⌁)\"] # Faces shown while thinking\n # thinking_verbs: [\"forging\", \"plotting\"] # Verbs for spinner messages\n # wings: # Optional left/right spinner decorations\n # - [\"⟪⚔\", \"⚔⟫\"]\n # - [\"⟪▲\", \"▲⟫\"]\n # branding:\n # agent_name: \"My Agent\" # Banner title and branding\n # welcome: \"Welcome message\" # Shown at CLI startup\n # response_label: \" ⚔ Agent \" # Response box header label\n # prompt_symbol: \"⚔\" # Prompt symbol (bare token; renderers add trailing space)\n # tool_prefix: \"╎\" # Tool output line prefix (default: ┊)\n #\n skin: default\n\n# =============================================================================\n# Model Aliases — short names for /model command\n# =============================================================================\n# Map short aliases to exact (model, provider, base_url) tuples.\n# Used by /model tab completion and resolve_alias().\n# Aliases are checked BEFORE the models.dev catalog, so they can route\n# to endpoints not in the catalog (e.g. Ollama Cloud, local servers).\n#\n# model_aliases:\n# opus:\n# model: claude-opus-4-6\n# provider: anthropic\n# qwen:\n# model: \"qwen3.5:397b\"\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n# glm:\n# model: glm-4.7\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n\n# =============================================================================\n# Privacy\n# =============================================================================\n# privacy:\n# # Redact PII from the LLM context prompt.\n# # When true, phone numbers are stripped and user/chat IDs are replaced\n# # with deterministic hashes before being sent to the model.\n# # Names and usernames are NOT affected (user-chosen, publicly visible).\n# # Routing/delivery still uses the original values internally.\n# redact_pii: false\n\n# =============================================================================\n# Shell-script hooks\n# =============================================================================\n# Register shell scripts as plugin-hook callbacks. Each entry is executed as\n# a subprocess (shell=False, shlex.split) with a JSON payload on stdin. On\n# stdout the script may return JSON that either blocks the tool call or\n# injects context into the next LLM call.\n#\n# Valid events (mirror hermes_cli.plugins.VALID_HOOKS):\n# pre_tool_call, post_tool_call, pre_llm_call, post_llm_call,\n# pre_api_request, post_api_request, on_session_start, on_session_end,\n# on_session_finalize, on_session_reset, subagent_stop\n#\n# First-use consent: each (event, command) pair prompts once on a TTY, then\n# is persisted to ~/.hermes/shell-hooks-allowlist.json. Non-interactive\n# runs (gateway, cron) need --accept-hooks, HERMES_ACCEPT_HOOKS=1, or the\n# hooks_auto_accept key below.\n#\n# See website/docs/user-guide/features/hooks.md for the full JSON wire\n# protocol and worked examples.\n#\n# hooks:\n# pre_tool_call:\n# - matcher: \"terminal\"\n# command: \"~/.hermes/agent-hooks/block-rm-rf.sh\"\n# timeout: 10\n# post_tool_call:\n# - matcher: \"write_file|patch\"\n# command: \"~/.hermes/agent-hooks/auto-format.sh\"\n# pre_llm_call:\n# - command: \"~/.hermes/agent-hooks/inject-cwd-context.sh\"\n# subagent_stop:\n# - command: \"~/.hermes/agent-hooks/log-orchestration.sh\"\n#\n# hooks_auto_accept: false\n\n\n# =============================================================================\n# Telemetry\n# =============================================================================\n# Shared metrics are disabled by default. When enabled, Hermes writes only\n# allowlisted aggregate counters and immutable JSON\n# packages under $HERMES_HOME/telemetry/shared_metrics; it does not upload them.\n# Packages include a random profile-scoped ID that stays stable until this\n# directory is deleted. It is not derived from hardware, account, or host data.\n# Successfully exported local history is retained for 30 days; pending deltas\n# are retained until they can be exported.\n# This profile-owned choice is not overridden by managed-scope configuration.\ntelemetry:\n shared_metrics:\n enabled: false\n\n\n# =============================================================================\n# Update Behavior\n# =============================================================================\nupdates:\n # Create a full HERMES_HOME zip before every `hermes update`.\n # Backups land in ~/.hermes/backups/ and can be restored with `hermes import`.\n # Off by default because large homes can add minutes to every update.\n pre_update_backup: false\n\n # Number of pre-update backup zips to retain.\n backup_keep: 5\n\n # What non-interactive updates do with local source edits in the Hermes repo.\n # Interactive terminal updates always prompt before restoring the autostash.\n #\n # stash - auto-stash before pull, then auto-restore after success (default)\n # discard - drop the update-created stash after success; use only on managed\n # installs where local source edits should not persist\n non_interactive_local_changes: \"stash\"\n\n\n# =============================================================================\n# Web Dashboard\n# =============================================================================\n# OAuth gate configuration for `hermes dashboard --host <non-loopback>`.\n# The bundled Nous Portal plugin reads these on startup; settings here are\n# the canonical surface. Each can be overridden by an environment variable:\n#\n# dashboard.oauth.client_id <- HERMES_DASHBOARD_OAUTH_CLIENT_ID\n# dashboard.oauth.portal_url <- HERMES_DASHBOARD_PORTAL_URL\n# dashboard.public_url <- HERMES_DASHBOARD_PUBLIC_URL\n#\n# Env wins when set to a non-empty value. This is what Fly.io's platform-\n# secret injection uses to push per-deploy client_ids without needing to\n# bake a config.yaml into the image. Empty env values are treated as unset\n# so a provisioned-but-not-populated secret can't shadow a valid entry here.\n#\n# Local dev / on-prem deploys should typically set these via config.yaml\n# (the ~/.hermes/.env file is reserved for API keys and secrets).\n#\n# dashboard:\n# oauth:\n# client_id: \"\" # agent:{instance_id}; Portal provisions this at deploy\n# portal_url: \"\" # blank → default https://portal.nousresearch.com\n#\n# # Force the absolute base URL the OAuth callback (and any other public\n# # URL the dashboard hands to external systems) is built from. Set this\n# # for deploys behind reverse proxies that don't reliably forward\n# # X-Forwarded-Host / X-Forwarded-Proto / X-Forwarded-Prefix (manual\n# # nginx setups, on-prem ingresses, custom-domain Fly deploys without\n# # full proxy header chains).\n# #\n# # When set, the value is the complete authority: scheme + host +\n# # optional path prefix (e.g. \"https://example.com/hermes\"). The OAuth\n# # callback URL becomes \"<public_url>/auth/callback\" — X-Forwarded-Prefix\n# # is IGNORED on this code path because the operator has explicitly\n# # declared the public URL and we no longer need to guess.\n# #\n# # Leave empty to use the existing proxy-header reconstruction (the\n# # default — works on Fly.io out of the box).\n# #\n# # public_url: \"https://example.com/hermes\"\n#\n# -----------------------------------------------------------------------------\n# Self-hosted OIDC dashboard auth (generic OpenID Connect — Authentik,\n# Keycloak, Zitadel, Authelia, Auth0, Okta, Google, …). Use this INSTEAD of the\n# nous block above when gating the dashboard with your own identity provider.\n# Each setting can be overridden by an environment variable:\n#\n# dashboard.oauth.self_hosted.issuer <- HERMES_DASHBOARD_OIDC_ISSUER\n# dashboard.oauth.self_hosted.client_id <- HERMES_DASHBOARD_OIDC_CLIENT_ID\n# dashboard.oauth.self_hosted.scopes <- HERMES_DASHBOARD_OIDC_SCOPES\n# dashboard.oauth.self_hosted.client_secret <- HERMES_DASHBOARD_OIDC_CLIENT_SECRET\n#\n# dashboard:\n# oauth:\n# provider: self-hosted\n# self_hosted:\n# issuer: \"https://auth.example.com/application/o/hermes/\" # required\n# client_id: \"hermes-dashboard\" # required\n# scopes: \"openid profile email\" # optional\n#\n# # OPTIONAL — set ONLY if your IDP registered the client as\n# # *confidential* (Authentik / Keycloak often default to this). When\n# # set, Hermes authenticates the client at the token endpoint\n# # (client_secret_basic or client_secret_post, auto-selected from the\n# # IDP's discovery doc) IN ADDITION to PKCE. Leave unset for a public\n# # (PKCE-only) client — the common case.\n# #\n# # This is a CREDENTIAL: prefer setting HERMES_DASHBOARD_OIDC_CLIENT_SECRET\n# # in ~/.hermes/.env over putting it here in config.yaml.\n# # client_secret: \"\"\n\n# =============================================================================\n# External secret sources\n# =============================================================================\n# Pull provider credentials from external secret managers at process startup\n# instead of storing them in ~/.hermes/.env. Only the manager's bootstrap\n# credential (e.g. BWS_ACCESS_TOKEN / OP_SERVICE_ACCOUNT_TOKEN) lives in .env\n# (or your shell / desktop session); everything else rotates centrally.\n# Failures never block startup — Hermes warns once and continues with\n# whatever .env already had.\n#\n# Multiple sources can be enabled at once:\n# - \"mapped\" sources (explicit VAR -> ref bindings, e.g. 1Password's env:\n# map) beat \"bulk\" sources (whole-project dumps like Bitwarden BSM)\n# - within a shape, the first source to claim a var wins; later claims\n# are skipped with a startup warning (never a silent clobber)\n# - a source's override_existing lets it beat .env/shell values, but\n# never another secret source's claim\n# Docs: https://hermes-agent.nousresearch.com/docs/user-guide/secrets/\n#\n# secrets:\n# # Optional explicit ordering of enabled sources.\n# # sources: [onepassword, bitwarden]\n#\n# # ---- Bitwarden Secrets Manager (bws CLI) --------------------------------\n# bitwarden:\n# enabled: false\n# access_token_env: BWS_ACCESS_TOKEN # bootstrap token, sourced from .env\n# project_id: \"\" # UUID of the BSM project to sync\n# server_url: \"\" # \"\" = US Cloud; EU/self-hosted URL otherwise\n# cache_ttl_seconds: 300 # 0 disables fresh caching\n# encrypted_cache: # optional encrypted stale fallback\n# enabled: false\n# max_stale_seconds: 0 # 0 disables stale fallback\n# override_existing: true # BSM values win over existing env\n# auto_install: true # lazy-download bws into ~/.hermes/bin\n#\n# # ---- 1Password (op CLI) -------------------------------------------------\n# onepassword:\n# enabled: false\n# # Map env-var names to op:// secret references. Each is resolved with a\n# # single `op read` at startup.\n# env:\n# OPENAI_API_KEY: \"op://Private/OpenAI/api key\"\n# ANTHROPIC_API_KEY: \"op://Private/Anthropic/credential\"\n# account: \"\" # op --account shorthand; \"\" = default\n# service_account_token_env: OP_SERVICE_ACCOUNT_TOKEN # headless auth; unset = desktop session\n# binary_path: \"\" # \"\" = resolve op via PATH; else absolute path\n# cache_ttl_seconds: 300 # 0 disables BOTH cache layers\n# override_existing: true # resolved values win over existing env\n#\n# # ---- Command helper (any CLI vault) --------------------------------------\n# # Run a user-configured helper that prints KEY=VALUE lines on stdout —\n# # works with any secret store that has a CLI: keepassxc-cli, secret-tool,\n# # pass, gpg, or a script that cats a tmpfs env file. Composes with the\n# # sources above (enable any combination). POSIX-only (needs /bin/sh).\n# # The helper must be fast and NON-interactive (hard timeout, 1 MiB cap);\n# # its stderr is discarded so diagnostics can't leak secret material.\n# command:\n# enabled: false\n# command: \"cat /run/user/1000/hermes-secrets.env\"\n# helper_timeout_seconds: 3\n# override_existing: false # .env/shell win by default\n"}}, "website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/user-guide/configuration.md": {"533d633ab9d18ce0f05bc44d4077576b69dc58e8": {"t": 1785747363.4828815, "n": 56672, "h": "86d8f1ce0d525265", "c": "---\nsidebar_position: 2\ntitle: \"配置\"\ndescription: \"配置 Hermes Agent — config.yaml、providers、模型、API 密钥等\"\n---\n\n# 配置\n\n所有设置均存储在 `~/.hermes/` 目录中,便于访问。\n\n## 目录结构\n\n```text\n~/.hermes/\n├── config.yaml # 设置(模型、终端、TTS、压缩等)\n├── .env # API 密钥和机密\n├── auth.json # OAuth provider 凭据(Nous Portal 等)\n├── SOUL.md # 主要 agent 身份(系统提示词第 #1 槽位)\n├── memories/ # 持久记忆(MEMORY.md、USER.md)\n├── skills/ # Agent 创建的技能(通过 skill_manage 工具管理)\n├── cron/ # 定时任务\n├── sessions/ # Gateway 会话\n└── logs/ # 日志(errors.log、gateway.log — 机密自动脱敏)\n```\n\n## 管理配置\n\n```bash\nhermes config # 查看当前配置\nhermes config edit # 在编辑器中打开 config.yaml\nhermes config set KEY VAL # 设置特定值\nhermes config check # 检查缺失选项(更新后使用)\nhermes config migrate # 交互式添加缺失选项\n\n# 示例:\nhermes config set model anthropic/claude-opus-4\nhermes config set terminal.backend docker\nhermes config set OPENROUTER_API_KEY sk-or-... # 保存到 .env\n```\n\n:::tip\n`hermes config set` 命令会自动将值路由到正确的文件 —— API 密钥保存到 `.env`,其他所有内容保存到 `config.yaml`。\n:::\n\n## 配置优先级\n\n设置按以下顺序解析(优先级从高到低):\n\n1. **CLI 参数** —— 例如 `hermes chat --model anthropic/claude-sonnet-4`(单次调用覆盖)\n2. **`~/.hermes/config.yaml`** —— 所有非机密设置的主配置文件\n3. **`~/.hermes/.env`** —— 环境变量的回退;机密(API 密钥、token、密码)**必须**放这里\n4. **内置默认值** —— 未设置任何内容时的硬编码安全默认值\n\n:::info 经验法则\n机密(API 密钥、bot token、密码)放入 `.env`。其他所有内容(模型、终端后端、压缩设置、内存限制、工具集)放入 `config.yaml`。当两者都设置时,`config.yaml` 对非机密设置优先。\n:::\n\n## 环境变量替换\n\n可以在 `config.yaml` 中使用 `${VAR_NAME}` 语法引用环境变量:\n\n```yaml\nauxiliary:\n vision:\n api_key: ${GOOGLE_API_KEY}\n base_url: ${CUSTOM_VISION_URL}\n\ndelegation:\n api_key: ${DELEGATION_KEY}\n```\n\n单个值中可以有多个引用:`url: \"${HOST}:${PORT}\"`。如果引用的变量未设置,占位符将保持原样(`${UNDEFINED_VAR}` 保持不变)。仅支持 `${VAR}` 语法 —— 裸 `$VAR` 不会被展开。\n\n关于 AI provider 设置(OpenRouter、Anthropic、Copilot、自定义端点、自托管 LLM、回退模型等),请参阅 [AI Providers](/integrations/providers)。\n\n### Provider 超时\n\n可以为 provider 设置 `providers.<id>.request_timeout_seconds` 作为全局请求超时,以及 `providers.<id>.models.<model>.timeout_seconds` 作为特定模型的覆盖值。适用于每种传输方式(OpenAI-wire、原生 Anthropic、Anthropic 兼容)上的主轮次客户端、回退链、凭据轮换后的重建,以及(对于 OpenAI-wire)每请求超时 kwarg —— 因此配置值优先于旧版 `HERMES_API_TIMEOUT` 环境变量。\n\n还可以设置 `providers.<id>.stale_timeout_seconds` 用于非流式陈旧调用检测器,以及 `providers.<id>.models.<model>.stale_timeout_seconds` 作为特定模型的覆盖值。此值优先于旧版 `HERMES_API_CALL_STALE_TIMEOUT` 环境变量。\n\n不设置这些值将保持旧版默认值(`HERMES_API_TIMEOUT=1800`s、`HERMES_API_CALL_STALE_TIMEOUT=90`s、原生 Anthropic 900s)。隐式的非流式 stale 检测会在本地端点上自动禁用,并且会在超大上下文下自动放宽。目前不适用于 AWS Bedrock(`bedrock_converse` 和 AnthropicBedrock SDK 路径均使用 boto3 及其自身的超时配置)。请参阅 [`cli-config.yaml.example`](https://github.com/NousResearch/hermes-agent/blob/main/cli-config.yaml.example) 中的注释示例。\n\n## 终端后端配置\n\nHermes 支持六种终端后端。每种后端决定 agent 的 shell 命令实际在哪里执行 —— 本地机器、Docker 容器、通过 SSH 的远程服务器、Modal 云沙箱(直接或通过 Nous 托管的 gateway)、Daytona 工作区,或 Singularity/Apptainer 容器。\n\n```yaml\nterminal:\n backend: local # local | docker | ssh | modal | daytona | singularity\n cwd: \".\" # Gateway/cron 工作目录(CLI 始终使用启动目录)\n timeout: 180 # 每条命令的超时时间(秒)\n env_passthrough: [] # 转发到沙箱执行的环境变量名(terminal + execute_code)\n singularity_image: \"docker://nikolaik/python-nodejs:python3.11-nodejs20\" # Singularity 后端的容器镜像\n modal_image: \"nikolaik/python-nodejs:python3.11-nodejs20\" # Modal 后端的容器镜像\n daytona_image: \"nikolaik/python-nodejs:python3.11-nodejs20\" # Daytona 后端的容器镜像\n```\n\n对于 Modal 和 Daytona 等云沙箱,`container_persistent: true` 表示 Hermes 将尝试在沙箱重建后保留文件系统状态。这并不保证相同的活跃沙箱、PID 空间或后台进程之后仍在运行。\n\n### 后端概览\n\n| 后端 | 命令运行位置 | 隔离性 | 最适合 |\n|---------|-------------------|-----------|----------|\n| **local** | 直接在您的机器上 | 无 | 开发、个人使用 |\n| **docker** | 单个持久 Docker 容器(跨会话、`/new`、子 agent 共享) | 完全(命名空间、cap-drop) | 安全沙箱、CI/CD |\n| **ssh** | 通过 SSH 的远程服务器 | 网络边界 | 远程开发、强大硬件 |\n| **modal** | Modal 云沙箱 | 完全(云 VM) | 临时云计算、评估 |\n| **daytona** | Daytona 工作区 | 完全(云容器) | 托管云开发环境 |\n| **singularity** | Singularity/Apptainer 容器 | 命名空间(--containall) | HPC 集群、共享机器 |\n\n### Local 后端\n\n默认后端。命令直接在您的机器上运行,无隔离。无需特殊设置。\n\n```yaml\nterminal:\n backend: local\n```\n\n:::warning\nAgent 拥有与您的用户账户相同的文件系统访问权限。使用 `hermes tools` 禁用不需要的工具,或切换到 Docker 进行沙箱隔离。\n:::\n\n### Docker 后端\n\n在具有安全加固的 Docker 容器内运行命令(所有权限已删除、无权限提升、PID 限制)。\n\n**单个持久容器,而非每条命令一个容器。** Hermes 在首次使用时启动一个长期运行的容器,并通过 `docker exec` 将每个终端、文件和 `execute_code` 调用路由到同一容器中 —— 跨会话、`/new`、`/reset` 和 `delegate_task` 子 agent,贯穿 Hermes 进程的整个生命周期。工作目录更改、已安装的包以及 `/workspace` 中的文件会从一次工具调用延续到下一次,就像本地 shell 一样。容器在关闭时停止并删除。详情请参阅下方的**容器生命周期**。\n\n```yaml\nterminal:\n backend: docker\n docker_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n docker_mount_cwd_to_workspace: false # 将启动目录挂载到 /workspace\n docker_run_as_host_user: false # 参见下方\"以宿主用户身份运行容器\"\n docker_forward_env: # 转发到容器的环境变量\n - \"GITHUB_TOKEN\"\n docker_volumes: # 宿主目录挂载\n - \"/home/user/projects:/workspace/projects\"\n - \"/home/user/data:/data:ro\" # :ro 表示只读\n docker_extra_args: # 附加到 `docker run` 的额外标志\n - \"--gpus=all\"\n - \"--network=host\"\n\n # 资源限制\n container_cpu: 1 # CPU 核心数(0 = 不限制)\n container_memory: 5120 # MB(0 = 不限制)\n container_disk: 51200 # MB(需要 XFS+pquota 上的 overlay2)\n container_persistent: true # 跨会话持久化 /workspace 和 /root\n```\n\n**`terminal.docker_extra_args`**(也可通过 `TERMINAL_DOCKER_EXTRA_ARGS='[\"--gpus=all\"]'` 覆盖)允许传递 Hermes 未作为一级键公开的任意 `docker run` 标志 —— `--gpus`、`--network`、`--add-host`、替代 `--security-opt` 覆盖等。每个条目必须是字符串;该列表最后附加到组装好的 `docker run` 调用中,因此可以在需要时覆盖 Hermes 的默认值。请谨慎使用 —— 与沙箱加固(权限删除、`--user`、workspace 绑定挂载)冲突的标志将悄然削弱隔离性。\n\n**要求:** 已安装并运行 Docker Desktop 或 Docker Engine。Hermes 会探测 `$PATH` 以及常见的 macOS 安装位置(`/usr/local/bin/docker`、`/opt/homebrew/bin/docker`、Docker Desktop 应用包)。开箱即用支持 Podman:设置 `HERMES_DOCKER_BINARY=podman`(或完整路径)以在两者都安装时强制使用它。\n\n**容器生命周期:** Hermes 为每个终端和文件工具调用重用单个长期运行的容器(`docker run -d ... sleep 2h`),跨会话、`/new`、`/reset` 和 `delegate_task` 子 agent,贯穿 Hermes 进程的整个生命周期。命令通过带登录 shell 的 `docker exec` 运行,因此工作目录更改、已安装的包以及 `/workspace` 中的文件都会从一次工具调用延续到下一次。容器在 Hermes 关闭时(或空闲清理回收时)停止并删除。\n\n通过 `delegate_task(tasks=[...])` 生成的并行子 agent 共享这一个容器 —— 并发的 `cd`、环境变量修改以及对同一路径的写入会发生冲突。如果子 agent 需要隔离的沙箱,必须通过 `register_task_env_overrides()` 注册每任务镜像覆盖,RL 和基准测试环境(TerminalBench2、HermesSweEnv 等)会自动为其每任务 Docker 镜像执行此操作。\n\n**安全加固:**\n- `--cap-drop ALL`,仅添加回 `DAC_OVERRIDE`、`CHOWN`、`FOWNER`\n- `--security-opt no-new-privileges`\n- `--pids-limit 256`\n- `/tmp`(512MB)、`/var/tmp`(256MB)、`/run`(64MB)的大小限制 tmpfs\n\n**凭据转发:** `docker_forward_env` 中列出的环境变量首先从您的 shell 环境解析,然后回退到 `~/.hermes/.env`。技能也可以声明 `required_environment_variables`,这些变量会自动合并。\n\n### SSH 后端\n\n通过 SSH 在远程服务器上运行命令。使用 ControlMaster 进行连接复用(5 分钟空闲保活)。默认启用持久 shell —— 状态(cwd、环境变量)在命令之间保持。\n\n```yaml\nterminal:\n backend: ssh\n persistent_shell: true # 保持长期运行的 bash 会话(默认:true)\n```\n\n**必需的环境变量:**\n\n```bash\nTERMINAL_SSH_HOST=my-server.example.com\nTERMINAL_SSH_USER=ubuntu\n```\n\n**可选:**\n\n| 变量 | 默认值 | 描述 |\n|----------|---------|-------------|\n| `TERMINAL_SSH_PORT` | `22` | SSH 端口 |\n| `TERMINAL_SSH_KEY` | (系统默认) | SSH 私钥路径 |\n| `TERMINAL_SSH_PERSISTENT` | `true` | 启用持久 shell |\n\n**工作原理:** 使用 `BatchMode=yes` 和 `StrictHostKeyChecking=accept-new` 在初始化时连接。持久 shell 在远程主机上保持单个 `bash -l` 进程存活,通过临时文件进行通信。需要 `stdin_data` 或 `sudo` 的命令会自动回退到单次模式。\n\n### Modal 后端\n\n在 [Modal](https://modal.com) 云沙箱中运行命令。每个任务获得一个具有可配置 CPU、内存和磁盘的隔离 VM。文件系统可以跨会话快照/恢复。\n\n```yaml\nterminal:\n backend: modal\n container_cpu: 1 # CPU 核心数\n container_memory: 5120 # MB(5GB)\n container_disk: 51200 # MB(50GB)\n container_persistent: true # 快照/恢复文件系统\n```\n\n**必需:** `MODAL_TOKEN_ID` + `MODAL_TOKEN_SECRET` 环境变量,或 `~/.modal.toml` 配置文件。\n\n**持久化:** 启用后,沙箱文件系统在清理时快照,并在下次会话时恢复。快照在 `~/.hermes/modal_snapshots.json` 中跟踪。这保留文件系统状态,而非活跃进程、PID 空间或后台任务。\n\n**凭据文件:** 自动从 `~/.hermes/` 挂载(OAuth token 等),并在每条命令前同步。\n\n### Daytona 后端\n\n在 [Daytona](https://daytona.io) 托管工作区中运行命令。支持停止/恢复以实现持久化。\n\n```yaml\nterminal:\n backend: daytona\n container_cpu: 1 # CPU 核心数\n container_memory: 5120 # MB → 转换为 GiB\n container_disk: 10240 # MB → 转换为 GiB(最大 10 GiB)\n container_persistent: true # 停止/恢复而非删除\n```\n\n**必需:** `DAYTONA_API_KEY` 环境变量。\n\n**持久化:** 启用后,沙箱在清理时停止(而非删除),并在下次会话时恢复。沙箱名称遵循 `hermes-{task_id}` 模式。\n\n**磁盘限制:** Daytona 强制执行 10 GiB 最大值。超过此值的请求将被截断并发出警告。\n\n### Singularity/Apptainer 后端\n\n在 [Singularity/Apptainer](https://apptainer.org) 容器中运行命令。专为 Docker 不可用的 HPC 集群和共享机器设计。\n\n```yaml\nterminal:\n backend: singularity\n singularity_image: \"docker://nikolaik/python-nodejs:python3.11-nodejs20\"\n container_cpu: 1 # CPU 核心数\n container_memory: 5120 # MB\n container_persistent: true # 可写覆盖层跨会话持久化\n```\n\n**要求:** `$PATH` 中有 `apptainer` 或 `singularity` 二进制文件。\n\n**镜像处理:** Docker URL(`docker://...`)自动转换为 SIF 文件并缓存。现有 `.sif` 文件直接使用。\n\n**临时目录:** 按顺序解析:`TERMINAL_SCRATCH_DIR` → `TERMINAL_SANDBOX_DIR/singularity` → `/scratch/$USER/hermes-agent`(HPC 惯例)→ `~/.hermes/sandboxes/singularity`。\n\n**隔离:** 使用 `--containall --no-home` 实现完全命名空间隔离,不挂载宿主 home 目录。\n\n### 常见终端后端问题\n\n如果终端命令立即失败或终端工具报告为已禁用:\n\n- **Local** —— 无特殊要求。入门时最安全的默认选项。\n- **Docker** —— 运行 `docker version` 验证 Docker 是否正常工作。如果失败,修复 Docker 或执行 `hermes config set terminal.backend local`。\n- **SSH** —— `TERMINAL_SSH_HOST` 和 `TERMINAL_SSH_USER` 都必须设置。如果缺少任一项,Hermes 会记录清晰的错误。\n- **Modal** —— 需要 `MODAL_TOKEN_ID` 环境变量或 `~/.modal.toml`。运行 `hermes doctor` 检查。\n- **Daytona** —— 需要 `DAYTONA_API_KEY`。Daytona SDK 处理服务器 URL 配置。\n- **Singularity** —— 需要 `$PATH` 中有 `apptainer` 或 `singularity`。HPC 集群上常见。\n\n如有疑问,将 `terminal.backend` 设回 `local` 并首先验证命令在那里运行。\n\n### 拆卸时远程到宿主文件同步\n\n对于 **SSH**、**Modal** 和 **Daytona** 后端(agent 的工作树位于与运行 Hermes 的宿主不同的机器上),Hermes 跟踪 agent 在远程沙箱中触及的文件,并在会话拆卸/沙箱清理时,将修改的文件**同步回宿主**,存放在 `~/.hermes/cache/remote-syncs/<session-id>/` 下。\n\n- 触发时机:会话关闭、`/new`、`/reset`、gateway 消息超时、子 agent 使用远程后端时 `delegate_task` 子 agent 完成。\n- 覆盖 agent 修改的整个树,而不仅仅是它明确打开的文件。添加、编辑和删除都会被捕获。\n- 远程沙箱可能在您查找时已被拆除;本地 `~/.hermes/cache/remote-syncs/…` 副本是 agent 更改内容的权威记录。\n- 大型二进制输出(模型检查点、原始数据集)按大小限制 —— 同步跳过超过 `file_sync_max_mb`(默认 `100`)的文件。如果您期望更大的工件返回,请调高该值。\n\n```yaml\nterminal:\n file_sync_max_mb: 100 # 默认 —— 同步最大 100 MB 的文件\n file_sync_enabled: true # 默认 —— 设为 false 可完全跳过同步\n```\n\n这是从会话结束后被销毁的临时云沙箱中恢复结果的方式,无需告诉 agent 显式地 `scp` 或 `modal volume put` 每个工件。\n\n### Docker 卷挂载\n\n使用 Docker 后端时,`docker_volumes` 允许您与容器共享宿主目录。每个条目使用标准 Docker `-v` 语法:`host_path:container_path[:options]`。\n\n```yaml\nterminal:\n backend: docker\n docker_volumes:\n - \"/home/user/projects:/workspace/projects\" # 读写(默认)\n - \"/home/user/datasets:/data:ro\" # 只读\n - \"/home/user/.hermes/cache/documents:/output\" # Gateway 可见的导出\n```\n\n适用于:\n- **向 agent 提供文件**(数据集、配置、参考代码)\n- **从 agent 接收文件**(生成的代码、报告、导出)\n- **共享工作区**,您和 agent 都访问相同的文件\n\n如果您使用消息 gateway 并希望 agent 通过 `MEDIA:/...` 发送生成的文件,建议使用专用的宿主可见导出挂载,例如 `/home/user/.hermes/cache/documents:/output`。\n\n- 在 Docker 中将文件写入 `/output/...`\n- 在 `MEDIA:` 中发出**宿主路径**,例如:`MEDIA:/home/user/.hermes/cache/documents/report.txt`\n- **不要**发出 `/workspace/...` 或 `/output/...`,除非该确切路径在宿主上对 gateway 进程也存在\n\n:::warning\nYAML 重复键会静默覆盖之前的键。如果您已有 `docker_volumes:` 块,请将新挂载合并到同一列表中,而不是在文件后面再添加一个 `docker_volumes:` 键。\n:::\n\n也可以通过环境变量设置:`TERMINAL_DOCKER_VOLUMES='[\"/host:/container\"]'`(JSON 数组)。\n\n### Docker 凭据转发\n\n默认情况下,Docker 终端会话不继承任意宿主凭据。如果您需要在容器内使用特定 token,请将其添加到 `terminal.docker_forward_env`。\n\n```yaml\nterminal:\n backend: docker\n docker_forward_env:\n - \"GITHUB_TOKEN\"\n - \"NPM_TOKEN\"\n```\n\nHermes 首先从您当前的 shell 解析每个列出的变量,然后回退到通过 `hermes config set` 保存的 `~/.hermes/.env`。\n\n:::warning\n`docker_forward_env` 中列出的任何内容都会对容器内运行的命令可见。只转发您愿意暴露给终端会话的凭据。\n:::\n\n### 以宿主用户身份运行容器\n\n默认情况下,Docker 容器以 `root`(UID 0)身份运行。在 `/workspace` 或其他绑定挂载中创建的文件在宿主上归 root 所有,因此会话结束后您必须 `sudo chown` 才能从宿主编辑器编辑它们。`terminal.docker_run_as_host_user` 标志解决了这个问题:\n\n```yaml\nterminal:\n backend: docker\n docker_run_as_host_user: true # 默认:false\n```\n\n启用后,Hermes 将 `--user $(id -u):$(id -g)` 附加到 `docker run` 命令,使写入绑定挂载目录(`/workspace`、`/root`、`docker_volumes` 中的任何内容)的文件归您的宿主用户所有,而非 root。权衡:容器将无法再 `apt install` 或写入 `/root/.npm` 等 root 拥有的路径 —— 如果您同时需要这两者,请使用 `HOME` 归非 root 用户所有的基础镜像(或在镜像构建时添加所需工具)。\n\n保持 `false`(默认)以获得向后兼容的行为。当您的工作流主要是\"编辑挂载的宿主文件\"且厌倦了 `sudo chown -R` 时,请开启此选项。\n\n### 可选:将启动目录挂载到 `/workspace`\n\nDocker 沙箱默认保持隔离。Hermes **不会**将您当前的宿主工作目录传入容器,除非您明确选择加入。\n\n在 `config.yaml` 中启用:\n\n```yaml\nterminal:\n backend: docker\n docker_mount_cwd_to_workspace: true\n```\n\n启用后:\n- 如果您从 `~/projects/my-app` 启动 Hermes,该宿主目录将绑定挂载到 `/workspace`\n- Docker 后端从 `/workspace` 开始\n- 文件工具和终端命令都能看到相同的挂载项目\n\n禁用时,`/workspace` 保持沙箱所有,除非您通过 `docker_volumes` 显式挂载内容。\n\n安全权衡:\n- `false` 保留沙箱边界\n- `true` 使沙箱直接访问您启动 Hermes 的目录\n\n仅在您有意希望容器处理实时宿主文件时才选择加入。\n\n### 持久 Shell\n\n默认情况下,每条终端命令在其自己的子进程中运行 —— 工作目录、环境变量和 shell 变量在命令之间重置。启用**持久 shell** 后,单个长期运行的 bash 进程在 `execute()` 调用之间保持存活,使状态在命令之间保持。\n\n这对 **SSH 后端**最有用,它还消除了每条命令的连接开销。持久 shell **对 SSH 默认启用**,对本地后端禁用。\n\n```yaml\nterminal:\n persistent_shell: true # 默认 —— 为 SSH 启用持久 shell\n```\n\n禁用:\n\n```bash\nhermes config set terminal.persistent_shell false\n```\n\n**跨命令保持的内容:**\n- 工作目录(`cd /tmp` 对下一条命令生效)\n- 导出的环境变量(`export FOO=bar`)\n- Shell 变量(`MY_VAR=hello`)\n\n**优先级:**\n\n| 级别 | 变量 | 默认值 |\n|-------|----------|---------|\n| 配置 | `terminal.persistent_shell` | `true` |\n| SSH 覆盖 | `TERMINAL_SSH_PERSISTENT` | 遵循配置 |\n| Local 覆盖 | `TERMINAL_LOCAL_PERSISTENT` | `false` |\n\n每个后端的环境变量具有最高优先级。如果您也想在本地后端使用持久 shell:\n\n```bash\nexport TERMINAL_LOCAL_PERSISTENT=true\n```\n\n:::note\n需要 `stdin_data` 或 sudo 的命令会自动回退到单次模式,因为持久 shell 的 stdin 已被 IPC 协议占用。\n:::\n\n有关每个后端的详细信息,请参阅[代码执行](features/code-execution.md)和 [README 的终端部分](features/tools.md)。\n\n## 技能设置\n\n技能可以通过其 SKILL.md frontmatter 声明自己的配置设置。这些是非机密值(路径、偏好、域设置),存储在 `config.yaml` 的 `skills.config` 命名空间下。\n\n```yaml\nskills:\n config:\n myplugin:\n path: ~/myplugin-data # 示例 —— 每个技能定义自己的键\n```\n\n**技能设置的工作原理:**\n\n- `hermes config migrate` 扫描所有已启用的技能,找到未配置的设置,并提供提示\n- `hermes config show` 在\"技能设置\"下显示所有技能设置及其所属技能\n- 技能加载时,其解析的配置值会自动注入到技能上下文中\n\n**手动设置值:**\n\n```bash\nhermes config set skills.config.myplugin.path ~/myplugin-data\n```\n\n有关在您自己的技能中声明配置设置的详细信息,请参阅[创建技能 — 配置设置](/developer-guide/creating-skills#config-settings-configyaml)。\n\n### Agent 创建技能写入的守卫\n\n当 agent 使用 `skill_manage` 创建、编辑、修补或删除技能时,Hermes 可以选择扫描新/更新的内容以查找危险关键字模式(凭据收集、明显的 prompt 注入、数据外泄指令)。扫描器**默认关闭** —— 合法触及 `~/.ssh/` 或提及 `$OPENAI_API_KEY` 的真实 agent 工作流触发启发式规则过于频繁。如果您希望扫描器在 agent 的技能写入落地前提示您,请重新开启:\n\n```yaml\nskills:\n guard_agent_created: true # 默认:false\n```\n\n开启后,任何被标记的 `skill_manage` 写入都会以审批提示的形式出现,并附带扫描器的理由。接受的写入落地;拒绝的写入向 agent 返回解释性错误。\n\n## 内存配置\n\n```yaml\nmemory:\n memory_enabled: true\n user_profile_enabled: true\n memory_char_limit: 2200 # ~800 tokens\n user_char_limit: 1375 # ~500 tokens\n```\n\n## 文件读取安全\n\n控制单次 `read_file` 调用可以返回多少内容。超过限制的读取将被拒绝,并向 agent 返回错误,提示使用 `offset` 和 `limit` 读取较小范围。这可以防止单次读取压缩的 JS 包或大型数据文件时淹没上下文窗口。\n\n```yaml\nfile_read_max_chars: 100000 # 默认 —— ~25-35K tokens\n```\n\n如果您使用具有大上下文窗口的模型并经常读取大文件,请调高此值。对于小上下文模型,请降低以保持读取高效:\n\n```yaml\n# 大上下文模型(200K+)\nfile_read_max_chars: 200000\n\n# 小型本地模型(16K 上下文)\nfile_read_max_chars: 30000\n```\n\nAgent 还会自动去重文件读取 —— 如果同一文件区域被读取两次且文件未更改,则返回轻量级存根而不是重新发送内容。这在上下文压缩后重置,以便 agent 在内容被摘要后可以重新读取文件。\n\n## 工具输出截断限制\n\n三个相关的上限控制工具在 Hermes 截断之前可以返回多少原始输出:\n\n```yaml\ntool_output:\n max_bytes: 50000 # 终端输出上限(字符)\n max_lines: 2000 # read_file 分页上限\n max_line_length: 2000 # read_file 行号视图中的每行上限\n```\n\n- **`max_bytes`** —— 当 `terminal` 命令产生超过此字符数的合并 stdout/stderr 时,Hermes 保留前 40% 和后 60%,并在中间插入 `[OUTPUT TRUNCATED]` 通知。默认 `50000`(典型分词器约 12-15K tokens)。\n- **`max_lines`** —— 单次 `read_file` 调用的 `limit` 参数上限。超过此值的请求将被截断,以防单次读取淹没上下文窗口。默认 `2000`。\n- **`max_line_length`** —— `read_file` 发出行号视图时应用的每行上限。超过此长度的行将被截断为此字符数,后跟 `... [truncated]`。默认 `2000`。\n\n对于具有大上下文窗口且每次调用可以承受更多原始输出的模型,请调高限制。对于小上下文模型,请降低以保持工具结果紧凑:\n\n```yaml\n# 大上下文模型(200K+)\ntool_output:\n max_bytes: 150000\n max_lines: 5000\n\n# 小型本地模型(16K 上下文)\ntool_output:\n max_bytes: 20000\n max_lines: 500\n```\n\n## 全局工具集禁用\n\n要在 CLI 和每个 gateway 平台上统一禁用特定工具集,请在 `agent.disabled_toolsets` 下列出其名称:\n\n```yaml\nagent:\n disabled_toolsets:\n - memory # 隐藏内存工具 + MEMORY_GUIDANCE 注入\n - web # 任何地方都不使用 web_search / web_extract\n```\n\n这在每个平台的工具配置(由 `hermes tools` 写入的 `platform_toolsets`)**之后**应用,因此此处列出的工具集始终被删除 —— 即使平台的已保存配置仍然列出它。当您希望有一个\"到处关闭 X\"的单一开关而不是编辑 `hermes tools` UI 中的 15+ 个平台行时,请使用此选项。\n\n留空列表或省略键不会产生任何效果。\n\n## Git Worktree 隔离\n\n启用隔离的 git worktree,以便在同一仓库上并行运行多个 agent:\n\n```yaml\nworktree: true # 始终创建 worktree(与 hermes -w 相同)\n# worktree: false # 默认 —— 仅在传递 -w 标志时\n```\n\n启用后,每个 CLI 会话在 `.worktrees/` 下创建一个带有自己分支的新 worktree。Agent 可以编辑文件、提交、推送和创建 PR,而不会相互干扰。干净的 worktree 在退出时删除;脏的 worktree 保留以供手动恢复。\n\n您还可以通过仓库根目录中的 `.worktreeinclude` 列出要复制到 worktree 的 gitignore 文件:\n\n```\n# .worktreeinclude\n.env\n.venv/\nnode_modules/\n```\n\n## 上下文压缩\n\nHermes 自动压缩长对话以保持在模型的上下文窗口内。压缩摘要器是一个单独的 LLM 调用 —— 您可以将其指向任何 provider 或端点。\n\n所有压缩设置都在 `config.yaml` 中(无环境变量)。\n\n### 完整参考\n\n```yaml\ncompression:\n enabled: true # 开启/关闭压缩\n threshold: 0.50 # 在上下文限制的此百分比时压缩\n target_ratio: 0.20 # 保留为最近尾部的阈值分数\n protect_last_n: 20 # 保持未压缩的最少最近消息数\n hygiene_hard_message_limit: 5000 # Gateway 安全阀 —— 见下文\n\n# 摘要模型/provider 在 auxiliary: 下配置:\nauxiliary:\n compression:\n model: \"\" # 空 = 使用主聊天模型。覆盖为例如 \"google/gemini-3-flash-preview\" 以获得更便宜/更快的压缩。\n provider: \"auto\" # Provider:\"auto\"、\"openrouter\"、\"nous\"、\"codex\"、\"main\" 等\n base_url: null # 自定义 OpenAI 兼容端点(覆盖 provider)\n```\n\n:::info 旧版配置迁移\n带有 `compression.summary_model`、`compression.summary_provider` 和 `compression.summary_base_url` 的旧版配置在首次加载时自动迁移到 `auxiliary.compression.*`(配置版本 17)。无需手动操作。\n:::\n\n`hygiene_hard_message_limit` 是仅限 gateway 的**预压缩安全阀**。它的存在是为了打破一个死循环:当超大会话的 API 调用持续断开时,gateway 永远收不到 token 使用数据,基于 token 的阈值因此无法触发,于是 transcript 持续增长、断开愈发严重。这个基于消息数的下限仅凭消息数量触发(无论 API 是否失败,消息数始终已知),强制压缩以恢复会话。默认 `5000` —— 远高于任何正常会话,包括做数千次短轮次的大上下文(1M+)模型,它们早就在 token 阈值处压缩了。对于异常平台可调得更高;要强制更积极的压缩则调低。在运行中的 gateway 上编辑此值将在下一条消息时生效(见下文)。\n\n:::tip Gateway 热重载压缩和上下文长度\n从最近的版本开始,在运行中的 gateway 上编辑 `config.yaml` 中的 `model.context_length` 或任何 `compression.*` 键将在下一条消息时生效 —— 无需 gateway 重启、`/reset` 或会话轮换。缓存的 agent 签名包含这些键,因此 gateway 在检测到更改时会透明地重建 agent。API 密钥和工具/技能配置仍需要通常的重载路径。\n:::\n\n### 常见设置\n\n**默认(自动检测)—— 无需配置:**\n```yaml\ncompression:\n enabled: true\n threshold: 0.50\n```\n使用您的主 provider 和主模型。如果您希望在比主聊天模型更便宜的模型上进行压缩,请覆盖每任务(例如 `auxiliary.compression.provider: openrouter` + `model: google/gemini-2.5-flash`)。\n\n**强制特定 provider**(基于 OAuth 或 API 密钥):\n```yaml\nauxiliary:\n compression:\n provider: nous\n model: gemini-3-flash\n```\n适用于任何 provider:`nous`、`openrouter`、`codex`、`anthropic`、`main` 等。\n\n**自定义端点**(自托管、Ollama、zai、DeepSeek 等):\n```yaml\nauxiliary:\n compression:\n model: glm-4.7\n base_url: https://api.z.ai/api/coding/paas/v4\n```\n指向自定义 OpenAI 兼容端点。使用 `OPENAI_API_KEY` 进行认证。\n\n### 三个旋钮的交互方式\n\n| `auxiliary.compression.provider` | `auxiliary.compression.base_url` | 结果 |\n|---------------------|---------------------|--------|\n| `auto`(默认) | 未设置 | 自动检测最佳可用 provider |\n| `nous` / `openrouter` / 等 | 未设置 | 强制使用该 provider,使用其认证 |\n| 任意 | 已设置 | 直接使用自定义端点(忽略 provider) |\n\n:::warning 摘要模型上下文长度要求\n摘要模型**必须**具有至少与您的主 agent 模型一样大的上下文窗口。压缩器将对话的完整中间部分发送给摘要模型 —— 如果该模型的上下文窗口小于主模型的,摘要调用将因上下文长度错误而失败。发生这种情况时,中间轮次将**在没有摘要的情况下被丢弃**,静默丢失对话上下文。如果您覆盖模型,请验证其上下文长度满足或超过您的主模型。\n:::\n\n## 上下文引擎\n\n上下文引擎控制在接近模型 token 限制时如何管理对话。内置的 `compressor` 引擎使用有损摘要(参见[上下文压缩](/developer-guide/context-compression-and-caching))。插件引擎可以用替代策略替换它。\n\n```yaml\ncontext:\n engine: \"compressor\" # 默认 —— 内置有损摘要\n```\n\n使用插件引擎(例如,用于无损上下文管理的 LCM):\n\n```yaml\ncontext:\n engine: \"lcm\" # 必须与插件名称匹配\n```\n\n插件引擎**永远不会自动激活** —— 您必须将 `context.engine` 显式设置为插件名称。可用引擎可以通过 `hermes plugins` → Provider Plugins → Context Engine 浏览和选择。\n\n有关内存插件的类似单选系统,请参阅[内存 Providers](/user-guide/features/memory-providers)。\n\n## 迭代预算\n\n当 agent 在处理具有许多工具调用的复杂任务时,它可能会耗尽其迭代预算(默认:500 轮)。Hermes **不会**在任务中途注入压力警告 —— 早期版本会在预算达到 70%/90% 时警告模型,这会导致模型过早放弃复杂任务,该机制已于 2026 年 4 月移除。\n\n取而代之的是,当预算真正耗尽(500/500)时,Hermes 注入一条消息要求模型收尾,并允许一次**宽限调用**以便其给出最终响应。如果该宽限调用仍未产生文本,则会要求 agent 总结已完成的工作。\n\n```yaml\nagent:\n max_turns: 500 # 每次对话轮次的最大迭代次数(默认:500)\n api_max_retries: 3 # 回退启动前每个 provider 的重试次数(默认:3)\n```\n\n当迭代预算完全耗尽时,CLI 向用户显示通知:`⚠ Iteration budget reached (500/500) — response may be incomplete`。\n\n`agent.api_max_retries` 控制 Hermes 在回退 provider 切换启动**之前**对瞬时错误(速率限制、连接断开、5xx)重试 provider API 调用的次数。默认为 `3` —— 总共四次尝试。如果您配置了[回退 providers](/user-guide/features/fallback-providers) 并希望更快地故障转移,请将其降至 `0`,这样主 provider 上的第一个瞬时错误会立即切换到回退,而不是对不稳定的端点进行重试。\n\n### API 超时\n\nHermes 对流式传输有单独的超时层,以及用于非流式调用的陈旧检测器。陈旧检测器仅在您将其保留为隐式默认值时才会自动调整本地 provider。\n\n| 超时 | 默认值 | 本地 providers | 配置/环境变量 |\n|---------|---------|----------------|--------------|\n| Socket 读取超时 | 120s | 自动提升至 1800s | `HERMES_STREAM_READ_TIMEOUT` |\n| 陈旧流检测 | 180s | 自动禁用 | `HERMES_STREAM_STALE_TIMEOUT` |\n| 陈旧非流检测 | 300s | 保持隐式时自动禁用 | `providers.<id>.stale_timeout_seconds` 或 `HERMES_API_CALL_STALE_TIMEOUT` |\n| API 调用(非流式) | 1800s | 不变 | `providers.<id>.request_timeout_seconds` / `timeout_seconds` 或 `HERMES_API_TIMEOUT` |\n\n**Socket 读取超时**控制 httpx 等待 provider 下一个数据块的时间。本地 LLM 在大上下文上预填充可能需要几分钟才能产生第一个 token,因此当 Hermes 检测到本地端点时,会将此值提升至 30 分钟。如果您显式设置 `HERMES_STREAM_READ_TIMEOUT`,无论端点检测如何,始终使用该值。\n\n**陈旧流检测**终止接收 SSE 保活 ping 但没有实际内容的连接。对于本地 providers,这完全禁用,因为它们在预填充期间不发送保活 ping。\n\n**陈旧非流检测**终止长时间没有响应的非流式调用。默认情况下,Hermes 在本地端点上禁用此功能,以避免长时间预填充期间的误报。如果您显式设置 `providers.<id>.stale_timeout_seconds`、`providers.<id>.models.<model>.stale_timeout_seconds` 或 `HERMES_API_CALL_STALE_TIMEOUT`,即使在本地端点上也会遵守该显式值。\n\n## 上下文压力警告\n\n与迭代预算压力分开,上下文压力跟踪对话距**压缩阈值**有多近 —— 即上下文压缩触发以摘要旧消息的点。这有助于您和 agent 了解对话何时变长。\n\n| 进度 | 级别 | 发生的事情 |\n|----------|-------|-------------|\n| **≥ 60%** 到阈值 | 信息 | CLI 显示青色进度条;gateway 发送信息通知 |\n| **≥ 85%** 到阈值 | 警告 | CLI 显示粗体黄色进度条;gateway 警告压缩即将发生 |\n\n在 CLI 中,上下文压力在工具输出流中显示为进度条:\n\n```\n ◐ context ████████████░░░░░░░░ 62% to compaction 48k threshold (50%) · approaching compaction\n```\n\n在消息平台上,发送纯文本通知:\n\n```\n◐ Context: ████████████░░░░░░░░ 62% to compaction (threshold: 50% of window).\n```\n\n如果自动压缩被禁用,警告会告诉您上下文可能被截断。\n\n上下文压力是自动的 —— 无需配置。它纯粹作为面向用户的通知触发,不修改消息流或向模型上下文注入任何内容。\n\n## 凭据池策略\n\n当您为同一 provider 拥有多个 API 密钥或 OAuth token 时,配置轮换策略:\n\n```yaml\ncredential_pool_strategies:\n openrouter: round_robin # 均匀循环使用密钥\n anthropic: least_used # 始终选择使用最少的密钥\n```\n\n选项:`fill_first`(默认)、`round_robin`、`least_used`、`random`。完整文档请参阅[凭据池](/user-guide/features/credential-pools)。\n\n## Prompt 缓存\n\n当活跃 provider 支持时,Hermes 自动开启跨会话 prompt 缓存 —— 无需用户配置。\n\n对于**原生 Anthropic**、**OpenRouter** 和 **Nous Portal** 上的 Claude,Hermes 在系统提示词和技能块上附加带有 1 小时 TTL(`ttl: \"1h\"`)的 `cache_control` 断点。在新鲜的一小时内首次发送时按完整输入费率计费;同一小时内任何会话的后续发送以折扣缓存读取费率从缓存中提取。这意味着系统提示词、加载的技能内容以及任何长上下文包含的早期部分在第一个小时内跨 `hermes` 会话和分叉子 agent 被重用。\n\nQwen Cloud(阿里巴巴 DashScope)上游将缓存 TTL 限制为 5 分钟,因此 Hermes 在那里使用 5 分钟断点 TTL。其他通过第三方的 Claude 路径(AWS Bedrock、Azure Foundry)回退到 provider 自己的缓存默认值。xAI Grok 使用单独的会话固定对话 ID 机制 —— 参阅 [xAI prompt 缓存](/integrations/providers#xai-grok--responses-api--prompt-caching)。\n\n不存在禁用此功能的旋钮 —— 缓存始终开启,即使在单轮对话中也能节省费用,因为仅系统提示词就占输入 token 数的相当大比例。\n\n## 辅助模型\n\nHermes 使用\"辅助\"模型处理图像分析、网页摘要、浏览器截图分析、会话标题生成和上下文压缩等附带任务。默认情况下(`auxiliary.*.provider: \"auto\"`),Hermes 将每个辅助任务路由到您的**主聊天模型** —— 与您在 `hermes model` 中选择的相同 provider/模型。您无需配置任何内容即可开始,但请注意,在昂贵的推理模型(Opus、MiniMax M2.7 等)上,辅助任务会增加显著成本。如果您希望无论主模型如何都使用便宜且快速的附带任务,请显式设置 `auxiliary.<task>.provider` 和 `auxiliary.<task>.model`(例如,在 OpenRouter 上使用 Gemini Flash 进行视觉和网页提取)。\n\n:::note 为什么 \"auto\" 使用您的主模型\n早期版本将聚合器用户(OpenRouter、Nous Portal)分流到便宜的 provider 端默认值。这令人惊讶 —— 付费购买聚合器订阅的用户会看到不同的模型处理其辅助流量。`auto` 现在对所有人使用主模型,`config.yaml` 中的每任务覆盖仍然优先(见下方[完整辅助配置参考](#full-auxiliary-config-reference))。\n:::\n\n### 交互式配置辅助模型\n\n无需手动编辑 YAML,运行 `hermes model` 并从菜单中选择**\"配置辅助模型\"**。您将获得交互式的每任务选择器:\n\n```\n$ hermes model\n→ Configure auxiliary models\n\n[ ] vision currently: auto / main model\n[ ] web_extract currently: auto / main model\n[ ] title_generation currently: openrouter / google/gemini-3-flash-preview\n[ ] compression currently: auto / main model\n[ ] approval currently: auto / main model\n[ ] triage_specifier currently: auto / main model\n[ ] kanban_decomposer currently: auto / main model\n[ ] profile_describer currently: auto / main model\n```\n\n选择任务,选择 provider(OAuth 流程打开浏览器;API 密钥 provider 提示输入),选择模型。更改持久化到 `config.yaml` 中的 `auxiliary.<task>.*`。与主模型选择器相同的机制 —— 无需学习额外语法。\n\n### 视频教程\n\n<div style={{position: 'relative', width: '100%', aspectRatio: '16 / 9', marginBottom: '1.5rem'}}>\n <iframe\n src=\"https://www.youtube.com/embed/NoF-YajElIM\"\n title=\"Hermes Agent — Auxiliary Models Tutorial\"\n style={{position: 'absolute', top: 0, left: 0, width: '100%', height: '100%', border: 0}}\n allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\"\n allowFullScreen\n />\n</div>\n\n### 通用配置模式\n\nHermes 中的每个模型槽位 —— 辅助任务、压缩、回退 —— 使用相同的三个旋钮:\n\n| 键 | 作用 | 默认值 |\n|-----|-------------|---------|\n| `provider` | 用于认证和路由的 provider | `\"auto\"` |\n| `model` | 请求的模型 | provider 的默认值 |\n| `base_url` | 自定义 OpenAI 兼容端点(覆盖 provider) | 未设置 |\n\n当设置 `base_url` 时,Hermes 忽略 provider 并直接调用该端点(使用 `api_key` 或 `OPENAI_API_KEY` 进行认证)。当仅设置 `provider` 时,Hermes 使用该 provider 的内置认证和基础 URL。\n\n辅助任务的可用 providers:`auto`、`main`,以及[provider 注册表](/reference/environment-variables)中的任何 provider —— `openrouter`、`nous`、`openai-codex`、`copilot`、`copilot-acp`、`anthropic`、`gemini`、`qwen-oauth`、`zai`、`kimi-coding`、`kimi-coding-cn`、`minimax`、`minimax-cn`、`minimax-oauth`、`deepseek`、`nvidia`、`xai`、`xai-oauth`、`ollama-cloud`、`alibaba`、`bedrock`、`huggingface`、`arcee`、`xiaomi`、`kilocode`、`opencode-zen`、`opencode-go`、`azure-foundry` —— 或您 `custom_providers` 列表中任何命名的自定义 provider(例如 `provider: \"beans\"`)。\n\n:::tip MiniMax OAuth\n`minimax-oauth` 通过浏览器 OAuth 登录(无需 API 密钥)。运行 `hermes model` 并选择 **MiniMax (OAuth)** 进行认证。辅助任务自动使用 `MiniMax-M2.7-highspeed`。参阅 [MiniMax OAuth 指南](../guides/minimax-oauth.md)。\n:::\n\n:::tip xAI Grok OAuth\n`xai-oauth` 通过浏览器 OAuth 为 SuperGrok 和 X Premium+ 订阅者登录(无需 API 密钥)。运行 `hermes model` 并选择 **xAI Grok OAuth (SuperGrok / Premium+)** 进行认证。相同的 OAuth token 可重用于每个直接到 xAI 的接口(聊天、辅助任务、TTS、图像生成、视频生成、转录)。参阅 [xAI Grok OAuth 指南](../guides/xai-grok-oauth.md),如果 Hermes 在远程主机上,请参阅 [SSH/远程主机上的 OAuth](../guides/oauth-over-ssh.md)。\n:::\n\n:::warning `\"main\"` 仅用于辅助任务\n`\"main\"` provider 选项表示\"使用我的主 agent 使用的任何 provider\" —— 它仅在 `auxiliary:`、`compression:` 和 `fallback_model:` 配置中有效。它**不是**顶级 `model.provider` 设置的有效值。如果您使用自定义 OpenAI 兼容端点,请在 `model:` 部分设置 `provider: custom`。所有主模型 provider 选项请参阅 [AI Providers](/integrations/providers)。\n:::\n\n### 完整辅助配置参考\n\n```yaml\nauxiliary:\n # 图像分析(vision_analyze 工具 + 浏览器截图)\n vision:\n provider: \"auto\" # \"auto\"、\"openrouter\"、\"nous\"、\"codex\"、\"main\" 等\n model: \"\" # 例如 \"openai/gpt-4o\"、\"google/gemini-2.5-flash\"\n base_url: \"\" # 自定义 OpenAI 兼容端点(覆盖 provider)\n api_key: \"\" # base_url 的 API 密钥(回退到 OPENAI_API_KEY)\n timeout: 120 # 秒 —— LLM API 调用超时;视觉负载需要宽裕的超时\n download_timeout: 30 # 秒 —— 图像 HTTP 下载;慢速连接请增加\n\n # 网页摘要 + 浏览器页面文本提取\n web_extract:\n provider: \"auto\"\n model: \"\" # 例如 \"google/gemini-2.5-flash\"\n base_url: \"\"\n api_key: \"\"\n timeout: 360 # 秒(6 分钟)—— 每次尝试的 LLM 摘要\n\n # 危险命令审批分类器\n approval:\n provider: \"auto\"\n model: \"\"\n base_url: \"\"\n api_key: \"\"\n timeout: 30 # 秒\n\n # 上下文压缩超时(与 compression.* 配置分开)\n compression:\n timeout: 120 # 秒 —— 压缩摘要长对话,需要更多时间\n # fallback_chain: # 可选 —— 发生速率限制/连接故障时尝试的 provider\n # - provider: nous\n # model: deepseek/deepseek-chat\n # - provider: openrouter\n # model: google/gemini-2.5-flash\n # base_url: \"\"\n # api_key: \"\"\n\n # 技能中心 —— 技能匹配和搜索\n skills_hub:\n provider: \"auto\"\n model: \"\"\n base_url: \"\"\n api_key: \"\"\n timeout: 30\n\n # MCP 工具调度\n mcp:\n provider: \"auto\"\n model: \"\"\n base_url: \"\"\n api_key: \"\"\n timeout: 30\n\n # Kanban 分类规格说明器 —— `hermes kanban specify <id>`(或\n # 仪表板上 Triage 列卡片的 ✨ Specify 按钮)使用此\n # 槽位将单行描述扩展为具体规格并将\n # 任务提升到 `todo`。便宜快速的模型在这里效果很好;规格扩展\n # 很短,不需要推理深度。\n triage_specifier:\n provider: \"auto\"\n model: \"\"\n base_url: \"\"\n api_key: \"\"\n timeout: 120\n```\n\n:::tip\n每个辅助任务都有可配置的 `timeout`(秒)。默认值:vision 120s、web_extract 360s、approval 30s、compression 120s。如果您为辅助任务使用慢速本地模型,请增加这些值。Vision 还有单独的 `download_timeout`(默认 30s)用于 HTTP 图像下载 —— 对于慢速连接或自托管图像服务器,请增加此值。\n:::\n\n:::info\n上下文压缩有自己的 `compression:` 块用于阈值,以及 `auxiliary.compression:` 块用于模型/provider 设置 —— 参阅上方的[上下文压缩](#context-compression)。主备用链使用顶层的 `fallback_providers:` 列表 —— 参阅[备用提供商](/integrations/providers#fallback-providers)。三者都遵循相同的 provider/model/base_url 模式。\n:::\n\n### 辅助任务的每任务回退链\n\n每个辅助任务都可以选择性地定义一个 `fallback_chain` —— 一个 provider/model 条目列表,当主要辅助 provider 因速率限制、网络连接问题或付费限制而失败时,Hermes 会尝试使用该列表:\n\n```yaml\nauxiliary:\n compression:\n provider: openrouter\n model: openai/gpt-4o-mini\n fallback_chain:\n - provider: nous\n model: deepseek/deepseek-chat\n - provider: openrouter\n model: google/gemini-2.5-flash\n```\n\n当主要辅助 provider(`openrouter` / `openai/gpt-4o-mini`)返回速率限制、连接超时或需要付费错误时,Hermes 将依次遍历 `fallback_chain`。它会跳过 provider 与已失败 provider 相同的条目,并尝试每个剩余条目,直到有一个成功或该链耗尽。如果所有回退都失败,Hermes 会回退到主 agent 模型作为最终的安全网。\n\n每个条目支持与任何辅助任务配置相同的三个旋钮:\n\n| 键 | 描述 |\n|-----|-------------|\n| `provider` | Provider 名称(`nous`、`openrouter`、`anthropic`、`gemini`、`main` 等) |\n| `model` | 该 provider 的模型名称 |\n| `base_url` | (可选)自定义 OpenAI 兼容端点 |\n\n`fallback_chain` 适用于任何辅助任务 —— `compression`、`vision`、`web_extract`、`approval`、`skills_hub`、`mcp` 等。\n\n### OpenRouter 路由和辅助任务的 Pareto Code\n\n当辅助任务解析到 OpenRouter(显式或通过 `provider: \"main\"` 而您的主 agent 在 OpenRouter 上)时,主 agent 的 `provider_routing` 和 `openrouter.min_coding_score` 设置**不会传播** —— 按设计,每个辅助任务是独立的。要为特定辅助任务设置 OpenRouter provider 偏好或使用 [Pareto Code 路由器](/integrations/providers#openrouter-pareto-code-router),请通过 `extra_body` 按任务设置:\n\n```yaml\nauxiliary:\n compression:\n provider: openrouter\n model: openrouter/pareto-code # 为此任务使用 Pareto Code 路由器\n extra_body:\n provider: # OpenRouter provider 路由偏好\n order: [anthropic, google] # 按此顺序尝试这些 providers\n sort: throughput # 或 \"price\" | \"latency\"\n # only: [anthropic] # 限制到特定 provider\n # ignore: [deepinfra] # 排除特定 providers\n plugins: # OpenRouter Pareto Code 路由器旋钮\n - id: pareto-router\n min_coding_score: 0.5 # 0.0–1.0;越高 = 更强的编码能力\n```\n\n形状与 OpenRouter 在聊天补全请求体中接受的内容一致。Hermes 原样转发整个 `extra_body`,因此 [openrouter.ai/docs](https://openrouter.ai/docs) 中记录的任何其他 OpenRouter 请求体字段都以相同方式工作。\n\n### 更改视觉模型\n\n使用 GPT-4o 而非 Gemini Flash 进行图像分析:\n\n```yaml\nauxiliary:\n vision:\n model: \"openai/gpt-4o\"\n```\n\n或通过环境变量(在 `~/.hermes/.env` 中):\n\n```bash\nAUXILIARY_VISION_MODEL=openai/gpt-4o\n```\n\n### Provider 选项\n\n这些选项适用于**辅助任务配置**(`auxiliary:`、`compression:`、`fallback_model:`),而非您的主 `model.provider` 设置。\n\n| Provider | 描述 | 要求 |\n|----------|-------------|-------------|\n| `\"auto\"` | 最佳可用(默认)。Vision 尝试 OpenRouter → Nous → Codex。 | — |\n| `\"openrouter\"` | 强制 OpenRouter —— 路由到任何模型(Gemini、GPT-4o、Claude 等) | `OPENROUTER_API_KEY` |\n| `\"nous\"` | 强制 Nous Portal | `hermes auth` |\n| `\"codex\"` | 强制 Codex OAuth(ChatGPT 账户)。支持视觉(gpt-5.3-codex)。 | `hermes model` → Codex |\n| `\"minimax-oauth\"` | 强制 MiniMax OAuth(浏览器登录,无需 API 密钥)。辅助任务使用 MiniMax-M2.7-highspeed。 | `hermes model` → MiniMax (OAuth) |\n| `\"xai-oauth\"` | 强制 xAI Grok OAuth(SuperGrok 或 X Premium+ 订阅者的浏览器登录,无需 API 密钥)。相同的 OAuth token 涵盖聊天、TTS、图像、视频和转录。 | `hermes model` → xAI Grok OAuth (SuperGrok / Premium+) |\n| `\"main\"` | 使用您的活跃自定义/主端点。可以来自 `OPENAI_BASE_URL` + `OPENAI_API_KEY` 或通过 `hermes model` / `config.yaml` 保存的自定义端点。适用于 OpenAI、本地模型或任何 OpenAI 兼容 API。**仅限辅助任务 —— 对 `model.provider` 无效。** | 自定义端点凭据 + 基础 URL |\n\n当您希望附带任务绕过默认路由器时,主 provider 目录中的直接 API 密钥 providers 也在这里工作。配置 `GMI_API_KEY` 后,`gmi` 有效:\n\n```yaml\nauxiliary:\n compression:\n provider: \"gmi\"\n model: \"anthropic/claude-opus-4.6\"\n```\n\n对于 GMI 辅助路由,使用 GMI 的 `/v1/models` 端点返回的确切模型 ID。\n\n### 常见设置\n\n**使用直接自定义端点**(比 `provider: \"main\"` 对本地/自托管 API 更清晰):\n```yaml\nauxiliary:\n vision:\n base_url: \"http://localhost:1234/v1\"\n api_key: \"local-key\"\n model: \"qwen2.5-vl\"\n```\n\n`base_url` 优先于 `provider`,因此这是将辅助任务路由到特定端点的最明确方式。对于直接端点覆盖,Hermes 使用配置的 `api_key` 或回退到 `OPENAI_API_KEY`;它不会为该自定义端点重用 `OPENROUTER_API_KEY`。\n\n**使用 OpenAI API 密钥进行视觉:**\n```yaml\n# 在 ~/.hermes/.env 中:\n# OPENAI_BASE_URL=https://api.openai.com/v1\n# OPENAI_API_KEY=sk-...\n\nauxiliary:\n vision:\n provider: \"main\"\n model: \"gpt-4o\" # 或 \"gpt-4o-mini\" 更便宜\n```\n\n**使用 OpenRouter 进行视觉**(路由到任何模型):\n```yaml\nauxiliary:\n vision:\n provider: \"openrouter\"\n model: \"openai/gpt-4o\" # 或 \"google/gemini-2.5-flash\" 等\n```\n\n**使用 Codex OAuth**(ChatGPT Pro/Plus 账户 —— 无需 API 密钥):\n```yaml\nauxiliary:\n vision:\n provider: \"codex\" # 使用您的 ChatGPT OAuth token\n # 模型默认为 gpt-5.3-codex(支持视觉)\n```\n\n**使用 MiniMax OAuth**(浏览器登录,无需 API 密钥):\n```yaml\nmodel:\n default: MiniMax-M2.7\n provider: minimax-oauth\n base_url: https://api.minimax.io/anthropic\n```\n运行 `hermes model` 并选择 **MiniMax (OAuth)** 自动登录并设置此项。对于中国区域,基础 URL 将是 `https://api.minimaxi.com/anthropic`。完整演练请参阅 [MiniMax OAuth 指南](../guides/minimax-oauth.md)。\n\n**使用本地/自托管模型:**\n```yaml\nauxiliary:\n vision:\n provider: \"main\" # 使用您的活跃自定义端点\n model: \"my-local-model\"\n```\n\n`provider: \"main\"` 使用 Hermes 用于普通聊天的任何 provider —— 无论是命名的自定义 provider(例如 `beans`)、内置 provider(如 `openrouter`)还是旧版 `OPENAI_BASE_URL` 端点。\n\n:::tip\n如果您使用 Codex OAuth 作为主模型 provider,视觉会自动工作 —— 无需额外配置。Codex 包含在视觉的自动检测链中。\n:::\n\n:::warning\n**视觉需要多模态模型。** 如果您设置 `provider: \"main\"`,请确保您的端点支持多模态/视觉 —— 否则图像分析将失败。\n:::\n\n### 环境变量(旧版)\n\n辅助模型也可以通过环境变量配置。但是,`config.yaml` 是首选方法 —— 它更易于管理,并支持所有选项,包括 `base_url` 和 `api_key`。\n\n| 设置 | 环境变量 |\n|---------|---------------------|\n| Vision provider | `AUXILIARY_VISION_PROVIDER` |\n| Vision 模型 | `AUXILIARY_VISION_MODEL` |\n| Vision 端点 | `AUXILIARY_VISION_BASE_URL` |\n| Vision API 密钥 | `AUXILIARY_VISION_API_KEY` |\n| Web 提取 provider | `AUXILIARY_WEB_EXTRACT_PROVIDER` |\n| Web 提取模型 | `AUXILIARY_WEB_EXTRACT_MODEL` |\n| Web 提取端点 | `AUXILIARY_WEB_EXTRACT_BASE_URL` |\n| Web 提取 API 密钥 | `AUXILIARY_WEB_EXTRACT_API_KEY` |\n\n压缩和回退模型设置仅限 config.yaml。\n\n:::tip\n运行 `hermes config` 查看您当前的辅助模型设置。覆盖仅在与默认值不同时显示。\n:::\n\n## 推理努力程度\n\n控制模型在响应前进行多少\"思考\":\n\n```yaml\nagent:\n reasoning_effort: \"\" # 空 = 中等。选项:none、minimal、low、medium、high、xhigh、max、ultra\n```\n\n未设置时(默认),推理努力程度默认为\"medium\" —— 适合大多数任务的平衡级别。设置值会覆盖它 —— 更高的推理努力程度在复杂任务上提供更好的结果,但代价是更多 token 和延迟。\n\n您也可以在运行时使用 `/reasoning` 命令更改推理努力程度:\n\n```\n/reasoning # 显示当前努力程度和显示状态\n/reasoning high # 将推理努力程度设为 high\n/reasoning none # 禁用推理\n/reasoning show # 在每次响应上方显示模型思考\n/reasoning hide # 隐藏模型思考\n```\n\n## 工具使用强制\n\n某些模型偶尔会将预期操作描述为文本而不是进行工具调用(\"我会运行测试...\"而不是实际调用终端)。工具使用强制会注入系统提示词指导,引导模型实际调用工具。\n\n```yaml\nagent:\n tool_use_enforcement: \"auto\" # \"auto\" | true | false | [\"model-substring\", ...]\n```\n\n| 值 | 行为 |\n|-------|----------|\n| `\"auto\"`(默认) | 对匹配以下模型启用:`gpt`、`codex`、`gemini`、`gemma`、`grok`。对所有其他模型禁用(Claude、DeepSeek、Qwen 等)。 |\n| `true` | 始终启用,无论模型如何。如果您注意到当前模型描述操作而不是执行操作,请使用此选项。 |\n| `false` | 始终禁用,无论模型如何。 |\n| `[\"gpt\", \"codex\", \"qwen\", \"llama\"]` | 仅当模型名称包含列出的子字符串之一时启用(不区分大小写)。 |\n\n### 注入的内容\n\n启用后,系统提示词中可能会添加三层指导:\n\n1. **通用工具使用强制**(所有匹配模型)—— 指示模型立即进行工具调用而不是描述意图,持续工作直到任务完成,永远不要以未来操作的承诺结束轮次。\n\n2. **OpenAI 执行纪律**(仅限 GPT 和 Codex 模型)—— 针对 GPT 特定失败模式的额外指导:在部分结果上放弃工作、跳过先决条件查找、幻觉而不是使用工具、在未验证的情况下宣布\"完成\"。\n\n3. **Google 操作指导**(仅限 Gemini 和 Gemma 模型)—— 简洁性、绝对路径、并行工具调用和编辑前验证模式。\n\n这些对用户透明,仅影响系统提示词。已经可靠使用工具的模型(如 Claude)不需要此指导,这就是为什么 `\"auto\"` 排除它们。\n\n### 何时开启\n\n如果您使用的模型不在默认自动列表中,并注意到它经常描述它*会*做什么而不是实际去做,请设置 `tool_use_enforcement: true` 或将模型子字符串添加到列表中:\n\n```yaml\nagent:\n tool_use_enforcement: [\"gpt\", \"codex\", \"gemini\", \"grok\", \"my-custom-model\"]\n```\n\n## TTS 配置\n\n```yaml\ntts:\n provider: \"edge\" # \"edge\" | \"elevenlabs\" | \"openai\" | \"minimax\" | \"mistral\" | \"gemini\" | \"xai\" | \"neutts\"\n speed: 1.0 # 全局速度倍数(所有 provider 的回退)\n edge:\n voice: \"en-US-AriaNeural\" # 322 种声音,74 种语言\n speed: 1.0 # 速度倍数(转换为速率百分比,例如 1.5 → +50%)\n elevenlabs:\n voice_id: \"pNInz6obpgDQGcFmaJgB\"\n model_id: \"eleven_multilingual_v2\"\n openai:\n model: \"gpt-4o-mini-tts\"\n voice: \"alloy\" # alloy、echo、fable、onyx、nova、shimmer\n speed: 1.0 # 速度倍数(API 限制为 0.25–4.0)\n base_url: \"https://api.openai.com/v1\" # 覆盖 OpenAI 兼容 TTS 端点\n minimax:\n speed: 1.0 # 语音速度倍数\n # base_url: \"\" # 可选:覆盖 OpenAI 兼容 TTS 端点\n mistral:\n model: \"voxtral-mini-tts-2603\"\n voice_id: \"c69964a6-ab8b-4f8a-9465-ec0925096ec8\" # Paul - Neutral(默认)\n gemini:\n model: \"gemini-2.5-flash-preview-tts\" # 或 gemini-2.5-pro-preview-tts\n voice: \"Kore\" # 30 种预置声音:Zephyr、Puck、Kore、Enceladus 等\n xai:\n voice_id: \"eve\" # xAI TTS 声音\n language: \"en\" # ISO 639-1\n sample_rate: 24000\n bit_rate: 128000 # MP3 比特率\n # base_url: \"https://api.x.ai/v1\"\n neutts:\n ref_audio: ''\n ref_text: ''\n model: neuphonic/neutts-air-q4-gguf\n device: cpu\n```\n\n这控制 `text_to_speech` 工具和语音模式中的口语回复(CLI 中的 `/voice tts` 或消息 gateway)。\n\n**速度回退层次:** provider 特定速度(例如 `tts.edge.speed`)→ 全局 `tts.speed` → `1.0` 默认值。设置全局 `tts.speed` 以在所有 provider 上应用统一速度,或按 provider 覆盖以进行精细控制。\n\n## 显示设置\n\n```yaml\ndisplay:\n tool_progress: all # off | new | all | verbose\n tool_progress_command: false # 在消息 gateway 中启用 /verbose 斜杠命令\n platforms: {} # 每平台显示覆盖(见下文)\n tool_progress_overrides: {} # 已弃用 —— 改用 display.platforms\n interim_assistant_messages: true # Gateway:将自然的轮次中 assistant 更新作为单独消息发送\n skin: default # 内置或自定义 CLI 皮肤(参阅 user-guide/features/skins)\n personality: \"kawaii\" # 旧版外观字段,仍在某些摘要中显示\n compact: false # 紧凑输出模式(减少空白)\n resume_display: full # full(恢复时显示之前的消息)| minimal(仅单行)\n bell_on_complete: false # 当 agent 完成时播放终端铃声(适合长任务)\n show_reasoning: false # 在每次响应上方显示模型推理/思考(用 /reasoning show|hide 切换)\n streaming: false # 将 token 实时流式传输到终端\n show_cost: false # 在 CLI 状态栏中显示估计 $ 成本\n timestamps: false # 为 true 时,在 CLI/TUI 记录中为用户和 assistant 标签添加 [HH:MM] 时间戳前缀\n tool_preview_length: 0 # 工具调用预览的最大字符数(0 = 无限制,显示完整路径/命令)\n runtime_footer: # Gateway:在最终回复中附加运行时上下文页脚\n enabled: false\n fields: [\"model\", \"context_pct\", \"cwd\"]\n file_mutation_verifier: true # 当本轮 write_file/patch 调用失败时附加建议性页脚\n language: en # 静态消息的 UI 语言(审批提示、部分 gateway 回复)。en | zh | zh-hant | ja | de | es | fr | tr | uk | af | ko | it | ga | pt | ru | hu\n```\n\n### 文件变更验证器\n\n当 `display.file_mutation_verifier` 为 `true`(默认)时,每当本轮中 `write_file` 或 `patch` 调用失败且从未被对同一路径的成功写入取代时,Hermes 会在 assistant 的最终响应中附加一行建议。这捕获了\"批量并行补丁,一半静默失败,模型总结成功\"这类过度声明,而无需您在每次编辑后手动运行 `git status`。\n\n示例页脚:\n\n```\n⚠️ File-mutation verifier: 3 file(s) were NOT modified this turn despite any wording above that may suggest otherwise. Run `git status` or `read_file` to confirm.\n • concepts/automatic-organization.md — [patch] Could not find match for old_string\n • concepts/lora.md — [patch] Could not find match for old_string\n • concepts/rag-pipeline.md — [patch] Could not find match for old_string\n```\n\n设置 `file_mutation_verifier: false`(或 `HERMES_FILE_MUTATION_VERIFIER=0`)以禁止页脚。验证器仅在轮次结束时有真实失败未解决时触发 —— 在同一轮次内重试失败补丁并成功的模型不会为该文件触发它。\n\n### 静态消息的 UI 语言\n\n`display.language` 设置翻译一小组静态面向用户的消息 —— CLI 审批提示、少数 gateway 斜杠命令回复(例如重启排空通知、\"审批已过期\"、\"目标已清除\")。它**不**翻译 agent 响应、日志行、工具输出、错误回溯或斜杠命令描述 —— 这些保持英文。如果您希望 agent 本身用另一种语言回复,只需在您的提示词或系统消息中告诉它。\n\n支持的值:`en`(默认)、`zh`(简体中文)、`zh-hant`(繁体中文)、`ja`(日语)、`de`(德语)、`es`(西班牙语)、`fr`(法语)、`tr`(土耳其语)、`uk`(乌克兰语)、`af`(南非荷兰语)、`ko`(韩语)、`it`(意大利语)、`ga`(爱尔兰语)、`pt`(葡萄牙语)、`ru`(俄语)、`hu`(匈牙利语)。未知值回退到英文。\n\n您也可以使用 `HERMES_LANGUAGE` 环境变量按会话设置,它会覆盖配置值。\n\n```yaml\ndisplay:\n language: zh # CLI 审批提示以中文显示\n```\n\n| 模式 | 您看到的内容 |\n|------|-------------|\n| `off` | 静默 —— 仅最终响应 |\n| `new` | 仅在工具更改时显示工具指示器 |\n| `all` | 每次工具调用附带简短预览(默认) |\n| `verbose` | 完整参数、结果和调试日志 |\n\n在 CLI 中,使用 `/verbose` 循环切换这些模式。要在消息平台(Telegram、Discord、Slack 等)中使用 `/verbose`,请在上方的 `display` 部分设置 `tool_progress_command: true`。该命令将循环切换模式并保存到配置。\n\n### 运行时元数据页脚(仅限 gateway)\n\n当 `display.runtime_footer.enabled: true` 时,Hermes 在每个 gateway 轮次的**最终**消息中附加一个小型运行时上下文页脚。目前页脚可显示模型、上下文窗口百分比和当前工作目录。默认关闭;如果您的团队希望每个回复都包含这些来源信息,请按 gateway 选择加入。\n\n```yaml\ndisplay:\n runtime_footer:\n enabled: true\n fields: [\"model\", \"context_pct\", \"cwd\"] # 支持字段:model、context_pct、cwd\n```\n\n`/footer` 斜杠命令在任何会话中运行时切换此功能。\n\n附加到 Telegram/Discord/Slack 回复的示例页脚:\n\n```\n— claude-opus-4.7 · 12 tool calls · 2m 14s · $0.042\n```\n\n只有轮次的**最终**消息获得页脚;中间更新保持干净。\n\n### 每平台进度覆盖\n\n不同平台有不同的详细程度需求。例如,Signal 无法编辑消息,因此每次进度更新都会成为单独的消息 —— 很嘈杂。使用 `display.platforms` 设置每平台模式:\n\n```yaml\ndisplay:\n tool_progress: all # 全局默认\n platforms:\n signal:\n tool_progress: 'off' # 在 Signal 上静默进度\n telegram:\n tool_progress: verbose # 在 Telegram 上详细进度\n slack:\n tool_progress: 'off' # 在共享 Slack 工作区中保持安静\n```\n\n没有覆盖的平台回退到全局 `tool_progress` 值。有效平台键:`telegram`、`discord`、`slack`、`signal`、`whatsapp`、`matrix`、`mattermost`、`email`、`sms`、`homeassistant`、`dingtalk`、`feishu`、`wecom`、`weixin`、`bluebubbles`、`qqbot`。旧版 `display.tool_progress_overrides` 键仍可加载以向后兼容,但已弃用,并在首次加载时迁移到 `display.platforms`。\n\n`interim_assistant_messages` 仅限 gateway。启用后,Hermes 将已完成的轮次中 assistant 更新作为单独的聊天消息发送。这与 `tool_progress` 无关,不需要 gateway 流式传输。\n\n## 隐私\n\n```yaml\nprivacy:\n redact_pii: false # 从 LLM 上下文中删除 PII(仅限 gateway)\n```\n\n当 `redact_pii` 为 `true` 时,gateway 在将系统提示词发送到受支持平台上的 LLM 之前,会从中删除个人身份信息:\n\n| 字段 | 处理方式 |\n|-------|-----------|\n| 电话号码(WhatsApp/Signal 上的用户 ID) | 哈希为 `user_<12-char-sha256>` |\n| 用户 ID | 哈希为 `user_<12-char-sha256>` |\n| 聊天 ID | 数字部分哈希,保留平台前缀(`telegram:<hash>`) |\n| 主频道 ID | 数字部分哈希 |\n| 用户名/昵称 | **不受影响**(用户选择的,公开可见) |\n\n**平台支持:** 删除适用于 WhatsApp、Signal 和 Telegram。Discord 和 Slack 被排除,因为它们的提及系统(`<@user_id>`)需要 LLM 上下文中的真实 ID。\n\n哈希是确定性的 —— 同一用户始终映射到同一哈希,因此模型仍然可以在群聊中区分用户。路由和传递在内部使用原始值。\n\n## 语音转文字(STT)\n\n```yaml\nstt:\n provider: \"local\" # \"local\" | \"groq\" | \"openai\" | \"mistral\"\n local:\n model: \"base\" # tiny、base、small、medium、large-v3\n openai:\n model: \"whisper-1\" # whisper-1 | gpt-4o-mini-transcribe | gpt-4o-transcribe\n # model: \"whisper-1\" # 旧版回退键仍受支持\n```\n\nProvider 行为:\n\n- `local` 使用在您机器上运行的 `faster-whisper`。使用 `pip install faster-whisper` 单独安装。\n- `groq` 使用 Groq 的 Whisper 兼容端点,读取 `GROQ_API_KEY`。\n- `openai` 使用 OpenAI 语音 API,读取 `VOICE_TOOLS_OPENAI_KEY`。\n\n如果请求的 provider 不可用,Hermes 按此顺序自动回退:`local` → `groq` → `openai`。\n\nGroq 和 OpenAI 模型覆盖由环境变量驱动:\n\n```bash\nSTT_GROQ_MODEL=whisper-large-v3-turbo\nSTT_OPENAI_MODEL=whisper-1\nGROQ_BASE_URL=https://api.groq.com/openai/v1\nSTT_OPENAI_BASE_URL=https://api.openai.com/v1\n```\n\n## 语音模式(CLI)\n\n```yaml\nvoice:\n record_key: \"ctrl+b\" # CLI 内的按键通话键\n max_recording_seconds: 120 # 长录音的硬停止\n auto_tts: false # /voice on 时自动启用口语回复\n beep_enabled: true # 在 CLI 语音模式中播放录音开始/停止提示音\n silence_threshold: 200 # 语音检测的 RMS 阈值\n silence_duration: 3.0 # 自动停止前的静默秒数\n```\n\n在 CLI 中使用 `/voice on` 启用麦克风模式,使用 `record_key` 开始/停止录音,使用 `/voice tts` 切换口语回复。端到端设置和平台特定行为请参阅[语音模式](/user-guide/features/voice-mode)。\n\n## 流式传输\n\n将 token 实时流式传输到终端或消息平台,而不是等待完整响应。\n\n### CLI 流式传输\n\n```yaml\ndisplay:\n streaming: true # 实时将 token 流式传输到终端\n show_reasoning: true # 同时流式传输推理/思考 token(可选)\n```\n\n启用后,响应在流式传输框内逐 token 出现。工具调用仍然静默捕获。如果 provider 不支持流式传输,它会自动回退到正常显示。\n\n### Gateway 流式传输(Telegram、Discord、Slack)\n\n```yaml\nstreaming:\n enabled: true # 启用渐进式消息编辑\n transport: edit # \"edit\"(渐进式消息编辑)或 \"off\"\n edit_interval: 0.3 # 消息编辑之间的秒数\n buffer_threshold: 40 # 强制编辑刷新前的字符数\n cursor: \" ▉\" # 流式传输期间显示的光标\n fresh_final_after_seconds: 0 # 预览超过此时间时选择发送新的最终消息(Telegram)\n```\n\n启用后,bot 在第一个 token 时发送消息,然后随着更多 token 到来渐进式编辑它。不支持消息编辑的平台(Signal、Email、Home Assistant)在第一次尝试时自动检测 —— 该会话的流式传输被优雅地禁用,不会产生大量消息。\n\n对于不带渐进式 token 编辑的独立自然轮次中 assistant 更新,请设置 `display.interim_assistant_messages: true`。\n\n**溢出处理:** 如果流式传输的文本超过平台的消息长度限制(约 4096 字符),当前消息被最终化,新消息自动开始。\n\n**新的最终消息(Telegram):** Telegram 的 `editMessageText` 保留原始消息时间戳,因此长时间运行的流式回复即使在完成后也会保留第一个 token 的时间戳。设置 `fresh_final_after_seconds > 0` 可选择将旧预览作为全新的最终消息传递,并尽力删除旧预览。默认值为 `0`,始终就地最终化流式回复,避免某些客户端短暂显示重复消息再删除其中一条。\n\n:::note\n主开关 `streaming.enabled` 默认为 `false`——在你启用之前不会有任何流式传输。启用后,是否流式传输按**平台**决定:Telegram 默认带有 `display.platforms.telegram.streaming: true`(流式传输),Discord 为 `display.platforms.discord.streaming: false`(不流式传输)。因此启用流式传输后,Telegram 开箱即用地流式传输,Discord 在你修改其开关之前仍使用整条消息回复。你可以在仪表盘的 **Channels** 开关中或直接在 `~/.hermes/config.yaml` 中调整这些按平台的开关。\n:::\n\n## 群聊会话隔离\n\n控制共享聊天是每个房间保持一个对话还是每个参与者一个对话:\n\n```yaml\ngroup_sessions_per_user: true # true = 群组/频道中每用户隔离,false = 每个聊天一个共享会话\n```\n\n- `true` 是默认和推荐设置。在 Discord 频道、Telegram 群组、Slack 频道和类似共享上下文中,当平台提供用户 ID 时,每个发送者获得自己的会话。\n- `false` 恢复到旧的共享房间行为。如果您明确希望 Hermes 将频道视为一个协作对话,这可能有用,但这也意味着用户共享上下文、token 成本和中断状态。\n- 私信不受影响。Hermes 仍然像往常一样通过聊天/DM ID 键入 DM。\n- 线程与其父频道保持隔离;使用 `true` 时,每个参与者在线程内也获得自己的会话。\n\n有关行为详情和示例,请参阅[会话](/user-guide/sessions)和 [Discord 指南](/user-guide/messaging/discord)。\n\n## 未授权 DM 行为\n\n控制当未知用户发送私信时 Hermes 的行为:\n\n```yaml\nunauthorized_dm_behavior: pair\n\nwhatsapp:\n unauthorized_dm_behavior: ignore\n```\n\n- `pair` 是默认值。Hermes 拒绝访问,但在 DM 中回复一次性配对码。\n- `ignore` 静默丢弃未授权的 DM。\n- 平台部分覆盖全局默认值,因此您可以在广泛范围内保持配对启用,同时使一个平台更安静。\n\n## 快速命令\n\n定义自定义命令,这些命令要么在不调用 LLM 的情况下运行 shell 命令,要么将一个斜杠命令别名为另一个。Exec 快速命令是零 token 的,对于从消息平台(Telegram、Discord 等)进行快速服务器检查或实用脚本很有用。\n\n```yaml\nquick_commands:\n status:\n type: exec\n command: systemctl status hermes-agent\n disk:\n type: exec\n command: df -h /\n update:\n type: exec\n command: cd ~/.hermes/hermes-agent && git pull && uv pip install -e .\n gpu:\n type: exec\n command: nvidia-smi --query-gpu=name,utilization.gpu,memory.used,memory.total --format=csv,noheader\n restart:\n type: alias\n target: /gateway restart\n```\n\n用法:在 CLI 或任何消息平台中输入 `/status`、`/disk`、`/update`、`/gpu` 或 `/restart`。`exec` 命令在宿主本地运行并直接返回输出 —— 无 LLM 调用,不消耗 token。`alias` 命令重写为配置的斜杠命令目标。\n\n- **30 秒超时** —— 长时间运行的命令被终止并显示错误消息\n- **优先级** —— 快速命令在技能命令之前检查,因此您可以覆盖技能名称\n- **自动补全** —— 快速命令在调度时解析,不显示在内置斜杠命令自动补全表中\n- **类型** —— 支持的类型为 `exec` 和 `alias`;其他类型显示错误\n- **到处可用** —— CLI、Telegram、Discord、Slack、WhatsApp、Signal、Email、Home Assistant\n\n仅字符串的 prompt 快捷方式不是有效的快速命令。对于可重用的 prompt 工作流,请创建技能或别名到现有斜杠命令。\n\n## 人类延迟\n\n在消息平台中模拟类人响应节奏:\n\n```yaml\nhuman_delay:\n mode: \"off\" # off | natural | custom\n min_ms: 800 # 最小延迟(自定义模式)\n max_ms: 2500 # 最大延迟(自定义模式)\n```\n\n## 代码执行\n\n配置 `execute_code` 工具:\n\n```yaml\ncode_execution:\n mode: project # project(默认)| strict\n timeout: 300 # 最大执行时间(秒)\n max_tool_calls: 50 # 代码执行中的最大工具调用次数\n```\n\n**`mode`** 控制脚本的工作目录和 Python 解释器:\n\n- **`project`**(默认)—— 脚本在会话的工作目录中以活跃 virtualenv/conda 环境的 python 运行。项目依赖(`pandas`、`torch`、项目包)和相对路径(`.env`、`./data.csv`)自然解析,与 `terminal()` 看到的一致。\n- **`strict`** —— 脚本在临时暂存目录中以 `sys.executable`(Hermes 自己的 python)运行。最大可重现性,但项目依赖和相对路径不会解析。\n\n环境清理(删除 `*_API_KEY`、`*_TOKEN`、`*_SECRET`、`*_PASSWORD`、`*_CREDENTIAL`、`*_PASSWD`、`*_AUTH`)和工具白名单在两种模式下完全相同 —— 切换模式不会改变安全态势。\n\n## Web 搜索后端\n\n`web_search` 和 `web_extract` 工具支持五种后端 provider。在 `config.yaml` 中或通过 `hermes tools` 配置后端:\n\n```yaml\nweb:\n backend: firecrawl # firecrawl | searxng | parallel | tavily | exa\n\n # 或使用每功能键混合 provider(例如免费搜索 + 付费提取):\n search_backend: \"searxng\"\n extract_backend: \"firecrawl\"\n```\n\n| 后端 | 环境变量 | 搜索 | 提取 |\n|---------|---------|--------|---------|\n| **Firecrawl**(默认) | `FIRECRAWL_API_KEY` | ✔ | ✔ |\n| **SearXNG** | `SEARXNG_URL` | ✔ | — |\n| **Parallel** | `PARALLEL_API_KEY` | ✔ | ✔ |\n| **Tavily** | `TAVILY_API_KEY` | ✔ | ✔ |\n| **Exa** | `EXA_API_KEY` | ✔ | ✔ |\n\n**后端选择:** 如果未设置 `web.backend`,后端从可用的 API 密钥自动检测。如果仅设置了 `SEARXNG_URL`,使用 SearXNG。如果仅设置了 `EXA_API_KEY`,使用 Exa。如果仅设置了 `TAVILY_API_KEY`,使用 Tavily。如果仅设置了 `PARALLEL_API_KEY`,使用 Parallel。否则 Firecrawl 是默认值。\n\n**SearXNG** 是一个免费、自托管、尊重隐私的元搜索引擎,查询 70+ 个搜索引擎。无需 API 密钥 —— 只需将 `SEARXNG_URL` 设置为您的实例(例如 `http://localhost:8080`)。SearXNG 仅限搜索;`web_extract` 需要单独的提取 provider(设置 `web.extract_backend`)。Docker 设置说明请参阅 [Web 搜索设置指南](/user-guide/features/web-search)。\n\n**自托管 Firecrawl:** 设置 `FIRECRAWL_API_URL` 指向您自己的实例。设置自定义 URL 后,API 密钥变为可选(在服务器上设置 `USE_DB_AUTHENTICATION=***` 以禁用认证)。\n\n**Parallel 搜索模式:** 设置 `PARALLEL_SEARCH_MODE` 控制搜索行为 —— `fast`、`one-shot` 或 `agentic`(默认:`agentic`)。\n\n**Exa:** 在 `~/.hermes/.env` 中设置 `EXA_API_KEY`。支持 `category` 过滤(`company`、`research paper`、`news`、`people`、`personal site`、`pdf`)和域名/日期过滤器。\n\n## 浏览器\n\n配置浏览器自动化行为:\n\n```yaml\nbrowser:\n inactivity_timeout: 120 # 自动关闭空闲会话前的秒数\n command_timeout: 30 # 浏览器命令超时(截图、导航等)(秒)\n record_sessions: false # 自动将浏览器会话录制为 WebM 视频到 ~/.hermes/browser_recordings/\n # 可选 CDP 覆盖 —— 设置后,Hermes 直接附加到您自己的\n # Chromium 系浏览器(通过 /browser connect),而不是启动无头浏览器。\n cdp_url: \"\"\n # 对话框监督器 —— 控制当 CDP 后端附加时(Browserbase、本地 Chromium 系\n # 浏览器通过 /browser connect)如何处理原生 JS 对话框(alert/confirm/prompt)。\n # 在 Camofox 和默认本地 agent 浏览器模式下忽略。\n dialog_policy: must_respond # must_respond | auto_dismiss | auto_accept\n dialog_timeout_s: 300 # must_respond 下的安全自动关闭(秒)\n camofox:\n managed_persistence: false # 为 true 时,Camofox 会话跨重启持久化 cookie/登录\n user_id: \"\" # 可选的外部管理 Camofox userId\n session_key: \"\" # Hermes 创建标签页时发送的可选会话密钥\n adopt_existing_tab: false # 在创建新标签页之前重用此身份的现有标签页\n```\n\n**对话框策略:**\n\n- `must_respond`(默认)—— 捕获对话框,在 `browser_snapshot.pending_dialogs` 中显示,等待 agent 调用 `browser_dialog(action=...)`。在 `dialog_timeout_s` 秒内无响应后,对话框被自动关闭以防止页面的 JS 线程永久停滞。\n- `auto_dismiss` —— 捕获,立即关闭。Agent 仍然在事后的 `browser_snapshot.recent_dialogs` 中看到对话框记录,`closed_by=\"auto_policy\"`。\n- `auto_accept` —— 捕获,立即接受。适用于有激进 `beforeunload` 提示的页面。\n\n完整对话框工作流请参阅[浏览器功能页面](./features/browser.md#browser_dialog)。\n\n浏览器工具集支持多个 provider。有关 Browserbase、Browser Use 和本地 Chromium 系 CDP 设置的详细信息,请参阅[浏览器功能页面](/user-guide/features/browser)。\n\n## 时区\n\n使用 IANA 时区字符串覆盖服务器本地时区。影响日志中的时间戳、cron 调度和系统提示词时间注入。\n\n```yaml\ntimezone: \"America/New_York\" # IANA 时区(默认:\"\" = 服务器本地时间)\n```\n\n支持的值:任何 IANA 时区标识符(例如 `America/New_York`、`Europe/London`、`Asia/Kolkata`、`UTC`)。留空或省略以使用服务器本地时间。\n\n## Discord\n\n为消息 gateway 配置 Discord 特定行为:\n\n```yaml\ndiscord:\n require_mention: true # 在服务器频道中需要 @提及才能响应\n free_response_channels: \"\" # 逗号分隔的频道 ID,bot 在这些频道无需 @提及即可响应\n auto_thread: true # 在频道中 @提及时自动创建线程\n```\n\n- `require_mention` —— 为 `true`(默认)时,bot 仅在服务器频道中被 `@BotName` 提及时响应。DM 始终无需提及即可工作。\n- `free_response_channels` —— 逗号分隔的频道 ID 列表,bot 在这些频道对每条消息响应,无需提及。\n- `auto_thread` —— 为 `true`(默认)时,频道中的提及会自动为对话创建线程,保持频道整洁(类似 Slack 线程)。\n\n## 安全\n\n预执行安全扫描和机密脱敏:\n\n```yaml\nsecurity:\n redact_secrets: false # 在工具输出和日志中脱敏 API 密钥模式(默认关闭)\n tirith_enabled: true # 为终端命令启用 Tirith 安全扫描\n tirith_path: \"tirith\" # tirith 二进制文件路径(默认:$PATH 中的 \"tirith\")\n tirith_timeout: 5 # 等待 tirith 扫描的秒数\n tirith_fail_open: true # 如果 tirith 不可用,允许命令执行\n website_blocklist: # 参见下方网站黑名单部分\n enabled: false\n domains: []\n shared_files: []\n```\n\n- `redact_secrets` —— 为 `true` 时,自动检测并脱敏工具输出中看起来像 API 密钥、token 和密码的模式,然后再进入对话上下文和日志。**默认关闭** —— 如果您经常在工具输出中处理真实凭据并希望有安全网,请启用。显式设置为 `true` 以开启。\n- `tirith_enabled` —— 为 `true` 时,终端命令在执行前由 [Tirith](https://github.com/sheeki03/tirith) 扫描以检测潜在危险操作。\n- `tirith_path` —— tirith 二进制文件的路径。如果 tirith 安装在非标准位置,请设置此项。\n- `tirith_timeout` —— 等待 tirith 扫描的最大秒数。如果扫描超时,命令继续执行。\n- `tirith_fail_open` —— 为 `true`(默认)时,如果 tirith 不可用或失败,允许命令执行。设置为 `false` 以在 tirith 无法验证时阻止命令。\n\n## 网站黑名单\n\n阻止 agent 的 web 和浏览器工具访问特定域名:\n\n```yaml\nsecurity:\n website_blocklist:\n enabled: false # 启用 URL 阻止(默认:false)\n domains: # 被阻止的域名模式列表\n - \"*.internal.company.com\"\n - \"admin.example.com\"\n - \"*.local\"\n shared_files: # 从外部文件加载额外规则\n - \"/etc/hermes/blocked-sites.txt\"\n```\n\n启用后,任何匹配被阻止域名模式的 URL 在 web 或浏览器工具执行之前都会被拒绝。这适用于 `web_search`、`web_extract`、`browser_navigate` 以及任何访问 URL 的工具。\n\n域名规则支持:\n- 精确域名:`admin.example.com`\n- 通配符子域名:`*.internal.company.com`(阻止所有子域名)\n- TLD 通配符:`*.local`\n\n共享文件每行包含一条域名规则(空行和 `#` 注释被忽略)。缺失或不可读的文件记录警告,但不禁用其他 web 工具。\n\n策略缓存 30 秒,因此配置更改无需重启即可快速生效。\n\n## 智能审批\n\n控制 Hermes 如何处理潜在危险命令:\n\n```yaml\napprovals:\n mode: smart # smart | manual | off\n```\n\n| 模式 | 行为 |\n|------|----------|\n| `smart`(默认) | 使用辅助 LLM 评估被标记的命令是否真正危险。低风险命令仅对当前命令自动批准,真正危险的命令自动拒绝,不确定的情况升级给用户。 |\n| `manual` | 在执行任何被标记的命令之前提示用户。在 CLI 中显示交互式审批对话框。在消息中排队待处理的审批请求。 |\n| `off` | 跳过所有审批检查。等同于 `HERMES_YOLO_MODE=true`。**谨慎使用。** |\n\n智能模式对于减少审批疲劳特别有用 —— 它让 agent 在安全操作上更自主地工作,同时仍然捕获真正破坏性的命令。\n\n:::warning\n设置 `approvals.mode: off` 会禁用终端命令的所有安全检查。仅在受信任的沙箱环境中使用。\n:::\n\n## 检查点\n\n破坏性文件操作之前的自动文件系统快照。详情请参阅[检查点与回滚](/user-guide/checkpoints-and-rollback)。\n\n```yaml\ncheckpoints:\n enabled: false # 启用自动检查点(也可:hermes chat --checkpoints)。默认:false(选择加入)。\n max_snapshots: 20 # 每个目录保留的最大检查点数(默认:20)\n```\n\n\n## 委托\n\n为委托工具配置子 agent 行为:\n\n```yaml\ndelegation:\n # model: \"google/gemini-3-flash-preview\" # 覆盖模型(空 = 继承父级)\n # provider: \"openrouter\" # 覆盖 provider(空 = 继承父级)\n # base_url: \"http://localhost:1234/v1\" # 直接 OpenAI 兼容端点(优先于 provider)\n # api_key: \"local-key\" # base_url 的 API 密钥(回退到 OPENAI_API_KEY)\n # api_mode: \"\" # base_url 的线路协议:\"chat_completions\"、\"codex_responses\" 或 \"anthropic_messages\"。空 = 从 URL 自动检测(例如 /anthropic 后缀 → anthropic_messages)。对启发式无法检测的非标准端点显式设置。\n max_concurrent_children: 3 # 每批并行子 agent 数(下限 1,无上限)。也可通过 DELEGATION_MAX_CONCURRENT_CHILDREN 环境变量设置。\n max_spawn_depth: 1 # 委托树深度上限(1-3,截断)。1 = 扁平(默认):父级生成无法委托的叶子。2 = 编排器子级可以生成叶子孙级。3 = 三级。\n orchestrator_enabled: true # 全局终止开关。为 false 时,role=\"orchestrator\" 被忽略,每个子级无论 max_spawn_depth 如何都被强制为叶子。\n```\n\n**子 agent provider:model 覆盖:** 默认情况下,子 agent 继承父 agent 的 provider 和模型。设置 `delegation.provider` 和 `delegation.model` 将子 agent 路由到不同的 provider:model 对 —— 例如,在您的主 agent 运行昂贵推理模型时,为范围较窄的子任务使用便宜/快速的模型。\n\n**直接端点覆盖:** 如果您想要明显的自定义端点路径,请设置 `delegation.base_url`、`delegation.api_key` 和 `delegation.model`。这将子 agent 直接发送到该 OpenAI 兼容端点,并优先于 `delegation.provider`。如果省略 `delegation.api_key`,Hermes 仅回退到 `OPENAI_API_KEY`。\n\n**线路协议(`api_mode`):** Hermes 从 `delegation.base_url` 自动检测线路协议(例如以 `/anthropic` 结尾的路径 → `anthropic_messages`;Codex/原生 Anthropic/Kimi-coding 主机名保留其现有检测)。对于启发式无法分类的端点 —— 例如 Azure AI Foundry、MiniMax、Zhipu GLM 或前置 Anthropic 形状后端的 LiteLLM 代理 —— 请将 `delegation.api_mode` 显式设置为 `chat_completions`、`codex_responses` 或 `anthropic_messages` 之一。留空(默认)以保持自动检测。\n\n委托 provider 使用与 CLI/gateway 启动相同的凭据解析。所有配置的 provider 均受支持:`openrouter`、`nous`、`copilot`、`zai`、`kimi-coding`、`minimax`、`minimax-cn`。设置 provider 时,系统自动解析正确的基础 URL、API 密钥和 API 模式 —— 无需手动凭据连接。\n\n**优先级:** 配置中的 `delegation.base_url` → 配置中的 `delegation.provider` → 父 provider(继承)。配置中的 `delegation.model` → 父模型(继承)。仅设置 `model` 而不设置 `provider` 仅更改模型名称,同时保留父级凭据(适用于在同一 provider(如 OpenRouter)内切换模型)。\n\n**宽度和深度:** `max_concurrent_children` 限制每批并行运行的子 agent 数量(默认 `3`,下限 1,无上限)。也可通过 `DELEGATION_MAX_CONCURRENT_CHILDREN` 环境变量设置。当模型提交的 `tasks` 数组超过上限时,`delegate_task` 返回工具错误解释限制,而不是静默截断。`max_spawn_depth` 控制委托树深度(截断到 1-3)。在默认 `1` 时,委托是扁平的:子级无法生成孙级,传递 `role=\"orchestrator\"` 静默降级为 `leaf`。提升到 `2` 使编排器子级可以生成叶子孙级;`3` 用于三级树。Agent 通过 `role=\"orchestrator\"` 按调用选择编排;`orchestrator_enabled: false` 强制每个子级回到叶子,无论如何。成本呈乘法增长 —— 在 `max_spawn_depth: 3` 和 `max_concurrent_children: 3` 时,树可以达到 3×3×3 = 27 个并发叶子 agent。使用模式请参阅[子 Agent 委托 → 深度限制和嵌套编排](features/delegation.md#depth-limit-and-nested-orchestration)。\n\n## 澄清\n\n配置澄清提示行为:\n\n```yaml\nclarify:\n timeout: 120 # 等待用户澄清响应的秒数\n```\n\n## 上下文文件(SOUL.md、AGENTS.md)\n\nHermes 使用两种不同的上下文范围:\n\n| 文件 | 用途 | 范围 |\n|------|---------|-------|\n| `SOUL.md` | **主要 agent 身份** —— 定义 agent 是谁(系统提示词第 #1 槽位) | `~/.hermes/SOUL.md` 或 `$HERMES_HOME/SOUL.md` |\n| `.hermes.md` / `HERMES.md` | 项目特定指令(最高优先级) | 向上走到 git 根目录 |\n| `AGENTS.md` | 项目特定指令、编码规范 | 递归目录遍历 |\n| `CLAUDE.md` | Claude Code 上下文文件(也会检测) | 仅工作目录 |\n| `.cursorrules` | Cursor IDE 规则(也会检测) | 仅工作目录 |\n| `.cursor/rules/*.mdc` | Cursor 规则文件(也会检测) | 仅工作目录 |\n\n- **SOUL.md** 是 agent 的主要身份。它占据系统提示词的第 #1 槽位,完全替换内置的默认身份。编辑它以完全自定义 agent 是谁。\n- 如果 SOUL.md 缺失、为空或无法加载,Hermes 回退到内置默认身份。\n- **项目上下文文件使用优先级系统** —— 仅加载一种类型(第一个匹配优先):`.hermes.md` → `AGENTS.md` → `CLAUDE.md` → `.cursorrules`。SOUL.md 始终独立加载。\n- **AGENTS.md** 是分层的:如果子目录也有 AGENTS.md,所有都会合并。\n- 如果 `SOUL.md` 不存在,Hermes 会自动生成默认的 `SOUL.md`。\n- 所有加载的上下文文件上限为 20,000 字符,并进行智能截断。\n\n另请参阅:\n- [个性与 SOUL.md](/user-guide/features/personality)\n- [上下文文件](/user-guide/features/context-files)\n\n## 工作目录\n\n| 上下文 | 默认值 |\n|---------|---------|\n| **CLI(`hermes`)** | 运行命令的当前目录 |\n| **消息 gateway** | 主目录 `~`(用 `MESSAGING_CWD` 覆盖) |\n| **Docker / Singularity / Modal / SSH** | 容器或远程机器内用户的主目录 |\n\n覆盖工作目录:\n```bash\n# 在 ~/.hermes/.env 或 ~/.hermes/config.yaml 中:\nMESSAGING_CWD=/home/myuser/projects # Gateway 会话\nTERMINAL_CWD=/workspace # 所有终端会话\n```\n"}, "2c1809e6ca62d8624e223d92086fe02edbecb9e6": {"t": 1785767968.087439, "n": 56672, "h": "86d8f1ce0d525265", "c": "---\nsidebar_position: 2\ntitle: \"配置\"\ndescription: \"配置 Hermes Agent — config.yaml、providers、模型、API 密钥等\"\n---\n\n# 配置\n\n所有设置均存储在 `~/.hermes/` 目录中,便于访问。\n\n## 目录结构\n\n```text\n~/.hermes/\n├── config.yaml # 设置(模型、终端、TTS、压缩等)\n├── .env # API 密钥和机密\n├── auth.json # OAuth provider 凭据(Nous Portal 等)\n├── SOUL.md # 主要 agent 身份(系统提示词第 #1 槽位)\n├── memories/ # 持久记忆(MEMORY.md、USER.md)\n├── skills/ # Agent 创建的技能(通过 skill_manage 工具管理)\n├── cron/ # 定时任务\n├── sessions/ # Gateway 会话\n└── logs/ # 日志(errors.log、gateway.log — 机密自动脱敏)\n```\n\n## 管理配置\n\n```bash\nhermes config # 查看当前配置\nhermes config edit # 在编辑器中打开 config.yaml\nhermes config set KEY VAL # 设置特定值\nhermes config check # 检查缺失选项(更新后使用)\nhermes config migrate # 交互式添加缺失选项\n\n# 示例:\nhermes config set model anthropic/claude-opus-4\nhermes config set terminal.backend docker\nhermes config set OPENROUTER_API_KEY sk-or-... # 保存到 .env\n```\n\n:::tip\n`hermes config set` 命令会自动将值路由到正确的文件 —— API 密钥保存到 `.env`,其他所有内容保存到 `config.yaml`。\n:::\n\n## 配置优先级\n\n设置按以下顺序解析(优先级从高到低):\n\n1. **CLI 参数** —— 例如 `hermes chat --model anthropic/claude-sonnet-4`(单次调用覆盖)\n2. **`~/.hermes/config.yaml`** —— 所有非机密设置的主配置文件\n3. **`~/.hermes/.env`** —— 环境变量的回退;机密(API 密钥、token、密码)**必须**放这里\n4. **内置默认值** —— 未设置任何内容时的硬编码安全默认值\n\n:::info 经验法则\n机密(API 密钥、bot token、密码)放入 `.env`。其他所有内容(模型、终端后端、压缩设置、内存限制、工具集)放入 `config.yaml`。当两者都设置时,`config.yaml` 对非机密设置优先。\n:::\n\n## 环境变量替换\n\n可以在 `config.yaml` 中使用 `${VAR_NAME}` 语法引用环境变量:\n\n```yaml\nauxiliary:\n vision:\n api_key: ${GOOGLE_API_KEY}\n base_url: ${CUSTOM_VISION_URL}\n\ndelegation:\n api_key: ${DELEGATION_KEY}\n```\n\n单个值中可以有多个引用:`url: \"${HOST}:${PORT}\"`。如果引用的变量未设置,占位符将保持原样(`${UNDEFINED_VAR}` 保持不变)。仅支持 `${VAR}` 语法 —— 裸 `$VAR` 不会被展开。\n\n关于 AI provider 设置(OpenRouter、Anthropic、Copilot、自定义端点、自托管 LLM、回退模型等),请参阅 [AI Providers](/integrations/providers)。\n\n### Provider 超时\n\n可以为 provider 设置 `providers.<id>.request_timeout_seconds` 作为全局请求超时,以及 `providers.<id>.models.<model>.timeout_seconds` 作为特定模型的覆盖值。适用于每种传输方式(OpenAI-wire、原生 Anthropic、Anthropic 兼容)上的主轮次客户端、回退链、凭据轮换后的重建,以及(对于 OpenAI-wire)每请求超时 kwarg —— 因此配置值优先于旧版 `HERMES_API_TIMEOUT` 环境变量。\n\n还可以设置 `providers.<id>.stale_timeout_seconds` 用于非流式陈旧调用检测器,以及 `providers.<id>.models.<model>.stale_timeout_seconds` 作为特定模型的覆盖值。此值优先于旧版 `HERMES_API_CALL_STALE_TIMEOUT` 环境变量。\n\n不设置这些值将保持旧版默认值(`HERMES_API_TIMEOUT=1800`s、`HERMES_API_CALL_STALE_TIMEOUT=90`s、原生 Anthropic 900s)。隐式的非流式 stale 检测会在本地端点上自动禁用,并且会在超大上下文下自动放宽。目前不适用于 AWS Bedrock(`bedrock_converse` 和 AnthropicBedrock SDK 路径均使用 boto3 及其自身的超时配置)。请参阅 [`cli-config.yaml.example`](https://github.com/NousResearch/hermes-agent/blob/main/cli-config.yaml.example) 中的注释示例。\n\n## 终端后端配置\n\nHermes 支持六种终端后端。每种后端决定 agent 的 shell 命令实际在哪里执行 —— 本地机器、Docker 容器、通过 SSH 的远程服务器、Modal 云沙箱(直接或通过 Nous 托管的 gateway)、Daytona 工作区,或 Singularity/Apptainer 容器。\n\n```yaml\nterminal:\n backend: local # local | docker | ssh | modal | daytona | singularity\n cwd: \".\" # Gateway/cron 工作目录(CLI 始终使用启动目录)\n timeout: 180 # 每条命令的超时时间(秒)\n env_passthrough: [] # 转发到沙箱执行的环境变量名(terminal + execute_code)\n singularity_image: \"docker://nikolaik/python-nodejs:python3.11-nodejs20\" # Singularity 后端的容器镜像\n modal_image: \"nikolaik/python-nodejs:python3.11-nodejs20\" # Modal 后端的容器镜像\n daytona_image: \"nikolaik/python-nodejs:python3.11-nodejs20\" # Daytona 后端的容器镜像\n```\n\n对于 Modal 和 Daytona 等云沙箱,`container_persistent: true` 表示 Hermes 将尝试在沙箱重建后保留文件系统状态。这并不保证相同的活跃沙箱、PID 空间或后台进程之后仍在运行。\n\n### 后端概览\n\n| 后端 | 命令运行位置 | 隔离性 | 最适合 |\n|---------|-------------------|-----------|----------|\n| **local** | 直接在您的机器上 | 无 | 开发、个人使用 |\n| **docker** | 单个持久 Docker 容器(跨会话、`/new`、子 agent 共享) | 完全(命名空间、cap-drop) | 安全沙箱、CI/CD |\n| **ssh** | 通过 SSH 的远程服务器 | 网络边界 | 远程开发、强大硬件 |\n| **modal** | Modal 云沙箱 | 完全(云 VM) | 临时云计算、评估 |\n| **daytona** | Daytona 工作区 | 完全(云容器) | 托管云开发环境 |\n| **singularity** | Singularity/Apptainer 容器 | 命名空间(--containall) | HPC 集群、共享机器 |\n\n### Local 后端\n\n默认后端。命令直接在您的机器上运行,无隔离。无需特殊设置。\n\n```yaml\nterminal:\n backend: local\n```\n\n:::warning\nAgent 拥有与您的用户账户相同的文件系统访问权限。使用 `hermes tools` 禁用不需要的工具,或切换到 Docker 进行沙箱隔离。\n:::\n\n### Docker 后端\n\n在具有安全加固的 Docker 容器内运行命令(所有权限已删除、无权限提升、PID 限制)。\n\n**单个持久容器,而非每条命令一个容器。** Hermes 在首次使用时启动一个长期运行的容器,并通过 `docker exec` 将每个终端、文件和 `execute_code` 调用路由到同一容器中 —— 跨会话、`/new`、`/reset` 和 `delegate_task` 子 agent,贯穿 Hermes 进程的整个生命周期。工作目录更改、已安装的包以及 `/workspace` 中的文件会从一次工具调用延续到下一次,就像本地 shell 一样。容器在关闭时停止并删除。详情请参阅下方的**容器生命周期**。\n\n```yaml\nterminal:\n backend: docker\n docker_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n docker_mount_cwd_to_workspace: false # 将启动目录挂载到 /workspace\n docker_run_as_host_user: false # 参见下方\"以宿主用户身份运行容器\"\n docker_forward_env: # 转发到容器的环境变量\n - \"GITHUB_TOKEN\"\n docker_volumes: # 宿主目录挂载\n - \"/home/user/projects:/workspace/projects\"\n - \"/home/user/data:/data:ro\" # :ro 表示只读\n docker_extra_args: # 附加到 `docker run` 的额外标志\n - \"--gpus=all\"\n - \"--network=host\"\n\n # 资源限制\n container_cpu: 1 # CPU 核心数(0 = 不限制)\n container_memory: 5120 # MB(0 = 不限制)\n container_disk: 51200 # MB(需要 XFS+pquota 上的 overlay2)\n container_persistent: true # 跨会话持久化 /workspace 和 /root\n```\n\n**`terminal.docker_extra_args`**(也可通过 `TERMINAL_DOCKER_EXTRA_ARGS='[\"--gpus=all\"]'` 覆盖)允许传递 Hermes 未作为一级键公开的任意 `docker run` 标志 —— `--gpus`、`--network`、`--add-host`、替代 `--security-opt` 覆盖等。每个条目必须是字符串;该列表最后附加到组装好的 `docker run` 调用中,因此可以在需要时覆盖 Hermes 的默认值。请谨慎使用 —— 与沙箱加固(权限删除、`--user`、workspace 绑定挂载)冲突的标志将悄然削弱隔离性。\n\n**要求:** 已安装并运行 Docker Desktop 或 Docker Engine。Hermes 会探测 `$PATH` 以及常见的 macOS 安装位置(`/usr/local/bin/docker`、`/opt/homebrew/bin/docker`、Docker Desktop 应用包)。开箱即用支持 Podman:设置 `HERMES_DOCKER_BINARY=podman`(或完整路径)以在两者都安装时强制使用它。\n\n**容器生命周期:** Hermes 为每个终端和文件工具调用重用单个长期运行的容器(`docker run -d ... sleep 2h`),跨会话、`/new`、`/reset` 和 `delegate_task` 子 agent,贯穿 Hermes 进程的整个生命周期。命令通过带登录 shell 的 `docker exec` 运行,因此工作目录更改、已安装的包以及 `/workspace` 中的文件都会从一次工具调用延续到下一次。容器在 Hermes 关闭时(或空闲清理回收时)停止并删除。\n\n通过 `delegate_task(tasks=[...])` 生成的并行子 agent 共享这一个容器 —— 并发的 `cd`、环境变量修改以及对同一路径的写入会发生冲突。如果子 agent 需要隔离的沙箱,必须通过 `register_task_env_overrides()` 注册每任务镜像覆盖,RL 和基准测试环境(TerminalBench2、HermesSweEnv 等)会自动为其每任务 Docker 镜像执行此操作。\n\n**安全加固:**\n- `--cap-drop ALL`,仅添加回 `DAC_OVERRIDE`、`CHOWN`、`FOWNER`\n- `--security-opt no-new-privileges`\n- `--pids-limit 256`\n- `/tmp`(512MB)、`/var/tmp`(256MB)、`/run`(64MB)的大小限制 tmpfs\n\n**凭据转发:** `docker_forward_env` 中列出的环境变量首先从您的 shell 环境解析,然后回退到 `~/.hermes/.env`。技能也可以声明 `required_environment_variables`,这些变量会自动合并。\n\n### SSH 后端\n\n通过 SSH 在远程服务器上运行命令。使用 ControlMaster 进行连接复用(5 分钟空闲保活)。默认启用持久 shell —— 状态(cwd、环境变量)在命令之间保持。\n\n```yaml\nterminal:\n backend: ssh\n persistent_shell: true # 保持长期运行的 bash 会话(默认:true)\n```\n\n**必需的环境变量:**\n\n```bash\nTERMINAL_SSH_HOST=my-server.example.com\nTERMINAL_SSH_USER=ubuntu\n```\n\n**可选:**\n\n| 变量 | 默认值 | 描述 |\n|----------|---------|-------------|\n| `TERMINAL_SSH_PORT` | `22` | SSH 端口 |\n| `TERMINAL_SSH_KEY` | (系统默认) | SSH 私钥路径 |\n| `TERMINAL_SSH_PERSISTENT` | `true` | 启用持久 shell |\n\n**工作原理:** 使用 `BatchMode=yes` 和 `StrictHostKeyChecking=accept-new` 在初始化时连接。持久 shell 在远程主机上保持单个 `bash -l` 进程存活,通过临时文件进行通信。需要 `stdin_data` 或 `sudo` 的命令会自动回退到单次模式。\n\n### Modal 后端\n\n在 [Modal](https://modal.com) 云沙箱中运行命令。每个任务获得一个具有可配置 CPU、内存和磁盘的隔离 VM。文件系统可以跨会话快照/恢复。\n\n```yaml\nterminal:\n backend: modal\n container_cpu: 1 # CPU 核心数\n container_memory: 5120 # MB(5GB)\n container_disk: 51200 # MB(50GB)\n container_persistent: true # 快照/恢复文件系统\n```\n\n**必需:** `MODAL_TOKEN_ID` + `MODAL_TOKEN_SECRET` 环境变量,或 `~/.modal.toml` 配置文件。\n\n**持久化:** 启用后,沙箱文件系统在清理时快照,并在下次会话时恢复。快照在 `~/.hermes/modal_snapshots.json` 中跟踪。这保留文件系统状态,而非活跃进程、PID 空间或后台任务。\n\n**凭据文件:** 自动从 `~/.hermes/` 挂载(OAuth token 等),并在每条命令前同步。\n\n### Daytona 后端\n\n在 [Daytona](https://daytona.io) 托管工作区中运行命令。支持停止/恢复以实现持久化。\n\n```yaml\nterminal:\n backend: daytona\n container_cpu: 1 # CPU 核心数\n container_memory: 5120 # MB → 转换为 GiB\n container_disk: 10240 # MB → 转换为 GiB(最大 10 GiB)\n container_persistent: true # 停止/恢复而非删除\n```\n\n**必需:** `DAYTONA_API_KEY` 环境变量。\n\n**持久化:** 启用后,沙箱在清理时停止(而非删除),并在下次会话时恢复。沙箱名称遵循 `hermes-{task_id}` 模式。\n\n**磁盘限制:** Daytona 强制执行 10 GiB 最大值。超过此值的请求将被截断并发出警告。\n\n### Singularity/Apptainer 后端\n\n在 [Singularity/Apptainer](https://apptainer.org) 容器中运行命令。专为 Docker 不可用的 HPC 集群和共享机器设计。\n\n```yaml\nterminal:\n backend: singularity\n singularity_image: \"docker://nikolaik/python-nodejs:python3.11-nodejs20\"\n container_cpu: 1 # CPU 核心数\n container_memory: 5120 # MB\n container_persistent: true # 可写覆盖层跨会话持久化\n```\n\n**要求:** `$PATH` 中有 `apptainer` 或 `singularity` 二进制文件。\n\n**镜像处理:** Docker URL(`docker://...`)自动转换为 SIF 文件并缓存。现有 `.sif` 文件直接使用。\n\n**临时目录:** 按顺序解析:`TERMINAL_SCRATCH_DIR` → `TERMINAL_SANDBOX_DIR/singularity` → `/scratch/$USER/hermes-agent`(HPC 惯例)→ `~/.hermes/sandboxes/singularity`。\n\n**隔离:** 使用 `--containall --no-home` 实现完全命名空间隔离,不挂载宿主 home 目录。\n\n### 常见终端后端问题\n\n如果终端命令立即失败或终端工具报告为已禁用:\n\n- **Local** —— 无特殊要求。入门时最安全的默认选项。\n- **Docker** —— 运行 `docker version` 验证 Docker 是否正常工作。如果失败,修复 Docker 或执行 `hermes config set terminal.backend local`。\n- **SSH** —— `TERMINAL_SSH_HOST` 和 `TERMINAL_SSH_USER` 都必须设置。如果缺少任一项,Hermes 会记录清晰的错误。\n- **Modal** —— 需要 `MODAL_TOKEN_ID` 环境变量或 `~/.modal.toml`。运行 `hermes doctor` 检查。\n- **Daytona** —— 需要 `DAYTONA_API_KEY`。Daytona SDK 处理服务器 URL 配置。\n- **Singularity** —— 需要 `$PATH` 中有 `apptainer` 或 `singularity`。HPC 集群上常见。\n\n如有疑问,将 `terminal.backend` 设回 `local` 并首先验证命令在那里运行。\n\n### 拆卸时远程到宿主文件同步\n\n对于 **SSH**、**Modal** 和 **Daytona** 后端(agent 的工作树位于与运行 Hermes 的宿主不同的机器上),Hermes 跟踪 agent 在远程沙箱中触及的文件,并在会话拆卸/沙箱清理时,将修改的文件**同步回宿主**,存放在 `~/.hermes/cache/remote-syncs/<session-id>/` 下。\n\n- 触发时机:会话关闭、`/new`、`/reset`、gateway 消息超时、子 agent 使用远程后端时 `delegate_task` 子 agent 完成。\n- 覆盖 agent 修改的整个树,而不仅仅是它明确打开的文件。添加、编辑和删除都会被捕获。\n- 远程沙箱可能在您查找时已被拆除;本地 `~/.hermes/cache/remote-syncs/…` 副本是 agent 更改内容的权威记录。\n- 大型二进制输出(模型检查点、原始数据集)按大小限制 —— 同步跳过超过 `file_sync_max_mb`(默认 `100`)的文件。如果您期望更大的工件返回,请调高该值。\n\n```yaml\nterminal:\n file_sync_max_mb: 100 # 默认 —— 同步最大 100 MB 的文件\n file_sync_enabled: true # 默认 —— 设为 false 可完全跳过同步\n```\n\n这是从会话结束后被销毁的临时云沙箱中恢复结果的方式,无需告诉 agent 显式地 `scp` 或 `modal volume put` 每个工件。\n\n### Docker 卷挂载\n\n使用 Docker 后端时,`docker_volumes` 允许您与容器共享宿主目录。每个条目使用标准 Docker `-v` 语法:`host_path:container_path[:options]`。\n\n```yaml\nterminal:\n backend: docker\n docker_volumes:\n - \"/home/user/projects:/workspace/projects\" # 读写(默认)\n - \"/home/user/datasets:/data:ro\" # 只读\n - \"/home/user/.hermes/cache/documents:/output\" # Gateway 可见的导出\n```\n\n适用于:\n- **向 agent 提供文件**(数据集、配置、参考代码)\n- **从 agent 接收文件**(生成的代码、报告、导出)\n- **共享工作区**,您和 agent 都访问相同的文件\n\n如果您使用消息 gateway 并希望 agent 通过 `MEDIA:/...` 发送生成的文件,建议使用专用的宿主可见导出挂载,例如 `/home/user/.hermes/cache/documents:/output`。\n\n- 在 Docker 中将文件写入 `/output/...`\n- 在 `MEDIA:` 中发出**宿主路径**,例如:`MEDIA:/home/user/.hermes/cache/documents/report.txt`\n- **不要**发出 `/workspace/...` 或 `/output/...`,除非该确切路径在宿主上对 gateway 进程也存在\n\n:::warning\nYAML 重复键会静默覆盖之前的键。如果您已有 `docker_volumes:` 块,请将新挂载合并到同一列表中,而不是在文件后面再添加一个 `docker_volumes:` 键。\n:::\n\n也可以通过环境变量设置:`TERMINAL_DOCKER_VOLUMES='[\"/host:/container\"]'`(JSON 数组)。\n\n### Docker 凭据转发\n\n默认情况下,Docker 终端会话不继承任意宿主凭据。如果您需要在容器内使用特定 token,请将其添加到 `terminal.docker_forward_env`。\n\n```yaml\nterminal:\n backend: docker\n docker_forward_env:\n - \"GITHUB_TOKEN\"\n - \"NPM_TOKEN\"\n```\n\nHermes 首先从您当前的 shell 解析每个列出的变量,然后回退到通过 `hermes config set` 保存的 `~/.hermes/.env`。\n\n:::warning\n`docker_forward_env` 中列出的任何内容都会对容器内运行的命令可见。只转发您愿意暴露给终端会话的凭据。\n:::\n\n### 以宿主用户身份运行容器\n\n默认情况下,Docker 容器以 `root`(UID 0)身份运行。在 `/workspace` 或其他绑定挂载中创建的文件在宿主上归 root 所有,因此会话结束后您必须 `sudo chown` 才能从宿主编辑器编辑它们。`terminal.docker_run_as_host_user` 标志解决了这个问题:\n\n```yaml\nterminal:\n backend: docker\n docker_run_as_host_user: true # 默认:false\n```\n\n启用后,Hermes 将 `--user $(id -u):$(id -g)` 附加到 `docker run` 命令,使写入绑定挂载目录(`/workspace`、`/root`、`docker_volumes` 中的任何内容)的文件归您的宿主用户所有,而非 root。权衡:容器将无法再 `apt install` 或写入 `/root/.npm` 等 root 拥有的路径 —— 如果您同时需要这两者,请使用 `HOME` 归非 root 用户所有的基础镜像(或在镜像构建时添加所需工具)。\n\n保持 `false`(默认)以获得向后兼容的行为。当您的工作流主要是\"编辑挂载的宿主文件\"且厌倦了 `sudo chown -R` 时,请开启此选项。\n\n### 可选:将启动目录挂载到 `/workspace`\n\nDocker 沙箱默认保持隔离。Hermes **不会**将您当前的宿主工作目录传入容器,除非您明确选择加入。\n\n在 `config.yaml` 中启用:\n\n```yaml\nterminal:\n backend: docker\n docker_mount_cwd_to_workspace: true\n```\n\n启用后:\n- 如果您从 `~/projects/my-app` 启动 Hermes,该宿主目录将绑定挂载到 `/workspace`\n- Docker 后端从 `/workspace` 开始\n- 文件工具和终端命令都能看到相同的挂载项目\n\n禁用时,`/workspace` 保持沙箱所有,除非您通过 `docker_volumes` 显式挂载内容。\n\n安全权衡:\n- `false` 保留沙箱边界\n- `true` 使沙箱直接访问您启动 Hermes 的目录\n\n仅在您有意希望容器处理实时宿主文件时才选择加入。\n\n### 持久 Shell\n\n默认情况下,每条终端命令在其自己的子进程中运行 —— 工作目录、环境变量和 shell 变量在命令之间重置。启用**持久 shell** 后,单个长期运行的 bash 进程在 `execute()` 调用之间保持存活,使状态在命令之间保持。\n\n这对 **SSH 后端**最有用,它还消除了每条命令的连接开销。持久 shell **对 SSH 默认启用**,对本地后端禁用。\n\n```yaml\nterminal:\n persistent_shell: true # 默认 —— 为 SSH 启用持久 shell\n```\n\n禁用:\n\n```bash\nhermes config set terminal.persistent_shell false\n```\n\n**跨命令保持的内容:**\n- 工作目录(`cd /tmp` 对下一条命令生效)\n- 导出的环境变量(`export FOO=bar`)\n- Shell 变量(`MY_VAR=hello`)\n\n**优先级:**\n\n| 级别 | 变量 | 默认值 |\n|-------|----------|---------|\n| 配置 | `terminal.persistent_shell` | `true` |\n| SSH 覆盖 | `TERMINAL_SSH_PERSISTENT` | 遵循配置 |\n| Local 覆盖 | `TERMINAL_LOCAL_PERSISTENT` | `false` |\n\n每个后端的环境变量具有最高优先级。如果您也想在本地后端使用持久 shell:\n\n```bash\nexport TERMINAL_LOCAL_PERSISTENT=true\n```\n\n:::note\n需要 `stdin_data` 或 sudo 的命令会自动回退到单次模式,因为持久 shell 的 stdin 已被 IPC 协议占用。\n:::\n\n有关每个后端的详细信息,请参阅[代码执行](features/code-execution.md)和 [README 的终端部分](features/tools.md)。\n\n## 技能设置\n\n技能可以通过其 SKILL.md frontmatter 声明自己的配置设置。这些是非机密值(路径、偏好、域设置),存储在 `config.yaml` 的 `skills.config` 命名空间下。\n\n```yaml\nskills:\n config:\n myplugin:\n path: ~/myplugin-data # 示例 —— 每个技能定义自己的键\n```\n\n**技能设置的工作原理:**\n\n- `hermes config migrate` 扫描所有已启用的技能,找到未配置的设置,并提供提示\n- `hermes config show` 在\"技能设置\"下显示所有技能设置及其所属技能\n- 技能加载时,其解析的配置值会自动注入到技能上下文中\n\n**手动设置值:**\n\n```bash\nhermes config set skills.config.myplugin.path ~/myplugin-data\n```\n\n有关在您自己的技能中声明配置设置的详细信息,请参阅[创建技能 — 配置设置](/developer-guide/creating-skills#config-settings-configyaml)。\n\n### Agent 创建技能写入的守卫\n\n当 agent 使用 `skill_manage` 创建、编辑、修补或删除技能时,Hermes 可以选择扫描新/更新的内容以查找危险关键字模式(凭据收集、明显的 prompt 注入、数据外泄指令)。扫描器**默认关闭** —— 合法触及 `~/.ssh/` 或提及 `$OPENAI_API_KEY` 的真实 agent 工作流触发启发式规则过于频繁。如果您希望扫描器在 agent 的技能写入落地前提示您,请重新开启:\n\n```yaml\nskills:\n guard_agent_created: true # 默认:false\n```\n\n开启后,任何被标记的 `skill_manage` 写入都会以审批提示的形式出现,并附带扫描器的理由。接受的写入落地;拒绝的写入向 agent 返回解释性错误。\n\n## 内存配置\n\n```yaml\nmemory:\n memory_enabled: true\n user_profile_enabled: true\n memory_char_limit: 2200 # ~800 tokens\n user_char_limit: 1375 # ~500 tokens\n```\n\n## 文件读取安全\n\n控制单次 `read_file` 调用可以返回多少内容。超过限制的读取将被拒绝,并向 agent 返回错误,提示使用 `offset` 和 `limit` 读取较小范围。这可以防止单次读取压缩的 JS 包或大型数据文件时淹没上下文窗口。\n\n```yaml\nfile_read_max_chars: 100000 # 默认 —— ~25-35K tokens\n```\n\n如果您使用具有大上下文窗口的模型并经常读取大文件,请调高此值。对于小上下文模型,请降低以保持读取高效:\n\n```yaml\n# 大上下文模型(200K+)\nfile_read_max_chars: 200000\n\n# 小型本地模型(16K 上下文)\nfile_read_max_chars: 30000\n```\n\nAgent 还会自动去重文件读取 —— 如果同一文件区域被读取两次且文件未更改,则返回轻量级存根而不是重新发送内容。这在上下文压缩后重置,以便 agent 在内容被摘要后可以重新读取文件。\n\n## 工具输出截断限制\n\n三个相关的上限控制工具在 Hermes 截断之前可以返回多少原始输出:\n\n```yaml\ntool_output:\n max_bytes: 50000 # 终端输出上限(字符)\n max_lines: 2000 # read_file 分页上限\n max_line_length: 2000 # read_file 行号视图中的每行上限\n```\n\n- **`max_bytes`** —— 当 `terminal` 命令产生超过此字符数的合并 stdout/stderr 时,Hermes 保留前 40% 和后 60%,并在中间插入 `[OUTPUT TRUNCATED]` 通知。默认 `50000`(典型分词器约 12-15K tokens)。\n- **`max_lines`** —— 单次 `read_file` 调用的 `limit` 参数上限。超过此值的请求将被截断,以防单次读取淹没上下文窗口。默认 `2000`。\n- **`max_line_length`** —— `read_file` 发出行号视图时应用的每行上限。超过此长度的行将被截断为此字符数,后跟 `... [truncated]`。默认 `2000`。\n\n对于具有大上下文窗口且每次调用可以承受更多原始输出的模型,请调高限制。对于小上下文模型,请降低以保持工具结果紧凑:\n\n```yaml\n# 大上下文模型(200K+)\ntool_output:\n max_bytes: 150000\n max_lines: 5000\n\n# 小型本地模型(16K 上下文)\ntool_output:\n max_bytes: 20000\n max_lines: 500\n```\n\n## 全局工具集禁用\n\n要在 CLI 和每个 gateway 平台上统一禁用特定工具集,请在 `agent.disabled_toolsets` 下列出其名称:\n\n```yaml\nagent:\n disabled_toolsets:\n - memory # 隐藏内存工具 + MEMORY_GUIDANCE 注入\n - web # 任何地方都不使用 web_search / web_extract\n```\n\n这在每个平台的工具配置(由 `hermes tools` 写入的 `platform_toolsets`)**之后**应用,因此此处列出的工具集始终被删除 —— 即使平台的已保存配置仍然列出它。当您希望有一个\"到处关闭 X\"的单一开关而不是编辑 `hermes tools` UI 中的 15+ 个平台行时,请使用此选项。\n\n留空列表或省略键不会产生任何效果。\n\n## Git Worktree 隔离\n\n启用隔离的 git worktree,以便在同一仓库上并行运行多个 agent:\n\n```yaml\nworktree: true # 始终创建 worktree(与 hermes -w 相同)\n# worktree: false # 默认 —— 仅在传递 -w 标志时\n```\n\n启用后,每个 CLI 会话在 `.worktrees/` 下创建一个带有自己分支的新 worktree。Agent 可以编辑文件、提交、推送和创建 PR,而不会相互干扰。干净的 worktree 在退出时删除;脏的 worktree 保留以供手动恢复。\n\n您还可以通过仓库根目录中的 `.worktreeinclude` 列出要复制到 worktree 的 gitignore 文件:\n\n```\n# .worktreeinclude\n.env\n.venv/\nnode_modules/\n```\n\n## 上下文压缩\n\nHermes 自动压缩长对话以保持在模型的上下文窗口内。压缩摘要器是一个单独的 LLM 调用 —— 您可以将其指向任何 provider 或端点。\n\n所有压缩设置都在 `config.yaml` 中(无环境变量)。\n\n### 完整参考\n\n```yaml\ncompression:\n enabled: true # 开启/关闭压缩\n threshold: 0.50 # 在上下文限制的此百分比时压缩\n target_ratio: 0.20 # 保留为最近尾部的阈值分数\n protect_last_n: 20 # 保持未压缩的最少最近消息数\n hygiene_hard_message_limit: 5000 # Gateway 安全阀 —— 见下文\n\n# 摘要模型/provider 在 auxiliary: 下配置:\nauxiliary:\n compression:\n model: \"\" # 空 = 使用主聊天模型。覆盖为例如 \"google/gemini-3-flash-preview\" 以获得更便宜/更快的压缩。\n provider: \"auto\" # Provider:\"auto\"、\"openrouter\"、\"nous\"、\"codex\"、\"main\" 等\n base_url: null # 自定义 OpenAI 兼容端点(覆盖 provider)\n```\n\n:::info 旧版配置迁移\n带有 `compression.summary_model`、`compression.summary_provider` 和 `compression.summary_base_url` 的旧版配置在首次加载时自动迁移到 `auxiliary.compression.*`(配置版本 17)。无需手动操作。\n:::\n\n`hygiene_hard_message_limit` 是仅限 gateway 的**预压缩安全阀**。它的存在是为了打破一个死循环:当超大会话的 API 调用持续断开时,gateway 永远收不到 token 使用数据,基于 token 的阈值因此无法触发,于是 transcript 持续增长、断开愈发严重。这个基于消息数的下限仅凭消息数量触发(无论 API 是否失败,消息数始终已知),强制压缩以恢复会话。默认 `5000` —— 远高于任何正常会话,包括做数千次短轮次的大上下文(1M+)模型,它们早就在 token 阈值处压缩了。对于异常平台可调得更高;要强制更积极的压缩则调低。在运行中的 gateway 上编辑此值将在下一条消息时生效(见下文)。\n\n:::tip Gateway 热重载压缩和上下文长度\n从最近的版本开始,在运行中的 gateway 上编辑 `config.yaml` 中的 `model.context_length` 或任何 `compression.*` 键将在下一条消息时生效 —— 无需 gateway 重启、`/reset` 或会话轮换。缓存的 agent 签名包含这些键,因此 gateway 在检测到更改时会透明地重建 agent。API 密钥和工具/技能配置仍需要通常的重载路径。\n:::\n\n### 常见设置\n\n**默认(自动检测)—— 无需配置:**\n```yaml\ncompression:\n enabled: true\n threshold: 0.50\n```\n使用您的主 provider 和主模型。如果您希望在比主聊天模型更便宜的模型上进行压缩,请覆盖每任务(例如 `auxiliary.compression.provider: openrouter` + `model: google/gemini-2.5-flash`)。\n\n**强制特定 provider**(基于 OAuth 或 API 密钥):\n```yaml\nauxiliary:\n compression:\n provider: nous\n model: gemini-3-flash\n```\n适用于任何 provider:`nous`、`openrouter`、`codex`、`anthropic`、`main` 等。\n\n**自定义端点**(自托管、Ollama、zai、DeepSeek 等):\n```yaml\nauxiliary:\n compression:\n model: glm-4.7\n base_url: https://api.z.ai/api/coding/paas/v4\n```\n指向自定义 OpenAI 兼容端点。使用 `OPENAI_API_KEY` 进行认证。\n\n### 三个旋钮的交互方式\n\n| `auxiliary.compression.provider` | `auxiliary.compression.base_url` | 结果 |\n|---------------------|---------------------|--------|\n| `auto`(默认) | 未设置 | 自动检测最佳可用 provider |\n| `nous` / `openrouter` / 等 | 未设置 | 强制使用该 provider,使用其认证 |\n| 任意 | 已设置 | 直接使用自定义端点(忽略 provider) |\n\n:::warning 摘要模型上下文长度要求\n摘要模型**必须**具有至少与您的主 agent 模型一样大的上下文窗口。压缩器将对话的完整中间部分发送给摘要模型 —— 如果该模型的上下文窗口小于主模型的,摘要调用将因上下文长度错误而失败。发生这种情况时,中间轮次将**在没有摘要的情况下被丢弃**,静默丢失对话上下文。如果您覆盖模型,请验证其上下文长度满足或超过您的主模型。\n:::\n\n## 上下文引擎\n\n上下文引擎控制在接近模型 token 限制时如何管理对话。内置的 `compressor` 引擎使用有损摘要(参见[上下文压缩](/developer-guide/context-compression-and-caching))。插件引擎可以用替代策略替换它。\n\n```yaml\ncontext:\n engine: \"compressor\" # 默认 —— 内置有损摘要\n```\n\n使用插件引擎(例如,用于无损上下文管理的 LCM):\n\n```yaml\ncontext:\n engine: \"lcm\" # 必须与插件名称匹配\n```\n\n插件引擎**永远不会自动激活** —— 您必须将 `context.engine` 显式设置为插件名称。可用引擎可以通过 `hermes plugins` → Provider Plugins → Context Engine 浏览和选择。\n\n有关内存插件的类似单选系统,请参阅[内存 Providers](/user-guide/features/memory-providers)。\n\n## 迭代预算\n\n当 agent 在处理具有许多工具调用的复杂任务时,它可能会耗尽其迭代预算(默认:500 轮)。Hermes **不会**在任务中途注入压力警告 —— 早期版本会在预算达到 70%/90% 时警告模型,这会导致模型过早放弃复杂任务,该机制已于 2026 年 4 月移除。\n\n取而代之的是,当预算真正耗尽(500/500)时,Hermes 注入一条消息要求模型收尾,并允许一次**宽限调用**以便其给出最终响应。如果该宽限调用仍未产生文本,则会要求 agent 总结已完成的工作。\n\n```yaml\nagent:\n max_turns: 500 # 每次对话轮次的最大迭代次数(默认:500)\n api_max_retries: 3 # 回退启动前每个 provider 的重试次数(默认:3)\n```\n\n当迭代预算完全耗尽时,CLI 向用户显示通知:`⚠ Iteration budget reached (500/500) — response may be incomplete`。\n\n`agent.api_max_retries` 控制 Hermes 在回退 provider 切换启动**之前**对瞬时错误(速率限制、连接断开、5xx)重试 provider API 调用的次数。默认为 `3` —— 总共四次尝试。如果您配置了[回退 providers](/user-guide/features/fallback-providers) 并希望更快地故障转移,请将其降至 `0`,这样主 provider 上的第一个瞬时错误会立即切换到回退,而不是对不稳定的端点进行重试。\n\n### API 超时\n\nHermes 对流式传输有单独的超时层,以及用于非流式调用的陈旧检测器。陈旧检测器仅在您将其保留为隐式默认值时才会自动调整本地 provider。\n\n| 超时 | 默认值 | 本地 providers | 配置/环境变量 |\n|---------|---------|----------------|--------------|\n| Socket 读取超时 | 120s | 自动提升至 1800s | `HERMES_STREAM_READ_TIMEOUT` |\n| 陈旧流检测 | 180s | 自动禁用 | `HERMES_STREAM_STALE_TIMEOUT` |\n| 陈旧非流检测 | 300s | 保持隐式时自动禁用 | `providers.<id>.stale_timeout_seconds` 或 `HERMES_API_CALL_STALE_TIMEOUT` |\n| API 调用(非流式) | 1800s | 不变 | `providers.<id>.request_timeout_seconds` / `timeout_seconds` 或 `HERMES_API_TIMEOUT` |\n\n**Socket 读取超时**控制 httpx 等待 provider 下一个数据块的时间。本地 LLM 在大上下文上预填充可能需要几分钟才能产生第一个 token,因此当 Hermes 检测到本地端点时,会将此值提升至 30 分钟。如果您显式设置 `HERMES_STREAM_READ_TIMEOUT`,无论端点检测如何,始终使用该值。\n\n**陈旧流检测**终止接收 SSE 保活 ping 但没有实际内容的连接。对于本地 providers,这完全禁用,因为它们在预填充期间不发送保活 ping。\n\n**陈旧非流检测**终止长时间没有响应的非流式调用。默认情况下,Hermes 在本地端点上禁用此功能,以避免长时间预填充期间的误报。如果您显式设置 `providers.<id>.stale_timeout_seconds`、`providers.<id>.models.<model>.stale_timeout_seconds` 或 `HERMES_API_CALL_STALE_TIMEOUT`,即使在本地端点上也会遵守该显式值。\n\n## 上下文压力警告\n\n与迭代预算压力分开,上下文压力跟踪对话距**压缩阈值**有多近 —— 即上下文压缩触发以摘要旧消息的点。这有助于您和 agent 了解对话何时变长。\n\n| 进度 | 级别 | 发生的事情 |\n|----------|-------|-------------|\n| **≥ 60%** 到阈值 | 信息 | CLI 显示青色进度条;gateway 发送信息通知 |\n| **≥ 85%** 到阈值 | 警告 | CLI 显示粗体黄色进度条;gateway 警告压缩即将发生 |\n\n在 CLI 中,上下文压力在工具输出流中显示为进度条:\n\n```\n ◐ context ████████████░░░░░░░░ 62% to compaction 48k threshold (50%) · approaching compaction\n```\n\n在消息平台上,发送纯文本通知:\n\n```\n◐ Context: ████████████░░░░░░░░ 62% to compaction (threshold: 50% of window).\n```\n\n如果自动压缩被禁用,警告会告诉您上下文可能被截断。\n\n上下文压力是自动的 —— 无需配置。它纯粹作为面向用户的通知触发,不修改消息流或向模型上下文注入任何内容。\n\n## 凭据池策略\n\n当您为同一 provider 拥有多个 API 密钥或 OAuth token 时,配置轮换策略:\n\n```yaml\ncredential_pool_strategies:\n openrouter: round_robin # 均匀循环使用密钥\n anthropic: least_used # 始终选择使用最少的密钥\n```\n\n选项:`fill_first`(默认)、`round_robin`、`least_used`、`random`。完整文档请参阅[凭据池](/user-guide/features/credential-pools)。\n\n## Prompt 缓存\n\n当活跃 provider 支持时,Hermes 自动开启跨会话 prompt 缓存 —— 无需用户配置。\n\n对于**原生 Anthropic**、**OpenRouter** 和 **Nous Portal** 上的 Claude,Hermes 在系统提示词和技能块上附加带有 1 小时 TTL(`ttl: \"1h\"`)的 `cache_control` 断点。在新鲜的一小时内首次发送时按完整输入费率计费;同一小时内任何会话的后续发送以折扣缓存读取费率从缓存中提取。这意味着系统提示词、加载的技能内容以及任何长上下文包含的早期部分在第一个小时内跨 `hermes` 会话和分叉子 agent 被重用。\n\nQwen Cloud(阿里巴巴 DashScope)上游将缓存 TTL 限制为 5 分钟,因此 Hermes 在那里使用 5 分钟断点 TTL。其他通过第三方的 Claude 路径(AWS Bedrock、Azure Foundry)回退到 provider 自己的缓存默认值。xAI Grok 使用单独的会话固定对话 ID 机制 —— 参阅 [xAI prompt 缓存](/integrations/providers#xai-grok--responses-api--prompt-caching)。\n\n不存在禁用此功能的旋钮 —— 缓存始终开启,即使在单轮对话中也能节省费用,因为仅系统提示词就占输入 token 数的相当大比例。\n\n## 辅助模型\n\nHermes 使用\"辅助\"模型处理图像分析、网页摘要、浏览器截图分析、会话标题生成和上下文压缩等附带任务。默认情况下(`auxiliary.*.provider: \"auto\"`),Hermes 将每个辅助任务路由到您的**主聊天模型** —— 与您在 `hermes model` 中选择的相同 provider/模型。您无需配置任何内容即可开始,但请注意,在昂贵的推理模型(Opus、MiniMax M2.7 等)上,辅助任务会增加显著成本。如果您希望无论主模型如何都使用便宜且快速的附带任务,请显式设置 `auxiliary.<task>.provider` 和 `auxiliary.<task>.model`(例如,在 OpenRouter 上使用 Gemini Flash 进行视觉和网页提取)。\n\n:::note 为什么 \"auto\" 使用您的主模型\n早期版本将聚合器用户(OpenRouter、Nous Portal)分流到便宜的 provider 端默认值。这令人惊讶 —— 付费购买聚合器订阅的用户会看到不同的模型处理其辅助流量。`auto` 现在对所有人使用主模型,`config.yaml` 中的每任务覆盖仍然优先(见下方[完整辅助配置参考](#full-auxiliary-config-reference))。\n:::\n\n### 交互式配置辅助模型\n\n无需手动编辑 YAML,运行 `hermes model` 并从菜单中选择**\"配置辅助模型\"**。您将获得交互式的每任务选择器:\n\n```\n$ hermes model\n→ Configure auxiliary models\n\n[ ] vision currently: auto / main model\n[ ] web_extract currently: auto / main model\n[ ] title_generation currently: openrouter / google/gemini-3-flash-preview\n[ ] compression currently: auto / main model\n[ ] approval currently: auto / main model\n[ ] triage_specifier currently: auto / main model\n[ ] kanban_decomposer currently: auto / main model\n[ ] profile_describer currently: auto / main model\n```\n\n选择任务,选择 provider(OAuth 流程打开浏览器;API 密钥 provider 提示输入),选择模型。更改持久化到 `config.yaml` 中的 `auxiliary.<task>.*`。与主模型选择器相同的机制 —— 无需学习额外语法。\n\n### 视频教程\n\n<div style={{position: 'relative', width: '100%', aspectRatio: '16 / 9', marginBottom: '1.5rem'}}>\n <iframe\n src=\"https://www.youtube.com/embed/NoF-YajElIM\"\n title=\"Hermes Agent — Auxiliary Models Tutorial\"\n style={{position: 'absolute', top: 0, left: 0, width: '100%', height: '100%', border: 0}}\n allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\"\n allowFullScreen\n />\n</div>\n\n### 通用配置模式\n\nHermes 中的每个模型槽位 —— 辅助任务、压缩、回退 —— 使用相同的三个旋钮:\n\n| 键 | 作用 | 默认值 |\n|-----|-------------|---------|\n| `provider` | 用于认证和路由的 provider | `\"auto\"` |\n| `model` | 请求的模型 | provider 的默认值 |\n| `base_url` | 自定义 OpenAI 兼容端点(覆盖 provider) | 未设置 |\n\n当设置 `base_url` 时,Hermes 忽略 provider 并直接调用该端点(使用 `api_key` 或 `OPENAI_API_KEY` 进行认证)。当仅设置 `provider` 时,Hermes 使用该 provider 的内置认证和基础 URL。\n\n辅助任务的可用 providers:`auto`、`main`,以及[provider 注册表](/reference/environment-variables)中的任何 provider —— `openrouter`、`nous`、`openai-codex`、`copilot`、`copilot-acp`、`anthropic`、`gemini`、`qwen-oauth`、`zai`、`kimi-coding`、`kimi-coding-cn`、`minimax`、`minimax-cn`、`minimax-oauth`、`deepseek`、`nvidia`、`xai`、`xai-oauth`、`ollama-cloud`、`alibaba`、`bedrock`、`huggingface`、`arcee`、`xiaomi`、`kilocode`、`opencode-zen`、`opencode-go`、`azure-foundry` —— 或您 `custom_providers` 列表中任何命名的自定义 provider(例如 `provider: \"beans\"`)。\n\n:::tip MiniMax OAuth\n`minimax-oauth` 通过浏览器 OAuth 登录(无需 API 密钥)。运行 `hermes model` 并选择 **MiniMax (OAuth)** 进行认证。辅助任务自动使用 `MiniMax-M2.7-highspeed`。参阅 [MiniMax OAuth 指南](../guides/minimax-oauth.md)。\n:::\n\n:::tip xAI Grok OAuth\n`xai-oauth` 通过浏览器 OAuth 为 SuperGrok 和 X Premium+ 订阅者登录(无需 API 密钥)。运行 `hermes model` 并选择 **xAI Grok OAuth (SuperGrok / Premium+)** 进行认证。相同的 OAuth token 可重用于每个直接到 xAI 的接口(聊天、辅助任务、TTS、图像生成、视频生成、转录)。参阅 [xAI Grok OAuth 指南](../guides/xai-grok-oauth.md),如果 Hermes 在远程主机上,请参阅 [SSH/远程主机上的 OAuth](../guides/oauth-over-ssh.md)。\n:::\n\n:::warning `\"main\"` 仅用于辅助任务\n`\"main\"` provider 选项表示\"使用我的主 agent 使用的任何 provider\" —— 它仅在 `auxiliary:`、`compression:` 和 `fallback_model:` 配置中有效。它**不是**顶级 `model.provider` 设置的有效值。如果您使用自定义 OpenAI 兼容端点,请在 `model:` 部分设置 `provider: custom`。所有主模型 provider 选项请参阅 [AI Providers](/integrations/providers)。\n:::\n\n### 完整辅助配置参考\n\n```yaml\nauxiliary:\n # 图像分析(vision_analyze 工具 + 浏览器截图)\n vision:\n provider: \"auto\" # \"auto\"、\"openrouter\"、\"nous\"、\"codex\"、\"main\" 等\n model: \"\" # 例如 \"openai/gpt-4o\"、\"google/gemini-2.5-flash\"\n base_url: \"\" # 自定义 OpenAI 兼容端点(覆盖 provider)\n api_key: \"\" # base_url 的 API 密钥(回退到 OPENAI_API_KEY)\n timeout: 120 # 秒 —— LLM API 调用超时;视觉负载需要宽裕的超时\n download_timeout: 30 # 秒 —— 图像 HTTP 下载;慢速连接请增加\n\n # 网页摘要 + 浏览器页面文本提取\n web_extract:\n provider: \"auto\"\n model: \"\" # 例如 \"google/gemini-2.5-flash\"\n base_url: \"\"\n api_key: \"\"\n timeout: 360 # 秒(6 分钟)—— 每次尝试的 LLM 摘要\n\n # 危险命令审批分类器\n approval:\n provider: \"auto\"\n model: \"\"\n base_url: \"\"\n api_key: \"\"\n timeout: 30 # 秒\n\n # 上下文压缩超时(与 compression.* 配置分开)\n compression:\n timeout: 120 # 秒 —— 压缩摘要长对话,需要更多时间\n # fallback_chain: # 可选 —— 发生速率限制/连接故障时尝试的 provider\n # - provider: nous\n # model: deepseek/deepseek-chat\n # - provider: openrouter\n # model: google/gemini-2.5-flash\n # base_url: \"\"\n # api_key: \"\"\n\n # 技能中心 —— 技能匹配和搜索\n skills_hub:\n provider: \"auto\"\n model: \"\"\n base_url: \"\"\n api_key: \"\"\n timeout: 30\n\n # MCP 工具调度\n mcp:\n provider: \"auto\"\n model: \"\"\n base_url: \"\"\n api_key: \"\"\n timeout: 30\n\n # Kanban 分类规格说明器 —— `hermes kanban specify <id>`(或\n # 仪表板上 Triage 列卡片的 ✨ Specify 按钮)使用此\n # 槽位将单行描述扩展为具体规格并将\n # 任务提升到 `todo`。便宜快速的模型在这里效果很好;规格扩展\n # 很短,不需要推理深度。\n triage_specifier:\n provider: \"auto\"\n model: \"\"\n base_url: \"\"\n api_key: \"\"\n timeout: 120\n```\n\n:::tip\n每个辅助任务都有可配置的 `timeout`(秒)。默认值:vision 120s、web_extract 360s、approval 30s、compression 120s。如果您为辅助任务使用慢速本地模型,请增加这些值。Vision 还有单独的 `download_timeout`(默认 30s)用于 HTTP 图像下载 —— 对于慢速连接或自托管图像服务器,请增加此值。\n:::\n\n:::info\n上下文压缩有自己的 `compression:` 块用于阈值,以及 `auxiliary.compression:` 块用于模型/provider 设置 —— 参阅上方的[上下文压缩](#context-compression)。主备用链使用顶层的 `fallback_providers:` 列表 —— 参阅[备用提供商](/integrations/providers#fallback-providers)。三者都遵循相同的 provider/model/base_url 模式。\n:::\n\n### 辅助任务的每任务回退链\n\n每个辅助任务都可以选择性地定义一个 `fallback_chain` —— 一个 provider/model 条目列表,当主要辅助 provider 因速率限制、网络连接问题或付费限制而失败时,Hermes 会尝试使用该列表:\n\n```yaml\nauxiliary:\n compression:\n provider: openrouter\n model: openai/gpt-4o-mini\n fallback_chain:\n - provider: nous\n model: deepseek/deepseek-chat\n - provider: openrouter\n model: google/gemini-2.5-flash\n```\n\n当主要辅助 provider(`openrouter` / `openai/gpt-4o-mini`)返回速率限制、连接超时或需要付费错误时,Hermes 将依次遍历 `fallback_chain`。它会跳过 provider 与已失败 provider 相同的条目,并尝试每个剩余条目,直到有一个成功或该链耗尽。如果所有回退都失败,Hermes 会回退到主 agent 模型作为最终的安全网。\n\n每个条目支持与任何辅助任务配置相同的三个旋钮:\n\n| 键 | 描述 |\n|-----|-------------|\n| `provider` | Provider 名称(`nous`、`openrouter`、`anthropic`、`gemini`、`main` 等) |\n| `model` | 该 provider 的模型名称 |\n| `base_url` | (可选)自定义 OpenAI 兼容端点 |\n\n`fallback_chain` 适用于任何辅助任务 —— `compression`、`vision`、`web_extract`、`approval`、`skills_hub`、`mcp` 等。\n\n### OpenRouter 路由和辅助任务的 Pareto Code\n\n当辅助任务解析到 OpenRouter(显式或通过 `provider: \"main\"` 而您的主 agent 在 OpenRouter 上)时,主 agent 的 `provider_routing` 和 `openrouter.min_coding_score` 设置**不会传播** —— 按设计,每个辅助任务是独立的。要为特定辅助任务设置 OpenRouter provider 偏好或使用 [Pareto Code 路由器](/integrations/providers#openrouter-pareto-code-router),请通过 `extra_body` 按任务设置:\n\n```yaml\nauxiliary:\n compression:\n provider: openrouter\n model: openrouter/pareto-code # 为此任务使用 Pareto Code 路由器\n extra_body:\n provider: # OpenRouter provider 路由偏好\n order: [anthropic, google] # 按此顺序尝试这些 providers\n sort: throughput # 或 \"price\" | \"latency\"\n # only: [anthropic] # 限制到特定 provider\n # ignore: [deepinfra] # 排除特定 providers\n plugins: # OpenRouter Pareto Code 路由器旋钮\n - id: pareto-router\n min_coding_score: 0.5 # 0.0–1.0;越高 = 更强的编码能力\n```\n\n形状与 OpenRouter 在聊天补全请求体中接受的内容一致。Hermes 原样转发整个 `extra_body`,因此 [openrouter.ai/docs](https://openrouter.ai/docs) 中记录的任何其他 OpenRouter 请求体字段都以相同方式工作。\n\n### 更改视觉模型\n\n使用 GPT-4o 而非 Gemini Flash 进行图像分析:\n\n```yaml\nauxiliary:\n vision:\n model: \"openai/gpt-4o\"\n```\n\n或通过环境变量(在 `~/.hermes/.env` 中):\n\n```bash\nAUXILIARY_VISION_MODEL=openai/gpt-4o\n```\n\n### Provider 选项\n\n这些选项适用于**辅助任务配置**(`auxiliary:`、`compression:`、`fallback_model:`),而非您的主 `model.provider` 设置。\n\n| Provider | 描述 | 要求 |\n|----------|-------------|-------------|\n| `\"auto\"` | 最佳可用(默认)。Vision 尝试 OpenRouter → Nous → Codex。 | — |\n| `\"openrouter\"` | 强制 OpenRouter —— 路由到任何模型(Gemini、GPT-4o、Claude 等) | `OPENROUTER_API_KEY` |\n| `\"nous\"` | 强制 Nous Portal | `hermes auth` |\n| `\"codex\"` | 强制 Codex OAuth(ChatGPT 账户)。支持视觉(gpt-5.3-codex)。 | `hermes model` → Codex |\n| `\"minimax-oauth\"` | 强制 MiniMax OAuth(浏览器登录,无需 API 密钥)。辅助任务使用 MiniMax-M2.7-highspeed。 | `hermes model` → MiniMax (OAuth) |\n| `\"xai-oauth\"` | 强制 xAI Grok OAuth(SuperGrok 或 X Premium+ 订阅者的浏览器登录,无需 API 密钥)。相同的 OAuth token 涵盖聊天、TTS、图像、视频和转录。 | `hermes model` → xAI Grok OAuth (SuperGrok / Premium+) |\n| `\"main\"` | 使用您的活跃自定义/主端点。可以来自 `OPENAI_BASE_URL` + `OPENAI_API_KEY` 或通过 `hermes model` / `config.yaml` 保存的自定义端点。适用于 OpenAI、本地模型或任何 OpenAI 兼容 API。**仅限辅助任务 —— 对 `model.provider` 无效。** | 自定义端点凭据 + 基础 URL |\n\n当您希望附带任务绕过默认路由器时,主 provider 目录中的直接 API 密钥 providers 也在这里工作。配置 `GMI_API_KEY` 后,`gmi` 有效:\n\n```yaml\nauxiliary:\n compression:\n provider: \"gmi\"\n model: \"anthropic/claude-opus-4.6\"\n```\n\n对于 GMI 辅助路由,使用 GMI 的 `/v1/models` 端点返回的确切模型 ID。\n\n### 常见设置\n\n**使用直接自定义端点**(比 `provider: \"main\"` 对本地/自托管 API 更清晰):\n```yaml\nauxiliary:\n vision:\n base_url: \"http://localhost:1234/v1\"\n api_key: \"local-key\"\n model: \"qwen2.5-vl\"\n```\n\n`base_url` 优先于 `provider`,因此这是将辅助任务路由到特定端点的最明确方式。对于直接端点覆盖,Hermes 使用配置的 `api_key` 或回退到 `OPENAI_API_KEY`;它不会为该自定义端点重用 `OPENROUTER_API_KEY`。\n\n**使用 OpenAI API 密钥进行视觉:**\n```yaml\n# 在 ~/.hermes/.env 中:\n# OPENAI_BASE_URL=https://api.openai.com/v1\n# OPENAI_API_KEY=sk-...\n\nauxiliary:\n vision:\n provider: \"main\"\n model: \"gpt-4o\" # 或 \"gpt-4o-mini\" 更便宜\n```\n\n**使用 OpenRouter 进行视觉**(路由到任何模型):\n```yaml\nauxiliary:\n vision:\n provider: \"openrouter\"\n model: \"openai/gpt-4o\" # 或 \"google/gemini-2.5-flash\" 等\n```\n\n**使用 Codex OAuth**(ChatGPT Pro/Plus 账户 —— 无需 API 密钥):\n```yaml\nauxiliary:\n vision:\n provider: \"codex\" # 使用您的 ChatGPT OAuth token\n # 模型默认为 gpt-5.3-codex(支持视觉)\n```\n\n**使用 MiniMax OAuth**(浏览器登录,无需 API 密钥):\n```yaml\nmodel:\n default: MiniMax-M2.7\n provider: minimax-oauth\n base_url: https://api.minimax.io/anthropic\n```\n运行 `hermes model` 并选择 **MiniMax (OAuth)** 自动登录并设置此项。对于中国区域,基础 URL 将是 `https://api.minimaxi.com/anthropic`。完整演练请参阅 [MiniMax OAuth 指南](../guides/minimax-oauth.md)。\n\n**使用本地/自托管模型:**\n```yaml\nauxiliary:\n vision:\n provider: \"main\" # 使用您的活跃自定义端点\n model: \"my-local-model\"\n```\n\n`provider: \"main\"` 使用 Hermes 用于普通聊天的任何 provider —— 无论是命名的自定义 provider(例如 `beans`)、内置 provider(如 `openrouter`)还是旧版 `OPENAI_BASE_URL` 端点。\n\n:::tip\n如果您使用 Codex OAuth 作为主模型 provider,视觉会自动工作 —— 无需额外配置。Codex 包含在视觉的自动检测链中。\n:::\n\n:::warning\n**视觉需要多模态模型。** 如果您设置 `provider: \"main\"`,请确保您的端点支持多模态/视觉 —— 否则图像分析将失败。\n:::\n\n### 环境变量(旧版)\n\n辅助模型也可以通过环境变量配置。但是,`config.yaml` 是首选方法 —— 它更易于管理,并支持所有选项,包括 `base_url` 和 `api_key`。\n\n| 设置 | 环境变量 |\n|---------|---------------------|\n| Vision provider | `AUXILIARY_VISION_PROVIDER` |\n| Vision 模型 | `AUXILIARY_VISION_MODEL` |\n| Vision 端点 | `AUXILIARY_VISION_BASE_URL` |\n| Vision API 密钥 | `AUXILIARY_VISION_API_KEY` |\n| Web 提取 provider | `AUXILIARY_WEB_EXTRACT_PROVIDER` |\n| Web 提取模型 | `AUXILIARY_WEB_EXTRACT_MODEL` |\n| Web 提取端点 | `AUXILIARY_WEB_EXTRACT_BASE_URL` |\n| Web 提取 API 密钥 | `AUXILIARY_WEB_EXTRACT_API_KEY` |\n\n压缩和回退模型设置仅限 config.yaml。\n\n:::tip\n运行 `hermes config` 查看您当前的辅助模型设置。覆盖仅在与默认值不同时显示。\n:::\n\n## 推理努力程度\n\n控制模型在响应前进行多少\"思考\":\n\n```yaml\nagent:\n reasoning_effort: \"\" # 空 = 中等。选项:none、minimal、low、medium、high、xhigh、max、ultra\n```\n\n未设置时(默认),推理努力程度默认为\"medium\" —— 适合大多数任务的平衡级别。设置值会覆盖它 —— 更高的推理努力程度在复杂任务上提供更好的结果,但代价是更多 token 和延迟。\n\n您也可以在运行时使用 `/reasoning` 命令更改推理努力程度:\n\n```\n/reasoning # 显示当前努力程度和显示状态\n/reasoning high # 将推理努力程度设为 high\n/reasoning none # 禁用推理\n/reasoning show # 在每次响应上方显示模型思考\n/reasoning hide # 隐藏模型思考\n```\n\n## 工具使用强制\n\n某些模型偶尔会将预期操作描述为文本而不是进行工具调用(\"我会运行测试...\"而不是实际调用终端)。工具使用强制会注入系统提示词指导,引导模型实际调用工具。\n\n```yaml\nagent:\n tool_use_enforcement: \"auto\" # \"auto\" | true | false | [\"model-substring\", ...]\n```\n\n| 值 | 行为 |\n|-------|----------|\n| `\"auto\"`(默认) | 对匹配以下模型启用:`gpt`、`codex`、`gemini`、`gemma`、`grok`。对所有其他模型禁用(Claude、DeepSeek、Qwen 等)。 |\n| `true` | 始终启用,无论模型如何。如果您注意到当前模型描述操作而不是执行操作,请使用此选项。 |\n| `false` | 始终禁用,无论模型如何。 |\n| `[\"gpt\", \"codex\", \"qwen\", \"llama\"]` | 仅当模型名称包含列出的子字符串之一时启用(不区分大小写)。 |\n\n### 注入的内容\n\n启用后,系统提示词中可能会添加三层指导:\n\n1. **通用工具使用强制**(所有匹配模型)—— 指示模型立即进行工具调用而不是描述意图,持续工作直到任务完成,永远不要以未来操作的承诺结束轮次。\n\n2. **OpenAI 执行纪律**(仅限 GPT 和 Codex 模型)—— 针对 GPT 特定失败模式的额外指导:在部分结果上放弃工作、跳过先决条件查找、幻觉而不是使用工具、在未验证的情况下宣布\"完成\"。\n\n3. **Google 操作指导**(仅限 Gemini 和 Gemma 模型)—— 简洁性、绝对路径、并行工具调用和编辑前验证模式。\n\n这些对用户透明,仅影响系统提示词。已经可靠使用工具的模型(如 Claude)不需要此指导,这就是为什么 `\"auto\"` 排除它们。\n\n### 何时开启\n\n如果您使用的模型不在默认自动列表中,并注意到它经常描述它*会*做什么而不是实际去做,请设置 `tool_use_enforcement: true` 或将模型子字符串添加到列表中:\n\n```yaml\nagent:\n tool_use_enforcement: [\"gpt\", \"codex\", \"gemini\", \"grok\", \"my-custom-model\"]\n```\n\n## TTS 配置\n\n```yaml\ntts:\n provider: \"edge\" # \"edge\" | \"elevenlabs\" | \"openai\" | \"minimax\" | \"mistral\" | \"gemini\" | \"xai\" | \"neutts\"\n speed: 1.0 # 全局速度倍数(所有 provider 的回退)\n edge:\n voice: \"en-US-AriaNeural\" # 322 种声音,74 种语言\n speed: 1.0 # 速度倍数(转换为速率百分比,例如 1.5 → +50%)\n elevenlabs:\n voice_id: \"pNInz6obpgDQGcFmaJgB\"\n model_id: \"eleven_multilingual_v2\"\n openai:\n model: \"gpt-4o-mini-tts\"\n voice: \"alloy\" # alloy、echo、fable、onyx、nova、shimmer\n speed: 1.0 # 速度倍数(API 限制为 0.25–4.0)\n base_url: \"https://api.openai.com/v1\" # 覆盖 OpenAI 兼容 TTS 端点\n minimax:\n speed: 1.0 # 语音速度倍数\n # base_url: \"\" # 可选:覆盖 OpenAI 兼容 TTS 端点\n mistral:\n model: \"voxtral-mini-tts-2603\"\n voice_id: \"c69964a6-ab8b-4f8a-9465-ec0925096ec8\" # Paul - Neutral(默认)\n gemini:\n model: \"gemini-2.5-flash-preview-tts\" # 或 gemini-2.5-pro-preview-tts\n voice: \"Kore\" # 30 种预置声音:Zephyr、Puck、Kore、Enceladus 等\n xai:\n voice_id: \"eve\" # xAI TTS 声音\n language: \"en\" # ISO 639-1\n sample_rate: 24000\n bit_rate: 128000 # MP3 比特率\n # base_url: \"https://api.x.ai/v1\"\n neutts:\n ref_audio: ''\n ref_text: ''\n model: neuphonic/neutts-air-q4-gguf\n device: cpu\n```\n\n这控制 `text_to_speech` 工具和语音模式中的口语回复(CLI 中的 `/voice tts` 或消息 gateway)。\n\n**速度回退层次:** provider 特定速度(例如 `tts.edge.speed`)→ 全局 `tts.speed` → `1.0` 默认值。设置全局 `tts.speed` 以在所有 provider 上应用统一速度,或按 provider 覆盖以进行精细控制。\n\n## 显示设置\n\n```yaml\ndisplay:\n tool_progress: all # off | new | all | verbose\n tool_progress_command: false # 在消息 gateway 中启用 /verbose 斜杠命令\n platforms: {} # 每平台显示覆盖(见下文)\n tool_progress_overrides: {} # 已弃用 —— 改用 display.platforms\n interim_assistant_messages: true # Gateway:将自然的轮次中 assistant 更新作为单独消息发送\n skin: default # 内置或自定义 CLI 皮肤(参阅 user-guide/features/skins)\n personality: \"kawaii\" # 旧版外观字段,仍在某些摘要中显示\n compact: false # 紧凑输出模式(减少空白)\n resume_display: full # full(恢复时显示之前的消息)| minimal(仅单行)\n bell_on_complete: false # 当 agent 完成时播放终端铃声(适合长任务)\n show_reasoning: false # 在每次响应上方显示模型推理/思考(用 /reasoning show|hide 切换)\n streaming: false # 将 token 实时流式传输到终端\n show_cost: false # 在 CLI 状态栏中显示估计 $ 成本\n timestamps: false # 为 true 时,在 CLI/TUI 记录中为用户和 assistant 标签添加 [HH:MM] 时间戳前缀\n tool_preview_length: 0 # 工具调用预览的最大字符数(0 = 无限制,显示完整路径/命令)\n runtime_footer: # Gateway:在最终回复中附加运行时上下文页脚\n enabled: false\n fields: [\"model\", \"context_pct\", \"cwd\"]\n file_mutation_verifier: true # 当本轮 write_file/patch 调用失败时附加建议性页脚\n language: en # 静态消息的 UI 语言(审批提示、部分 gateway 回复)。en | zh | zh-hant | ja | de | es | fr | tr | uk | af | ko | it | ga | pt | ru | hu\n```\n\n### 文件变更验证器\n\n当 `display.file_mutation_verifier` 为 `true`(默认)时,每当本轮中 `write_file` 或 `patch` 调用失败且从未被对同一路径的成功写入取代时,Hermes 会在 assistant 的最终响应中附加一行建议。这捕获了\"批量并行补丁,一半静默失败,模型总结成功\"这类过度声明,而无需您在每次编辑后手动运行 `git status`。\n\n示例页脚:\n\n```\n⚠️ File-mutation verifier: 3 file(s) were NOT modified this turn despite any wording above that may suggest otherwise. Run `git status` or `read_file` to confirm.\n • concepts/automatic-organization.md — [patch] Could not find match for old_string\n • concepts/lora.md — [patch] Could not find match for old_string\n • concepts/rag-pipeline.md — [patch] Could not find match for old_string\n```\n\n设置 `file_mutation_verifier: false`(或 `HERMES_FILE_MUTATION_VERIFIER=0`)以禁止页脚。验证器仅在轮次结束时有真实失败未解决时触发 —— 在同一轮次内重试失败补丁并成功的模型不会为该文件触发它。\n\n### 静态消息的 UI 语言\n\n`display.language` 设置翻译一小组静态面向用户的消息 —— CLI 审批提示、少数 gateway 斜杠命令回复(例如重启排空通知、\"审批已过期\"、\"目标已清除\")。它**不**翻译 agent 响应、日志行、工具输出、错误回溯或斜杠命令描述 —— 这些保持英文。如果您希望 agent 本身用另一种语言回复,只需在您的提示词或系统消息中告诉它。\n\n支持的值:`en`(默认)、`zh`(简体中文)、`zh-hant`(繁体中文)、`ja`(日语)、`de`(德语)、`es`(西班牙语)、`fr`(法语)、`tr`(土耳其语)、`uk`(乌克兰语)、`af`(南非荷兰语)、`ko`(韩语)、`it`(意大利语)、`ga`(爱尔兰语)、`pt`(葡萄牙语)、`ru`(俄语)、`hu`(匈牙利语)。未知值回退到英文。\n\n您也可以使用 `HERMES_LANGUAGE` 环境变量按会话设置,它会覆盖配置值。\n\n```yaml\ndisplay:\n language: zh # CLI 审批提示以中文显示\n```\n\n| 模式 | 您看到的内容 |\n|------|-------------|\n| `off` | 静默 —— 仅最终响应 |\n| `new` | 仅在工具更改时显示工具指示器 |\n| `all` | 每次工具调用附带简短预览(默认) |\n| `verbose` | 完整参数、结果和调试日志 |\n\n在 CLI 中,使用 `/verbose` 循环切换这些模式。要在消息平台(Telegram、Discord、Slack 等)中使用 `/verbose`,请在上方的 `display` 部分设置 `tool_progress_command: true`。该命令将循环切换模式并保存到配置。\n\n### 运行时元数据页脚(仅限 gateway)\n\n当 `display.runtime_footer.enabled: true` 时,Hermes 在每个 gateway 轮次的**最终**消息中附加一个小型运行时上下文页脚。目前页脚可显示模型、上下文窗口百分比和当前工作目录。默认关闭;如果您的团队希望每个回复都包含这些来源信息,请按 gateway 选择加入。\n\n```yaml\ndisplay:\n runtime_footer:\n enabled: true\n fields: [\"model\", \"context_pct\", \"cwd\"] # 支持字段:model、context_pct、cwd\n```\n\n`/footer` 斜杠命令在任何会话中运行时切换此功能。\n\n附加到 Telegram/Discord/Slack 回复的示例页脚:\n\n```\n— claude-opus-4.7 · 12 tool calls · 2m 14s · $0.042\n```\n\n只有轮次的**最终**消息获得页脚;中间更新保持干净。\n\n### 每平台进度覆盖\n\n不同平台有不同的详细程度需求。例如,Signal 无法编辑消息,因此每次进度更新都会成为单独的消息 —— 很嘈杂。使用 `display.platforms` 设置每平台模式:\n\n```yaml\ndisplay:\n tool_progress: all # 全局默认\n platforms:\n signal:\n tool_progress: 'off' # 在 Signal 上静默进度\n telegram:\n tool_progress: verbose # 在 Telegram 上详细进度\n slack:\n tool_progress: 'off' # 在共享 Slack 工作区中保持安静\n```\n\n没有覆盖的平台回退到全局 `tool_progress` 值。有效平台键:`telegram`、`discord`、`slack`、`signal`、`whatsapp`、`matrix`、`mattermost`、`email`、`sms`、`homeassistant`、`dingtalk`、`feishu`、`wecom`、`weixin`、`bluebubbles`、`qqbot`。旧版 `display.tool_progress_overrides` 键仍可加载以向后兼容,但已弃用,并在首次加载时迁移到 `display.platforms`。\n\n`interim_assistant_messages` 仅限 gateway。启用后,Hermes 将已完成的轮次中 assistant 更新作为单独的聊天消息发送。这与 `tool_progress` 无关,不需要 gateway 流式传输。\n\n## 隐私\n\n```yaml\nprivacy:\n redact_pii: false # 从 LLM 上下文中删除 PII(仅限 gateway)\n```\n\n当 `redact_pii` 为 `true` 时,gateway 在将系统提示词发送到受支持平台上的 LLM 之前,会从中删除个人身份信息:\n\n| 字段 | 处理方式 |\n|-------|-----------|\n| 电话号码(WhatsApp/Signal 上的用户 ID) | 哈希为 `user_<12-char-sha256>` |\n| 用户 ID | 哈希为 `user_<12-char-sha256>` |\n| 聊天 ID | 数字部分哈希,保留平台前缀(`telegram:<hash>`) |\n| 主频道 ID | 数字部分哈希 |\n| 用户名/昵称 | **不受影响**(用户选择的,公开可见) |\n\n**平台支持:** 删除适用于 WhatsApp、Signal 和 Telegram。Discord 和 Slack 被排除,因为它们的提及系统(`<@user_id>`)需要 LLM 上下文中的真实 ID。\n\n哈希是确定性的 —— 同一用户始终映射到同一哈希,因此模型仍然可以在群聊中区分用户。路由和传递在内部使用原始值。\n\n## 语音转文字(STT)\n\n```yaml\nstt:\n provider: \"local\" # \"local\" | \"groq\" | \"openai\" | \"mistral\"\n local:\n model: \"base\" # tiny、base、small、medium、large-v3\n openai:\n model: \"whisper-1\" # whisper-1 | gpt-4o-mini-transcribe | gpt-4o-transcribe\n # model: \"whisper-1\" # 旧版回退键仍受支持\n```\n\nProvider 行为:\n\n- `local` 使用在您机器上运行的 `faster-whisper`。使用 `pip install faster-whisper` 单独安装。\n- `groq` 使用 Groq 的 Whisper 兼容端点,读取 `GROQ_API_KEY`。\n- `openai` 使用 OpenAI 语音 API,读取 `VOICE_TOOLS_OPENAI_KEY`。\n\n如果请求的 provider 不可用,Hermes 按此顺序自动回退:`local` → `groq` → `openai`。\n\nGroq 和 OpenAI 模型覆盖由环境变量驱动:\n\n```bash\nSTT_GROQ_MODEL=whisper-large-v3-turbo\nSTT_OPENAI_MODEL=whisper-1\nGROQ_BASE_URL=https://api.groq.com/openai/v1\nSTT_OPENAI_BASE_URL=https://api.openai.com/v1\n```\n\n## 语音模式(CLI)\n\n```yaml\nvoice:\n record_key: \"ctrl+b\" # CLI 内的按键通话键\n max_recording_seconds: 120 # 长录音的硬停止\n auto_tts: false # /voice on 时自动启用口语回复\n beep_enabled: true # 在 CLI 语音模式中播放录音开始/停止提示音\n silence_threshold: 200 # 语音检测的 RMS 阈值\n silence_duration: 3.0 # 自动停止前的静默秒数\n```\n\n在 CLI 中使用 `/voice on` 启用麦克风模式,使用 `record_key` 开始/停止录音,使用 `/voice tts` 切换口语回复。端到端设置和平台特定行为请参阅[语音模式](/user-guide/features/voice-mode)。\n\n## 流式传输\n\n将 token 实时流式传输到终端或消息平台,而不是等待完整响应。\n\n### CLI 流式传输\n\n```yaml\ndisplay:\n streaming: true # 实时将 token 流式传输到终端\n show_reasoning: true # 同时流式传输推理/思考 token(可选)\n```\n\n启用后,响应在流式传输框内逐 token 出现。工具调用仍然静默捕获。如果 provider 不支持流式传输,它会自动回退到正常显示。\n\n### Gateway 流式传输(Telegram、Discord、Slack)\n\n```yaml\nstreaming:\n enabled: true # 启用渐进式消息编辑\n transport: edit # \"edit\"(渐进式消息编辑)或 \"off\"\n edit_interval: 0.3 # 消息编辑之间的秒数\n buffer_threshold: 40 # 强制编辑刷新前的字符数\n cursor: \" ▉\" # 流式传输期间显示的光标\n fresh_final_after_seconds: 0 # 预览超过此时间时选择发送新的最终消息(Telegram)\n```\n\n启用后,bot 在第一个 token 时发送消息,然后随着更多 token 到来渐进式编辑它。不支持消息编辑的平台(Signal、Email、Home Assistant)在第一次尝试时自动检测 —— 该会话的流式传输被优雅地禁用,不会产生大量消息。\n\n对于不带渐进式 token 编辑的独立自然轮次中 assistant 更新,请设置 `display.interim_assistant_messages: true`。\n\n**溢出处理:** 如果流式传输的文本超过平台的消息长度限制(约 4096 字符),当前消息被最终化,新消息自动开始。\n\n**新的最终消息(Telegram):** Telegram 的 `editMessageText` 保留原始消息时间戳,因此长时间运行的流式回复即使在完成后也会保留第一个 token 的时间戳。设置 `fresh_final_after_seconds > 0` 可选择将旧预览作为全新的最终消息传递,并尽力删除旧预览。默认值为 `0`,始终就地最终化流式回复,避免某些客户端短暂显示重复消息再删除其中一条。\n\n:::note\n主开关 `streaming.enabled` 默认为 `false`——在你启用之前不会有任何流式传输。启用后,是否流式传输按**平台**决定:Telegram 默认带有 `display.platforms.telegram.streaming: true`(流式传输),Discord 为 `display.platforms.discord.streaming: false`(不流式传输)。因此启用流式传输后,Telegram 开箱即用地流式传输,Discord 在你修改其开关之前仍使用整条消息回复。你可以在仪表盘的 **Channels** 开关中或直接在 `~/.hermes/config.yaml` 中调整这些按平台的开关。\n:::\n\n## 群聊会话隔离\n\n控制共享聊天是每个房间保持一个对话还是每个参与者一个对话:\n\n```yaml\ngroup_sessions_per_user: true # true = 群组/频道中每用户隔离,false = 每个聊天一个共享会话\n```\n\n- `true` 是默认和推荐设置。在 Discord 频道、Telegram 群组、Slack 频道和类似共享上下文中,当平台提供用户 ID 时,每个发送者获得自己的会话。\n- `false` 恢复到旧的共享房间行为。如果您明确希望 Hermes 将频道视为一个协作对话,这可能有用,但这也意味着用户共享上下文、token 成本和中断状态。\n- 私信不受影响。Hermes 仍然像往常一样通过聊天/DM ID 键入 DM。\n- 线程与其父频道保持隔离;使用 `true` 时,每个参与者在线程内也获得自己的会话。\n\n有关行为详情和示例,请参阅[会话](/user-guide/sessions)和 [Discord 指南](/user-guide/messaging/discord)。\n\n## 未授权 DM 行为\n\n控制当未知用户发送私信时 Hermes 的行为:\n\n```yaml\nunauthorized_dm_behavior: pair\n\nwhatsapp:\n unauthorized_dm_behavior: ignore\n```\n\n- `pair` 是默认值。Hermes 拒绝访问,但在 DM 中回复一次性配对码。\n- `ignore` 静默丢弃未授权的 DM。\n- 平台部分覆盖全局默认值,因此您可以在广泛范围内保持配对启用,同时使一个平台更安静。\n\n## 快速命令\n\n定义自定义命令,这些命令要么在不调用 LLM 的情况下运行 shell 命令,要么将一个斜杠命令别名为另一个。Exec 快速命令是零 token 的,对于从消息平台(Telegram、Discord 等)进行快速服务器检查或实用脚本很有用。\n\n```yaml\nquick_commands:\n status:\n type: exec\n command: systemctl status hermes-agent\n disk:\n type: exec\n command: df -h /\n update:\n type: exec\n command: cd ~/.hermes/hermes-agent && git pull && uv pip install -e .\n gpu:\n type: exec\n command: nvidia-smi --query-gpu=name,utilization.gpu,memory.used,memory.total --format=csv,noheader\n restart:\n type: alias\n target: /gateway restart\n```\n\n用法:在 CLI 或任何消息平台中输入 `/status`、`/disk`、`/update`、`/gpu` 或 `/restart`。`exec` 命令在宿主本地运行并直接返回输出 —— 无 LLM 调用,不消耗 token。`alias` 命令重写为配置的斜杠命令目标。\n\n- **30 秒超时** —— 长时间运行的命令被终止并显示错误消息\n- **优先级** —— 快速命令在技能命令之前检查,因此您可以覆盖技能名称\n- **自动补全** —— 快速命令在调度时解析,不显示在内置斜杠命令自动补全表中\n- **类型** —— 支持的类型为 `exec` 和 `alias`;其他类型显示错误\n- **到处可用** —— CLI、Telegram、Discord、Slack、WhatsApp、Signal、Email、Home Assistant\n\n仅字符串的 prompt 快捷方式不是有效的快速命令。对于可重用的 prompt 工作流,请创建技能或别名到现有斜杠命令。\n\n## 人类延迟\n\n在消息平台中模拟类人响应节奏:\n\n```yaml\nhuman_delay:\n mode: \"off\" # off | natural | custom\n min_ms: 800 # 最小延迟(自定义模式)\n max_ms: 2500 # 最大延迟(自定义模式)\n```\n\n## 代码执行\n\n配置 `execute_code` 工具:\n\n```yaml\ncode_execution:\n mode: project # project(默认)| strict\n timeout: 300 # 最大执行时间(秒)\n max_tool_calls: 50 # 代码执行中的最大工具调用次数\n```\n\n**`mode`** 控制脚本的工作目录和 Python 解释器:\n\n- **`project`**(默认)—— 脚本在会话的工作目录中以活跃 virtualenv/conda 环境的 python 运行。项目依赖(`pandas`、`torch`、项目包)和相对路径(`.env`、`./data.csv`)自然解析,与 `terminal()` 看到的一致。\n- **`strict`** —— 脚本在临时暂存目录中以 `sys.executable`(Hermes 自己的 python)运行。最大可重现性,但项目依赖和相对路径不会解析。\n\n环境清理(删除 `*_API_KEY`、`*_TOKEN`、`*_SECRET`、`*_PASSWORD`、`*_CREDENTIAL`、`*_PASSWD`、`*_AUTH`)和工具白名单在两种模式下完全相同 —— 切换模式不会改变安全态势。\n\n## Web 搜索后端\n\n`web_search` 和 `web_extract` 工具支持五种后端 provider。在 `config.yaml` 中或通过 `hermes tools` 配置后端:\n\n```yaml\nweb:\n backend: firecrawl # firecrawl | searxng | parallel | tavily | exa\n\n # 或使用每功能键混合 provider(例如免费搜索 + 付费提取):\n search_backend: \"searxng\"\n extract_backend: \"firecrawl\"\n```\n\n| 后端 | 环境变量 | 搜索 | 提取 |\n|---------|---------|--------|---------|\n| **Firecrawl**(默认) | `FIRECRAWL_API_KEY` | ✔ | ✔ |\n| **SearXNG** | `SEARXNG_URL` | ✔ | — |\n| **Parallel** | `PARALLEL_API_KEY` | ✔ | ✔ |\n| **Tavily** | `TAVILY_API_KEY` | ✔ | ✔ |\n| **Exa** | `EXA_API_KEY` | ✔ | ✔ |\n\n**后端选择:** 如果未设置 `web.backend`,后端从可用的 API 密钥自动检测。如果仅设置了 `SEARXNG_URL`,使用 SearXNG。如果仅设置了 `EXA_API_KEY`,使用 Exa。如果仅设置了 `TAVILY_API_KEY`,使用 Tavily。如果仅设置了 `PARALLEL_API_KEY`,使用 Parallel。否则 Firecrawl 是默认值。\n\n**SearXNG** 是一个免费、自托管、尊重隐私的元搜索引擎,查询 70+ 个搜索引擎。无需 API 密钥 —— 只需将 `SEARXNG_URL` 设置为您的实例(例如 `http://localhost:8080`)。SearXNG 仅限搜索;`web_extract` 需要单独的提取 provider(设置 `web.extract_backend`)。Docker 设置说明请参阅 [Web 搜索设置指南](/user-guide/features/web-search)。\n\n**自托管 Firecrawl:** 设置 `FIRECRAWL_API_URL` 指向您自己的实例。设置自定义 URL 后,API 密钥变为可选(在服务器上设置 `USE_DB_AUTHENTICATION=***` 以禁用认证)。\n\n**Parallel 搜索模式:** 设置 `PARALLEL_SEARCH_MODE` 控制搜索行为 —— `fast`、`one-shot` 或 `agentic`(默认:`agentic`)。\n\n**Exa:** 在 `~/.hermes/.env` 中设置 `EXA_API_KEY`。支持 `category` 过滤(`company`、`research paper`、`news`、`people`、`personal site`、`pdf`)和域名/日期过滤器。\n\n## 浏览器\n\n配置浏览器自动化行为:\n\n```yaml\nbrowser:\n inactivity_timeout: 120 # 自动关闭空闲会话前的秒数\n command_timeout: 30 # 浏览器命令超时(截图、导航等)(秒)\n record_sessions: false # 自动将浏览器会话录制为 WebM 视频到 ~/.hermes/browser_recordings/\n # 可选 CDP 覆盖 —— 设置后,Hermes 直接附加到您自己的\n # Chromium 系浏览器(通过 /browser connect),而不是启动无头浏览器。\n cdp_url: \"\"\n # 对话框监督器 —— 控制当 CDP 后端附加时(Browserbase、本地 Chromium 系\n # 浏览器通过 /browser connect)如何处理原生 JS 对话框(alert/confirm/prompt)。\n # 在 Camofox 和默认本地 agent 浏览器模式下忽略。\n dialog_policy: must_respond # must_respond | auto_dismiss | auto_accept\n dialog_timeout_s: 300 # must_respond 下的安全自动关闭(秒)\n camofox:\n managed_persistence: false # 为 true 时,Camofox 会话跨重启持久化 cookie/登录\n user_id: \"\" # 可选的外部管理 Camofox userId\n session_key: \"\" # Hermes 创建标签页时发送的可选会话密钥\n adopt_existing_tab: false # 在创建新标签页之前重用此身份的现有标签页\n```\n\n**对话框策略:**\n\n- `must_respond`(默认)—— 捕获对话框,在 `browser_snapshot.pending_dialogs` 中显示,等待 agent 调用 `browser_dialog(action=...)`。在 `dialog_timeout_s` 秒内无响应后,对话框被自动关闭以防止页面的 JS 线程永久停滞。\n- `auto_dismiss` —— 捕获,立即关闭。Agent 仍然在事后的 `browser_snapshot.recent_dialogs` 中看到对话框记录,`closed_by=\"auto_policy\"`。\n- `auto_accept` —— 捕获,立即接受。适用于有激进 `beforeunload` 提示的页面。\n\n完整对话框工作流请参阅[浏览器功能页面](./features/browser.md#browser_dialog)。\n\n浏览器工具集支持多个 provider。有关 Browserbase、Browser Use 和本地 Chromium 系 CDP 设置的详细信息,请参阅[浏览器功能页面](/user-guide/features/browser)。\n\n## 时区\n\n使用 IANA 时区字符串覆盖服务器本地时区。影响日志中的时间戳、cron 调度和系统提示词时间注入。\n\n```yaml\ntimezone: \"America/New_York\" # IANA 时区(默认:\"\" = 服务器本地时间)\n```\n\n支持的值:任何 IANA 时区标识符(例如 `America/New_York`、`Europe/London`、`Asia/Kolkata`、`UTC`)。留空或省略以使用服务器本地时间。\n\n## Discord\n\n为消息 gateway 配置 Discord 特定行为:\n\n```yaml\ndiscord:\n require_mention: true # 在服务器频道中需要 @提及才能响应\n free_response_channels: \"\" # 逗号分隔的频道 ID,bot 在这些频道无需 @提及即可响应\n auto_thread: true # 在频道中 @提及时自动创建线程\n```\n\n- `require_mention` —— 为 `true`(默认)时,bot 仅在服务器频道中被 `@BotName` 提及时响应。DM 始终无需提及即可工作。\n- `free_response_channels` —— 逗号分隔的频道 ID 列表,bot 在这些频道对每条消息响应,无需提及。\n- `auto_thread` —— 为 `true`(默认)时,频道中的提及会自动为对话创建线程,保持频道整洁(类似 Slack 线程)。\n\n## 安全\n\n预执行安全扫描和机密脱敏:\n\n```yaml\nsecurity:\n redact_secrets: false # 在工具输出和日志中脱敏 API 密钥模式(默认关闭)\n tirith_enabled: true # 为终端命令启用 Tirith 安全扫描\n tirith_path: \"tirith\" # tirith 二进制文件路径(默认:$PATH 中的 \"tirith\")\n tirith_timeout: 5 # 等待 tirith 扫描的秒数\n tirith_fail_open: true # 如果 tirith 不可用,允许命令执行\n website_blocklist: # 参见下方网站黑名单部分\n enabled: false\n domains: []\n shared_files: []\n```\n\n- `redact_secrets` —— 为 `true` 时,自动检测并脱敏工具输出中看起来像 API 密钥、token 和密码的模式,然后再进入对话上下文和日志。**默认关闭** —— 如果您经常在工具输出中处理真实凭据并希望有安全网,请启用。显式设置为 `true` 以开启。\n- `tirith_enabled` —— 为 `true` 时,终端命令在执行前由 [Tirith](https://github.com/sheeki03/tirith) 扫描以检测潜在危险操作。\n- `tirith_path` —— tirith 二进制文件的路径。如果 tirith 安装在非标准位置,请设置此项。\n- `tirith_timeout` —— 等待 tirith 扫描的最大秒数。如果扫描超时,命令继续执行。\n- `tirith_fail_open` —— 为 `true`(默认)时,如果 tirith 不可用或失败,允许命令执行。设置为 `false` 以在 tirith 无法验证时阻止命令。\n\n## 网站黑名单\n\n阻止 agent 的 web 和浏览器工具访问特定域名:\n\n```yaml\nsecurity:\n website_blocklist:\n enabled: false # 启用 URL 阻止(默认:false)\n domains: # 被阻止的域名模式列表\n - \"*.internal.company.com\"\n - \"admin.example.com\"\n - \"*.local\"\n shared_files: # 从外部文件加载额外规则\n - \"/etc/hermes/blocked-sites.txt\"\n```\n\n启用后,任何匹配被阻止域名模式的 URL 在 web 或浏览器工具执行之前都会被拒绝。这适用于 `web_search`、`web_extract`、`browser_navigate` 以及任何访问 URL 的工具。\n\n域名规则支持:\n- 精确域名:`admin.example.com`\n- 通配符子域名:`*.internal.company.com`(阻止所有子域名)\n- TLD 通配符:`*.local`\n\n共享文件每行包含一条域名规则(空行和 `#` 注释被忽略)。缺失或不可读的文件记录警告,但不禁用其他 web 工具。\n\n策略缓存 30 秒,因此配置更改无需重启即可快速生效。\n\n## 智能审批\n\n控制 Hermes 如何处理潜在危险命令:\n\n```yaml\napprovals:\n mode: smart # smart | manual | off\n```\n\n| 模式 | 行为 |\n|------|----------|\n| `smart`(默认) | 使用辅助 LLM 评估被标记的命令是否真正危险。低风险命令仅对当前命令自动批准,真正危险的命令自动拒绝,不确定的情况升级给用户。 |\n| `manual` | 在执行任何被标记的命令之前提示用户。在 CLI 中显示交互式审批对话框。在消息中排队待处理的审批请求。 |\n| `off` | 跳过所有审批检查。等同于 `HERMES_YOLO_MODE=true`。**谨慎使用。** |\n\n智能模式对于减少审批疲劳特别有用 —— 它让 agent 在安全操作上更自主地工作,同时仍然捕获真正破坏性的命令。\n\n:::warning\n设置 `approvals.mode: off` 会禁用终端命令的所有安全检查。仅在受信任的沙箱环境中使用。\n:::\n\n## 检查点\n\n破坏性文件操作之前的自动文件系统快照。详情请参阅[检查点与回滚](/user-guide/checkpoints-and-rollback)。\n\n```yaml\ncheckpoints:\n enabled: false # 启用自动检查点(也可:hermes chat --checkpoints)。默认:false(选择加入)。\n max_snapshots: 20 # 每个目录保留的最大检查点数(默认:20)\n```\n\n\n## 委托\n\n为委托工具配置子 agent 行为:\n\n```yaml\ndelegation:\n # model: \"google/gemini-3-flash-preview\" # 覆盖模型(空 = 继承父级)\n # provider: \"openrouter\" # 覆盖 provider(空 = 继承父级)\n # base_url: \"http://localhost:1234/v1\" # 直接 OpenAI 兼容端点(优先于 provider)\n # api_key: \"local-key\" # base_url 的 API 密钥(回退到 OPENAI_API_KEY)\n # api_mode: \"\" # base_url 的线路协议:\"chat_completions\"、\"codex_responses\" 或 \"anthropic_messages\"。空 = 从 URL 自动检测(例如 /anthropic 后缀 → anthropic_messages)。对启发式无法检测的非标准端点显式设置。\n max_concurrent_children: 3 # 每批并行子 agent 数(下限 1,无上限)。也可通过 DELEGATION_MAX_CONCURRENT_CHILDREN 环境变量设置。\n max_spawn_depth: 1 # 委托树深度上限(1-3,截断)。1 = 扁平(默认):父级生成无法委托的叶子。2 = 编排器子级可以生成叶子孙级。3 = 三级。\n orchestrator_enabled: true # 全局终止开关。为 false 时,role=\"orchestrator\" 被忽略,每个子级无论 max_spawn_depth 如何都被强制为叶子。\n```\n\n**子 agent provider:model 覆盖:** 默认情况下,子 agent 继承父 agent 的 provider 和模型。设置 `delegation.provider` 和 `delegation.model` 将子 agent 路由到不同的 provider:model 对 —— 例如,在您的主 agent 运行昂贵推理模型时,为范围较窄的子任务使用便宜/快速的模型。\n\n**直接端点覆盖:** 如果您想要明显的自定义端点路径,请设置 `delegation.base_url`、`delegation.api_key` 和 `delegation.model`。这将子 agent 直接发送到该 OpenAI 兼容端点,并优先于 `delegation.provider`。如果省略 `delegation.api_key`,Hermes 仅回退到 `OPENAI_API_KEY`。\n\n**线路协议(`api_mode`):** Hermes 从 `delegation.base_url` 自动检测线路协议(例如以 `/anthropic` 结尾的路径 → `anthropic_messages`;Codex/原生 Anthropic/Kimi-coding 主机名保留其现有检测)。对于启发式无法分类的端点 —— 例如 Azure AI Foundry、MiniMax、Zhipu GLM 或前置 Anthropic 形状后端的 LiteLLM 代理 —— 请将 `delegation.api_mode` 显式设置为 `chat_completions`、`codex_responses` 或 `anthropic_messages` 之一。留空(默认)以保持自动检测。\n\n委托 provider 使用与 CLI/gateway 启动相同的凭据解析。所有配置的 provider 均受支持:`openrouter`、`nous`、`copilot`、`zai`、`kimi-coding`、`minimax`、`minimax-cn`。设置 provider 时,系统自动解析正确的基础 URL、API 密钥和 API 模式 —— 无需手动凭据连接。\n\n**优先级:** 配置中的 `delegation.base_url` → 配置中的 `delegation.provider` → 父 provider(继承)。配置中的 `delegation.model` → 父模型(继承)。仅设置 `model` 而不设置 `provider` 仅更改模型名称,同时保留父级凭据(适用于在同一 provider(如 OpenRouter)内切换模型)。\n\n**宽度和深度:** `max_concurrent_children` 限制每批并行运行的子 agent 数量(默认 `3`,下限 1,无上限)。也可通过 `DELEGATION_MAX_CONCURRENT_CHILDREN` 环境变量设置。当模型提交的 `tasks` 数组超过上限时,`delegate_task` 返回工具错误解释限制,而不是静默截断。`max_spawn_depth` 控制委托树深度(截断到 1-3)。在默认 `1` 时,委托是扁平的:子级无法生成孙级,传递 `role=\"orchestrator\"` 静默降级为 `leaf`。提升到 `2` 使编排器子级可以生成叶子孙级;`3` 用于三级树。Agent 通过 `role=\"orchestrator\"` 按调用选择编排;`orchestrator_enabled: false` 强制每个子级回到叶子,无论如何。成本呈乘法增长 —— 在 `max_spawn_depth: 3` 和 `max_concurrent_children: 3` 时,树可以达到 3×3×3 = 27 个并发叶子 agent。使用模式请参阅[子 Agent 委托 → 深度限制和嵌套编排](features/delegation.md#depth-limit-and-nested-orchestration)。\n\n## 澄清\n\n配置澄清提示行为:\n\n```yaml\nclarify:\n timeout: 120 # 等待用户澄清响应的秒数\n```\n\n## 上下文文件(SOUL.md、AGENTS.md)\n\nHermes 使用两种不同的上下文范围:\n\n| 文件 | 用途 | 范围 |\n|------|---------|-------|\n| `SOUL.md` | **主要 agent 身份** —— 定义 agent 是谁(系统提示词第 #1 槽位) | `~/.hermes/SOUL.md` 或 `$HERMES_HOME/SOUL.md` |\n| `.hermes.md` / `HERMES.md` | 项目特定指令(最高优先级) | 向上走到 git 根目录 |\n| `AGENTS.md` | 项目特定指令、编码规范 | 递归目录遍历 |\n| `CLAUDE.md` | Claude Code 上下文文件(也会检测) | 仅工作目录 |\n| `.cursorrules` | Cursor IDE 规则(也会检测) | 仅工作目录 |\n| `.cursor/rules/*.mdc` | Cursor 规则文件(也会检测) | 仅工作目录 |\n\n- **SOUL.md** 是 agent 的主要身份。它占据系统提示词的第 #1 槽位,完全替换内置的默认身份。编辑它以完全自定义 agent 是谁。\n- 如果 SOUL.md 缺失、为空或无法加载,Hermes 回退到内置默认身份。\n- **项目上下文文件使用优先级系统** —— 仅加载一种类型(第一个匹配优先):`.hermes.md` → `AGENTS.md` → `CLAUDE.md` → `.cursorrules`。SOUL.md 始终独立加载。\n- **AGENTS.md** 是分层的:如果子目录也有 AGENTS.md,所有都会合并。\n- 如果 `SOUL.md` 不存在,Hermes 会自动生成默认的 `SOUL.md`。\n- 所有加载的上下文文件上限为 20,000 字符,并进行智能截断。\n\n另请参阅:\n- [个性与 SOUL.md](/user-guide/features/personality)\n- [上下文文件](/user-guide/features/context-files)\n\n## 工作目录\n\n| 上下文 | 默认值 |\n|---------|---------|\n| **CLI(`hermes`)** | 运行命令的当前目录 |\n| **消息 gateway** | 主目录 `~`(用 `MESSAGING_CWD` 覆盖) |\n| **Docker / Singularity / Modal / SSH** | 容器或远程机器内用户的主目录 |\n\n覆盖工作目录:\n```bash\n# 在 ~/.hermes/.env 或 ~/.hermes/config.yaml 中:\nMESSAGING_CWD=/home/myuser/projects # Gateway 会话\nTERMINAL_CWD=/workspace # 所有终端会话\n```\n"}, "cfb206fe2e8034793a97a10944cfc733d2ddfc8b": {"t": 1785767971.461, "n": 57227, "h": "266e0ccd5c7d1200", "c": "---\nsidebar_position: 2\ntitle: \"配置\"\ndescription: \"配置 Hermes Agent — config.yaml、providers、模型、API 密钥等\"\n---\n\n# 配置\n\n所有设置均存储在 `~/.hermes/` 目录中,便于访问。\n\n## 目录结构\n\n```text\n~/.hermes/\n├── config.yaml # 设置(模型、终端、TTS、压缩等)\n├── .env # API 密钥和机密\n├── auth.json # OAuth provider 凭据(Nous Portal 等)\n├── SOUL.md # 主要 agent 身份(系统提示词第 #1 槽位)\n├── memories/ # 持久记忆(MEMORY.md、USER.md)\n├── skills/ # Agent 创建的技能(通过 skill_manage 工具管理)\n├── cron/ # 定时任务\n├── sessions/ # Gateway 会话\n└── logs/ # 日志(errors.log、gateway.log — 机密自动脱敏)\n```\n\n## 管理配置\n\n```bash\nhermes config # 查看当前配置\nhermes config edit # 在编辑器中打开 config.yaml\nhermes config set KEY VAL # 设置特定值\nhermes config check # 检查缺失选项(更新后使用)\nhermes config migrate # 交互式添加缺失选项\n\n# 示例:\nhermes config set model anthropic/claude-opus-4\nhermes config set terminal.backend docker\nhermes config set OPENROUTER_API_KEY sk-or-... # 保存到 .env\n```\n\n:::tip\n`hermes config set` 命令会自动将值路由到正确的文件 —— API 密钥保存到 `.env`,其他所有内容保存到 `config.yaml`。\n:::\n\n## 配置优先级\n\n设置按以下顺序解析(优先级从高到低):\n\n1. **CLI 参数** —— 例如 `hermes chat --model anthropic/claude-sonnet-4`(单次调用覆盖)\n2. **`~/.hermes/config.yaml`** —— 所有非机密设置的主配置文件\n3. **`~/.hermes/.env`** —— 环境变量的回退;机密(API 密钥、token、密码)**必须**放这里\n4. **内置默认值** —— 未设置任何内容时的硬编码安全默认值\n\n:::info 经验法则\n机密(API 密钥、bot token、密码)放入 `.env`。其他所有内容(模型、终端后端、压缩设置、内存限制、工具集)放入 `config.yaml`。当两者都设置时,`config.yaml` 对非机密设置优先。\n:::\n\n## 环境变量替换\n\n可以在 `config.yaml` 中使用 `${VAR_NAME}` 语法引用环境变量:\n\n```yaml\nauxiliary:\n vision:\n api_key: ${GOOGLE_API_KEY}\n base_url: ${CUSTOM_VISION_URL}\n\ndelegation:\n api_key: ${DELEGATION_KEY}\n```\n\n单个值中可以有多个引用:`url: \"${HOST}:${PORT}\"`。如果引用的变量未设置,占位符将保持原样(`${UNDEFINED_VAR}` 保持不变)。仅支持 `${VAR}` 语法 —— 裸 `$VAR` 不会被展开。\n\n关于 AI provider 设置(OpenRouter、Anthropic、Copilot、自定义端点、自托管 LLM、回退模型等),请参阅 [AI Providers](/integrations/providers)。\n\n### Provider 超时\n\n可以为 provider 设置 `providers.<id>.request_timeout_seconds` 作为全局请求超时,以及 `providers.<id>.models.<model>.timeout_seconds` 作为特定模型的覆盖值。适用于每种传输方式(OpenAI-wire、原生 Anthropic、Anthropic 兼容)上的主轮次客户端、回退链、凭据轮换后的重建,以及(对于 OpenAI-wire)每请求超时 kwarg —— 因此配置值优先于旧版 `HERMES_API_TIMEOUT` 环境变量。\n\n还可以设置 `providers.<id>.stale_timeout_seconds` 用于非流式陈旧调用检测器,以及 `providers.<id>.models.<model>.stale_timeout_seconds` 作为特定模型的覆盖值。此值优先于旧版 `HERMES_API_CALL_STALE_TIMEOUT` 环境变量。\n\n不设置这些值将保持旧版默认值(`HERMES_API_TIMEOUT=1800`s、`HERMES_API_CALL_STALE_TIMEOUT=90`s、原生 Anthropic 900s)。隐式的非流式 stale 检测会在本地端点上自动禁用,并且会在超大上下文下自动放宽。目前不适用于 AWS Bedrock(`bedrock_converse` 和 AnthropicBedrock SDK 路径均使用 boto3 及其自身的超时配置)。请参阅 [`cli-config.yaml.example`](https://github.com/NousResearch/hermes-agent/blob/main/cli-config.yaml.example) 中的注释示例。\n\n## 终端后端配置\n\nHermes 支持六种终端后端。每种后端决定 agent 的 shell 命令实际在哪里执行 —— 本地机器、Docker 容器、通过 SSH 的远程服务器、Modal 云沙箱(直接或通过 Nous 托管的 gateway)、Daytona 工作区,或 Singularity/Apptainer 容器。\n\n```yaml\nterminal:\n backend: local # local | docker | ssh | modal | daytona | singularity\n cwd: \".\" # Gateway/cron 工作目录(CLI 始终使用启动目录)\n timeout: 180 # 每条命令的超时时间(秒)\n env_passthrough: [] # 转发到沙箱执行的环境变量名(terminal + execute_code)\n singularity_image: \"docker://nikolaik/python-nodejs:python3.11-nodejs20\" # Singularity 后端的容器镜像\n modal_image: \"nikolaik/python-nodejs:python3.11-nodejs20\" # Modal 后端的容器镜像\n daytona_image: \"nikolaik/python-nodejs:python3.11-nodejs20\" # Daytona 后端的容器镜像\n```\n\n对于 Modal 和 Daytona 等云沙箱,`container_persistent: true` 表示 Hermes 将尝试在沙箱重建后保留文件系统状态。这并不保证相同的活跃沙箱、PID 空间或后台进程之后仍在运行。\n\n### 后端概览\n\n| 后端 | 命令运行位置 | 隔离性 | 最适合 |\n|---------|-------------------|-----------|----------|\n| **local** | 直接在您的机器上 | 无 | 开发、个人使用 |\n| **docker** | 单个持久 Docker 容器(跨会话、`/new`、子 agent 共享) | 完全(命名空间、cap-drop) | 安全沙箱、CI/CD |\n| **ssh** | 通过 SSH 的远程服务器 | 网络边界 | 远程开发、强大硬件 |\n| **modal** | Modal 云沙箱 | 完全(云 VM) | 临时云计算、评估 |\n| **daytona** | Daytona 工作区 | 完全(云容器) | 托管云开发环境 |\n| **singularity** | Singularity/Apptainer 容器 | 命名空间(--containall) | HPC 集群、共享机器 |\n\n### Local 后端\n\n默认后端。命令直接在您的机器上运行,无隔离。无需特殊设置。\n\n```yaml\nterminal:\n backend: local\n```\n\n:::warning\nAgent 拥有与您的用户账户相同的文件系统访问权限。使用 `hermes tools` 禁用不需要的工具,或切换到 Docker 进行沙箱隔离。\n:::\n\n### Docker 后端\n\n在具有安全加固的 Docker 容器内运行命令(所有权限已删除、无权限提升、PID 限制)。\n\n**单个持久容器,而非每条命令一个容器。** Hermes 在首次使用时启动一个长期运行的容器,并通过 `docker exec` 将每个终端、文件和 `execute_code` 调用路由到同一容器中 —— 跨会话、`/new`、`/reset` 和 `delegate_task` 子 agent,贯穿 Hermes 进程的整个生命周期。工作目录更改、已安装的包以及 `/workspace` 中的文件会从一次工具调用延续到下一次,就像本地 shell 一样。容器在关闭时停止并删除。详情请参阅下方的**容器生命周期**。\n\n```yaml\nterminal:\n backend: docker\n docker_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n docker_mount_cwd_to_workspace: false # 将启动目录挂载到 /workspace\n docker_run_as_host_user: false # 参见下方\"以宿主用户身份运行容器\"\n docker_forward_env: # 转发到容器的环境变量\n - \"GITHUB_TOKEN\"\n docker_volumes: # 宿主目录挂载\n - \"/home/user/projects:/workspace/projects\"\n - \"/home/user/data:/data:ro\" # :ro 表示只读\n docker_extra_args: # 附加到 `docker run` 的额外标志\n - \"--gpus=all\"\n - \"--network=host\"\n\n # 资源限制\n container_cpu: 1 # CPU 核心数(0 = 不限制)\n container_memory: 5120 # MB(0 = 不限制)\n container_disk: 51200 # MB(需要 XFS+pquota 上的 overlay2)\n container_persistent: true # 跨会话持久化 /workspace 和 /root\n```\n\n**`terminal.docker_extra_args`**(也可通过 `TERMINAL_DOCKER_EXTRA_ARGS='[\"--gpus=all\"]'` 覆盖)允许传递 Hermes 未作为一级键公开的任意 `docker run` 标志 —— `--gpus`、`--network`、`--add-host`、替代 `--security-opt` 覆盖等。每个条目必须是字符串;该列表最后附加到组装好的 `docker run` 调用中,因此可以在需要时覆盖 Hermes 的默认值。请谨慎使用 —— 与沙箱加固(权限删除、`--user`、workspace 绑定挂载)冲突的标志将悄然削弱隔离性。\n\n**要求:** 已安装并运行 Docker Desktop 或 Docker Engine。Hermes 会探测 `$PATH` 以及常见的 macOS 安装位置(`/usr/local/bin/docker`、`/opt/homebrew/bin/docker`、Docker Desktop 应用包)。开箱即用支持 Podman:设置 `HERMES_DOCKER_BINARY=podman`(或完整路径)以在两者都安装时强制使用它。\n\n**容器生命周期:** Hermes 为每个终端和文件工具调用重用单个长期运行的容器(`docker run -d ... sleep 2h`),跨会话、`/new`、`/reset` 和 `delegate_task` 子 agent,贯穿 Hermes 进程的整个生命周期。命令通过带登录 shell 的 `docker exec` 运行,因此工作目录更改、已安装的包以及 `/workspace` 中的文件都会从一次工具调用延续到下一次。容器在 Hermes 关闭时(或空闲清理回收时)停止并删除。\n\n通过 `delegate_task(tasks=[...])` 生成的并行子 agent 共享这一个容器 —— 并发的 `cd`、环境变量修改以及对同一路径的写入会发生冲突。如果子 agent 需要隔离的沙箱,必须通过 `register_task_env_overrides()` 注册每任务镜像覆盖,RL 和基准测试环境(TerminalBench2、HermesSweEnv 等)会自动为其每任务 Docker 镜像执行此操作。\n\n**安全加固:**\n- `--cap-drop ALL`,仅添加回 `DAC_OVERRIDE`、`CHOWN`、`FOWNER`\n- `--security-opt no-new-privileges`\n- `--pids-limit 256`\n- `/tmp`(512MB)、`/var/tmp`(256MB)、`/run`(64MB)的大小限制 tmpfs\n\n**凭据转发:** `docker_forward_env` 中列出的环境变量首先从您的 shell 环境解析,然后回退到 `~/.hermes/.env`。技能也可以声明 `required_environment_variables`,这些变量会自动合并。\n\n### SSH 后端\n\n通过 SSH 在远程服务器上运行命令。使用 ControlMaster 进行连接复用(5 分钟空闲保活)。默认启用持久 shell —— 状态(cwd、环境变量)在命令之间保持。\n\n```yaml\nterminal:\n backend: ssh\n persistent_shell: true # 保持长期运行的 bash 会话(默认:true)\n```\n\n**必需的环境变量:**\n\n```bash\nTERMINAL_SSH_HOST=my-server.example.com\nTERMINAL_SSH_USER=ubuntu\n```\n\n**可选:**\n\n| 变量 | 默认值 | 描述 |\n|----------|---------|-------------|\n| `TERMINAL_SSH_PORT` | `22` | SSH 端口 |\n| `TERMINAL_SSH_KEY` | (系统默认) | SSH 私钥路径 |\n| `TERMINAL_SSH_PERSISTENT` | `true` | 启用持久 shell |\n\n**工作原理:** 使用 `BatchMode=yes` 和 `StrictHostKeyChecking=accept-new` 在初始化时连接。持久 shell 在远程主机上保持单个 `bash -l` 进程存活,通过临时文件进行通信。需要 `stdin_data` 或 `sudo` 的命令会自动回退到单次模式。\n\n### Modal 后端\n\n在 [Modal](https://modal.com) 云沙箱中运行命令。每个任务获得一个具有可配置 CPU、内存和磁盘的隔离 VM。文件系统可以跨会话快照/恢复。\n\n```yaml\nterminal:\n backend: modal\n container_cpu: 1 # CPU 核心数\n container_memory: 5120 # MB(5GB)\n container_disk: 51200 # MB(50GB)\n container_persistent: true # 快照/恢复文件系统\n```\n\n**必需:** `MODAL_TOKEN_ID` + `MODAL_TOKEN_SECRET` 环境变量,或 `~/.modal.toml` 配置文件。\n\n**持久化:** 启用后,沙箱文件系统在清理时快照,并在下次会话时恢复。快照在 `~/.hermes/modal_snapshots.json` 中跟踪。这保留文件系统状态,而非活跃进程、PID 空间或后台任务。\n\n**凭据文件:** 自动从 `~/.hermes/` 挂载(OAuth token 等),并在每条命令前同步。\n\n### Daytona 后端\n\n在 [Daytona](https://daytona.io) 托管工作区中运行命令。支持停止/恢复以实现持久化。\n\n```yaml\nterminal:\n backend: daytona\n container_cpu: 1 # CPU 核心数\n container_memory: 5120 # MB → 转换为 GiB\n container_disk: 10240 # MB → 转换为 GiB(最大 10 GiB)\n container_persistent: true # 停止/恢复而非删除\n```\n\n**必需:** `DAYTONA_API_KEY` 环境变量。\n\n**持久化:** 启用后,沙箱在清理时停止(而非删除),并在下次会话时恢复。沙箱名称遵循 `hermes-{task_id}` 模式。\n\n**磁盘限制:** Daytona 强制执行 10 GiB 最大值。超过此值的请求将被截断并发出警告。\n\n### Singularity/Apptainer 后端\n\n在 [Singularity/Apptainer](https://apptainer.org) 容器中运行命令。专为 Docker 不可用的 HPC 集群和共享机器设计。\n\n```yaml\nterminal:\n backend: singularity\n singularity_image: \"docker://nikolaik/python-nodejs:python3.11-nodejs20\"\n container_cpu: 1 # CPU 核心数\n container_memory: 5120 # MB\n container_persistent: true # 可写覆盖层跨会话持久化\n```\n\n**要求:** `$PATH` 中有 `apptainer` 或 `singularity` 二进制文件。\n\n**镜像处理:** Docker URL(`docker://...`)自动转换为 SIF 文件并缓存。现有 `.sif` 文件直接使用。\n\n**临时目录:** 按顺序解析:`TERMINAL_SCRATCH_DIR` → `TERMINAL_SANDBOX_DIR/singularity` → `/scratch/$USER/hermes-agent`(HPC 惯例)→ `~/.hermes/sandboxes/singularity`。\n\n**隔离:** 使用 `--containall --no-home` 实现完全命名空间隔离,不挂载宿主 home 目录。\n\n### 常见终端后端问题\n\n如果终端命令立即失败或终端工具报告为已禁用:\n\n- **Local** —— 无特殊要求。入门时最安全的默认选项。\n- **Docker** —— 运行 `docker version` 验证 Docker 是否正常工作。如果失败,修复 Docker 或执行 `hermes config set terminal.backend local`。\n- **SSH** —— `TERMINAL_SSH_HOST` 和 `TERMINAL_SSH_USER` 都必须设置。如果缺少任一项,Hermes 会记录清晰的错误。\n- **Modal** —— 需要 `MODAL_TOKEN_ID` 环境变量或 `~/.modal.toml`。运行 `hermes doctor` 检查。\n- **Daytona** —— 需要 `DAYTONA_API_KEY`。Daytona SDK 处理服务器 URL 配置。\n- **Singularity** —— 需要 `$PATH` 中有 `apptainer` 或 `singularity`。HPC 集群上常见。\n\n如有疑问,将 `terminal.backend` 设回 `local` 并首先验证命令在那里运行。\n\n### 拆卸时远程到宿主文件同步\n\n对于 **SSH**、**Modal** 和 **Daytona** 后端(agent 的工作树位于与运行 Hermes 的宿主不同的机器上),Hermes 跟踪 agent 在远程沙箱中触及的文件,并在会话拆卸/沙箱清理时,将修改的文件**同步回宿主**,存放在 `~/.hermes/cache/remote-syncs/<session-id>/` 下。\n\n- 触发时机:会话关闭、`/new`、`/reset`、gateway 消息超时、子 agent 使用远程后端时 `delegate_task` 子 agent 完成。\n- 覆盖 agent 修改的整个树,而不仅仅是它明确打开的文件。添加、编辑和删除都会被捕获。\n- 远程沙箱可能在您查找时已被拆除;本地 `~/.hermes/cache/remote-syncs/…` 副本是 agent 更改内容的权威记录。\n- 大型二进制输出(模型检查点、原始数据集)按大小限制 —— 同步跳过超过 `file_sync_max_mb`(默认 `100`)的文件。如果您期望更大的工件返回,请调高该值。\n\n```yaml\nterminal:\n file_sync_max_mb: 100 # 默认 —— 同步最大 100 MB 的文件\n file_sync_enabled: true # 默认 —— 设为 false 可完全跳过同步\n```\n\n这是从会话结束后被销毁的临时云沙箱中恢复结果的方式,无需告诉 agent 显式地 `scp` 或 `modal volume put` 每个工件。\n\n### Docker 卷挂载\n\n使用 Docker 后端时,`docker_volumes` 允许您与容器共享宿主目录。每个条目使用标准 Docker `-v` 语法:`host_path:container_path[:options]`。\n\n```yaml\nterminal:\n backend: docker\n docker_volumes:\n - \"/home/user/projects:/workspace/projects\" # 读写(默认)\n - \"/home/user/datasets:/data:ro\" # 只读\n - \"/home/user/.hermes/cache/documents:/output\" # Gateway 可见的导出\n```\n\n适用于:\n- **向 agent 提供文件**(数据集、配置、参考代码)\n- **从 agent 接收文件**(生成的代码、报告、导出)\n- **共享工作区**,您和 agent 都访问相同的文件\n\n如果您使用消息 gateway 并希望 agent 通过 `MEDIA:/...` 发送生成的文件,建议使用专用的宿主可见导出挂载,例如 `/home/user/.hermes/cache/documents:/output`。\n\n- 在 Docker 中将文件写入 `/output/...`\n- 在 `MEDIA:` 中发出**宿主路径**,例如:`MEDIA:/home/user/.hermes/cache/documents/report.txt`\n- **不要**发出 `/workspace/...` 或 `/output/...`,除非该确切路径在宿主上对 gateway 进程也存在\n\n:::warning\nYAML 重复键会静默覆盖之前的键。如果您已有 `docker_volumes:` 块,请将新挂载合并到同一列表中,而不是在文件后面再添加一个 `docker_volumes:` 键。\n:::\n\n也可以通过环境变量设置:`TERMINAL_DOCKER_VOLUMES='[\"/host:/container\"]'`(JSON 数组)。\n\n### Docker 凭据转发\n\n默认情况下,Docker 终端会话不继承任意宿主凭据。如果您需要在容器内使用特定 token,请将其添加到 `terminal.docker_forward_env`。\n\n```yaml\nterminal:\n backend: docker\n docker_forward_env:\n - \"GITHUB_TOKEN\"\n - \"NPM_TOKEN\"\n```\n\nHermes 首先从您当前的 shell 解析每个列出的变量,然后回退到通过 `hermes config set` 保存的 `~/.hermes/.env`。\n\n:::warning\n`docker_forward_env` 中列出的任何内容都会对容器内运行的命令可见。只转发您愿意暴露给终端会话的凭据。\n:::\n\n### 以宿主用户身份运行容器\n\n默认情况下,Docker 容器以 `root`(UID 0)身份运行。在 `/workspace` 或其他绑定挂载中创建的文件在宿主上归 root 所有,因此会话结束后您必须 `sudo chown` 才能从宿主编辑器编辑它们。`terminal.docker_run_as_host_user` 标志解决了这个问题:\n\n```yaml\nterminal:\n backend: docker\n docker_run_as_host_user: true # 默认:false\n```\n\n启用后,Hermes 将 `--user $(id -u):$(id -g)` 附加到 `docker run` 命令,使写入绑定挂载目录(`/workspace`、`/root`、`docker_volumes` 中的任何内容)的文件归您的宿主用户所有,而非 root。权衡:容器将无法再 `apt install` 或写入 `/root/.npm` 等 root 拥有的路径 —— 如果您同时需要这两者,请使用 `HOME` 归非 root 用户所有的基础镜像(或在镜像构建时添加所需工具)。\n\n保持 `false`(默认)以获得向后兼容的行为。当您的工作流主要是\"编辑挂载的宿主文件\"且厌倦了 `sudo chown -R` 时,请开启此选项。\n\n### 可选:将启动目录挂载到 `/workspace`\n\nDocker 沙箱默认保持隔离。Hermes **不会**将您当前的宿主工作目录传入容器,除非您明确选择加入。\n\n在 `config.yaml` 中启用:\n\n```yaml\nterminal:\n backend: docker\n docker_mount_cwd_to_workspace: true\n```\n\n启用后:\n- 如果您从 `~/projects/my-app` 启动 Hermes,该宿主目录将绑定挂载到 `/workspace`\n- Docker 后端从 `/workspace` 开始\n- 文件工具和终端命令都能看到相同的挂载项目\n\n禁用时,`/workspace` 保持沙箱所有,除非您通过 `docker_volumes` 显式挂载内容。\n\n安全权衡:\n- `false` 保留沙箱边界\n- `true` 使沙箱直接访问您启动 Hermes 的目录\n\n仅在您有意希望容器处理实时宿主文件时才选择加入。\n\n### 持久 Shell\n\n默认情况下,每条终端命令在其自己的子进程中运行 —— 工作目录、环境变量和 shell 变量在命令之间重置。启用**持久 shell** 后,单个长期运行的 bash 进程在 `execute()` 调用之间保持存活,使状态在命令之间保持。\n\n这对 **SSH 后端**最有用,它还消除了每条命令的连接开销。持久 shell **对 SSH 默认启用**,对本地后端禁用。\n\n```yaml\nterminal:\n persistent_shell: true # 默认 —— 为 SSH 启用持久 shell\n```\n\n禁用:\n\n```bash\nhermes config set terminal.persistent_shell false\n```\n\n**跨命令保持的内容:**\n- 工作目录(`cd /tmp` 对下一条命令生效)\n- 导出的环境变量(`export FOO=bar`)\n- Shell 变量(`MY_VAR=hello`)\n\n**优先级:**\n\n| 级别 | 变量 | 默认值 |\n|-------|----------|---------|\n| 配置 | `terminal.persistent_shell` | `true` |\n| SSH 覆盖 | `TERMINAL_SSH_PERSISTENT` | 遵循配置 |\n| Local 覆盖 | `TERMINAL_LOCAL_PERSISTENT` | `false` |\n\n每个后端的环境变量具有最高优先级。如果您也想在本地后端使用持久 shell:\n\n```bash\nexport TERMINAL_LOCAL_PERSISTENT=true\n```\n\n:::note\n需要 `stdin_data` 或 sudo 的命令会自动回退到单次模式,因为持久 shell 的 stdin 已被 IPC 协议占用。\n:::\n\n有关每个后端的详细信息,请参阅[代码执行](features/code-execution.md)和 [README 的终端部分](features/tools.md)。\n\n## 技能设置\n\n技能可以通过其 SKILL.md frontmatter 声明自己的配置设置。这些是非机密值(路径、偏好、域设置),存储在 `config.yaml` 的 `skills.config` 命名空间下。\n\n```yaml\nskills:\n config:\n myplugin:\n path: ~/myplugin-data # 示例 —— 每个技能定义自己的键\n```\n\n**技能设置的工作原理:**\n\n- `hermes config migrate` 扫描所有已启用的技能,找到未配置的设置,并提供提示\n- `hermes config show` 在\"技能设置\"下显示所有技能设置及其所属技能\n- 技能加载时,其解析的配置值会自动注入到技能上下文中\n\n**手动设置值:**\n\n```bash\nhermes config set skills.config.myplugin.path ~/myplugin-data\n```\n\n有关在您自己的技能中声明配置设置的详细信息,请参阅[创建技能 — 配置设置](/developer-guide/creating-skills#config-settings-configyaml)。\n\n### Agent 创建技能写入的守卫\n\n当 agent 使用 `skill_manage` 创建、编辑、修补或删除技能时,Hermes 可以选择扫描新/更新的内容以查找危险关键字模式(凭据收集、明显的 prompt 注入、数据外泄指令)。扫描器**默认关闭** —— 合法触及 `~/.ssh/` 或提及 `$OPENAI_API_KEY` 的真实 agent 工作流触发启发式规则过于频繁。如果您希望扫描器在 agent 的技能写入落地前提示您,请重新开启:\n\n```yaml\nskills:\n guard_agent_created: true # 默认:false\n```\n\n开启后,任何被标记的 `skill_manage` 写入都会以审批提示的形式出现,并附带扫描器的理由。接受的写入落地;拒绝的写入向 agent 返回解释性错误。\n\n## 内存配置\n\n```yaml\nmemory:\n memory_enabled: true\n user_profile_enabled: true\n memory_char_limit: 2200 # ~800 tokens\n user_char_limit: 1375 # ~500 tokens\n```\n\n## 文件读取安全\n\n控制单次 `read_file` 调用可以返回多少内容。超过限制的读取将被拒绝,并向 agent 返回错误,提示使用 `offset` 和 `limit` 读取较小范围。这可以防止单次读取压缩的 JS 包或大型数据文件时淹没上下文窗口。\n\n```yaml\nfile_read_max_chars: 100000 # 默认 —— ~25-35K tokens\n```\n\n如果您使用具有大上下文窗口的模型并经常读取大文件,请调高此值。对于小上下文模型,请降低以保持读取高效:\n\n```yaml\n# 大上下文模型(200K+)\nfile_read_max_chars: 200000\n\n# 小型本地模型(16K 上下文)\nfile_read_max_chars: 30000\n```\n\nAgent 还会自动去重文件读取 —— 如果同一文件区域被读取两次且文件未更改,则返回轻量级存根而不是重新发送内容。这在上下文压缩后重置,以便 agent 在内容被摘要后可以重新读取文件。\n\n## 工具输出截断限制\n\n三个相关的上限控制工具在 Hermes 截断之前可以返回多少原始输出:\n\n```yaml\ntool_output:\n max_bytes: 50000 # 终端输出上限(字符)\n max_lines: 2000 # read_file 分页上限\n max_line_length: 2000 # read_file 行号视图中的每行上限\n```\n\n- **`max_bytes`** —— 当 `terminal` 命令产生超过此字符数的合并 stdout/stderr 时,Hermes 保留前 40% 和后 60%,并在中间插入 `[OUTPUT TRUNCATED]` 通知。默认 `50000`(典型分词器约 12-15K tokens)。\n- **`max_lines`** —— 单次 `read_file` 调用的 `limit` 参数上限。超过此值的请求将被截断,以防单次读取淹没上下文窗口。默认 `2000`。\n- **`max_line_length`** —— `read_file` 发出行号视图时应用的每行上限。超过此长度的行将被截断为此字符数,后跟 `... [truncated]`。默认 `2000`。\n\n对于具有大上下文窗口且每次调用可以承受更多原始输出的模型,请调高限制。对于小上下文模型,请降低以保持工具结果紧凑:\n\n```yaml\n# 大上下文模型(200K+)\ntool_output:\n max_bytes: 150000\n max_lines: 5000\n\n# 小型本地模型(16K 上下文)\ntool_output:\n max_bytes: 20000\n max_lines: 500\n```\n\n## 全局工具集禁用\n\n要在 CLI 和每个 gateway 平台上统一禁用特定工具集,请在 `agent.disabled_toolsets` 下列出其名称:\n\n```yaml\nagent:\n disabled_toolsets:\n - memory # 隐藏内存工具 + MEMORY_GUIDANCE 注入\n - web # 任何地方都不使用 web_search / web_extract\n```\n\n这在每个平台的工具配置(由 `hermes tools` 写入的 `platform_toolsets`)**之后**应用,因此此处列出的工具集始终被删除 —— 即使平台的已保存配置仍然列出它。当您希望有一个\"到处关闭 X\"的单一开关而不是编辑 `hermes tools` UI 中的 15+ 个平台行时,请使用此选项。\n\n留空列表或省略键不会产生任何效果。\n\n## Git Worktree 隔离\n\n启用隔离的 git worktree,以便在同一仓库上并行运行多个 agent:\n\n```yaml\nworktree: true # 始终创建 worktree(与 hermes -w 相同)\n# worktree: false # 默认 —— 仅在传递 -w 标志时\n```\n\n启用后,每个 CLI 会话在 `.worktrees/` 下创建一个带有自己分支的新 worktree。Agent 可以编辑文件、提交、推送和创建 PR,而不会相互干扰。干净的 worktree 在退出时删除;脏的 worktree 保留以供手动恢复。\n\n您还可以通过仓库根目录中的 `.worktreeinclude` 列出要复制到 worktree 的 gitignore 文件:\n\n```\n# .worktreeinclude\n.env\n.venv/\nnode_modules/\n```\n\n## 上下文压缩\n\nHermes 自动压缩长对话以保持在模型的上下文窗口内。压缩摘要器是一个单独的 LLM 调用 —— 您可以将其指向任何 provider 或端点。\n\n所有压缩设置都在 `config.yaml` 中(无环境变量)。\n\n### 完整参考\n\n```yaml\ncompression:\n enabled: true # 开启/关闭压缩\n threshold: 0.50 # 在上下文限制的此百分比时压缩\n target_ratio: 0.20 # 保留为最近尾部的阈值分数\n protect_last_n: 20 # 保持未压缩的最少最近消息数\n hygiene_hard_message_limit: 5000 # Gateway 安全阀 —— 见下文\n context_timeout_seconds: 120 # Agent 侧 compress_context 无进展超时(秒)—— 见下文\n context_total_ceiling_seconds: 600 # Agent 侧 compress_context 总等待上限(秒)\n\n# 摘要模型/provider 在 auxiliary: 下配置:\nauxiliary:\n compression:\n model: \"\" # 空 = 使用主聊天模型。覆盖为例如 \"google/gemini-3-flash-preview\" 以获得更便宜/更快的压缩。\n provider: \"auto\" # Provider:\"auto\"、\"openrouter\"、\"nous\"、\"codex\"、\"main\" 等\n base_url: null # 自定义 OpenAI 兼容端点(覆盖 provider)\n```\n\n:::info 旧版配置迁移\n带有 `compression.summary_model`、`compression.summary_provider` 和 `compression.summary_base_url` 的旧版配置在首次加载时自动迁移到 `auxiliary.compression.*`(配置版本 17)。无需手动操作。\n:::\n\n`hygiene_hard_message_limit` 是仅限 gateway 的**预压缩安全阀**。它的存在是为了打破一个死循环:当超大会话的 API 调用持续断开时,gateway 永远收不到 token 使用数据,基于 token 的阈值因此无法触发,于是 transcript 持续增长、断开愈发严重。这个基于消息数的下限仅凭消息数量触发(无论 API 是否失败,消息数始终已知),强制压缩以恢复会话。默认 `5000` —— 远高于任何正常会话,包括做数千次短轮次的大上下文(1M+)模型,它们早就在 token 阈值处压缩了。对于异常平台可调得更高;要强制更积极的压缩则调低。在运行中的 gateway 上编辑此值将在下一条消息时生效(见下文)。\n\n`context_timeout_seconds`(默认 `120`)是 agent 侧 `compress_context`(对话循环、预检压缩、手动 `/compress`)的**无进展超时**,语义与 gateway 会话预压缩(session hygiene)的 inactivity 预算相同:摘要模型仍在流式出 token 时会延长等待;仅当完全无输出时才跳过压缩并保留原消息。设为 `0` 可关闭。Gateway 会话预压缩仍使用自己的 `hygiene_timeout_seconds`,不会被双重包装。\n\n`context_total_ceiling_seconds`(默认 `600`)限制即使仍有 token 推进时的 agent 侧总等待时间,并会被钳制为至少等于 `context_timeout_seconds`。\n\n:::tip Gateway 热重载压缩和上下文长度\n从最近的版本开始,在运行中的 gateway 上编辑 `config.yaml` 中的 `model.context_length` 或任何 `compression.*` 键将在下一条消息时生效 —— 无需 gateway 重启、`/reset` 或会话轮换。缓存的 agent 签名包含这些键,因此 gateway 在检测到更改时会透明地重建 agent。API 密钥和工具/技能配置仍需要通常的重载路径。\n:::\n\n### 常见设置\n\n**默认(自动检测)—— 无需配置:**\n```yaml\ncompression:\n enabled: true\n threshold: 0.50\n```\n使用您的主 provider 和主模型。如果您希望在比主聊天模型更便宜的模型上进行压缩,请覆盖每任务(例如 `auxiliary.compression.provider: openrouter` + `model: google/gemini-2.5-flash`)。\n\n**强制特定 provider**(基于 OAuth 或 API 密钥):\n```yaml\nauxiliary:\n compression:\n provider: nous\n model: gemini-3-flash\n```\n适用于任何 provider:`nous`、`openrouter`、`codex`、`anthropic`、`main` 等。\n\n**自定义端点**(自托管、Ollama、zai、DeepSeek 等):\n```yaml\nauxiliary:\n compression:\n model: glm-4.7\n base_url: https://api.z.ai/api/coding/paas/v4\n```\n指向自定义 OpenAI 兼容端点。使用 `OPENAI_API_KEY` 进行认证。\n\n### 三个旋钮的交互方式\n\n| `auxiliary.compression.provider` | `auxiliary.compression.base_url` | 结果 |\n|---------------------|---------------------|--------|\n| `auto`(默认) | 未设置 | 自动检测最佳可用 provider |\n| `nous` / `openrouter` / 等 | 未设置 | 强制使用该 provider,使用其认证 |\n| 任意 | 已设置 | 直接使用自定义端点(忽略 provider) |\n\n:::warning 摘要模型上下文长度要求\n摘要模型**必须**具有至少与您的主 agent 模型一样大的上下文窗口。压缩器将对话的完整中间部分发送给摘要模型 —— 如果该模型的上下文窗口小于主模型的,摘要调用将因上下文长度错误而失败。发生这种情况时,中间轮次将**在没有摘要的情况下被丢弃**,静默丢失对话上下文。如果您覆盖模型,请验证其上下文长度满足或超过您的主模型。\n:::\n\n## 上下文引擎\n\n上下文引擎控制在接近模型 token 限制时如何管理对话。内置的 `compressor` 引擎使用有损摘要(参见[上下文压缩](/developer-guide/context-compression-and-caching))。插件引擎可以用替代策略替换它。\n\n```yaml\ncontext:\n engine: \"compressor\" # 默认 —— 内置有损摘要\n```\n\n使用插件引擎(例如,用于无损上下文管理的 LCM):\n\n```yaml\ncontext:\n engine: \"lcm\" # 必须与插件名称匹配\n```\n\n插件引擎**永远不会自动激活** —— 您必须将 `context.engine` 显式设置为插件名称。可用引擎可以通过 `hermes plugins` → Provider Plugins → Context Engine 浏览和选择。\n\n有关内存插件的类似单选系统,请参阅[内存 Providers](/user-guide/features/memory-providers)。\n\n## 迭代预算\n\n当 agent 在处理具有许多工具调用的复杂任务时,它可能会耗尽其迭代预算(默认:500 轮)。Hermes **不会**在任务中途注入压力警告 —— 早期版本会在预算达到 70%/90% 时警告模型,这会导致模型过早放弃复杂任务,该机制已于 2026 年 4 月移除。\n\n取而代之的是,当预算真正耗尽(500/500)时,Hermes 注入一条消息要求模型收尾,并允许一次**宽限调用**以便其给出最终响应。如果该宽限调用仍未产生文本,则会要求 agent 总结已完成的工作。\n\n```yaml\nagent:\n max_turns: 500 # 每次对话轮次的最大迭代次数(默认:500)\n api_max_retries: 3 # 回退启动前每个 provider 的重试次数(默认:3)\n```\n\n当迭代预算完全耗尽时,CLI 向用户显示通知:`⚠ Iteration budget reached (500/500) — response may be incomplete`。\n\n`agent.api_max_retries` 控制 Hermes 在回退 provider 切换启动**之前**对瞬时错误(速率限制、连接断开、5xx)重试 provider API 调用的次数。默认为 `3` —— 总共四次尝试。如果您配置了[回退 providers](/user-guide/features/fallback-providers) 并希望更快地故障转移,请将其降至 `0`,这样主 provider 上的第一个瞬时错误会立即切换到回退,而不是对不稳定的端点进行重试。\n\n### API 超时\n\nHermes 对流式传输有单独的超时层,以及用于非流式调用的陈旧检测器。陈旧检测器仅在您将其保留为隐式默认值时才会自动调整本地 provider。\n\n| 超时 | 默认值 | 本地 providers | 配置/环境变量 |\n|---------|---------|----------------|--------------|\n| Socket 读取超时 | 120s | 自动提升至 1800s | `HERMES_STREAM_READ_TIMEOUT` |\n| 陈旧流检测 | 180s | 自动禁用 | `HERMES_STREAM_STALE_TIMEOUT` |\n| 陈旧非流检测 | 300s | 保持隐式时自动禁用 | `providers.<id>.stale_timeout_seconds` 或 `HERMES_API_CALL_STALE_TIMEOUT` |\n| API 调用(非流式) | 1800s | 不变 | `providers.<id>.request_timeout_seconds` / `timeout_seconds` 或 `HERMES_API_TIMEOUT` |\n\n**Socket 读取超时**控制 httpx 等待 provider 下一个数据块的时间。本地 LLM 在大上下文上预填充可能需要几分钟才能产生第一个 token,因此当 Hermes 检测到本地端点时,会将此值提升至 30 分钟。如果您显式设置 `HERMES_STREAM_READ_TIMEOUT`,无论端点检测如何,始终使用该值。\n\n**陈旧流检测**终止接收 SSE 保活 ping 但没有实际内容的连接。对于本地 providers,这完全禁用,因为它们在预填充期间不发送保活 ping。\n\n**陈旧非流检测**终止长时间没有响应的非流式调用。默认情况下,Hermes 在本地端点上禁用此功能,以避免长时间预填充期间的误报。如果您显式设置 `providers.<id>.stale_timeout_seconds`、`providers.<id>.models.<model>.stale_timeout_seconds` 或 `HERMES_API_CALL_STALE_TIMEOUT`,即使在本地端点上也会遵守该显式值。\n\n## 上下文压力警告\n\n与迭代预算压力分开,上下文压力跟踪对话距**压缩阈值**有多近 —— 即上下文压缩触发以摘要旧消息的点。这有助于您和 agent 了解对话何时变长。\n\n| 进度 | 级别 | 发生的事情 |\n|----------|-------|-------------|\n| **≥ 60%** 到阈值 | 信息 | CLI 显示青色进度条;gateway 发送信息通知 |\n| **≥ 85%** 到阈值 | 警告 | CLI 显示粗体黄色进度条;gateway 警告压缩即将发生 |\n\n在 CLI 中,上下文压力在工具输出流中显示为进度条:\n\n```\n ◐ context ████████████░░░░░░░░ 62% to compaction 48k threshold (50%) · approaching compaction\n```\n\n在消息平台上,发送纯文本通知:\n\n```\n◐ Context: ████████████░░░░░░░░ 62% to compaction (threshold: 50% of window).\n```\n\n如果自动压缩被禁用,警告会告诉您上下文可能被截断。\n\n上下文压力是自动的 —— 无需配置。它纯粹作为面向用户的通知触发,不修改消息流或向模型上下文注入任何内容。\n\n## 凭据池策略\n\n当您为同一 provider 拥有多个 API 密钥或 OAuth token 时,配置轮换策略:\n\n```yaml\ncredential_pool_strategies:\n openrouter: round_robin # 均匀循环使用密钥\n anthropic: least_used # 始终选择使用最少的密钥\n```\n\n选项:`fill_first`(默认)、`round_robin`、`least_used`、`random`。完整文档请参阅[凭据池](/user-guide/features/credential-pools)。\n\n## Prompt 缓存\n\n当活跃 provider 支持时,Hermes 自动开启跨会话 prompt 缓存 —— 无需用户配置。\n\n对于**原生 Anthropic**、**OpenRouter** 和 **Nous Portal** 上的 Claude,Hermes 在系统提示词和技能块上附加带有 1 小时 TTL(`ttl: \"1h\"`)的 `cache_control` 断点。在新鲜的一小时内首次发送时按完整输入费率计费;同一小时内任何会话的后续发送以折扣缓存读取费率从缓存中提取。这意味着系统提示词、加载的技能内容以及任何长上下文包含的早期部分在第一个小时内跨 `hermes` 会话和分叉子 agent 被重用。\n\nQwen Cloud(阿里巴巴 DashScope)上游将缓存 TTL 限制为 5 分钟,因此 Hermes 在那里使用 5 分钟断点 TTL。其他通过第三方的 Claude 路径(AWS Bedrock、Azure Foundry)回退到 provider 自己的缓存默认值。xAI Grok 使用单独的会话固定对话 ID 机制 —— 参阅 [xAI prompt 缓存](/integrations/providers#xai-grok--responses-api--prompt-caching)。\n\n不存在禁用此功能的旋钮 —— 缓存始终开启,即使在单轮对话中也能节省费用,因为仅系统提示词就占输入 token 数的相当大比例。\n\n## 辅助模型\n\nHermes 使用\"辅助\"模型处理图像分析、网页摘要、浏览器截图分析、会话标题生成和上下文压缩等附带任务。默认情况下(`auxiliary.*.provider: \"auto\"`),Hermes 将每个辅助任务路由到您的**主聊天模型** —— 与您在 `hermes model` 中选择的相同 provider/模型。您无需配置任何内容即可开始,但请注意,在昂贵的推理模型(Opus、MiniMax M2.7 等)上,辅助任务会增加显著成本。如果您希望无论主模型如何都使用便宜且快速的附带任务,请显式设置 `auxiliary.<task>.provider` 和 `auxiliary.<task>.model`(例如,在 OpenRouter 上使用 Gemini Flash 进行视觉和网页提取)。\n\n:::note 为什么 \"auto\" 使用您的主模型\n早期版本将聚合器用户(OpenRouter、Nous Portal)分流到便宜的 provider 端默认值。这令人惊讶 —— 付费购买聚合器订阅的用户会看到不同的模型处理其辅助流量。`auto` 现在对所有人使用主模型,`config.yaml` 中的每任务覆盖仍然优先(见下方[完整辅助配置参考](#full-auxiliary-config-reference))。\n:::\n\n### 交互式配置辅助模型\n\n无需手动编辑 YAML,运行 `hermes model` 并从菜单中选择**\"配置辅助模型\"**。您将获得交互式的每任务选择器:\n\n```\n$ hermes model\n→ Configure auxiliary models\n\n[ ] vision currently: auto / main model\n[ ] web_extract currently: auto / main model\n[ ] title_generation currently: openrouter / google/gemini-3-flash-preview\n[ ] compression currently: auto / main model\n[ ] approval currently: auto / main model\n[ ] triage_specifier currently: auto / main model\n[ ] kanban_decomposer currently: auto / main model\n[ ] profile_describer currently: auto / main model\n```\n\n选择任务,选择 provider(OAuth 流程打开浏览器;API 密钥 provider 提示输入),选择模型。更改持久化到 `config.yaml` 中的 `auxiliary.<task>.*`。与主模型选择器相同的机制 —— 无需学习额外语法。\n\n### 视频教程\n\n<div style={{position: 'relative', width: '100%', aspectRatio: '16 / 9', marginBottom: '1.5rem'}}>\n <iframe\n src=\"https://www.youtube.com/embed/NoF-YajElIM\"\n title=\"Hermes Agent — Auxiliary Models Tutorial\"\n style={{position: 'absolute', top: 0, left: 0, width: '100%', height: '100%', border: 0}}\n allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\"\n allowFullScreen\n />\n</div>\n\n### 通用配置模式\n\nHermes 中的每个模型槽位 —— 辅助任务、压缩、回退 —— 使用相同的三个旋钮:\n\n| 键 | 作用 | 默认值 |\n|-----|-------------|---------|\n| `provider` | 用于认证和路由的 provider | `\"auto\"` |\n| `model` | 请求的模型 | provider 的默认值 |\n| `base_url` | 自定义 OpenAI 兼容端点(覆盖 provider) | 未设置 |\n\n当设置 `base_url` 时,Hermes 忽略 provider 并直接调用该端点(使用 `api_key` 或 `OPENAI_API_KEY` 进行认证)。当仅设置 `provider` 时,Hermes 使用该 provider 的内置认证和基础 URL。\n\n辅助任务的可用 providers:`auto`、`main`,以及[provider 注册表](/reference/environment-variables)中的任何 provider —— `openrouter`、`nous`、`openai-codex`、`copilot`、`copilot-acp`、`anthropic`、`gemini`、`qwen-oauth`、`zai`、`kimi-coding`、`kimi-coding-cn`、`minimax`、`minimax-cn`、`minimax-oauth`、`deepseek`、`nvidia`、`xai`、`xai-oauth`、`ollama-cloud`、`alibaba`、`bedrock`、`huggingface`、`arcee`、`xiaomi`、`kilocode`、`opencode-zen`、`opencode-go`、`azure-foundry` —— 或您 `custom_providers` 列表中任何命名的自定义 provider(例如 `provider: \"beans\"`)。\n\n:::tip MiniMax OAuth\n`minimax-oauth` 通过浏览器 OAuth 登录(无需 API 密钥)。运行 `hermes model` 并选择 **MiniMax (OAuth)** 进行认证。辅助任务自动使用 `MiniMax-M2.7-highspeed`。参阅 [MiniMax OAuth 指南](../guides/minimax-oauth.md)。\n:::\n\n:::tip xAI Grok OAuth\n`xai-oauth` 通过浏览器 OAuth 为 SuperGrok 和 X Premium+ 订阅者登录(无需 API 密钥)。运行 `hermes model` 并选择 **xAI Grok OAuth (SuperGrok / Premium+)** 进行认证。相同的 OAuth token 可重用于每个直接到 xAI 的接口(聊天、辅助任务、TTS、图像生成、视频生成、转录)。参阅 [xAI Grok OAuth 指南](../guides/xai-grok-oauth.md),如果 Hermes 在远程主机上,请参阅 [SSH/远程主机上的 OAuth](../guides/oauth-over-ssh.md)。\n:::\n\n:::warning `\"main\"` 仅用于辅助任务\n`\"main\"` provider 选项表示\"使用我的主 agent 使用的任何 provider\" —— 它仅在 `auxiliary:`、`compression:` 和 `fallback_model:` 配置中有效。它**不是**顶级 `model.provider` 设置的有效值。如果您使用自定义 OpenAI 兼容端点,请在 `model:` 部分设置 `provider: custom`。所有主模型 provider 选项请参阅 [AI Providers](/integrations/providers)。\n:::\n\n### 完整辅助配置参考\n\n```yaml\nauxiliary:\n # 图像分析(vision_analyze 工具 + 浏览器截图)\n vision:\n provider: \"auto\" # \"auto\"、\"openrouter\"、\"nous\"、\"codex\"、\"main\" 等\n model: \"\" # 例如 \"openai/gpt-4o\"、\"google/gemini-2.5-flash\"\n base_url: \"\" # 自定义 OpenAI 兼容端点(覆盖 provider)\n api_key: \"\" # base_url 的 API 密钥(回退到 OPENAI_API_KEY)\n timeout: 120 # 秒 —— LLM API 调用超时;视觉负载需要宽裕的超时\n download_timeout: 30 # 秒 —— 图像 HTTP 下载;慢速连接请增加\n\n # 网页摘要 + 浏览器页面文本提取\n web_extract:\n provider: \"auto\"\n model: \"\" # 例如 \"google/gemini-2.5-flash\"\n base_url: \"\"\n api_key: \"\"\n timeout: 360 # 秒(6 分钟)—— 每次尝试的 LLM 摘要\n\n # 危险命令审批分类器\n approval:\n provider: \"auto\"\n model: \"\"\n base_url: \"\"\n api_key: \"\"\n timeout: 30 # 秒\n\n # 上下文压缩超时(与 compression.* 配置分开)\n compression:\n timeout: 120 # 秒 —— 压缩摘要长对话,需要更多时间\n # fallback_chain: # 可选 —— 发生速率限制/连接故障时尝试的 provider\n # - provider: nous\n # model: deepseek/deepseek-chat\n # - provider: openrouter\n # model: google/gemini-2.5-flash\n # base_url: \"\"\n # api_key: \"\"\n\n # 技能中心 —— 技能匹配和搜索\n skills_hub:\n provider: \"auto\"\n model: \"\"\n base_url: \"\"\n api_key: \"\"\n timeout: 30\n\n # MCP 工具调度\n mcp:\n provider: \"auto\"\n model: \"\"\n base_url: \"\"\n api_key: \"\"\n timeout: 30\n\n # Kanban 分类规格说明器 —— `hermes kanban specify <id>`(或\n # 仪表板上 Triage 列卡片的 ✨ Specify 按钮)使用此\n # 槽位将单行描述扩展为具体规格并将\n # 任务提升到 `todo`。便宜快速的模型在这里效果很好;规格扩展\n # 很短,不需要推理深度。\n triage_specifier:\n provider: \"auto\"\n model: \"\"\n base_url: \"\"\n api_key: \"\"\n timeout: 120\n```\n\n:::tip\n每个辅助任务都有可配置的 `timeout`(秒)。默认值:vision 120s、web_extract 360s、approval 30s、compression 120s。如果您为辅助任务使用慢速本地模型,请增加这些值。Vision 还有单独的 `download_timeout`(默认 30s)用于 HTTP 图像下载 —— 对于慢速连接或自托管图像服务器,请增加此值。\n:::\n\n:::info\n上下文压缩有自己的 `compression:` 块用于阈值,以及 `auxiliary.compression:` 块用于模型/provider 设置 —— 参阅上方的[上下文压缩](#context-compression)。主备用链使用顶层的 `fallback_providers:` 列表 —— 参阅[备用提供商](/integrations/providers#fallback-providers)。三者都遵循相同的 provider/model/base_url 模式。\n:::\n\n### 辅助任务的每任务回退链\n\n每个辅助任务都可以选择性地定义一个 `fallback_chain` —— 一个 provider/model 条目列表,当主要辅助 provider 因速率限制、网络连接问题或付费限制而失败时,Hermes 会尝试使用该列表:\n\n```yaml\nauxiliary:\n compression:\n provider: openrouter\n model: openai/gpt-4o-mini\n fallback_chain:\n - provider: nous\n model: deepseek/deepseek-chat\n - provider: openrouter\n model: google/gemini-2.5-flash\n```\n\n当主要辅助 provider(`openrouter` / `openai/gpt-4o-mini`)返回速率限制、连接超时或需要付费错误时,Hermes 将依次遍历 `fallback_chain`。它会跳过 provider 与已失败 provider 相同的条目,并尝试每个剩余条目,直到有一个成功或该链耗尽。如果所有回退都失败,Hermes 会回退到主 agent 模型作为最终的安全网。\n\n每个条目支持与任何辅助任务配置相同的三个旋钮:\n\n| 键 | 描述 |\n|-----|-------------|\n| `provider` | Provider 名称(`nous`、`openrouter`、`anthropic`、`gemini`、`main` 等) |\n| `model` | 该 provider 的模型名称 |\n| `base_url` | (可选)自定义 OpenAI 兼容端点 |\n\n`fallback_chain` 适用于任何辅助任务 —— `compression`、`vision`、`web_extract`、`approval`、`skills_hub`、`mcp` 等。\n\n### OpenRouter 路由和辅助任务的 Pareto Code\n\n当辅助任务解析到 OpenRouter(显式或通过 `provider: \"main\"` 而您的主 agent 在 OpenRouter 上)时,主 agent 的 `provider_routing` 和 `openrouter.min_coding_score` 设置**不会传播** —— 按设计,每个辅助任务是独立的。要为特定辅助任务设置 OpenRouter provider 偏好或使用 [Pareto Code 路由器](/integrations/providers#openrouter-pareto-code-router),请通过 `extra_body` 按任务设置:\n\n```yaml\nauxiliary:\n compression:\n provider: openrouter\n model: openrouter/pareto-code # 为此任务使用 Pareto Code 路由器\n extra_body:\n provider: # OpenRouter provider 路由偏好\n order: [anthropic, google] # 按此顺序尝试这些 providers\n sort: throughput # 或 \"price\" | \"latency\"\n # only: [anthropic] # 限制到特定 provider\n # ignore: [deepinfra] # 排除特定 providers\n plugins: # OpenRouter Pareto Code 路由器旋钮\n - id: pareto-router\n min_coding_score: 0.5 # 0.0–1.0;越高 = 更强的编码能力\n```\n\n形状与 OpenRouter 在聊天补全请求体中接受的内容一致。Hermes 原样转发整个 `extra_body`,因此 [openrouter.ai/docs](https://openrouter.ai/docs) 中记录的任何其他 OpenRouter 请求体字段都以相同方式工作。\n\n### 更改视觉模型\n\n使用 GPT-4o 而非 Gemini Flash 进行图像分析:\n\n```yaml\nauxiliary:\n vision:\n model: \"openai/gpt-4o\"\n```\n\n或通过环境变量(在 `~/.hermes/.env` 中):\n\n```bash\nAUXILIARY_VISION_MODEL=openai/gpt-4o\n```\n\n### Provider 选项\n\n这些选项适用于**辅助任务配置**(`auxiliary:`、`compression:`、`fallback_model:`),而非您的主 `model.provider` 设置。\n\n| Provider | 描述 | 要求 |\n|----------|-------------|-------------|\n| `\"auto\"` | 最佳可用(默认)。Vision 尝试 OpenRouter → Nous → Codex。 | — |\n| `\"openrouter\"` | 强制 OpenRouter —— 路由到任何模型(Gemini、GPT-4o、Claude 等) | `OPENROUTER_API_KEY` |\n| `\"nous\"` | 强制 Nous Portal | `hermes auth` |\n| `\"codex\"` | 强制 Codex OAuth(ChatGPT 账户)。支持视觉(gpt-5.3-codex)。 | `hermes model` → Codex |\n| `\"minimax-oauth\"` | 强制 MiniMax OAuth(浏览器登录,无需 API 密钥)。辅助任务使用 MiniMax-M2.7-highspeed。 | `hermes model` → MiniMax (OAuth) |\n| `\"xai-oauth\"` | 强制 xAI Grok OAuth(SuperGrok 或 X Premium+ 订阅者的浏览器登录,无需 API 密钥)。相同的 OAuth token 涵盖聊天、TTS、图像、视频和转录。 | `hermes model` → xAI Grok OAuth (SuperGrok / Premium+) |\n| `\"main\"` | 使用您的活跃自定义/主端点。可以来自 `OPENAI_BASE_URL` + `OPENAI_API_KEY` 或通过 `hermes model` / `config.yaml` 保存的自定义端点。适用于 OpenAI、本地模型或任何 OpenAI 兼容 API。**仅限辅助任务 —— 对 `model.provider` 无效。** | 自定义端点凭据 + 基础 URL |\n\n当您希望附带任务绕过默认路由器时,主 provider 目录中的直接 API 密钥 providers 也在这里工作。配置 `GMI_API_KEY` 后,`gmi` 有效:\n\n```yaml\nauxiliary:\n compression:\n provider: \"gmi\"\n model: \"anthropic/claude-opus-4.6\"\n```\n\n对于 GMI 辅助路由,使用 GMI 的 `/v1/models` 端点返回的确切模型 ID。\n\n### 常见设置\n\n**使用直接自定义端点**(比 `provider: \"main\"` 对本地/自托管 API 更清晰):\n```yaml\nauxiliary:\n vision:\n base_url: \"http://localhost:1234/v1\"\n api_key: \"local-key\"\n model: \"qwen2.5-vl\"\n```\n\n`base_url` 优先于 `provider`,因此这是将辅助任务路由到特定端点的最明确方式。对于直接端点覆盖,Hermes 使用配置的 `api_key` 或回退到 `OPENAI_API_KEY`;它不会为该自定义端点重用 `OPENROUTER_API_KEY`。\n\n**使用 OpenAI API 密钥进行视觉:**\n```yaml\n# 在 ~/.hermes/.env 中:\n# OPENAI_BASE_URL=https://api.openai.com/v1\n# OPENAI_API_KEY=sk-...\n\nauxiliary:\n vision:\n provider: \"main\"\n model: \"gpt-4o\" # 或 \"gpt-4o-mini\" 更便宜\n```\n\n**使用 OpenRouter 进行视觉**(路由到任何模型):\n```yaml\nauxiliary:\n vision:\n provider: \"openrouter\"\n model: \"openai/gpt-4o\" # 或 \"google/gemini-2.5-flash\" 等\n```\n\n**使用 Codex OAuth**(ChatGPT Pro/Plus 账户 —— 无需 API 密钥):\n```yaml\nauxiliary:\n vision:\n provider: \"codex\" # 使用您的 ChatGPT OAuth token\n # 模型默认为 gpt-5.3-codex(支持视觉)\n```\n\n**使用 MiniMax OAuth**(浏览器登录,无需 API 密钥):\n```yaml\nmodel:\n default: MiniMax-M2.7\n provider: minimax-oauth\n base_url: https://api.minimax.io/anthropic\n```\n运行 `hermes model` 并选择 **MiniMax (OAuth)** 自动登录并设置此项。对于中国区域,基础 URL 将是 `https://api.minimaxi.com/anthropic`。完整演练请参阅 [MiniMax OAuth 指南](../guides/minimax-oauth.md)。\n\n**使用本地/自托管模型:**\n```yaml\nauxiliary:\n vision:\n provider: \"main\" # 使用您的活跃自定义端点\n model: \"my-local-model\"\n```\n\n`provider: \"main\"` 使用 Hermes 用于普通聊天的任何 provider —— 无论是命名的自定义 provider(例如 `beans`)、内置 provider(如 `openrouter`)还是旧版 `OPENAI_BASE_URL` 端点。\n\n:::tip\n如果您使用 Codex OAuth 作为主模型 provider,视觉会自动工作 —— 无需额外配置。Codex 包含在视觉的自动检测链中。\n:::\n\n:::warning\n**视觉需要多模态模型。** 如果您设置 `provider: \"main\"`,请确保您的端点支持多模态/视觉 —— 否则图像分析将失败。\n:::\n\n### 环境变量(旧版)\n\n辅助模型也可以通过环境变量配置。但是,`config.yaml` 是首选方法 —— 它更易于管理,并支持所有选项,包括 `base_url` 和 `api_key`。\n\n| 设置 | 环境变量 |\n|---------|---------------------|\n| Vision provider | `AUXILIARY_VISION_PROVIDER` |\n| Vision 模型 | `AUXILIARY_VISION_MODEL` |\n| Vision 端点 | `AUXILIARY_VISION_BASE_URL` |\n| Vision API 密钥 | `AUXILIARY_VISION_API_KEY` |\n| Web 提取 provider | `AUXILIARY_WEB_EXTRACT_PROVIDER` |\n| Web 提取模型 | `AUXILIARY_WEB_EXTRACT_MODEL` |\n| Web 提取端点 | `AUXILIARY_WEB_EXTRACT_BASE_URL` |\n| Web 提取 API 密钥 | `AUXILIARY_WEB_EXTRACT_API_KEY` |\n\n压缩和回退模型设置仅限 config.yaml。\n\n:::tip\n运行 `hermes config` 查看您当前的辅助模型设置。覆盖仅在与默认值不同时显示。\n:::\n\n## 推理努力程度\n\n控制模型在响应前进行多少\"思考\":\n\n```yaml\nagent:\n reasoning_effort: \"\" # 空 = 中等。选项:none、minimal、low、medium、high、xhigh、max、ultra\n```\n\n未设置时(默认),推理努力程度默认为\"medium\" —— 适合大多数任务的平衡级别。设置值会覆盖它 —— 更高的推理努力程度在复杂任务上提供更好的结果,但代价是更多 token 和延迟。\n\n您也可以在运行时使用 `/reasoning` 命令更改推理努力程度:\n\n```\n/reasoning # 显示当前努力程度和显示状态\n/reasoning high # 将推理努力程度设为 high\n/reasoning none # 禁用推理\n/reasoning show # 在每次响应上方显示模型思考\n/reasoning hide # 隐藏模型思考\n```\n\n## 工具使用强制\n\n某些模型偶尔会将预期操作描述为文本而不是进行工具调用(\"我会运行测试...\"而不是实际调用终端)。工具使用强制会注入系统提示词指导,引导模型实际调用工具。\n\n```yaml\nagent:\n tool_use_enforcement: \"auto\" # \"auto\" | true | false | [\"model-substring\", ...]\n```\n\n| 值 | 行为 |\n|-------|----------|\n| `\"auto\"`(默认) | 对匹配以下模型启用:`gpt`、`codex`、`gemini`、`gemma`、`grok`。对所有其他模型禁用(Claude、DeepSeek、Qwen 等)。 |\n| `true` | 始终启用,无论模型如何。如果您注意到当前模型描述操作而不是执行操作,请使用此选项。 |\n| `false` | 始终禁用,无论模型如何。 |\n| `[\"gpt\", \"codex\", \"qwen\", \"llama\"]` | 仅当模型名称包含列出的子字符串之一时启用(不区分大小写)。 |\n\n### 注入的内容\n\n启用后,系统提示词中可能会添加三层指导:\n\n1. **通用工具使用强制**(所有匹配模型)—— 指示模型立即进行工具调用而不是描述意图,持续工作直到任务完成,永远不要以未来操作的承诺结束轮次。\n\n2. **OpenAI 执行纪律**(仅限 GPT 和 Codex 模型)—— 针对 GPT 特定失败模式的额外指导:在部分结果上放弃工作、跳过先决条件查找、幻觉而不是使用工具、在未验证的情况下宣布\"完成\"。\n\n3. **Google 操作指导**(仅限 Gemini 和 Gemma 模型)—— 简洁性、绝对路径、并行工具调用和编辑前验证模式。\n\n这些对用户透明,仅影响系统提示词。已经可靠使用工具的模型(如 Claude)不需要此指导,这就是为什么 `\"auto\"` 排除它们。\n\n### 何时开启\n\n如果您使用的模型不在默认自动列表中,并注意到它经常描述它*会*做什么而不是实际去做,请设置 `tool_use_enforcement: true` 或将模型子字符串添加到列表中:\n\n```yaml\nagent:\n tool_use_enforcement: [\"gpt\", \"codex\", \"gemini\", \"grok\", \"my-custom-model\"]\n```\n\n## TTS 配置\n\n```yaml\ntts:\n provider: \"edge\" # \"edge\" | \"elevenlabs\" | \"openai\" | \"minimax\" | \"mistral\" | \"gemini\" | \"xai\" | \"neutts\"\n speed: 1.0 # 全局速度倍数(所有 provider 的回退)\n edge:\n voice: \"en-US-AriaNeural\" # 322 种声音,74 种语言\n speed: 1.0 # 速度倍数(转换为速率百分比,例如 1.5 → +50%)\n elevenlabs:\n voice_id: \"pNInz6obpgDQGcFmaJgB\"\n model_id: \"eleven_multilingual_v2\"\n openai:\n model: \"gpt-4o-mini-tts\"\n voice: \"alloy\" # alloy、echo、fable、onyx、nova、shimmer\n speed: 1.0 # 速度倍数(API 限制为 0.25–4.0)\n base_url: \"https://api.openai.com/v1\" # 覆盖 OpenAI 兼容 TTS 端点\n minimax:\n speed: 1.0 # 语音速度倍数\n # base_url: \"\" # 可选:覆盖 OpenAI 兼容 TTS 端点\n mistral:\n model: \"voxtral-mini-tts-2603\"\n voice_id: \"c69964a6-ab8b-4f8a-9465-ec0925096ec8\" # Paul - Neutral(默认)\n gemini:\n model: \"gemini-2.5-flash-preview-tts\" # 或 gemini-2.5-pro-preview-tts\n voice: \"Kore\" # 30 种预置声音:Zephyr、Puck、Kore、Enceladus 等\n xai:\n voice_id: \"eve\" # xAI TTS 声音\n language: \"en\" # ISO 639-1\n sample_rate: 24000\n bit_rate: 128000 # MP3 比特率\n # base_url: \"https://api.x.ai/v1\"\n neutts:\n ref_audio: ''\n ref_text: ''\n model: neuphonic/neutts-air-q4-gguf\n device: cpu\n```\n\n这控制 `text_to_speech` 工具和语音模式中的口语回复(CLI 中的 `/voice tts` 或消息 gateway)。\n\n**速度回退层次:** provider 特定速度(例如 `tts.edge.speed`)→ 全局 `tts.speed` → `1.0` 默认值。设置全局 `tts.speed` 以在所有 provider 上应用统一速度,或按 provider 覆盖以进行精细控制。\n\n## 显示设置\n\n```yaml\ndisplay:\n tool_progress: all # off | new | all | verbose\n tool_progress_command: false # 在消息 gateway 中启用 /verbose 斜杠命令\n platforms: {} # 每平台显示覆盖(见下文)\n tool_progress_overrides: {} # 已弃用 —— 改用 display.platforms\n interim_assistant_messages: true # Gateway:将自然的轮次中 assistant 更新作为单独消息发送\n skin: default # 内置或自定义 CLI 皮肤(参阅 user-guide/features/skins)\n personality: \"kawaii\" # 旧版外观字段,仍在某些摘要中显示\n compact: false # 紧凑输出模式(减少空白)\n resume_display: full # full(恢复时显示之前的消息)| minimal(仅单行)\n bell_on_complete: false # 当 agent 完成时播放终端铃声(适合长任务)\n show_reasoning: false # 在每次响应上方显示模型推理/思考(用 /reasoning show|hide 切换)\n streaming: false # 将 token 实时流式传输到终端\n show_cost: false # 在 CLI 状态栏中显示估计 $ 成本\n timestamps: false # 为 true 时,在 CLI/TUI 记录中为用户和 assistant 标签添加 [HH:MM] 时间戳前缀\n tool_preview_length: 0 # 工具调用预览的最大字符数(0 = 无限制,显示完整路径/命令)\n runtime_footer: # Gateway:在最终回复中附加运行时上下文页脚\n enabled: false\n fields: [\"model\", \"context_pct\", \"cwd\"]\n file_mutation_verifier: true # 当本轮 write_file/patch 调用失败时附加建议性页脚\n language: en # 静态消息的 UI 语言(审批提示、部分 gateway 回复)。en | zh | zh-hant | ja | de | es | fr | tr | uk | af | ko | it | ga | pt | ru | hu\n```\n\n### 文件变更验证器\n\n当 `display.file_mutation_verifier` 为 `true`(默认)时,每当本轮中 `write_file` 或 `patch` 调用失败且从未被对同一路径的成功写入取代时,Hermes 会在 assistant 的最终响应中附加一行建议。这捕获了\"批量并行补丁,一半静默失败,模型总结成功\"这类过度声明,而无需您在每次编辑后手动运行 `git status`。\n\n示例页脚:\n\n```\n⚠️ File-mutation verifier: 3 file(s) were NOT modified this turn despite any wording above that may suggest otherwise. Run `git status` or `read_file` to confirm.\n • concepts/automatic-organization.md — [patch] Could not find match for old_string\n • concepts/lora.md — [patch] Could not find match for old_string\n • concepts/rag-pipeline.md — [patch] Could not find match for old_string\n```\n\n设置 `file_mutation_verifier: false`(或 `HERMES_FILE_MUTATION_VERIFIER=0`)以禁止页脚。验证器仅在轮次结束时有真实失败未解决时触发 —— 在同一轮次内重试失败补丁并成功的模型不会为该文件触发它。\n\n### 静态消息的 UI 语言\n\n`display.language` 设置翻译一小组静态面向用户的消息 —— CLI 审批提示、少数 gateway 斜杠命令回复(例如重启排空通知、\"审批已过期\"、\"目标已清除\")。它**不**翻译 agent 响应、日志行、工具输出、错误回溯或斜杠命令描述 —— 这些保持英文。如果您希望 agent 本身用另一种语言回复,只需在您的提示词或系统消息中告诉它。\n\n支持的值:`en`(默认)、`zh`(简体中文)、`zh-hant`(繁体中文)、`ja`(日语)、`de`(德语)、`es`(西班牙语)、`fr`(法语)、`tr`(土耳其语)、`uk`(乌克兰语)、`af`(南非荷兰语)、`ko`(韩语)、`it`(意大利语)、`ga`(爱尔兰语)、`pt`(葡萄牙语)、`ru`(俄语)、`hu`(匈牙利语)。未知值回退到英文。\n\n您也可以使用 `HERMES_LANGUAGE` 环境变量按会话设置,它会覆盖配置值。\n\n```yaml\ndisplay:\n language: zh # CLI 审批提示以中文显示\n```\n\n| 模式 | 您看到的内容 |\n|------|-------------|\n| `off` | 静默 —— 仅最终响应 |\n| `new` | 仅在工具更改时显示工具指示器 |\n| `all` | 每次工具调用附带简短预览(默认) |\n| `verbose` | 完整参数、结果和调试日志 |\n\n在 CLI 中,使用 `/verbose` 循环切换这些模式。要在消息平台(Telegram、Discord、Slack 等)中使用 `/verbose`,请在上方的 `display` 部分设置 `tool_progress_command: true`。该命令将循环切换模式并保存到配置。\n\n### 运行时元数据页脚(仅限 gateway)\n\n当 `display.runtime_footer.enabled: true` 时,Hermes 在每个 gateway 轮次的**最终**消息中附加一个小型运行时上下文页脚。目前页脚可显示模型、上下文窗口百分比和当前工作目录。默认关闭;如果您的团队希望每个回复都包含这些来源信息,请按 gateway 选择加入。\n\n```yaml\ndisplay:\n runtime_footer:\n enabled: true\n fields: [\"model\", \"context_pct\", \"cwd\"] # 支持字段:model、context_pct、cwd\n```\n\n`/footer` 斜杠命令在任何会话中运行时切换此功能。\n\n附加到 Telegram/Discord/Slack 回复的示例页脚:\n\n```\n— claude-opus-4.7 · 12 tool calls · 2m 14s · $0.042\n```\n\n只有轮次的**最终**消息获得页脚;中间更新保持干净。\n\n### 每平台进度覆盖\n\n不同平台有不同的详细程度需求。例如,Signal 无法编辑消息,因此每次进度更新都会成为单独的消息 —— 很嘈杂。使用 `display.platforms` 设置每平台模式:\n\n```yaml\ndisplay:\n tool_progress: all # 全局默认\n platforms:\n signal:\n tool_progress: 'off' # 在 Signal 上静默进度\n telegram:\n tool_progress: verbose # 在 Telegram 上详细进度\n slack:\n tool_progress: 'off' # 在共享 Slack 工作区中保持安静\n```\n\n没有覆盖的平台回退到全局 `tool_progress` 值。有效平台键:`telegram`、`discord`、`slack`、`signal`、`whatsapp`、`matrix`、`mattermost`、`email`、`sms`、`homeassistant`、`dingtalk`、`feishu`、`wecom`、`weixin`、`bluebubbles`、`qqbot`。旧版 `display.tool_progress_overrides` 键仍可加载以向后兼容,但已弃用,并在首次加载时迁移到 `display.platforms`。\n\n`interim_assistant_messages` 仅限 gateway。启用后,Hermes 将已完成的轮次中 assistant 更新作为单独的聊天消息发送。这与 `tool_progress` 无关,不需要 gateway 流式传输。\n\n## 隐私\n\n```yaml\nprivacy:\n redact_pii: false # 从 LLM 上下文中删除 PII(仅限 gateway)\n```\n\n当 `redact_pii` 为 `true` 时,gateway 在将系统提示词发送到受支持平台上的 LLM 之前,会从中删除个人身份信息:\n\n| 字段 | 处理方式 |\n|-------|-----------|\n| 电话号码(WhatsApp/Signal 上的用户 ID) | 哈希为 `user_<12-char-sha256>` |\n| 用户 ID | 哈希为 `user_<12-char-sha256>` |\n| 聊天 ID | 数字部分哈希,保留平台前缀(`telegram:<hash>`) |\n| 主频道 ID | 数字部分哈希 |\n| 用户名/昵称 | **不受影响**(用户选择的,公开可见) |\n\n**平台支持:** 删除适用于 WhatsApp、Signal 和 Telegram。Discord 和 Slack 被排除,因为它们的提及系统(`<@user_id>`)需要 LLM 上下文中的真实 ID。\n\n哈希是确定性的 —— 同一用户始终映射到同一哈希,因此模型仍然可以在群聊中区分用户。路由和传递在内部使用原始值。\n\n## 语音转文字(STT)\n\n```yaml\nstt:\n provider: \"local\" # \"local\" | \"groq\" | \"openai\" | \"mistral\"\n local:\n model: \"base\" # tiny、base、small、medium、large-v3\n openai:\n model: \"whisper-1\" # whisper-1 | gpt-4o-mini-transcribe | gpt-4o-transcribe\n # model: \"whisper-1\" # 旧版回退键仍受支持\n```\n\nProvider 行为:\n\n- `local` 使用在您机器上运行的 `faster-whisper`。使用 `pip install faster-whisper` 单独安装。\n- `groq` 使用 Groq 的 Whisper 兼容端点,读取 `GROQ_API_KEY`。\n- `openai` 使用 OpenAI 语音 API,读取 `VOICE_TOOLS_OPENAI_KEY`。\n\n如果请求的 provider 不可用,Hermes 按此顺序自动回退:`local` → `groq` → `openai`。\n\nGroq 和 OpenAI 模型覆盖由环境变量驱动:\n\n```bash\nSTT_GROQ_MODEL=whisper-large-v3-turbo\nSTT_OPENAI_MODEL=whisper-1\nGROQ_BASE_URL=https://api.groq.com/openai/v1\nSTT_OPENAI_BASE_URL=https://api.openai.com/v1\n```\n\n## 语音模式(CLI)\n\n```yaml\nvoice:\n record_key: \"ctrl+b\" # CLI 内的按键通话键\n max_recording_seconds: 120 # 长录音的硬停止\n auto_tts: false # /voice on 时自动启用口语回复\n beep_enabled: true # 在 CLI 语音模式中播放录音开始/停止提示音\n silence_threshold: 200 # 语音检测的 RMS 阈值\n silence_duration: 3.0 # 自动停止前的静默秒数\n```\n\n在 CLI 中使用 `/voice on` 启用麦克风模式,使用 `record_key` 开始/停止录音,使用 `/voice tts` 切换口语回复。端到端设置和平台特定行为请参阅[语音模式](/user-guide/features/voice-mode)。\n\n## 流式传输\n\n将 token 实时流式传输到终端或消息平台,而不是等待完整响应。\n\n### CLI 流式传输\n\n```yaml\ndisplay:\n streaming: true # 实时将 token 流式传输到终端\n show_reasoning: true # 同时流式传输推理/思考 token(可选)\n```\n\n启用后,响应在流式传输框内逐 token 出现。工具调用仍然静默捕获。如果 provider 不支持流式传输,它会自动回退到正常显示。\n\n### Gateway 流式传输(Telegram、Discord、Slack)\n\n```yaml\nstreaming:\n enabled: true # 启用渐进式消息编辑\n transport: edit # \"edit\"(渐进式消息编辑)或 \"off\"\n edit_interval: 0.3 # 消息编辑之间的秒数\n buffer_threshold: 40 # 强制编辑刷新前的字符数\n cursor: \" ▉\" # 流式传输期间显示的光标\n fresh_final_after_seconds: 0 # 预览超过此时间时选择发送新的最终消息(Telegram)\n```\n\n启用后,bot 在第一个 token 时发送消息,然后随着更多 token 到来渐进式编辑它。不支持消息编辑的平台(Signal、Email、Home Assistant)在第一次尝试时自动检测 —— 该会话的流式传输被优雅地禁用,不会产生大量消息。\n\n对于不带渐进式 token 编辑的独立自然轮次中 assistant 更新,请设置 `display.interim_assistant_messages: true`。\n\n**溢出处理:** 如果流式传输的文本超过平台的消息长度限制(约 4096 字符),当前消息被最终化,新消息自动开始。\n\n**新的最终消息(Telegram):** Telegram 的 `editMessageText` 保留原始消息时间戳,因此长时间运行的流式回复即使在完成后也会保留第一个 token 的时间戳。设置 `fresh_final_after_seconds > 0` 可选择将旧预览作为全新的最终消息传递,并尽力删除旧预览。默认值为 `0`,始终就地最终化流式回复,避免某些客户端短暂显示重复消息再删除其中一条。\n\n:::note\n主开关 `streaming.enabled` 默认为 `false`——在你启用之前不会有任何流式传输。启用后,是否流式传输按**平台**决定:Telegram 默认带有 `display.platforms.telegram.streaming: true`(流式传输),Discord 为 `display.platforms.discord.streaming: false`(不流式传输)。因此启用流式传输后,Telegram 开箱即用地流式传输,Discord 在你修改其开关之前仍使用整条消息回复。你可以在仪表盘的 **Channels** 开关中或直接在 `~/.hermes/config.yaml` 中调整这些按平台的开关。\n:::\n\n## 群聊会话隔离\n\n控制共享聊天是每个房间保持一个对话还是每个参与者一个对话:\n\n```yaml\ngroup_sessions_per_user: true # true = 群组/频道中每用户隔离,false = 每个聊天一个共享会话\n```\n\n- `true` 是默认和推荐设置。在 Discord 频道、Telegram 群组、Slack 频道和类似共享上下文中,当平台提供用户 ID 时,每个发送者获得自己的会话。\n- `false` 恢复到旧的共享房间行为。如果您明确希望 Hermes 将频道视为一个协作对话,这可能有用,但这也意味着用户共享上下文、token 成本和中断状态。\n- 私信不受影响。Hermes 仍然像往常一样通过聊天/DM ID 键入 DM。\n- 线程与其父频道保持隔离;使用 `true` 时,每个参与者在线程内也获得自己的会话。\n\n有关行为详情和示例,请参阅[会话](/user-guide/sessions)和 [Discord 指南](/user-guide/messaging/discord)。\n\n## 未授权 DM 行为\n\n控制当未知用户发送私信时 Hermes 的行为:\n\n```yaml\nunauthorized_dm_behavior: pair\n\nwhatsapp:\n unauthorized_dm_behavior: ignore\n```\n\n- `pair` 是默认值。Hermes 拒绝访问,但在 DM 中回复一次性配对码。\n- `ignore` 静默丢弃未授权的 DM。\n- 平台部分覆盖全局默认值,因此您可以在广泛范围内保持配对启用,同时使一个平台更安静。\n\n## 快速命令\n\n定义自定义命令,这些命令要么在不调用 LLM 的情况下运行 shell 命令,要么将一个斜杠命令别名为另一个。Exec 快速命令是零 token 的,对于从消息平台(Telegram、Discord 等)进行快速服务器检查或实用脚本很有用。\n\n```yaml\nquick_commands:\n status:\n type: exec\n command: systemctl status hermes-agent\n disk:\n type: exec\n command: df -h /\n update:\n type: exec\n command: cd ~/.hermes/hermes-agent && git pull && uv pip install -e .\n gpu:\n type: exec\n command: nvidia-smi --query-gpu=name,utilization.gpu,memory.used,memory.total --format=csv,noheader\n restart:\n type: alias\n target: /gateway restart\n```\n\n用法:在 CLI 或任何消息平台中输入 `/status`、`/disk`、`/update`、`/gpu` 或 `/restart`。`exec` 命令在宿主本地运行并直接返回输出 —— 无 LLM 调用,不消耗 token。`alias` 命令重写为配置的斜杠命令目标。\n\n- **30 秒超时** —— 长时间运行的命令被终止并显示错误消息\n- **优先级** —— 快速命令在技能命令之前检查,因此您可以覆盖技能名称\n- **自动补全** —— 快速命令在调度时解析,不显示在内置斜杠命令自动补全表中\n- **类型** —— 支持的类型为 `exec` 和 `alias`;其他类型显示错误\n- **到处可用** —— CLI、Telegram、Discord、Slack、WhatsApp、Signal、Email、Home Assistant\n\n仅字符串的 prompt 快捷方式不是有效的快速命令。对于可重用的 prompt 工作流,请创建技能或别名到现有斜杠命令。\n\n## 人类延迟\n\n在消息平台中模拟类人响应节奏:\n\n```yaml\nhuman_delay:\n mode: \"off\" # off | natural | custom\n min_ms: 800 # 最小延迟(自定义模式)\n max_ms: 2500 # 最大延迟(自定义模式)\n```\n\n## 代码执行\n\n配置 `execute_code` 工具:\n\n```yaml\ncode_execution:\n mode: project # project(默认)| strict\n timeout: 300 # 最大执行时间(秒)\n max_tool_calls: 50 # 代码执行中的最大工具调用次数\n```\n\n**`mode`** 控制脚本的工作目录和 Python 解释器:\n\n- **`project`**(默认)—— 脚本在会话的工作目录中以活跃 virtualenv/conda 环境的 python 运行。项目依赖(`pandas`、`torch`、项目包)和相对路径(`.env`、`./data.csv`)自然解析,与 `terminal()` 看到的一致。\n- **`strict`** —— 脚本在临时暂存目录中以 `sys.executable`(Hermes 自己的 python)运行。最大可重现性,但项目依赖和相对路径不会解析。\n\n环境清理(删除 `*_API_KEY`、`*_TOKEN`、`*_SECRET`、`*_PASSWORD`、`*_CREDENTIAL`、`*_PASSWD`、`*_AUTH`)和工具白名单在两种模式下完全相同 —— 切换模式不会改变安全态势。\n\n## Web 搜索后端\n\n`web_search` 和 `web_extract` 工具支持五种后端 provider。在 `config.yaml` 中或通过 `hermes tools` 配置后端:\n\n```yaml\nweb:\n backend: firecrawl # firecrawl | searxng | parallel | tavily | exa\n\n # 或使用每功能键混合 provider(例如免费搜索 + 付费提取):\n search_backend: \"searxng\"\n extract_backend: \"firecrawl\"\n```\n\n| 后端 | 环境变量 | 搜索 | 提取 |\n|---------|---------|--------|---------|\n| **Firecrawl**(默认) | `FIRECRAWL_API_KEY` | ✔ | ✔ |\n| **SearXNG** | `SEARXNG_URL` | ✔ | — |\n| **Parallel** | `PARALLEL_API_KEY` | ✔ | ✔ |\n| **Tavily** | `TAVILY_API_KEY` | ✔ | ✔ |\n| **Exa** | `EXA_API_KEY` | ✔ | ✔ |\n\n**后端选择:** 如果未设置 `web.backend`,后端从可用的 API 密钥自动检测。如果仅设置了 `SEARXNG_URL`,使用 SearXNG。如果仅设置了 `EXA_API_KEY`,使用 Exa。如果仅设置了 `TAVILY_API_KEY`,使用 Tavily。如果仅设置了 `PARALLEL_API_KEY`,使用 Parallel。否则 Firecrawl 是默认值。\n\n**SearXNG** 是一个免费、自托管、尊重隐私的元搜索引擎,查询 70+ 个搜索引擎。无需 API 密钥 —— 只需将 `SEARXNG_URL` 设置为您的实例(例如 `http://localhost:8080`)。SearXNG 仅限搜索;`web_extract` 需要单独的提取 provider(设置 `web.extract_backend`)。Docker 设置说明请参阅 [Web 搜索设置指南](/user-guide/features/web-search)。\n\n**自托管 Firecrawl:** 设置 `FIRECRAWL_API_URL` 指向您自己的实例。设置自定义 URL 后,API 密钥变为可选(在服务器上设置 `USE_DB_AUTHENTICATION=***` 以禁用认证)。\n\n**Parallel 搜索模式:** 设置 `PARALLEL_SEARCH_MODE` 控制搜索行为 —— `fast`、`one-shot` 或 `agentic`(默认:`agentic`)。\n\n**Exa:** 在 `~/.hermes/.env` 中设置 `EXA_API_KEY`。支持 `category` 过滤(`company`、`research paper`、`news`、`people`、`personal site`、`pdf`)和域名/日期过滤器。\n\n## 浏览器\n\n配置浏览器自动化行为:\n\n```yaml\nbrowser:\n inactivity_timeout: 120 # 自动关闭空闲会话前的秒数\n command_timeout: 30 # 浏览器命令超时(截图、导航等)(秒)\n record_sessions: false # 自动将浏览器会话录制为 WebM 视频到 ~/.hermes/browser_recordings/\n # 可选 CDP 覆盖 —— 设置后,Hermes 直接附加到您自己的\n # Chromium 系浏览器(通过 /browser connect),而不是启动无头浏览器。\n cdp_url: \"\"\n # 对话框监督器 —— 控制当 CDP 后端附加时(Browserbase、本地 Chromium 系\n # 浏览器通过 /browser connect)如何处理原生 JS 对话框(alert/confirm/prompt)。\n # 在 Camofox 和默认本地 agent 浏览器模式下忽略。\n dialog_policy: must_respond # must_respond | auto_dismiss | auto_accept\n dialog_timeout_s: 300 # must_respond 下的安全自动关闭(秒)\n camofox:\n managed_persistence: false # 为 true 时,Camofox 会话跨重启持久化 cookie/登录\n user_id: \"\" # 可选的外部管理 Camofox userId\n session_key: \"\" # Hermes 创建标签页时发送的可选会话密钥\n adopt_existing_tab: false # 在创建新标签页之前重用此身份的现有标签页\n```\n\n**对话框策略:**\n\n- `must_respond`(默认)—— 捕获对话框,在 `browser_snapshot.pending_dialogs` 中显示,等待 agent 调用 `browser_dialog(action=...)`。在 `dialog_timeout_s` 秒内无响应后,对话框被自动关闭以防止页面的 JS 线程永久停滞。\n- `auto_dismiss` —— 捕获,立即关闭。Agent 仍然在事后的 `browser_snapshot.recent_dialogs` 中看到对话框记录,`closed_by=\"auto_policy\"`。\n- `auto_accept` —— 捕获,立即接受。适用于有激进 `beforeunload` 提示的页面。\n\n完整对话框工作流请参阅[浏览器功能页面](./features/browser.md#browser_dialog)。\n\n浏览器工具集支持多个 provider。有关 Browserbase、Browser Use 和本地 Chromium 系 CDP 设置的详细信息,请参阅[浏览器功能页面](/user-guide/features/browser)。\n\n## 时区\n\n使用 IANA 时区字符串覆盖服务器本地时区。影响日志中的时间戳、cron 调度和系统提示词时间注入。\n\n```yaml\ntimezone: \"America/New_York\" # IANA 时区(默认:\"\" = 服务器本地时间)\n```\n\n支持的值:任何 IANA 时区标识符(例如 `America/New_York`、`Europe/London`、`Asia/Kolkata`、`UTC`)。留空或省略以使用服务器本地时间。\n\n## Discord\n\n为消息 gateway 配置 Discord 特定行为:\n\n```yaml\ndiscord:\n require_mention: true # 在服务器频道中需要 @提及才能响应\n free_response_channels: \"\" # 逗号分隔的频道 ID,bot 在这些频道无需 @提及即可响应\n auto_thread: true # 在频道中 @提及时自动创建线程\n```\n\n- `require_mention` —— 为 `true`(默认)时,bot 仅在服务器频道中被 `@BotName` 提及时响应。DM 始终无需提及即可工作。\n- `free_response_channels` —— 逗号分隔的频道 ID 列表,bot 在这些频道对每条消息响应,无需提及。\n- `auto_thread` —— 为 `true`(默认)时,频道中的提及会自动为对话创建线程,保持频道整洁(类似 Slack 线程)。\n\n## 安全\n\n预执行安全扫描和机密脱敏:\n\n```yaml\nsecurity:\n redact_secrets: false # 在工具输出和日志中脱敏 API 密钥模式(默认关闭)\n tirith_enabled: true # 为终端命令启用 Tirith 安全扫描\n tirith_path: \"tirith\" # tirith 二进制文件路径(默认:$PATH 中的 \"tirith\")\n tirith_timeout: 5 # 等待 tirith 扫描的秒数\n tirith_fail_open: true # 如果 tirith 不可用,允许命令执行\n website_blocklist: # 参见下方网站黑名单部分\n enabled: false\n domains: []\n shared_files: []\n```\n\n- `redact_secrets` —— 为 `true` 时,自动检测并脱敏工具输出中看起来像 API 密钥、token 和密码的模式,然后再进入对话上下文和日志。**默认关闭** —— 如果您经常在工具输出中处理真实凭据并希望有安全网,请启用。显式设置为 `true` 以开启。\n- `tirith_enabled` —— 为 `true` 时,终端命令在执行前由 [Tirith](https://github.com/sheeki03/tirith) 扫描以检测潜在危险操作。\n- `tirith_path` —— tirith 二进制文件的路径。如果 tirith 安装在非标准位置,请设置此项。\n- `tirith_timeout` —— 等待 tirith 扫描的最大秒数。如果扫描超时,命令继续执行。\n- `tirith_fail_open` —— 为 `true`(默认)时,如果 tirith 不可用或失败,允许命令执行。设置为 `false` 以在 tirith 无法验证时阻止命令。\n\n## 网站黑名单\n\n阻止 agent 的 web 和浏览器工具访问特定域名:\n\n```yaml\nsecurity:\n website_blocklist:\n enabled: false # 启用 URL 阻止(默认:false)\n domains: # 被阻止的域名模式列表\n - \"*.internal.company.com\"\n - \"admin.example.com\"\n - \"*.local\"\n shared_files: # 从外部文件加载额外规则\n - \"/etc/hermes/blocked-sites.txt\"\n```\n\n启用后,任何匹配被阻止域名模式的 URL 在 web 或浏览器工具执行之前都会被拒绝。这适用于 `web_search`、`web_extract`、`browser_navigate` 以及任何访问 URL 的工具。\n\n域名规则支持:\n- 精确域名:`admin.example.com`\n- 通配符子域名:`*.internal.company.com`(阻止所有子域名)\n- TLD 通配符:`*.local`\n\n共享文件每行包含一条域名规则(空行和 `#` 注释被忽略)。缺失或不可读的文件记录警告,但不禁用其他 web 工具。\n\n策略缓存 30 秒,因此配置更改无需重启即可快速生效。\n\n## 智能审批\n\n控制 Hermes 如何处理潜在危险命令:\n\n```yaml\napprovals:\n mode: smart # smart | manual | off\n```\n\n| 模式 | 行为 |\n|------|----------|\n| `smart`(默认) | 使用辅助 LLM 评估被标记的命令是否真正危险。低风险命令仅对当前命令自动批准,真正危险的命令自动拒绝,不确定的情况升级给用户。 |\n| `manual` | 在执行任何被标记的命令之前提示用户。在 CLI 中显示交互式审批对话框。在消息中排队待处理的审批请求。 |\n| `off` | 跳过所有审批检查。等同于 `HERMES_YOLO_MODE=true`。**谨慎使用。** |\n\n智能模式对于减少审批疲劳特别有用 —— 它让 agent 在安全操作上更自主地工作,同时仍然捕获真正破坏性的命令。\n\n:::warning\n设置 `approvals.mode: off` 会禁用终端命令的所有安全检查。仅在受信任的沙箱环境中使用。\n:::\n\n## 检查点\n\n破坏性文件操作之前的自动文件系统快照。详情请参阅[检查点与回滚](/user-guide/checkpoints-and-rollback)。\n\n```yaml\ncheckpoints:\n enabled: false # 启用自动检查点(也可:hermes chat --checkpoints)。默认:false(选择加入)。\n max_snapshots: 20 # 每个目录保留的最大检查点数(默认:20)\n```\n\n\n## 委托\n\n为委托工具配置子 agent 行为:\n\n```yaml\ndelegation:\n # model: \"google/gemini-3-flash-preview\" # 覆盖模型(空 = 继承父级)\n # provider: \"openrouter\" # 覆盖 provider(空 = 继承父级)\n # base_url: \"http://localhost:1234/v1\" # 直接 OpenAI 兼容端点(优先于 provider)\n # api_key: \"local-key\" # base_url 的 API 密钥(回退到 OPENAI_API_KEY)\n # api_mode: \"\" # base_url 的线路协议:\"chat_completions\"、\"codex_responses\" 或 \"anthropic_messages\"。空 = 从 URL 自动检测(例如 /anthropic 后缀 → anthropic_messages)。对启发式无法检测的非标准端点显式设置。\n max_concurrent_children: 3 # 每批并行子 agent 数(下限 1,无上限)。也可通过 DELEGATION_MAX_CONCURRENT_CHILDREN 环境变量设置。\n max_spawn_depth: 1 # 委托树深度上限(1-3,截断)。1 = 扁平(默认):父级生成无法委托的叶子。2 = 编排器子级可以生成叶子孙级。3 = 三级。\n orchestrator_enabled: true # 全局终止开关。为 false 时,role=\"orchestrator\" 被忽略,每个子级无论 max_spawn_depth 如何都被强制为叶子。\n```\n\n**子 agent provider:model 覆盖:** 默认情况下,子 agent 继承父 agent 的 provider 和模型。设置 `delegation.provider` 和 `delegation.model` 将子 agent 路由到不同的 provider:model 对 —— 例如,在您的主 agent 运行昂贵推理模型时,为范围较窄的子任务使用便宜/快速的模型。\n\n**直接端点覆盖:** 如果您想要明显的自定义端点路径,请设置 `delegation.base_url`、`delegation.api_key` 和 `delegation.model`。这将子 agent 直接发送到该 OpenAI 兼容端点,并优先于 `delegation.provider`。如果省略 `delegation.api_key`,Hermes 仅回退到 `OPENAI_API_KEY`。\n\n**线路协议(`api_mode`):** Hermes 从 `delegation.base_url` 自动检测线路协议(例如以 `/anthropic` 结尾的路径 → `anthropic_messages`;Codex/原生 Anthropic/Kimi-coding 主机名保留其现有检测)。对于启发式无法分类的端点 —— 例如 Azure AI Foundry、MiniMax、Zhipu GLM 或前置 Anthropic 形状后端的 LiteLLM 代理 —— 请将 `delegation.api_mode` 显式设置为 `chat_completions`、`codex_responses` 或 `anthropic_messages` 之一。留空(默认)以保持自动检测。\n\n委托 provider 使用与 CLI/gateway 启动相同的凭据解析。所有配置的 provider 均受支持:`openrouter`、`nous`、`copilot`、`zai`、`kimi-coding`、`minimax`、`minimax-cn`。设置 provider 时,系统自动解析正确的基础 URL、API 密钥和 API 模式 —— 无需手动凭据连接。\n\n**优先级:** 配置中的 `delegation.base_url` → 配置中的 `delegation.provider` → 父 provider(继承)。配置中的 `delegation.model` → 父模型(继承)。仅设置 `model` 而不设置 `provider` 仅更改模型名称,同时保留父级凭据(适用于在同一 provider(如 OpenRouter)内切换模型)。\n\n**宽度和深度:** `max_concurrent_children` 限制每批并行运行的子 agent 数量(默认 `3`,下限 1,无上限)。也可通过 `DELEGATION_MAX_CONCURRENT_CHILDREN` 环境变量设置。当模型提交的 `tasks` 数组超过上限时,`delegate_task` 返回工具错误解释限制,而不是静默截断。`max_spawn_depth` 控制委托树深度(截断到 1-3)。在默认 `1` 时,委托是扁平的:子级无法生成孙级,传递 `role=\"orchestrator\"` 静默降级为 `leaf`。提升到 `2` 使编排器子级可以生成叶子孙级;`3` 用于三级树。Agent 通过 `role=\"orchestrator\"` 按调用选择编排;`orchestrator_enabled: false` 强制每个子级回到叶子,无论如何。成本呈乘法增长 —— 在 `max_spawn_depth: 3` 和 `max_concurrent_children: 3` 时,树可以达到 3×3×3 = 27 个并发叶子 agent。使用模式请参阅[子 Agent 委托 → 深度限制和嵌套编排](features/delegation.md#depth-limit-and-nested-orchestration)。\n\n## 澄清\n\n配置澄清提示行为:\n\n```yaml\nclarify:\n timeout: 120 # 等待用户澄清响应的秒数\n```\n\n## 上下文文件(SOUL.md、AGENTS.md)\n\nHermes 使用两种不同的上下文范围:\n\n| 文件 | 用途 | 范围 |\n|------|---------|-------|\n| `SOUL.md` | **主要 agent 身份** —— 定义 agent 是谁(系统提示词第 #1 槽位) | `~/.hermes/SOUL.md` 或 `$HERMES_HOME/SOUL.md` |\n| `.hermes.md` / `HERMES.md` | 项目特定指令(最高优先级) | 向上走到 git 根目录 |\n| `AGENTS.md` | 项目特定指令、编码规范 | 递归目录遍历 |\n| `CLAUDE.md` | Claude Code 上下文文件(也会检测) | 仅工作目录 |\n| `.cursorrules` | Cursor IDE 规则(也会检测) | 仅工作目录 |\n| `.cursor/rules/*.mdc` | Cursor 规则文件(也会检测) | 仅工作目录 |\n\n- **SOUL.md** 是 agent 的主要身份。它占据系统提示词的第 #1 槽位,完全替换内置的默认身份。编辑它以完全自定义 agent 是谁。\n- 如果 SOUL.md 缺失、为空或无法加载,Hermes 回退到内置默认身份。\n- **项目上下文文件使用优先级系统** —— 仅加载一种类型(第一个匹配优先):`.hermes.md` → `AGENTS.md` → `CLAUDE.md` → `.cursorrules`。SOUL.md 始终独立加载。\n- **AGENTS.md** 是分层的:如果子目录也有 AGENTS.md,所有都会合并。\n- 如果 `SOUL.md` 不存在,Hermes 会自动生成默认的 `SOUL.md`。\n- 所有加载的上下文文件上限为 20,000 字符,并进行智能截断。\n\n另请参阅:\n- [个性与 SOUL.md](/user-guide/features/personality)\n- [上下文文件](/user-guide/features/context-files)\n\n## 工作目录\n\n| 上下文 | 默认值 |\n|---------|---------|\n| **CLI(`hermes`)** | 运行命令的当前目录 |\n| **消息 gateway** | 主目录 `~`(用 `MESSAGING_CWD` 覆盖) |\n| **Docker / Singularity / Modal / SSH** | 容器或远程机器内用户的主目录 |\n\n覆盖工作目录:\n```bash\n# 在 ~/.hermes/.env 或 ~/.hermes/config.yaml 中:\nMESSAGING_CWD=/home/myuser/projects # Gateway 会话\nTERMINAL_CWD=/workspace # 所有终端会话\n```\n"}, "1b081e489156b84d067ddcebb1b72b45bd9b2b46": {"t": 1785767974.682054, "n": 56672, "h": "86d8f1ce0d525265", "c": "---\nsidebar_position: 2\ntitle: \"配置\"\ndescription: \"配置 Hermes Agent — config.yaml、providers、模型、API 密钥等\"\n---\n\n# 配置\n\n所有设置均存储在 `~/.hermes/` 目录中,便于访问。\n\n## 目录结构\n\n```text\n~/.hermes/\n├── config.yaml # 设置(模型、终端、TTS、压缩等)\n├── .env # API 密钥和机密\n├── auth.json # OAuth provider 凭据(Nous Portal 等)\n├── SOUL.md # 主要 agent 身份(系统提示词第 #1 槽位)\n├── memories/ # 持久记忆(MEMORY.md、USER.md)\n├── skills/ # Agent 创建的技能(通过 skill_manage 工具管理)\n├── cron/ # 定时任务\n├── sessions/ # Gateway 会话\n└── logs/ # 日志(errors.log、gateway.log — 机密自动脱敏)\n```\n\n## 管理配置\n\n```bash\nhermes config # 查看当前配置\nhermes config edit # 在编辑器中打开 config.yaml\nhermes config set KEY VAL # 设置特定值\nhermes config check # 检查缺失选项(更新后使用)\nhermes config migrate # 交互式添加缺失选项\n\n# 示例:\nhermes config set model anthropic/claude-opus-4\nhermes config set terminal.backend docker\nhermes config set OPENROUTER_API_KEY sk-or-... # 保存到 .env\n```\n\n:::tip\n`hermes config set` 命令会自动将值路由到正确的文件 —— API 密钥保存到 `.env`,其他所有内容保存到 `config.yaml`。\n:::\n\n## 配置优先级\n\n设置按以下顺序解析(优先级从高到低):\n\n1. **CLI 参数** —— 例如 `hermes chat --model anthropic/claude-sonnet-4`(单次调用覆盖)\n2. **`~/.hermes/config.yaml`** —— 所有非机密设置的主配置文件\n3. **`~/.hermes/.env`** —— 环境变量的回退;机密(API 密钥、token、密码)**必须**放这里\n4. **内置默认值** —— 未设置任何内容时的硬编码安全默认值\n\n:::info 经验法则\n机密(API 密钥、bot token、密码)放入 `.env`。其他所有内容(模型、终端后端、压缩设置、内存限制、工具集)放入 `config.yaml`。当两者都设置时,`config.yaml` 对非机密设置优先。\n:::\n\n## 环境变量替换\n\n可以在 `config.yaml` 中使用 `${VAR_NAME}` 语法引用环境变量:\n\n```yaml\nauxiliary:\n vision:\n api_key: ${GOOGLE_API_KEY}\n base_url: ${CUSTOM_VISION_URL}\n\ndelegation:\n api_key: ${DELEGATION_KEY}\n```\n\n单个值中可以有多个引用:`url: \"${HOST}:${PORT}\"`。如果引用的变量未设置,占位符将保持原样(`${UNDEFINED_VAR}` 保持不变)。仅支持 `${VAR}` 语法 —— 裸 `$VAR` 不会被展开。\n\n关于 AI provider 设置(OpenRouter、Anthropic、Copilot、自定义端点、自托管 LLM、回退模型等),请参阅 [AI Providers](/integrations/providers)。\n\n### Provider 超时\n\n可以为 provider 设置 `providers.<id>.request_timeout_seconds` 作为全局请求超时,以及 `providers.<id>.models.<model>.timeout_seconds` 作为特定模型的覆盖值。适用于每种传输方式(OpenAI-wire、原生 Anthropic、Anthropic 兼容)上的主轮次客户端、回退链、凭据轮换后的重建,以及(对于 OpenAI-wire)每请求超时 kwarg —— 因此配置值优先于旧版 `HERMES_API_TIMEOUT` 环境变量。\n\n还可以设置 `providers.<id>.stale_timeout_seconds` 用于非流式陈旧调用检测器,以及 `providers.<id>.models.<model>.stale_timeout_seconds` 作为特定模型的覆盖值。此值优先于旧版 `HERMES_API_CALL_STALE_TIMEOUT` 环境变量。\n\n不设置这些值将保持旧版默认值(`HERMES_API_TIMEOUT=1800`s、`HERMES_API_CALL_STALE_TIMEOUT=90`s、原生 Anthropic 900s)。隐式的非流式 stale 检测会在本地端点上自动禁用,并且会在超大上下文下自动放宽。目前不适用于 AWS Bedrock(`bedrock_converse` 和 AnthropicBedrock SDK 路径均使用 boto3 及其自身的超时配置)。请参阅 [`cli-config.yaml.example`](https://github.com/NousResearch/hermes-agent/blob/main/cli-config.yaml.example) 中的注释示例。\n\n## 终端后端配置\n\nHermes 支持六种终端后端。每种后端决定 agent 的 shell 命令实际在哪里执行 —— 本地机器、Docker 容器、通过 SSH 的远程服务器、Modal 云沙箱(直接或通过 Nous 托管的 gateway)、Daytona 工作区,或 Singularity/Apptainer 容器。\n\n```yaml\nterminal:\n backend: local # local | docker | ssh | modal | daytona | singularity\n cwd: \".\" # Gateway/cron 工作目录(CLI 始终使用启动目录)\n timeout: 180 # 每条命令的超时时间(秒)\n env_passthrough: [] # 转发到沙箱执行的环境变量名(terminal + execute_code)\n singularity_image: \"docker://nikolaik/python-nodejs:python3.11-nodejs20\" # Singularity 后端的容器镜像\n modal_image: \"nikolaik/python-nodejs:python3.11-nodejs20\" # Modal 后端的容器镜像\n daytona_image: \"nikolaik/python-nodejs:python3.11-nodejs20\" # Daytona 后端的容器镜像\n```\n\n对于 Modal 和 Daytona 等云沙箱,`container_persistent: true` 表示 Hermes 将尝试在沙箱重建后保留文件系统状态。这并不保证相同的活跃沙箱、PID 空间或后台进程之后仍在运行。\n\n### 后端概览\n\n| 后端 | 命令运行位置 | 隔离性 | 最适合 |\n|---------|-------------------|-----------|----------|\n| **local** | 直接在您的机器上 | 无 | 开发、个人使用 |\n| **docker** | 单个持久 Docker 容器(跨会话、`/new`、子 agent 共享) | 完全(命名空间、cap-drop) | 安全沙箱、CI/CD |\n| **ssh** | 通过 SSH 的远程服务器 | 网络边界 | 远程开发、强大硬件 |\n| **modal** | Modal 云沙箱 | 完全(云 VM) | 临时云计算、评估 |\n| **daytona** | Daytona 工作区 | 完全(云容器) | 托管云开发环境 |\n| **singularity** | Singularity/Apptainer 容器 | 命名空间(--containall) | HPC 集群、共享机器 |\n\n### Local 后端\n\n默认后端。命令直接在您的机器上运行,无隔离。无需特殊设置。\n\n```yaml\nterminal:\n backend: local\n```\n\n:::warning\nAgent 拥有与您的用户账户相同的文件系统访问权限。使用 `hermes tools` 禁用不需要的工具,或切换到 Docker 进行沙箱隔离。\n:::\n\n### Docker 后端\n\n在具有安全加固的 Docker 容器内运行命令(所有权限已删除、无权限提升、PID 限制)。\n\n**单个持久容器,而非每条命令一个容器。** Hermes 在首次使用时启动一个长期运行的容器,并通过 `docker exec` 将每个终端、文件和 `execute_code` 调用路由到同一容器中 —— 跨会话、`/new`、`/reset` 和 `delegate_task` 子 agent,贯穿 Hermes 进程的整个生命周期。工作目录更改、已安装的包以及 `/workspace` 中的文件会从一次工具调用延续到下一次,就像本地 shell 一样。容器在关闭时停止并删除。详情请参阅下方的**容器生命周期**。\n\n```yaml\nterminal:\n backend: docker\n docker_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n docker_mount_cwd_to_workspace: false # 将启动目录挂载到 /workspace\n docker_run_as_host_user: false # 参见下方\"以宿主用户身份运行容器\"\n docker_forward_env: # 转发到容器的环境变量\n - \"GITHUB_TOKEN\"\n docker_volumes: # 宿主目录挂载\n - \"/home/user/projects:/workspace/projects\"\n - \"/home/user/data:/data:ro\" # :ro 表示只读\n docker_extra_args: # 附加到 `docker run` 的额外标志\n - \"--gpus=all\"\n - \"--network=host\"\n\n # 资源限制\n container_cpu: 1 # CPU 核心数(0 = 不限制)\n container_memory: 5120 # MB(0 = 不限制)\n container_disk: 51200 # MB(需要 XFS+pquota 上的 overlay2)\n container_persistent: true # 跨会话持久化 /workspace 和 /root\n```\n\n**`terminal.docker_extra_args`**(也可通过 `TERMINAL_DOCKER_EXTRA_ARGS='[\"--gpus=all\"]'` 覆盖)允许传递 Hermes 未作为一级键公开的任意 `docker run` 标志 —— `--gpus`、`--network`、`--add-host`、替代 `--security-opt` 覆盖等。每个条目必须是字符串;该列表最后附加到组装好的 `docker run` 调用中,因此可以在需要时覆盖 Hermes 的默认值。请谨慎使用 —— 与沙箱加固(权限删除、`--user`、workspace 绑定挂载)冲突的标志将悄然削弱隔离性。\n\n**要求:** 已安装并运行 Docker Desktop 或 Docker Engine。Hermes 会探测 `$PATH` 以及常见的 macOS 安装位置(`/usr/local/bin/docker`、`/opt/homebrew/bin/docker`、Docker Desktop 应用包)。开箱即用支持 Podman:设置 `HERMES_DOCKER_BINARY=podman`(或完整路径)以在两者都安装时强制使用它。\n\n**容器生命周期:** Hermes 为每个终端和文件工具调用重用单个长期运行的容器(`docker run -d ... sleep 2h`),跨会话、`/new`、`/reset` 和 `delegate_task` 子 agent,贯穿 Hermes 进程的整个生命周期。命令通过带登录 shell 的 `docker exec` 运行,因此工作目录更改、已安装的包以及 `/workspace` 中的文件都会从一次工具调用延续到下一次。容器在 Hermes 关闭时(或空闲清理回收时)停止并删除。\n\n通过 `delegate_task(tasks=[...])` 生成的并行子 agent 共享这一个容器 —— 并发的 `cd`、环境变量修改以及对同一路径的写入会发生冲突。如果子 agent 需要隔离的沙箱,必须通过 `register_task_env_overrides()` 注册每任务镜像覆盖,RL 和基准测试环境(TerminalBench2、HermesSweEnv 等)会自动为其每任务 Docker 镜像执行此操作。\n\n**安全加固:**\n- `--cap-drop ALL`,仅添加回 `DAC_OVERRIDE`、`CHOWN`、`FOWNER`\n- `--security-opt no-new-privileges`\n- `--pids-limit 256`\n- `/tmp`(512MB)、`/var/tmp`(256MB)、`/run`(64MB)的大小限制 tmpfs\n\n**凭据转发:** `docker_forward_env` 中列出的环境变量首先从您的 shell 环境解析,然后回退到 `~/.hermes/.env`。技能也可以声明 `required_environment_variables`,这些变量会自动合并。\n\n### SSH 后端\n\n通过 SSH 在远程服务器上运行命令。使用 ControlMaster 进行连接复用(5 分钟空闲保活)。默认启用持久 shell —— 状态(cwd、环境变量)在命令之间保持。\n\n```yaml\nterminal:\n backend: ssh\n persistent_shell: true # 保持长期运行的 bash 会话(默认:true)\n```\n\n**必需的环境变量:**\n\n```bash\nTERMINAL_SSH_HOST=my-server.example.com\nTERMINAL_SSH_USER=ubuntu\n```\n\n**可选:**\n\n| 变量 | 默认值 | 描述 |\n|----------|---------|-------------|\n| `TERMINAL_SSH_PORT` | `22` | SSH 端口 |\n| `TERMINAL_SSH_KEY` | (系统默认) | SSH 私钥路径 |\n| `TERMINAL_SSH_PERSISTENT` | `true` | 启用持久 shell |\n\n**工作原理:** 使用 `BatchMode=yes` 和 `StrictHostKeyChecking=accept-new` 在初始化时连接。持久 shell 在远程主机上保持单个 `bash -l` 进程存活,通过临时文件进行通信。需要 `stdin_data` 或 `sudo` 的命令会自动回退到单次模式。\n\n### Modal 后端\n\n在 [Modal](https://modal.com) 云沙箱中运行命令。每个任务获得一个具有可配置 CPU、内存和磁盘的隔离 VM。文件系统可以跨会话快照/恢复。\n\n```yaml\nterminal:\n backend: modal\n container_cpu: 1 # CPU 核心数\n container_memory: 5120 # MB(5GB)\n container_disk: 51200 # MB(50GB)\n container_persistent: true # 快照/恢复文件系统\n```\n\n**必需:** `MODAL_TOKEN_ID` + `MODAL_TOKEN_SECRET` 环境变量,或 `~/.modal.toml` 配置文件。\n\n**持久化:** 启用后,沙箱文件系统在清理时快照,并在下次会话时恢复。快照在 `~/.hermes/modal_snapshots.json` 中跟踪。这保留文件系统状态,而非活跃进程、PID 空间或后台任务。\n\n**凭据文件:** 自动从 `~/.hermes/` 挂载(OAuth token 等),并在每条命令前同步。\n\n### Daytona 后端\n\n在 [Daytona](https://daytona.io) 托管工作区中运行命令。支持停止/恢复以实现持久化。\n\n```yaml\nterminal:\n backend: daytona\n container_cpu: 1 # CPU 核心数\n container_memory: 5120 # MB → 转换为 GiB\n container_disk: 10240 # MB → 转换为 GiB(最大 10 GiB)\n container_persistent: true # 停止/恢复而非删除\n```\n\n**必需:** `DAYTONA_API_KEY` 环境变量。\n\n**持久化:** 启用后,沙箱在清理时停止(而非删除),并在下次会话时恢复。沙箱名称遵循 `hermes-{task_id}` 模式。\n\n**磁盘限制:** Daytona 强制执行 10 GiB 最大值。超过此值的请求将被截断并发出警告。\n\n### Singularity/Apptainer 后端\n\n在 [Singularity/Apptainer](https://apptainer.org) 容器中运行命令。专为 Docker 不可用的 HPC 集群和共享机器设计。\n\n```yaml\nterminal:\n backend: singularity\n singularity_image: \"docker://nikolaik/python-nodejs:python3.11-nodejs20\"\n container_cpu: 1 # CPU 核心数\n container_memory: 5120 # MB\n container_persistent: true # 可写覆盖层跨会话持久化\n```\n\n**要求:** `$PATH` 中有 `apptainer` 或 `singularity` 二进制文件。\n\n**镜像处理:** Docker URL(`docker://...`)自动转换为 SIF 文件并缓存。现有 `.sif` 文件直接使用。\n\n**临时目录:** 按顺序解析:`TERMINAL_SCRATCH_DIR` → `TERMINAL_SANDBOX_DIR/singularity` → `/scratch/$USER/hermes-agent`(HPC 惯例)→ `~/.hermes/sandboxes/singularity`。\n\n**隔离:** 使用 `--containall --no-home` 实现完全命名空间隔离,不挂载宿主 home 目录。\n\n### 常见终端后端问题\n\n如果终端命令立即失败或终端工具报告为已禁用:\n\n- **Local** —— 无特殊要求。入门时最安全的默认选项。\n- **Docker** —— 运行 `docker version` 验证 Docker 是否正常工作。如果失败,修复 Docker 或执行 `hermes config set terminal.backend local`。\n- **SSH** —— `TERMINAL_SSH_HOST` 和 `TERMINAL_SSH_USER` 都必须设置。如果缺少任一项,Hermes 会记录清晰的错误。\n- **Modal** —— 需要 `MODAL_TOKEN_ID` 环境变量或 `~/.modal.toml`。运行 `hermes doctor` 检查。\n- **Daytona** —— 需要 `DAYTONA_API_KEY`。Daytona SDK 处理服务器 URL 配置。\n- **Singularity** —— 需要 `$PATH` 中有 `apptainer` 或 `singularity`。HPC 集群上常见。\n\n如有疑问,将 `terminal.backend` 设回 `local` 并首先验证命令在那里运行。\n\n### 拆卸时远程到宿主文件同步\n\n对于 **SSH**、**Modal** 和 **Daytona** 后端(agent 的工作树位于与运行 Hermes 的宿主不同的机器上),Hermes 跟踪 agent 在远程沙箱中触及的文件,并在会话拆卸/沙箱清理时,将修改的文件**同步回宿主**,存放在 `~/.hermes/cache/remote-syncs/<session-id>/` 下。\n\n- 触发时机:会话关闭、`/new`、`/reset`、gateway 消息超时、子 agent 使用远程后端时 `delegate_task` 子 agent 完成。\n- 覆盖 agent 修改的整个树,而不仅仅是它明确打开的文件。添加、编辑和删除都会被捕获。\n- 远程沙箱可能在您查找时已被拆除;本地 `~/.hermes/cache/remote-syncs/…` 副本是 agent 更改内容的权威记录。\n- 大型二进制输出(模型检查点、原始数据集)按大小限制 —— 同步跳过超过 `file_sync_max_mb`(默认 `100`)的文件。如果您期望更大的工件返回,请调高该值。\n\n```yaml\nterminal:\n file_sync_max_mb: 100 # 默认 —— 同步最大 100 MB 的文件\n file_sync_enabled: true # 默认 —— 设为 false 可完全跳过同步\n```\n\n这是从会话结束后被销毁的临时云沙箱中恢复结果的方式,无需告诉 agent 显式地 `scp` 或 `modal volume put` 每个工件。\n\n### Docker 卷挂载\n\n使用 Docker 后端时,`docker_volumes` 允许您与容器共享宿主目录。每个条目使用标准 Docker `-v` 语法:`host_path:container_path[:options]`。\n\n```yaml\nterminal:\n backend: docker\n docker_volumes:\n - \"/home/user/projects:/workspace/projects\" # 读写(默认)\n - \"/home/user/datasets:/data:ro\" # 只读\n - \"/home/user/.hermes/cache/documents:/output\" # Gateway 可见的导出\n```\n\n适用于:\n- **向 agent 提供文件**(数据集、配置、参考代码)\n- **从 agent 接收文件**(生成的代码、报告、导出)\n- **共享工作区**,您和 agent 都访问相同的文件\n\n如果您使用消息 gateway 并希望 agent 通过 `MEDIA:/...` 发送生成的文件,建议使用专用的宿主可见导出挂载,例如 `/home/user/.hermes/cache/documents:/output`。\n\n- 在 Docker 中将文件写入 `/output/...`\n- 在 `MEDIA:` 中发出**宿主路径**,例如:`MEDIA:/home/user/.hermes/cache/documents/report.txt`\n- **不要**发出 `/workspace/...` 或 `/output/...`,除非该确切路径在宿主上对 gateway 进程也存在\n\n:::warning\nYAML 重复键会静默覆盖之前的键。如果您已有 `docker_volumes:` 块,请将新挂载合并到同一列表中,而不是在文件后面再添加一个 `docker_volumes:` 键。\n:::\n\n也可以通过环境变量设置:`TERMINAL_DOCKER_VOLUMES='[\"/host:/container\"]'`(JSON 数组)。\n\n### Docker 凭据转发\n\n默认情况下,Docker 终端会话不继承任意宿主凭据。如果您需要在容器内使用特定 token,请将其添加到 `terminal.docker_forward_env`。\n\n```yaml\nterminal:\n backend: docker\n docker_forward_env:\n - \"GITHUB_TOKEN\"\n - \"NPM_TOKEN\"\n```\n\nHermes 首先从您当前的 shell 解析每个列出的变量,然后回退到通过 `hermes config set` 保存的 `~/.hermes/.env`。\n\n:::warning\n`docker_forward_env` 中列出的任何内容都会对容器内运行的命令可见。只转发您愿意暴露给终端会话的凭据。\n:::\n\n### 以宿主用户身份运行容器\n\n默认情况下,Docker 容器以 `root`(UID 0)身份运行。在 `/workspace` 或其他绑定挂载中创建的文件在宿主上归 root 所有,因此会话结束后您必须 `sudo chown` 才能从宿主编辑器编辑它们。`terminal.docker_run_as_host_user` 标志解决了这个问题:\n\n```yaml\nterminal:\n backend: docker\n docker_run_as_host_user: true # 默认:false\n```\n\n启用后,Hermes 将 `--user $(id -u):$(id -g)` 附加到 `docker run` 命令,使写入绑定挂载目录(`/workspace`、`/root`、`docker_volumes` 中的任何内容)的文件归您的宿主用户所有,而非 root。权衡:容器将无法再 `apt install` 或写入 `/root/.npm` 等 root 拥有的路径 —— 如果您同时需要这两者,请使用 `HOME` 归非 root 用户所有的基础镜像(或在镜像构建时添加所需工具)。\n\n保持 `false`(默认)以获得向后兼容的行为。当您的工作流主要是\"编辑挂载的宿主文件\"且厌倦了 `sudo chown -R` 时,请开启此选项。\n\n### 可选:将启动目录挂载到 `/workspace`\n\nDocker 沙箱默认保持隔离。Hermes **不会**将您当前的宿主工作目录传入容器,除非您明确选择加入。\n\n在 `config.yaml` 中启用:\n\n```yaml\nterminal:\n backend: docker\n docker_mount_cwd_to_workspace: true\n```\n\n启用后:\n- 如果您从 `~/projects/my-app` 启动 Hermes,该宿主目录将绑定挂载到 `/workspace`\n- Docker 后端从 `/workspace` 开始\n- 文件工具和终端命令都能看到相同的挂载项目\n\n禁用时,`/workspace` 保持沙箱所有,除非您通过 `docker_volumes` 显式挂载内容。\n\n安全权衡:\n- `false` 保留沙箱边界\n- `true` 使沙箱直接访问您启动 Hermes 的目录\n\n仅在您有意希望容器处理实时宿主文件时才选择加入。\n\n### 持久 Shell\n\n默认情况下,每条终端命令在其自己的子进程中运行 —— 工作目录、环境变量和 shell 变量在命令之间重置。启用**持久 shell** 后,单个长期运行的 bash 进程在 `execute()` 调用之间保持存活,使状态在命令之间保持。\n\n这对 **SSH 后端**最有用,它还消除了每条命令的连接开销。持久 shell **对 SSH 默认启用**,对本地后端禁用。\n\n```yaml\nterminal:\n persistent_shell: true # 默认 —— 为 SSH 启用持久 shell\n```\n\n禁用:\n\n```bash\nhermes config set terminal.persistent_shell false\n```\n\n**跨命令保持的内容:**\n- 工作目录(`cd /tmp` 对下一条命令生效)\n- 导出的环境变量(`export FOO=bar`)\n- Shell 变量(`MY_VAR=hello`)\n\n**优先级:**\n\n| 级别 | 变量 | 默认值 |\n|-------|----------|---------|\n| 配置 | `terminal.persistent_shell` | `true` |\n| SSH 覆盖 | `TERMINAL_SSH_PERSISTENT` | 遵循配置 |\n| Local 覆盖 | `TERMINAL_LOCAL_PERSISTENT` | `false` |\n\n每个后端的环境变量具有最高优先级。如果您也想在本地后端使用持久 shell:\n\n```bash\nexport TERMINAL_LOCAL_PERSISTENT=true\n```\n\n:::note\n需要 `stdin_data` 或 sudo 的命令会自动回退到单次模式,因为持久 shell 的 stdin 已被 IPC 协议占用。\n:::\n\n有关每个后端的详细信息,请参阅[代码执行](features/code-execution.md)和 [README 的终端部分](features/tools.md)。\n\n## 技能设置\n\n技能可以通过其 SKILL.md frontmatter 声明自己的配置设置。这些是非机密值(路径、偏好、域设置),存储在 `config.yaml` 的 `skills.config` 命名空间下。\n\n```yaml\nskills:\n config:\n myplugin:\n path: ~/myplugin-data # 示例 —— 每个技能定义自己的键\n```\n\n**技能设置的工作原理:**\n\n- `hermes config migrate` 扫描所有已启用的技能,找到未配置的设置,并提供提示\n- `hermes config show` 在\"技能设置\"下显示所有技能设置及其所属技能\n- 技能加载时,其解析的配置值会自动注入到技能上下文中\n\n**手动设置值:**\n\n```bash\nhermes config set skills.config.myplugin.path ~/myplugin-data\n```\n\n有关在您自己的技能中声明配置设置的详细信息,请参阅[创建技能 — 配置设置](/developer-guide/creating-skills#config-settings-configyaml)。\n\n### Agent 创建技能写入的守卫\n\n当 agent 使用 `skill_manage` 创建、编辑、修补或删除技能时,Hermes 可以选择扫描新/更新的内容以查找危险关键字模式(凭据收集、明显的 prompt 注入、数据外泄指令)。扫描器**默认关闭** —— 合法触及 `~/.ssh/` 或提及 `$OPENAI_API_KEY` 的真实 agent 工作流触发启发式规则过于频繁。如果您希望扫描器在 agent 的技能写入落地前提示您,请重新开启:\n\n```yaml\nskills:\n guard_agent_created: true # 默认:false\n```\n\n开启后,任何被标记的 `skill_manage` 写入都会以审批提示的形式出现,并附带扫描器的理由。接受的写入落地;拒绝的写入向 agent 返回解释性错误。\n\n## 内存配置\n\n```yaml\nmemory:\n memory_enabled: true\n user_profile_enabled: true\n memory_char_limit: 2200 # ~800 tokens\n user_char_limit: 1375 # ~500 tokens\n```\n\n## 文件读取安全\n\n控制单次 `read_file` 调用可以返回多少内容。超过限制的读取将被拒绝,并向 agent 返回错误,提示使用 `offset` 和 `limit` 读取较小范围。这可以防止单次读取压缩的 JS 包或大型数据文件时淹没上下文窗口。\n\n```yaml\nfile_read_max_chars: 100000 # 默认 —— ~25-35K tokens\n```\n\n如果您使用具有大上下文窗口的模型并经常读取大文件,请调高此值。对于小上下文模型,请降低以保持读取高效:\n\n```yaml\n# 大上下文模型(200K+)\nfile_read_max_chars: 200000\n\n# 小型本地模型(16K 上下文)\nfile_read_max_chars: 30000\n```\n\nAgent 还会自动去重文件读取 —— 如果同一文件区域被读取两次且文件未更改,则返回轻量级存根而不是重新发送内容。这在上下文压缩后重置,以便 agent 在内容被摘要后可以重新读取文件。\n\n## 工具输出截断限制\n\n三个相关的上限控制工具在 Hermes 截断之前可以返回多少原始输出:\n\n```yaml\ntool_output:\n max_bytes: 50000 # 终端输出上限(字符)\n max_lines: 2000 # read_file 分页上限\n max_line_length: 2000 # read_file 行号视图中的每行上限\n```\n\n- **`max_bytes`** —— 当 `terminal` 命令产生超过此字符数的合并 stdout/stderr 时,Hermes 保留前 40% 和后 60%,并在中间插入 `[OUTPUT TRUNCATED]` 通知。默认 `50000`(典型分词器约 12-15K tokens)。\n- **`max_lines`** —— 单次 `read_file` 调用的 `limit` 参数上限。超过此值的请求将被截断,以防单次读取淹没上下文窗口。默认 `2000`。\n- **`max_line_length`** —— `read_file` 发出行号视图时应用的每行上限。超过此长度的行将被截断为此字符数,后跟 `... [truncated]`。默认 `2000`。\n\n对于具有大上下文窗口且每次调用可以承受更多原始输出的模型,请调高限制。对于小上下文模型,请降低以保持工具结果紧凑:\n\n```yaml\n# 大上下文模型(200K+)\ntool_output:\n max_bytes: 150000\n max_lines: 5000\n\n# 小型本地模型(16K 上下文)\ntool_output:\n max_bytes: 20000\n max_lines: 500\n```\n\n## 全局工具集禁用\n\n要在 CLI 和每个 gateway 平台上统一禁用特定工具集,请在 `agent.disabled_toolsets` 下列出其名称:\n\n```yaml\nagent:\n disabled_toolsets:\n - memory # 隐藏内存工具 + MEMORY_GUIDANCE 注入\n - web # 任何地方都不使用 web_search / web_extract\n```\n\n这在每个平台的工具配置(由 `hermes tools` 写入的 `platform_toolsets`)**之后**应用,因此此处列出的工具集始终被删除 —— 即使平台的已保存配置仍然列出它。当您希望有一个\"到处关闭 X\"的单一开关而不是编辑 `hermes tools` UI 中的 15+ 个平台行时,请使用此选项。\n\n留空列表或省略键不会产生任何效果。\n\n## Git Worktree 隔离\n\n启用隔离的 git worktree,以便在同一仓库上并行运行多个 agent:\n\n```yaml\nworktree: true # 始终创建 worktree(与 hermes -w 相同)\n# worktree: false # 默认 —— 仅在传递 -w 标志时\n```\n\n启用后,每个 CLI 会话在 `.worktrees/` 下创建一个带有自己分支的新 worktree。Agent 可以编辑文件、提交、推送和创建 PR,而不会相互干扰。干净的 worktree 在退出时删除;脏的 worktree 保留以供手动恢复。\n\n您还可以通过仓库根目录中的 `.worktreeinclude` 列出要复制到 worktree 的 gitignore 文件:\n\n```\n# .worktreeinclude\n.env\n.venv/\nnode_modules/\n```\n\n## 上下文压缩\n\nHermes 自动压缩长对话以保持在模型的上下文窗口内。压缩摘要器是一个单独的 LLM 调用 —— 您可以将其指向任何 provider 或端点。\n\n所有压缩设置都在 `config.yaml` 中(无环境变量)。\n\n### 完整参考\n\n```yaml\ncompression:\n enabled: true # 开启/关闭压缩\n threshold: 0.50 # 在上下文限制的此百分比时压缩\n target_ratio: 0.20 # 保留为最近尾部的阈值分数\n protect_last_n: 20 # 保持未压缩的最少最近消息数\n hygiene_hard_message_limit: 5000 # Gateway 安全阀 —— 见下文\n\n# 摘要模型/provider 在 auxiliary: 下配置:\nauxiliary:\n compression:\n model: \"\" # 空 = 使用主聊天模型。覆盖为例如 \"google/gemini-3-flash-preview\" 以获得更便宜/更快的压缩。\n provider: \"auto\" # Provider:\"auto\"、\"openrouter\"、\"nous\"、\"codex\"、\"main\" 等\n base_url: null # 自定义 OpenAI 兼容端点(覆盖 provider)\n```\n\n:::info 旧版配置迁移\n带有 `compression.summary_model`、`compression.summary_provider` 和 `compression.summary_base_url` 的旧版配置在首次加载时自动迁移到 `auxiliary.compression.*`(配置版本 17)。无需手动操作。\n:::\n\n`hygiene_hard_message_limit` 是仅限 gateway 的**预压缩安全阀**。它的存在是为了打破一个死循环:当超大会话的 API 调用持续断开时,gateway 永远收不到 token 使用数据,基于 token 的阈值因此无法触发,于是 transcript 持续增长、断开愈发严重。这个基于消息数的下限仅凭消息数量触发(无论 API 是否失败,消息数始终已知),强制压缩以恢复会话。默认 `5000` —— 远高于任何正常会话,包括做数千次短轮次的大上下文(1M+)模型,它们早就在 token 阈值处压缩了。对于异常平台可调得更高;要强制更积极的压缩则调低。在运行中的 gateway 上编辑此值将在下一条消息时生效(见下文)。\n\n:::tip Gateway 热重载压缩和上下文长度\n从最近的版本开始,在运行中的 gateway 上编辑 `config.yaml` 中的 `model.context_length` 或任何 `compression.*` 键将在下一条消息时生效 —— 无需 gateway 重启、`/reset` 或会话轮换。缓存的 agent 签名包含这些键,因此 gateway 在检测到更改时会透明地重建 agent。API 密钥和工具/技能配置仍需要通常的重载路径。\n:::\n\n### 常见设置\n\n**默认(自动检测)—— 无需配置:**\n```yaml\ncompression:\n enabled: true\n threshold: 0.50\n```\n使用您的主 provider 和主模型。如果您希望在比主聊天模型更便宜的模型上进行压缩,请覆盖每任务(例如 `auxiliary.compression.provider: openrouter` + `model: google/gemini-2.5-flash`)。\n\n**强制特定 provider**(基于 OAuth 或 API 密钥):\n```yaml\nauxiliary:\n compression:\n provider: nous\n model: gemini-3-flash\n```\n适用于任何 provider:`nous`、`openrouter`、`codex`、`anthropic`、`main` 等。\n\n**自定义端点**(自托管、Ollama、zai、DeepSeek 等):\n```yaml\nauxiliary:\n compression:\n model: glm-4.7\n base_url: https://api.z.ai/api/coding/paas/v4\n```\n指向自定义 OpenAI 兼容端点。使用 `OPENAI_API_KEY` 进行认证。\n\n### 三个旋钮的交互方式\n\n| `auxiliary.compression.provider` | `auxiliary.compression.base_url` | 结果 |\n|---------------------|---------------------|--------|\n| `auto`(默认) | 未设置 | 自动检测最佳可用 provider |\n| `nous` / `openrouter` / 等 | 未设置 | 强制使用该 provider,使用其认证 |\n| 任意 | 已设置 | 直接使用自定义端点(忽略 provider) |\n\n:::warning 摘要模型上下文长度要求\n摘要模型**必须**具有至少与您的主 agent 模型一样大的上下文窗口。压缩器将对话的完整中间部分发送给摘要模型 —— 如果该模型的上下文窗口小于主模型的,摘要调用将因上下文长度错误而失败。发生这种情况时,中间轮次将**在没有摘要的情况下被丢弃**,静默丢失对话上下文。如果您覆盖模型,请验证其上下文长度满足或超过您的主模型。\n:::\n\n## 上下文引擎\n\n上下文引擎控制在接近模型 token 限制时如何管理对话。内置的 `compressor` 引擎使用有损摘要(参见[上下文压缩](/developer-guide/context-compression-and-caching))。插件引擎可以用替代策略替换它。\n\n```yaml\ncontext:\n engine: \"compressor\" # 默认 —— 内置有损摘要\n```\n\n使用插件引擎(例如,用于无损上下文管理的 LCM):\n\n```yaml\ncontext:\n engine: \"lcm\" # 必须与插件名称匹配\n```\n\n插件引擎**永远不会自动激活** —— 您必须将 `context.engine` 显式设置为插件名称。可用引擎可以通过 `hermes plugins` → Provider Plugins → Context Engine 浏览和选择。\n\n有关内存插件的类似单选系统,请参阅[内存 Providers](/user-guide/features/memory-providers)。\n\n## 迭代预算\n\n当 agent 在处理具有许多工具调用的复杂任务时,它可能会耗尽其迭代预算(默认:500 轮)。Hermes **不会**在任务中途注入压力警告 —— 早期版本会在预算达到 70%/90% 时警告模型,这会导致模型过早放弃复杂任务,该机制已于 2026 年 4 月移除。\n\n取而代之的是,当预算真正耗尽(500/500)时,Hermes 注入一条消息要求模型收尾,并允许一次**宽限调用**以便其给出最终响应。如果该宽限调用仍未产生文本,则会要求 agent 总结已完成的工作。\n\n```yaml\nagent:\n max_turns: 500 # 每次对话轮次的最大迭代次数(默认:500)\n api_max_retries: 3 # 回退启动前每个 provider 的重试次数(默认:3)\n```\n\n当迭代预算完全耗尽时,CLI 向用户显示通知:`⚠ Iteration budget reached (500/500) — response may be incomplete`。\n\n`agent.api_max_retries` 控制 Hermes 在回退 provider 切换启动**之前**对瞬时错误(速率限制、连接断开、5xx)重试 provider API 调用的次数。默认为 `3` —— 总共四次尝试。如果您配置了[回退 providers](/user-guide/features/fallback-providers) 并希望更快地故障转移,请将其降至 `0`,这样主 provider 上的第一个瞬时错误会立即切换到回退,而不是对不稳定的端点进行重试。\n\n### API 超时\n\nHermes 对流式传输有单独的超时层,以及用于非流式调用的陈旧检测器。陈旧检测器仅在您将其保留为隐式默认值时才会自动调整本地 provider。\n\n| 超时 | 默认值 | 本地 providers | 配置/环境变量 |\n|---------|---------|----------------|--------------|\n| Socket 读取超时 | 120s | 自动提升至 1800s | `HERMES_STREAM_READ_TIMEOUT` |\n| 陈旧流检测 | 180s | 自动禁用 | `HERMES_STREAM_STALE_TIMEOUT` |\n| 陈旧非流检测 | 300s | 保持隐式时自动禁用 | `providers.<id>.stale_timeout_seconds` 或 `HERMES_API_CALL_STALE_TIMEOUT` |\n| API 调用(非流式) | 1800s | 不变 | `providers.<id>.request_timeout_seconds` / `timeout_seconds` 或 `HERMES_API_TIMEOUT` |\n\n**Socket 读取超时**控制 httpx 等待 provider 下一个数据块的时间。本地 LLM 在大上下文上预填充可能需要几分钟才能产生第一个 token,因此当 Hermes 检测到本地端点时,会将此值提升至 30 分钟。如果您显式设置 `HERMES_STREAM_READ_TIMEOUT`,无论端点检测如何,始终使用该值。\n\n**陈旧流检测**终止接收 SSE 保活 ping 但没有实际内容的连接。对于本地 providers,这完全禁用,因为它们在预填充期间不发送保活 ping。\n\n**陈旧非流检测**终止长时间没有响应的非流式调用。默认情况下,Hermes 在本地端点上禁用此功能,以避免长时间预填充期间的误报。如果您显式设置 `providers.<id>.stale_timeout_seconds`、`providers.<id>.models.<model>.stale_timeout_seconds` 或 `HERMES_API_CALL_STALE_TIMEOUT`,即使在本地端点上也会遵守该显式值。\n\n## 上下文压力警告\n\n与迭代预算压力分开,上下文压力跟踪对话距**压缩阈值**有多近 —— 即上下文压缩触发以摘要旧消息的点。这有助于您和 agent 了解对话何时变长。\n\n| 进度 | 级别 | 发生的事情 |\n|----------|-------|-------------|\n| **≥ 60%** 到阈值 | 信息 | CLI 显示青色进度条;gateway 发送信息通知 |\n| **≥ 85%** 到阈值 | 警告 | CLI 显示粗体黄色进度条;gateway 警告压缩即将发生 |\n\n在 CLI 中,上下文压力在工具输出流中显示为进度条:\n\n```\n ◐ context ████████████░░░░░░░░ 62% to compaction 48k threshold (50%) · approaching compaction\n```\n\n在消息平台上,发送纯文本通知:\n\n```\n◐ Context: ████████████░░░░░░░░ 62% to compaction (threshold: 50% of window).\n```\n\n如果自动压缩被禁用,警告会告诉您上下文可能被截断。\n\n上下文压力是自动的 —— 无需配置。它纯粹作为面向用户的通知触发,不修改消息流或向模型上下文注入任何内容。\n\n## 凭据池策略\n\n当您为同一 provider 拥有多个 API 密钥或 OAuth token 时,配置轮换策略:\n\n```yaml\ncredential_pool_strategies:\n openrouter: round_robin # 均匀循环使用密钥\n anthropic: least_used # 始终选择使用最少的密钥\n```\n\n选项:`fill_first`(默认)、`round_robin`、`least_used`、`random`。完整文档请参阅[凭据池](/user-guide/features/credential-pools)。\n\n## Prompt 缓存\n\n当活跃 provider 支持时,Hermes 自动开启跨会话 prompt 缓存 —— 无需用户配置。\n\n对于**原生 Anthropic**、**OpenRouter** 和 **Nous Portal** 上的 Claude,Hermes 在系统提示词和技能块上附加带有 1 小时 TTL(`ttl: \"1h\"`)的 `cache_control` 断点。在新鲜的一小时内首次发送时按完整输入费率计费;同一小时内任何会话的后续发送以折扣缓存读取费率从缓存中提取。这意味着系统提示词、加载的技能内容以及任何长上下文包含的早期部分在第一个小时内跨 `hermes` 会话和分叉子 agent 被重用。\n\nQwen Cloud(阿里巴巴 DashScope)上游将缓存 TTL 限制为 5 分钟,因此 Hermes 在那里使用 5 分钟断点 TTL。其他通过第三方的 Claude 路径(AWS Bedrock、Azure Foundry)回退到 provider 自己的缓存默认值。xAI Grok 使用单独的会话固定对话 ID 机制 —— 参阅 [xAI prompt 缓存](/integrations/providers#xai-grok--responses-api--prompt-caching)。\n\n不存在禁用此功能的旋钮 —— 缓存始终开启,即使在单轮对话中也能节省费用,因为仅系统提示词就占输入 token 数的相当大比例。\n\n## 辅助模型\n\nHermes 使用\"辅助\"模型处理图像分析、网页摘要、浏览器截图分析、会话标题生成和上下文压缩等附带任务。默认情况下(`auxiliary.*.provider: \"auto\"`),Hermes 将每个辅助任务路由到您的**主聊天模型** —— 与您在 `hermes model` 中选择的相同 provider/模型。您无需配置任何内容即可开始,但请注意,在昂贵的推理模型(Opus、MiniMax M2.7 等)上,辅助任务会增加显著成本。如果您希望无论主模型如何都使用便宜且快速的附带任务,请显式设置 `auxiliary.<task>.provider` 和 `auxiliary.<task>.model`(例如,在 OpenRouter 上使用 Gemini Flash 进行视觉和网页提取)。\n\n:::note 为什么 \"auto\" 使用您的主模型\n早期版本将聚合器用户(OpenRouter、Nous Portal)分流到便宜的 provider 端默认值。这令人惊讶 —— 付费购买聚合器订阅的用户会看到不同的模型处理其辅助流量。`auto` 现在对所有人使用主模型,`config.yaml` 中的每任务覆盖仍然优先(见下方[完整辅助配置参考](#full-auxiliary-config-reference))。\n:::\n\n### 交互式配置辅助模型\n\n无需手动编辑 YAML,运行 `hermes model` 并从菜单中选择**\"配置辅助模型\"**。您将获得交互式的每任务选择器:\n\n```\n$ hermes model\n→ Configure auxiliary models\n\n[ ] vision currently: auto / main model\n[ ] web_extract currently: auto / main model\n[ ] title_generation currently: openrouter / google/gemini-3-flash-preview\n[ ] compression currently: auto / main model\n[ ] approval currently: auto / main model\n[ ] triage_specifier currently: auto / main model\n[ ] kanban_decomposer currently: auto / main model\n[ ] profile_describer currently: auto / main model\n```\n\n选择任务,选择 provider(OAuth 流程打开浏览器;API 密钥 provider 提示输入),选择模型。更改持久化到 `config.yaml` 中的 `auxiliary.<task>.*`。与主模型选择器相同的机制 —— 无需学习额外语法。\n\n### 视频教程\n\n<div style={{position: 'relative', width: '100%', aspectRatio: '16 / 9', marginBottom: '1.5rem'}}>\n <iframe\n src=\"https://www.youtube.com/embed/NoF-YajElIM\"\n title=\"Hermes Agent — Auxiliary Models Tutorial\"\n style={{position: 'absolute', top: 0, left: 0, width: '100%', height: '100%', border: 0}}\n allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\"\n allowFullScreen\n />\n</div>\n\n### 通用配置模式\n\nHermes 中的每个模型槽位 —— 辅助任务、压缩、回退 —— 使用相同的三个旋钮:\n\n| 键 | 作用 | 默认值 |\n|-----|-------------|---------|\n| `provider` | 用于认证和路由的 provider | `\"auto\"` |\n| `model` | 请求的模型 | provider 的默认值 |\n| `base_url` | 自定义 OpenAI 兼容端点(覆盖 provider) | 未设置 |\n\n当设置 `base_url` 时,Hermes 忽略 provider 并直接调用该端点(使用 `api_key` 或 `OPENAI_API_KEY` 进行认证)。当仅设置 `provider` 时,Hermes 使用该 provider 的内置认证和基础 URL。\n\n辅助任务的可用 providers:`auto`、`main`,以及[provider 注册表](/reference/environment-variables)中的任何 provider —— `openrouter`、`nous`、`openai-codex`、`copilot`、`copilot-acp`、`anthropic`、`gemini`、`qwen-oauth`、`zai`、`kimi-coding`、`kimi-coding-cn`、`minimax`、`minimax-cn`、`minimax-oauth`、`deepseek`、`nvidia`、`xai`、`xai-oauth`、`ollama-cloud`、`alibaba`、`bedrock`、`huggingface`、`arcee`、`xiaomi`、`kilocode`、`opencode-zen`、`opencode-go`、`azure-foundry` —— 或您 `custom_providers` 列表中任何命名的自定义 provider(例如 `provider: \"beans\"`)。\n\n:::tip MiniMax OAuth\n`minimax-oauth` 通过浏览器 OAuth 登录(无需 API 密钥)。运行 `hermes model` 并选择 **MiniMax (OAuth)** 进行认证。辅助任务自动使用 `MiniMax-M2.7-highspeed`。参阅 [MiniMax OAuth 指南](../guides/minimax-oauth.md)。\n:::\n\n:::tip xAI Grok OAuth\n`xai-oauth` 通过浏览器 OAuth 为 SuperGrok 和 X Premium+ 订阅者登录(无需 API 密钥)。运行 `hermes model` 并选择 **xAI Grok OAuth (SuperGrok / Premium+)** 进行认证。相同的 OAuth token 可重用于每个直接到 xAI 的接口(聊天、辅助任务、TTS、图像生成、视频生成、转录)。参阅 [xAI Grok OAuth 指南](../guides/xai-grok-oauth.md),如果 Hermes 在远程主机上,请参阅 [SSH/远程主机上的 OAuth](../guides/oauth-over-ssh.md)。\n:::\n\n:::warning `\"main\"` 仅用于辅助任务\n`\"main\"` provider 选项表示\"使用我的主 agent 使用的任何 provider\" —— 它仅在 `auxiliary:`、`compression:` 和 `fallback_model:` 配置中有效。它**不是**顶级 `model.provider` 设置的有效值。如果您使用自定义 OpenAI 兼容端点,请在 `model:` 部分设置 `provider: custom`。所有主模型 provider 选项请参阅 [AI Providers](/integrations/providers)。\n:::\n\n### 完整辅助配置参考\n\n```yaml\nauxiliary:\n # 图像分析(vision_analyze 工具 + 浏览器截图)\n vision:\n provider: \"auto\" # \"auto\"、\"openrouter\"、\"nous\"、\"codex\"、\"main\" 等\n model: \"\" # 例如 \"openai/gpt-4o\"、\"google/gemini-2.5-flash\"\n base_url: \"\" # 自定义 OpenAI 兼容端点(覆盖 provider)\n api_key: \"\" # base_url 的 API 密钥(回退到 OPENAI_API_KEY)\n timeout: 120 # 秒 —— LLM API 调用超时;视觉负载需要宽裕的超时\n download_timeout: 30 # 秒 —— 图像 HTTP 下载;慢速连接请增加\n\n # 网页摘要 + 浏览器页面文本提取\n web_extract:\n provider: \"auto\"\n model: \"\" # 例如 \"google/gemini-2.5-flash\"\n base_url: \"\"\n api_key: \"\"\n timeout: 360 # 秒(6 分钟)—— 每次尝试的 LLM 摘要\n\n # 危险命令审批分类器\n approval:\n provider: \"auto\"\n model: \"\"\n base_url: \"\"\n api_key: \"\"\n timeout: 30 # 秒\n\n # 上下文压缩超时(与 compression.* 配置分开)\n compression:\n timeout: 120 # 秒 —— 压缩摘要长对话,需要更多时间\n # fallback_chain: # 可选 —— 发生速率限制/连接故障时尝试的 provider\n # - provider: nous\n # model: deepseek/deepseek-chat\n # - provider: openrouter\n # model: google/gemini-2.5-flash\n # base_url: \"\"\n # api_key: \"\"\n\n # 技能中心 —— 技能匹配和搜索\n skills_hub:\n provider: \"auto\"\n model: \"\"\n base_url: \"\"\n api_key: \"\"\n timeout: 30\n\n # MCP 工具调度\n mcp:\n provider: \"auto\"\n model: \"\"\n base_url: \"\"\n api_key: \"\"\n timeout: 30\n\n # Kanban 分类规格说明器 —— `hermes kanban specify <id>`(或\n # 仪表板上 Triage 列卡片的 ✨ Specify 按钮)使用此\n # 槽位将单行描述扩展为具体规格并将\n # 任务提升到 `todo`。便宜快速的模型在这里效果很好;规格扩展\n # 很短,不需要推理深度。\n triage_specifier:\n provider: \"auto\"\n model: \"\"\n base_url: \"\"\n api_key: \"\"\n timeout: 120\n```\n\n:::tip\n每个辅助任务都有可配置的 `timeout`(秒)。默认值:vision 120s、web_extract 360s、approval 30s、compression 120s。如果您为辅助任务使用慢速本地模型,请增加这些值。Vision 还有单独的 `download_timeout`(默认 30s)用于 HTTP 图像下载 —— 对于慢速连接或自托管图像服务器,请增加此值。\n:::\n\n:::info\n上下文压缩有自己的 `compression:` 块用于阈值,以及 `auxiliary.compression:` 块用于模型/provider 设置 —— 参阅上方的[上下文压缩](#context-compression)。主备用链使用顶层的 `fallback_providers:` 列表 —— 参阅[备用提供商](/integrations/providers#fallback-providers)。三者都遵循相同的 provider/model/base_url 模式。\n:::\n\n### 辅助任务的每任务回退链\n\n每个辅助任务都可以选择性地定义一个 `fallback_chain` —— 一个 provider/model 条目列表,当主要辅助 provider 因速率限制、网络连接问题或付费限制而失败时,Hermes 会尝试使用该列表:\n\n```yaml\nauxiliary:\n compression:\n provider: openrouter\n model: openai/gpt-4o-mini\n fallback_chain:\n - provider: nous\n model: deepseek/deepseek-chat\n - provider: openrouter\n model: google/gemini-2.5-flash\n```\n\n当主要辅助 provider(`openrouter` / `openai/gpt-4o-mini`)返回速率限制、连接超时或需要付费错误时,Hermes 将依次遍历 `fallback_chain`。它会跳过 provider 与已失败 provider 相同的条目,并尝试每个剩余条目,直到有一个成功或该链耗尽。如果所有回退都失败,Hermes 会回退到主 agent 模型作为最终的安全网。\n\n每个条目支持与任何辅助任务配置相同的三个旋钮:\n\n| 键 | 描述 |\n|-----|-------------|\n| `provider` | Provider 名称(`nous`、`openrouter`、`anthropic`、`gemini`、`main` 等) |\n| `model` | 该 provider 的模型名称 |\n| `base_url` | (可选)自定义 OpenAI 兼容端点 |\n\n`fallback_chain` 适用于任何辅助任务 —— `compression`、`vision`、`web_extract`、`approval`、`skills_hub`、`mcp` 等。\n\n### OpenRouter 路由和辅助任务的 Pareto Code\n\n当辅助任务解析到 OpenRouter(显式或通过 `provider: \"main\"` 而您的主 agent 在 OpenRouter 上)时,主 agent 的 `provider_routing` 和 `openrouter.min_coding_score` 设置**不会传播** —— 按设计,每个辅助任务是独立的。要为特定辅助任务设置 OpenRouter provider 偏好或使用 [Pareto Code 路由器](/integrations/providers#openrouter-pareto-code-router),请通过 `extra_body` 按任务设置:\n\n```yaml\nauxiliary:\n compression:\n provider: openrouter\n model: openrouter/pareto-code # 为此任务使用 Pareto Code 路由器\n extra_body:\n provider: # OpenRouter provider 路由偏好\n order: [anthropic, google] # 按此顺序尝试这些 providers\n sort: throughput # 或 \"price\" | \"latency\"\n # only: [anthropic] # 限制到特定 provider\n # ignore: [deepinfra] # 排除特定 providers\n plugins: # OpenRouter Pareto Code 路由器旋钮\n - id: pareto-router\n min_coding_score: 0.5 # 0.0–1.0;越高 = 更强的编码能力\n```\n\n形状与 OpenRouter 在聊天补全请求体中接受的内容一致。Hermes 原样转发整个 `extra_body`,因此 [openrouter.ai/docs](https://openrouter.ai/docs) 中记录的任何其他 OpenRouter 请求体字段都以相同方式工作。\n\n### 更改视觉模型\n\n使用 GPT-4o 而非 Gemini Flash 进行图像分析:\n\n```yaml\nauxiliary:\n vision:\n model: \"openai/gpt-4o\"\n```\n\n或通过环境变量(在 `~/.hermes/.env` 中):\n\n```bash\nAUXILIARY_VISION_MODEL=openai/gpt-4o\n```\n\n### Provider 选项\n\n这些选项适用于**辅助任务配置**(`auxiliary:`、`compression:`、`fallback_model:`),而非您的主 `model.provider` 设置。\n\n| Provider | 描述 | 要求 |\n|----------|-------------|-------------|\n| `\"auto\"` | 最佳可用(默认)。Vision 尝试 OpenRouter → Nous → Codex。 | — |\n| `\"openrouter\"` | 强制 OpenRouter —— 路由到任何模型(Gemini、GPT-4o、Claude 等) | `OPENROUTER_API_KEY` |\n| `\"nous\"` | 强制 Nous Portal | `hermes auth` |\n| `\"codex\"` | 强制 Codex OAuth(ChatGPT 账户)。支持视觉(gpt-5.3-codex)。 | `hermes model` → Codex |\n| `\"minimax-oauth\"` | 强制 MiniMax OAuth(浏览器登录,无需 API 密钥)。辅助任务使用 MiniMax-M2.7-highspeed。 | `hermes model` → MiniMax (OAuth) |\n| `\"xai-oauth\"` | 强制 xAI Grok OAuth(SuperGrok 或 X Premium+ 订阅者的浏览器登录,无需 API 密钥)。相同的 OAuth token 涵盖聊天、TTS、图像、视频和转录。 | `hermes model` → xAI Grok OAuth (SuperGrok / Premium+) |\n| `\"main\"` | 使用您的活跃自定义/主端点。可以来自 `OPENAI_BASE_URL` + `OPENAI_API_KEY` 或通过 `hermes model` / `config.yaml` 保存的自定义端点。适用于 OpenAI、本地模型或任何 OpenAI 兼容 API。**仅限辅助任务 —— 对 `model.provider` 无效。** | 自定义端点凭据 + 基础 URL |\n\n当您希望附带任务绕过默认路由器时,主 provider 目录中的直接 API 密钥 providers 也在这里工作。配置 `GMI_API_KEY` 后,`gmi` 有效:\n\n```yaml\nauxiliary:\n compression:\n provider: \"gmi\"\n model: \"anthropic/claude-opus-4.6\"\n```\n\n对于 GMI 辅助路由,使用 GMI 的 `/v1/models` 端点返回的确切模型 ID。\n\n### 常见设置\n\n**使用直接自定义端点**(比 `provider: \"main\"` 对本地/自托管 API 更清晰):\n```yaml\nauxiliary:\n vision:\n base_url: \"http://localhost:1234/v1\"\n api_key: \"local-key\"\n model: \"qwen2.5-vl\"\n```\n\n`base_url` 优先于 `provider`,因此这是将辅助任务路由到特定端点的最明确方式。对于直接端点覆盖,Hermes 使用配置的 `api_key` 或回退到 `OPENAI_API_KEY`;它不会为该自定义端点重用 `OPENROUTER_API_KEY`。\n\n**使用 OpenAI API 密钥进行视觉:**\n```yaml\n# 在 ~/.hermes/.env 中:\n# OPENAI_BASE_URL=https://api.openai.com/v1\n# OPENAI_API_KEY=sk-...\n\nauxiliary:\n vision:\n provider: \"main\"\n model: \"gpt-4o\" # 或 \"gpt-4o-mini\" 更便宜\n```\n\n**使用 OpenRouter 进行视觉**(路由到任何模型):\n```yaml\nauxiliary:\n vision:\n provider: \"openrouter\"\n model: \"openai/gpt-4o\" # 或 \"google/gemini-2.5-flash\" 等\n```\n\n**使用 Codex OAuth**(ChatGPT Pro/Plus 账户 —— 无需 API 密钥):\n```yaml\nauxiliary:\n vision:\n provider: \"codex\" # 使用您的 ChatGPT OAuth token\n # 模型默认为 gpt-5.3-codex(支持视觉)\n```\n\n**使用 MiniMax OAuth**(浏览器登录,无需 API 密钥):\n```yaml\nmodel:\n default: MiniMax-M2.7\n provider: minimax-oauth\n base_url: https://api.minimax.io/anthropic\n```\n运行 `hermes model` 并选择 **MiniMax (OAuth)** 自动登录并设置此项。对于中国区域,基础 URL 将是 `https://api.minimaxi.com/anthropic`。完整演练请参阅 [MiniMax OAuth 指南](../guides/minimax-oauth.md)。\n\n**使用本地/自托管模型:**\n```yaml\nauxiliary:\n vision:\n provider: \"main\" # 使用您的活跃自定义端点\n model: \"my-local-model\"\n```\n\n`provider: \"main\"` 使用 Hermes 用于普通聊天的任何 provider —— 无论是命名的自定义 provider(例如 `beans`)、内置 provider(如 `openrouter`)还是旧版 `OPENAI_BASE_URL` 端点。\n\n:::tip\n如果您使用 Codex OAuth 作为主模型 provider,视觉会自动工作 —— 无需额外配置。Codex 包含在视觉的自动检测链中。\n:::\n\n:::warning\n**视觉需要多模态模型。** 如果您设置 `provider: \"main\"`,请确保您的端点支持多模态/视觉 —— 否则图像分析将失败。\n:::\n\n### 环境变量(旧版)\n\n辅助模型也可以通过环境变量配置。但是,`config.yaml` 是首选方法 —— 它更易于管理,并支持所有选项,包括 `base_url` 和 `api_key`。\n\n| 设置 | 环境变量 |\n|---------|---------------------|\n| Vision provider | `AUXILIARY_VISION_PROVIDER` |\n| Vision 模型 | `AUXILIARY_VISION_MODEL` |\n| Vision 端点 | `AUXILIARY_VISION_BASE_URL` |\n| Vision API 密钥 | `AUXILIARY_VISION_API_KEY` |\n| Web 提取 provider | `AUXILIARY_WEB_EXTRACT_PROVIDER` |\n| Web 提取模型 | `AUXILIARY_WEB_EXTRACT_MODEL` |\n| Web 提取端点 | `AUXILIARY_WEB_EXTRACT_BASE_URL` |\n| Web 提取 API 密钥 | `AUXILIARY_WEB_EXTRACT_API_KEY` |\n\n压缩和回退模型设置仅限 config.yaml。\n\n:::tip\n运行 `hermes config` 查看您当前的辅助模型设置。覆盖仅在与默认值不同时显示。\n:::\n\n## 推理努力程度\n\n控制模型在响应前进行多少\"思考\":\n\n```yaml\nagent:\n reasoning_effort: \"\" # 空 = 中等。选项:none、minimal、low、medium、high、xhigh、max、ultra\n```\n\n未设置时(默认),推理努力程度默认为\"medium\" —— 适合大多数任务的平衡级别。设置值会覆盖它 —— 更高的推理努力程度在复杂任务上提供更好的结果,但代价是更多 token 和延迟。\n\n您也可以在运行时使用 `/reasoning` 命令更改推理努力程度:\n\n```\n/reasoning # 显示当前努力程度和显示状态\n/reasoning high # 将推理努力程度设为 high\n/reasoning none # 禁用推理\n/reasoning show # 在每次响应上方显示模型思考\n/reasoning hide # 隐藏模型思考\n```\n\n## 工具使用强制\n\n某些模型偶尔会将预期操作描述为文本而不是进行工具调用(\"我会运行测试...\"而不是实际调用终端)。工具使用强制会注入系统提示词指导,引导模型实际调用工具。\n\n```yaml\nagent:\n tool_use_enforcement: \"auto\" # \"auto\" | true | false | [\"model-substring\", ...]\n```\n\n| 值 | 行为 |\n|-------|----------|\n| `\"auto\"`(默认) | 对匹配以下模型启用:`gpt`、`codex`、`gemini`、`gemma`、`grok`。对所有其他模型禁用(Claude、DeepSeek、Qwen 等)。 |\n| `true` | 始终启用,无论模型如何。如果您注意到当前模型描述操作而不是执行操作,请使用此选项。 |\n| `false` | 始终禁用,无论模型如何。 |\n| `[\"gpt\", \"codex\", \"qwen\", \"llama\"]` | 仅当模型名称包含列出的子字符串之一时启用(不区分大小写)。 |\n\n### 注入的内容\n\n启用后,系统提示词中可能会添加三层指导:\n\n1. **通用工具使用强制**(所有匹配模型)—— 指示模型立即进行工具调用而不是描述意图,持续工作直到任务完成,永远不要以未来操作的承诺结束轮次。\n\n2. **OpenAI 执行纪律**(仅限 GPT 和 Codex 模型)—— 针对 GPT 特定失败模式的额外指导:在部分结果上放弃工作、跳过先决条件查找、幻觉而不是使用工具、在未验证的情况下宣布\"完成\"。\n\n3. **Google 操作指导**(仅限 Gemini 和 Gemma 模型)—— 简洁性、绝对路径、并行工具调用和编辑前验证模式。\n\n这些对用户透明,仅影响系统提示词。已经可靠使用工具的模型(如 Claude)不需要此指导,这就是为什么 `\"auto\"` 排除它们。\n\n### 何时开启\n\n如果您使用的模型不在默认自动列表中,并注意到它经常描述它*会*做什么而不是实际去做,请设置 `tool_use_enforcement: true` 或将模型子字符串添加到列表中:\n\n```yaml\nagent:\n tool_use_enforcement: [\"gpt\", \"codex\", \"gemini\", \"grok\", \"my-custom-model\"]\n```\n\n## TTS 配置\n\n```yaml\ntts:\n provider: \"edge\" # \"edge\" | \"elevenlabs\" | \"openai\" | \"minimax\" | \"mistral\" | \"gemini\" | \"xai\" | \"neutts\"\n speed: 1.0 # 全局速度倍数(所有 provider 的回退)\n edge:\n voice: \"en-US-AriaNeural\" # 322 种声音,74 种语言\n speed: 1.0 # 速度倍数(转换为速率百分比,例如 1.5 → +50%)\n elevenlabs:\n voice_id: \"pNInz6obpgDQGcFmaJgB\"\n model_id: \"eleven_multilingual_v2\"\n openai:\n model: \"gpt-4o-mini-tts\"\n voice: \"alloy\" # alloy、echo、fable、onyx、nova、shimmer\n speed: 1.0 # 速度倍数(API 限制为 0.25–4.0)\n base_url: \"https://api.openai.com/v1\" # 覆盖 OpenAI 兼容 TTS 端点\n minimax:\n speed: 1.0 # 语音速度倍数\n # base_url: \"\" # 可选:覆盖 OpenAI 兼容 TTS 端点\n mistral:\n model: \"voxtral-mini-tts-2603\"\n voice_id: \"c69964a6-ab8b-4f8a-9465-ec0925096ec8\" # Paul - Neutral(默认)\n gemini:\n model: \"gemini-2.5-flash-preview-tts\" # 或 gemini-2.5-pro-preview-tts\n voice: \"Kore\" # 30 种预置声音:Zephyr、Puck、Kore、Enceladus 等\n xai:\n voice_id: \"eve\" # xAI TTS 声音\n language: \"en\" # ISO 639-1\n sample_rate: 24000\n bit_rate: 128000 # MP3 比特率\n # base_url: \"https://api.x.ai/v1\"\n neutts:\n ref_audio: ''\n ref_text: ''\n model: neuphonic/neutts-air-q4-gguf\n device: cpu\n```\n\n这控制 `text_to_speech` 工具和语音模式中的口语回复(CLI 中的 `/voice tts` 或消息 gateway)。\n\n**速度回退层次:** provider 特定速度(例如 `tts.edge.speed`)→ 全局 `tts.speed` → `1.0` 默认值。设置全局 `tts.speed` 以在所有 provider 上应用统一速度,或按 provider 覆盖以进行精细控制。\n\n## 显示设置\n\n```yaml\ndisplay:\n tool_progress: all # off | new | all | verbose\n tool_progress_command: false # 在消息 gateway 中启用 /verbose 斜杠命令\n platforms: {} # 每平台显示覆盖(见下文)\n tool_progress_overrides: {} # 已弃用 —— 改用 display.platforms\n interim_assistant_messages: true # Gateway:将自然的轮次中 assistant 更新作为单独消息发送\n skin: default # 内置或自定义 CLI 皮肤(参阅 user-guide/features/skins)\n personality: \"kawaii\" # 旧版外观字段,仍在某些摘要中显示\n compact: false # 紧凑输出模式(减少空白)\n resume_display: full # full(恢复时显示之前的消息)| minimal(仅单行)\n bell_on_complete: false # 当 agent 完成时播放终端铃声(适合长任务)\n show_reasoning: false # 在每次响应上方显示模型推理/思考(用 /reasoning show|hide 切换)\n streaming: false # 将 token 实时流式传输到终端\n show_cost: false # 在 CLI 状态栏中显示估计 $ 成本\n timestamps: false # 为 true 时,在 CLI/TUI 记录中为用户和 assistant 标签添加 [HH:MM] 时间戳前缀\n tool_preview_length: 0 # 工具调用预览的最大字符数(0 = 无限制,显示完整路径/命令)\n runtime_footer: # Gateway:在最终回复中附加运行时上下文页脚\n enabled: false\n fields: [\"model\", \"context_pct\", \"cwd\"]\n file_mutation_verifier: true # 当本轮 write_file/patch 调用失败时附加建议性页脚\n language: en # 静态消息的 UI 语言(审批提示、部分 gateway 回复)。en | zh | zh-hant | ja | de | es | fr | tr | uk | af | ko | it | ga | pt | ru | hu\n```\n\n### 文件变更验证器\n\n当 `display.file_mutation_verifier` 为 `true`(默认)时,每当本轮中 `write_file` 或 `patch` 调用失败且从未被对同一路径的成功写入取代时,Hermes 会在 assistant 的最终响应中附加一行建议。这捕获了\"批量并行补丁,一半静默失败,模型总结成功\"这类过度声明,而无需您在每次编辑后手动运行 `git status`。\n\n示例页脚:\n\n```\n⚠️ File-mutation verifier: 3 file(s) were NOT modified this turn despite any wording above that may suggest otherwise. Run `git status` or `read_file` to confirm.\n • concepts/automatic-organization.md — [patch] Could not find match for old_string\n • concepts/lora.md — [patch] Could not find match for old_string\n • concepts/rag-pipeline.md — [patch] Could not find match for old_string\n```\n\n设置 `file_mutation_verifier: false`(或 `HERMES_FILE_MUTATION_VERIFIER=0`)以禁止页脚。验证器仅在轮次结束时有真实失败未解决时触发 —— 在同一轮次内重试失败补丁并成功的模型不会为该文件触发它。\n\n### 静态消息的 UI 语言\n\n`display.language` 设置翻译一小组静态面向用户的消息 —— CLI 审批提示、少数 gateway 斜杠命令回复(例如重启排空通知、\"审批已过期\"、\"目标已清除\")。它**不**翻译 agent 响应、日志行、工具输出、错误回溯或斜杠命令描述 —— 这些保持英文。如果您希望 agent 本身用另一种语言回复,只需在您的提示词或系统消息中告诉它。\n\n支持的值:`en`(默认)、`zh`(简体中文)、`zh-hant`(繁体中文)、`ja`(日语)、`de`(德语)、`es`(西班牙语)、`fr`(法语)、`tr`(土耳其语)、`uk`(乌克兰语)、`af`(南非荷兰语)、`ko`(韩语)、`it`(意大利语)、`ga`(爱尔兰语)、`pt`(葡萄牙语)、`ru`(俄语)、`hu`(匈牙利语)。未知值回退到英文。\n\n您也可以使用 `HERMES_LANGUAGE` 环境变量按会话设置,它会覆盖配置值。\n\n```yaml\ndisplay:\n language: zh # CLI 审批提示以中文显示\n```\n\n| 模式 | 您看到的内容 |\n|------|-------------|\n| `off` | 静默 —— 仅最终响应 |\n| `new` | 仅在工具更改时显示工具指示器 |\n| `all` | 每次工具调用附带简短预览(默认) |\n| `verbose` | 完整参数、结果和调试日志 |\n\n在 CLI 中,使用 `/verbose` 循环切换这些模式。要在消息平台(Telegram、Discord、Slack 等)中使用 `/verbose`,请在上方的 `display` 部分设置 `tool_progress_command: true`。该命令将循环切换模式并保存到配置。\n\n### 运行时元数据页脚(仅限 gateway)\n\n当 `display.runtime_footer.enabled: true` 时,Hermes 在每个 gateway 轮次的**最终**消息中附加一个小型运行时上下文页脚。目前页脚可显示模型、上下文窗口百分比和当前工作目录。默认关闭;如果您的团队希望每个回复都包含这些来源信息,请按 gateway 选择加入。\n\n```yaml\ndisplay:\n runtime_footer:\n enabled: true\n fields: [\"model\", \"context_pct\", \"cwd\"] # 支持字段:model、context_pct、cwd\n```\n\n`/footer` 斜杠命令在任何会话中运行时切换此功能。\n\n附加到 Telegram/Discord/Slack 回复的示例页脚:\n\n```\n— claude-opus-4.7 · 12 tool calls · 2m 14s · $0.042\n```\n\n只有轮次的**最终**消息获得页脚;中间更新保持干净。\n\n### 每平台进度覆盖\n\n不同平台有不同的详细程度需求。例如,Signal 无法编辑消息,因此每次进度更新都会成为单独的消息 —— 很嘈杂。使用 `display.platforms` 设置每平台模式:\n\n```yaml\ndisplay:\n tool_progress: all # 全局默认\n platforms:\n signal:\n tool_progress: 'off' # 在 Signal 上静默进度\n telegram:\n tool_progress: verbose # 在 Telegram 上详细进度\n slack:\n tool_progress: 'off' # 在共享 Slack 工作区中保持安静\n```\n\n没有覆盖的平台回退到全局 `tool_progress` 值。有效平台键:`telegram`、`discord`、`slack`、`signal`、`whatsapp`、`matrix`、`mattermost`、`email`、`sms`、`homeassistant`、`dingtalk`、`feishu`、`wecom`、`weixin`、`bluebubbles`、`qqbot`。旧版 `display.tool_progress_overrides` 键仍可加载以向后兼容,但已弃用,并在首次加载时迁移到 `display.platforms`。\n\n`interim_assistant_messages` 仅限 gateway。启用后,Hermes 将已完成的轮次中 assistant 更新作为单独的聊天消息发送。这与 `tool_progress` 无关,不需要 gateway 流式传输。\n\n## 隐私\n\n```yaml\nprivacy:\n redact_pii: false # 从 LLM 上下文中删除 PII(仅限 gateway)\n```\n\n当 `redact_pii` 为 `true` 时,gateway 在将系统提示词发送到受支持平台上的 LLM 之前,会从中删除个人身份信息:\n\n| 字段 | 处理方式 |\n|-------|-----------|\n| 电话号码(WhatsApp/Signal 上的用户 ID) | 哈希为 `user_<12-char-sha256>` |\n| 用户 ID | 哈希为 `user_<12-char-sha256>` |\n| 聊天 ID | 数字部分哈希,保留平台前缀(`telegram:<hash>`) |\n| 主频道 ID | 数字部分哈希 |\n| 用户名/昵称 | **不受影响**(用户选择的,公开可见) |\n\n**平台支持:** 删除适用于 WhatsApp、Signal 和 Telegram。Discord 和 Slack 被排除,因为它们的提及系统(`<@user_id>`)需要 LLM 上下文中的真实 ID。\n\n哈希是确定性的 —— 同一用户始终映射到同一哈希,因此模型仍然可以在群聊中区分用户。路由和传递在内部使用原始值。\n\n## 语音转文字(STT)\n\n```yaml\nstt:\n provider: \"local\" # \"local\" | \"groq\" | \"openai\" | \"mistral\"\n local:\n model: \"base\" # tiny、base、small、medium、large-v3\n openai:\n model: \"whisper-1\" # whisper-1 | gpt-4o-mini-transcribe | gpt-4o-transcribe\n # model: \"whisper-1\" # 旧版回退键仍受支持\n```\n\nProvider 行为:\n\n- `local` 使用在您机器上运行的 `faster-whisper`。使用 `pip install faster-whisper` 单独安装。\n- `groq` 使用 Groq 的 Whisper 兼容端点,读取 `GROQ_API_KEY`。\n- `openai` 使用 OpenAI 语音 API,读取 `VOICE_TOOLS_OPENAI_KEY`。\n\n如果请求的 provider 不可用,Hermes 按此顺序自动回退:`local` → `groq` → `openai`。\n\nGroq 和 OpenAI 模型覆盖由环境变量驱动:\n\n```bash\nSTT_GROQ_MODEL=whisper-large-v3-turbo\nSTT_OPENAI_MODEL=whisper-1\nGROQ_BASE_URL=https://api.groq.com/openai/v1\nSTT_OPENAI_BASE_URL=https://api.openai.com/v1\n```\n\n## 语音模式(CLI)\n\n```yaml\nvoice:\n record_key: \"ctrl+b\" # CLI 内的按键通话键\n max_recording_seconds: 120 # 长录音的硬停止\n auto_tts: false # /voice on 时自动启用口语回复\n beep_enabled: true # 在 CLI 语音模式中播放录音开始/停止提示音\n silence_threshold: 200 # 语音检测的 RMS 阈值\n silence_duration: 3.0 # 自动停止前的静默秒数\n```\n\n在 CLI 中使用 `/voice on` 启用麦克风模式,使用 `record_key` 开始/停止录音,使用 `/voice tts` 切换口语回复。端到端设置和平台特定行为请参阅[语音模式](/user-guide/features/voice-mode)。\n\n## 流式传输\n\n将 token 实时流式传输到终端或消息平台,而不是等待完整响应。\n\n### CLI 流式传输\n\n```yaml\ndisplay:\n streaming: true # 实时将 token 流式传输到终端\n show_reasoning: true # 同时流式传输推理/思考 token(可选)\n```\n\n启用后,响应在流式传输框内逐 token 出现。工具调用仍然静默捕获。如果 provider 不支持流式传输,它会自动回退到正常显示。\n\n### Gateway 流式传输(Telegram、Discord、Slack)\n\n```yaml\nstreaming:\n enabled: true # 启用渐进式消息编辑\n transport: edit # \"edit\"(渐进式消息编辑)或 \"off\"\n edit_interval: 0.3 # 消息编辑之间的秒数\n buffer_threshold: 40 # 强制编辑刷新前的字符数\n cursor: \" ▉\" # 流式传输期间显示的光标\n fresh_final_after_seconds: 0 # 预览超过此时间时选择发送新的最终消息(Telegram)\n```\n\n启用后,bot 在第一个 token 时发送消息,然后随着更多 token 到来渐进式编辑它。不支持消息编辑的平台(Signal、Email、Home Assistant)在第一次尝试时自动检测 —— 该会话的流式传输被优雅地禁用,不会产生大量消息。\n\n对于不带渐进式 token 编辑的独立自然轮次中 assistant 更新,请设置 `display.interim_assistant_messages: true`。\n\n**溢出处理:** 如果流式传输的文本超过平台的消息长度限制(约 4096 字符),当前消息被最终化,新消息自动开始。\n\n**新的最终消息(Telegram):** Telegram 的 `editMessageText` 保留原始消息时间戳,因此长时间运行的流式回复即使在完成后也会保留第一个 token 的时间戳。设置 `fresh_final_after_seconds > 0` 可选择将旧预览作为全新的最终消息传递,并尽力删除旧预览。默认值为 `0`,始终就地最终化流式回复,避免某些客户端短暂显示重复消息再删除其中一条。\n\n:::note\n主开关 `streaming.enabled` 默认为 `false`——在你启用之前不会有任何流式传输。启用后,是否流式传输按**平台**决定:Telegram 默认带有 `display.platforms.telegram.streaming: true`(流式传输),Discord 为 `display.platforms.discord.streaming: false`(不流式传输)。因此启用流式传输后,Telegram 开箱即用地流式传输,Discord 在你修改其开关之前仍使用整条消息回复。你可以在仪表盘的 **Channels** 开关中或直接在 `~/.hermes/config.yaml` 中调整这些按平台的开关。\n:::\n\n## 群聊会话隔离\n\n控制共享聊天是每个房间保持一个对话还是每个参与者一个对话:\n\n```yaml\ngroup_sessions_per_user: true # true = 群组/频道中每用户隔离,false = 每个聊天一个共享会话\n```\n\n- `true` 是默认和推荐设置。在 Discord 频道、Telegram 群组、Slack 频道和类似共享上下文中,当平台提供用户 ID 时,每个发送者获得自己的会话。\n- `false` 恢复到旧的共享房间行为。如果您明确希望 Hermes 将频道视为一个协作对话,这可能有用,但这也意味着用户共享上下文、token 成本和中断状态。\n- 私信不受影响。Hermes 仍然像往常一样通过聊天/DM ID 键入 DM。\n- 线程与其父频道保持隔离;使用 `true` 时,每个参与者在线程内也获得自己的会话。\n\n有关行为详情和示例,请参阅[会话](/user-guide/sessions)和 [Discord 指南](/user-guide/messaging/discord)。\n\n## 未授权 DM 行为\n\n控制当未知用户发送私信时 Hermes 的行为:\n\n```yaml\nunauthorized_dm_behavior: pair\n\nwhatsapp:\n unauthorized_dm_behavior: ignore\n```\n\n- `pair` 是默认值。Hermes 拒绝访问,但在 DM 中回复一次性配对码。\n- `ignore` 静默丢弃未授权的 DM。\n- 平台部分覆盖全局默认值,因此您可以在广泛范围内保持配对启用,同时使一个平台更安静。\n\n## 快速命令\n\n定义自定义命令,这些命令要么在不调用 LLM 的情况下运行 shell 命令,要么将一个斜杠命令别名为另一个。Exec 快速命令是零 token 的,对于从消息平台(Telegram、Discord 等)进行快速服务器检查或实用脚本很有用。\n\n```yaml\nquick_commands:\n status:\n type: exec\n command: systemctl status hermes-agent\n disk:\n type: exec\n command: df -h /\n update:\n type: exec\n command: cd ~/.hermes/hermes-agent && git pull && uv pip install -e .\n gpu:\n type: exec\n command: nvidia-smi --query-gpu=name,utilization.gpu,memory.used,memory.total --format=csv,noheader\n restart:\n type: alias\n target: /gateway restart\n```\n\n用法:在 CLI 或任何消息平台中输入 `/status`、`/disk`、`/update`、`/gpu` 或 `/restart`。`exec` 命令在宿主本地运行并直接返回输出 —— 无 LLM 调用,不消耗 token。`alias` 命令重写为配置的斜杠命令目标。\n\n- **30 秒超时** —— 长时间运行的命令被终止并显示错误消息\n- **优先级** —— 快速命令在技能命令之前检查,因此您可以覆盖技能名称\n- **自动补全** —— 快速命令在调度时解析,不显示在内置斜杠命令自动补全表中\n- **类型** —— 支持的类型为 `exec` 和 `alias`;其他类型显示错误\n- **到处可用** —— CLI、Telegram、Discord、Slack、WhatsApp、Signal、Email、Home Assistant\n\n仅字符串的 prompt 快捷方式不是有效的快速命令。对于可重用的 prompt 工作流,请创建技能或别名到现有斜杠命令。\n\n## 人类延迟\n\n在消息平台中模拟类人响应节奏:\n\n```yaml\nhuman_delay:\n mode: \"off\" # off | natural | custom\n min_ms: 800 # 最小延迟(自定义模式)\n max_ms: 2500 # 最大延迟(自定义模式)\n```\n\n## 代码执行\n\n配置 `execute_code` 工具:\n\n```yaml\ncode_execution:\n mode: project # project(默认)| strict\n timeout: 300 # 最大执行时间(秒)\n max_tool_calls: 50 # 代码执行中的最大工具调用次数\n```\n\n**`mode`** 控制脚本的工作目录和 Python 解释器:\n\n- **`project`**(默认)—— 脚本在会话的工作目录中以活跃 virtualenv/conda 环境的 python 运行。项目依赖(`pandas`、`torch`、项目包)和相对路径(`.env`、`./data.csv`)自然解析,与 `terminal()` 看到的一致。\n- **`strict`** —— 脚本在临时暂存目录中以 `sys.executable`(Hermes 自己的 python)运行。最大可重现性,但项目依赖和相对路径不会解析。\n\n环境清理(删除 `*_API_KEY`、`*_TOKEN`、`*_SECRET`、`*_PASSWORD`、`*_CREDENTIAL`、`*_PASSWD`、`*_AUTH`)和工具白名单在两种模式下完全相同 —— 切换模式不会改变安全态势。\n\n## Web 搜索后端\n\n`web_search` 和 `web_extract` 工具支持五种后端 provider。在 `config.yaml` 中或通过 `hermes tools` 配置后端:\n\n```yaml\nweb:\n backend: firecrawl # firecrawl | searxng | parallel | tavily | exa\n\n # 或使用每功能键混合 provider(例如免费搜索 + 付费提取):\n search_backend: \"searxng\"\n extract_backend: \"firecrawl\"\n```\n\n| 后端 | 环境变量 | 搜索 | 提取 |\n|---------|---------|--------|---------|\n| **Firecrawl**(默认) | `FIRECRAWL_API_KEY` | ✔ | ✔ |\n| **SearXNG** | `SEARXNG_URL` | ✔ | — |\n| **Parallel** | `PARALLEL_API_KEY` | ✔ | ✔ |\n| **Tavily** | `TAVILY_API_KEY` | ✔ | ✔ |\n| **Exa** | `EXA_API_KEY` | ✔ | ✔ |\n\n**后端选择:** 如果未设置 `web.backend`,后端从可用的 API 密钥自动检测。如果仅设置了 `SEARXNG_URL`,使用 SearXNG。如果仅设置了 `EXA_API_KEY`,使用 Exa。如果仅设置了 `TAVILY_API_KEY`,使用 Tavily。如果仅设置了 `PARALLEL_API_KEY`,使用 Parallel。否则 Firecrawl 是默认值。\n\n**SearXNG** 是一个免费、自托管、尊重隐私的元搜索引擎,查询 70+ 个搜索引擎。无需 API 密钥 —— 只需将 `SEARXNG_URL` 设置为您的实例(例如 `http://localhost:8080`)。SearXNG 仅限搜索;`web_extract` 需要单独的提取 provider(设置 `web.extract_backend`)。Docker 设置说明请参阅 [Web 搜索设置指南](/user-guide/features/web-search)。\n\n**自托管 Firecrawl:** 设置 `FIRECRAWL_API_URL` 指向您自己的实例。设置自定义 URL 后,API 密钥变为可选(在服务器上设置 `USE_DB_AUTHENTICATION=***` 以禁用认证)。\n\n**Parallel 搜索模式:** 设置 `PARALLEL_SEARCH_MODE` 控制搜索行为 —— `fast`、`one-shot` 或 `agentic`(默认:`agentic`)。\n\n**Exa:** 在 `~/.hermes/.env` 中设置 `EXA_API_KEY`。支持 `category` 过滤(`company`、`research paper`、`news`、`people`、`personal site`、`pdf`)和域名/日期过滤器。\n\n## 浏览器\n\n配置浏览器自动化行为:\n\n```yaml\nbrowser:\n inactivity_timeout: 120 # 自动关闭空闲会话前的秒数\n command_timeout: 30 # 浏览器命令超时(截图、导航等)(秒)\n record_sessions: false # 自动将浏览器会话录制为 WebM 视频到 ~/.hermes/browser_recordings/\n # 可选 CDP 覆盖 —— 设置后,Hermes 直接附加到您自己的\n # Chromium 系浏览器(通过 /browser connect),而不是启动无头浏览器。\n cdp_url: \"\"\n # 对话框监督器 —— 控制当 CDP 后端附加时(Browserbase、本地 Chromium 系\n # 浏览器通过 /browser connect)如何处理原生 JS 对话框(alert/confirm/prompt)。\n # 在 Camofox 和默认本地 agent 浏览器模式下忽略。\n dialog_policy: must_respond # must_respond | auto_dismiss | auto_accept\n dialog_timeout_s: 300 # must_respond 下的安全自动关闭(秒)\n camofox:\n managed_persistence: false # 为 true 时,Camofox 会话跨重启持久化 cookie/登录\n user_id: \"\" # 可选的外部管理 Camofox userId\n session_key: \"\" # Hermes 创建标签页时发送的可选会话密钥\n adopt_existing_tab: false # 在创建新标签页之前重用此身份的现有标签页\n```\n\n**对话框策略:**\n\n- `must_respond`(默认)—— 捕获对话框,在 `browser_snapshot.pending_dialogs` 中显示,等待 agent 调用 `browser_dialog(action=...)`。在 `dialog_timeout_s` 秒内无响应后,对话框被自动关闭以防止页面的 JS 线程永久停滞。\n- `auto_dismiss` —— 捕获,立即关闭。Agent 仍然在事后的 `browser_snapshot.recent_dialogs` 中看到对话框记录,`closed_by=\"auto_policy\"`。\n- `auto_accept` —— 捕获,立即接受。适用于有激进 `beforeunload` 提示的页面。\n\n完整对话框工作流请参阅[浏览器功能页面](./features/browser.md#browser_dialog)。\n\n浏览器工具集支持多个 provider。有关 Browserbase、Browser Use 和本地 Chromium 系 CDP 设置的详细信息,请参阅[浏览器功能页面](/user-guide/features/browser)。\n\n## 时区\n\n使用 IANA 时区字符串覆盖服务器本地时区。影响日志中的时间戳、cron 调度和系统提示词时间注入。\n\n```yaml\ntimezone: \"America/New_York\" # IANA 时区(默认:\"\" = 服务器本地时间)\n```\n\n支持的值:任何 IANA 时区标识符(例如 `America/New_York`、`Europe/London`、`Asia/Kolkata`、`UTC`)。留空或省略以使用服务器本地时间。\n\n## Discord\n\n为消息 gateway 配置 Discord 特定行为:\n\n```yaml\ndiscord:\n require_mention: true # 在服务器频道中需要 @提及才能响应\n free_response_channels: \"\" # 逗号分隔的频道 ID,bot 在这些频道无需 @提及即可响应\n auto_thread: true # 在频道中 @提及时自动创建线程\n```\n\n- `require_mention` —— 为 `true`(默认)时,bot 仅在服务器频道中被 `@BotName` 提及时响应。DM 始终无需提及即可工作。\n- `free_response_channels` —— 逗号分隔的频道 ID 列表,bot 在这些频道对每条消息响应,无需提及。\n- `auto_thread` —— 为 `true`(默认)时,频道中的提及会自动为对话创建线程,保持频道整洁(类似 Slack 线程)。\n\n## 安全\n\n预执行安全扫描和机密脱敏:\n\n```yaml\nsecurity:\n redact_secrets: false # 在工具输出和日志中脱敏 API 密钥模式(默认关闭)\n tirith_enabled: true # 为终端命令启用 Tirith 安全扫描\n tirith_path: \"tirith\" # tirith 二进制文件路径(默认:$PATH 中的 \"tirith\")\n tirith_timeout: 5 # 等待 tirith 扫描的秒数\n tirith_fail_open: true # 如果 tirith 不可用,允许命令执行\n website_blocklist: # 参见下方网站黑名单部分\n enabled: false\n domains: []\n shared_files: []\n```\n\n- `redact_secrets` —— 为 `true` 时,自动检测并脱敏工具输出中看起来像 API 密钥、token 和密码的模式,然后再进入对话上下文和日志。**默认关闭** —— 如果您经常在工具输出中处理真实凭据并希望有安全网,请启用。显式设置为 `true` 以开启。\n- `tirith_enabled` —— 为 `true` 时,终端命令在执行前由 [Tirith](https://github.com/sheeki03/tirith) 扫描以检测潜在危险操作。\n- `tirith_path` —— tirith 二进制文件的路径。如果 tirith 安装在非标准位置,请设置此项。\n- `tirith_timeout` —— 等待 tirith 扫描的最大秒数。如果扫描超时,命令继续执行。\n- `tirith_fail_open` —— 为 `true`(默认)时,如果 tirith 不可用或失败,允许命令执行。设置为 `false` 以在 tirith 无法验证时阻止命令。\n\n## 网站黑名单\n\n阻止 agent 的 web 和浏览器工具访问特定域名:\n\n```yaml\nsecurity:\n website_blocklist:\n enabled: false # 启用 URL 阻止(默认:false)\n domains: # 被阻止的域名模式列表\n - \"*.internal.company.com\"\n - \"admin.example.com\"\n - \"*.local\"\n shared_files: # 从外部文件加载额外规则\n - \"/etc/hermes/blocked-sites.txt\"\n```\n\n启用后,任何匹配被阻止域名模式的 URL 在 web 或浏览器工具执行之前都会被拒绝。这适用于 `web_search`、`web_extract`、`browser_navigate` 以及任何访问 URL 的工具。\n\n域名规则支持:\n- 精确域名:`admin.example.com`\n- 通配符子域名:`*.internal.company.com`(阻止所有子域名)\n- TLD 通配符:`*.local`\n\n共享文件每行包含一条域名规则(空行和 `#` 注释被忽略)。缺失或不可读的文件记录警告,但不禁用其他 web 工具。\n\n策略缓存 30 秒,因此配置更改无需重启即可快速生效。\n\n## 智能审批\n\n控制 Hermes 如何处理潜在危险命令:\n\n```yaml\napprovals:\n mode: smart # smart | manual | off\n```\n\n| 模式 | 行为 |\n|------|----------|\n| `smart`(默认) | 使用辅助 LLM 评估被标记的命令是否真正危险。低风险命令仅对当前命令自动批准,真正危险的命令自动拒绝,不确定的情况升级给用户。 |\n| `manual` | 在执行任何被标记的命令之前提示用户。在 CLI 中显示交互式审批对话框。在消息中排队待处理的审批请求。 |\n| `off` | 跳过所有审批检查。等同于 `HERMES_YOLO_MODE=true`。**谨慎使用。** |\n\n智能模式对于减少审批疲劳特别有用 —— 它让 agent 在安全操作上更自主地工作,同时仍然捕获真正破坏性的命令。\n\n:::warning\n设置 `approvals.mode: off` 会禁用终端命令的所有安全检查。仅在受信任的沙箱环境中使用。\n:::\n\n## 检查点\n\n破坏性文件操作之前的自动文件系统快照。详情请参阅[检查点与回滚](/user-guide/checkpoints-and-rollback)。\n\n```yaml\ncheckpoints:\n enabled: false # 启用自动检查点(也可:hermes chat --checkpoints)。默认:false(选择加入)。\n max_snapshots: 20 # 每个目录保留的最大检查点数(默认:20)\n```\n\n\n## 委托\n\n为委托工具配置子 agent 行为:\n\n```yaml\ndelegation:\n # model: \"google/gemini-3-flash-preview\" # 覆盖模型(空 = 继承父级)\n # provider: \"openrouter\" # 覆盖 provider(空 = 继承父级)\n # base_url: \"http://localhost:1234/v1\" # 直接 OpenAI 兼容端点(优先于 provider)\n # api_key: \"local-key\" # base_url 的 API 密钥(回退到 OPENAI_API_KEY)\n # api_mode: \"\" # base_url 的线路协议:\"chat_completions\"、\"codex_responses\" 或 \"anthropic_messages\"。空 = 从 URL 自动检测(例如 /anthropic 后缀 → anthropic_messages)。对启发式无法检测的非标准端点显式设置。\n max_concurrent_children: 3 # 每批并行子 agent 数(下限 1,无上限)。也可通过 DELEGATION_MAX_CONCURRENT_CHILDREN 环境变量设置。\n max_spawn_depth: 1 # 委托树深度上限(1-3,截断)。1 = 扁平(默认):父级生成无法委托的叶子。2 = 编排器子级可以生成叶子孙级。3 = 三级。\n orchestrator_enabled: true # 全局终止开关。为 false 时,role=\"orchestrator\" 被忽略,每个子级无论 max_spawn_depth 如何都被强制为叶子。\n```\n\n**子 agent provider:model 覆盖:** 默认情况下,子 agent 继承父 agent 的 provider 和模型。设置 `delegation.provider` 和 `delegation.model` 将子 agent 路由到不同的 provider:model 对 —— 例如,在您的主 agent 运行昂贵推理模型时,为范围较窄的子任务使用便宜/快速的模型。\n\n**直接端点覆盖:** 如果您想要明显的自定义端点路径,请设置 `delegation.base_url`、`delegation.api_key` 和 `delegation.model`。这将子 agent 直接发送到该 OpenAI 兼容端点,并优先于 `delegation.provider`。如果省略 `delegation.api_key`,Hermes 仅回退到 `OPENAI_API_KEY`。\n\n**线路协议(`api_mode`):** Hermes 从 `delegation.base_url` 自动检测线路协议(例如以 `/anthropic` 结尾的路径 → `anthropic_messages`;Codex/原生 Anthropic/Kimi-coding 主机名保留其现有检测)。对于启发式无法分类的端点 —— 例如 Azure AI Foundry、MiniMax、Zhipu GLM 或前置 Anthropic 形状后端的 LiteLLM 代理 —— 请将 `delegation.api_mode` 显式设置为 `chat_completions`、`codex_responses` 或 `anthropic_messages` 之一。留空(默认)以保持自动检测。\n\n委托 provider 使用与 CLI/gateway 启动相同的凭据解析。所有配置的 provider 均受支持:`openrouter`、`nous`、`copilot`、`zai`、`kimi-coding`、`minimax`、`minimax-cn`。设置 provider 时,系统自动解析正确的基础 URL、API 密钥和 API 模式 —— 无需手动凭据连接。\n\n**优先级:** 配置中的 `delegation.base_url` → 配置中的 `delegation.provider` → 父 provider(继承)。配置中的 `delegation.model` → 父模型(继承)。仅设置 `model` 而不设置 `provider` 仅更改模型名称,同时保留父级凭据(适用于在同一 provider(如 OpenRouter)内切换模型)。\n\n**宽度和深度:** `max_concurrent_children` 限制每批并行运行的子 agent 数量(默认 `3`,下限 1,无上限)。也可通过 `DELEGATION_MAX_CONCURRENT_CHILDREN` 环境变量设置。当模型提交的 `tasks` 数组超过上限时,`delegate_task` 返回工具错误解释限制,而不是静默截断。`max_spawn_depth` 控制委托树深度(截断到 1-3)。在默认 `1` 时,委托是扁平的:子级无法生成孙级,传递 `role=\"orchestrator\"` 静默降级为 `leaf`。提升到 `2` 使编排器子级可以生成叶子孙级;`3` 用于三级树。Agent 通过 `role=\"orchestrator\"` 按调用选择编排;`orchestrator_enabled: false` 强制每个子级回到叶子,无论如何。成本呈乘法增长 —— 在 `max_spawn_depth: 3` 和 `max_concurrent_children: 3` 时,树可以达到 3×3×3 = 27 个并发叶子 agent。使用模式请参阅[子 Agent 委托 → 深度限制和嵌套编排](features/delegation.md#depth-limit-and-nested-orchestration)。\n\n## 澄清\n\n配置澄清提示行为:\n\n```yaml\nclarify:\n timeout: 120 # 等待用户澄清响应的秒数\n```\n\n## 上下文文件(SOUL.md、AGENTS.md)\n\nHermes 使用两种不同的上下文范围:\n\n| 文件 | 用途 | 范围 |\n|------|---------|-------|\n| `SOUL.md` | **主要 agent 身份** —— 定义 agent 是谁(系统提示词第 #1 槽位) | `~/.hermes/SOUL.md` 或 `$HERMES_HOME/SOUL.md` |\n| `.hermes.md` / `HERMES.md` | 项目特定指令(最高优先级) | 向上走到 git 根目录 |\n| `AGENTS.md` | 项目特定指令、编码规范 | 递归目录遍历 |\n| `CLAUDE.md` | Claude Code 上下文文件(也会检测) | 仅工作目录 |\n| `.cursorrules` | Cursor IDE 规则(也会检测) | 仅工作目录 |\n| `.cursor/rules/*.mdc` | Cursor 规则文件(也会检测) | 仅工作目录 |\n\n- **SOUL.md** 是 agent 的主要身份。它占据系统提示词的第 #1 槽位,完全替换内置的默认身份。编辑它以完全自定义 agent 是谁。\n- 如果 SOUL.md 缺失、为空或无法加载,Hermes 回退到内置默认身份。\n- **项目上下文文件使用优先级系统** —— 仅加载一种类型(第一个匹配优先):`.hermes.md` → `AGENTS.md` → `CLAUDE.md` → `.cursorrules`。SOUL.md 始终独立加载。\n- **AGENTS.md** 是分层的:如果子目录也有 AGENTS.md,所有都会合并。\n- 如果 `SOUL.md` 不存在,Hermes 会自动生成默认的 `SOUL.md`。\n- 所有加载的上下文文件上限为 20,000 字符,并进行智能截断。\n\n另请参阅:\n- [个性与 SOUL.md](/user-guide/features/personality)\n- [上下文文件](/user-guide/features/context-files)\n\n## 工作目录\n\n| 上下文 | 默认值 |\n|---------|---------|\n| **CLI(`hermes`)** | 运行命令的当前目录 |\n| **消息 gateway** | 主目录 `~`(用 `MESSAGING_CWD` 覆盖) |\n| **Docker / Singularity / Modal / SSH** | 容器或远程机器内用户的主目录 |\n\n覆盖工作目录:\n```bash\n# 在 ~/.hermes/.env 或 ~/.hermes/config.yaml 中:\nMESSAGING_CWD=/home/myuser/projects # Gateway 会话\nTERMINAL_CWD=/workspace # 所有终端会话\n```\n"}, "d84e11af4d9927c41ad0a3b4db72042cca250c64": {"t": 1785767977.7179914, "n": 56666, "h": "ce0e9c5d797eba5f", "c": "---\nsidebar_position: 2\ntitle: \"配置\"\ndescription: \"配置 Hermes Agent — config.yaml、providers、模型、API 密钥等\"\n---\n\n# 配置\n\n所有设置均存储在 `~/.hermes/` 目录中,便于访问。\n\n## 目录结构\n\n```text\n~/.hermes/\n├── config.yaml # 设置(模型、终端、TTS、压缩等)\n├── .env # API 密钥和机密\n├── auth.json # OAuth provider 凭据(Nous Portal 等)\n├── SOUL.md # 主要 agent 身份(系统提示词第 #1 槽位)\n├── memories/ # 持久记忆(MEMORY.md、USER.md)\n├── skills/ # Agent 创建的技能(通过 skill_manage 工具管理)\n├── cron/ # 定时任务\n├── sessions/ # Gateway 会话\n└── logs/ # 日志(errors.log、gateway.log — 机密自动脱敏)\n```\n\n## 管理配置\n\n```bash\nhermes config # 查看当前配置\nhermes config edit # 在编辑器中打开 config.yaml\nhermes config set KEY VAL # 设置特定值\nhermes config check # 检查缺失选项(更新后使用)\nhermes config migrate # 交互式添加缺失选项\n\n# 示例:\nhermes config set model anthropic/claude-opus-4\nhermes config set terminal.backend docker\nhermes config set OPENROUTER_API_KEY sk-or-... # 保存到 .env\n```\n\n:::tip\n`hermes config set` 命令会自动将值路由到正确的文件 —— API 密钥保存到 `.env`,其他所有内容保存到 `config.yaml`。\n:::\n\n## 配置优先级\n\n设置按以下顺序解析(优先级从高到低):\n\n1. **CLI 参数** —— 例如 `hermes chat --model anthropic/claude-sonnet-4`(单次调用覆盖)\n2. **`~/.hermes/config.yaml`** —— 所有非机密设置的主配置文件\n3. **`~/.hermes/.env`** —— 环境变量的回退;机密(API 密钥、token、密码)**必须**放这里\n4. **内置默认值** —— 未设置任何内容时的硬编码安全默认值\n\n:::info 经验法则\n机密(API 密钥、bot token、密码)放入 `.env`。其他所有内容(模型、终端后端、压缩设置、内存限制、工具集)放入 `config.yaml`。当两者都设置时,`config.yaml` 对非机密设置优先。\n:::\n\n## 环境变量替换\n\n可以在 `config.yaml` 中使用 `${VAR_NAME}` 语法引用环境变量:\n\n```yaml\nauxiliary:\n vision:\n api_key: ${GOOGLE_API_KEY}\n base_url: ${CUSTOM_VISION_URL}\n\ndelegation:\n api_key: ${DELEGATION_KEY}\n```\n\n单个值中可以有多个引用:`url: \"${HOST}:${PORT}\"`。如果引用的变量未设置,占位符将保持原样(`${UNDEFINED_VAR}` 保持不变)。仅支持 `${VAR}` 语法 —— 裸 `$VAR` 不会被展开。\n\n关于 AI provider 设置(OpenRouter、Anthropic、Copilot、自定义端点、自托管 LLM、回退模型等),请参阅 [AI Providers](/integrations/providers)。\n\n### Provider 超时\n\n可以为 provider 设置 `providers.<id>.request_timeout_seconds` 作为全局请求超时,以及 `providers.<id>.models.<model>.timeout_seconds` 作为特定模型的覆盖值。适用于每种传输方式(OpenAI-wire、原生 Anthropic、Anthropic 兼容)上的主轮次客户端、回退链、凭据轮换后的重建,以及(对于 OpenAI-wire)每请求超时 kwarg —— 因此配置值优先于旧版 `HERMES_API_TIMEOUT` 环境变量。\n\n还可以设置 `providers.<id>.stale_timeout_seconds` 用于非流式陈旧调用检测器,以及 `providers.<id>.models.<model>.stale_timeout_seconds` 作为特定模型的覆盖值。此值优先于旧版 `HERMES_API_CALL_STALE_TIMEOUT` 环境变量。\n\n不设置这些值将保持旧版默认值(`HERMES_API_TIMEOUT=1800`s、`HERMES_API_CALL_STALE_TIMEOUT=90`s、原生 Anthropic 900s)。隐式的非流式 stale 检测会在本地端点上自动禁用,并且会在超大上下文下自动放宽。目前不适用于 AWS Bedrock(`bedrock_converse` 和 AnthropicBedrock SDK 路径均使用 boto3 及其自身的超时配置)。请参阅 [`cli-config.yaml.example`](https://github.com/NousResearch/hermes-agent/blob/main/cli-config.yaml.example) 中的注释示例。\n\n## 终端后端配置\n\nHermes 支持六种终端后端。每种后端决定 agent 的 shell 命令实际在哪里执行 —— 本地机器、Docker 容器、通过 SSH 的远程服务器、Modal 云沙箱(直接或通过 Nous 托管的 gateway)、Daytona 工作区,或 Singularity/Apptainer 容器。\n\n```yaml\nterminal:\n backend: local # local | docker | ssh | modal | daytona | singularity\n cwd: \".\" # Gateway/cron 工作目录(CLI 始终使用启动目录)\n timeout: 180 # 每条命令的超时时间(秒)\n env_passthrough: [] # 转发到沙箱执行的环境变量名(terminal + execute_code)\n singularity_image: \"docker://nikolaik/python-nodejs:python3.11-nodejs20\" # Singularity 后端的容器镜像\n modal_image: \"nikolaik/python-nodejs:python3.11-nodejs20\" # Modal 后端的容器镜像\n daytona_image: \"nikolaik/python-nodejs:python3.11-nodejs20\" # Daytona 后端的容器镜像\n```\n\n对于 Modal 和 Daytona 等云沙箱,`container_persistent: true` 表示 Hermes 将尝试在沙箱重建后保留文件系统状态。这并不保证相同的活跃沙箱、PID 空间或后台进程之后仍在运行。\n\n### 后端概览\n\n| 后端 | 命令运行位置 | 隔离性 | 最适合 |\n|---------|-------------------|-----------|----------|\n| **local** | 直接在您的机器上 | 无 | 开发、个人使用 |\n| **docker** | 单个持久 Docker 容器(跨会话、`/new`、子 agent 共享) | 完全(命名空间、cap-drop) | 安全沙箱、CI/CD |\n| **ssh** | 通过 SSH 的远程服务器 | 网络边界 | 远程开发、强大硬件 |\n| **modal** | Modal 云沙箱 | 完全(云 VM) | 临时云计算、评估 |\n| **daytona** | Daytona 工作区 | 完全(云容器) | 托管云开发环境 |\n| **singularity** | Singularity/Apptainer 容器 | 命名空间(--containall) | HPC 集群、共享机器 |\n\n### Local 后端\n\n默认后端。命令直接在您的机器上运行,无隔离。无需特殊设置。\n\n```yaml\nterminal:\n backend: local\n```\n\n:::warning\nAgent 拥有与您的用户账户相同的文件系统访问权限。使用 `hermes tools` 禁用不需要的工具,或切换到 Docker 进行沙箱隔离。\n:::\n\n### Docker 后端\n\n在具有安全加固的 Docker 容器内运行命令(所有权限已删除、无权限提升、PID 限制)。\n\n**单个持久容器,而非每条命令一个容器。** Hermes 在首次使用时启动一个长期运行的容器,并通过 `docker exec` 将每个终端、文件和 `execute_code` 调用路由到同一容器中 —— 跨会话、`/new`、`/reset` 和 `delegate_task` 子 agent,贯穿 Hermes 进程的整个生命周期。工作目录更改、已安装的包以及 `/workspace` 中的文件会从一次工具调用延续到下一次,就像本地 shell 一样。容器在关闭时停止并删除。详情请参阅下方的**容器生命周期**。\n\n```yaml\nterminal:\n backend: docker\n docker_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n docker_mount_cwd_to_workspace: false # 将启动目录挂载到 /workspace\n docker_run_as_host_user: false # 参见下方\"以宿主用户身份运行容器\"\n docker_forward_env: # 转发到容器的环境变量\n - \"GITHUB_TOKEN\"\n docker_volumes: # 宿主目录挂载\n - \"/home/user/projects:/workspace/projects\"\n - \"/home/user/data:/data:ro\" # :ro 表示只读\n docker_extra_args: # 附加到 `docker run` 的额外标志\n - \"--gpus=all\"\n - \"--network=host\"\n\n # 资源限制\n container_cpu: 1 # CPU 核心数(0 = 不限制)\n container_memory: 5120 # MB(0 = 不限制)\n container_disk: 51200 # MB(需要 XFS+pquota 上的 overlay2)\n container_persistent: true # 跨会话持久化 /workspace 和 /root\n```\n\n**`terminal.docker_extra_args`**(也可通过 `TERMINAL_DOCKER_EXTRA_ARGS='[\"--gpus=all\"]'` 覆盖)允许传递 Hermes 未作为一级键公开的任意 `docker run` 标志 —— `--gpus`、`--network`、`--add-host`、替代 `--security-opt` 覆盖等。每个条目必须是字符串;该列表最后附加到组装好的 `docker run` 调用中,因此可以在需要时覆盖 Hermes 的默认值。请谨慎使用 —— 与沙箱加固(权限删除、`--user`、workspace 绑定挂载)冲突的标志将悄然削弱隔离性。\n\n**要求:** 已安装并运行 Docker Desktop 或 Docker Engine。Hermes 会探测 `$PATH` 以及常见的 macOS 安装位置(`/usr/local/bin/docker`、`/opt/homebrew/bin/docker`、Docker Desktop 应用包)。开箱即用支持 Podman:设置 `HERMES_DOCKER_BINARY=podman`(或完整路径)以在两者都安装时强制使用它。\n\n**容器生命周期:** Hermes 为每个终端和文件工具调用重用单个长期运行的容器(`docker run -d ... sleep 2h`),跨会话、`/new`、`/reset` 和 `delegate_task` 子 agent,贯穿 Hermes 进程的整个生命周期。命令通过带登录 shell 的 `docker exec` 运行,因此工作目录更改、已安装的包以及 `/workspace` 中的文件都会从一次工具调用延续到下一次。容器在 Hermes 关闭时(或空闲清理回收时)停止并删除。\n\n通过 `delegate_task(tasks=[...])` 生成的并行子 agent 共享这一个容器 —— 并发的 `cd`、环境变量修改以及对同一路径的写入会发生冲突。如果子 agent 需要隔离的沙箱,必须通过 `register_task_env_overrides()` 注册每任务镜像覆盖,RL 和基准测试环境(TerminalBench2、HermesSweEnv 等)会自动为其每任务 Docker 镜像执行此操作。\n\n**安全加固:**\n- `--cap-drop ALL`,仅添加回 `DAC_OVERRIDE`、`CHOWN`、`FOWNER`\n- `--security-opt no-new-privileges`\n- `--pids-limit 256`\n- `/tmp`(512MB)、`/var/tmp`(256MB)、`/run`(64MB)的大小限制 tmpfs\n\n**凭据转发:** `docker_forward_env` 中列出的环境变量首先从您的 shell 环境解析,然后回退到 `~/.hermes/.env`。技能也可以声明 `required_environment_variables`,这些变量会自动合并。\n\n### SSH 后端\n\n通过 SSH 在远程服务器上运行命令。使用 ControlMaster 进行连接复用(5 分钟空闲保活)。默认启用持久 shell —— 状态(cwd、环境变量)在命令之间保持。\n\n```yaml\nterminal:\n backend: ssh\n persistent_shell: true # 保持长期运行的 bash 会话(默认:true)\n```\n\n**必需的环境变量:**\n\n```bash\nTERMINAL_SSH_HOST=my-server.example.com\nTERMINAL_SSH_USER=ubuntu\n```\n\n**可选:**\n\n| 变量 | 默认值 | 描述 |\n|----------|---------|-------------|\n| `TERMINAL_SSH_PORT` | `22` | SSH 端口 |\n| `TERMINAL_SSH_KEY` | (系统默认) | SSH 私钥路径 |\n| `TERMINAL_SSH_PERSISTENT` | `true` | 启用持久 shell |\n\n**工作原理:** 使用 `BatchMode=yes` 和 `StrictHostKeyChecking=accept-new` 在初始化时连接。持久 shell 在远程主机上保持单个 `bash -l` 进程存活,通过临时文件进行通信。需要 `stdin_data` 或 `sudo` 的命令会自动回退到单次模式。\n\n### Modal 后端\n\n在 [Modal](https://modal.com) 云沙箱中运行命令。每个任务获得一个具有可配置 CPU、内存和磁盘的隔离 VM。文件系统可以跨会话快照/恢复。\n\n```yaml\nterminal:\n backend: modal\n container_cpu: 1 # CPU 核心数\n container_memory: 5120 # MB(5GB)\n container_disk: 51200 # MB(50GB)\n container_persistent: true # 快照/恢复文件系统\n```\n\n**必需:** `MODAL_TOKEN_ID` + `MODAL_TOKEN_SECRET` 环境变量,或 `~/.modal.toml` 配置文件。\n\n**持久化:** 启用后,沙箱文件系统在清理时快照,并在下次会话时恢复。快照在 `~/.hermes/modal_snapshots.json` 中跟踪。这保留文件系统状态,而非活跃进程、PID 空间或后台任务。\n\n**凭据文件:** 自动从 `~/.hermes/` 挂载(OAuth token 等),并在每条命令前同步。\n\n### Daytona 后端\n\n在 [Daytona](https://daytona.io) 托管工作区中运行命令。支持停止/恢复以实现持久化。\n\n```yaml\nterminal:\n backend: daytona\n container_cpu: 1 # CPU 核心数\n container_memory: 5120 # MB → 转换为 GiB\n container_disk: 10240 # MB → 转换为 GiB(最大 10 GiB)\n container_persistent: true # 停止/恢复而非删除\n```\n\n**必需:** `DAYTONA_API_KEY` 环境变量。\n\n**持久化:** 启用后,沙箱在清理时停止(而非删除),并在下次会话时恢复。沙箱名称遵循 `hermes-{task_id}` 模式。\n\n**磁盘限制:** Daytona 强制执行 10 GiB 最大值。超过此值的请求将被截断并发出警告。\n\n### Singularity/Apptainer 后端\n\n在 [Singularity/Apptainer](https://apptainer.org) 容器中运行命令。专为 Docker 不可用的 HPC 集群和共享机器设计。\n\n```yaml\nterminal:\n backend: singularity\n singularity_image: \"docker://nikolaik/python-nodejs:python3.11-nodejs20\"\n container_cpu: 1 # CPU 核心数\n container_memory: 5120 # MB\n container_persistent: true # 可写覆盖层跨会话持久化\n```\n\n**要求:** `$PATH` 中有 `apptainer` 或 `singularity` 二进制文件。\n\n**镜像处理:** Docker URL(`docker://...`)自动转换为 SIF 文件并缓存。现有 `.sif` 文件直接使用。\n\n**临时目录:** 按顺序解析:`TERMINAL_SCRATCH_DIR` → `TERMINAL_SANDBOX_DIR/singularity` → `/scratch/$USER/hermes-agent`(HPC 惯例)→ `~/.hermes/sandboxes/singularity`。\n\n**隔离:** 使用 `--containall --no-home` 实现完全命名空间隔离,不挂载宿主 home 目录。\n\n### 常见终端后端问题\n\n如果终端命令立即失败或终端工具报告为已禁用:\n\n- **Local** —— 无特殊要求。入门时最安全的默认选项。\n- **Docker** —— 运行 `docker version` 验证 Docker 是否正常工作。如果失败,修复 Docker 或执行 `hermes config set terminal.backend local`。\n- **SSH** —— `TERMINAL_SSH_HOST` 和 `TERMINAL_SSH_USER` 都必须设置。如果缺少任一项,Hermes 会记录清晰的错误。\n- **Modal** —— 需要 `MODAL_TOKEN_ID` 环境变量或 `~/.modal.toml`。运行 `hermes doctor` 检查。\n- **Daytona** —— 需要 `DAYTONA_API_KEY`。Daytona SDK 处理服务器 URL 配置。\n- **Singularity** —— 需要 `$PATH` 中有 `apptainer` 或 `singularity`。HPC 集群上常见。\n\n如有疑问,将 `terminal.backend` 设回 `local` 并首先验证命令在那里运行。\n\n### 拆卸时远程到宿主文件同步\n\n对于 **SSH**、**Modal** 和 **Daytona** 后端(agent 的工作树位于与运行 Hermes 的宿主不同的机器上),Hermes 跟踪 agent 在远程沙箱中触及的文件,并在会话拆卸/沙箱清理时,将修改的文件**同步回宿主**,存放在 `~/.hermes/cache/remote-syncs/<session-id>/` 下。\n\n- 触发时机:会话关闭、`/new`、`/reset`、gateway 消息超时、子 agent 使用远程后端时 `delegate_task` 子 agent 完成。\n- 覆盖 agent 修改的整个树,而不仅仅是它明确打开的文件。添加、编辑和删除都会被捕获。\n- 远程沙箱可能在您查找时已被拆除;本地 `~/.hermes/cache/remote-syncs/…` 副本是 agent 更改内容的权威记录。\n- 大型二进制输出(模型检查点、原始数据集)按大小限制 —— 同步跳过超过 `file_sync_max_mb`(默认 `100`)的文件。如果您期望更大的工件返回,请调高该值。\n\n```yaml\nterminal:\n file_sync_max_mb: 100 # 默认 —— 同步最大 100 MB 的文件\n file_sync_enabled: true # 默认 —— 设为 false 可完全跳过同步\n```\n\n这是从会话结束后被销毁的临时云沙箱中恢复结果的方式,无需告诉 agent 显式地 `scp` 或 `modal volume put` 每个工件。\n\n### Docker 卷挂载\n\n使用 Docker 后端时,`docker_volumes` 允许您与容器共享宿主目录。每个条目使用标准 Docker `-v` 语法:`host_path:container_path[:options]`。\n\n```yaml\nterminal:\n backend: docker\n docker_volumes:\n - \"/home/user/projects:/workspace/projects\" # 读写(默认)\n - \"/home/user/datasets:/data:ro\" # 只读\n - \"/home/user/.hermes/cache/documents:/output\" # Gateway 可见的导出\n```\n\n适用于:\n- **向 agent 提供文件**(数据集、配置、参考代码)\n- **从 agent 接收文件**(生成的代码、报告、导出)\n- **共享工作区**,您和 agent 都访问相同的文件\n\n如果您使用消息 gateway 并希望 agent 通过 `MEDIA:/...` 发送生成的文件,建议使用专用的宿主可见导出挂载,例如 `/home/user/.hermes/cache/documents:/output`。\n\n- 在 Docker 中将文件写入 `/output/...`\n- 在 `MEDIA:` 中发出**宿主路径**,例如:`MEDIA:/home/user/.hermes/cache/documents/report.txt`\n- **不要**发出 `/workspace/...` 或 `/output/...`,除非该确切路径在宿主上对 gateway 进程也存在\n\n:::warning\nYAML 重复键会静默覆盖之前的键。如果您已有 `docker_volumes:` 块,请将新挂载合并到同一列表中,而不是在文件后面再添加一个 `docker_volumes:` 键。\n:::\n\n也可以通过环境变量设置:`TERMINAL_DOCKER_VOLUMES='[\"/host:/container\"]'`(JSON 数组)。\n\n### Docker 凭据转发\n\n默认情况下,Docker 终端会话不继承任意宿主凭据。如果您需要在容器内使用特定 token,请将其添加到 `terminal.docker_forward_env`。\n\n```yaml\nterminal:\n backend: docker\n docker_forward_env:\n - \"GITHUB_TOKEN\"\n - \"NPM_TOKEN\"\n```\n\nHermes 首先从您当前的 shell 解析每个列出的变量,然后回退到通过 `hermes config set` 保存的 `~/.hermes/.env`。\n\n:::warning\n`docker_forward_env` 中列出的任何内容都会对容器内运行的命令可见。只转发您愿意暴露给终端会话的凭据。\n:::\n\n### 以宿主用户身份运行容器\n\n默认情况下,Docker 容器以 `root`(UID 0)身份运行。在 `/workspace` 或其他绑定挂载中创建的文件在宿主上归 root 所有,因此会话结束后您必须 `sudo chown` 才能从宿主编辑器编辑它们。`terminal.docker_run_as_host_user` 标志解决了这个问题:\n\n```yaml\nterminal:\n backend: docker\n docker_run_as_host_user: true # 默认:false\n```\n\n启用后,Hermes 将 `--user $(id -u):$(id -g)` 附加到 `docker run` 命令,使写入绑定挂载目录(`/workspace`、`/root`、`docker_volumes` 中的任何内容)的文件归您的宿主用户所有,而非 root。权衡:容器将无法再 `apt install` 或写入 `/root/.npm` 等 root 拥有的路径 —— 如果您同时需要这两者,请使用 `HOME` 归非 root 用户所有的基础镜像(或在镜像构建时添加所需工具)。\n\n保持 `false`(默认)以获得向后兼容的行为。当您的工作流主要是\"编辑挂载的宿主文件\"且厌倦了 `sudo chown -R` 时,请开启此选项。\n\n### 可选:将启动目录挂载到 `/workspace`\n\nDocker 沙箱默认保持隔离。Hermes **不会**将您当前的宿主工作目录传入容器,除非您明确选择加入。\n\n在 `config.yaml` 中启用:\n\n```yaml\nterminal:\n backend: docker\n docker_mount_cwd_to_workspace: true\n```\n\n启用后:\n- 如果您从 `~/projects/my-app` 启动 Hermes,该宿主目录将绑定挂载到 `/workspace`\n- Docker 后端从 `/workspace` 开始\n- 文件工具和终端命令都能看到相同的挂载项目\n\n禁用时,`/workspace` 保持沙箱所有,除非您通过 `docker_volumes` 显式挂载内容。\n\n安全权衡:\n- `false` 保留沙箱边界\n- `true` 使沙箱直接访问您启动 Hermes 的目录\n\n仅在您有意希望容器处理实时宿主文件时才选择加入。\n\n### 持久 Shell\n\n默认情况下,每条终端命令在其自己的子进程中运行 —— 工作目录、环境变量和 shell 变量在命令之间重置。启用**持久 shell** 后,单个长期运行的 bash 进程在 `execute()` 调用之间保持存活,使状态在命令之间保持。\n\n这对 **SSH 后端**最有用,它还消除了每条命令的连接开销。持久 shell **对 SSH 默认启用**,对本地后端禁用。\n\n```yaml\nterminal:\n persistent_shell: true # 默认 —— 为 SSH 启用持久 shell\n```\n\n禁用:\n\n```bash\nhermes config set terminal.persistent_shell false\n```\n\n**跨命令保持的内容:**\n- 工作目录(`cd /tmp` 对下一条命令生效)\n- 导出的环境变量(`export FOO=bar`)\n- Shell 变量(`MY_VAR=hello`)\n\n**优先级:**\n\n| 级别 | 变量 | 默认值 |\n|-------|----------|---------|\n| 配置 | `terminal.persistent_shell` | `true` |\n| SSH 覆盖 | `TERMINAL_SSH_PERSISTENT` | 遵循配置 |\n| Local 覆盖 | `TERMINAL_LOCAL_PERSISTENT` | `false` |\n\n每个后端的环境变量具有最高优先级。如果您也想在本地后端使用持久 shell:\n\n```bash\nexport TERMINAL_LOCAL_PERSISTENT=true\n```\n\n:::note\n需要 `stdin_data` 或 sudo 的命令会自动回退到单次模式,因为持久 shell 的 stdin 已被 IPC 协议占用。\n:::\n\n有关每个后端的详细信息,请参阅[代码执行](features/code-execution.md)和 [README 的终端部分](features/tools.md)。\n\n## 技能设置\n\n技能可以通过其 SKILL.md frontmatter 声明自己的配置设置。这些是非机密值(路径、偏好、域设置),存储在 `config.yaml` 的 `skills.config` 命名空间下。\n\n```yaml\nskills:\n config:\n myplugin:\n path: ~/myplugin-data # 示例 —— 每个技能定义自己的键\n```\n\n**技能设置的工作原理:**\n\n- `hermes config migrate` 扫描所有已启用的技能,找到未配置的设置,并提供提示\n- `hermes config show` 在\"技能设置\"下显示所有技能设置及其所属技能\n- 技能加载时,其解析的配置值会自动注入到技能上下文中\n\n**手动设置值:**\n\n```bash\nhermes config set skills.config.myplugin.path ~/myplugin-data\n```\n\n有关在您自己的技能中声明配置设置的详细信息,请参阅[创建技能 — 配置设置](/developer-guide/creating-skills#config-settings-configyaml)。\n\n### Agent 创建技能写入的守卫\n\n当 agent 使用 `skill_manage` 创建、编辑、修补或删除技能时,Hermes 可以选择扫描新/更新的内容以查找危险关键字模式(凭据收集、明显的 prompt 注入、数据外泄指令)。扫描器**默认关闭** —— 合法触及 `~/.ssh/` 或提及 `$OPENAI_API_KEY` 的真实 agent 工作流触发启发式规则过于频繁。如果您希望扫描器在 agent 的技能写入落地前提示您,请重新开启:\n\n```yaml\nskills:\n guard_agent_created: true # 默认:false\n```\n\n开启后,任何被标记的 `skill_manage` 写入都会以审批提示的形式出现,并附带扫描器的理由。接受的写入落地;拒绝的写入向 agent 返回解释性错误。\n\n## 内存配置\n\n```yaml\nmemory:\n memory_enabled: true\n user_profile_enabled: true\n memory_char_limit: 2200 # ~800 tokens\n user_char_limit: 1375 # ~500 tokens\n```\n\n## 文件读取安全\n\n控制单次 `read_file` 调用可以返回多少内容。超过限制的读取将被拒绝,并向 agent 返回错误,提示使用 `offset` 和 `limit` 读取较小范围。这可以防止单次读取压缩的 JS 包或大型数据文件时淹没上下文窗口。\n\n```yaml\nfile_read_max_chars: 100000 # 默认 —— ~25-35K tokens\n```\n\n如果您使用具有大上下文窗口的模型并经常读取大文件,请调高此值。对于小上下文模型,请降低以保持读取高效:\n\n```yaml\n# 大上下文模型(200K+)\nfile_read_max_chars: 200000\n\n# 小型本地模型(16K 上下文)\nfile_read_max_chars: 30000\n```\n\nAgent 还会自动去重文件读取 —— 如果同一文件区域被读取两次且文件未更改,则返回轻量级存根而不是重新发送内容。这在上下文压缩后重置,以便 agent 在内容被摘要后可以重新读取文件。\n\n## 工具输出截断限制\n\n三个相关的上限控制工具在 Hermes 截断之前可以返回多少原始输出:\n\n```yaml\ntool_output:\n max_bytes: 50000 # 终端输出上限(字符)\n max_lines: 2000 # read_file 分页上限\n max_line_length: 2000 # read_file 行号视图中的每行上限\n```\n\n- **`max_bytes`** —— 当 `terminal` 命令产生超过此字符数的合并 stdout/stderr 时,Hermes 保留前 40% 和后 60%,并在中间插入 `[OUTPUT TRUNCATED]` 通知。默认 `50000`(典型分词器约 12-15K tokens)。\n- **`max_lines`** —— 单次 `read_file` 调用的 `limit` 参数上限。超过此值的请求将被截断,以防单次读取淹没上下文窗口。默认 `2000`。\n- **`max_line_length`** —— `read_file` 发出行号视图时应用的每行上限。超过此长度的行将被截断为此字符数,后跟 `... [truncated]`。默认 `2000`。\n\n对于具有大上下文窗口且每次调用可以承受更多原始输出的模型,请调高限制。对于小上下文模型,请降低以保持工具结果紧凑:\n\n```yaml\n# 大上下文模型(200K+)\ntool_output:\n max_bytes: 150000\n max_lines: 5000\n\n# 小型本地模型(16K 上下文)\ntool_output:\n max_bytes: 20000\n max_lines: 500\n```\n\n## 全局工具集禁用\n\n要在 CLI 和每个 gateway 平台上统一禁用特定工具集,请在 `agent.disabled_toolsets` 下列出其名称:\n\n```yaml\nagent:\n disabled_toolsets:\n - memory # 隐藏内存工具 + MEMORY_GUIDANCE 注入\n - web # 任何地方都不使用 web_search / web_extract\n```\n\n这在每个平台的工具配置(由 `hermes tools` 写入的 `platform_toolsets`)**之后**应用,因此此处列出的工具集始终被删除 —— 即使平台的已保存配置仍然列出它。当您希望有一个\"到处关闭 X\"的单一开关而不是编辑 `hermes tools` UI 中的 15+ 个平台行时,请使用此选项。\n\n留空列表或省略键不会产生任何效果。\n\n## Git Worktree 隔离\n\n启用隔离的 git worktree,以便在同一仓库上并行运行多个 agent:\n\n```yaml\nworktree: true # 始终创建 worktree(与 hermes -w 相同)\n# worktree: false # 默认 —— 仅在传递 -w 标志时\n```\n\n启用后,每个 CLI 会话在 `.worktrees/` 下创建一个带有自己分支的新 worktree。Agent 可以编辑文件、提交、推送和创建 PR,而不会相互干扰。干净的 worktree 在退出时删除;脏的 worktree 保留以供手动恢复。\n\n您还可以通过仓库根目录中的 `.worktreeinclude` 列出要复制到 worktree 的 gitignore 文件:\n\n```\n# .worktreeinclude\n.env\n.venv/\nnode_modules/\n```\n\n## 上下文压缩\n\nHermes 自动压缩长对话以保持在模型的上下文窗口内。压缩摘要器是一个单独的 LLM 调用 —— 您可以将其指向任何 provider 或端点。\n\n所有压缩设置都在 `config.yaml` 中(无环境变量)。\n\n### 完整参考\n\n```yaml\ncompression:\n enabled: true # 开启/关闭压缩\n threshold: 0.50 # 在上下文限制的此百分比时压缩\n target_ratio: 0.20 # 保留为最近尾部的阈值分数\n protect_last_n: 20 # 保持未压缩的最少最近消息数\n hygiene_hard_message_limit: 5000 # Gateway 安全阀 —— 见下文\n\n# 摘要模型/provider 在 auxiliary: 下配置:\nauxiliary:\n compression:\n model: \"\" # 空 = 使用主聊天模型。覆盖为例如 \"google/gemini-3-flash-preview\" 以获得更便宜/更快的压缩。\n provider: \"auto\" # Provider:\"auto\"、\"openrouter\"、\"nous\"、\"codex\"、\"main\" 等\n base_url: null # 自定义 OpenAI 兼容端点(覆盖 provider)\n```\n\n:::info 旧版配置迁移\n带有 `compression.summary_model`、`compression.summary_provider` 和 `compression.summary_base_url` 的旧版配置在首次加载时自动迁移到 `auxiliary.compression.*`(配置版本 17)。无需手动操作。\n:::\n\n`hygiene_hard_message_limit` 是仅限 gateway 的**预压缩安全阀**。它的存在是为了打破一个死循环:当超大会话的 API 调用持续断开时,gateway 永远收不到 token 使用数据,基于 token 的阈值因此无法触发,于是 transcript 持续增长、断开愈发严重。这个基于消息数的下限仅凭消息数量触发(无论 API 是否失败,消息数始终已知),强制压缩以恢复会话。默认 `5000` —— 远高于任何正常会话,包括做数千次短轮次的大上下文(1M+)模型,它们早就在 token 阈值处压缩了。对于异常平台可调得更高;要强制更积极的压缩则调低。在运行中的 gateway 上编辑此值将在下一条消息时生效(见下文)。\n\n:::tip Gateway 热重载压缩和上下文长度\n从最近的版本开始,在运行中的 gateway 上编辑 `config.yaml` 中的 `model.context_length` 或任何 `compression.*` 键将在下一条消息时生效 —— 无需 gateway 重启、`/reset` 或会话轮换。缓存的 agent 签名包含这些键,因此 gateway 在检测到更改时会透明地重建 agent。API 密钥和工具/技能配置仍需要通常的重载路径。\n:::\n\n### 常见设置\n\n**默认(自动检测)—— 无需配置:**\n```yaml\ncompression:\n enabled: true\n threshold: 0.50\n```\n使用您的主 provider 和主模型。如果您希望在比主聊天模型更便宜的模型上进行压缩,请覆盖每任务(例如 `auxiliary.compression.provider: openrouter` + `model: google/gemini-2.5-flash`)。\n\n**强制特定 provider**(基于 OAuth 或 API 密钥):\n```yaml\nauxiliary:\n compression:\n provider: nous\n model: gemini-3-flash\n```\n适用于任何 provider:`nous`、`openrouter`、`codex`、`anthropic`、`main` 等。\n\n**自定义端点**(自托管、Ollama、zai、DeepSeek 等):\n```yaml\nauxiliary:\n compression:\n model: glm-4.7\n base_url: https://api.z.ai/api/coding/paas/v4\n```\n指向自定义 OpenAI 兼容端点。使用 `OPENAI_API_KEY` 进行认证。\n\n### 三个旋钮的交互方式\n\n| `auxiliary.compression.provider` | `auxiliary.compression.base_url` | 结果 |\n|---------------------|---------------------|--------|\n| `auto`(默认) | 未设置 | 自动检测最佳可用 provider |\n| `nous` / `openrouter` / 等 | 未设置 | 强制使用该 provider,使用其认证 |\n| 任意 | 已设置 | 直接使用自定义端点(忽略 provider) |\n\n:::warning 摘要模型上下文长度要求\n摘要模型**必须**具有至少与您的主 agent 模型一样大的上下文窗口。压缩器将对话的完整中间部分发送给摘要模型 —— 如果该模型的上下文窗口小于主模型的,摘要调用将因上下文长度错误而失败。发生这种情况时,中间轮次将**在没有摘要的情况下被丢弃**,静默丢失对话上下文。如果您覆盖模型,请验证其上下文长度满足或超过您的主模型。\n:::\n\n## 上下文引擎\n\n上下文引擎控制在接近模型 token 限制时如何管理对话。内置的 `compressor` 引擎使用有损摘要(参见[上下文压缩](/developer-guide/context-compression-and-caching))。插件引擎可以用替代策略替换它。\n\n```yaml\ncontext:\n engine: \"compressor\" # 默认 —— 内置有损摘要\n```\n\n使用插件引擎(例如,用于无损上下文管理的 LCM):\n\n```yaml\ncontext:\n engine: \"lcm\" # 必须与插件名称匹配\n```\n\n插件引擎**永远不会自动激活** —— 您必须将 `context.engine` 显式设置为插件名称。可用引擎可以通过 `hermes plugins` → Provider Plugins → Context Engine 浏览和选择。\n\n有关内存插件的类似单选系统,请参阅[内存 Providers](/user-guide/features/memory-providers)。\n\n## 迭代预算\n\n当 agent 在处理具有许多工具调用的复杂任务时,它可能会耗尽其迭代预算(默认:90 轮)。Hermes **不会**在任务中途注入压力警告 —— 早期版本会在预算达到 70%/90% 时警告模型,这会导致模型过早放弃复杂任务,该机制已于 2026 年 4 月移除。\n\n取而代之的是,当预算真正耗尽(90/90)时,Hermes 注入一条消息要求模型收尾,并允许一次**宽限调用**以便其给出最终响应。如果该宽限调用仍未产生文本,则会要求 agent 总结已完成的工作。\n\n```yaml\nagent:\n max_turns: 90 # 每次对话轮次的最大迭代次数(默认:90)\n api_max_retries: 3 # 回退启动前每个 provider 的重试次数(默认:3)\n```\n\n当迭代预算完全耗尽时,CLI 向用户显示通知:`⚠ Iteration budget reached (90/90) — response may be incomplete`。\n\n`agent.api_max_retries` 控制 Hermes 在回退 provider 切换启动**之前**对瞬时错误(速率限制、连接断开、5xx)重试 provider API 调用的次数。默认为 `3` —— 总共四次尝试。如果您配置了[回退 providers](/user-guide/features/fallback-providers) 并希望更快地故障转移,请将其降至 `0`,这样主 provider 上的第一个瞬时错误会立即切换到回退,而不是对不稳定的端点进行重试。\n\n### API 超时\n\nHermes 对流式传输有单独的超时层,以及用于非流式调用的陈旧检测器。陈旧检测器仅在您将其保留为隐式默认值时才会自动调整本地 provider。\n\n| 超时 | 默认值 | 本地 providers | 配置/环境变量 |\n|---------|---------|----------------|--------------|\n| Socket 读取超时 | 120s | 自动提升至 1800s | `HERMES_STREAM_READ_TIMEOUT` |\n| 陈旧流检测 | 180s | 自动禁用 | `HERMES_STREAM_STALE_TIMEOUT` |\n| 陈旧非流检测 | 300s | 保持隐式时自动禁用 | `providers.<id>.stale_timeout_seconds` 或 `HERMES_API_CALL_STALE_TIMEOUT` |\n| API 调用(非流式) | 1800s | 不变 | `providers.<id>.request_timeout_seconds` / `timeout_seconds` 或 `HERMES_API_TIMEOUT` |\n\n**Socket 读取超时**控制 httpx 等待 provider 下一个数据块的时间。本地 LLM 在大上下文上预填充可能需要几分钟才能产生第一个 token,因此当 Hermes 检测到本地端点时,会将此值提升至 30 分钟。如果您显式设置 `HERMES_STREAM_READ_TIMEOUT`,无论端点检测如何,始终使用该值。\n\n**陈旧流检测**终止接收 SSE 保活 ping 但没有实际内容的连接。对于本地 providers,这完全禁用,因为它们在预填充期间不发送保活 ping。\n\n**陈旧非流检测**终止长时间没有响应的非流式调用。默认情况下,Hermes 在本地端点上禁用此功能,以避免长时间预填充期间的误报。如果您显式设置 `providers.<id>.stale_timeout_seconds`、`providers.<id>.models.<model>.stale_timeout_seconds` 或 `HERMES_API_CALL_STALE_TIMEOUT`,即使在本地端点上也会遵守该显式值。\n\n## 上下文压力警告\n\n与迭代预算压力分开,上下文压力跟踪对话距**压缩阈值**有多近 —— 即上下文压缩触发以摘要旧消息的点。这有助于您和 agent 了解对话何时变长。\n\n| 进度 | 级别 | 发生的事情 |\n|----------|-------|-------------|\n| **≥ 60%** 到阈值 | 信息 | CLI 显示青色进度条;gateway 发送信息通知 |\n| **≥ 85%** 到阈值 | 警告 | CLI 显示粗体黄色进度条;gateway 警告压缩即将发生 |\n\n在 CLI 中,上下文压力在工具输出流中显示为进度条:\n\n```\n ◐ context ████████████░░░░░░░░ 62% to compaction 48k threshold (50%) · approaching compaction\n```\n\n在消息平台上,发送纯文本通知:\n\n```\n◐ Context: ████████████░░░░░░░░ 62% to compaction (threshold: 50% of window).\n```\n\n如果自动压缩被禁用,警告会告诉您上下文可能被截断。\n\n上下文压力是自动的 —— 无需配置。它纯粹作为面向用户的通知触发,不修改消息流或向模型上下文注入任何内容。\n\n## 凭据池策略\n\n当您为同一 provider 拥有多个 API 密钥或 OAuth token 时,配置轮换策略:\n\n```yaml\ncredential_pool_strategies:\n openrouter: round_robin # 均匀循环使用密钥\n anthropic: least_used # 始终选择使用最少的密钥\n```\n\n选项:`fill_first`(默认)、`round_robin`、`least_used`、`random`。完整文档请参阅[凭据池](/user-guide/features/credential-pools)。\n\n## Prompt 缓存\n\n当活跃 provider 支持时,Hermes 自动开启跨会话 prompt 缓存 —— 无需用户配置。\n\n对于**原生 Anthropic**、**OpenRouter** 和 **Nous Portal** 上的 Claude,Hermes 在系统提示词和技能块上附加带有 1 小时 TTL(`ttl: \"1h\"`)的 `cache_control` 断点。在新鲜的一小时内首次发送时按完整输入费率计费;同一小时内任何会话的后续发送以折扣缓存读取费率从缓存中提取。这意味着系统提示词、加载的技能内容以及任何长上下文包含的早期部分在第一个小时内跨 `hermes` 会话和分叉子 agent 被重用。\n\nQwen Cloud(阿里巴巴 DashScope)上游将缓存 TTL 限制为 5 分钟,因此 Hermes 在那里使用 5 分钟断点 TTL。其他通过第三方的 Claude 路径(AWS Bedrock、Azure Foundry)回退到 provider 自己的缓存默认值。xAI Grok 使用单独的会话固定对话 ID 机制 —— 参阅 [xAI prompt 缓存](/integrations/providers#xai-grok--responses-api--prompt-caching)。\n\n不存在禁用此功能的旋钮 —— 缓存始终开启,即使在单轮对话中也能节省费用,因为仅系统提示词就占输入 token 数的相当大比例。\n\n## 辅助模型\n\nHermes 使用\"辅助\"模型处理图像分析、网页摘要、浏览器截图分析、会话标题生成和上下文压缩等附带任务。默认情况下(`auxiliary.*.provider: \"auto\"`),Hermes 将每个辅助任务路由到您的**主聊天模型** —— 与您在 `hermes model` 中选择的相同 provider/模型。您无需配置任何内容即可开始,但请注意,在昂贵的推理模型(Opus、MiniMax M2.7 等)上,辅助任务会增加显著成本。如果您希望无论主模型如何都使用便宜且快速的附带任务,请显式设置 `auxiliary.<task>.provider` 和 `auxiliary.<task>.model`(例如,在 OpenRouter 上使用 Gemini Flash 进行视觉和网页提取)。\n\n:::note 为什么 \"auto\" 使用您的主模型\n早期版本将聚合器用户(OpenRouter、Nous Portal)分流到便宜的 provider 端默认值。这令人惊讶 —— 付费购买聚合器订阅的用户会看到不同的模型处理其辅助流量。`auto` 现在对所有人使用主模型,`config.yaml` 中的每任务覆盖仍然优先(见下方[完整辅助配置参考](#full-auxiliary-config-reference))。\n:::\n\n### 交互式配置辅助模型\n\n无需手动编辑 YAML,运行 `hermes model` 并从菜单中选择**\"配置辅助模型\"**。您将获得交互式的每任务选择器:\n\n```\n$ hermes model\n→ Configure auxiliary models\n\n[ ] vision currently: auto / main model\n[ ] web_extract currently: auto / main model\n[ ] title_generation currently: openrouter / google/gemini-3-flash-preview\n[ ] compression currently: auto / main model\n[ ] approval currently: auto / main model\n[ ] triage_specifier currently: auto / main model\n[ ] kanban_decomposer currently: auto / main model\n[ ] profile_describer currently: auto / main model\n```\n\n选择任务,选择 provider(OAuth 流程打开浏览器;API 密钥 provider 提示输入),选择模型。更改持久化到 `config.yaml` 中的 `auxiliary.<task>.*`。与主模型选择器相同的机制 —— 无需学习额外语法。\n\n### 视频教程\n\n<div style={{position: 'relative', width: '100%', aspectRatio: '16 / 9', marginBottom: '1.5rem'}}>\n <iframe\n src=\"https://www.youtube.com/embed/NoF-YajElIM\"\n title=\"Hermes Agent — Auxiliary Models Tutorial\"\n style={{position: 'absolute', top: 0, left: 0, width: '100%', height: '100%', border: 0}}\n allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\"\n allowFullScreen\n />\n</div>\n\n### 通用配置模式\n\nHermes 中的每个模型槽位 —— 辅助任务、压缩、回退 —— 使用相同的三个旋钮:\n\n| 键 | 作用 | 默认值 |\n|-----|-------------|---------|\n| `provider` | 用于认证和路由的 provider | `\"auto\"` |\n| `model` | 请求的模型 | provider 的默认值 |\n| `base_url` | 自定义 OpenAI 兼容端点(覆盖 provider) | 未设置 |\n\n当设置 `base_url` 时,Hermes 忽略 provider 并直接调用该端点(使用 `api_key` 或 `OPENAI_API_KEY` 进行认证)。当仅设置 `provider` 时,Hermes 使用该 provider 的内置认证和基础 URL。\n\n辅助任务的可用 providers:`auto`、`main`,以及[provider 注册表](/reference/environment-variables)中的任何 provider —— `openrouter`、`nous`、`openai-codex`、`copilot`、`copilot-acp`、`anthropic`、`gemini`、`qwen-oauth`、`zai`、`kimi-coding`、`kimi-coding-cn`、`minimax`、`minimax-cn`、`minimax-oauth`、`deepseek`、`nvidia`、`xai`、`xai-oauth`、`ollama-cloud`、`alibaba`、`bedrock`、`huggingface`、`arcee`、`xiaomi`、`kilocode`、`opencode-zen`、`opencode-go`、`azure-foundry` —— 或您 `custom_providers` 列表中任何命名的自定义 provider(例如 `provider: \"beans\"`)。\n\n:::tip MiniMax OAuth\n`minimax-oauth` 通过浏览器 OAuth 登录(无需 API 密钥)。运行 `hermes model` 并选择 **MiniMax (OAuth)** 进行认证。辅助任务自动使用 `MiniMax-M2.7-highspeed`。参阅 [MiniMax OAuth 指南](../guides/minimax-oauth.md)。\n:::\n\n:::tip xAI Grok OAuth\n`xai-oauth` 通过浏览器 OAuth 为 SuperGrok 和 X Premium+ 订阅者登录(无需 API 密钥)。运行 `hermes model` 并选择 **xAI Grok OAuth (SuperGrok / Premium+)** 进行认证。相同的 OAuth token 可重用于每个直接到 xAI 的接口(聊天、辅助任务、TTS、图像生成、视频生成、转录)。参阅 [xAI Grok OAuth 指南](../guides/xai-grok-oauth.md),如果 Hermes 在远程主机上,请参阅 [SSH/远程主机上的 OAuth](../guides/oauth-over-ssh.md)。\n:::\n\n:::warning `\"main\"` 仅用于辅助任务\n`\"main\"` provider 选项表示\"使用我的主 agent 使用的任何 provider\" —— 它仅在 `auxiliary:`、`compression:` 和 `fallback_model:` 配置中有效。它**不是**顶级 `model.provider` 设置的有效值。如果您使用自定义 OpenAI 兼容端点,请在 `model:` 部分设置 `provider: custom`。所有主模型 provider 选项请参阅 [AI Providers](/integrations/providers)。\n:::\n\n### 完整辅助配置参考\n\n```yaml\nauxiliary:\n # 图像分析(vision_analyze 工具 + 浏览器截图)\n vision:\n provider: \"auto\" # \"auto\"、\"openrouter\"、\"nous\"、\"codex\"、\"main\" 等\n model: \"\" # 例如 \"openai/gpt-4o\"、\"google/gemini-2.5-flash\"\n base_url: \"\" # 自定义 OpenAI 兼容端点(覆盖 provider)\n api_key: \"\" # base_url 的 API 密钥(回退到 OPENAI_API_KEY)\n timeout: 120 # 秒 —— LLM API 调用超时;视觉负载需要宽裕的超时\n download_timeout: 30 # 秒 —— 图像 HTTP 下载;慢速连接请增加\n\n # 网页摘要 + 浏览器页面文本提取\n web_extract:\n provider: \"auto\"\n model: \"\" # 例如 \"google/gemini-2.5-flash\"\n base_url: \"\"\n api_key: \"\"\n timeout: 360 # 秒(6 分钟)—— 每次尝试的 LLM 摘要\n\n # 危险命令审批分类器\n approval:\n provider: \"auto\"\n model: \"\"\n base_url: \"\"\n api_key: \"\"\n timeout: 30 # 秒\n\n # 上下文压缩超时(与 compression.* 配置分开)\n compression:\n timeout: 120 # 秒 —— 压缩摘要长对话,需要更多时间\n # fallback_chain: # 可选 —— 发生速率限制/连接故障时尝试的 provider\n # - provider: nous\n # model: deepseek/deepseek-chat\n # - provider: openrouter\n # model: google/gemini-2.5-flash\n # base_url: \"\"\n # api_key: \"\"\n\n # 技能中心 —— 技能匹配和搜索\n skills_hub:\n provider: \"auto\"\n model: \"\"\n base_url: \"\"\n api_key: \"\"\n timeout: 30\n\n # MCP 工具调度\n mcp:\n provider: \"auto\"\n model: \"\"\n base_url: \"\"\n api_key: \"\"\n timeout: 30\n\n # Kanban 分类规格说明器 —— `hermes kanban specify <id>`(或\n # 仪表板上 Triage 列卡片的 ✨ Specify 按钮)使用此\n # 槽位将单行描述扩展为具体规格并将\n # 任务提升到 `todo`。便宜快速的模型在这里效果很好;规格扩展\n # 很短,不需要推理深度。\n triage_specifier:\n provider: \"auto\"\n model: \"\"\n base_url: \"\"\n api_key: \"\"\n timeout: 120\n```\n\n:::tip\n每个辅助任务都有可配置的 `timeout`(秒)。默认值:vision 120s、web_extract 360s、approval 30s、compression 120s。如果您为辅助任务使用慢速本地模型,请增加这些值。Vision 还有单独的 `download_timeout`(默认 30s)用于 HTTP 图像下载 —— 对于慢速连接或自托管图像服务器,请增加此值。\n:::\n\n:::info\n上下文压缩有自己的 `compression:` 块用于阈值,以及 `auxiliary.compression:` 块用于模型/provider 设置 —— 参阅上方的[上下文压缩](#context-compression)。主备用链使用顶层的 `fallback_providers:` 列表 —— 参阅[备用提供商](/integrations/providers#fallback-providers)。三者都遵循相同的 provider/model/base_url 模式。\n:::\n\n### 辅助任务的每任务回退链\n\n每个辅助任务都可以选择性地定义一个 `fallback_chain` —— 一个 provider/model 条目列表,当主要辅助 provider 因速率限制、网络连接问题或付费限制而失败时,Hermes 会尝试使用该列表:\n\n```yaml\nauxiliary:\n compression:\n provider: openrouter\n model: openai/gpt-4o-mini\n fallback_chain:\n - provider: nous\n model: deepseek/deepseek-chat\n - provider: openrouter\n model: google/gemini-2.5-flash\n```\n\n当主要辅助 provider(`openrouter` / `openai/gpt-4o-mini`)返回速率限制、连接超时或需要付费错误时,Hermes 将依次遍历 `fallback_chain`。它会跳过 provider 与已失败 provider 相同的条目,并尝试每个剩余条目,直到有一个成功或该链耗尽。如果所有回退都失败,Hermes 会回退到主 agent 模型作为最终的安全网。\n\n每个条目支持与任何辅助任务配置相同的三个旋钮:\n\n| 键 | 描述 |\n|-----|-------------|\n| `provider` | Provider 名称(`nous`、`openrouter`、`anthropic`、`gemini`、`main` 等) |\n| `model` | 该 provider 的模型名称 |\n| `base_url` | (可选)自定义 OpenAI 兼容端点 |\n\n`fallback_chain` 适用于任何辅助任务 —— `compression`、`vision`、`web_extract`、`approval`、`skills_hub`、`mcp` 等。\n\n### OpenRouter 路由和辅助任务的 Pareto Code\n\n当辅助任务解析到 OpenRouter(显式或通过 `provider: \"main\"` 而您的主 agent 在 OpenRouter 上)时,主 agent 的 `provider_routing` 和 `openrouter.min_coding_score` 设置**不会传播** —— 按设计,每个辅助任务是独立的。要为特定辅助任务设置 OpenRouter provider 偏好或使用 [Pareto Code 路由器](/integrations/providers#openrouter-pareto-code-router),请通过 `extra_body` 按任务设置:\n\n```yaml\nauxiliary:\n compression:\n provider: openrouter\n model: openrouter/pareto-code # 为此任务使用 Pareto Code 路由器\n extra_body:\n provider: # OpenRouter provider 路由偏好\n order: [anthropic, google] # 按此顺序尝试这些 providers\n sort: throughput # 或 \"price\" | \"latency\"\n # only: [anthropic] # 限制到特定 provider\n # ignore: [deepinfra] # 排除特定 providers\n plugins: # OpenRouter Pareto Code 路由器旋钮\n - id: pareto-router\n min_coding_score: 0.5 # 0.0–1.0;越高 = 更强的编码能力\n```\n\n形状与 OpenRouter 在聊天补全请求体中接受的内容一致。Hermes 原样转发整个 `extra_body`,因此 [openrouter.ai/docs](https://openrouter.ai/docs) 中记录的任何其他 OpenRouter 请求体字段都以相同方式工作。\n\n### 更改视觉模型\n\n使用 GPT-4o 而非 Gemini Flash 进行图像分析:\n\n```yaml\nauxiliary:\n vision:\n model: \"openai/gpt-4o\"\n```\n\n或通过环境变量(在 `~/.hermes/.env` 中):\n\n```bash\nAUXILIARY_VISION_MODEL=openai/gpt-4o\n```\n\n### Provider 选项\n\n这些选项适用于**辅助任务配置**(`auxiliary:`、`compression:`、`fallback_model:`),而非您的主 `model.provider` 设置。\n\n| Provider | 描述 | 要求 |\n|----------|-------------|-------------|\n| `\"auto\"` | 最佳可用(默认)。Vision 尝试 OpenRouter → Nous → Codex。 | — |\n| `\"openrouter\"` | 强制 OpenRouter —— 路由到任何模型(Gemini、GPT-4o、Claude 等) | `OPENROUTER_API_KEY` |\n| `\"nous\"` | 强制 Nous Portal | `hermes auth` |\n| `\"codex\"` | 强制 Codex OAuth(ChatGPT 账户)。支持视觉(gpt-5.3-codex)。 | `hermes model` → Codex |\n| `\"minimax-oauth\"` | 强制 MiniMax OAuth(浏览器登录,无需 API 密钥)。辅助任务使用 MiniMax-M2.7-highspeed。 | `hermes model` → MiniMax (OAuth) |\n| `\"xai-oauth\"` | 强制 xAI Grok OAuth(SuperGrok 或 X Premium+ 订阅者的浏览器登录,无需 API 密钥)。相同的 OAuth token 涵盖聊天、TTS、图像、视频和转录。 | `hermes model` → xAI Grok OAuth (SuperGrok / Premium+) |\n| `\"main\"` | 使用您的活跃自定义/主端点。可以来自 `OPENAI_BASE_URL` + `OPENAI_API_KEY` 或通过 `hermes model` / `config.yaml` 保存的自定义端点。适用于 OpenAI、本地模型或任何 OpenAI 兼容 API。**仅限辅助任务 —— 对 `model.provider` 无效。** | 自定义端点凭据 + 基础 URL |\n\n当您希望附带任务绕过默认路由器时,主 provider 目录中的直接 API 密钥 providers 也在这里工作。配置 `GMI_API_KEY` 后,`gmi` 有效:\n\n```yaml\nauxiliary:\n compression:\n provider: \"gmi\"\n model: \"anthropic/claude-opus-4.6\"\n```\n\n对于 GMI 辅助路由,使用 GMI 的 `/v1/models` 端点返回的确切模型 ID。\n\n### 常见设置\n\n**使用直接自定义端点**(比 `provider: \"main\"` 对本地/自托管 API 更清晰):\n```yaml\nauxiliary:\n vision:\n base_url: \"http://localhost:1234/v1\"\n api_key: \"local-key\"\n model: \"qwen2.5-vl\"\n```\n\n`base_url` 优先于 `provider`,因此这是将辅助任务路由到特定端点的最明确方式。对于直接端点覆盖,Hermes 使用配置的 `api_key` 或回退到 `OPENAI_API_KEY`;它不会为该自定义端点重用 `OPENROUTER_API_KEY`。\n\n**使用 OpenAI API 密钥进行视觉:**\n```yaml\n# 在 ~/.hermes/.env 中:\n# OPENAI_BASE_URL=https://api.openai.com/v1\n# OPENAI_API_KEY=sk-...\n\nauxiliary:\n vision:\n provider: \"main\"\n model: \"gpt-4o\" # 或 \"gpt-4o-mini\" 更便宜\n```\n\n**使用 OpenRouter 进行视觉**(路由到任何模型):\n```yaml\nauxiliary:\n vision:\n provider: \"openrouter\"\n model: \"openai/gpt-4o\" # 或 \"google/gemini-2.5-flash\" 等\n```\n\n**使用 Codex OAuth**(ChatGPT Pro/Plus 账户 —— 无需 API 密钥):\n```yaml\nauxiliary:\n vision:\n provider: \"codex\" # 使用您的 ChatGPT OAuth token\n # 模型默认为 gpt-5.3-codex(支持视觉)\n```\n\n**使用 MiniMax OAuth**(浏览器登录,无需 API 密钥):\n```yaml\nmodel:\n default: MiniMax-M2.7\n provider: minimax-oauth\n base_url: https://api.minimax.io/anthropic\n```\n运行 `hermes model` 并选择 **MiniMax (OAuth)** 自动登录并设置此项。对于中国区域,基础 URL 将是 `https://api.minimaxi.com/anthropic`。完整演练请参阅 [MiniMax OAuth 指南](../guides/minimax-oauth.md)。\n\n**使用本地/自托管模型:**\n```yaml\nauxiliary:\n vision:\n provider: \"main\" # 使用您的活跃自定义端点\n model: \"my-local-model\"\n```\n\n`provider: \"main\"` 使用 Hermes 用于普通聊天的任何 provider —— 无论是命名的自定义 provider(例如 `beans`)、内置 provider(如 `openrouter`)还是旧版 `OPENAI_BASE_URL` 端点。\n\n:::tip\n如果您使用 Codex OAuth 作为主模型 provider,视觉会自动工作 —— 无需额外配置。Codex 包含在视觉的自动检测链中。\n:::\n\n:::warning\n**视觉需要多模态模型。** 如果您设置 `provider: \"main\"`,请确保您的端点支持多模态/视觉 —— 否则图像分析将失败。\n:::\n\n### 环境变量(旧版)\n\n辅助模型也可以通过环境变量配置。但是,`config.yaml` 是首选方法 —— 它更易于管理,并支持所有选项,包括 `base_url` 和 `api_key`。\n\n| 设置 | 环境变量 |\n|---------|---------------------|\n| Vision provider | `AUXILIARY_VISION_PROVIDER` |\n| Vision 模型 | `AUXILIARY_VISION_MODEL` |\n| Vision 端点 | `AUXILIARY_VISION_BASE_URL` |\n| Vision API 密钥 | `AUXILIARY_VISION_API_KEY` |\n| Web 提取 provider | `AUXILIARY_WEB_EXTRACT_PROVIDER` |\n| Web 提取模型 | `AUXILIARY_WEB_EXTRACT_MODEL` |\n| Web 提取端点 | `AUXILIARY_WEB_EXTRACT_BASE_URL` |\n| Web 提取 API 密钥 | `AUXILIARY_WEB_EXTRACT_API_KEY` |\n\n压缩和回退模型设置仅限 config.yaml。\n\n:::tip\n运行 `hermes config` 查看您当前的辅助模型设置。覆盖仅在与默认值不同时显示。\n:::\n\n## 推理努力程度\n\n控制模型在响应前进行多少\"思考\":\n\n```yaml\nagent:\n reasoning_effort: \"\" # 空 = 中等。选项:none、minimal、low、medium、high、xhigh、max、ultra\n```\n\n未设置时(默认),推理努力程度默认为\"medium\" —— 适合大多数任务的平衡级别。设置值会覆盖它 —— 更高的推理努力程度在复杂任务上提供更好的结果,但代价是更多 token 和延迟。\n\n您也可以在运行时使用 `/reasoning` 命令更改推理努力程度:\n\n```\n/reasoning # 显示当前努力程度和显示状态\n/reasoning high # 将推理努力程度设为 high\n/reasoning none # 禁用推理\n/reasoning show # 在每次响应上方显示模型思考\n/reasoning hide # 隐藏模型思考\n```\n\n## 工具使用强制\n\n某些模型偶尔会将预期操作描述为文本而不是进行工具调用(\"我会运行测试...\"而不是实际调用终端)。工具使用强制会注入系统提示词指导,引导模型实际调用工具。\n\n```yaml\nagent:\n tool_use_enforcement: \"auto\" # \"auto\" | true | false | [\"model-substring\", ...]\n```\n\n| 值 | 行为 |\n|-------|----------|\n| `\"auto\"`(默认) | 对匹配以下模型启用:`gpt`、`codex`、`gemini`、`gemma`、`grok`。对所有其他模型禁用(Claude、DeepSeek、Qwen 等)。 |\n| `true` | 始终启用,无论模型如何。如果您注意到当前模型描述操作而不是执行操作,请使用此选项。 |\n| `false` | 始终禁用,无论模型如何。 |\n| `[\"gpt\", \"codex\", \"qwen\", \"llama\"]` | 仅当模型名称包含列出的子字符串之一时启用(不区分大小写)。 |\n\n### 注入的内容\n\n启用后,系统提示词中可能会添加三层指导:\n\n1. **通用工具使用强制**(所有匹配模型)—— 指示模型立即进行工具调用而不是描述意图,持续工作直到任务完成,永远不要以未来操作的承诺结束轮次。\n\n2. **OpenAI 执行纪律**(仅限 GPT 和 Codex 模型)—— 针对 GPT 特定失败模式的额外指导:在部分结果上放弃工作、跳过先决条件查找、幻觉而不是使用工具、在未验证的情况下宣布\"完成\"。\n\n3. **Google 操作指导**(仅限 Gemini 和 Gemma 模型)—— 简洁性、绝对路径、并行工具调用和编辑前验证模式。\n\n这些对用户透明,仅影响系统提示词。已经可靠使用工具的模型(如 Claude)不需要此指导,这就是为什么 `\"auto\"` 排除它们。\n\n### 何时开启\n\n如果您使用的模型不在默认自动列表中,并注意到它经常描述它*会*做什么而不是实际去做,请设置 `tool_use_enforcement: true` 或将模型子字符串添加到列表中:\n\n```yaml\nagent:\n tool_use_enforcement: [\"gpt\", \"codex\", \"gemini\", \"grok\", \"my-custom-model\"]\n```\n\n## TTS 配置\n\n```yaml\ntts:\n provider: \"edge\" # \"edge\" | \"elevenlabs\" | \"openai\" | \"minimax\" | \"mistral\" | \"gemini\" | \"xai\" | \"neutts\"\n speed: 1.0 # 全局速度倍数(所有 provider 的回退)\n edge:\n voice: \"en-US-AriaNeural\" # 322 种声音,74 种语言\n speed: 1.0 # 速度倍数(转换为速率百分比,例如 1.5 → +50%)\n elevenlabs:\n voice_id: \"pNInz6obpgDQGcFmaJgB\"\n model_id: \"eleven_multilingual_v2\"\n openai:\n model: \"gpt-4o-mini-tts\"\n voice: \"alloy\" # alloy、echo、fable、onyx、nova、shimmer\n speed: 1.0 # 速度倍数(API 限制为 0.25–4.0)\n base_url: \"https://api.openai.com/v1\" # 覆盖 OpenAI 兼容 TTS 端点\n minimax:\n speed: 1.0 # 语音速度倍数\n # base_url: \"\" # 可选:覆盖 OpenAI 兼容 TTS 端点\n mistral:\n model: \"voxtral-mini-tts-2603\"\n voice_id: \"c69964a6-ab8b-4f8a-9465-ec0925096ec8\" # Paul - Neutral(默认)\n gemini:\n model: \"gemini-2.5-flash-preview-tts\" # 或 gemini-2.5-pro-preview-tts\n voice: \"Kore\" # 30 种预置声音:Zephyr、Puck、Kore、Enceladus 等\n xai:\n voice_id: \"eve\" # xAI TTS 声音\n language: \"en\" # ISO 639-1\n sample_rate: 24000\n bit_rate: 128000 # MP3 比特率\n # base_url: \"https://api.x.ai/v1\"\n neutts:\n ref_audio: ''\n ref_text: ''\n model: neuphonic/neutts-air-q4-gguf\n device: cpu\n```\n\n这控制 `text_to_speech` 工具和语音模式中的口语回复(CLI 中的 `/voice tts` 或消息 gateway)。\n\n**速度回退层次:** provider 特定速度(例如 `tts.edge.speed`)→ 全局 `tts.speed` → `1.0` 默认值。设置全局 `tts.speed` 以在所有 provider 上应用统一速度,或按 provider 覆盖以进行精细控制。\n\n## 显示设置\n\n```yaml\ndisplay:\n tool_progress: all # off | new | all | verbose\n tool_progress_command: false # 在消息 gateway 中启用 /verbose 斜杠命令\n platforms: {} # 每平台显示覆盖(见下文)\n tool_progress_overrides: {} # 已弃用 —— 改用 display.platforms\n interim_assistant_messages: true # Gateway:将自然的轮次中 assistant 更新作为单独消息发送\n skin: default # 内置或自定义 CLI 皮肤(参阅 user-guide/features/skins)\n personality: \"kawaii\" # 旧版外观字段,仍在某些摘要中显示\n compact: false # 紧凑输出模式(减少空白)\n resume_display: full # full(恢复时显示之前的消息)| minimal(仅单行)\n bell_on_complete: false # 当 agent 完成时播放终端铃声(适合长任务)\n show_reasoning: false # 在每次响应上方显示模型推理/思考(用 /reasoning show|hide 切换)\n streaming: false # 将 token 实时流式传输到终端\n show_cost: false # 在 CLI 状态栏中显示估计 $ 成本\n timestamps: false # 为 true 时,在 CLI/TUI 记录中为用户和 assistant 标签添加 [HH:MM] 时间戳前缀\n tool_preview_length: 0 # 工具调用预览的最大字符数(0 = 无限制,显示完整路径/命令)\n runtime_footer: # Gateway:在最终回复中附加运行时上下文页脚\n enabled: false\n fields: [\"model\", \"context_pct\", \"cwd\"]\n file_mutation_verifier: true # 当本轮 write_file/patch 调用失败时附加建议性页脚\n language: en # 静态消息的 UI 语言(审批提示、部分 gateway 回复)。en | zh | zh-hant | ja | de | es | fr | tr | uk | af | ko | it | ga | pt | ru | hu\n```\n\n### 文件变更验证器\n\n当 `display.file_mutation_verifier` 为 `true`(默认)时,每当本轮中 `write_file` 或 `patch` 调用失败且从未被对同一路径的成功写入取代时,Hermes 会在 assistant 的最终响应中附加一行建议。这捕获了\"批量并行补丁,一半静默失败,模型总结成功\"这类过度声明,而无需您在每次编辑后手动运行 `git status`。\n\n示例页脚:\n\n```\n⚠️ File-mutation verifier: 3 file(s) were NOT modified this turn despite any wording above that may suggest otherwise. Run `git status` or `read_file` to confirm.\n • concepts/automatic-organization.md — [patch] Could not find match for old_string\n • concepts/lora.md — [patch] Could not find match for old_string\n • concepts/rag-pipeline.md — [patch] Could not find match for old_string\n```\n\n设置 `file_mutation_verifier: false`(或 `HERMES_FILE_MUTATION_VERIFIER=0`)以禁止页脚。验证器仅在轮次结束时有真实失败未解决时触发 —— 在同一轮次内重试失败补丁并成功的模型不会为该文件触发它。\n\n### 静态消息的 UI 语言\n\n`display.language` 设置翻译一小组静态面向用户的消息 —— CLI 审批提示、少数 gateway 斜杠命令回复(例如重启排空通知、\"审批已过期\"、\"目标已清除\")。它**不**翻译 agent 响应、日志行、工具输出、错误回溯或斜杠命令描述 —— 这些保持英文。如果您希望 agent 本身用另一种语言回复,只需在您的提示词或系统消息中告诉它。\n\n支持的值:`en`(默认)、`zh`(简体中文)、`zh-hant`(繁体中文)、`ja`(日语)、`de`(德语)、`es`(西班牙语)、`fr`(法语)、`tr`(土耳其语)、`uk`(乌克兰语)、`af`(南非荷兰语)、`ko`(韩语)、`it`(意大利语)、`ga`(爱尔兰语)、`pt`(葡萄牙语)、`ru`(俄语)、`hu`(匈牙利语)。未知值回退到英文。\n\n您也可以使用 `HERMES_LANGUAGE` 环境变量按会话设置,它会覆盖配置值。\n\n```yaml\ndisplay:\n language: zh # CLI 审批提示以中文显示\n```\n\n| 模式 | 您看到的内容 |\n|------|-------------|\n| `off` | 静默 —— 仅最终响应 |\n| `new` | 仅在工具更改时显示工具指示器 |\n| `all` | 每次工具调用附带简短预览(默认) |\n| `verbose` | 完整参数、结果和调试日志 |\n\n在 CLI 中,使用 `/verbose` 循环切换这些模式。要在消息平台(Telegram、Discord、Slack 等)中使用 `/verbose`,请在上方的 `display` 部分设置 `tool_progress_command: true`。该命令将循环切换模式并保存到配置。\n\n### 运行时元数据页脚(仅限 gateway)\n\n当 `display.runtime_footer.enabled: true` 时,Hermes 在每个 gateway 轮次的**最终**消息中附加一个小型运行时上下文页脚。目前页脚可显示模型、上下文窗口百分比和当前工作目录。默认关闭;如果您的团队希望每个回复都包含这些来源信息,请按 gateway 选择加入。\n\n```yaml\ndisplay:\n runtime_footer:\n enabled: true\n fields: [\"model\", \"context_pct\", \"cwd\"] # 支持字段:model、context_pct、cwd\n```\n\n`/footer` 斜杠命令在任何会话中运行时切换此功能。\n\n附加到 Telegram/Discord/Slack 回复的示例页脚:\n\n```\n— claude-opus-4.7 · 12 tool calls · 2m 14s · $0.042\n```\n\n只有轮次的**最终**消息获得页脚;中间更新保持干净。\n\n### 每平台进度覆盖\n\n不同平台有不同的详细程度需求。例如,Signal 无法编辑消息,因此每次进度更新都会成为单独的消息 —— 很嘈杂。使用 `display.platforms` 设置每平台模式:\n\n```yaml\ndisplay:\n tool_progress: all # 全局默认\n platforms:\n signal:\n tool_progress: 'off' # 在 Signal 上静默进度\n telegram:\n tool_progress: verbose # 在 Telegram 上详细进度\n slack:\n tool_progress: 'off' # 在共享 Slack 工作区中保持安静\n```\n\n没有覆盖的平台回退到全局 `tool_progress` 值。有效平台键:`telegram`、`discord`、`slack`、`signal`、`whatsapp`、`matrix`、`mattermost`、`email`、`sms`、`homeassistant`、`dingtalk`、`feishu`、`wecom`、`weixin`、`bluebubbles`、`qqbot`。旧版 `display.tool_progress_overrides` 键仍可加载以向后兼容,但已弃用,并在首次加载时迁移到 `display.platforms`。\n\n`interim_assistant_messages` 仅限 gateway。启用后,Hermes 将已完成的轮次中 assistant 更新作为单独的聊天消息发送。这与 `tool_progress` 无关,不需要 gateway 流式传输。\n\n## 隐私\n\n```yaml\nprivacy:\n redact_pii: false # 从 LLM 上下文中删除 PII(仅限 gateway)\n```\n\n当 `redact_pii` 为 `true` 时,gateway 在将系统提示词发送到受支持平台上的 LLM 之前,会从中删除个人身份信息:\n\n| 字段 | 处理方式 |\n|-------|-----------|\n| 电话号码(WhatsApp/Signal 上的用户 ID) | 哈希为 `user_<12-char-sha256>` |\n| 用户 ID | 哈希为 `user_<12-char-sha256>` |\n| 聊天 ID | 数字部分哈希,保留平台前缀(`telegram:<hash>`) |\n| 主频道 ID | 数字部分哈希 |\n| 用户名/昵称 | **不受影响**(用户选择的,公开可见) |\n\n**平台支持:** 删除适用于 WhatsApp、Signal 和 Telegram。Discord 和 Slack 被排除,因为它们的提及系统(`<@user_id>`)需要 LLM 上下文中的真实 ID。\n\n哈希是确定性的 —— 同一用户始终映射到同一哈希,因此模型仍然可以在群聊中区分用户。路由和传递在内部使用原始值。\n\n## 语音转文字(STT)\n\n```yaml\nstt:\n provider: \"local\" # \"local\" | \"groq\" | \"openai\" | \"mistral\"\n local:\n model: \"base\" # tiny、base、small、medium、large-v3\n openai:\n model: \"whisper-1\" # whisper-1 | gpt-4o-mini-transcribe | gpt-4o-transcribe\n # model: \"whisper-1\" # 旧版回退键仍受支持\n```\n\nProvider 行为:\n\n- `local` 使用在您机器上运行的 `faster-whisper`。使用 `pip install faster-whisper` 单独安装。\n- `groq` 使用 Groq 的 Whisper 兼容端点,读取 `GROQ_API_KEY`。\n- `openai` 使用 OpenAI 语音 API,读取 `VOICE_TOOLS_OPENAI_KEY`。\n\n如果请求的 provider 不可用,Hermes 按此顺序自动回退:`local` → `groq` → `openai`。\n\nGroq 和 OpenAI 模型覆盖由环境变量驱动:\n\n```bash\nSTT_GROQ_MODEL=whisper-large-v3-turbo\nSTT_OPENAI_MODEL=whisper-1\nGROQ_BASE_URL=https://api.groq.com/openai/v1\nSTT_OPENAI_BASE_URL=https://api.openai.com/v1\n```\n\n## 语音模式(CLI)\n\n```yaml\nvoice:\n record_key: \"ctrl+b\" # CLI 内的按键通话键\n max_recording_seconds: 120 # 长录音的硬停止\n auto_tts: false # /voice on 时自动启用口语回复\n beep_enabled: true # 在 CLI 语音模式中播放录音开始/停止提示音\n silence_threshold: 200 # 语音检测的 RMS 阈值\n silence_duration: 3.0 # 自动停止前的静默秒数\n```\n\n在 CLI 中使用 `/voice on` 启用麦克风模式,使用 `record_key` 开始/停止录音,使用 `/voice tts` 切换口语回复。端到端设置和平台特定行为请参阅[语音模式](/user-guide/features/voice-mode)。\n\n## 流式传输\n\n将 token 实时流式传输到终端或消息平台,而不是等待完整响应。\n\n### CLI 流式传输\n\n```yaml\ndisplay:\n streaming: true # 实时将 token 流式传输到终端\n show_reasoning: true # 同时流式传输推理/思考 token(可选)\n```\n\n启用后,响应在流式传输框内逐 token 出现。工具调用仍然静默捕获。如果 provider 不支持流式传输,它会自动回退到正常显示。\n\n### Gateway 流式传输(Telegram、Discord、Slack)\n\n```yaml\nstreaming:\n enabled: true # 启用渐进式消息编辑\n transport: edit # \"edit\"(渐进式消息编辑)或 \"off\"\n edit_interval: 0.3 # 消息编辑之间的秒数\n buffer_threshold: 40 # 强制编辑刷新前的字符数\n cursor: \" ▉\" # 流式传输期间显示的光标\n fresh_final_after_seconds: 0 # 预览超过此时间时选择发送新的最终消息(Telegram)\n```\n\n启用后,bot 在第一个 token 时发送消息,然后随着更多 token 到来渐进式编辑它。不支持消息编辑的平台(Signal、Email、Home Assistant)在第一次尝试时自动检测 —— 该会话的流式传输被优雅地禁用,不会产生大量消息。\n\n对于不带渐进式 token 编辑的独立自然轮次中 assistant 更新,请设置 `display.interim_assistant_messages: true`。\n\n**溢出处理:** 如果流式传输的文本超过平台的消息长度限制(约 4096 字符),当前消息被最终化,新消息自动开始。\n\n**新的最终消息(Telegram):** Telegram 的 `editMessageText` 保留原始消息时间戳,因此长时间运行的流式回复即使在完成后也会保留第一个 token 的时间戳。设置 `fresh_final_after_seconds > 0` 可选择将旧预览作为全新的最终消息传递,并尽力删除旧预览。默认值为 `0`,始终就地最终化流式回复,避免某些客户端短暂显示重复消息再删除其中一条。\n\n:::note\n主开关 `streaming.enabled` 默认为 `false`——在你启用之前不会有任何流式传输。启用后,是否流式传输按**平台**决定:Telegram 默认带有 `display.platforms.telegram.streaming: true`(流式传输),Discord 为 `display.platforms.discord.streaming: false`(不流式传输)。因此启用流式传输后,Telegram 开箱即用地流式传输,Discord 在你修改其开关之前仍使用整条消息回复。你可以在仪表盘的 **Channels** 开关中或直接在 `~/.hermes/config.yaml` 中调整这些按平台的开关。\n:::\n\n## 群聊会话隔离\n\n控制共享聊天是每个房间保持一个对话还是每个参与者一个对话:\n\n```yaml\ngroup_sessions_per_user: true # true = 群组/频道中每用户隔离,false = 每个聊天一个共享会话\n```\n\n- `true` 是默认和推荐设置。在 Discord 频道、Telegram 群组、Slack 频道和类似共享上下文中,当平台提供用户 ID 时,每个发送者获得自己的会话。\n- `false` 恢复到旧的共享房间行为。如果您明确希望 Hermes 将频道视为一个协作对话,这可能有用,但这也意味着用户共享上下文、token 成本和中断状态。\n- 私信不受影响。Hermes 仍然像往常一样通过聊天/DM ID 键入 DM。\n- 线程与其父频道保持隔离;使用 `true` 时,每个参与者在线程内也获得自己的会话。\n\n有关行为详情和示例,请参阅[会话](/user-guide/sessions)和 [Discord 指南](/user-guide/messaging/discord)。\n\n## 未授权 DM 行为\n\n控制当未知用户发送私信时 Hermes 的行为:\n\n```yaml\nunauthorized_dm_behavior: pair\n\nwhatsapp:\n unauthorized_dm_behavior: ignore\n```\n\n- `pair` 是默认值。Hermes 拒绝访问,但在 DM 中回复一次性配对码。\n- `ignore` 静默丢弃未授权的 DM。\n- 平台部分覆盖全局默认值,因此您可以在广泛范围内保持配对启用,同时使一个平台更安静。\n\n## 快速命令\n\n定义自定义命令,这些命令要么在不调用 LLM 的情况下运行 shell 命令,要么将一个斜杠命令别名为另一个。Exec 快速命令是零 token 的,对于从消息平台(Telegram、Discord 等)进行快速服务器检查或实用脚本很有用。\n\n```yaml\nquick_commands:\n status:\n type: exec\n command: systemctl status hermes-agent\n disk:\n type: exec\n command: df -h /\n update:\n type: exec\n command: cd ~/.hermes/hermes-agent && git pull && uv pip install -e .\n gpu:\n type: exec\n command: nvidia-smi --query-gpu=name,utilization.gpu,memory.used,memory.total --format=csv,noheader\n restart:\n type: alias\n target: /gateway restart\n```\n\n用法:在 CLI 或任何消息平台中输入 `/status`、`/disk`、`/update`、`/gpu` 或 `/restart`。`exec` 命令在宿主本地运行并直接返回输出 —— 无 LLM 调用,不消耗 token。`alias` 命令重写为配置的斜杠命令目标。\n\n- **30 秒超时** —— 长时间运行的命令被终止并显示错误消息\n- **优先级** —— 快速命令在技能命令之前检查,因此您可以覆盖技能名称\n- **自动补全** —— 快速命令在调度时解析,不显示在内置斜杠命令自动补全表中\n- **类型** —— 支持的类型为 `exec` 和 `alias`;其他类型显示错误\n- **到处可用** —— CLI、Telegram、Discord、Slack、WhatsApp、Signal、Email、Home Assistant\n\n仅字符串的 prompt 快捷方式不是有效的快速命令。对于可重用的 prompt 工作流,请创建技能或别名到现有斜杠命令。\n\n## 人类延迟\n\n在消息平台中模拟类人响应节奏:\n\n```yaml\nhuman_delay:\n mode: \"off\" # off | natural | custom\n min_ms: 800 # 最小延迟(自定义模式)\n max_ms: 2500 # 最大延迟(自定义模式)\n```\n\n## 代码执行\n\n配置 `execute_code` 工具:\n\n```yaml\ncode_execution:\n mode: project # project(默认)| strict\n timeout: 300 # 最大执行时间(秒)\n max_tool_calls: 50 # 代码执行中的最大工具调用次数\n```\n\n**`mode`** 控制脚本的工作目录和 Python 解释器:\n\n- **`project`**(默认)—— 脚本在会话的工作目录中以活跃 virtualenv/conda 环境的 python 运行。项目依赖(`pandas`、`torch`、项目包)和相对路径(`.env`、`./data.csv`)自然解析,与 `terminal()` 看到的一致。\n- **`strict`** —— 脚本在临时暂存目录中以 `sys.executable`(Hermes 自己的 python)运行。最大可重现性,但项目依赖和相对路径不会解析。\n\n环境清理(删除 `*_API_KEY`、`*_TOKEN`、`*_SECRET`、`*_PASSWORD`、`*_CREDENTIAL`、`*_PASSWD`、`*_AUTH`)和工具白名单在两种模式下完全相同 —— 切换模式不会改变安全态势。\n\n## Web 搜索后端\n\n`web_search` 和 `web_extract` 工具支持五种后端 provider。在 `config.yaml` 中或通过 `hermes tools` 配置后端:\n\n```yaml\nweb:\n backend: firecrawl # firecrawl | searxng | parallel | tavily | exa\n\n # 或使用每功能键混合 provider(例如免费搜索 + 付费提取):\n search_backend: \"searxng\"\n extract_backend: \"firecrawl\"\n```\n\n| 后端 | 环境变量 | 搜索 | 提取 |\n|---------|---------|--------|---------|\n| **Firecrawl**(默认) | `FIRECRAWL_API_KEY` | ✔ | ✔ |\n| **SearXNG** | `SEARXNG_URL` | ✔ | — |\n| **Parallel** | `PARALLEL_API_KEY` | ✔ | ✔ |\n| **Tavily** | `TAVILY_API_KEY` | ✔ | ✔ |\n| **Exa** | `EXA_API_KEY` | ✔ | ✔ |\n\n**后端选择:** 如果未设置 `web.backend`,后端从可用的 API 密钥自动检测。如果仅设置了 `SEARXNG_URL`,使用 SearXNG。如果仅设置了 `EXA_API_KEY`,使用 Exa。如果仅设置了 `TAVILY_API_KEY`,使用 Tavily。如果仅设置了 `PARALLEL_API_KEY`,使用 Parallel。否则 Firecrawl 是默认值。\n\n**SearXNG** 是一个免费、自托管、尊重隐私的元搜索引擎,查询 70+ 个搜索引擎。无需 API 密钥 —— 只需将 `SEARXNG_URL` 设置为您的实例(例如 `http://localhost:8080`)。SearXNG 仅限搜索;`web_extract` 需要单独的提取 provider(设置 `web.extract_backend`)。Docker 设置说明请参阅 [Web 搜索设置指南](/user-guide/features/web-search)。\n\n**自托管 Firecrawl:** 设置 `FIRECRAWL_API_URL` 指向您自己的实例。设置自定义 URL 后,API 密钥变为可选(在服务器上设置 `USE_DB_AUTHENTICATION=***` 以禁用认证)。\n\n**Parallel 搜索模式:** 设置 `PARALLEL_SEARCH_MODE` 控制搜索行为 —— `fast`、`one-shot` 或 `agentic`(默认:`agentic`)。\n\n**Exa:** 在 `~/.hermes/.env` 中设置 `EXA_API_KEY`。支持 `category` 过滤(`company`、`research paper`、`news`、`people`、`personal site`、`pdf`)和域名/日期过滤器。\n\n## 浏览器\n\n配置浏览器自动化行为:\n\n```yaml\nbrowser:\n inactivity_timeout: 120 # 自动关闭空闲会话前的秒数\n command_timeout: 30 # 浏览器命令超时(截图、导航等)(秒)\n record_sessions: false # 自动将浏览器会话录制为 WebM 视频到 ~/.hermes/browser_recordings/\n # 可选 CDP 覆盖 —— 设置后,Hermes 直接附加到您自己的\n # Chromium 系浏览器(通过 /browser connect),而不是启动无头浏览器。\n cdp_url: \"\"\n # 对话框监督器 —— 控制当 CDP 后端附加时(Browserbase、本地 Chromium 系\n # 浏览器通过 /browser connect)如何处理原生 JS 对话框(alert/confirm/prompt)。\n # 在 Camofox 和默认本地 agent 浏览器模式下忽略。\n dialog_policy: must_respond # must_respond | auto_dismiss | auto_accept\n dialog_timeout_s: 300 # must_respond 下的安全自动关闭(秒)\n camofox:\n managed_persistence: false # 为 true 时,Camofox 会话跨重启持久化 cookie/登录\n user_id: \"\" # 可选的外部管理 Camofox userId\n session_key: \"\" # Hermes 创建标签页时发送的可选会话密钥\n adopt_existing_tab: false # 在创建新标签页之前重用此身份的现有标签页\n```\n\n**对话框策略:**\n\n- `must_respond`(默认)—— 捕获对话框,在 `browser_snapshot.pending_dialogs` 中显示,等待 agent 调用 `browser_dialog(action=...)`。在 `dialog_timeout_s` 秒内无响应后,对话框被自动关闭以防止页面的 JS 线程永久停滞。\n- `auto_dismiss` —— 捕获,立即关闭。Agent 仍然在事后的 `browser_snapshot.recent_dialogs` 中看到对话框记录,`closed_by=\"auto_policy\"`。\n- `auto_accept` —— 捕获,立即接受。适用于有激进 `beforeunload` 提示的页面。\n\n完整对话框工作流请参阅[浏览器功能页面](./features/browser.md#browser_dialog)。\n\n浏览器工具集支持多个 provider。有关 Browserbase、Browser Use 和本地 Chromium 系 CDP 设置的详细信息,请参阅[浏览器功能页面](/user-guide/features/browser)。\n\n## 时区\n\n使用 IANA 时区字符串覆盖服务器本地时区。影响日志中的时间戳、cron 调度和系统提示词时间注入。\n\n```yaml\ntimezone: \"America/New_York\" # IANA 时区(默认:\"\" = 服务器本地时间)\n```\n\n支持的值:任何 IANA 时区标识符(例如 `America/New_York`、`Europe/London`、`Asia/Kolkata`、`UTC`)。留空或省略以使用服务器本地时间。\n\n## Discord\n\n为消息 gateway 配置 Discord 特定行为:\n\n```yaml\ndiscord:\n require_mention: true # 在服务器频道中需要 @提及才能响应\n free_response_channels: \"\" # 逗号分隔的频道 ID,bot 在这些频道无需 @提及即可响应\n auto_thread: true # 在频道中 @提及时自动创建线程\n```\n\n- `require_mention` —— 为 `true`(默认)时,bot 仅在服务器频道中被 `@BotName` 提及时响应。DM 始终无需提及即可工作。\n- `free_response_channels` —— 逗号分隔的频道 ID 列表,bot 在这些频道对每条消息响应,无需提及。\n- `auto_thread` —— 为 `true`(默认)时,频道中的提及会自动为对话创建线程,保持频道整洁(类似 Slack 线程)。\n\n## 安全\n\n预执行安全扫描和机密脱敏:\n\n```yaml\nsecurity:\n redact_secrets: false # 在工具输出和日志中脱敏 API 密钥模式(默认关闭)\n tirith_enabled: true # 为终端命令启用 Tirith 安全扫描\n tirith_path: \"tirith\" # tirith 二进制文件路径(默认:$PATH 中的 \"tirith\")\n tirith_timeout: 5 # 等待 tirith 扫描的秒数\n tirith_fail_open: true # 如果 tirith 不可用,允许命令执行\n website_blocklist: # 参见下方网站黑名单部分\n enabled: false\n domains: []\n shared_files: []\n```\n\n- `redact_secrets` —— 为 `true` 时,自动检测并脱敏工具输出中看起来像 API 密钥、token 和密码的模式,然后再进入对话上下文和日志。**默认关闭** —— 如果您经常在工具输出中处理真实凭据并希望有安全网,请启用。显式设置为 `true` 以开启。\n- `tirith_enabled` —— 为 `true` 时,终端命令在执行前由 [Tirith](https://github.com/sheeki03/tirith) 扫描以检测潜在危险操作。\n- `tirith_path` —— tirith 二进制文件的路径。如果 tirith 安装在非标准位置,请设置此项。\n- `tirith_timeout` —— 等待 tirith 扫描的最大秒数。如果扫描超时,命令继续执行。\n- `tirith_fail_open` —— 为 `true`(默认)时,如果 tirith 不可用或失败,允许命令执行。设置为 `false` 以在 tirith 无法验证时阻止命令。\n\n## 网站黑名单\n\n阻止 agent 的 web 和浏览器工具访问特定域名:\n\n```yaml\nsecurity:\n website_blocklist:\n enabled: false # 启用 URL 阻止(默认:false)\n domains: # 被阻止的域名模式列表\n - \"*.internal.company.com\"\n - \"admin.example.com\"\n - \"*.local\"\n shared_files: # 从外部文件加载额外规则\n - \"/etc/hermes/blocked-sites.txt\"\n```\n\n启用后,任何匹配被阻止域名模式的 URL 在 web 或浏览器工具执行之前都会被拒绝。这适用于 `web_search`、`web_extract`、`browser_navigate` 以及任何访问 URL 的工具。\n\n域名规则支持:\n- 精确域名:`admin.example.com`\n- 通配符子域名:`*.internal.company.com`(阻止所有子域名)\n- TLD 通配符:`*.local`\n\n共享文件每行包含一条域名规则(空行和 `#` 注释被忽略)。缺失或不可读的文件记录警告,但不禁用其他 web 工具。\n\n策略缓存 30 秒,因此配置更改无需重启即可快速生效。\n\n## 智能审批\n\n控制 Hermes 如何处理潜在危险命令:\n\n```yaml\napprovals:\n mode: smart # smart | manual | off\n```\n\n| 模式 | 行为 |\n|------|----------|\n| `smart`(默认) | 使用辅助 LLM 评估被标记的命令是否真正危险。低风险命令仅对当前命令自动批准,真正危险的命令自动拒绝,不确定的情况升级给用户。 |\n| `manual` | 在执行任何被标记的命令之前提示用户。在 CLI 中显示交互式审批对话框。在消息中排队待处理的审批请求。 |\n| `off` | 跳过所有审批检查。等同于 `HERMES_YOLO_MODE=true`。**谨慎使用。** |\n\n智能模式对于减少审批疲劳特别有用 —— 它让 agent 在安全操作上更自主地工作,同时仍然捕获真正破坏性的命令。\n\n:::warning\n设置 `approvals.mode: off` 会禁用终端命令的所有安全检查。仅在受信任的沙箱环境中使用。\n:::\n\n## 检查点\n\n破坏性文件操作之前的自动文件系统快照。详情请参阅[检查点与回滚](/user-guide/checkpoints-and-rollback)。\n\n```yaml\ncheckpoints:\n enabled: false # 启用自动检查点(也可:hermes chat --checkpoints)。默认:false(选择加入)。\n max_snapshots: 20 # 每个目录保留的最大检查点数(默认:20)\n```\n\n\n## 委托\n\n为委托工具配置子 agent 行为:\n\n```yaml\ndelegation:\n # model: \"google/gemini-3-flash-preview\" # 覆盖模型(空 = 继承父级)\n # provider: \"openrouter\" # 覆盖 provider(空 = 继承父级)\n # base_url: \"http://localhost:1234/v1\" # 直接 OpenAI 兼容端点(优先于 provider)\n # api_key: \"local-key\" # base_url 的 API 密钥(回退到 OPENAI_API_KEY)\n # api_mode: \"\" # base_url 的线路协议:\"chat_completions\"、\"codex_responses\" 或 \"anthropic_messages\"。空 = 从 URL 自动检测(例如 /anthropic 后缀 → anthropic_messages)。对启发式无法检测的非标准端点显式设置。\n max_concurrent_children: 3 # 每批并行子 agent 数(下限 1,无上限)。也可通过 DELEGATION_MAX_CONCURRENT_CHILDREN 环境变量设置。\n max_spawn_depth: 1 # 委托树深度上限(1-3,截断)。1 = 扁平(默认):父级生成无法委托的叶子。2 = 编排器子级可以生成叶子孙级。3 = 三级。\n orchestrator_enabled: true # 全局终止开关。为 false 时,role=\"orchestrator\" 被忽略,每个子级无论 max_spawn_depth 如何都被强制为叶子。\n```\n\n**子 agent provider:model 覆盖:** 默认情况下,子 agent 继承父 agent 的 provider 和模型。设置 `delegation.provider` 和 `delegation.model` 将子 agent 路由到不同的 provider:model 对 —— 例如,在您的主 agent 运行昂贵推理模型时,为范围较窄的子任务使用便宜/快速的模型。\n\n**直接端点覆盖:** 如果您想要明显的自定义端点路径,请设置 `delegation.base_url`、`delegation.api_key` 和 `delegation.model`。这将子 agent 直接发送到该 OpenAI 兼容端点,并优先于 `delegation.provider`。如果省略 `delegation.api_key`,Hermes 仅回退到 `OPENAI_API_KEY`。\n\n**线路协议(`api_mode`):** Hermes 从 `delegation.base_url` 自动检测线路协议(例如以 `/anthropic` 结尾的路径 → `anthropic_messages`;Codex/原生 Anthropic/Kimi-coding 主机名保留其现有检测)。对于启发式无法分类的端点 —— 例如 Azure AI Foundry、MiniMax、Zhipu GLM 或前置 Anthropic 形状后端的 LiteLLM 代理 —— 请将 `delegation.api_mode` 显式设置为 `chat_completions`、`codex_responses` 或 `anthropic_messages` 之一。留空(默认)以保持自动检测。\n\n委托 provider 使用与 CLI/gateway 启动相同的凭据解析。所有配置的 provider 均受支持:`openrouter`、`nous`、`copilot`、`zai`、`kimi-coding`、`minimax`、`minimax-cn`。设置 provider 时,系统自动解析正确的基础 URL、API 密钥和 API 模式 —— 无需手动凭据连接。\n\n**优先级:** 配置中的 `delegation.base_url` → 配置中的 `delegation.provider` → 父 provider(继承)。配置中的 `delegation.model` → 父模型(继承)。仅设置 `model` 而不设置 `provider` 仅更改模型名称,同时保留父级凭据(适用于在同一 provider(如 OpenRouter)内切换模型)。\n\n**宽度和深度:** `max_concurrent_children` 限制每批并行运行的子 agent 数量(默认 `3`,下限 1,无上限)。也可通过 `DELEGATION_MAX_CONCURRENT_CHILDREN` 环境变量设置。当模型提交的 `tasks` 数组超过上限时,`delegate_task` 返回工具错误解释限制,而不是静默截断。`max_spawn_depth` 控制委托树深度(截断到 1-3)。在默认 `1` 时,委托是扁平的:子级无法生成孙级,传递 `role=\"orchestrator\"` 静默降级为 `leaf`。提升到 `2` 使编排器子级可以生成叶子孙级;`3` 用于三级树。Agent 通过 `role=\"orchestrator\"` 按调用选择编排;`orchestrator_enabled: false` 强制每个子级回到叶子,无论如何。成本呈乘法增长 —— 在 `max_spawn_depth: 3` 和 `max_concurrent_children: 3` 时,树可以达到 3×3×3 = 27 个并发叶子 agent。使用模式请参阅[子 Agent 委托 → 深度限制和嵌套编排](features/delegation.md#depth-limit-and-nested-orchestration)。\n\n## 澄清\n\n配置澄清提示行为:\n\n```yaml\nclarify:\n timeout: 120 # 等待用户澄清响应的秒数\n```\n\n## 上下文文件(SOUL.md、AGENTS.md)\n\nHermes 使用两种不同的上下文范围:\n\n| 文件 | 用途 | 范围 |\n|------|---------|-------|\n| `SOUL.md` | **主要 agent 身份** —— 定义 agent 是谁(系统提示词第 #1 槽位) | `~/.hermes/SOUL.md` 或 `$HERMES_HOME/SOUL.md` |\n| `.hermes.md` / `HERMES.md` | 项目特定指令(最高优先级) | 向上走到 git 根目录 |\n| `AGENTS.md` | 项目特定指令、编码规范 | 递归目录遍历 |\n| `CLAUDE.md` | Claude Code 上下文文件(也会检测) | 仅工作目录 |\n| `.cursorrules` | Cursor IDE 规则(也会检测) | 仅工作目录 |\n| `.cursor/rules/*.mdc` | Cursor 规则文件(也会检测) | 仅工作目录 |\n\n- **SOUL.md** 是 agent 的主要身份。它占据系统提示词的第 #1 槽位,完全替换内置的默认身份。编辑它以完全自定义 agent 是谁。\n- 如果 SOUL.md 缺失、为空或无法加载,Hermes 回退到内置默认身份。\n- **项目上下文文件使用优先级系统** —— 仅加载一种类型(第一个匹配优先):`.hermes.md` → `AGENTS.md` → `CLAUDE.md` → `.cursorrules`。SOUL.md 始终独立加载。\n- **AGENTS.md** 是分层的:如果子目录也有 AGENTS.md,所有都会合并。\n- 如果 `SOUL.md` 不存在,Hermes 会自动生成默认的 `SOUL.md`。\n- 所有加载的上下文文件上限为 20,000 字符,并进行智能截断。\n\n另请参阅:\n- [个性与 SOUL.md](/user-guide/features/personality)\n- [上下文文件](/user-guide/features/context-files)\n\n## 工作目录\n\n| 上下文 | 默认值 |\n|---------|---------|\n| **CLI(`hermes`)** | 运行命令的当前目录 |\n| **消息 gateway** | 主目录 `~`(用 `MESSAGING_CWD` 覆盖) |\n| **Docker / Singularity / Modal / SSH** | 容器或远程机器内用户的主目录 |\n\n覆盖工作目录:\n```bash\n# 在 ~/.hermes/.env 或 ~/.hermes/config.yaml 中:\nMESSAGING_CWD=/home/myuser/projects # Gateway 会话\nTERMINAL_CWD=/workspace # 所有终端会话\n```\n"}, "7550c594ce18d7d100014c2120112576efb03c26": {"t": 1785767981.0319169, "n": 56663, "h": "06632195519ff9d4", "c": "---\nsidebar_position: 2\ntitle: \"配置\"\ndescription: \"配置 Hermes Agent — config.yaml、providers、模型、API 密钥等\"\n---\n\n# 配置\n\n所有设置均存储在 `~/.hermes/` 目录中,便于访问。\n\n## 目录结构\n\n```text\n~/.hermes/\n├── config.yaml # 设置(模型、终端、TTS、压缩等)\n├── .env # API 密钥和机密\n├── auth.json # OAuth provider 凭据(Nous Portal 等)\n├── SOUL.md # 主要 agent 身份(系统提示词第 #1 槽位)\n├── memories/ # 持久记忆(MEMORY.md、USER.md)\n├── skills/ # Agent 创建的技能(通过 skill_manage 工具管理)\n├── cron/ # 定时任务\n├── sessions/ # Gateway 会话\n└── logs/ # 日志(errors.log、gateway.log — 机密自动脱敏)\n```\n\n## 管理配置\n\n```bash\nhermes config # 查看当前配置\nhermes config edit # 在编辑器中打开 config.yaml\nhermes config set KEY VAL # 设置特定值\nhermes config check # 检查缺失选项(更新后使用)\nhermes config migrate # 交互式添加缺失选项\n\n# 示例:\nhermes config set model anthropic/claude-opus-4\nhermes config set terminal.backend docker\nhermes config set OPENROUTER_API_KEY sk-or-... # 保存到 .env\n```\n\n:::tip\n`hermes config set` 命令会自动将值路由到正确的文件 —— API 密钥保存到 `.env`,其他所有内容保存到 `config.yaml`。\n:::\n\n## 配置优先级\n\n设置按以下顺序解析(优先级从高到低):\n\n1. **CLI 参数** —— 例如 `hermes chat --model anthropic/claude-sonnet-4`(单次调用覆盖)\n2. **`~/.hermes/config.yaml`** —— 所有非机密设置的主配置文件\n3. **`~/.hermes/.env`** —— 环境变量的回退;机密(API 密钥、token、密码)**必须**放这里\n4. **内置默认值** —— 未设置任何内容时的硬编码安全默认值\n\n:::info 经验法则\n机密(API 密钥、bot token、密码)放入 `.env`。其他所有内容(模型、终端后端、压缩设置、内存限制、工具集)放入 `config.yaml`。当两者都设置时,`config.yaml` 对非机密设置优先。\n:::\n\n## 环境变量替换\n\n可以在 `config.yaml` 中使用 `${VAR_NAME}` 语法引用环境变量:\n\n```yaml\nauxiliary:\n vision:\n api_key: ${GOOGLE_API_KEY}\n base_url: ${CUSTOM_VISION_URL}\n\ndelegation:\n api_key: ${DELEGATION_KEY}\n```\n\n单个值中可以有多个引用:`url: \"${HOST}:${PORT}\"`。如果引用的变量未设置,占位符将保持原样(`${UNDEFINED_VAR}` 保持不变)。仅支持 `${VAR}` 语法 —— 裸 `$VAR` 不会被展开。\n\n关于 AI provider 设置(OpenRouter、Anthropic、Copilot、自定义端点、自托管 LLM、回退模型等),请参阅 [AI Providers](/integrations/providers)。\n\n### Provider 超时\n\n可以为 provider 设置 `providers.<id>.request_timeout_seconds` 作为全局请求超时,以及 `providers.<id>.models.<model>.timeout_seconds` 作为特定模型的覆盖值。适用于每种传输方式(OpenAI-wire、原生 Anthropic、Anthropic 兼容)上的主轮次客户端、回退链、凭据轮换后的重建,以及(对于 OpenAI-wire)每请求超时 kwarg —— 因此配置值优先于旧版 `HERMES_API_TIMEOUT` 环境变量。\n\n还可以设置 `providers.<id>.stale_timeout_seconds` 用于非流式陈旧调用检测器,以及 `providers.<id>.models.<model>.stale_timeout_seconds` 作为特定模型的覆盖值。此值优先于旧版 `HERMES_API_CALL_STALE_TIMEOUT` 环境变量。\n\n不设置这些值将保持旧版默认值(`HERMES_API_TIMEOUT=1800`s、`HERMES_API_CALL_STALE_TIMEOUT=90`s、原生 Anthropic 900s)。隐式的非流式 stale 检测会在本地端点上自动禁用,并且会在超大上下文下自动放宽。目前不适用于 AWS Bedrock(`bedrock_converse` 和 AnthropicBedrock SDK 路径均使用 boto3 及其自身的超时配置)。请参阅 [`cli-config.yaml.example`](https://github.com/NousResearch/hermes-agent/blob/main/cli-config.yaml.example) 中的注释示例。\n\n## 终端后端配置\n\nHermes 支持六种终端后端。每种后端决定 agent 的 shell 命令实际在哪里执行 —— 本地机器、Docker 容器、通过 SSH 的远程服务器、Modal 云沙箱(直接或通过 Nous 托管的 gateway)、Daytona 工作区,或 Singularity/Apptainer 容器。\n\n```yaml\nterminal:\n backend: local # local | docker | ssh | modal | daytona | singularity\n cwd: \".\" # Gateway/cron 工作目录(CLI 始终使用启动目录)\n timeout: 180 # 每条命令的超时时间(秒)\n env_passthrough: [] # 转发到沙箱执行的环境变量名(terminal + execute_code)\n singularity_image: \"docker://nikolaik/python-nodejs:python3.11-nodejs20\" # Singularity 后端的容器镜像\n modal_image: \"nikolaik/python-nodejs:python3.11-nodejs20\" # Modal 后端的容器镜像\n daytona_image: \"nikolaik/python-nodejs:python3.11-nodejs20\" # Daytona 后端的容器镜像\n```\n\n对于 Modal 和 Daytona 等云沙箱,`container_persistent: true` 表示 Hermes 将尝试在沙箱重建后保留文件系统状态。这并不保证相同的活跃沙箱、PID 空间或后台进程之后仍在运行。\n\n### 后端概览\n\n| 后端 | 命令运行位置 | 隔离性 | 最适合 |\n|---------|-------------------|-----------|----------|\n| **local** | 直接在您的机器上 | 无 | 开发、个人使用 |\n| **docker** | 单个持久 Docker 容器(跨会话、`/new`、子 agent 共享) | 完全(命名空间、cap-drop) | 安全沙箱、CI/CD |\n| **ssh** | 通过 SSH 的远程服务器 | 网络边界 | 远程开发、强大硬件 |\n| **modal** | Modal 云沙箱 | 完全(云 VM) | 临时云计算、评估 |\n| **daytona** | Daytona 工作区 | 完全(云容器) | 托管云开发环境 |\n| **singularity** | Singularity/Apptainer 容器 | 命名空间(--containall) | HPC 集群、共享机器 |\n\n### Local 后端\n\n默认后端。命令直接在您的机器上运行,无隔离。无需特殊设置。\n\n```yaml\nterminal:\n backend: local\n```\n\n:::warning\nAgent 拥有与您的用户账户相同的文件系统访问权限。使用 `hermes tools` 禁用不需要的工具,或切换到 Docker 进行沙箱隔离。\n:::\n\n### Docker 后端\n\n在具有安全加固的 Docker 容器内运行命令(所有权限已删除、无权限提升、PID 限制)。\n\n**单个持久容器,而非每条命令一个容器。** Hermes 在首次使用时启动一个长期运行的容器,并通过 `docker exec` 将每个终端、文件和 `execute_code` 调用路由到同一容器中 —— 跨会话、`/new`、`/reset` 和 `delegate_task` 子 agent,贯穿 Hermes 进程的整个生命周期。工作目录更改、已安装的包以及 `/workspace` 中的文件会从一次工具调用延续到下一次,就像本地 shell 一样。容器在关闭时停止并删除。详情请参阅下方的**容器生命周期**。\n\n```yaml\nterminal:\n backend: docker\n docker_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n docker_mount_cwd_to_workspace: false # 将启动目录挂载到 /workspace\n docker_run_as_host_user: false # 参见下方\"以宿主用户身份运行容器\"\n docker_forward_env: # 转发到容器的环境变量\n - \"GITHUB_TOKEN\"\n docker_volumes: # 宿主目录挂载\n - \"/home/user/projects:/workspace/projects\"\n - \"/home/user/data:/data:ro\" # :ro 表示只读\n docker_extra_args: # 附加到 `docker run` 的额外标志\n - \"--gpus=all\"\n - \"--network=host\"\n\n # 资源限制\n container_cpu: 1 # CPU 核心数(0 = 不限制)\n container_memory: 5120 # MB(0 = 不限制)\n container_disk: 51200 # MB(需要 XFS+pquota 上的 overlay2)\n container_persistent: true # 跨会话持久化 /workspace 和 /root\n```\n\n**`terminal.docker_extra_args`**(也可通过 `TERMINAL_DOCKER_EXTRA_ARGS='[\"--gpus=all\"]'` 覆盖)允许传递 Hermes 未作为一级键公开的任意 `docker run` 标志 —— `--gpus`、`--network`、`--add-host`、替代 `--security-opt` 覆盖等。每个条目必须是字符串;该列表最后附加到组装好的 `docker run` 调用中,因此可以在需要时覆盖 Hermes 的默认值。请谨慎使用 —— 与沙箱加固(权限删除、`--user`、workspace 绑定挂载)冲突的标志将悄然削弱隔离性。\n\n**要求:** 已安装并运行 Docker Desktop 或 Docker Engine。Hermes 会探测 `$PATH` 以及常见的 macOS 安装位置(`/usr/local/bin/docker`、`/opt/homebrew/bin/docker`、Docker Desktop 应用包)。开箱即用支持 Podman:设置 `HERMES_DOCKER_BINARY=podman`(或完整路径)以在两者都安装时强制使用它。\n\n**容器生命周期:** Hermes 为每个终端和文件工具调用重用单个长期运行的容器(`docker run -d ... sleep 2h`),跨会话、`/new`、`/reset` 和 `delegate_task` 子 agent,贯穿 Hermes 进程的整个生命周期。命令通过带登录 shell 的 `docker exec` 运行,因此工作目录更改、已安装的包以及 `/workspace` 中的文件都会从一次工具调用延续到下一次。容器在 Hermes 关闭时(或空闲清理回收时)停止并删除。\n\n通过 `delegate_task(tasks=[...])` 生成的并行子 agent 共享这一个容器 —— 并发的 `cd`、环境变量修改以及对同一路径的写入会发生冲突。如果子 agent 需要隔离的沙箱,必须通过 `register_task_env_overrides()` 注册每任务镜像覆盖,RL 和基准测试环境(TerminalBench2、HermesSweEnv 等)会自动为其每任务 Docker 镜像执行此操作。\n\n**安全加固:**\n- `--cap-drop ALL`,仅添加回 `DAC_OVERRIDE`、`CHOWN`、`FOWNER`\n- `--security-opt no-new-privileges`\n- `--pids-limit 256`\n- `/tmp`(512MB)、`/var/tmp`(256MB)、`/run`(64MB)的大小限制 tmpfs\n\n**凭据转发:** `docker_forward_env` 中列出的环境变量首先从您的 shell 环境解析,然后回退到 `~/.hermes/.env`。技能也可以声明 `required_environment_variables`,这些变量会自动合并。\n\n### SSH 后端\n\n通过 SSH 在远程服务器上运行命令。使用 ControlMaster 进行连接复用(5 分钟空闲保活)。默认启用持久 shell —— 状态(cwd、环境变量)在命令之间保持。\n\n```yaml\nterminal:\n backend: ssh\n persistent_shell: true # 保持长期运行的 bash 会话(默认:true)\n```\n\n**必需的环境变量:**\n\n```bash\nTERMINAL_SSH_HOST=my-server.example.com\nTERMINAL_SSH_USER=ubuntu\n```\n\n**可选:**\n\n| 变量 | 默认值 | 描述 |\n|----------|---------|-------------|\n| `TERMINAL_SSH_PORT` | `22` | SSH 端口 |\n| `TERMINAL_SSH_KEY` | (系统默认) | SSH 私钥路径 |\n| `TERMINAL_SSH_PERSISTENT` | `true` | 启用持久 shell |\n\n**工作原理:** 使用 `BatchMode=yes` 和 `StrictHostKeyChecking=accept-new` 在初始化时连接。持久 shell 在远程主机上保持单个 `bash -l` 进程存活,通过临时文件进行通信。需要 `stdin_data` 或 `sudo` 的命令会自动回退到单次模式。\n\n### Modal 后端\n\n在 [Modal](https://modal.com) 云沙箱中运行命令。每个任务获得一个具有可配置 CPU、内存和磁盘的隔离 VM。文件系统可以跨会话快照/恢复。\n\n```yaml\nterminal:\n backend: modal\n container_cpu: 1 # CPU 核心数\n container_memory: 5120 # MB(5GB)\n container_disk: 51200 # MB(50GB)\n container_persistent: true # 快照/恢复文件系统\n```\n\n**必需:** `MODAL_TOKEN_ID` + `MODAL_TOKEN_SECRET` 环境变量,或 `~/.modal.toml` 配置文件。\n\n**持久化:** 启用后,沙箱文件系统在清理时快照,并在下次会话时恢复。快照在 `~/.hermes/modal_snapshots.json` 中跟踪。这保留文件系统状态,而非活跃进程、PID 空间或后台任务。\n\n**凭据文件:** 自动从 `~/.hermes/` 挂载(OAuth token 等),并在每条命令前同步。\n\n### Daytona 后端\n\n在 [Daytona](https://daytona.io) 托管工作区中运行命令。支持停止/恢复以实现持久化。\n\n```yaml\nterminal:\n backend: daytona\n container_cpu: 1 # CPU 核心数\n container_memory: 5120 # MB → 转换为 GiB\n container_disk: 10240 # MB → 转换为 GiB(最大 10 GiB)\n container_persistent: true # 停止/恢复而非删除\n```\n\n**必需:** `DAYTONA_API_KEY` 环境变量。\n\n**持久化:** 启用后,沙箱在清理时停止(而非删除),并在下次会话时恢复。沙箱名称遵循 `hermes-{task_id}` 模式。\n\n**磁盘限制:** Daytona 强制执行 10 GiB 最大值。超过此值的请求将被截断并发出警告。\n\n### Singularity/Apptainer 后端\n\n在 [Singularity/Apptainer](https://apptainer.org) 容器中运行命令。专为 Docker 不可用的 HPC 集群和共享机器设计。\n\n```yaml\nterminal:\n backend: singularity\n singularity_image: \"docker://nikolaik/python-nodejs:python3.11-nodejs20\"\n container_cpu: 1 # CPU 核心数\n container_memory: 5120 # MB\n container_persistent: true # 可写覆盖层跨会话持久化\n```\n\n**要求:** `$PATH` 中有 `apptainer` 或 `singularity` 二进制文件。\n\n**镜像处理:** Docker URL(`docker://...`)自动转换为 SIF 文件并缓存。现有 `.sif` 文件直接使用。\n\n**临时目录:** 按顺序解析:`TERMINAL_SCRATCH_DIR` → `TERMINAL_SANDBOX_DIR/singularity` → `/scratch/$USER/hermes-agent`(HPC 惯例)→ `~/.hermes/sandboxes/singularity`。\n\n**隔离:** 使用 `--containall --no-home` 实现完全命名空间隔离,不挂载宿主 home 目录。\n\n### 常见终端后端问题\n\n如果终端命令立即失败或终端工具报告为已禁用:\n\n- **Local** —— 无特殊要求。入门时最安全的默认选项。\n- **Docker** —— 运行 `docker version` 验证 Docker 是否正常工作。如果失败,修复 Docker 或执行 `hermes config set terminal.backend local`。\n- **SSH** —— `TERMINAL_SSH_HOST` 和 `TERMINAL_SSH_USER` 都必须设置。如果缺少任一项,Hermes 会记录清晰的错误。\n- **Modal** —— 需要 `MODAL_TOKEN_ID` 环境变量或 `~/.modal.toml`。运行 `hermes doctor` 检查。\n- **Daytona** —— 需要 `DAYTONA_API_KEY`。Daytona SDK 处理服务器 URL 配置。\n- **Singularity** —— 需要 `$PATH` 中有 `apptainer` 或 `singularity`。HPC 集群上常见。\n\n如有疑问,将 `terminal.backend` 设回 `local` 并首先验证命令在那里运行。\n\n### 拆卸时远程到宿主文件同步\n\n对于 **SSH**、**Modal** 和 **Daytona** 后端(agent 的工作树位于与运行 Hermes 的宿主不同的机器上),Hermes 跟踪 agent 在远程沙箱中触及的文件,并在会话拆卸/沙箱清理时,将修改的文件**同步回宿主**,存放在 `~/.hermes/cache/remote-syncs/<session-id>/` 下。\n\n- 触发时机:会话关闭、`/new`、`/reset`、gateway 消息超时、子 agent 使用远程后端时 `delegate_task` 子 agent 完成。\n- 覆盖 agent 修改的整个树,而不仅仅是它明确打开的文件。添加、编辑和删除都会被捕获。\n- 远程沙箱可能在您查找时已被拆除;本地 `~/.hermes/cache/remote-syncs/…` 副本是 agent 更改内容的权威记录。\n- 大型二进制输出(模型检查点、原始数据集)按大小限制 —— 同步跳过超过 `file_sync_max_mb`(默认 `100`)的文件。如果您期望更大的工件返回,请调高该值。\n\n```yaml\nterminal:\n file_sync_max_mb: 100 # 默认 —— 同步最大 100 MB 的文件\n file_sync_enabled: true # 默认 —— 设为 false 可完全跳过同步\n```\n\n这是从会话结束后被销毁的临时云沙箱中恢复结果的方式,无需告诉 agent 显式地 `scp` 或 `modal volume put` 每个工件。\n\n### Docker 卷挂载\n\n使用 Docker 后端时,`docker_volumes` 允许您与容器共享宿主目录。每个条目使用标准 Docker `-v` 语法:`host_path:container_path[:options]`。\n\n```yaml\nterminal:\n backend: docker\n docker_volumes:\n - \"/home/user/projects:/workspace/projects\" # 读写(默认)\n - \"/home/user/datasets:/data:ro\" # 只读\n - \"/home/user/.hermes/cache/documents:/output\" # Gateway 可见的导出\n```\n\n适用于:\n- **向 agent 提供文件**(数据集、配置、参考代码)\n- **从 agent 接收文件**(生成的代码、报告、导出)\n- **共享工作区**,您和 agent 都访问相同的文件\n\n如果您使用消息 gateway 并希望 agent 通过 `MEDIA:/...` 发送生成的文件,建议使用专用的宿主可见导出挂载,例如 `/home/user/.hermes/cache/documents:/output`。\n\n- 在 Docker 中将文件写入 `/output/...`\n- 在 `MEDIA:` 中发出**宿主路径**,例如:`MEDIA:/home/user/.hermes/cache/documents/report.txt`\n- **不要**发出 `/workspace/...` 或 `/output/...`,除非该确切路径在宿主上对 gateway 进程也存在\n\n:::warning\nYAML 重复键会静默覆盖之前的键。如果您已有 `docker_volumes:` 块,请将新挂载合并到同一列表中,而不是在文件后面再添加一个 `docker_volumes:` 键。\n:::\n\n也可以通过环境变量设置:`TERMINAL_DOCKER_VOLUMES='[\"/host:/container\"]'`(JSON 数组)。\n\n### Docker 凭据转发\n\n默认情况下,Docker 终端会话不继承任意宿主凭据。如果您需要在容器内使用特定 token,请将其添加到 `terminal.docker_forward_env`。\n\n```yaml\nterminal:\n backend: docker\n docker_forward_env:\n - \"GITHUB_TOKEN\"\n - \"NPM_TOKEN\"\n```\n\nHermes 首先从您当前的 shell 解析每个列出的变量,然后回退到通过 `hermes config set` 保存的 `~/.hermes/.env`。\n\n:::warning\n`docker_forward_env` 中列出的任何内容都会对容器内运行的命令可见。只转发您愿意暴露给终端会话的凭据。\n:::\n\n### 以宿主用户身份运行容器\n\n默认情况下,Docker 容器以 `root`(UID 0)身份运行。在 `/workspace` 或其他绑定挂载中创建的文件在宿主上归 root 所有,因此会话结束后您必须 `sudo chown` 才能从宿主编辑器编辑它们。`terminal.docker_run_as_host_user` 标志解决了这个问题:\n\n```yaml\nterminal:\n backend: docker\n docker_run_as_host_user: true # 默认:false\n```\n\n启用后,Hermes 将 `--user $(id -u):$(id -g)` 附加到 `docker run` 命令,使写入绑定挂载目录(`/workspace`、`/root`、`docker_volumes` 中的任何内容)的文件归您的宿主用户所有,而非 root。权衡:容器将无法再 `apt install` 或写入 `/root/.npm` 等 root 拥有的路径 —— 如果您同时需要这两者,请使用 `HOME` 归非 root 用户所有的基础镜像(或在镜像构建时添加所需工具)。\n\n保持 `false`(默认)以获得向后兼容的行为。当您的工作流主要是\"编辑挂载的宿主文件\"且厌倦了 `sudo chown -R` 时,请开启此选项。\n\n### 可选:将启动目录挂载到 `/workspace`\n\nDocker 沙箱默认保持隔离。Hermes **不会**将您当前的宿主工作目录传入容器,除非您明确选择加入。\n\n在 `config.yaml` 中启用:\n\n```yaml\nterminal:\n backend: docker\n docker_mount_cwd_to_workspace: true\n```\n\n启用后:\n- 如果您从 `~/projects/my-app` 启动 Hermes,该宿主目录将绑定挂载到 `/workspace`\n- Docker 后端从 `/workspace` 开始\n- 文件工具和终端命令都能看到相同的挂载项目\n\n禁用时,`/workspace` 保持沙箱所有,除非您通过 `docker_volumes` 显式挂载内容。\n\n安全权衡:\n- `false` 保留沙箱边界\n- `true` 使沙箱直接访问您启动 Hermes 的目录\n\n仅在您有意希望容器处理实时宿主文件时才选择加入。\n\n### 持久 Shell\n\n默认情况下,每条终端命令在其自己的子进程中运行 —— 工作目录、环境变量和 shell 变量在命令之间重置。启用**持久 shell** 后,单个长期运行的 bash 进程在 `execute()` 调用之间保持存活,使状态在命令之间保持。\n\n这对 **SSH 后端**最有用,它还消除了每条命令的连接开销。持久 shell **对 SSH 默认启用**,对本地后端禁用。\n\n```yaml\nterminal:\n persistent_shell: true # 默认 —— 为 SSH 启用持久 shell\n```\n\n禁用:\n\n```bash\nhermes config set terminal.persistent_shell false\n```\n\n**跨命令保持的内容:**\n- 工作目录(`cd /tmp` 对下一条命令生效)\n- 导出的环境变量(`export FOO=bar`)\n- Shell 变量(`MY_VAR=hello`)\n\n**优先级:**\n\n| 级别 | 变量 | 默认值 |\n|-------|----------|---------|\n| 配置 | `terminal.persistent_shell` | `true` |\n| SSH 覆盖 | `TERMINAL_SSH_PERSISTENT` | 遵循配置 |\n| Local 覆盖 | `TERMINAL_LOCAL_PERSISTENT` | `false` |\n\n每个后端的环境变量具有最高优先级。如果您也想在本地后端使用持久 shell:\n\n```bash\nexport TERMINAL_LOCAL_PERSISTENT=true\n```\n\n:::note\n需要 `stdin_data` 或 sudo 的命令会自动回退到单次模式,因为持久 shell 的 stdin 已被 IPC 协议占用。\n:::\n\n有关每个后端的详细信息,请参阅[代码执行](features/code-execution.md)和 [README 的终端部分](features/tools.md)。\n\n## 技能设置\n\n技能可以通过其 SKILL.md frontmatter 声明自己的配置设置。这些是非机密值(路径、偏好、域设置),存储在 `config.yaml` 的 `skills.config` 命名空间下。\n\n```yaml\nskills:\n config:\n myplugin:\n path: ~/myplugin-data # 示例 —— 每个技能定义自己的键\n```\n\n**技能设置的工作原理:**\n\n- `hermes config migrate` 扫描所有已启用的技能,找到未配置的设置,并提供提示\n- `hermes config show` 在\"技能设置\"下显示所有技能设置及其所属技能\n- 技能加载时,其解析的配置值会自动注入到技能上下文中\n\n**手动设置值:**\n\n```bash\nhermes config set skills.config.myplugin.path ~/myplugin-data\n```\n\n有关在您自己的技能中声明配置设置的详细信息,请参阅[创建技能 — 配置设置](/developer-guide/creating-skills#config-settings-configyaml)。\n\n### Agent 创建技能写入的守卫\n\n当 agent 使用 `skill_manage` 创建、编辑、修补或删除技能时,Hermes 可以选择扫描新/更新的内容以查找危险关键字模式(凭据收集、明显的 prompt 注入、数据外泄指令)。扫描器**默认关闭** —— 合法触及 `~/.ssh/` 或提及 `$OPENAI_API_KEY` 的真实 agent 工作流触发启发式规则过于频繁。如果您希望扫描器在 agent 的技能写入落地前提示您,请重新开启:\n\n```yaml\nskills:\n guard_agent_created: true # 默认:false\n```\n\n开启后,任何被标记的 `skill_manage` 写入都会以审批提示的形式出现,并附带扫描器的理由。接受的写入落地;拒绝的写入向 agent 返回解释性错误。\n\n## 内存配置\n\n```yaml\nmemory:\n memory_enabled: true\n user_profile_enabled: true\n memory_char_limit: 2200 # ~800 tokens\n user_char_limit: 1375 # ~500 tokens\n```\n\n## 文件读取安全\n\n控制单次 `read_file` 调用可以返回多少内容。超过限制的读取将被拒绝,并向 agent 返回错误,提示使用 `offset` 和 `limit` 读取较小范围。这可以防止单次读取压缩的 JS 包或大型数据文件时淹没上下文窗口。\n\n```yaml\nfile_read_max_chars: 100000 # 默认 —— ~25-35K tokens\n```\n\n如果您使用具有大上下文窗口的模型并经常读取大文件,请调高此值。对于小上下文模型,请降低以保持读取高效:\n\n```yaml\n# 大上下文模型(200K+)\nfile_read_max_chars: 200000\n\n# 小型本地模型(16K 上下文)\nfile_read_max_chars: 30000\n```\n\nAgent 还会自动去重文件读取 —— 如果同一文件区域被读取两次且文件未更改,则返回轻量级存根而不是重新发送内容。这在上下文压缩后重置,以便 agent 在内容被摘要后可以重新读取文件。\n\n## 工具输出截断限制\n\n三个相关的上限控制工具在 Hermes 截断之前可以返回多少原始输出:\n\n```yaml\ntool_output:\n max_bytes: 50000 # 终端输出上限(字符)\n max_lines: 2000 # read_file 分页上限\n max_line_length: 2000 # read_file 行号视图中的每行上限\n```\n\n- **`max_bytes`** —— 当 `terminal` 命令产生超过此字符数的合并 stdout/stderr 时,Hermes 保留前 40% 和后 60%,并在中间插入 `[OUTPUT TRUNCATED]` 通知。默认 `50000`(典型分词器约 12-15K tokens)。\n- **`max_lines`** —— 单次 `read_file` 调用的 `limit` 参数上限。超过此值的请求将被截断,以防单次读取淹没上下文窗口。默认 `2000`。\n- **`max_line_length`** —— `read_file` 发出行号视图时应用的每行上限。超过此长度的行将被截断为此字符数,后跟 `... [truncated]`。默认 `2000`。\n\n对于具有大上下文窗口且每次调用可以承受更多原始输出的模型,请调高限制。对于小上下文模型,请降低以保持工具结果紧凑:\n\n```yaml\n# 大上下文模型(200K+)\ntool_output:\n max_bytes: 150000\n max_lines: 5000\n\n# 小型本地模型(16K 上下文)\ntool_output:\n max_bytes: 20000\n max_lines: 500\n```\n\n## 全局工具集禁用\n\n要在 CLI 和每个 gateway 平台上统一禁用特定工具集,请在 `agent.disabled_toolsets` 下列出其名称:\n\n```yaml\nagent:\n disabled_toolsets:\n - memory # 隐藏内存工具 + MEMORY_GUIDANCE 注入\n - web # 任何地方都不使用 web_search / web_extract\n```\n\n这在每个平台的工具配置(由 `hermes tools` 写入的 `platform_toolsets`)**之后**应用,因此此处列出的工具集始终被删除 —— 即使平台的已保存配置仍然列出它。当您希望有一个\"到处关闭 X\"的单一开关而不是编辑 `hermes tools` UI 中的 15+ 个平台行时,请使用此选项。\n\n留空列表或省略键不会产生任何效果。\n\n## Git Worktree 隔离\n\n启用隔离的 git worktree,以便在同一仓库上并行运行多个 agent:\n\n```yaml\nworktree: true # 始终创建 worktree(与 hermes -w 相同)\n# worktree: false # 默认 —— 仅在传递 -w 标志时\n```\n\n启用后,每个 CLI 会话在 `.worktrees/` 下创建一个带有自己分支的新 worktree。Agent 可以编辑文件、提交、推送和创建 PR,而不会相互干扰。干净的 worktree 在退出时删除;脏的 worktree 保留以供手动恢复。\n\n您还可以通过仓库根目录中的 `.worktreeinclude` 列出要复制到 worktree 的 gitignore 文件:\n\n```\n# .worktreeinclude\n.env\n.venv/\nnode_modules/\n```\n\n## 上下文压缩\n\nHermes 自动压缩长对话以保持在模型的上下文窗口内。压缩摘要器是一个单独的 LLM 调用 —— 您可以将其指向任何 provider 或端点。\n\n所有压缩设置都在 `config.yaml` 中(无环境变量)。\n\n### 完整参考\n\n```yaml\ncompression:\n enabled: true # 开启/关闭压缩\n threshold: 0.50 # 在上下文限制的此百分比时压缩\n target_ratio: 0.20 # 保留为最近尾部的阈值分数\n protect_last_n: 20 # 保持未压缩的最少最近消息数\n hygiene_hard_message_limit: 5000 # Gateway 安全阀 —— 见下文\n\n# 摘要模型/provider 在 auxiliary: 下配置:\nauxiliary:\n compression:\n model: \"\" # 空 = 使用主聊天模型。覆盖为例如 \"google/gemini-3-flash-preview\" 以获得更便宜/更快的压缩。\n provider: \"auto\" # Provider:\"auto\"、\"openrouter\"、\"nous\"、\"codex\"、\"main\" 等\n base_url: null # 自定义 OpenAI 兼容端点(覆盖 provider)\n```\n\n:::info 旧版配置迁移\n带有 `compression.summary_model`、`compression.summary_provider` 和 `compression.summary_base_url` 的旧版配置在首次加载时自动迁移到 `auxiliary.compression.*`(配置版本 17)。无需手动操作。\n:::\n\n`hygiene_hard_message_limit` 是仅限 gateway 的**预压缩安全阀**。它的存在是为了打破一个死循环:当超大会话的 API 调用持续断开时,gateway 永远收不到 token 使用数据,基于 token 的阈值因此无法触发,于是 transcript 持续增长、断开愈发严重。这个基于消息数的下限仅凭消息数量触发(无论 API 是否失败,消息数始终已知),强制压缩以恢复会话。默认 `5000` —— 远高于任何正常会话,包括做数千次短轮次的大上下文(1M+)模型,它们早就在 token 阈值处压缩了。对于异常平台可调得更高;要强制更积极的压缩则调低。在运行中的 gateway 上编辑此值将在下一条消息时生效(见下文)。\n\n:::tip Gateway 热重载压缩和上下文长度\n从最近的版本开始,在运行中的 gateway 上编辑 `config.yaml` 中的 `model.context_length` 或任何 `compression.*` 键将在下一条消息时生效 —— 无需 gateway 重启、`/reset` 或会话轮换。缓存的 agent 签名包含这些键,因此 gateway 在检测到更改时会透明地重建 agent。API 密钥和工具/技能配置仍需要通常的重载路径。\n:::\n\n### 常见设置\n\n**默认(自动检测)—— 无需配置:**\n```yaml\ncompression:\n enabled: true\n threshold: 0.50\n```\n使用您的主 provider 和主模型。如果您希望在比主聊天模型更便宜的模型上进行压缩,请覆盖每任务(例如 `auxiliary.compression.provider: openrouter` + `model: google/gemini-2.5-flash`)。\n\n**强制特定 provider**(基于 OAuth 或 API 密钥):\n```yaml\nauxiliary:\n compression:\n provider: nous\n model: gemini-3-flash\n```\n适用于任何 provider:`nous`、`openrouter`、`codex`、`anthropic`、`main` 等。\n\n**自定义端点**(自托管、Ollama、zai、DeepSeek 等):\n```yaml\nauxiliary:\n compression:\n model: glm-4.7\n base_url: https://api.z.ai/api/coding/paas/v4\n```\n指向自定义 OpenAI 兼容端点。使用 `OPENAI_API_KEY` 进行认证。\n\n### 三个旋钮的交互方式\n\n| `auxiliary.compression.provider` | `auxiliary.compression.base_url` | 结果 |\n|---------------------|---------------------|--------|\n| `auto`(默认) | 未设置 | 自动检测最佳可用 provider |\n| `nous` / `openrouter` / 等 | 未设置 | 强制使用该 provider,使用其认证 |\n| 任意 | 已设置 | 直接使用自定义端点(忽略 provider) |\n\n:::warning 摘要模型上下文长度要求\n摘要模型**必须**具有至少与您的主 agent 模型一样大的上下文窗口。压缩器将对话的完整中间部分发送给摘要模型 —— 如果该模型的上下文窗口小于主模型的,摘要调用将因上下文长度错误而失败。发生这种情况时,中间轮次将**在没有摘要的情况下被丢弃**,静默丢失对话上下文。如果您覆盖模型,请验证其上下文长度满足或超过您的主模型。\n:::\n\n## 上下文引擎\n\n上下文引擎控制在接近模型 token 限制时如何管理对话。内置的 `compressor` 引擎使用有损摘要(参见[上下文压缩](/developer-guide/context-compression-and-caching))。插件引擎可以用替代策略替换它。\n\n```yaml\ncontext:\n engine: \"compressor\" # 默认 —— 内置有损摘要\n```\n\n使用插件引擎(例如,用于无损上下文管理的 LCM):\n\n```yaml\ncontext:\n engine: \"lcm\" # 必须与插件名称匹配\n```\n\n插件引擎**永远不会自动激活** —— 您必须将 `context.engine` 显式设置为插件名称。可用引擎可以通过 `hermes plugins` → Provider Plugins → Context Engine 浏览和选择。\n\n有关内存插件的类似单选系统,请参阅[内存 Providers](/user-guide/features/memory-providers)。\n\n## 迭代预算\n\n当 agent 在处理具有许多工具调用的复杂任务时,它可能会耗尽其迭代预算(默认:90 轮)。Hermes **不会**在任务中途注入压力警告 —— 早期版本会在预算达到 70%/90% 时警告模型,这会导致模型过早放弃复杂任务,该机制已于 2026 年 4 月移除。\n\n取而代之的是,当预算真正耗尽(90/90)时,Hermes 注入一条消息要求模型收尾,并允许一次**宽限调用**以便其给出最终响应。如果该宽限调用仍未产生文本,则会要求 agent 总结已完成的工作。\n\n```yaml\nagent:\n max_turns: 90 # 每次对话轮次的最大迭代次数(默认:90)\n api_max_retries: 3 # 回退启动前每个 provider 的重试次数(默认:3)\n```\n\n当迭代预算完全耗尽时,CLI 向用户显示通知:`⚠ Iteration budget reached (90/90) — response may be incomplete`。\n\n`agent.api_max_retries` 控制 Hermes 在回退 provider 切换启动**之前**对瞬时错误(速率限制、连接断开、5xx)重试 provider API 调用的次数。默认为 `3` —— 总共四次尝试。如果您配置了[回退 providers](/user-guide/features/fallback-providers) 并希望更快地故障转移,请将其降至 `0`,这样主 provider 上的第一个瞬时错误会立即切换到回退,而不是对不稳定的端点进行重试。\n\n### API 超时\n\nHermes 对流式传输有单独的超时层,以及用于非流式调用的陈旧检测器。陈旧检测器仅在您将其保留为隐式默认值时才会自动调整本地 provider。\n\n| 超时 | 默认值 | 本地 providers | 配置/环境变量 |\n|---------|---------|----------------|--------------|\n| Socket 读取超时 | 120s | 自动提升至 1800s | `HERMES_STREAM_READ_TIMEOUT` |\n| 陈旧流检测 | 180s | 自动禁用 | `HERMES_STREAM_STALE_TIMEOUT` |\n| 陈旧非流检测 | 300s | 保持隐式时自动禁用 | `providers.<id>.stale_timeout_seconds` 或 `HERMES_API_CALL_STALE_TIMEOUT` |\n| API 调用(非流式) | 1800s | 不变 | `providers.<id>.request_timeout_seconds` / `timeout_seconds` 或 `HERMES_API_TIMEOUT` |\n\n**Socket 读取超时**控制 httpx 等待 provider 下一个数据块的时间。本地 LLM 在大上下文上预填充可能需要几分钟才能产生第一个 token,因此当 Hermes 检测到本地端点时,会将此值提升至 30 分钟。如果您显式设置 `HERMES_STREAM_READ_TIMEOUT`,无论端点检测如何,始终使用该值。\n\n**陈旧流检测**终止接收 SSE 保活 ping 但没有实际内容的连接。对于本地 providers,这完全禁用,因为它们在预填充期间不发送保活 ping。\n\n**陈旧非流检测**终止长时间没有响应的非流式调用。默认情况下,Hermes 在本地端点上禁用此功能,以避免长时间预填充期间的误报。如果您显式设置 `providers.<id>.stale_timeout_seconds`、`providers.<id>.models.<model>.stale_timeout_seconds` 或 `HERMES_API_CALL_STALE_TIMEOUT`,即使在本地端点上也会遵守该显式值。\n\n## 上下文压力警告\n\n与迭代预算压力分开,上下文压力跟踪对话距**压缩阈值**有多近 —— 即上下文压缩触发以摘要旧消息的点。这有助于您和 agent 了解对话何时变长。\n\n| 进度 | 级别 | 发生的事情 |\n|----------|-------|-------------|\n| **≥ 60%** 到阈值 | 信息 | CLI 显示青色进度条;gateway 发送信息通知 |\n| **≥ 85%** 到阈值 | 警告 | CLI 显示粗体黄色进度条;gateway 警告压缩即将发生 |\n\n在 CLI 中,上下文压力在工具输出流中显示为进度条:\n\n```\n ◐ context ████████████░░░░░░░░ 62% to compaction 48k threshold (50%) · approaching compaction\n```\n\n在消息平台上,发送纯文本通知:\n\n```\n◐ Context: ████████████░░░░░░░░ 62% to compaction (threshold: 50% of window).\n```\n\n如果自动压缩被禁用,警告会告诉您上下文可能被截断。\n\n上下文压力是自动的 —— 无需配置。它纯粹作为面向用户的通知触发,不修改消息流或向模型上下文注入任何内容。\n\n## 凭据池策略\n\n当您为同一 provider 拥有多个 API 密钥或 OAuth token 时,配置轮换策略:\n\n```yaml\ncredential_pool_strategies:\n openrouter: round_robin # 均匀循环使用密钥\n anthropic: least_used # 始终选择使用最少的密钥\n```\n\n选项:`fill_first`(默认)、`round_robin`、`least_used`、`random`。完整文档请参阅[凭据池](/user-guide/features/credential-pools)。\n\n## Prompt 缓存\n\n当活跃 provider 支持时,Hermes 自动开启跨会话 prompt 缓存 —— 无需用户配置。\n\n对于**原生 Anthropic**、**OpenRouter** 和 **Nous Portal** 上的 Claude,Hermes 在系统提示词和技能块上附加带有 1 小时 TTL(`ttl: \"1h\"`)的 `cache_control` 断点。在新鲜的一小时内首次发送时按完整输入费率计费;同一小时内任何会话的后续发送以折扣缓存读取费率从缓存中提取。这意味着系统提示词、加载的技能内容以及任何长上下文包含的早期部分在第一个小时内跨 `hermes` 会话和分叉子 agent 被重用。\n\nQwen Cloud(阿里巴巴 DashScope)上游将缓存 TTL 限制为 5 分钟,因此 Hermes 在那里使用 5 分钟断点 TTL。其他通过第三方的 Claude 路径(AWS Bedrock、Azure Foundry)回退到 provider 自己的缓存默认值。xAI Grok 使用单独的会话固定对话 ID 机制 —— 参阅 [xAI prompt 缓存](/integrations/providers#xai-grok--responses-api--prompt-caching)。\n\n不存在禁用此功能的旋钮 —— 缓存始终开启,即使在单轮对话中也能节省费用,因为仅系统提示词就占输入 token 数的相当大比例。\n\n## 辅助模型\n\nHermes 使用\"辅助\"模型处理图像分析、网页摘要、浏览器截图分析、会话标题生成和上下文压缩等附带任务。默认情况下(`auxiliary.*.provider: \"auto\"`),Hermes 将每个辅助任务路由到您的**主聊天模型** —— 与您在 `hermes model` 中选择的相同 provider/模型。您无需配置任何内容即可开始,但请注意,在昂贵的推理模型(Opus、MiniMax M2.7 等)上,辅助任务会增加显著成本。如果您希望无论主模型如何都使用便宜且快速的附带任务,请显式设置 `auxiliary.<task>.provider` 和 `auxiliary.<task>.model`(例如,在 OpenRouter 上使用 Gemini Flash 进行视觉和网页提取)。\n\n:::note 为什么 \"auto\" 使用您的主模型\n早期版本将聚合器用户(OpenRouter、Nous Portal)分流到便宜的 provider 端默认值。这令人惊讶 —— 付费购买聚合器订阅的用户会看到不同的模型处理其辅助流量。`auto` 现在对所有人使用主模型,`config.yaml` 中的每任务覆盖仍然优先(见下方[完整辅助配置参考](#full-auxiliary-config-reference))。\n:::\n\n### 交互式配置辅助模型\n\n无需手动编辑 YAML,运行 `hermes model` 并从菜单中选择**\"配置辅助模型\"**。您将获得交互式的每任务选择器:\n\n```\n$ hermes model\n→ Configure auxiliary models\n\n[ ] vision currently: auto / main model\n[ ] web_extract currently: auto / main model\n[ ] title_generation currently: openrouter / google/gemini-3-flash-preview\n[ ] compression currently: auto / main model\n[ ] approval currently: auto / main model\n[ ] triage_specifier currently: auto / main model\n[ ] kanban_decomposer currently: auto / main model\n[ ] profile_describer currently: auto / main model\n```\n\n选择任务,选择 provider(OAuth 流程打开浏览器;API 密钥 provider 提示输入),选择模型。更改持久化到 `config.yaml` 中的 `auxiliary.<task>.*`。与主模型选择器相同的机制 —— 无需学习额外语法。\n\n### 视频教程\n\n<div style={{position: 'relative', width: '100%', aspectRatio: '16 / 9', marginBottom: '1.5rem'}}>\n <iframe\n src=\"https://www.youtube.com/embed/NoF-YajElIM\"\n title=\"Hermes Agent — Auxiliary Models Tutorial\"\n style={{position: 'absolute', top: 0, left: 0, width: '100%', height: '100%', border: 0}}\n allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\"\n allowFullScreen\n />\n</div>\n\n### 通用配置模式\n\nHermes 中的每个模型槽位 —— 辅助任务、压缩、回退 —— 使用相同的三个旋钮:\n\n| 键 | 作用 | 默认值 |\n|-----|-------------|---------|\n| `provider` | 用于认证和路由的 provider | `\"auto\"` |\n| `model` | 请求的模型 | provider 的默认值 |\n| `base_url` | 自定义 OpenAI 兼容端点(覆盖 provider) | 未设置 |\n\n当设置 `base_url` 时,Hermes 忽略 provider 并直接调用该端点(使用 `api_key` 或 `OPENAI_API_KEY` 进行认证)。当仅设置 `provider` 时,Hermes 使用该 provider 的内置认证和基础 URL。\n\n辅助任务的可用 providers:`auto`、`main`,以及[provider 注册表](/reference/environment-variables)中的任何 provider —— `openrouter`、`nous`、`openai-codex`、`copilot`、`copilot-acp`、`anthropic`、`gemini`、`qwen-oauth`、`zai`、`kimi-coding`、`kimi-coding-cn`、`minimax`、`minimax-cn`、`minimax-oauth`、`deepseek`、`nvidia`、`xai`、`xai-oauth`、`ollama-cloud`、`alibaba`、`bedrock`、`huggingface`、`arcee`、`xiaomi`、`kilocode`、`opencode-zen`、`opencode-go`、`azure-foundry` —— 或您 `custom_providers` 列表中任何命名的自定义 provider(例如 `provider: \"beans\"`)。\n\n:::tip MiniMax OAuth\n`minimax-oauth` 通过浏览器 OAuth 登录(无需 API 密钥)。运行 `hermes model` 并选择 **MiniMax (OAuth)** 进行认证。辅助任务自动使用 `MiniMax-M2.7-highspeed`。参阅 [MiniMax OAuth 指南](../guides/minimax-oauth.md)。\n:::\n\n:::tip xAI Grok OAuth\n`xai-oauth` 通过浏览器 OAuth 为 SuperGrok 和 X Premium+ 订阅者登录(无需 API 密钥)。运行 `hermes model` 并选择 **xAI Grok OAuth (SuperGrok / Premium+)** 进行认证。相同的 OAuth token 可重用于每个直接到 xAI 的接口(聊天、辅助任务、TTS、图像生成、视频生成、转录)。参阅 [xAI Grok OAuth 指南](../guides/xai-grok-oauth.md),如果 Hermes 在远程主机上,请参阅 [SSH/远程主机上的 OAuth](../guides/oauth-over-ssh.md)。\n:::\n\n:::warning `\"main\"` 仅用于辅助任务\n`\"main\"` provider 选项表示\"使用我的主 agent 使用的任何 provider\" —— 它仅在 `auxiliary:`、`compression:` 和 `fallback_model:` 配置中有效。它**不是**顶级 `model.provider` 设置的有效值。如果您使用自定义 OpenAI 兼容端点,请在 `model:` 部分设置 `provider: custom`。所有主模型 provider 选项请参阅 [AI Providers](/integrations/providers)。\n:::\n\n### 完整辅助配置参考\n\n```yaml\nauxiliary:\n # 图像分析(vision_analyze 工具 + 浏览器截图)\n vision:\n provider: \"auto\" # \"auto\"、\"openrouter\"、\"nous\"、\"codex\"、\"main\" 等\n model: \"\" # 例如 \"openai/gpt-4o\"、\"google/gemini-2.5-flash\"\n base_url: \"\" # 自定义 OpenAI 兼容端点(覆盖 provider)\n api_key: \"\" # base_url 的 API 密钥(回退到 OPENAI_API_KEY)\n timeout: 120 # 秒 —— LLM API 调用超时;视觉负载需要宽裕的超时\n download_timeout: 30 # 秒 —— 图像 HTTP 下载;慢速连接请增加\n\n # 网页摘要 + 浏览器页面文本提取\n web_extract:\n provider: \"auto\"\n model: \"\" # 例如 \"google/gemini-2.5-flash\"\n base_url: \"\"\n api_key: \"\"\n timeout: 360 # 秒(6 分钟)—— 每次尝试的 LLM 摘要\n\n # 危险命令审批分类器\n approval:\n provider: \"auto\"\n model: \"\"\n base_url: \"\"\n api_key: \"\"\n timeout: 30 # 秒\n\n # 上下文压缩超时(与 compression.* 配置分开)\n compression:\n timeout: 120 # 秒 —— 压缩摘要长对话,需要更多时间\n # fallback_chain: # 可选 —— 发生速率限制/连接故障时尝试的 provider\n # - provider: nous\n # model: deepseek/deepseek-chat\n # - provider: openrouter\n # model: google/gemini-2.5-flash\n # base_url: \"\"\n # api_key: \"\"\n\n # 技能中心 —— 技能匹配和搜索\n skills_hub:\n provider: \"auto\"\n model: \"\"\n base_url: \"\"\n api_key: \"\"\n timeout: 30\n\n # MCP 工具调度\n mcp:\n provider: \"auto\"\n model: \"\"\n base_url: \"\"\n api_key: \"\"\n timeout: 30\n\n # Kanban 分类规格说明器 —— `hermes kanban specify <id>`(或\n # 仪表板上 Triage 列卡片的 ✨ Specify 按钮)使用此\n # 槽位将单行描述扩展为具体规格并将\n # 任务提升到 `todo`。便宜快速的模型在这里效果很好;规格扩展\n # 很短,不需要推理深度。\n triage_specifier:\n provider: \"auto\"\n model: \"\"\n base_url: \"\"\n api_key: \"\"\n timeout: 120\n```\n\n:::tip\n每个辅助任务都有可配置的 `timeout`(秒)。默认值:vision 120s、web_extract 360s、approval 30s、compression 120s。如果您为辅助任务使用慢速本地模型,请增加这些值。Vision 还有单独的 `download_timeout`(默认 30s)用于 HTTP 图像下载 —— 对于慢速连接或自托管图像服务器,请增加此值。\n:::\n\n:::info\n上下文压缩有自己的 `compression:` 块用于阈值,以及 `auxiliary.compression:` 块用于模型/provider 设置 —— 参阅上方的[上下文压缩](#context-compression)。主备用链使用顶层的 `fallback_providers:` 列表 —— 参阅[备用提供商](/integrations/providers#fallback-providers)。三者都遵循相同的 provider/model/base_url 模式。\n:::\n\n### 辅助任务的每任务回退链\n\n每个辅助任务都可以选择性地定义一个 `fallback_chain` —— 一个 provider/model 条目列表,当主要辅助 provider 因速率限制、网络连接问题或付费限制而失败时,Hermes 会尝试使用该列表:\n\n```yaml\nauxiliary:\n compression:\n provider: openrouter\n model: openai/gpt-4o-mini\n fallback_chain:\n - provider: nous\n model: deepseek/deepseek-chat\n - provider: openrouter\n model: google/gemini-2.5-flash\n```\n\n当主要辅助 provider(`openrouter` / `openai/gpt-4o-mini`)返回速率限制、连接超时或需要付费错误时,Hermes 将依次遍历 `fallback_chain`。它会跳过 provider 与已失败 provider 相同的条目,并尝试每个剩余条目,直到有一个成功或该链耗尽。如果所有回退都失败,Hermes 会回退到主 agent 模型作为最终的安全网。\n\n每个条目支持与任何辅助任务配置相同的三个旋钮:\n\n| 键 | 描述 |\n|-----|-------------|\n| `provider` | Provider 名称(`nous`、`openrouter`、`anthropic`、`gemini`、`main` 等) |\n| `model` | 该 provider 的模型名称 |\n| `base_url` | (可选)自定义 OpenAI 兼容端点 |\n\n`fallback_chain` 适用于任何辅助任务 —— `compression`、`vision`、`web_extract`、`approval`、`skills_hub`、`mcp` 等。\n\n### OpenRouter 路由和辅助任务的 Pareto Code\n\n当辅助任务解析到 OpenRouter(显式或通过 `provider: \"main\"` 而您的主 agent 在 OpenRouter 上)时,主 agent 的 `provider_routing` 和 `openrouter.min_coding_score` 设置**不会传播** —— 按设计,每个辅助任务是独立的。要为特定辅助任务设置 OpenRouter provider 偏好或使用 [Pareto Code 路由器](/integrations/providers#openrouter-pareto-code-router),请通过 `extra_body` 按任务设置:\n\n```yaml\nauxiliary:\n compression:\n provider: openrouter\n model: openrouter/pareto-code # 为此任务使用 Pareto Code 路由器\n extra_body:\n provider: # OpenRouter provider 路由偏好\n order: [anthropic, google] # 按此顺序尝试这些 providers\n sort: throughput # 或 \"price\" | \"latency\"\n # only: [anthropic] # 限制到特定 provider\n # ignore: [deepinfra] # 排除特定 providers\n plugins: # OpenRouter Pareto Code 路由器旋钮\n - id: pareto-router\n min_coding_score: 0.5 # 0.0–1.0;越高 = 更强的编码能力\n```\n\n形状与 OpenRouter 在聊天补全请求体中接受的内容一致。Hermes 原样转发整个 `extra_body`,因此 [openrouter.ai/docs](https://openrouter.ai/docs) 中记录的任何其他 OpenRouter 请求体字段都以相同方式工作。\n\n### 更改视觉模型\n\n使用 GPT-4o 而非 Gemini Flash 进行图像分析:\n\n```yaml\nauxiliary:\n vision:\n model: \"openai/gpt-4o\"\n```\n\n或通过环境变量(在 `~/.hermes/.env` 中):\n\n```bash\nAUXILIARY_VISION_MODEL=openai/gpt-4o\n```\n\n### Provider 选项\n\n这些选项适用于**辅助任务配置**(`auxiliary:`、`compression:`、`fallback_model:`),而非您的主 `model.provider` 设置。\n\n| Provider | 描述 | 要求 |\n|----------|-------------|-------------|\n| `\"auto\"` | 最佳可用(默认)。Vision 尝试 OpenRouter → Nous → Codex。 | — |\n| `\"openrouter\"` | 强制 OpenRouter —— 路由到任何模型(Gemini、GPT-4o、Claude 等) | `OPENROUTER_API_KEY` |\n| `\"nous\"` | 强制 Nous Portal | `hermes auth` |\n| `\"codex\"` | 强制 Codex OAuth(ChatGPT 账户)。支持视觉(gpt-5.3-codex)。 | `hermes model` → Codex |\n| `\"minimax-oauth\"` | 强制 MiniMax OAuth(浏览器登录,无需 API 密钥)。辅助任务使用 MiniMax-M2.7-highspeed。 | `hermes model` → MiniMax (OAuth) |\n| `\"xai-oauth\"` | 强制 xAI Grok OAuth(SuperGrok 或 X Premium+ 订阅者的浏览器登录,无需 API 密钥)。相同的 OAuth token 涵盖聊天、TTS、图像、视频和转录。 | `hermes model` → xAI Grok OAuth (SuperGrok / Premium+) |\n| `\"main\"` | 使用您的活跃自定义/主端点。可以来自 `OPENAI_BASE_URL` + `OPENAI_API_KEY` 或通过 `hermes model` / `config.yaml` 保存的自定义端点。适用于 OpenAI、本地模型或任何 OpenAI 兼容 API。**仅限辅助任务 —— 对 `model.provider` 无效。** | 自定义端点凭据 + 基础 URL |\n\n当您希望附带任务绕过默认路由器时,主 provider 目录中的直接 API 密钥 providers 也在这里工作。配置 `GMI_API_KEY` 后,`gmi` 有效:\n\n```yaml\nauxiliary:\n compression:\n provider: \"gmi\"\n model: \"anthropic/claude-opus-4.6\"\n```\n\n对于 GMI 辅助路由,使用 GMI 的 `/v1/models` 端点返回的确切模型 ID。\n\n### 常见设置\n\n**使用直接自定义端点**(比 `provider: \"main\"` 对本地/自托管 API 更清晰):\n```yaml\nauxiliary:\n vision:\n base_url: \"http://localhost:1234/v1\"\n api_key: \"local-key\"\n model: \"qwen2.5-vl\"\n```\n\n`base_url` 优先于 `provider`,因此这是将辅助任务路由到特定端点的最明确方式。对于直接端点覆盖,Hermes 使用配置的 `api_key` 或回退到 `OPENAI_API_KEY`;它不会为该自定义端点重用 `OPENROUTER_API_KEY`。\n\n**使用 OpenAI API 密钥进行视觉:**\n```yaml\n# 在 ~/.hermes/.env 中:\n# OPENAI_BASE_URL=https://api.openai.com/v1\n# OPENAI_API_KEY=sk-...\n\nauxiliary:\n vision:\n provider: \"main\"\n model: \"gpt-4o\" # 或 \"gpt-4o-mini\" 更便宜\n```\n\n**使用 OpenRouter 进行视觉**(路由到任何模型):\n```yaml\nauxiliary:\n vision:\n provider: \"openrouter\"\n model: \"openai/gpt-4o\" # 或 \"google/gemini-2.5-flash\" 等\n```\n\n**使用 Codex OAuth**(ChatGPT Pro/Plus 账户 —— 无需 API 密钥):\n```yaml\nauxiliary:\n vision:\n provider: \"codex\" # 使用您的 ChatGPT OAuth token\n # 模型默认为 gpt-5.3-codex(支持视觉)\n```\n\n**使用 MiniMax OAuth**(浏览器登录,无需 API 密钥):\n```yaml\nmodel:\n default: MiniMax-M2.7\n provider: minimax-oauth\n base_url: https://api.minimax.io/anthropic\n```\n运行 `hermes model` 并选择 **MiniMax (OAuth)** 自动登录并设置此项。对于中国区域,基础 URL 将是 `https://api.minimaxi.com/anthropic`。完整演练请参阅 [MiniMax OAuth 指南](../guides/minimax-oauth.md)。\n\n**使用本地/自托管模型:**\n```yaml\nauxiliary:\n vision:\n provider: \"main\" # 使用您的活跃自定义端点\n model: \"my-local-model\"\n```\n\n`provider: \"main\"` 使用 Hermes 用于普通聊天的任何 provider —— 无论是命名的自定义 provider(例如 `beans`)、内置 provider(如 `openrouter`)还是旧版 `OPENAI_BASE_URL` 端点。\n\n:::tip\n如果您使用 Codex OAuth 作为主模型 provider,视觉会自动工作 —— 无需额外配置。Codex 包含在视觉的自动检测链中。\n:::\n\n:::warning\n**视觉需要多模态模型。** 如果您设置 `provider: \"main\"`,请确保您的端点支持多模态/视觉 —— 否则图像分析将失败。\n:::\n\n### 环境变量(旧版)\n\n辅助模型也可以通过环境变量配置。但是,`config.yaml` 是首选方法 —— 它更易于管理,并支持所有选项,包括 `base_url` 和 `api_key`。\n\n| 设置 | 环境变量 |\n|---------|---------------------|\n| Vision provider | `AUXILIARY_VISION_PROVIDER` |\n| Vision 模型 | `AUXILIARY_VISION_MODEL` |\n| Vision 端点 | `AUXILIARY_VISION_BASE_URL` |\n| Vision API 密钥 | `AUXILIARY_VISION_API_KEY` |\n| Web 提取 provider | `AUXILIARY_WEB_EXTRACT_PROVIDER` |\n| Web 提取模型 | `AUXILIARY_WEB_EXTRACT_MODEL` |\n| Web 提取端点 | `AUXILIARY_WEB_EXTRACT_BASE_URL` |\n| Web 提取 API 密钥 | `AUXILIARY_WEB_EXTRACT_API_KEY` |\n\n压缩和回退模型设置仅限 config.yaml。\n\n:::tip\n运行 `hermes config` 查看您当前的辅助模型设置。覆盖仅在与默认值不同时显示。\n:::\n\n## 推理努力程度\n\n控制模型在响应前进行多少\"思考\":\n\n```yaml\nagent:\n reasoning_effort: \"\" # 空 = 中等。选项:none、minimal、low、medium、high、xhigh、max、ultra\n```\n\n未设置时(默认),推理努力程度默认为\"medium\" —— 适合大多数任务的平衡级别。设置值会覆盖它 —— 更高的推理努力程度在复杂任务上提供更好的结果,但代价是更多 token 和延迟。\n\n您也可以在运行时使用 `/reasoning` 命令更改推理努力程度:\n\n```\n/reasoning # 显示当前努力程度和显示状态\n/reasoning high # 将推理努力程度设为 high\n/reasoning none # 禁用推理\n/reasoning show # 在每次响应上方显示模型思考\n/reasoning hide # 隐藏模型思考\n```\n\n## 工具使用强制\n\n某些模型偶尔会将预期操作描述为文本而不是进行工具调用(\"我会运行测试...\"而不是实际调用终端)。工具使用强制会注入系统提示词指导,引导模型实际调用工具。\n\n```yaml\nagent:\n tool_use_enforcement: \"auto\" # \"auto\" | true | false | [\"model-substring\", ...]\n```\n\n| 值 | 行为 |\n|-------|----------|\n| `\"auto\"`(默认) | 对匹配以下模型启用:`gpt`、`codex`、`gemini`、`gemma`、`grok`。对所有其他模型禁用(Claude、DeepSeek、Qwen 等)。 |\n| `true` | 始终启用,无论模型如何。如果您注意到当前模型描述操作而不是执行操作,请使用此选项。 |\n| `false` | 始终禁用,无论模型如何。 |\n| `[\"gpt\", \"codex\", \"qwen\", \"llama\"]` | 仅当模型名称包含列出的子字符串之一时启用(不区分大小写)。 |\n\n### 注入的内容\n\n启用后,系统提示词中可能会添加三层指导:\n\n1. **通用工具使用强制**(所有匹配模型)—— 指示模型立即进行工具调用而不是描述意图,持续工作直到任务完成,永远不要以未来操作的承诺结束轮次。\n\n2. **OpenAI 执行纪律**(仅限 GPT 和 Codex 模型)—— 针对 GPT 特定失败模式的额外指导:在部分结果上放弃工作、跳过先决条件查找、幻觉而不是使用工具、在未验证的情况下宣布\"完成\"。\n\n3. **Google 操作指导**(仅限 Gemini 和 Gemma 模型)—— 简洁性、绝对路径、并行工具调用和编辑前验证模式。\n\n这些对用户透明,仅影响系统提示词。已经可靠使用工具的模型(如 Claude)不需要此指导,这就是为什么 `\"auto\"` 排除它们。\n\n### 何时开启\n\n如果您使用的模型不在默认自动列表中,并注意到它经常描述它*会*做什么而不是实际去做,请设置 `tool_use_enforcement: true` 或将模型子字符串添加到列表中:\n\n```yaml\nagent:\n tool_use_enforcement: [\"gpt\", \"codex\", \"gemini\", \"grok\", \"my-custom-model\"]\n```\n\n## TTS 配置\n\n```yaml\ntts:\n provider: \"edge\" # \"edge\" | \"elevenlabs\" | \"openai\" | \"minimax\" | \"mistral\" | \"gemini\" | \"xai\" | \"neutts\"\n speed: 1.0 # 全局速度倍数(所有 provider 的回退)\n edge:\n voice: \"en-US-AriaNeural\" # 322 种声音,74 种语言\n speed: 1.0 # 速度倍数(转换为速率百分比,例如 1.5 → +50%)\n elevenlabs:\n voice_id: \"pNInz6obpgDQGcFmaJgB\"\n model_id: \"eleven_multilingual_v2\"\n openai:\n model: \"gpt-4o-mini-tts\"\n voice: \"alloy\" # alloy、echo、fable、onyx、nova、shimmer\n speed: 1.0 # 速度倍数(API 限制为 0.25–4.0)\n base_url: \"https://api.openai.com/v1\" # 覆盖 OpenAI 兼容 TTS 端点\n minimax:\n speed: 1.0 # 语音速度倍数\n # base_url: \"\" # 可选:覆盖 OpenAI 兼容 TTS 端点\n mistral:\n model: \"voxtral-mini-tts-2603\"\n voice_id: \"c69964a6-ab8b-4f8a-9465-ec0925096ec8\" # Paul - Neutral(默认)\n gemini:\n model: \"gemini-2.5-flash-preview-tts\" # 或 gemini-2.5-pro-preview-tts\n voice: \"Kore\" # 30 种预置声音:Zephyr、Puck、Kore、Enceladus 等\n xai:\n voice_id: \"eve\" # xAI TTS 声音\n language: \"en\" # ISO 639-1\n sample_rate: 24000\n bit_rate: 128000 # MP3 比特率\n # base_url: \"https://api.x.ai/v1\"\n neutts:\n ref_audio: ''\n ref_text: ''\n model: neuphonic/neutts-air-q4-gguf\n device: cpu\n```\n\n这控制 `text_to_speech` 工具和语音模式中的口语回复(CLI 中的 `/voice tts` 或消息 gateway)。\n\n**速度回退层次:** provider 特定速度(例如 `tts.edge.speed`)→ 全局 `tts.speed` → `1.0` 默认值。设置全局 `tts.speed` 以在所有 provider 上应用统一速度,或按 provider 覆盖以进行精细控制。\n\n## 显示设置\n\n```yaml\ndisplay:\n tool_progress: all # off | new | all | verbose\n tool_progress_command: false # 在消息 gateway 中启用 /verbose 斜杠命令\n platforms: {} # 每平台显示覆盖(见下文)\n tool_progress_overrides: {} # 已弃用 —— 改用 display.platforms\n interim_assistant_messages: true # Gateway:将自然的轮次中 assistant 更新作为单独消息发送\n skin: default # 内置或自定义 CLI 皮肤(参阅 user-guide/features/skins)\n personality: \"kawaii\" # 旧版外观字段,仍在某些摘要中显示\n compact: false # 紧凑输出模式(减少空白)\n resume_display: full # full(恢复时显示之前的消息)| minimal(仅单行)\n bell_on_complete: false # 当 agent 完成时播放终端铃声(适合长任务)\n show_reasoning: false # 在每次响应上方显示模型推理/思考(用 /reasoning show|hide 切换)\n streaming: false # 将 token 实时流式传输到终端\n show_cost: false # 在 CLI 状态栏中显示估计 $ 成本\n timestamps: false # 为 true 时,在 CLI/TUI 记录中为用户和 assistant 标签添加 [HH:MM] 时间戳前缀\n tool_preview_length: 0 # 工具调用预览的最大字符数(0 = 无限制,显示完整路径/命令)\n runtime_footer: # Gateway:在最终回复中附加运行时上下文页脚\n enabled: false\n fields: [\"model\", \"context_pct\", \"cwd\"]\n file_mutation_verifier: true # 当本轮 write_file/patch 调用失败时附加建议性页脚\n language: en # 静态消息的 UI 语言(审批提示、部分 gateway 回复)。en | zh | zh-hant | ja | de | es | fr | tr | uk | af | ko | it | ga | pt | ru | hu\n```\n\n### 文件变更验证器\n\n当 `display.file_mutation_verifier` 为 `true`(默认)时,每当本轮中 `write_file` 或 `patch` 调用失败且从未被对同一路径的成功写入取代时,Hermes 会在 assistant 的最终响应中附加一行建议。这捕获了\"批量并行补丁,一半静默失败,模型总结成功\"这类过度声明,而无需您在每次编辑后手动运行 `git status`。\n\n示例页脚:\n\n```\n⚠️ File-mutation verifier: 3 file(s) were NOT modified this turn despite any wording above that may suggest otherwise. Run `git status` or `read_file` to confirm.\n • concepts/automatic-organization.md — [patch] Could not find match for old_string\n • concepts/lora.md — [patch] Could not find match for old_string\n • concepts/rag-pipeline.md — [patch] Could not find match for old_string\n```\n\n设置 `file_mutation_verifier: false`(或 `HERMES_FILE_MUTATION_VERIFIER=0`)以禁止页脚。验证器仅在轮次结束时有真实失败未解决时触发 —— 在同一轮次内重试失败补丁并成功的模型不会为该文件触发它。\n\n### 静态消息的 UI 语言\n\n`display.language` 设置翻译一小组静态面向用户的消息 —— CLI 审批提示、少数 gateway 斜杠命令回复(例如重启排空通知、\"审批已过期\"、\"目标已清除\")。它**不**翻译 agent 响应、日志行、工具输出、错误回溯或斜杠命令描述 —— 这些保持英文。如果您希望 agent 本身用另一种语言回复,只需在您的提示词或系统消息中告诉它。\n\n支持的值:`en`(默认)、`zh`(简体中文)、`zh-hant`(繁体中文)、`ja`(日语)、`de`(德语)、`es`(西班牙语)、`fr`(法语)、`tr`(土耳其语)、`uk`(乌克兰语)、`af`(南非荷兰语)、`ko`(韩语)、`it`(意大利语)、`ga`(爱尔兰语)、`pt`(葡萄牙语)、`ru`(俄语)、`hu`(匈牙利语)。未知值回退到英文。\n\n您也可以使用 `HERMES_LANGUAGE` 环境变量按会话设置,它会覆盖配置值。\n\n```yaml\ndisplay:\n language: zh # CLI 审批提示以中文显示\n```\n\n| 模式 | 您看到的内容 |\n|------|-------------|\n| `off` | 静默 —— 仅最终响应 |\n| `new` | 仅在工具更改时显示工具指示器 |\n| `all` | 每次工具调用附带简短预览(默认) |\n| `verbose` | 完整参数、结果和调试日志 |\n\n在 CLI 中,使用 `/verbose` 循环切换这些模式。要在消息平台(Telegram、Discord、Slack 等)中使用 `/verbose`,请在上方的 `display` 部分设置 `tool_progress_command: true`。该命令将循环切换模式并保存到配置。\n\n### 运行时元数据页脚(仅限 gateway)\n\n当 `display.runtime_footer.enabled: true` 时,Hermes 在每个 gateway 轮次的**最终**消息中附加一个小型运行时上下文页脚。目前页脚可显示模型、上下文窗口百分比和当前工作目录。默认关闭;如果您的团队希望每个回复都包含这些来源信息,请按 gateway 选择加入。\n\n```yaml\ndisplay:\n runtime_footer:\n enabled: true\n fields: [\"model\", \"context_pct\", \"cwd\"] # 支持字段:model、context_pct、cwd\n```\n\n`/footer` 斜杠命令在任何会话中运行时切换此功能。\n\n附加到 Telegram/Discord/Slack 回复的示例页脚:\n\n```\n— claude-opus-4.7 · 12 tool calls · 2m 14s · $0.042\n```\n\n只有轮次的**最终**消息获得页脚;中间更新保持干净。\n\n### 每平台进度覆盖\n\n不同平台有不同的详细程度需求。例如,Signal 无法编辑消息,因此每次进度更新都会成为单独的消息 —— 很嘈杂。使用 `display.platforms` 设置每平台模式:\n\n```yaml\ndisplay:\n tool_progress: all # 全局默认\n platforms:\n signal:\n tool_progress: 'off' # 在 Signal 上静默进度\n telegram:\n tool_progress: verbose # 在 Telegram 上详细进度\n slack:\n tool_progress: 'off' # 在共享 Slack 工作区中保持安静\n```\n\n没有覆盖的平台回退到全局 `tool_progress` 值。有效平台键:`telegram`、`discord`、`slack`、`signal`、`whatsapp`、`matrix`、`mattermost`、`email`、`sms`、`homeassistant`、`dingtalk`、`feishu`、`wecom`、`weixin`、`bluebubbles`、`qqbot`。旧版 `display.tool_progress_overrides` 键仍可加载以向后兼容,但已弃用,并在首次加载时迁移到 `display.platforms`。\n\n`interim_assistant_messages` 仅限 gateway。启用后,Hermes 将已完成的轮次中 assistant 更新作为单独的聊天消息发送。这与 `tool_progress` 无关,不需要 gateway 流式传输。\n\n## 隐私\n\n```yaml\nprivacy:\n redact_pii: false # 从 LLM 上下文中删除 PII(仅限 gateway)\n```\n\n当 `redact_pii` 为 `true` 时,gateway 在将系统提示词发送到受支持平台上的 LLM 之前,会从中删除个人身份信息:\n\n| 字段 | 处理方式 |\n|-------|-----------|\n| 电话号码(WhatsApp/Signal 上的用户 ID) | 哈希为 `user_<12-char-sha256>` |\n| 用户 ID | 哈希为 `user_<12-char-sha256>` |\n| 聊天 ID | 数字部分哈希,保留平台前缀(`telegram:<hash>`) |\n| 主频道 ID | 数字部分哈希 |\n| 用户名/昵称 | **不受影响**(用户选择的,公开可见) |\n\n**平台支持:** 删除适用于 WhatsApp、Signal 和 Telegram。Discord 和 Slack 被排除,因为它们的提及系统(`<@user_id>`)需要 LLM 上下文中的真实 ID。\n\n哈希是确定性的 —— 同一用户始终映射到同一哈希,因此模型仍然可以在群聊中区分用户。路由和传递在内部使用原始值。\n\n## 语音转文字(STT)\n\n```yaml\nstt:\n provider: \"local\" # \"local\" | \"groq\" | \"openai\" | \"mistral\"\n local:\n model: \"base\" # tiny、base、small、medium、large-v3\n openai:\n model: \"whisper-1\" # whisper-1 | gpt-4o-mini-transcribe | gpt-4o-transcribe\n # model: \"whisper-1\" # 旧版回退键仍受支持\n```\n\nProvider 行为:\n\n- `local` 使用在您机器上运行的 `faster-whisper`。使用 `pip install faster-whisper` 单独安装。\n- `groq` 使用 Groq 的 Whisper 兼容端点,读取 `GROQ_API_KEY`。\n- `openai` 使用 OpenAI 语音 API,读取 `VOICE_TOOLS_OPENAI_KEY`。\n\n如果请求的 provider 不可用,Hermes 按此顺序自动回退:`local` → `groq` → `openai`。\n\nGroq 和 OpenAI 模型覆盖由环境变量驱动:\n\n```bash\nSTT_GROQ_MODEL=whisper-large-v3-turbo\nSTT_OPENAI_MODEL=whisper-1\nGROQ_BASE_URL=https://api.groq.com/openai/v1\nSTT_OPENAI_BASE_URL=https://api.openai.com/v1\n```\n\n## 语音模式(CLI)\n\n```yaml\nvoice:\n record_key: \"ctrl+b\" # CLI 内的按键通话键\n max_recording_seconds: 120 # 长录音的硬停止\n auto_tts: false # /voice on 时自动启用口语回复\n beep_enabled: true # 在 CLI 语音模式中播放录音开始/停止提示音\n silence_threshold: 200 # 语音检测的 RMS 阈值\n silence_duration: 3.0 # 自动停止前的静默秒数\n```\n\n在 CLI 中使用 `/voice on` 启用麦克风模式,使用 `record_key` 开始/停止录音,使用 `/voice tts` 切换口语回复。端到端设置和平台特定行为请参阅[语音模式](/user-guide/features/voice-mode)。\n\n## 流式传输\n\n将 token 实时流式传输到终端或消息平台,而不是等待完整响应。\n\n### CLI 流式传输\n\n```yaml\ndisplay:\n streaming: true # 实时将 token 流式传输到终端\n show_reasoning: true # 同时流式传输推理/思考 token(可选)\n```\n\n启用后,响应在流式传输框内逐 token 出现。工具调用仍然静默捕获。如果 provider 不支持流式传输,它会自动回退到正常显示。\n\n### Gateway 流式传输(Telegram、Discord、Slack)\n\n```yaml\nstreaming:\n enabled: true # 启用渐进式消息编辑\n transport: edit # \"edit\"(渐进式消息编辑)或 \"off\"\n edit_interval: 0.3 # 消息编辑之间的秒数\n buffer_threshold: 40 # 强制编辑刷新前的字符数\n cursor: \" ▉\" # 流式传输期间显示的光标\n fresh_final_after_seconds: 0 # 预览超过此时间时选择发送新的最终消息(Telegram)\n```\n\n启用后,bot 在第一个 token 时发送消息,然后随着更多 token 到来渐进式编辑它。不支持消息编辑的平台(Signal、Email、Home Assistant)在第一次尝试时自动检测 —— 该会话的流式传输被优雅地禁用,不会产生大量消息。\n\n对于不带渐进式 token 编辑的独立自然轮次中 assistant 更新,请设置 `display.interim_assistant_messages: true`。\n\n**溢出处理:** 如果流式传输的文本超过平台的消息长度限制(约 4096 字符),当前消息被最终化,新消息自动开始。\n\n**新的最终消息(Telegram):** Telegram 的 `editMessageText` 保留原始消息时间戳,因此长时间运行的流式回复即使在完成后也会保留第一个 token 的时间戳。设置 `fresh_final_after_seconds > 0` 可选择将旧预览作为全新的最终消息传递,并尽力删除旧预览。默认值为 `0`,始终就地最终化流式回复,避免某些客户端短暂显示重复消息再删除其中一条。\n\n:::note\n主开关 `streaming.enabled` 默认为 `false`——在你启用之前不会有任何流式传输。启用后,是否流式传输按**平台**决定:Telegram 默认带有 `display.platforms.telegram.streaming: true`(流式传输),Discord 为 `display.platforms.discord.streaming: false`(不流式传输)。因此启用流式传输后,Telegram 开箱即用地流式传输,Discord 在你修改其开关之前仍使用整条消息回复。你可以在仪表盘的 **Channels** 开关中或直接在 `~/.hermes/config.yaml` 中调整这些按平台的开关。\n:::\n\n## 群聊会话隔离\n\n控制共享聊天是每个房间保持一个对话还是每个参与者一个对话:\n\n```yaml\ngroup_sessions_per_user: true # true = 群组/频道中每用户隔离,false = 每个聊天一个共享会话\n```\n\n- `true` 是默认和推荐设置。在 Discord 频道、Telegram 群组、Slack 频道和类似共享上下文中,当平台提供用户 ID 时,每个发送者获得自己的会话。\n- `false` 恢复到旧的共享房间行为。如果您明确希望 Hermes 将频道视为一个协作对话,这可能有用,但这也意味着用户共享上下文、token 成本和中断状态。\n- 私信不受影响。Hermes 仍然像往常一样通过聊天/DM ID 键入 DM。\n- 线程与其父频道保持隔离;使用 `true` 时,每个参与者在线程内也获得自己的会话。\n\n有关行为详情和示例,请参阅[会话](/user-guide/sessions)和 [Discord 指南](/user-guide/messaging/discord)。\n\n## 未授权 DM 行为\n\n控制当未知用户发送私信时 Hermes 的行为:\n\n```yaml\nunauthorized_dm_behavior: pair\n\nwhatsapp:\n unauthorized_dm_behavior: ignore\n```\n\n- `pair` 是默认值。Hermes 拒绝访问,但在 DM 中回复一次性配对码。\n- `ignore` 静默丢弃未授权的 DM。\n- 平台部分覆盖全局默认值,因此您可以在广泛范围内保持配对启用,同时使一个平台更安静。\n\n## 快速命令\n\n定义自定义命令,这些命令要么在不调用 LLM 的情况下运行 shell 命令,要么将一个斜杠命令别名为另一个。Exec 快速命令是零 token 的,对于从消息平台(Telegram、Discord 等)进行快速服务器检查或实用脚本很有用。\n\n```yaml\nquick_commands:\n status:\n type: exec\n command: systemctl status hermes-agent\n disk:\n type: exec\n command: df -h /\n update:\n type: exec\n command: cd ~/.hermes/hermes-agent && git pull && pip install -e .\n gpu:\n type: exec\n command: nvidia-smi --query-gpu=name,utilization.gpu,memory.used,memory.total --format=csv,noheader\n restart:\n type: alias\n target: /gateway restart\n```\n\n用法:在 CLI 或任何消息平台中输入 `/status`、`/disk`、`/update`、`/gpu` 或 `/restart`。`exec` 命令在宿主本地运行并直接返回输出 —— 无 LLM 调用,不消耗 token。`alias` 命令重写为配置的斜杠命令目标。\n\n- **30 秒超时** —— 长时间运行的命令被终止并显示错误消息\n- **优先级** —— 快速命令在技能命令之前检查,因此您可以覆盖技能名称\n- **自动补全** —— 快速命令在调度时解析,不显示在内置斜杠命令自动补全表中\n- **类型** —— 支持的类型为 `exec` 和 `alias`;其他类型显示错误\n- **到处可用** —— CLI、Telegram、Discord、Slack、WhatsApp、Signal、Email、Home Assistant\n\n仅字符串的 prompt 快捷方式不是有效的快速命令。对于可重用的 prompt 工作流,请创建技能或别名到现有斜杠命令。\n\n## 人类延迟\n\n在消息平台中模拟类人响应节奏:\n\n```yaml\nhuman_delay:\n mode: \"off\" # off | natural | custom\n min_ms: 800 # 最小延迟(自定义模式)\n max_ms: 2500 # 最大延迟(自定义模式)\n```\n\n## 代码执行\n\n配置 `execute_code` 工具:\n\n```yaml\ncode_execution:\n mode: project # project(默认)| strict\n timeout: 300 # 最大执行时间(秒)\n max_tool_calls: 50 # 代码执行中的最大工具调用次数\n```\n\n**`mode`** 控制脚本的工作目录和 Python 解释器:\n\n- **`project`**(默认)—— 脚本在会话的工作目录中以活跃 virtualenv/conda 环境的 python 运行。项目依赖(`pandas`、`torch`、项目包)和相对路径(`.env`、`./data.csv`)自然解析,与 `terminal()` 看到的一致。\n- **`strict`** —— 脚本在临时暂存目录中以 `sys.executable`(Hermes 自己的 python)运行。最大可重现性,但项目依赖和相对路径不会解析。\n\n环境清理(删除 `*_API_KEY`、`*_TOKEN`、`*_SECRET`、`*_PASSWORD`、`*_CREDENTIAL`、`*_PASSWD`、`*_AUTH`)和工具白名单在两种模式下完全相同 —— 切换模式不会改变安全态势。\n\n## Web 搜索后端\n\n`web_search` 和 `web_extract` 工具支持五种后端 provider。在 `config.yaml` 中或通过 `hermes tools` 配置后端:\n\n```yaml\nweb:\n backend: firecrawl # firecrawl | searxng | parallel | tavily | exa\n\n # 或使用每功能键混合 provider(例如免费搜索 + 付费提取):\n search_backend: \"searxng\"\n extract_backend: \"firecrawl\"\n```\n\n| 后端 | 环境变量 | 搜索 | 提取 |\n|---------|---------|--------|---------|\n| **Firecrawl**(默认) | `FIRECRAWL_API_KEY` | ✔ | ✔ |\n| **SearXNG** | `SEARXNG_URL` | ✔ | — |\n| **Parallel** | `PARALLEL_API_KEY` | ✔ | ✔ |\n| **Tavily** | `TAVILY_API_KEY` | ✔ | ✔ |\n| **Exa** | `EXA_API_KEY` | ✔ | ✔ |\n\n**后端选择:** 如果未设置 `web.backend`,后端从可用的 API 密钥自动检测。如果仅设置了 `SEARXNG_URL`,使用 SearXNG。如果仅设置了 `EXA_API_KEY`,使用 Exa。如果仅设置了 `TAVILY_API_KEY`,使用 Tavily。如果仅设置了 `PARALLEL_API_KEY`,使用 Parallel。否则 Firecrawl 是默认值。\n\n**SearXNG** 是一个免费、自托管、尊重隐私的元搜索引擎,查询 70+ 个搜索引擎。无需 API 密钥 —— 只需将 `SEARXNG_URL` 设置为您的实例(例如 `http://localhost:8080`)。SearXNG 仅限搜索;`web_extract` 需要单独的提取 provider(设置 `web.extract_backend`)。Docker 设置说明请参阅 [Web 搜索设置指南](/user-guide/features/web-search)。\n\n**自托管 Firecrawl:** 设置 `FIRECRAWL_API_URL` 指向您自己的实例。设置自定义 URL 后,API 密钥变为可选(在服务器上设置 `USE_DB_AUTHENTICATION=***` 以禁用认证)。\n\n**Parallel 搜索模式:** 设置 `PARALLEL_SEARCH_MODE` 控制搜索行为 —— `fast`、`one-shot` 或 `agentic`(默认:`agentic`)。\n\n**Exa:** 在 `~/.hermes/.env` 中设置 `EXA_API_KEY`。支持 `category` 过滤(`company`、`research paper`、`news`、`people`、`personal site`、`pdf`)和域名/日期过滤器。\n\n## 浏览器\n\n配置浏览器自动化行为:\n\n```yaml\nbrowser:\n inactivity_timeout: 120 # 自动关闭空闲会话前的秒数\n command_timeout: 30 # 浏览器命令超时(截图、导航等)(秒)\n record_sessions: false # 自动将浏览器会话录制为 WebM 视频到 ~/.hermes/browser_recordings/\n # 可选 CDP 覆盖 —— 设置后,Hermes 直接附加到您自己的\n # Chromium 系浏览器(通过 /browser connect),而不是启动无头浏览器。\n cdp_url: \"\"\n # 对话框监督器 —— 控制当 CDP 后端附加时(Browserbase、本地 Chromium 系\n # 浏览器通过 /browser connect)如何处理原生 JS 对话框(alert/confirm/prompt)。\n # 在 Camofox 和默认本地 agent 浏览器模式下忽略。\n dialog_policy: must_respond # must_respond | auto_dismiss | auto_accept\n dialog_timeout_s: 300 # must_respond 下的安全自动关闭(秒)\n camofox:\n managed_persistence: false # 为 true 时,Camofox 会话跨重启持久化 cookie/登录\n user_id: \"\" # 可选的外部管理 Camofox userId\n session_key: \"\" # Hermes 创建标签页时发送的可选会话密钥\n adopt_existing_tab: false # 在创建新标签页之前重用此身份的现有标签页\n```\n\n**对话框策略:**\n\n- `must_respond`(默认)—— 捕获对话框,在 `browser_snapshot.pending_dialogs` 中显示,等待 agent 调用 `browser_dialog(action=...)`。在 `dialog_timeout_s` 秒内无响应后,对话框被自动关闭以防止页面的 JS 线程永久停滞。\n- `auto_dismiss` —— 捕获,立即关闭。Agent 仍然在事后的 `browser_snapshot.recent_dialogs` 中看到对话框记录,`closed_by=\"auto_policy\"`。\n- `auto_accept` —— 捕获,立即接受。适用于有激进 `beforeunload` 提示的页面。\n\n完整对话框工作流请参阅[浏览器功能页面](./features/browser.md#browser_dialog)。\n\n浏览器工具集支持多个 provider。有关 Browserbase、Browser Use 和本地 Chromium 系 CDP 设置的详细信息,请参阅[浏览器功能页面](/user-guide/features/browser)。\n\n## 时区\n\n使用 IANA 时区字符串覆盖服务器本地时区。影响日志中的时间戳、cron 调度和系统提示词时间注入。\n\n```yaml\ntimezone: \"America/New_York\" # IANA 时区(默认:\"\" = 服务器本地时间)\n```\n\n支持的值:任何 IANA 时区标识符(例如 `America/New_York`、`Europe/London`、`Asia/Kolkata`、`UTC`)。留空或省略以使用服务器本地时间。\n\n## Discord\n\n为消息 gateway 配置 Discord 特定行为:\n\n```yaml\ndiscord:\n require_mention: true # 在服务器频道中需要 @提及才能响应\n free_response_channels: \"\" # 逗号分隔的频道 ID,bot 在这些频道无需 @提及即可响应\n auto_thread: true # 在频道中 @提及时自动创建线程\n```\n\n- `require_mention` —— 为 `true`(默认)时,bot 仅在服务器频道中被 `@BotName` 提及时响应。DM 始终无需提及即可工作。\n- `free_response_channels` —— 逗号分隔的频道 ID 列表,bot 在这些频道对每条消息响应,无需提及。\n- `auto_thread` —— 为 `true`(默认)时,频道中的提及会自动为对话创建线程,保持频道整洁(类似 Slack 线程)。\n\n## 安全\n\n预执行安全扫描和机密脱敏:\n\n```yaml\nsecurity:\n redact_secrets: false # 在工具输出和日志中脱敏 API 密钥模式(默认关闭)\n tirith_enabled: true # 为终端命令启用 Tirith 安全扫描\n tirith_path: \"tirith\" # tirith 二进制文件路径(默认:$PATH 中的 \"tirith\")\n tirith_timeout: 5 # 等待 tirith 扫描的秒数\n tirith_fail_open: true # 如果 tirith 不可用,允许命令执行\n website_blocklist: # 参见下方网站黑名单部分\n enabled: false\n domains: []\n shared_files: []\n```\n\n- `redact_secrets` —— 为 `true` 时,自动检测并脱敏工具输出中看起来像 API 密钥、token 和密码的模式,然后再进入对话上下文和日志。**默认关闭** —— 如果您经常在工具输出中处理真实凭据并希望有安全网,请启用。显式设置为 `true` 以开启。\n- `tirith_enabled` —— 为 `true` 时,终端命令在执行前由 [Tirith](https://github.com/sheeki03/tirith) 扫描以检测潜在危险操作。\n- `tirith_path` —— tirith 二进制文件的路径。如果 tirith 安装在非标准位置,请设置此项。\n- `tirith_timeout` —— 等待 tirith 扫描的最大秒数。如果扫描超时,命令继续执行。\n- `tirith_fail_open` —— 为 `true`(默认)时,如果 tirith 不可用或失败,允许命令执行。设置为 `false` 以在 tirith 无法验证时阻止命令。\n\n## 网站黑名单\n\n阻止 agent 的 web 和浏览器工具访问特定域名:\n\n```yaml\nsecurity:\n website_blocklist:\n enabled: false # 启用 URL 阻止(默认:false)\n domains: # 被阻止的域名模式列表\n - \"*.internal.company.com\"\n - \"admin.example.com\"\n - \"*.local\"\n shared_files: # 从外部文件加载额外规则\n - \"/etc/hermes/blocked-sites.txt\"\n```\n\n启用后,任何匹配被阻止域名模式的 URL 在 web 或浏览器工具执行之前都会被拒绝。这适用于 `web_search`、`web_extract`、`browser_navigate` 以及任何访问 URL 的工具。\n\n域名规则支持:\n- 精确域名:`admin.example.com`\n- 通配符子域名:`*.internal.company.com`(阻止所有子域名)\n- TLD 通配符:`*.local`\n\n共享文件每行包含一条域名规则(空行和 `#` 注释被忽略)。缺失或不可读的文件记录警告,但不禁用其他 web 工具。\n\n策略缓存 30 秒,因此配置更改无需重启即可快速生效。\n\n## 智能审批\n\n控制 Hermes 如何处理潜在危险命令:\n\n```yaml\napprovals:\n mode: smart # smart | manual | off\n```\n\n| 模式 | 行为 |\n|------|----------|\n| `smart`(默认) | 使用辅助 LLM 评估被标记的命令是否真正危险。低风险命令仅对当前命令自动批准,真正危险的命令自动拒绝,不确定的情况升级给用户。 |\n| `manual` | 在执行任何被标记的命令之前提示用户。在 CLI 中显示交互式审批对话框。在消息中排队待处理的审批请求。 |\n| `off` | 跳过所有审批检查。等同于 `HERMES_YOLO_MODE=true`。**谨慎使用。** |\n\n智能模式对于减少审批疲劳特别有用 —— 它让 agent 在安全操作上更自主地工作,同时仍然捕获真正破坏性的命令。\n\n:::warning\n设置 `approvals.mode: off` 会禁用终端命令的所有安全检查。仅在受信任的沙箱环境中使用。\n:::\n\n## 检查点\n\n破坏性文件操作之前的自动文件系统快照。详情请参阅[检查点与回滚](/user-guide/checkpoints-and-rollback)。\n\n```yaml\ncheckpoints:\n enabled: false # 启用自动检查点(也可:hermes chat --checkpoints)。默认:false(选择加入)。\n max_snapshots: 20 # 每个目录保留的最大检查点数(默认:20)\n```\n\n\n## 委托\n\n为委托工具配置子 agent 行为:\n\n```yaml\ndelegation:\n # model: \"google/gemini-3-flash-preview\" # 覆盖模型(空 = 继承父级)\n # provider: \"openrouter\" # 覆盖 provider(空 = 继承父级)\n # base_url: \"http://localhost:1234/v1\" # 直接 OpenAI 兼容端点(优先于 provider)\n # api_key: \"local-key\" # base_url 的 API 密钥(回退到 OPENAI_API_KEY)\n # api_mode: \"\" # base_url 的线路协议:\"chat_completions\"、\"codex_responses\" 或 \"anthropic_messages\"。空 = 从 URL 自动检测(例如 /anthropic 后缀 → anthropic_messages)。对启发式无法检测的非标准端点显式设置。\n max_concurrent_children: 3 # 每批并行子 agent 数(下限 1,无上限)。也可通过 DELEGATION_MAX_CONCURRENT_CHILDREN 环境变量设置。\n max_spawn_depth: 1 # 委托树深度上限(1-3,截断)。1 = 扁平(默认):父级生成无法委托的叶子。2 = 编排器子级可以生成叶子孙级。3 = 三级。\n orchestrator_enabled: true # 全局终止开关。为 false 时,role=\"orchestrator\" 被忽略,每个子级无论 max_spawn_depth 如何都被强制为叶子。\n```\n\n**子 agent provider:model 覆盖:** 默认情况下,子 agent 继承父 agent 的 provider 和模型。设置 `delegation.provider` 和 `delegation.model` 将子 agent 路由到不同的 provider:model 对 —— 例如,在您的主 agent 运行昂贵推理模型时,为范围较窄的子任务使用便宜/快速的模型。\n\n**直接端点覆盖:** 如果您想要明显的自定义端点路径,请设置 `delegation.base_url`、`delegation.api_key` 和 `delegation.model`。这将子 agent 直接发送到该 OpenAI 兼容端点,并优先于 `delegation.provider`。如果省略 `delegation.api_key`,Hermes 仅回退到 `OPENAI_API_KEY`。\n\n**线路协议(`api_mode`):** Hermes 从 `delegation.base_url` 自动检测线路协议(例如以 `/anthropic` 结尾的路径 → `anthropic_messages`;Codex/原生 Anthropic/Kimi-coding 主机名保留其现有检测)。对于启发式无法分类的端点 —— 例如 Azure AI Foundry、MiniMax、Zhipu GLM 或前置 Anthropic 形状后端的 LiteLLM 代理 —— 请将 `delegation.api_mode` 显式设置为 `chat_completions`、`codex_responses` 或 `anthropic_messages` 之一。留空(默认)以保持自动检测。\n\n委托 provider 使用与 CLI/gateway 启动相同的凭据解析。所有配置的 provider 均受支持:`openrouter`、`nous`、`copilot`、`zai`、`kimi-coding`、`minimax`、`minimax-cn`。设置 provider 时,系统自动解析正确的基础 URL、API 密钥和 API 模式 —— 无需手动凭据连接。\n\n**优先级:** 配置中的 `delegation.base_url` → 配置中的 `delegation.provider` → 父 provider(继承)。配置中的 `delegation.model` → 父模型(继承)。仅设置 `model` 而不设置 `provider` 仅更改模型名称,同时保留父级凭据(适用于在同一 provider(如 OpenRouter)内切换模型)。\n\n**宽度和深度:** `max_concurrent_children` 限制每批并行运行的子 agent 数量(默认 `3`,下限 1,无上限)。也可通过 `DELEGATION_MAX_CONCURRENT_CHILDREN` 环境变量设置。当模型提交的 `tasks` 数组超过上限时,`delegate_task` 返回工具错误解释限制,而不是静默截断。`max_spawn_depth` 控制委托树深度(截断到 1-3)。在默认 `1` 时,委托是扁平的:子级无法生成孙级,传递 `role=\"orchestrator\"` 静默降级为 `leaf`。提升到 `2` 使编排器子级可以生成叶子孙级;`3` 用于三级树。Agent 通过 `role=\"orchestrator\"` 按调用选择编排;`orchestrator_enabled: false` 强制每个子级回到叶子,无论如何。成本呈乘法增长 —— 在 `max_spawn_depth: 3` 和 `max_concurrent_children: 3` 时,树可以达到 3×3×3 = 27 个并发叶子 agent。使用模式请参阅[子 Agent 委托 → 深度限制和嵌套编排](features/delegation.md#depth-limit-and-nested-orchestration)。\n\n## 澄清\n\n配置澄清提示行为:\n\n```yaml\nclarify:\n timeout: 120 # 等待用户澄清响应的秒数\n```\n\n## 上下文文件(SOUL.md、AGENTS.md)\n\nHermes 使用两种不同的上下文范围:\n\n| 文件 | 用途 | 范围 |\n|------|---------|-------|\n| `SOUL.md` | **主要 agent 身份** —— 定义 agent 是谁(系统提示词第 #1 槽位) | `~/.hermes/SOUL.md` 或 `$HERMES_HOME/SOUL.md` |\n| `.hermes.md` / `HERMES.md` | 项目特定指令(最高优先级) | 向上走到 git 根目录 |\n| `AGENTS.md` | 项目特定指令、编码规范 | 递归目录遍历 |\n| `CLAUDE.md` | Claude Code 上下文文件(也会检测) | 仅工作目录 |\n| `.cursorrules` | Cursor IDE 规则(也会检测) | 仅工作目录 |\n| `.cursor/rules/*.mdc` | Cursor 规则文件(也会检测) | 仅工作目录 |\n\n- **SOUL.md** 是 agent 的主要身份。它占据系统提示词的第 #1 槽位,完全替换内置的默认身份。编辑它以完全自定义 agent 是谁。\n- 如果 SOUL.md 缺失、为空或无法加载,Hermes 回退到内置默认身份。\n- **项目上下文文件使用优先级系统** —— 仅加载一种类型(第一个匹配优先):`.hermes.md` → `AGENTS.md` → `CLAUDE.md` → `.cursorrules`。SOUL.md 始终独立加载。\n- **AGENTS.md** 是分层的:如果子目录也有 AGENTS.md,所有都会合并。\n- 如果 `SOUL.md` 不存在,Hermes 会自动生成默认的 `SOUL.md`。\n- 所有加载的上下文文件上限为 20,000 字符,并进行智能截断。\n\n另请参阅:\n- [个性与 SOUL.md](/user-guide/features/personality)\n- [上下文文件](/user-guide/features/context-files)\n\n## 工作目录\n\n| 上下文 | 默认值 |\n|---------|---------|\n| **CLI(`hermes`)** | 运行命令的当前目录 |\n| **消息 gateway** | 主目录 `~`(用 `MESSAGING_CWD` 覆盖) |\n| **Docker / Singularity / Modal / SSH** | 容器或远程机器内用户的主目录 |\n\n覆盖工作目录:\n```bash\n# 在 ~/.hermes/.env 或 ~/.hermes/config.yaml 中:\nMESSAGING_CWD=/home/myuser/projects # Gateway 会话\nTERMINAL_CWD=/workspace # 所有终端会话\n```\n"}, "62a76bd3d5a658b84b3dafff6233f13e2522b95e": {"t": 1785767984.9302175, "n": 56661, "h": "1fc1190c4135439b", "c": "---\nsidebar_position: 2\ntitle: \"配置\"\ndescription: \"配置 Hermes Agent — config.yaml、providers、模型、API 密钥等\"\n---\n\n# 配置\n\n所有设置均存储在 `~/.hermes/` 目录中,便于访问。\n\n## 目录结构\n\n```text\n~/.hermes/\n├── config.yaml # 设置(模型、终端、TTS、压缩等)\n├── .env # API 密钥和机密\n├── auth.json # OAuth provider 凭据(Nous Portal 等)\n├── SOUL.md # 主要 agent 身份(系统提示词第 #1 槽位)\n├── memories/ # 持久记忆(MEMORY.md、USER.md)\n├── skills/ # Agent 创建的技能(通过 skill_manage 工具管理)\n├── cron/ # 定时任务\n├── sessions/ # Gateway 会话\n└── logs/ # 日志(errors.log、gateway.log — 机密自动脱敏)\n```\n\n## 管理配置\n\n```bash\nhermes config # 查看当前配置\nhermes config edit # 在编辑器中打开 config.yaml\nhermes config set KEY VAL # 设置特定值\nhermes config check # 检查缺失选项(更新后使用)\nhermes config migrate # 交互式添加缺失选项\n\n# 示例:\nhermes config set model anthropic/claude-opus-4\nhermes config set terminal.backend docker\nhermes config set OPENROUTER_API_KEY sk-or-... # 保存到 .env\n```\n\n:::tip\n`hermes config set` 命令会自动将值路由到正确的文件 —— API 密钥保存到 `.env`,其他所有内容保存到 `config.yaml`。\n:::\n\n## 配置优先级\n\n设置按以下顺序解析(优先级从高到低):\n\n1. **CLI 参数** —— 例如 `hermes chat --model anthropic/claude-sonnet-4`(单次调用覆盖)\n2. **`~/.hermes/config.yaml`** —— 所有非机密设置的主配置文件\n3. **`~/.hermes/.env`** —— 环境变量的回退;机密(API 密钥、token、密码)**必须**放这里\n4. **内置默认值** —— 未设置任何内容时的硬编码安全默认值\n\n:::info 经验法则\n机密(API 密钥、bot token、密码)放入 `.env`。其他所有内容(模型、终端后端、压缩设置、内存限制、工具集)放入 `config.yaml`。当两者都设置时,`config.yaml` 对非机密设置优先。\n:::\n\n## 环境变量替换\n\n可以在 `config.yaml` 中使用 `${VAR_NAME}` 语法引用环境变量:\n\n```yaml\nauxiliary:\n vision:\n api_key: ${GOOGLE_API_KEY}\n base_url: ${CUSTOM_VISION_URL}\n\ndelegation:\n api_key: ${DELEGATION_KEY}\n```\n\n单个值中可以有多个引用:`url: \"${HOST}:${PORT}\"`。如果引用的变量未设置,占位符将保持原样(`${UNDEFINED_VAR}` 保持不变)。仅支持 `${VAR}` 语法 —— 裸 `$VAR` 不会被展开。\n\n关于 AI provider 设置(OpenRouter、Anthropic、Copilot、自定义端点、自托管 LLM、回退模型等),请参阅 [AI Providers](/integrations/providers)。\n\n### Provider 超时\n\n可以为 provider 设置 `providers.<id>.request_timeout_seconds` 作为全局请求超时,以及 `providers.<id>.models.<model>.timeout_seconds` 作为特定模型的覆盖值。适用于每种传输方式(OpenAI-wire、原生 Anthropic、Anthropic 兼容)上的主轮次客户端、回退链、凭据轮换后的重建,以及(对于 OpenAI-wire)每请求超时 kwarg —— 因此配置值优先于旧版 `HERMES_API_TIMEOUT` 环境变量。\n\n还可以设置 `providers.<id>.stale_timeout_seconds` 用于非流式陈旧调用检测器,以及 `providers.<id>.models.<model>.stale_timeout_seconds` 作为特定模型的覆盖值。此值优先于旧版 `HERMES_API_CALL_STALE_TIMEOUT` 环境变量。\n\n不设置这些值将保持旧版默认值(`HERMES_API_TIMEOUT=1800`s、`HERMES_API_CALL_STALE_TIMEOUT=90`s、原生 Anthropic 900s)。隐式的非流式 stale 检测会在本地端点上自动禁用,并且会在超大上下文下自动放宽。目前不适用于 AWS Bedrock(`bedrock_converse` 和 AnthropicBedrock SDK 路径均使用 boto3 及其自身的超时配置)。请参阅 [`cli-config.yaml.example`](https://github.com/NousResearch/hermes-agent/blob/main/cli-config.yaml.example) 中的注释示例。\n\n## 终端后端配置\n\nHermes 支持六种终端后端。每种后端决定 agent 的 shell 命令实际在哪里执行 —— 本地机器、Docker 容器、通过 SSH 的远程服务器、Modal 云沙箱(直接或通过 Nous 托管的 gateway)、Daytona 工作区,或 Singularity/Apptainer 容器。\n\n```yaml\nterminal:\n backend: local # local | docker | ssh | modal | daytona | singularity\n cwd: \".\" # Gateway/cron 工作目录(CLI 始终使用启动目录)\n timeout: 180 # 每条命令的超时时间(秒)\n env_passthrough: [] # 转发到沙箱执行的环境变量名(terminal + execute_code)\n singularity_image: \"docker://nikolaik/python-nodejs:python3.11-nodejs20\" # Singularity 后端的容器镜像\n modal_image: \"nikolaik/python-nodejs:python3.11-nodejs20\" # Modal 后端的容器镜像\n daytona_image: \"nikolaik/python-nodejs:python3.11-nodejs20\" # Daytona 后端的容器镜像\n```\n\n对于 Modal 和 Daytona 等云沙箱,`container_persistent: true` 表示 Hermes 将尝试在沙箱重建后保留文件系统状态。这并不保证相同的活跃沙箱、PID 空间或后台进程之后仍在运行。\n\n### 后端概览\n\n| 后端 | 命令运行位置 | 隔离性 | 最适合 |\n|---------|-------------------|-----------|----------|\n| **local** | 直接在您的机器上 | 无 | 开发、个人使用 |\n| **docker** | 单个持久 Docker 容器(跨会话、`/new`、子 agent 共享) | 完全(命名空间、cap-drop) | 安全沙箱、CI/CD |\n| **ssh** | 通过 SSH 的远程服务器 | 网络边界 | 远程开发、强大硬件 |\n| **modal** | Modal 云沙箱 | 完全(云 VM) | 临时云计算、评估 |\n| **daytona** | Daytona 工作区 | 完全(云容器) | 托管云开发环境 |\n| **singularity** | Singularity/Apptainer 容器 | 命名空间(--containall) | HPC 集群、共享机器 |\n\n### Local 后端\n\n默认后端。命令直接在您的机器上运行,无隔离。无需特殊设置。\n\n```yaml\nterminal:\n backend: local\n```\n\n:::warning\nAgent 拥有与您的用户账户相同的文件系统访问权限。使用 `hermes tools` 禁用不需要的工具,或切换到 Docker 进行沙箱隔离。\n:::\n\n### Docker 后端\n\n在具有安全加固的 Docker 容器内运行命令(所有权限已删除、无权限提升、PID 限制)。\n\n**单个持久容器,而非每条命令一个容器。** Hermes 在首次使用时启动一个长期运行的容器,并通过 `docker exec` 将每个终端、文件和 `execute_code` 调用路由到同一容器中 —— 跨会话、`/new`、`/reset` 和 `delegate_task` 子 agent,贯穿 Hermes 进程的整个生命周期。工作目录更改、已安装的包以及 `/workspace` 中的文件会从一次工具调用延续到下一次,就像本地 shell 一样。容器在关闭时停止并删除。详情请参阅下方的**容器生命周期**。\n\n```yaml\nterminal:\n backend: docker\n docker_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n docker_mount_cwd_to_workspace: false # 将启动目录挂载到 /workspace\n docker_run_as_host_user: false # 参见下方\"以宿主用户身份运行容器\"\n docker_forward_env: # 转发到容器的环境变量\n - \"GITHUB_TOKEN\"\n docker_volumes: # 宿主目录挂载\n - \"/home/user/projects:/workspace/projects\"\n - \"/home/user/data:/data:ro\" # :ro 表示只读\n docker_extra_args: # 附加到 `docker run` 的额外标志\n - \"--gpus=all\"\n - \"--network=host\"\n\n # 资源限制\n container_cpu: 1 # CPU 核心数(0 = 不限制)\n container_memory: 5120 # MB(0 = 不限制)\n container_disk: 51200 # MB(需要 XFS+pquota 上的 overlay2)\n container_persistent: true # 跨会话持久化 /workspace 和 /root\n```\n\n**`terminal.docker_extra_args`**(也可通过 `TERMINAL_DOCKER_EXTRA_ARGS='[\"--gpus=all\"]'` 覆盖)允许传递 Hermes 未作为一级键公开的任意 `docker run` 标志 —— `--gpus`、`--network`、`--add-host`、替代 `--security-opt` 覆盖等。每个条目必须是字符串;该列表最后附加到组装好的 `docker run` 调用中,因此可以在需要时覆盖 Hermes 的默认值。请谨慎使用 —— 与沙箱加固(权限删除、`--user`、workspace 绑定挂载)冲突的标志将悄然削弱隔离性。\n\n**要求:** 已安装并运行 Docker Desktop 或 Docker Engine。Hermes 会探测 `$PATH` 以及常见的 macOS 安装位置(`/usr/local/bin/docker`、`/opt/homebrew/bin/docker`、Docker Desktop 应用包)。开箱即用支持 Podman:设置 `HERMES_DOCKER_BINARY=podman`(或完整路径)以在两者都安装时强制使用它。\n\n**容器生命周期:** Hermes 为每个终端和文件工具调用重用单个长期运行的容器(`docker run -d ... sleep 2h`),跨会话、`/new`、`/reset` 和 `delegate_task` 子 agent,贯穿 Hermes 进程的整个生命周期。命令通过带登录 shell 的 `docker exec` 运行,因此工作目录更改、已安装的包以及 `/workspace` 中的文件都会从一次工具调用延续到下一次。容器在 Hermes 关闭时(或空闲清理回收时)停止并删除。\n\n通过 `delegate_task(tasks=[...])` 生成的并行子 agent 共享这一个容器 —— 并发的 `cd`、环境变量修改以及对同一路径的写入会发生冲突。如果子 agent 需要隔离的沙箱,必须通过 `register_task_env_overrides()` 注册每任务镜像覆盖,RL 和基准测试环境(TerminalBench2、HermesSweEnv 等)会自动为其每任务 Docker 镜像执行此操作。\n\n**安全加固:**\n- `--cap-drop ALL`,仅添加回 `DAC_OVERRIDE`、`CHOWN`、`FOWNER`\n- `--security-opt no-new-privileges`\n- `--pids-limit 256`\n- `/tmp`(512MB)、`/var/tmp`(256MB)、`/run`(64MB)的大小限制 tmpfs\n\n**凭据转发:** `docker_forward_env` 中列出的环境变量首先从您的 shell 环境解析,然后回退到 `~/.hermes/.env`。技能也可以声明 `required_environment_variables`,这些变量会自动合并。\n\n### SSH 后端\n\n通过 SSH 在远程服务器上运行命令。使用 ControlMaster 进行连接复用(5 分钟空闲保活)。默认启用持久 shell —— 状态(cwd、环境变量)在命令之间保持。\n\n```yaml\nterminal:\n backend: ssh\n persistent_shell: true # 保持长期运行的 bash 会话(默认:true)\n```\n\n**必需的环境变量:**\n\n```bash\nTERMINAL_SSH_HOST=my-server.example.com\nTERMINAL_SSH_USER=ubuntu\n```\n\n**可选:**\n\n| 变量 | 默认值 | 描述 |\n|----------|---------|-------------|\n| `TERMINAL_SSH_PORT` | `22` | SSH 端口 |\n| `TERMINAL_SSH_KEY` | (系统默认) | SSH 私钥路径 |\n| `TERMINAL_SSH_PERSISTENT` | `true` | 启用持久 shell |\n\n**工作原理:** 使用 `BatchMode=yes` 和 `StrictHostKeyChecking=accept-new` 在初始化时连接。持久 shell 在远程主机上保持单个 `bash -l` 进程存活,通过临时文件进行通信。需要 `stdin_data` 或 `sudo` 的命令会自动回退到单次模式。\n\n### Modal 后端\n\n在 [Modal](https://modal.com) 云沙箱中运行命令。每个任务获得一个具有可配置 CPU、内存和磁盘的隔离 VM。文件系统可以跨会话快照/恢复。\n\n```yaml\nterminal:\n backend: modal\n container_cpu: 1 # CPU 核心数\n container_memory: 5120 # MB(5GB)\n container_disk: 51200 # MB(50GB)\n container_persistent: true # 快照/恢复文件系统\n```\n\n**必需:** `MODAL_TOKEN_ID` + `MODAL_TOKEN_SECRET` 环境变量,或 `~/.modal.toml` 配置文件。\n\n**持久化:** 启用后,沙箱文件系统在清理时快照,并在下次会话时恢复。快照在 `~/.hermes/modal_snapshots.json` 中跟踪。这保留文件系统状态,而非活跃进程、PID 空间或后台任务。\n\n**凭据文件:** 自动从 `~/.hermes/` 挂载(OAuth token 等),并在每条命令前同步。\n\n### Daytona 后端\n\n在 [Daytona](https://daytona.io) 托管工作区中运行命令。支持停止/恢复以实现持久化。\n\n```yaml\nterminal:\n backend: daytona\n container_cpu: 1 # CPU 核心数\n container_memory: 5120 # MB → 转换为 GiB\n container_disk: 10240 # MB → 转换为 GiB(最大 10 GiB)\n container_persistent: true # 停止/恢复而非删除\n```\n\n**必需:** `DAYTONA_API_KEY` 环境变量。\n\n**持久化:** 启用后,沙箱在清理时停止(而非删除),并在下次会话时恢复。沙箱名称遵循 `hermes-{task_id}` 模式。\n\n**磁盘限制:** Daytona 强制执行 10 GiB 最大值。超过此值的请求将被截断并发出警告。\n\n### Singularity/Apptainer 后端\n\n在 [Singularity/Apptainer](https://apptainer.org) 容器中运行命令。专为 Docker 不可用的 HPC 集群和共享机器设计。\n\n```yaml\nterminal:\n backend: singularity\n singularity_image: \"docker://nikolaik/python-nodejs:python3.11-nodejs20\"\n container_cpu: 1 # CPU 核心数\n container_memory: 5120 # MB\n container_persistent: true # 可写覆盖层跨会话持久化\n```\n\n**要求:** `$PATH` 中有 `apptainer` 或 `singularity` 二进制文件。\n\n**镜像处理:** Docker URL(`docker://...`)自动转换为 SIF 文件并缓存。现有 `.sif` 文件直接使用。\n\n**临时目录:** 按顺序解析:`TERMINAL_SCRATCH_DIR` → `TERMINAL_SANDBOX_DIR/singularity` → `/scratch/$USER/hermes-agent`(HPC 惯例)→ `~/.hermes/sandboxes/singularity`。\n\n**隔离:** 使用 `--containall --no-home` 实现完全命名空间隔离,不挂载宿主 home 目录。\n\n### 常见终端后端问题\n\n如果终端命令立即失败或终端工具报告为已禁用:\n\n- **Local** —— 无特殊要求。入门时最安全的默认选项。\n- **Docker** —— 运行 `docker version` 验证 Docker 是否正常工作。如果失败,修复 Docker 或执行 `hermes config set terminal.backend local`。\n- **SSH** —— `TERMINAL_SSH_HOST` 和 `TERMINAL_SSH_USER` 都必须设置。如果缺少任一项,Hermes 会记录清晰的错误。\n- **Modal** —— 需要 `MODAL_TOKEN_ID` 环境变量或 `~/.modal.toml`。运行 `hermes doctor` 检查。\n- **Daytona** —— 需要 `DAYTONA_API_KEY`。Daytona SDK 处理服务器 URL 配置。\n- **Singularity** —— 需要 `$PATH` 中有 `apptainer` 或 `singularity`。HPC 集群上常见。\n\n如有疑问,将 `terminal.backend` 设回 `local` 并首先验证命令在那里运行。\n\n### 拆卸时远程到宿主文件同步\n\n对于 **SSH**、**Modal** 和 **Daytona** 后端(agent 的工作树位于与运行 Hermes 的宿主不同的机器上),Hermes 跟踪 agent 在远程沙箱中触及的文件,并在会话拆卸/沙箱清理时,将修改的文件**同步回宿主**,存放在 `~/.hermes/cache/remote-syncs/<session-id>/` 下。\n\n- 触发时机:会话关闭、`/new`、`/reset`、gateway 消息超时、子 agent 使用远程后端时 `delegate_task` 子 agent 完成。\n- 覆盖 agent 修改的整个树,而不仅仅是它明确打开的文件。添加、编辑和删除都会被捕获。\n- 远程沙箱可能在您查找时已被拆除;本地 `~/.hermes/cache/remote-syncs/…` 副本是 agent 更改内容的权威记录。\n- 大型二进制输出(模型检查点、原始数据集)按大小限制 —— 同步跳过超过 `file_sync_max_mb`(默认 `100`)的文件。如果您期望更大的工件返回,请调高该值。\n\n```yaml\nterminal:\n file_sync_max_mb: 100 # 默认 —— 同步最大 100 MB 的文件\n file_sync_enabled: true # 默认 —— 设为 false 可完全跳过同步\n```\n\n这是从会话结束后被销毁的临时云沙箱中恢复结果的方式,无需告诉 agent 显式地 `scp` 或 `modal volume put` 每个工件。\n\n### Docker 卷挂载\n\n使用 Docker 后端时,`docker_volumes` 允许您与容器共享宿主目录。每个条目使用标准 Docker `-v` 语法:`host_path:container_path[:options]`。\n\n```yaml\nterminal:\n backend: docker\n docker_volumes:\n - \"/home/user/projects:/workspace/projects\" # 读写(默认)\n - \"/home/user/datasets:/data:ro\" # 只读\n - \"/home/user/.hermes/cache/documents:/output\" # Gateway 可见的导出\n```\n\n适用于:\n- **向 agent 提供文件**(数据集、配置、参考代码)\n- **从 agent 接收文件**(生成的代码、报告、导出)\n- **共享工作区**,您和 agent 都访问相同的文件\n\n如果您使用消息 gateway 并希望 agent 通过 `MEDIA:/...` 发送生成的文件,建议使用专用的宿主可见导出挂载,例如 `/home/user/.hermes/cache/documents:/output`。\n\n- 在 Docker 中将文件写入 `/output/...`\n- 在 `MEDIA:` 中发出**宿主路径**,例如:`MEDIA:/home/user/.hermes/cache/documents/report.txt`\n- **不要**发出 `/workspace/...` 或 `/output/...`,除非该确切路径在宿主上对 gateway 进程也存在\n\n:::warning\nYAML 重复键会静默覆盖之前的键。如果您已有 `docker_volumes:` 块,请将新挂载合并到同一列表中,而不是在文件后面再添加一个 `docker_volumes:` 键。\n:::\n\n也可以通过环境变量设置:`TERMINAL_DOCKER_VOLUMES='[\"/host:/container\"]'`(JSON 数组)。\n\n### Docker 凭据转发\n\n默认情况下,Docker 终端会话不继承任意宿主凭据。如果您需要在容器内使用特定 token,请将其添加到 `terminal.docker_forward_env`。\n\n```yaml\nterminal:\n backend: docker\n docker_forward_env:\n - \"GITHUB_TOKEN\"\n - \"NPM_TOKEN\"\n```\n\nHermes 首先从您当前的 shell 解析每个列出的变量,然后回退到通过 `hermes config set` 保存的 `~/.hermes/.env`。\n\n:::warning\n`docker_forward_env` 中列出的任何内容都会对容器内运行的命令可见。只转发您愿意暴露给终端会话的凭据。\n:::\n\n### 以宿主用户身份运行容器\n\n默认情况下,Docker 容器以 `root`(UID 0)身份运行。在 `/workspace` 或其他绑定挂载中创建的文件在宿主上归 root 所有,因此会话结束后您必须 `sudo chown` 才能从宿主编辑器编辑它们。`terminal.docker_run_as_host_user` 标志解决了这个问题:\n\n```yaml\nterminal:\n backend: docker\n docker_run_as_host_user: true # 默认:false\n```\n\n启用后,Hermes 将 `--user $(id -u):$(id -g)` 附加到 `docker run` 命令,使写入绑定挂载目录(`/workspace`、`/root`、`docker_volumes` 中的任何内容)的文件归您的宿主用户所有,而非 root。权衡:容器将无法再 `apt install` 或写入 `/root/.npm` 等 root 拥有的路径 —— 如果您同时需要这两者,请使用 `HOME` 归非 root 用户所有的基础镜像(或在镜像构建时添加所需工具)。\n\n保持 `false`(默认)以获得向后兼容的行为。当您的工作流主要是\"编辑挂载的宿主文件\"且厌倦了 `sudo chown -R` 时,请开启此选项。\n\n### 可选:将启动目录挂载到 `/workspace`\n\nDocker 沙箱默认保持隔离。Hermes **不会**将您当前的宿主工作目录传入容器,除非您明确选择加入。\n\n在 `config.yaml` 中启用:\n\n```yaml\nterminal:\n backend: docker\n docker_mount_cwd_to_workspace: true\n```\n\n启用后:\n- 如果您从 `~/projects/my-app` 启动 Hermes,该宿主目录将绑定挂载到 `/workspace`\n- Docker 后端从 `/workspace` 开始\n- 文件工具和终端命令都能看到相同的挂载项目\n\n禁用时,`/workspace` 保持沙箱所有,除非您通过 `docker_volumes` 显式挂载内容。\n\n安全权衡:\n- `false` 保留沙箱边界\n- `true` 使沙箱直接访问您启动 Hermes 的目录\n\n仅在您有意希望容器处理实时宿主文件时才选择加入。\n\n### 持久 Shell\n\n默认情况下,每条终端命令在其自己的子进程中运行 —— 工作目录、环境变量和 shell 变量在命令之间重置。启用**持久 shell** 后,单个长期运行的 bash 进程在 `execute()` 调用之间保持存活,使状态在命令之间保持。\n\n这对 **SSH 后端**最有用,它还消除了每条命令的连接开销。持久 shell **对 SSH 默认启用**,对本地后端禁用。\n\n```yaml\nterminal:\n persistent_shell: true # 默认 —— 为 SSH 启用持久 shell\n```\n\n禁用:\n\n```bash\nhermes config set terminal.persistent_shell false\n```\n\n**跨命令保持的内容:**\n- 工作目录(`cd /tmp` 对下一条命令生效)\n- 导出的环境变量(`export FOO=bar`)\n- Shell 变量(`MY_VAR=hello`)\n\n**优先级:**\n\n| 级别 | 变量 | 默认值 |\n|-------|----------|---------|\n| 配置 | `terminal.persistent_shell` | `true` |\n| SSH 覆盖 | `TERMINAL_SSH_PERSISTENT` | 遵循配置 |\n| Local 覆盖 | `TERMINAL_LOCAL_PERSISTENT` | `false` |\n\n每个后端的环境变量具有最高优先级。如果您也想在本地后端使用持久 shell:\n\n```bash\nexport TERMINAL_LOCAL_PERSISTENT=true\n```\n\n:::note\n需要 `stdin_data` 或 sudo 的命令会自动回退到单次模式,因为持久 shell 的 stdin 已被 IPC 协议占用。\n:::\n\n有关每个后端的详细信息,请参阅[代码执行](features/code-execution.md)和 [README 的终端部分](features/tools.md)。\n\n## 技能设置\n\n技能可以通过其 SKILL.md frontmatter 声明自己的配置设置。这些是非机密值(路径、偏好、域设置),存储在 `config.yaml` 的 `skills.config` 命名空间下。\n\n```yaml\nskills:\n config:\n myplugin:\n path: ~/myplugin-data # 示例 —— 每个技能定义自己的键\n```\n\n**技能设置的工作原理:**\n\n- `hermes config migrate` 扫描所有已启用的技能,找到未配置的设置,并提供提示\n- `hermes config show` 在\"技能设置\"下显示所有技能设置及其所属技能\n- 技能加载时,其解析的配置值会自动注入到技能上下文中\n\n**手动设置值:**\n\n```bash\nhermes config set skills.config.myplugin.path ~/myplugin-data\n```\n\n有关在您自己的技能中声明配置设置的详细信息,请参阅[创建技能 — 配置设置](/developer-guide/creating-skills#config-settings-configyaml)。\n\n### Agent 创建技能写入的守卫\n\n当 agent 使用 `skill_manage` 创建、编辑、修补或删除技能时,Hermes 可以选择扫描新/更新的内容以查找危险关键字模式(凭据收集、明显的 prompt 注入、数据外泄指令)。扫描器**默认关闭** —— 合法触及 `~/.ssh/` 或提及 `$OPENAI_API_KEY` 的真实 agent 工作流触发启发式规则过于频繁。如果您希望扫描器在 agent 的技能写入落地前提示您,请重新开启:\n\n```yaml\nskills:\n guard_agent_created: true # 默认:false\n```\n\n开启后,任何被标记的 `skill_manage` 写入都会以审批提示的形式出现,并附带扫描器的理由。接受的写入落地;拒绝的写入向 agent 返回解释性错误。\n\n## 内存配置\n\n```yaml\nmemory:\n memory_enabled: true\n user_profile_enabled: true\n memory_char_limit: 2200 # ~800 tokens\n user_char_limit: 1375 # ~500 tokens\n```\n\n## 文件读取安全\n\n控制单次 `read_file` 调用可以返回多少内容。超过限制的读取将被拒绝,并向 agent 返回错误,提示使用 `offset` 和 `limit` 读取较小范围。这可以防止单次读取压缩的 JS 包或大型数据文件时淹没上下文窗口。\n\n```yaml\nfile_read_max_chars: 100000 # 默认 —— ~25-35K tokens\n```\n\n如果您使用具有大上下文窗口的模型并经常读取大文件,请调高此值。对于小上下文模型,请降低以保持读取高效:\n\n```yaml\n# 大上下文模型(200K+)\nfile_read_max_chars: 200000\n\n# 小型本地模型(16K 上下文)\nfile_read_max_chars: 30000\n```\n\nAgent 还会自动去重文件读取 —— 如果同一文件区域被读取两次且文件未更改,则返回轻量级存根而不是重新发送内容。这在上下文压缩后重置,以便 agent 在内容被摘要后可以重新读取文件。\n\n## 工具输出截断限制\n\n三个相关的上限控制工具在 Hermes 截断之前可以返回多少原始输出:\n\n```yaml\ntool_output:\n max_bytes: 50000 # 终端输出上限(字符)\n max_lines: 2000 # read_file 分页上限\n max_line_length: 2000 # read_file 行号视图中的每行上限\n```\n\n- **`max_bytes`** —— 当 `terminal` 命令产生超过此字符数的合并 stdout/stderr 时,Hermes 保留前 40% 和后 60%,并在中间插入 `[OUTPUT TRUNCATED]` 通知。默认 `50000`(典型分词器约 12-15K tokens)。\n- **`max_lines`** —— 单次 `read_file` 调用的 `limit` 参数上限。超过此值的请求将被截断,以防单次读取淹没上下文窗口。默认 `2000`。\n- **`max_line_length`** —— `read_file` 发出行号视图时应用的每行上限。超过此长度的行将被截断为此字符数,后跟 `... [truncated]`。默认 `2000`。\n\n对于具有大上下文窗口且每次调用可以承受更多原始输出的模型,请调高限制。对于小上下文模型,请降低以保持工具结果紧凑:\n\n```yaml\n# 大上下文模型(200K+)\ntool_output:\n max_bytes: 150000\n max_lines: 5000\n\n# 小型本地模型(16K 上下文)\ntool_output:\n max_bytes: 20000\n max_lines: 500\n```\n\n## 全局工具集禁用\n\n要在 CLI 和每个 gateway 平台上统一禁用特定工具集,请在 `agent.disabled_toolsets` 下列出其名称:\n\n```yaml\nagent:\n disabled_toolsets:\n - memory # 隐藏内存工具 + MEMORY_GUIDANCE 注入\n - web # 任何地方都不使用 web_search / web_extract\n```\n\n这在每个平台的工具配置(由 `hermes tools` 写入的 `platform_toolsets`)**之后**应用,因此此处列出的工具集始终被删除 —— 即使平台的已保存配置仍然列出它。当您希望有一个\"到处关闭 X\"的单一开关而不是编辑 `hermes tools` UI 中的 15+ 个平台行时,请使用此选项。\n\n留空列表或省略键不会产生任何效果。\n\n## Git Worktree 隔离\n\n启用隔离的 git worktree,以便在同一仓库上并行运行多个 agent:\n\n```yaml\nworktree: true # 始终创建 worktree(与 hermes -w 相同)\n# worktree: false # 默认 —— 仅在传递 -w 标志时\n```\n\n启用后,每个 CLI 会话在 `.worktrees/` 下创建一个带有自己分支的新 worktree。Agent 可以编辑文件、提交、推送和创建 PR,而不会相互干扰。干净的 worktree 在退出时删除;脏的 worktree 保留以供手动恢复。\n\n您还可以通过仓库根目录中的 `.worktreeinclude` 列出要复制到 worktree 的 gitignore 文件:\n\n```\n# .worktreeinclude\n.env\n.venv/\nnode_modules/\n```\n\n## 上下文压缩\n\nHermes 自动压缩长对话以保持在模型的上下文窗口内。压缩摘要器是一个单独的 LLM 调用 —— 您可以将其指向任何 provider 或端点。\n\n所有压缩设置都在 `config.yaml` 中(无环境变量)。\n\n### 完整参考\n\n```yaml\ncompression:\n enabled: true # 开启/关闭压缩\n threshold: 0.50 # 在上下文限制的此百分比时压缩\n target_ratio: 0.20 # 保留为最近尾部的阈值分数\n protect_last_n: 20 # 保持未压缩的最少最近消息数\n hygiene_hard_message_limit: 5000 # Gateway 安全阀 —— 见下文\n\n# 摘要模型/provider 在 auxiliary: 下配置:\nauxiliary:\n compression:\n model: \"\" # 空 = 使用主聊天模型。覆盖为例如 \"google/gemini-3-flash-preview\" 以获得更便宜/更快的压缩。\n provider: \"auto\" # Provider:\"auto\"、\"openrouter\"、\"nous\"、\"codex\"、\"main\" 等\n base_url: null # 自定义 OpenAI 兼容端点(覆盖 provider)\n```\n\n:::info 旧版配置迁移\n带有 `compression.summary_model`、`compression.summary_provider` 和 `compression.summary_base_url` 的旧版配置在首次加载时自动迁移到 `auxiliary.compression.*`(配置版本 17)。无需手动操作。\n:::\n\n`hygiene_hard_message_limit` 是仅限 gateway 的**预压缩安全阀**。它的存在是为了打破一个死循环:当超大会话的 API 调用持续断开时,gateway 永远收不到 token 使用数据,基于 token 的阈值因此无法触发,于是 transcript 持续增长、断开愈发严重。这个基于消息数的下限仅凭消息数量触发(无论 API 是否失败,消息数始终已知),强制压缩以恢复会话。默认 `5000` —— 远高于任何正常会话,包括做数千次短轮次的大上下文(1M+)模型,它们早就在 token 阈值处压缩了。对于异常平台可调得更高;要强制更积极的压缩则调低。在运行中的 gateway 上编辑此值将在下一条消息时生效(见下文)。\n\n:::tip Gateway 热重载压缩和上下文长度\n从最近的版本开始,在运行中的 gateway 上编辑 `config.yaml` 中的 `model.context_length` 或任何 `compression.*` 键将在下一条消息时生效 —— 无需 gateway 重启、`/reset` 或会话轮换。缓存的 agent 签名包含这些键,因此 gateway 在检测到更改时会透明地重建 agent。API 密钥和工具/技能配置仍需要通常的重载路径。\n:::\n\n### 常见设置\n\n**默认(自动检测)—— 无需配置:**\n```yaml\ncompression:\n enabled: true\n threshold: 0.50\n```\n使用您的主 provider 和主模型。如果您希望在比主聊天模型更便宜的模型上进行压缩,请覆盖每任务(例如 `auxiliary.compression.provider: openrouter` + `model: google/gemini-2.5-flash`)。\n\n**强制特定 provider**(基于 OAuth 或 API 密钥):\n```yaml\nauxiliary:\n compression:\n provider: nous\n model: gemini-3-flash\n```\n适用于任何 provider:`nous`、`openrouter`、`codex`、`anthropic`、`main` 等。\n\n**自定义端点**(自托管、Ollama、zai、DeepSeek 等):\n```yaml\nauxiliary:\n compression:\n model: glm-4.7\n base_url: https://api.z.ai/api/coding/paas/v4\n```\n指向自定义 OpenAI 兼容端点。使用 `OPENAI_API_KEY` 进行认证。\n\n### 三个旋钮的交互方式\n\n| `auxiliary.compression.provider` | `auxiliary.compression.base_url` | 结果 |\n|---------------------|---------------------|--------|\n| `auto`(默认) | 未设置 | 自动检测最佳可用 provider |\n| `nous` / `openrouter` / 等 | 未设置 | 强制使用该 provider,使用其认证 |\n| 任意 | 已设置 | 直接使用自定义端点(忽略 provider) |\n\n:::warning 摘要模型上下文长度要求\n摘要模型**必须**具有至少与您的主 agent 模型一样大的上下文窗口。压缩器将对话的完整中间部分发送给摘要模型 —— 如果该模型的上下文窗口小于主模型的,摘要调用将因上下文长度错误而失败。发生这种情况时,中间轮次将**在没有摘要的情况下被丢弃**,静默丢失对话上下文。如果您覆盖模型,请验证其上下文长度满足或超过您的主模型。\n:::\n\n## 上下文引擎\n\n上下文引擎控制在接近模型 token 限制时如何管理对话。内置的 `compressor` 引擎使用有损摘要(参见[上下文压缩](/developer-guide/context-compression-and-caching))。插件引擎可以用替代策略替换它。\n\n```yaml\ncontext:\n engine: \"compressor\" # 默认 —— 内置有损摘要\n```\n\n使用插件引擎(例如,用于无损上下文管理的 LCM):\n\n```yaml\ncontext:\n engine: \"lcm\" # 必须与插件名称匹配\n```\n\n插件引擎**永远不会自动激活** —— 您必须将 `context.engine` 显式设置为插件名称。可用引擎可以通过 `hermes plugins` → Provider Plugins → Context Engine 浏览和选择。\n\n有关内存插件的类似单选系统,请参阅[内存 Providers](/user-guide/features/memory-providers)。\n\n## 迭代预算\n\n当 agent 在处理具有许多工具调用的复杂任务时,它可能会耗尽其迭代预算(默认:90 轮)。Hermes **不会**在任务中途注入压力警告 —— 早期版本会在预算达到 70%/90% 时警告模型,这会导致模型过早放弃复杂任务,该机制已于 2026 年 4 月移除。\n\n取而代之的是,当预算真正耗尽(90/90)时,Hermes 注入一条消息要求模型收尾,并允许一次**宽限调用**以便其给出最终响应。如果该宽限调用仍未产生文本,则会要求 agent 总结已完成的工作。\n\n```yaml\nagent:\n max_turns: 90 # 每次对话轮次的最大迭代次数(默认:90)\n api_max_retries: 3 # 回退启动前每个 provider 的重试次数(默认:3)\n```\n\n当迭代预算完全耗尽时,CLI 向用户显示通知:`⚠ Iteration budget reached (90/90) — response may be incomplete`。\n\n`agent.api_max_retries` 控制 Hermes 在回退 provider 切换启动**之前**对瞬时错误(速率限制、连接断开、5xx)重试 provider API 调用的次数。默认为 `3` —— 总共四次尝试。如果您配置了[回退 providers](/user-guide/features/fallback-providers) 并希望更快地故障转移,请将其降至 `0`,这样主 provider 上的第一个瞬时错误会立即切换到回退,而不是对不稳定的端点进行重试。\n\n### API 超时\n\nHermes 对流式传输有单独的超时层,以及用于非流式调用的陈旧检测器。陈旧检测器仅在您将其保留为隐式默认值时才会自动调整本地 provider。\n\n| 超时 | 默认值 | 本地 providers | 配置/环境变量 |\n|---------|---------|----------------|--------------|\n| Socket 读取超时 | 120s | 自动提升至 1800s | `HERMES_STREAM_READ_TIMEOUT` |\n| 陈旧流检测 | 180s | 自动禁用 | `HERMES_STREAM_STALE_TIMEOUT` |\n| 陈旧非流检测 | 300s | 保持隐式时自动禁用 | `providers.<id>.stale_timeout_seconds` 或 `HERMES_API_CALL_STALE_TIMEOUT` |\n| API 调用(非流式) | 1800s | 不变 | `providers.<id>.request_timeout_seconds` / `timeout_seconds` 或 `HERMES_API_TIMEOUT` |\n\n**Socket 读取超时**控制 httpx 等待 provider 下一个数据块的时间。本地 LLM 在大上下文上预填充可能需要几分钟才能产生第一个 token,因此当 Hermes 检测到本地端点时,会将此值提升至 30 分钟。如果您显式设置 `HERMES_STREAM_READ_TIMEOUT`,无论端点检测如何,始终使用该值。\n\n**陈旧流检测**终止接收 SSE 保活 ping 但没有实际内容的连接。对于本地 providers,这完全禁用,因为它们在预填充期间不发送保活 ping。\n\n**陈旧非流检测**终止长时间没有响应的非流式调用。默认情况下,Hermes 在本地端点上禁用此功能,以避免长时间预填充期间的误报。如果您显式设置 `providers.<id>.stale_timeout_seconds`、`providers.<id>.models.<model>.stale_timeout_seconds` 或 `HERMES_API_CALL_STALE_TIMEOUT`,即使在本地端点上也会遵守该显式值。\n\n## 上下文压力警告\n\n与迭代预算压力分开,上下文压力跟踪对话距**压缩阈值**有多近 —— 即上下文压缩触发以摘要旧消息的点。这有助于您和 agent 了解对话何时变长。\n\n| 进度 | 级别 | 发生的事情 |\n|----------|-------|-------------|\n| **≥ 60%** 到阈值 | 信息 | CLI 显示青色进度条;gateway 发送信息通知 |\n| **≥ 85%** 到阈值 | 警告 | CLI 显示粗体黄色进度条;gateway 警告压缩即将发生 |\n\n在 CLI 中,上下文压力在工具输出流中显示为进度条:\n\n```\n ◐ context ████████████░░░░░░░░ 62% to compaction 48k threshold (50%) · approaching compaction\n```\n\n在消息平台上,发送纯文本通知:\n\n```\n◐ Context: ████████████░░░░░░░░ 62% to compaction (threshold: 50% of window).\n```\n\n如果自动压缩被禁用,警告会告诉您上下文可能被截断。\n\n上下文压力是自动的 —— 无需配置。它纯粹作为面向用户的通知触发,不修改消息流或向模型上下文注入任何内容。\n\n## 凭据池策略\n\n当您为同一 provider 拥有多个 API 密钥或 OAuth token 时,配置轮换策略:\n\n```yaml\ncredential_pool_strategies:\n openrouter: round_robin # 均匀循环使用密钥\n anthropic: least_used # 始终选择使用最少的密钥\n```\n\n选项:`fill_first`(默认)、`round_robin`、`least_used`、`random`。完整文档请参阅[凭据池](/user-guide/features/credential-pools)。\n\n## Prompt 缓存\n\n当活跃 provider 支持时,Hermes 自动开启跨会话 prompt 缓存 —— 无需用户配置。\n\n对于**原生 Anthropic**、**OpenRouter** 和 **Nous Portal** 上的 Claude,Hermes 在系统提示词和技能块上附加带有 1 小时 TTL(`ttl: \"1h\"`)的 `cache_control` 断点。在新鲜的一小时内首次发送时按完整输入费率计费;同一小时内任何会话的后续发送以折扣缓存读取费率从缓存中提取。这意味着系统提示词、加载的技能内容以及任何长上下文包含的早期部分在第一个小时内跨 `hermes` 会话和分叉子 agent 被重用。\n\nQwen Cloud(阿里巴巴 DashScope)上游将缓存 TTL 限制为 5 分钟,因此 Hermes 在那里使用 5 分钟断点 TTL。其他通过第三方的 Claude 路径(AWS Bedrock、Azure Foundry)回退到 provider 自己的缓存默认值。xAI Grok 使用单独的会话固定对话 ID 机制 —— 参阅 [xAI prompt 缓存](/integrations/providers#xai-grok--responses-api--prompt-caching)。\n\n不存在禁用此功能的旋钮 —— 缓存始终开启,即使在单轮对话中也能节省费用,因为仅系统提示词就占输入 token 数的相当大比例。\n\n## 辅助模型\n\nHermes 使用\"辅助\"模型处理图像分析、网页摘要、浏览器截图分析、会话标题生成和上下文压缩等附带任务。默认情况下(`auxiliary.*.provider: \"auto\"`),Hermes 将每个辅助任务路由到您的**主聊天模型** —— 与您在 `hermes model` 中选择的相同 provider/模型。您无需配置任何内容即可开始,但请注意,在昂贵的推理模型(Opus、MiniMax M2.7 等)上,辅助任务会增加显著成本。如果您希望无论主模型如何都使用便宜且快速的附带任务,请显式设置 `auxiliary.<task>.provider` 和 `auxiliary.<task>.model`(例如,在 OpenRouter 上使用 Gemini Flash 进行视觉和网页提取)。\n\n:::note 为什么 \"auto\" 使用您的主模型\n早期版本将聚合器用户(OpenRouter、Nous Portal)分流到便宜的 provider 端默认值。这令人惊讶 —— 付费购买聚合器订阅的用户会看到不同的模型处理其辅助流量。`auto` 现在对所有人使用主模型,`config.yaml` 中的每任务覆盖仍然优先(见下方[完整辅助配置参考](#full-auxiliary-config-reference))。\n:::\n\n### 交互式配置辅助模型\n\n无需手动编辑 YAML,运行 `hermes model` 并从菜单中选择**\"配置辅助模型\"**。您将获得交互式的每任务选择器:\n\n```\n$ hermes model\n→ Configure auxiliary models\n\n[ ] vision currently: auto / main model\n[ ] web_extract currently: auto / main model\n[ ] title_generation currently: openrouter / google/gemini-3-flash-preview\n[ ] compression currently: auto / main model\n[ ] approval currently: auto / main model\n[ ] triage_specifier currently: auto / main model\n[ ] kanban_decomposer currently: auto / main model\n[ ] profile_describer currently: auto / main model\n```\n\n选择任务,选择 provider(OAuth 流程打开浏览器;API 密钥 provider 提示输入),选择模型。更改持久化到 `config.yaml` 中的 `auxiliary.<task>.*`。与主模型选择器相同的机制 —— 无需学习额外语法。\n\n### 视频教程\n\n<div style={{position: 'relative', width: '100%', aspectRatio: '16 / 9', marginBottom: '1.5rem'}}>\n <iframe\n src=\"https://www.youtube.com/embed/NoF-YajElIM\"\n title=\"Hermes Agent — Auxiliary Models Tutorial\"\n style={{position: 'absolute', top: 0, left: 0, width: '100%', height: '100%', border: 0}}\n allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\"\n allowFullScreen\n />\n</div>\n\n### 通用配置模式\n\nHermes 中的每个模型槽位 —— 辅助任务、压缩、回退 —— 使用相同的三个旋钮:\n\n| 键 | 作用 | 默认值 |\n|-----|-------------|---------|\n| `provider` | 用于认证和路由的 provider | `\"auto\"` |\n| `model` | 请求的模型 | provider 的默认值 |\n| `base_url` | 自定义 OpenAI 兼容端点(覆盖 provider) | 未设置 |\n\n当设置 `base_url` 时,Hermes 忽略 provider 并直接调用该端点(使用 `api_key` 或 `OPENAI_API_KEY` 进行认证)。当仅设置 `provider` 时,Hermes 使用该 provider 的内置认证和基础 URL。\n\n辅助任务的可用 providers:`auto`、`main`,以及[provider 注册表](/reference/environment-variables)中的任何 provider —— `openrouter`、`nous`、`openai-codex`、`copilot`、`copilot-acp`、`anthropic`、`gemini`、`qwen-oauth`、`zai`、`kimi-coding`、`kimi-coding-cn`、`minimax`、`minimax-cn`、`minimax-oauth`、`deepseek`、`nvidia`、`xai`、`xai-oauth`、`ollama-cloud`、`alibaba`、`bedrock`、`huggingface`、`arcee`、`xiaomi`、`kilocode`、`opencode-zen`、`opencode-go`、`azure-foundry` —— 或您 `custom_providers` 列表中任何命名的自定义 provider(例如 `provider: \"beans\"`)。\n\n:::tip MiniMax OAuth\n`minimax-oauth` 通过浏览器 OAuth 登录(无需 API 密钥)。运行 `hermes model` 并选择 **MiniMax (OAuth)** 进行认证。辅助任务自动使用 `MiniMax-M2.7-highspeed`。参阅 [MiniMax OAuth 指南](../guides/minimax-oauth.md)。\n:::\n\n:::tip xAI Grok OAuth\n`xai-oauth` 通过浏览器 OAuth 为 SuperGrok 和 X Premium+ 订阅者登录(无需 API 密钥)。运行 `hermes model` 并选择 **xAI Grok OAuth (SuperGrok / Premium+)** 进行认证。相同的 OAuth token 可重用于每个直接到 xAI 的接口(聊天、辅助任务、TTS、图像生成、视频生成、转录)。参阅 [xAI Grok OAuth 指南](../guides/xai-grok-oauth.md),如果 Hermes 在远程主机上,请参阅 [SSH/远程主机上的 OAuth](../guides/oauth-over-ssh.md)。\n:::\n\n:::warning `\"main\"` 仅用于辅助任务\n`\"main\"` provider 选项表示\"使用我的主 agent 使用的任何 provider\" —— 它仅在 `auxiliary:`、`compression:` 和 `fallback_model:` 配置中有效。它**不是**顶级 `model.provider` 设置的有效值。如果您使用自定义 OpenAI 兼容端点,请在 `model:` 部分设置 `provider: custom`。所有主模型 provider 选项请参阅 [AI Providers](/integrations/providers)。\n:::\n\n### 完整辅助配置参考\n\n```yaml\nauxiliary:\n # 图像分析(vision_analyze 工具 + 浏览器截图)\n vision:\n provider: \"auto\" # \"auto\"、\"openrouter\"、\"nous\"、\"codex\"、\"main\" 等\n model: \"\" # 例如 \"openai/gpt-4o\"、\"google/gemini-2.5-flash\"\n base_url: \"\" # 自定义 OpenAI 兼容端点(覆盖 provider)\n api_key: \"\" # base_url 的 API 密钥(回退到 OPENAI_API_KEY)\n timeout: 120 # 秒 —— LLM API 调用超时;视觉负载需要宽裕的超时\n download_timeout: 30 # 秒 —— 图像 HTTP 下载;慢速连接请增加\n\n # 网页摘要 + 浏览器页面文本提取\n web_extract:\n provider: \"auto\"\n model: \"\" # 例如 \"google/gemini-2.5-flash\"\n base_url: \"\"\n api_key: \"\"\n timeout: 360 # 秒(6 分钟)—— 每次尝试的 LLM 摘要\n\n # 危险命令审批分类器\n approval:\n provider: \"auto\"\n model: \"\"\n base_url: \"\"\n api_key: \"\"\n timeout: 30 # 秒\n\n # 上下文压缩超时(与 compression.* 配置分开)\n compression:\n timeout: 120 # 秒 —— 压缩摘要长对话,需要更多时间\n # fallback_chain: # 可选 —— 发生速率限制/连接故障时尝试的 provider\n # - provider: nous\n # model: deepseek/deepseek-chat\n # - provider: openrouter\n # model: google/gemini-2.5-flash\n # base_url: \"\"\n # api_key: \"\"\n\n # 技能中心 —— 技能匹配和搜索\n skills_hub:\n provider: \"auto\"\n model: \"\"\n base_url: \"\"\n api_key: \"\"\n timeout: 30\n\n # MCP 工具调度\n mcp:\n provider: \"auto\"\n model: \"\"\n base_url: \"\"\n api_key: \"\"\n timeout: 30\n\n # Kanban 分类规格说明器 —— `hermes kanban specify <id>`(或\n # 仪表板上 Triage 列卡片的 ✨ Specify 按钮)使用此\n # 槽位将单行描述扩展为具体规格并将\n # 任务提升到 `todo`。便宜快速的模型在这里效果很好;规格扩展\n # 很短,不需要推理深度。\n triage_specifier:\n provider: \"auto\"\n model: \"\"\n base_url: \"\"\n api_key: \"\"\n timeout: 120\n```\n\n:::tip\n每个辅助任务都有可配置的 `timeout`(秒)。默认值:vision 120s、web_extract 360s、approval 30s、compression 120s。如果您为辅助任务使用慢速本地模型,请增加这些值。Vision 还有单独的 `download_timeout`(默认 30s)用于 HTTP 图像下载 —— 对于慢速连接或自托管图像服务器,请增加此值。\n:::\n\n:::info\n上下文压缩有自己的 `compression:` 块用于阈值,以及 `auxiliary.compression:` 块用于模型/provider 设置 —— 参阅上方的[上下文压缩](#context-compression)。主备用链使用顶层的 `fallback_providers:` 列表 —— 参阅[备用提供商](/integrations/providers#fallback-providers)。三者都遵循相同的 provider/model/base_url 模式。\n:::\n\n### 辅助任务的每任务回退链\n\n每个辅助任务都可以选择性地定义一个 `fallback_chain` —— 一个 provider/model 条目列表,当主要辅助 provider 因速率限制、网络连接问题或付费限制而失败时,Hermes 会尝试使用该列表:\n\n```yaml\nauxiliary:\n compression:\n provider: openrouter\n model: openai/gpt-4o-mini\n fallback_chain:\n - provider: nous\n model: deepseek/deepseek-chat\n - provider: openrouter\n model: google/gemini-2.5-flash\n```\n\n当主要辅助 provider(`openrouter` / `openai/gpt-4o-mini`)返回速率限制、连接超时或需要付费错误时,Hermes 将依次遍历 `fallback_chain`。它会跳过 provider 与已失败 provider 相同的条目,并尝试每个剩余条目,直到有一个成功或该链耗尽。如果所有回退都失败,Hermes 会回退到主 agent 模型作为最终的安全网。\n\n每个条目支持与任何辅助任务配置相同的三个旋钮:\n\n| 键 | 描述 |\n|-----|-------------|\n| `provider` | Provider 名称(`nous`、`openrouter`、`anthropic`、`gemini`、`main` 等) |\n| `model` | 该 provider 的模型名称 |\n| `base_url` | (可选)自定义 OpenAI 兼容端点 |\n\n`fallback_chain` 适用于任何辅助任务 —— `compression`、`vision`、`web_extract`、`approval`、`skills_hub`、`mcp` 等。\n\n### OpenRouter 路由和辅助任务的 Pareto Code\n\n当辅助任务解析到 OpenRouter(显式或通过 `provider: \"main\"` 而您的主 agent 在 OpenRouter 上)时,主 agent 的 `provider_routing` 和 `openrouter.min_coding_score` 设置**不会传播** —— 按设计,每个辅助任务是独立的。要为特定辅助任务设置 OpenRouter provider 偏好或使用 [Pareto Code 路由器](/integrations/providers#openrouter-pareto-code-router),请通过 `extra_body` 按任务设置:\n\n```yaml\nauxiliary:\n compression:\n provider: openrouter\n model: openrouter/pareto-code # 为此任务使用 Pareto Code 路由器\n extra_body:\n provider: # OpenRouter provider 路由偏好\n order: [anthropic, google] # 按此顺序尝试这些 providers\n sort: throughput # 或 \"price\" | \"latency\"\n # only: [anthropic] # 限制到特定 provider\n # ignore: [deepinfra] # 排除特定 providers\n plugins: # OpenRouter Pareto Code 路由器旋钮\n - id: pareto-router\n min_coding_score: 0.5 # 0.0–1.0;越高 = 更强的编码能力\n```\n\n形状与 OpenRouter 在聊天补全请求体中接受的内容一致。Hermes 原样转发整个 `extra_body`,因此 [openrouter.ai/docs](https://openrouter.ai/docs) 中记录的任何其他 OpenRouter 请求体字段都以相同方式工作。\n\n### 更改视觉模型\n\n使用 GPT-4o 而非 Gemini Flash 进行图像分析:\n\n```yaml\nauxiliary:\n vision:\n model: \"openai/gpt-4o\"\n```\n\n或通过环境变量(在 `~/.hermes/.env` 中):\n\n```bash\nAUXILIARY_VISION_MODEL=openai/gpt-4o\n```\n\n### Provider 选项\n\n这些选项适用于**辅助任务配置**(`auxiliary:`、`compression:`、`fallback_model:`),而非您的主 `model.provider` 设置。\n\n| Provider | 描述 | 要求 |\n|----------|-------------|-------------|\n| `\"auto\"` | 最佳可用(默认)。Vision 尝试 OpenRouter → Nous → Codex。 | — |\n| `\"openrouter\"` | 强制 OpenRouter —— 路由到任何模型(Gemini、GPT-4o、Claude 等) | `OPENROUTER_API_KEY` |\n| `\"nous\"` | 强制 Nous Portal | `hermes auth` |\n| `\"codex\"` | 强制 Codex OAuth(ChatGPT 账户)。支持视觉(gpt-5.3-codex)。 | `hermes model` → Codex |\n| `\"minimax-oauth\"` | 强制 MiniMax OAuth(浏览器登录,无需 API 密钥)。辅助任务使用 MiniMax-M2.7-highspeed。 | `hermes model` → MiniMax (OAuth) |\n| `\"xai-oauth\"` | 强制 xAI Grok OAuth(SuperGrok 或 X Premium+ 订阅者的浏览器登录,无需 API 密钥)。相同的 OAuth token 涵盖聊天、TTS、图像、视频和转录。 | `hermes model` → xAI Grok OAuth (SuperGrok / Premium+) |\n| `\"main\"` | 使用您的活跃自定义/主端点。可以来自 `OPENAI_BASE_URL` + `OPENAI_API_KEY` 或通过 `hermes model` / `config.yaml` 保存的自定义端点。适用于 OpenAI、本地模型或任何 OpenAI 兼容 API。**仅限辅助任务 —— 对 `model.provider` 无效。** | 自定义端点凭据 + 基础 URL |\n\n当您希望附带任务绕过默认路由器时,主 provider 目录中的直接 API 密钥 providers 也在这里工作。配置 `GMI_API_KEY` 后,`gmi` 有效:\n\n```yaml\nauxiliary:\n compression:\n provider: \"gmi\"\n model: \"anthropic/claude-opus-4.6\"\n```\n\n对于 GMI 辅助路由,使用 GMI 的 `/v1/models` 端点返回的确切模型 ID。\n\n### 常见设置\n\n**使用直接自定义端点**(比 `provider: \"main\"` 对本地/自托管 API 更清晰):\n```yaml\nauxiliary:\n vision:\n base_url: \"http://localhost:1234/v1\"\n api_key: \"local-key\"\n model: \"qwen2.5-vl\"\n```\n\n`base_url` 优先于 `provider`,因此这是将辅助任务路由到特定端点的最明确方式。对于直接端点覆盖,Hermes 使用配置的 `api_key` 或回退到 `OPENAI_API_KEY`;它不会为该自定义端点重用 `OPENROUTER_API_KEY`。\n\n**使用 OpenAI API 密钥进行视觉:**\n```yaml\n# 在 ~/.hermes/.env 中:\n# OPENAI_BASE_URL=https://api.openai.com/v1\n# OPENAI_API_KEY=sk-...\n\nauxiliary:\n vision:\n provider: \"main\"\n model: \"gpt-4o\" # 或 \"gpt-4o-mini\" 更便宜\n```\n\n**使用 OpenRouter 进行视觉**(路由到任何模型):\n```yaml\nauxiliary:\n vision:\n provider: \"openrouter\"\n model: \"openai/gpt-4o\" # 或 \"google/gemini-2.5-flash\" 等\n```\n\n**使用 Codex OAuth**(ChatGPT Pro/Plus 账户 —— 无需 API 密钥):\n```yaml\nauxiliary:\n vision:\n provider: \"codex\" # 使用您的 ChatGPT OAuth token\n # 模型默认为 gpt-5.3-codex(支持视觉)\n```\n\n**使用 MiniMax OAuth**(浏览器登录,无需 API 密钥):\n```yaml\nmodel:\n default: MiniMax-M2.7\n provider: minimax-oauth\n base_url: https://api.minimax.io/anthropic\n```\n运行 `hermes model` 并选择 **MiniMax (OAuth)** 自动登录并设置此项。对于中国区域,基础 URL 将是 `https://api.minimaxi.com/anthropic`。完整演练请参阅 [MiniMax OAuth 指南](../guides/minimax-oauth.md)。\n\n**使用本地/自托管模型:**\n```yaml\nauxiliary:\n vision:\n provider: \"main\" # 使用您的活跃自定义端点\n model: \"my-local-model\"\n```\n\n`provider: \"main\"` 使用 Hermes 用于普通聊天的任何 provider —— 无论是命名的自定义 provider(例如 `beans`)、内置 provider(如 `openrouter`)还是旧版 `OPENAI_BASE_URL` 端点。\n\n:::tip\n如果您使用 Codex OAuth 作为主模型 provider,视觉会自动工作 —— 无需额外配置。Codex 包含在视觉的自动检测链中。\n:::\n\n:::warning\n**视觉需要多模态模型。** 如果您设置 `provider: \"main\"`,请确保您的端点支持多模态/视觉 —— 否则图像分析将失败。\n:::\n\n### 环境变量(旧版)\n\n辅助模型也可以通过环境变量配置。但是,`config.yaml` 是首选方法 —— 它更易于管理,并支持所有选项,包括 `base_url` 和 `api_key`。\n\n| 设置 | 环境变量 |\n|---------|---------------------|\n| Vision provider | `AUXILIARY_VISION_PROVIDER` |\n| Vision 模型 | `AUXILIARY_VISION_MODEL` |\n| Vision 端点 | `AUXILIARY_VISION_BASE_URL` |\n| Vision API 密钥 | `AUXILIARY_VISION_API_KEY` |\n| Web 提取 provider | `AUXILIARY_WEB_EXTRACT_PROVIDER` |\n| Web 提取模型 | `AUXILIARY_WEB_EXTRACT_MODEL` |\n| Web 提取端点 | `AUXILIARY_WEB_EXTRACT_BASE_URL` |\n| Web 提取 API 密钥 | `AUXILIARY_WEB_EXTRACT_API_KEY` |\n\n压缩和回退模型设置仅限 config.yaml。\n\n:::tip\n运行 `hermes config` 查看您当前的辅助模型设置。覆盖仅在与默认值不同时显示。\n:::\n\n## 推理努力程度\n\n控制模型在响应前进行多少\"思考\":\n\n```yaml\nagent:\n reasoning_effort: \"\" # 空 = 中等(默认)。选项:none、minimal、low、medium、high、xhigh(最大)\n```\n\n未设置时(默认),推理努力程度默认为\"medium\" —— 适合大多数任务的平衡级别。设置值会覆盖它 —— 更高的推理努力程度在复杂任务上提供更好的结果,但代价是更多 token 和延迟。\n\n您也可以在运行时使用 `/reasoning` 命令更改推理努力程度:\n\n```\n/reasoning # 显示当前努力程度和显示状态\n/reasoning high # 将推理努力程度设为 high\n/reasoning none # 禁用推理\n/reasoning show # 在每次响应上方显示模型思考\n/reasoning hide # 隐藏模型思考\n```\n\n## 工具使用强制\n\n某些模型偶尔会将预期操作描述为文本而不是进行工具调用(\"我会运行测试...\"而不是实际调用终端)。工具使用强制会注入系统提示词指导,引导模型实际调用工具。\n\n```yaml\nagent:\n tool_use_enforcement: \"auto\" # \"auto\" | true | false | [\"model-substring\", ...]\n```\n\n| 值 | 行为 |\n|-------|----------|\n| `\"auto\"`(默认) | 对匹配以下模型启用:`gpt`、`codex`、`gemini`、`gemma`、`grok`。对所有其他模型禁用(Claude、DeepSeek、Qwen 等)。 |\n| `true` | 始终启用,无论模型如何。如果您注意到当前模型描述操作而不是执行操作,请使用此选项。 |\n| `false` | 始终禁用,无论模型如何。 |\n| `[\"gpt\", \"codex\", \"qwen\", \"llama\"]` | 仅当模型名称包含列出的子字符串之一时启用(不区分大小写)。 |\n\n### 注入的内容\n\n启用后,系统提示词中可能会添加三层指导:\n\n1. **通用工具使用强制**(所有匹配模型)—— 指示模型立即进行工具调用而不是描述意图,持续工作直到任务完成,永远不要以未来操作的承诺结束轮次。\n\n2. **OpenAI 执行纪律**(仅限 GPT 和 Codex 模型)—— 针对 GPT 特定失败模式的额外指导:在部分结果上放弃工作、跳过先决条件查找、幻觉而不是使用工具、在未验证的情况下宣布\"完成\"。\n\n3. **Google 操作指导**(仅限 Gemini 和 Gemma 模型)—— 简洁性、绝对路径、并行工具调用和编辑前验证模式。\n\n这些对用户透明,仅影响系统提示词。已经可靠使用工具的模型(如 Claude)不需要此指导,这就是为什么 `\"auto\"` 排除它们。\n\n### 何时开启\n\n如果您使用的模型不在默认自动列表中,并注意到它经常描述它*会*做什么而不是实际去做,请设置 `tool_use_enforcement: true` 或将模型子字符串添加到列表中:\n\n```yaml\nagent:\n tool_use_enforcement: [\"gpt\", \"codex\", \"gemini\", \"grok\", \"my-custom-model\"]\n```\n\n## TTS 配置\n\n```yaml\ntts:\n provider: \"edge\" # \"edge\" | \"elevenlabs\" | \"openai\" | \"minimax\" | \"mistral\" | \"gemini\" | \"xai\" | \"neutts\"\n speed: 1.0 # 全局速度倍数(所有 provider 的回退)\n edge:\n voice: \"en-US-AriaNeural\" # 322 种声音,74 种语言\n speed: 1.0 # 速度倍数(转换为速率百分比,例如 1.5 → +50%)\n elevenlabs:\n voice_id: \"pNInz6obpgDQGcFmaJgB\"\n model_id: \"eleven_multilingual_v2\"\n openai:\n model: \"gpt-4o-mini-tts\"\n voice: \"alloy\" # alloy、echo、fable、onyx、nova、shimmer\n speed: 1.0 # 速度倍数(API 限制为 0.25–4.0)\n base_url: \"https://api.openai.com/v1\" # 覆盖 OpenAI 兼容 TTS 端点\n minimax:\n speed: 1.0 # 语音速度倍数\n # base_url: \"\" # 可选:覆盖 OpenAI 兼容 TTS 端点\n mistral:\n model: \"voxtral-mini-tts-2603\"\n voice_id: \"c69964a6-ab8b-4f8a-9465-ec0925096ec8\" # Paul - Neutral(默认)\n gemini:\n model: \"gemini-2.5-flash-preview-tts\" # 或 gemini-2.5-pro-preview-tts\n voice: \"Kore\" # 30 种预置声音:Zephyr、Puck、Kore、Enceladus 等\n xai:\n voice_id: \"eve\" # xAI TTS 声音\n language: \"en\" # ISO 639-1\n sample_rate: 24000\n bit_rate: 128000 # MP3 比特率\n # base_url: \"https://api.x.ai/v1\"\n neutts:\n ref_audio: ''\n ref_text: ''\n model: neuphonic/neutts-air-q4-gguf\n device: cpu\n```\n\n这控制 `text_to_speech` 工具和语音模式中的口语回复(CLI 中的 `/voice tts` 或消息 gateway)。\n\n**速度回退层次:** provider 特定速度(例如 `tts.edge.speed`)→ 全局 `tts.speed` → `1.0` 默认值。设置全局 `tts.speed` 以在所有 provider 上应用统一速度,或按 provider 覆盖以进行精细控制。\n\n## 显示设置\n\n```yaml\ndisplay:\n tool_progress: all # off | new | all | verbose\n tool_progress_command: false # 在消息 gateway 中启用 /verbose 斜杠命令\n platforms: {} # 每平台显示覆盖(见下文)\n tool_progress_overrides: {} # 已弃用 —— 改用 display.platforms\n interim_assistant_messages: true # Gateway:将自然的轮次中 assistant 更新作为单独消息发送\n skin: default # 内置或自定义 CLI 皮肤(参阅 user-guide/features/skins)\n personality: \"kawaii\" # 旧版外观字段,仍在某些摘要中显示\n compact: false # 紧凑输出模式(减少空白)\n resume_display: full # full(恢复时显示之前的消息)| minimal(仅单行)\n bell_on_complete: false # 当 agent 完成时播放终端铃声(适合长任务)\n show_reasoning: false # 在每次响应上方显示模型推理/思考(用 /reasoning show|hide 切换)\n streaming: false # 将 token 实时流式传输到终端\n show_cost: false # 在 CLI 状态栏中显示估计 $ 成本\n timestamps: false # 为 true 时,在 CLI/TUI 记录中为用户和 assistant 标签添加 [HH:MM] 时间戳前缀\n tool_preview_length: 0 # 工具调用预览的最大字符数(0 = 无限制,显示完整路径/命令)\n runtime_footer: # Gateway:在最终回复中附加运行时上下文页脚\n enabled: false\n fields: [\"model\", \"context_pct\", \"cwd\"]\n file_mutation_verifier: true # 当本轮 write_file/patch 调用失败时附加建议性页脚\n language: en # 静态消息的 UI 语言(审批提示、部分 gateway 回复)。en | zh | zh-hant | ja | de | es | fr | tr | uk | af | ko | it | ga | pt | ru | hu\n```\n\n### 文件变更验证器\n\n当 `display.file_mutation_verifier` 为 `true`(默认)时,每当本轮中 `write_file` 或 `patch` 调用失败且从未被对同一路径的成功写入取代时,Hermes 会在 assistant 的最终响应中附加一行建议。这捕获了\"批量并行补丁,一半静默失败,模型总结成功\"这类过度声明,而无需您在每次编辑后手动运行 `git status`。\n\n示例页脚:\n\n```\n⚠️ File-mutation verifier: 3 file(s) were NOT modified this turn despite any wording above that may suggest otherwise. Run `git status` or `read_file` to confirm.\n • concepts/automatic-organization.md — [patch] Could not find match for old_string\n • concepts/lora.md — [patch] Could not find match for old_string\n • concepts/rag-pipeline.md — [patch] Could not find match for old_string\n```\n\n设置 `file_mutation_verifier: false`(或 `HERMES_FILE_MUTATION_VERIFIER=0`)以禁止页脚。验证器仅在轮次结束时有真实失败未解决时触发 —— 在同一轮次内重试失败补丁并成功的模型不会为该文件触发它。\n\n### 静态消息的 UI 语言\n\n`display.language` 设置翻译一小组静态面向用户的消息 —— CLI 审批提示、少数 gateway 斜杠命令回复(例如重启排空通知、\"审批已过期\"、\"目标已清除\")。它**不**翻译 agent 响应、日志行、工具输出、错误回溯或斜杠命令描述 —— 这些保持英文。如果您希望 agent 本身用另一种语言回复,只需在您的提示词或系统消息中告诉它。\n\n支持的值:`en`(默认)、`zh`(简体中文)、`zh-hant`(繁体中文)、`ja`(日语)、`de`(德语)、`es`(西班牙语)、`fr`(法语)、`tr`(土耳其语)、`uk`(乌克兰语)、`af`(南非荷兰语)、`ko`(韩语)、`it`(意大利语)、`ga`(爱尔兰语)、`pt`(葡萄牙语)、`ru`(俄语)、`hu`(匈牙利语)。未知值回退到英文。\n\n您也可以使用 `HERMES_LANGUAGE` 环境变量按会话设置,它会覆盖配置值。\n\n```yaml\ndisplay:\n language: zh # CLI 审批提示以中文显示\n```\n\n| 模式 | 您看到的内容 |\n|------|-------------|\n| `off` | 静默 —— 仅最终响应 |\n| `new` | 仅在工具更改时显示工具指示器 |\n| `all` | 每次工具调用附带简短预览(默认) |\n| `verbose` | 完整参数、结果和调试日志 |\n\n在 CLI 中,使用 `/verbose` 循环切换这些模式。要在消息平台(Telegram、Discord、Slack 等)中使用 `/verbose`,请在上方的 `display` 部分设置 `tool_progress_command: true`。该命令将循环切换模式并保存到配置。\n\n### 运行时元数据页脚(仅限 gateway)\n\n当 `display.runtime_footer.enabled: true` 时,Hermes 在每个 gateway 轮次的**最终**消息中附加一个小型运行时上下文页脚。目前页脚可显示模型、上下文窗口百分比和当前工作目录。默认关闭;如果您的团队希望每个回复都包含这些来源信息,请按 gateway 选择加入。\n\n```yaml\ndisplay:\n runtime_footer:\n enabled: true\n fields: [\"model\", \"context_pct\", \"cwd\"] # 支持字段:model、context_pct、cwd\n```\n\n`/footer` 斜杠命令在任何会话中运行时切换此功能。\n\n附加到 Telegram/Discord/Slack 回复的示例页脚:\n\n```\n— claude-opus-4.7 · 12 tool calls · 2m 14s · $0.042\n```\n\n只有轮次的**最终**消息获得页脚;中间更新保持干净。\n\n### 每平台进度覆盖\n\n不同平台有不同的详细程度需求。例如,Signal 无法编辑消息,因此每次进度更新都会成为单独的消息 —— 很嘈杂。使用 `display.platforms` 设置每平台模式:\n\n```yaml\ndisplay:\n tool_progress: all # 全局默认\n platforms:\n signal:\n tool_progress: 'off' # 在 Signal 上静默进度\n telegram:\n tool_progress: verbose # 在 Telegram 上详细进度\n slack:\n tool_progress: 'off' # 在共享 Slack 工作区中保持安静\n```\n\n没有覆盖的平台回退到全局 `tool_progress` 值。有效平台键:`telegram`、`discord`、`slack`、`signal`、`whatsapp`、`matrix`、`mattermost`、`email`、`sms`、`homeassistant`、`dingtalk`、`feishu`、`wecom`、`weixin`、`bluebubbles`、`qqbot`。旧版 `display.tool_progress_overrides` 键仍可加载以向后兼容,但已弃用,并在首次加载时迁移到 `display.platforms`。\n\n`interim_assistant_messages` 仅限 gateway。启用后,Hermes 将已完成的轮次中 assistant 更新作为单独的聊天消息发送。这与 `tool_progress` 无关,不需要 gateway 流式传输。\n\n## 隐私\n\n```yaml\nprivacy:\n redact_pii: false # 从 LLM 上下文中删除 PII(仅限 gateway)\n```\n\n当 `redact_pii` 为 `true` 时,gateway 在将系统提示词发送到受支持平台上的 LLM 之前,会从中删除个人身份信息:\n\n| 字段 | 处理方式 |\n|-------|-----------|\n| 电话号码(WhatsApp/Signal 上的用户 ID) | 哈希为 `user_<12-char-sha256>` |\n| 用户 ID | 哈希为 `user_<12-char-sha256>` |\n| 聊天 ID | 数字部分哈希,保留平台前缀(`telegram:<hash>`) |\n| 主频道 ID | 数字部分哈希 |\n| 用户名/昵称 | **不受影响**(用户选择的,公开可见) |\n\n**平台支持:** 删除适用于 WhatsApp、Signal 和 Telegram。Discord 和 Slack 被排除,因为它们的提及系统(`<@user_id>`)需要 LLM 上下文中的真实 ID。\n\n哈希是确定性的 —— 同一用户始终映射到同一哈希,因此模型仍然可以在群聊中区分用户。路由和传递在内部使用原始值。\n\n## 语音转文字(STT)\n\n```yaml\nstt:\n provider: \"local\" # \"local\" | \"groq\" | \"openai\" | \"mistral\"\n local:\n model: \"base\" # tiny、base、small、medium、large-v3\n openai:\n model: \"whisper-1\" # whisper-1 | gpt-4o-mini-transcribe | gpt-4o-transcribe\n # model: \"whisper-1\" # 旧版回退键仍受支持\n```\n\nProvider 行为:\n\n- `local` 使用在您机器上运行的 `faster-whisper`。使用 `pip install faster-whisper` 单独安装。\n- `groq` 使用 Groq 的 Whisper 兼容端点,读取 `GROQ_API_KEY`。\n- `openai` 使用 OpenAI 语音 API,读取 `VOICE_TOOLS_OPENAI_KEY`。\n\n如果请求的 provider 不可用,Hermes 按此顺序自动回退:`local` → `groq` → `openai`。\n\nGroq 和 OpenAI 模型覆盖由环境变量驱动:\n\n```bash\nSTT_GROQ_MODEL=whisper-large-v3-turbo\nSTT_OPENAI_MODEL=whisper-1\nGROQ_BASE_URL=https://api.groq.com/openai/v1\nSTT_OPENAI_BASE_URL=https://api.openai.com/v1\n```\n\n## 语音模式(CLI)\n\n```yaml\nvoice:\n record_key: \"ctrl+b\" # CLI 内的按键通话键\n max_recording_seconds: 120 # 长录音的硬停止\n auto_tts: false # /voice on 时自动启用口语回复\n beep_enabled: true # 在 CLI 语音模式中播放录音开始/停止提示音\n silence_threshold: 200 # 语音检测的 RMS 阈值\n silence_duration: 3.0 # 自动停止前的静默秒数\n```\n\n在 CLI 中使用 `/voice on` 启用麦克风模式,使用 `record_key` 开始/停止录音,使用 `/voice tts` 切换口语回复。端到端设置和平台特定行为请参阅[语音模式](/user-guide/features/voice-mode)。\n\n## 流式传输\n\n将 token 实时流式传输到终端或消息平台,而不是等待完整响应。\n\n### CLI 流式传输\n\n```yaml\ndisplay:\n streaming: true # 实时将 token 流式传输到终端\n show_reasoning: true # 同时流式传输推理/思考 token(可选)\n```\n\n启用后,响应在流式传输框内逐 token 出现。工具调用仍然静默捕获。如果 provider 不支持流式传输,它会自动回退到正常显示。\n\n### Gateway 流式传输(Telegram、Discord、Slack)\n\n```yaml\nstreaming:\n enabled: true # 启用渐进式消息编辑\n transport: edit # \"edit\"(渐进式消息编辑)或 \"off\"\n edit_interval: 0.3 # 消息编辑之间的秒数\n buffer_threshold: 40 # 强制编辑刷新前的字符数\n cursor: \" ▉\" # 流式传输期间显示的光标\n fresh_final_after_seconds: 0 # 预览超过此时间时选择发送新的最终消息(Telegram)\n```\n\n启用后,bot 在第一个 token 时发送消息,然后随着更多 token 到来渐进式编辑它。不支持消息编辑的平台(Signal、Email、Home Assistant)在第一次尝试时自动检测 —— 该会话的流式传输被优雅地禁用,不会产生大量消息。\n\n对于不带渐进式 token 编辑的独立自然轮次中 assistant 更新,请设置 `display.interim_assistant_messages: true`。\n\n**溢出处理:** 如果流式传输的文本超过平台的消息长度限制(约 4096 字符),当前消息被最终化,新消息自动开始。\n\n**新的最终消息(Telegram):** Telegram 的 `editMessageText` 保留原始消息时间戳,因此长时间运行的流式回复即使在完成后也会保留第一个 token 的时间戳。设置 `fresh_final_after_seconds > 0` 可选择将旧预览作为全新的最终消息传递,并尽力删除旧预览。默认值为 `0`,始终就地最终化流式回复,避免某些客户端短暂显示重复消息再删除其中一条。\n\n:::note\n主开关 `streaming.enabled` 默认为 `false`——在你启用之前不会有任何流式传输。启用后,是否流式传输按**平台**决定:Telegram 默认带有 `display.platforms.telegram.streaming: true`(流式传输),Discord 为 `display.platforms.discord.streaming: false`(不流式传输)。因此启用流式传输后,Telegram 开箱即用地流式传输,Discord 在你修改其开关之前仍使用整条消息回复。你可以在仪表盘的 **Channels** 开关中或直接在 `~/.hermes/config.yaml` 中调整这些按平台的开关。\n:::\n\n## 群聊会话隔离\n\n控制共享聊天是每个房间保持一个对话还是每个参与者一个对话:\n\n```yaml\ngroup_sessions_per_user: true # true = 群组/频道中每用户隔离,false = 每个聊天一个共享会话\n```\n\n- `true` 是默认和推荐设置。在 Discord 频道、Telegram 群组、Slack 频道和类似共享上下文中,当平台提供用户 ID 时,每个发送者获得自己的会话。\n- `false` 恢复到旧的共享房间行为。如果您明确希望 Hermes 将频道视为一个协作对话,这可能有用,但这也意味着用户共享上下文、token 成本和中断状态。\n- 私信不受影响。Hermes 仍然像往常一样通过聊天/DM ID 键入 DM。\n- 线程与其父频道保持隔离;使用 `true` 时,每个参与者在线程内也获得自己的会话。\n\n有关行为详情和示例,请参阅[会话](/user-guide/sessions)和 [Discord 指南](/user-guide/messaging/discord)。\n\n## 未授权 DM 行为\n\n控制当未知用户发送私信时 Hermes 的行为:\n\n```yaml\nunauthorized_dm_behavior: pair\n\nwhatsapp:\n unauthorized_dm_behavior: ignore\n```\n\n- `pair` 是默认值。Hermes 拒绝访问,但在 DM 中回复一次性配对码。\n- `ignore` 静默丢弃未授权的 DM。\n- 平台部分覆盖全局默认值,因此您可以在广泛范围内保持配对启用,同时使一个平台更安静。\n\n## 快速命令\n\n定义自定义命令,这些命令要么在不调用 LLM 的情况下运行 shell 命令,要么将一个斜杠命令别名为另一个。Exec 快速命令是零 token 的,对于从消息平台(Telegram、Discord 等)进行快速服务器检查或实用脚本很有用。\n\n```yaml\nquick_commands:\n status:\n type: exec\n command: systemctl status hermes-agent\n disk:\n type: exec\n command: df -h /\n update:\n type: exec\n command: cd ~/.hermes/hermes-agent && git pull && pip install -e .\n gpu:\n type: exec\n command: nvidia-smi --query-gpu=name,utilization.gpu,memory.used,memory.total --format=csv,noheader\n restart:\n type: alias\n target: /gateway restart\n```\n\n用法:在 CLI 或任何消息平台中输入 `/status`、`/disk`、`/update`、`/gpu` 或 `/restart`。`exec` 命令在宿主本地运行并直接返回输出 —— 无 LLM 调用,不消耗 token。`alias` 命令重写为配置的斜杠命令目标。\n\n- **30 秒超时** —— 长时间运行的命令被终止并显示错误消息\n- **优先级** —— 快速命令在技能命令之前检查,因此您可以覆盖技能名称\n- **自动补全** —— 快速命令在调度时解析,不显示在内置斜杠命令自动补全表中\n- **类型** —— 支持的类型为 `exec` 和 `alias`;其他类型显示错误\n- **到处可用** —— CLI、Telegram、Discord、Slack、WhatsApp、Signal、Email、Home Assistant\n\n仅字符串的 prompt 快捷方式不是有效的快速命令。对于可重用的 prompt 工作流,请创建技能或别名到现有斜杠命令。\n\n## 人类延迟\n\n在消息平台中模拟类人响应节奏:\n\n```yaml\nhuman_delay:\n mode: \"off\" # off | natural | custom\n min_ms: 800 # 最小延迟(自定义模式)\n max_ms: 2500 # 最大延迟(自定义模式)\n```\n\n## 代码执行\n\n配置 `execute_code` 工具:\n\n```yaml\ncode_execution:\n mode: project # project(默认)| strict\n timeout: 300 # 最大执行时间(秒)\n max_tool_calls: 50 # 代码执行中的最大工具调用次数\n```\n\n**`mode`** 控制脚本的工作目录和 Python 解释器:\n\n- **`project`**(默认)—— 脚本在会话的工作目录中以活跃 virtualenv/conda 环境的 python 运行。项目依赖(`pandas`、`torch`、项目包)和相对路径(`.env`、`./data.csv`)自然解析,与 `terminal()` 看到的一致。\n- **`strict`** —— 脚本在临时暂存目录中以 `sys.executable`(Hermes 自己的 python)运行。最大可重现性,但项目依赖和相对路径不会解析。\n\n环境清理(删除 `*_API_KEY`、`*_TOKEN`、`*_SECRET`、`*_PASSWORD`、`*_CREDENTIAL`、`*_PASSWD`、`*_AUTH`)和工具白名单在两种模式下完全相同 —— 切换模式不会改变安全态势。\n\n## Web 搜索后端\n\n`web_search` 和 `web_extract` 工具支持五种后端 provider。在 `config.yaml` 中或通过 `hermes tools` 配置后端:\n\n```yaml\nweb:\n backend: firecrawl # firecrawl | searxng | parallel | tavily | exa\n\n # 或使用每功能键混合 provider(例如免费搜索 + 付费提取):\n search_backend: \"searxng\"\n extract_backend: \"firecrawl\"\n```\n\n| 后端 | 环境变量 | 搜索 | 提取 |\n|---------|---------|--------|---------|\n| **Firecrawl**(默认) | `FIRECRAWL_API_KEY` | ✔ | ✔ |\n| **SearXNG** | `SEARXNG_URL` | ✔ | — |\n| **Parallel** | `PARALLEL_API_KEY` | ✔ | ✔ |\n| **Tavily** | `TAVILY_API_KEY` | ✔ | ✔ |\n| **Exa** | `EXA_API_KEY` | ✔ | ✔ |\n\n**后端选择:** 如果未设置 `web.backend`,后端从可用的 API 密钥自动检测。如果仅设置了 `SEARXNG_URL`,使用 SearXNG。如果仅设置了 `EXA_API_KEY`,使用 Exa。如果仅设置了 `TAVILY_API_KEY`,使用 Tavily。如果仅设置了 `PARALLEL_API_KEY`,使用 Parallel。否则 Firecrawl 是默认值。\n\n**SearXNG** 是一个免费、自托管、尊重隐私的元搜索引擎,查询 70+ 个搜索引擎。无需 API 密钥 —— 只需将 `SEARXNG_URL` 设置为您的实例(例如 `http://localhost:8080`)。SearXNG 仅限搜索;`web_extract` 需要单独的提取 provider(设置 `web.extract_backend`)。Docker 设置说明请参阅 [Web 搜索设置指南](/user-guide/features/web-search)。\n\n**自托管 Firecrawl:** 设置 `FIRECRAWL_API_URL` 指向您自己的实例。设置自定义 URL 后,API 密钥变为可选(在服务器上设置 `USE_DB_AUTHENTICATION=***` 以禁用认证)。\n\n**Parallel 搜索模式:** 设置 `PARALLEL_SEARCH_MODE` 控制搜索行为 —— `fast`、`one-shot` 或 `agentic`(默认:`agentic`)。\n\n**Exa:** 在 `~/.hermes/.env` 中设置 `EXA_API_KEY`。支持 `category` 过滤(`company`、`research paper`、`news`、`people`、`personal site`、`pdf`)和域名/日期过滤器。\n\n## 浏览器\n\n配置浏览器自动化行为:\n\n```yaml\nbrowser:\n inactivity_timeout: 120 # 自动关闭空闲会话前的秒数\n command_timeout: 30 # 浏览器命令超时(截图、导航等)(秒)\n record_sessions: false # 自动将浏览器会话录制为 WebM 视频到 ~/.hermes/browser_recordings/\n # 可选 CDP 覆盖 —— 设置后,Hermes 直接附加到您自己的\n # Chromium 系浏览器(通过 /browser connect),而不是启动无头浏览器。\n cdp_url: \"\"\n # 对话框监督器 —— 控制当 CDP 后端附加时(Browserbase、本地 Chromium 系\n # 浏览器通过 /browser connect)如何处理原生 JS 对话框(alert/confirm/prompt)。\n # 在 Camofox 和默认本地 agent 浏览器模式下忽略。\n dialog_policy: must_respond # must_respond | auto_dismiss | auto_accept\n dialog_timeout_s: 300 # must_respond 下的安全自动关闭(秒)\n camofox:\n managed_persistence: false # 为 true 时,Camofox 会话跨重启持久化 cookie/登录\n user_id: \"\" # 可选的外部管理 Camofox userId\n session_key: \"\" # Hermes 创建标签页时发送的可选会话密钥\n adopt_existing_tab: false # 在创建新标签页之前重用此身份的现有标签页\n```\n\n**对话框策略:**\n\n- `must_respond`(默认)—— 捕获对话框,在 `browser_snapshot.pending_dialogs` 中显示,等待 agent 调用 `browser_dialog(action=...)`。在 `dialog_timeout_s` 秒内无响应后,对话框被自动关闭以防止页面的 JS 线程永久停滞。\n- `auto_dismiss` —— 捕获,立即关闭。Agent 仍然在事后的 `browser_snapshot.recent_dialogs` 中看到对话框记录,`closed_by=\"auto_policy\"`。\n- `auto_accept` —— 捕获,立即接受。适用于有激进 `beforeunload` 提示的页面。\n\n完整对话框工作流请参阅[浏览器功能页面](./features/browser.md#browser_dialog)。\n\n浏览器工具集支持多个 provider。有关 Browserbase、Browser Use 和本地 Chromium 系 CDP 设置的详细信息,请参阅[浏览器功能页面](/user-guide/features/browser)。\n\n## 时区\n\n使用 IANA 时区字符串覆盖服务器本地时区。影响日志中的时间戳、cron 调度和系统提示词时间注入。\n\n```yaml\ntimezone: \"America/New_York\" # IANA 时区(默认:\"\" = 服务器本地时间)\n```\n\n支持的值:任何 IANA 时区标识符(例如 `America/New_York`、`Europe/London`、`Asia/Kolkata`、`UTC`)。留空或省略以使用服务器本地时间。\n\n## Discord\n\n为消息 gateway 配置 Discord 特定行为:\n\n```yaml\ndiscord:\n require_mention: true # 在服务器频道中需要 @提及才能响应\n free_response_channels: \"\" # 逗号分隔的频道 ID,bot 在这些频道无需 @提及即可响应\n auto_thread: true # 在频道中 @提及时自动创建线程\n```\n\n- `require_mention` —— 为 `true`(默认)时,bot 仅在服务器频道中被 `@BotName` 提及时响应。DM 始终无需提及即可工作。\n- `free_response_channels` —— 逗号分隔的频道 ID 列表,bot 在这些频道对每条消息响应,无需提及。\n- `auto_thread` —— 为 `true`(默认)时,频道中的提及会自动为对话创建线程,保持频道整洁(类似 Slack 线程)。\n\n## 安全\n\n预执行安全扫描和机密脱敏:\n\n```yaml\nsecurity:\n redact_secrets: false # 在工具输出和日志中脱敏 API 密钥模式(默认关闭)\n tirith_enabled: true # 为终端命令启用 Tirith 安全扫描\n tirith_path: \"tirith\" # tirith 二进制文件路径(默认:$PATH 中的 \"tirith\")\n tirith_timeout: 5 # 等待 tirith 扫描的秒数\n tirith_fail_open: true # 如果 tirith 不可用,允许命令执行\n website_blocklist: # 参见下方网站黑名单部分\n enabled: false\n domains: []\n shared_files: []\n```\n\n- `redact_secrets` —— 为 `true` 时,自动检测并脱敏工具输出中看起来像 API 密钥、token 和密码的模式,然后再进入对话上下文和日志。**默认关闭** —— 如果您经常在工具输出中处理真实凭据并希望有安全网,请启用。显式设置为 `true` 以开启。\n- `tirith_enabled` —— 为 `true` 时,终端命令在执行前由 [Tirith](https://github.com/sheeki03/tirith) 扫描以检测潜在危险操作。\n- `tirith_path` —— tirith 二进制文件的路径。如果 tirith 安装在非标准位置,请设置此项。\n- `tirith_timeout` —— 等待 tirith 扫描的最大秒数。如果扫描超时,命令继续执行。\n- `tirith_fail_open` —— 为 `true`(默认)时,如果 tirith 不可用或失败,允许命令执行。设置为 `false` 以在 tirith 无法验证时阻止命令。\n\n## 网站黑名单\n\n阻止 agent 的 web 和浏览器工具访问特定域名:\n\n```yaml\nsecurity:\n website_blocklist:\n enabled: false # 启用 URL 阻止(默认:false)\n domains: # 被阻止的域名模式列表\n - \"*.internal.company.com\"\n - \"admin.example.com\"\n - \"*.local\"\n shared_files: # 从外部文件加载额外规则\n - \"/etc/hermes/blocked-sites.txt\"\n```\n\n启用后,任何匹配被阻止域名模式的 URL 在 web 或浏览器工具执行之前都会被拒绝。这适用于 `web_search`、`web_extract`、`browser_navigate` 以及任何访问 URL 的工具。\n\n域名规则支持:\n- 精确域名:`admin.example.com`\n- 通配符子域名:`*.internal.company.com`(阻止所有子域名)\n- TLD 通配符:`*.local`\n\n共享文件每行包含一条域名规则(空行和 `#` 注释被忽略)。缺失或不可读的文件记录警告,但不禁用其他 web 工具。\n\n策略缓存 30 秒,因此配置更改无需重启即可快速生效。\n\n## 智能审批\n\n控制 Hermes 如何处理潜在危险命令:\n\n```yaml\napprovals:\n mode: smart # smart | manual | off\n```\n\n| 模式 | 行为 |\n|------|----------|\n| `smart`(默认) | 使用辅助 LLM 评估被标记的命令是否真正危险。低风险命令仅对当前命令自动批准,真正危险的命令自动拒绝,不确定的情况升级给用户。 |\n| `manual` | 在执行任何被标记的命令之前提示用户。在 CLI 中显示交互式审批对话框。在消息中排队待处理的审批请求。 |\n| `off` | 跳过所有审批检查。等同于 `HERMES_YOLO_MODE=true`。**谨慎使用。** |\n\n智能模式对于减少审批疲劳特别有用 —— 它让 agent 在安全操作上更自主地工作,同时仍然捕获真正破坏性的命令。\n\n:::warning\n设置 `approvals.mode: off` 会禁用终端命令的所有安全检查。仅在受信任的沙箱环境中使用。\n:::\n\n## 检查点\n\n破坏性文件操作之前的自动文件系统快照。详情请参阅[检查点与回滚](/user-guide/checkpoints-and-rollback)。\n\n```yaml\ncheckpoints:\n enabled: false # 启用自动检查点(也可:hermes chat --checkpoints)。默认:false(选择加入)。\n max_snapshots: 20 # 每个目录保留的最大检查点数(默认:20)\n```\n\n\n## 委托\n\n为委托工具配置子 agent 行为:\n\n```yaml\ndelegation:\n # model: \"google/gemini-3-flash-preview\" # 覆盖模型(空 = 继承父级)\n # provider: \"openrouter\" # 覆盖 provider(空 = 继承父级)\n # base_url: \"http://localhost:1234/v1\" # 直接 OpenAI 兼容端点(优先于 provider)\n # api_key: \"local-key\" # base_url 的 API 密钥(回退到 OPENAI_API_KEY)\n # api_mode: \"\" # base_url 的线路协议:\"chat_completions\"、\"codex_responses\" 或 \"anthropic_messages\"。空 = 从 URL 自动检测(例如 /anthropic 后缀 → anthropic_messages)。对启发式无法检测的非标准端点显式设置。\n max_concurrent_children: 3 # 每批并行子 agent 数(下限 1,无上限)。也可通过 DELEGATION_MAX_CONCURRENT_CHILDREN 环境变量设置。\n max_spawn_depth: 1 # 委托树深度上限(1-3,截断)。1 = 扁平(默认):父级生成无法委托的叶子。2 = 编排器子级可以生成叶子孙级。3 = 三级。\n orchestrator_enabled: true # 全局终止开关。为 false 时,role=\"orchestrator\" 被忽略,每个子级无论 max_spawn_depth 如何都被强制为叶子。\n```\n\n**子 agent provider:model 覆盖:** 默认情况下,子 agent 继承父 agent 的 provider 和模型。设置 `delegation.provider` 和 `delegation.model` 将子 agent 路由到不同的 provider:model 对 —— 例如,在您的主 agent 运行昂贵推理模型时,为范围较窄的子任务使用便宜/快速的模型。\n\n**直接端点覆盖:** 如果您想要明显的自定义端点路径,请设置 `delegation.base_url`、`delegation.api_key` 和 `delegation.model`。这将子 agent 直接发送到该 OpenAI 兼容端点,并优先于 `delegation.provider`。如果省略 `delegation.api_key`,Hermes 仅回退到 `OPENAI_API_KEY`。\n\n**线路协议(`api_mode`):** Hermes 从 `delegation.base_url` 自动检测线路协议(例如以 `/anthropic` 结尾的路径 → `anthropic_messages`;Codex/原生 Anthropic/Kimi-coding 主机名保留其现有检测)。对于启发式无法分类的端点 —— 例如 Azure AI Foundry、MiniMax、Zhipu GLM 或前置 Anthropic 形状后端的 LiteLLM 代理 —— 请将 `delegation.api_mode` 显式设置为 `chat_completions`、`codex_responses` 或 `anthropic_messages` 之一。留空(默认)以保持自动检测。\n\n委托 provider 使用与 CLI/gateway 启动相同的凭据解析。所有配置的 provider 均受支持:`openrouter`、`nous`、`copilot`、`zai`、`kimi-coding`、`minimax`、`minimax-cn`。设置 provider 时,系统自动解析正确的基础 URL、API 密钥和 API 模式 —— 无需手动凭据连接。\n\n**优先级:** 配置中的 `delegation.base_url` → 配置中的 `delegation.provider` → 父 provider(继承)。配置中的 `delegation.model` → 父模型(继承)。仅设置 `model` 而不设置 `provider` 仅更改模型名称,同时保留父级凭据(适用于在同一 provider(如 OpenRouter)内切换模型)。\n\n**宽度和深度:** `max_concurrent_children` 限制每批并行运行的子 agent 数量(默认 `3`,下限 1,无上限)。也可通过 `DELEGATION_MAX_CONCURRENT_CHILDREN` 环境变量设置。当模型提交的 `tasks` 数组超过上限时,`delegate_task` 返回工具错误解释限制,而不是静默截断。`max_spawn_depth` 控制委托树深度(截断到 1-3)。在默认 `1` 时,委托是扁平的:子级无法生成孙级,传递 `role=\"orchestrator\"` 静默降级为 `leaf`。提升到 `2` 使编排器子级可以生成叶子孙级;`3` 用于三级树。Agent 通过 `role=\"orchestrator\"` 按调用选择编排;`orchestrator_enabled: false` 强制每个子级回到叶子,无论如何。成本呈乘法增长 —— 在 `max_spawn_depth: 3` 和 `max_concurrent_children: 3` 时,树可以达到 3×3×3 = 27 个并发叶子 agent。使用模式请参阅[子 Agent 委托 → 深度限制和嵌套编排](features/delegation.md#depth-limit-and-nested-orchestration)。\n\n## 澄清\n\n配置澄清提示行为:\n\n```yaml\nclarify:\n timeout: 120 # 等待用户澄清响应的秒数\n```\n\n## 上下文文件(SOUL.md、AGENTS.md)\n\nHermes 使用两种不同的上下文范围:\n\n| 文件 | 用途 | 范围 |\n|------|---------|-------|\n| `SOUL.md` | **主要 agent 身份** —— 定义 agent 是谁(系统提示词第 #1 槽位) | `~/.hermes/SOUL.md` 或 `$HERMES_HOME/SOUL.md` |\n| `.hermes.md` / `HERMES.md` | 项目特定指令(最高优先级) | 向上走到 git 根目录 |\n| `AGENTS.md` | 项目特定指令、编码规范 | 递归目录遍历 |\n| `CLAUDE.md` | Claude Code 上下文文件(也会检测) | 仅工作目录 |\n| `.cursorrules` | Cursor IDE 规则(也会检测) | 仅工作目录 |\n| `.cursor/rules/*.mdc` | Cursor 规则文件(也会检测) | 仅工作目录 |\n\n- **SOUL.md** 是 agent 的主要身份。它占据系统提示词的第 #1 槽位,完全替换内置的默认身份。编辑它以完全自定义 agent 是谁。\n- 如果 SOUL.md 缺失、为空或无法加载,Hermes 回退到内置默认身份。\n- **项目上下文文件使用优先级系统** —— 仅加载一种类型(第一个匹配优先):`.hermes.md` → `AGENTS.md` → `CLAUDE.md` → `.cursorrules`。SOUL.md 始终独立加载。\n- **AGENTS.md** 是分层的:如果子目录也有 AGENTS.md,所有都会合并。\n- 如果 `SOUL.md` 不存在,Hermes 会自动生成默认的 `SOUL.md`。\n- 所有加载的上下文文件上限为 20,000 字符,并进行智能截断。\n\n另请参阅:\n- [个性与 SOUL.md](/user-guide/features/personality)\n- [上下文文件](/user-guide/features/context-files)\n\n## 工作目录\n\n| 上下文 | 默认值 |\n|---------|---------|\n| **CLI(`hermes`)** | 运行命令的当前目录 |\n| **消息 gateway** | 主目录 `~`(用 `MESSAGING_CWD` 覆盖) |\n| **Docker / Singularity / Modal / SSH** | 容器或远程机器内用户的主目录 |\n\n覆盖工作目录:\n```bash\n# 在 ~/.hermes/.env 或 ~/.hermes/config.yaml 中:\nMESSAGING_CWD=/home/myuser/projects # Gateway 会话\nTERMINAL_CWD=/workspace # 所有终端会话\n```\n"}, "0800af0b8ae01fd808e54be53d2cf12eca1d0638": {"t": 1785767987.8995104, "n": 56653, "h": "ed06d32313c0afaa", "c": "---\nsidebar_position: 2\ntitle: \"配置\"\ndescription: \"配置 Hermes Agent — config.yaml、providers、模型、API 密钥等\"\n---\n\n# 配置\n\n所有设置均存储在 `~/.hermes/` 目录中,便于访问。\n\n## 目录结构\n\n```text\n~/.hermes/\n├── config.yaml # 设置(模型、终端、TTS、压缩等)\n├── .env # API 密钥和机密\n├── auth.json # OAuth provider 凭据(Nous Portal 等)\n├── SOUL.md # 主要 agent 身份(系统提示词第 #1 槽位)\n├── memories/ # 持久记忆(MEMORY.md、USER.md)\n├── skills/ # Agent 创建的技能(通过 skill_manage 工具管理)\n├── cron/ # 定时任务\n├── sessions/ # Gateway 会话\n└── logs/ # 日志(errors.log、gateway.log — 机密自动脱敏)\n```\n\n## 管理配置\n\n```bash\nhermes config # 查看当前配置\nhermes config edit # 在编辑器中打开 config.yaml\nhermes config set KEY VAL # 设置特定值\nhermes config check # 检查缺失选项(更新后使用)\nhermes config migrate # 交互式添加缺失选项\n\n# 示例:\nhermes config set model anthropic/claude-opus-4\nhermes config set terminal.backend docker\nhermes config set OPENROUTER_API_KEY sk-or-... # 保存到 .env\n```\n\n:::tip\n`hermes config set` 命令会自动将值路由到正确的文件 —— API 密钥保存到 `.env`,其他所有内容保存到 `config.yaml`。\n:::\n\n## 配置优先级\n\n设置按以下顺序解析(优先级从高到低):\n\n1. **CLI 参数** —— 例如 `hermes chat --model anthropic/claude-sonnet-4`(单次调用覆盖)\n2. **`~/.hermes/config.yaml`** —— 所有非机密设置的主配置文件\n3. **`~/.hermes/.env`** —— 环境变量的回退;机密(API 密钥、token、密码)**必须**放这里\n4. **内置默认值** —— 未设置任何内容时的硬编码安全默认值\n\n:::info 经验法则\n机密(API 密钥、bot token、密码)放入 `.env`。其他所有内容(模型、终端后端、压缩设置、内存限制、工具集)放入 `config.yaml`。当两者都设置时,`config.yaml` 对非机密设置优先。\n:::\n\n## 环境变量替换\n\n可以在 `config.yaml` 中使用 `${VAR_NAME}` 语法引用环境变量:\n\n```yaml\nauxiliary:\n vision:\n api_key: ${GOOGLE_API_KEY}\n base_url: ${CUSTOM_VISION_URL}\n\ndelegation:\n api_key: ${DELEGATION_KEY}\n```\n\n单个值中可以有多个引用:`url: \"${HOST}:${PORT}\"`。如果引用的变量未设置,占位符将保持原样(`${UNDEFINED_VAR}` 保持不变)。仅支持 `${VAR}` 语法 —— 裸 `$VAR` 不会被展开。\n\n关于 AI provider 设置(OpenRouter、Anthropic、Copilot、自定义端点、自托管 LLM、回退模型等),请参阅 [AI Providers](/integrations/providers)。\n\n### Provider 超时\n\n可以为 provider 设置 `providers.<id>.request_timeout_seconds` 作为全局请求超时,以及 `providers.<id>.models.<model>.timeout_seconds` 作为特定模型的覆盖值。适用于每种传输方式(OpenAI-wire、原生 Anthropic、Anthropic 兼容)上的主轮次客户端、回退链、凭据轮换后的重建,以及(对于 OpenAI-wire)每请求超时 kwarg —— 因此配置值优先于旧版 `HERMES_API_TIMEOUT` 环境变量。\n\n还可以设置 `providers.<id>.stale_timeout_seconds` 用于非流式陈旧调用检测器,以及 `providers.<id>.models.<model>.stale_timeout_seconds` 作为特定模型的覆盖值。此值优先于旧版 `HERMES_API_CALL_STALE_TIMEOUT` 环境变量。\n\n不设置这些值将保持旧版默认值(`HERMES_API_TIMEOUT=1800`s、`HERMES_API_CALL_STALE_TIMEOUT=90`s、原生 Anthropic 900s)。隐式的非流式 stale 检测会在本地端点上自动禁用,并且会在超大上下文下自动放宽。目前不适用于 AWS Bedrock(`bedrock_converse` 和 AnthropicBedrock SDK 路径均使用 boto3 及其自身的超时配置)。请参阅 [`cli-config.yaml.example`](https://github.com/NousResearch/hermes-agent/blob/main/cli-config.yaml.example) 中的注释示例。\n\n## 终端后端配置\n\nHermes 支持六种终端后端。每种后端决定 agent 的 shell 命令实际在哪里执行 —— 本地机器、Docker 容器、通过 SSH 的远程服务器、Modal 云沙箱(直接或通过 Nous 托管的 gateway)、Daytona 工作区,或 Singularity/Apptainer 容器。\n\n```yaml\nterminal:\n backend: local # local | docker | ssh | modal | daytona | singularity\n cwd: \".\" # Gateway/cron 工作目录(CLI 始终使用启动目录)\n timeout: 180 # 每条命令的超时时间(秒)\n env_passthrough: [] # 转发到沙箱执行的环境变量名(terminal + execute_code)\n singularity_image: \"docker://nikolaik/python-nodejs:python3.11-nodejs20\" # Singularity 后端的容器镜像\n modal_image: \"nikolaik/python-nodejs:python3.11-nodejs20\" # Modal 后端的容器镜像\n daytona_image: \"nikolaik/python-nodejs:python3.11-nodejs20\" # Daytona 后端的容器镜像\n```\n\n对于 Modal 和 Daytona 等云沙箱,`container_persistent: true` 表示 Hermes 将尝试在沙箱重建后保留文件系统状态。这并不保证相同的活跃沙箱、PID 空间或后台进程之后仍在运行。\n\n### 后端概览\n\n| 后端 | 命令运行位置 | 隔离性 | 最适合 |\n|---------|-------------------|-----------|----------|\n| **local** | 直接在您的机器上 | 无 | 开发、个人使用 |\n| **docker** | 单个持久 Docker 容器(跨会话、`/new`、子 agent 共享) | 完全(命名空间、cap-drop) | 安全沙箱、CI/CD |\n| **ssh** | 通过 SSH 的远程服务器 | 网络边界 | 远程开发、强大硬件 |\n| **modal** | Modal 云沙箱 | 完全(云 VM) | 临时云计算、评估 |\n| **daytona** | Daytona 工作区 | 完全(云容器) | 托管云开发环境 |\n| **singularity** | Singularity/Apptainer 容器 | 命名空间(--containall) | HPC 集群、共享机器 |\n\n### Local 后端\n\n默认后端。命令直接在您的机器上运行,无隔离。无需特殊设置。\n\n```yaml\nterminal:\n backend: local\n```\n\n:::warning\nAgent 拥有与您的用户账户相同的文件系统访问权限。使用 `hermes tools` 禁用不需要的工具,或切换到 Docker 进行沙箱隔离。\n:::\n\n### Docker 后端\n\n在具有安全加固的 Docker 容器内运行命令(所有权限已删除、无权限提升、PID 限制)。\n\n**单个持久容器,而非每条命令一个容器。** Hermes 在首次使用时启动一个长期运行的容器,并通过 `docker exec` 将每个终端、文件和 `execute_code` 调用路由到同一容器中 —— 跨会话、`/new`、`/reset` 和 `delegate_task` 子 agent,贯穿 Hermes 进程的整个生命周期。工作目录更改、已安装的包以及 `/workspace` 中的文件会从一次工具调用延续到下一次,就像本地 shell 一样。容器在关闭时停止并删除。详情请参阅下方的**容器生命周期**。\n\n```yaml\nterminal:\n backend: docker\n docker_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n docker_mount_cwd_to_workspace: false # 将启动目录挂载到 /workspace\n docker_run_as_host_user: false # 参见下方\"以宿主用户身份运行容器\"\n docker_forward_env: # 转发到容器的环境变量\n - \"GITHUB_TOKEN\"\n docker_volumes: # 宿主目录挂载\n - \"/home/user/projects:/workspace/projects\"\n - \"/home/user/data:/data:ro\" # :ro 表示只读\n docker_extra_args: # 附加到 `docker run` 的额外标志\n - \"--gpus=all\"\n - \"--network=host\"\n\n # 资源限制\n container_cpu: 1 # CPU 核心数(0 = 不限制)\n container_memory: 5120 # MB(0 = 不限制)\n container_disk: 51200 # MB(需要 XFS+pquota 上的 overlay2)\n container_persistent: true # 跨会话持久化 /workspace 和 /root\n```\n\n**`terminal.docker_extra_args`**(也可通过 `TERMINAL_DOCKER_EXTRA_ARGS='[\"--gpus=all\"]'` 覆盖)允许传递 Hermes 未作为一级键公开的任意 `docker run` 标志 —— `--gpus`、`--network`、`--add-host`、替代 `--security-opt` 覆盖等。每个条目必须是字符串;该列表最后附加到组装好的 `docker run` 调用中,因此可以在需要时覆盖 Hermes 的默认值。请谨慎使用 —— 与沙箱加固(权限删除、`--user`、workspace 绑定挂载)冲突的标志将悄然削弱隔离性。\n\n**要求:** 已安装并运行 Docker Desktop 或 Docker Engine。Hermes 会探测 `$PATH` 以及常见的 macOS 安装位置(`/usr/local/bin/docker`、`/opt/homebrew/bin/docker`、Docker Desktop 应用包)。开箱即用支持 Podman:设置 `HERMES_DOCKER_BINARY=podman`(或完整路径)以在两者都安装时强制使用它。\n\n**容器生命周期:** Hermes 为每个终端和文件工具调用重用单个长期运行的容器(`docker run -d ... sleep 2h`),跨会话、`/new`、`/reset` 和 `delegate_task` 子 agent,贯穿 Hermes 进程的整个生命周期。命令通过带登录 shell 的 `docker exec` 运行,因此工作目录更改、已安装的包以及 `/workspace` 中的文件都会从一次工具调用延续到下一次。容器在 Hermes 关闭时(或空闲清理回收时)停止并删除。\n\n通过 `delegate_task(tasks=[...])` 生成的并行子 agent 共享这一个容器 —— 并发的 `cd`、环境变量修改以及对同一路径的写入会发生冲突。如果子 agent 需要隔离的沙箱,必须通过 `register_task_env_overrides()` 注册每任务镜像覆盖,RL 和基准测试环境(TerminalBench2、HermesSweEnv 等)会自动为其每任务 Docker 镜像执行此操作。\n\n**安全加固:**\n- `--cap-drop ALL`,仅添加回 `DAC_OVERRIDE`、`CHOWN`、`FOWNER`\n- `--security-opt no-new-privileges`\n- `--pids-limit 256`\n- `/tmp`(512MB)、`/var/tmp`(256MB)、`/run`(64MB)的大小限制 tmpfs\n\n**凭据转发:** `docker_forward_env` 中列出的环境变量首先从您的 shell 环境解析,然后回退到 `~/.hermes/.env`。技能也可以声明 `required_environment_variables`,这些变量会自动合并。\n\n### SSH 后端\n\n通过 SSH 在远程服务器上运行命令。使用 ControlMaster 进行连接复用(5 分钟空闲保活)。默认启用持久 shell —— 状态(cwd、环境变量)在命令之间保持。\n\n```yaml\nterminal:\n backend: ssh\n persistent_shell: true # 保持长期运行的 bash 会话(默认:true)\n```\n\n**必需的环境变量:**\n\n```bash\nTERMINAL_SSH_HOST=my-server.example.com\nTERMINAL_SSH_USER=ubuntu\n```\n\n**可选:**\n\n| 变量 | 默认值 | 描述 |\n|----------|---------|-------------|\n| `TERMINAL_SSH_PORT` | `22` | SSH 端口 |\n| `TERMINAL_SSH_KEY` | (系统默认) | SSH 私钥路径 |\n| `TERMINAL_SSH_PERSISTENT` | `true` | 启用持久 shell |\n\n**工作原理:** 使用 `BatchMode=yes` 和 `StrictHostKeyChecking=accept-new` 在初始化时连接。持久 shell 在远程主机上保持单个 `bash -l` 进程存活,通过临时文件进行通信。需要 `stdin_data` 或 `sudo` 的命令会自动回退到单次模式。\n\n### Modal 后端\n\n在 [Modal](https://modal.com) 云沙箱中运行命令。每个任务获得一个具有可配置 CPU、内存和磁盘的隔离 VM。文件系统可以跨会话快照/恢复。\n\n```yaml\nterminal:\n backend: modal\n container_cpu: 1 # CPU 核心数\n container_memory: 5120 # MB(5GB)\n container_disk: 51200 # MB(50GB)\n container_persistent: true # 快照/恢复文件系统\n```\n\n**必需:** `MODAL_TOKEN_ID` + `MODAL_TOKEN_SECRET` 环境变量,或 `~/.modal.toml` 配置文件。\n\n**持久化:** 启用后,沙箱文件系统在清理时快照,并在下次会话时恢复。快照在 `~/.hermes/modal_snapshots.json` 中跟踪。这保留文件系统状态,而非活跃进程、PID 空间或后台任务。\n\n**凭据文件:** 自动从 `~/.hermes/` 挂载(OAuth token 等),并在每条命令前同步。\n\n### Daytona 后端\n\n在 [Daytona](https://daytona.io) 托管工作区中运行命令。支持停止/恢复以实现持久化。\n\n```yaml\nterminal:\n backend: daytona\n container_cpu: 1 # CPU 核心数\n container_memory: 5120 # MB → 转换为 GiB\n container_disk: 10240 # MB → 转换为 GiB(最大 10 GiB)\n container_persistent: true # 停止/恢复而非删除\n```\n\n**必需:** `DAYTONA_API_KEY` 环境变量。\n\n**持久化:** 启用后,沙箱在清理时停止(而非删除),并在下次会话时恢复。沙箱名称遵循 `hermes-{task_id}` 模式。\n\n**磁盘限制:** Daytona 强制执行 10 GiB 最大值。超过此值的请求将被截断并发出警告。\n\n### Singularity/Apptainer 后端\n\n在 [Singularity/Apptainer](https://apptainer.org) 容器中运行命令。专为 Docker 不可用的 HPC 集群和共享机器设计。\n\n```yaml\nterminal:\n backend: singularity\n singularity_image: \"docker://nikolaik/python-nodejs:python3.11-nodejs20\"\n container_cpu: 1 # CPU 核心数\n container_memory: 5120 # MB\n container_persistent: true # 可写覆盖层跨会话持久化\n```\n\n**要求:** `$PATH` 中有 `apptainer` 或 `singularity` 二进制文件。\n\n**镜像处理:** Docker URL(`docker://...`)自动转换为 SIF 文件并缓存。现有 `.sif` 文件直接使用。\n\n**临时目录:** 按顺序解析:`TERMINAL_SCRATCH_DIR` → `TERMINAL_SANDBOX_DIR/singularity` → `/scratch/$USER/hermes-agent`(HPC 惯例)→ `~/.hermes/sandboxes/singularity`。\n\n**隔离:** 使用 `--containall --no-home` 实现完全命名空间隔离,不挂载宿主 home 目录。\n\n### 常见终端后端问题\n\n如果终端命令立即失败或终端工具报告为已禁用:\n\n- **Local** —— 无特殊要求。入门时最安全的默认选项。\n- **Docker** —— 运行 `docker version` 验证 Docker 是否正常工作。如果失败,修复 Docker 或执行 `hermes config set terminal.backend local`。\n- **SSH** —— `TERMINAL_SSH_HOST` 和 `TERMINAL_SSH_USER` 都必须设置。如果缺少任一项,Hermes 会记录清晰的错误。\n- **Modal** —— 需要 `MODAL_TOKEN_ID` 环境变量或 `~/.modal.toml`。运行 `hermes doctor` 检查。\n- **Daytona** —— 需要 `DAYTONA_API_KEY`。Daytona SDK 处理服务器 URL 配置。\n- **Singularity** —— 需要 `$PATH` 中有 `apptainer` 或 `singularity`。HPC 集群上常见。\n\n如有疑问,将 `terminal.backend` 设回 `local` 并首先验证命令在那里运行。\n\n### 拆卸时远程到宿主文件同步\n\n对于 **SSH**、**Modal** 和 **Daytona** 后端(agent 的工作树位于与运行 Hermes 的宿主不同的机器上),Hermes 跟踪 agent 在远程沙箱中触及的文件,并在会话拆卸/沙箱清理时,将修改的文件**同步回宿主**,存放在 `~/.hermes/cache/remote-syncs/<session-id>/` 下。\n\n- 触发时机:会话关闭、`/new`、`/reset`、gateway 消息超时、子 agent 使用远程后端时 `delegate_task` 子 agent 完成。\n- 覆盖 agent 修改的整个树,而不仅仅是它明确打开的文件。添加、编辑和删除都会被捕获。\n- 远程沙箱可能在您查找时已被拆除;本地 `~/.hermes/cache/remote-syncs/…` 副本是 agent 更改内容的权威记录。\n- 大型二进制输出(模型检查点、原始数据集)按大小限制 —— 同步跳过超过 `file_sync_max_mb`(默认 `100`)的文件。如果您期望更大的工件返回,请调高该值。\n\n```yaml\nterminal:\n file_sync_max_mb: 100 # 默认 —— 同步最大 100 MB 的文件\n file_sync_enabled: true # 默认 —— 设为 false 可完全跳过同步\n```\n\n这是从会话结束后被销毁的临时云沙箱中恢复结果的方式,无需告诉 agent 显式地 `scp` 或 `modal volume put` 每个工件。\n\n### Docker 卷挂载\n\n使用 Docker 后端时,`docker_volumes` 允许您与容器共享宿主目录。每个条目使用标准 Docker `-v` 语法:`host_path:container_path[:options]`。\n\n```yaml\nterminal:\n backend: docker\n docker_volumes:\n - \"/home/user/projects:/workspace/projects\" # 读写(默认)\n - \"/home/user/datasets:/data:ro\" # 只读\n - \"/home/user/.hermes/cache/documents:/output\" # Gateway 可见的导出\n```\n\n适用于:\n- **向 agent 提供文件**(数据集、配置、参考代码)\n- **从 agent 接收文件**(生成的代码、报告、导出)\n- **共享工作区**,您和 agent 都访问相同的文件\n\n如果您使用消息 gateway 并希望 agent 通过 `MEDIA:/...` 发送生成的文件,建议使用专用的宿主可见导出挂载,例如 `/home/user/.hermes/cache/documents:/output`。\n\n- 在 Docker 中将文件写入 `/output/...`\n- 在 `MEDIA:` 中发出**宿主路径**,例如:`MEDIA:/home/user/.hermes/cache/documents/report.txt`\n- **不要**发出 `/workspace/...` 或 `/output/...`,除非该确切路径在宿主上对 gateway 进程也存在\n\n:::warning\nYAML 重复键会静默覆盖之前的键。如果您已有 `docker_volumes:` 块,请将新挂载合并到同一列表中,而不是在文件后面再添加一个 `docker_volumes:` 键。\n:::\n\n也可以通过环境变量设置:`TERMINAL_DOCKER_VOLUMES='[\"/host:/container\"]'`(JSON 数组)。\n\n### Docker 凭据转发\n\n默认情况下,Docker 终端会话不继承任意宿主凭据。如果您需要在容器内使用特定 token,请将其添加到 `terminal.docker_forward_env`。\n\n```yaml\nterminal:\n backend: docker\n docker_forward_env:\n - \"GITHUB_TOKEN\"\n - \"NPM_TOKEN\"\n```\n\nHermes 首先从您当前的 shell 解析每个列出的变量,然后回退到通过 `hermes config set` 保存的 `~/.hermes/.env`。\n\n:::warning\n`docker_forward_env` 中列出的任何内容都会对容器内运行的命令可见。只转发您愿意暴露给终端会话的凭据。\n:::\n\n### 以宿主用户身份运行容器\n\n默认情况下,Docker 容器以 `root`(UID 0)身份运行。在 `/workspace` 或其他绑定挂载中创建的文件在宿主上归 root 所有,因此会话结束后您必须 `sudo chown` 才能从宿主编辑器编辑它们。`terminal.docker_run_as_host_user` 标志解决了这个问题:\n\n```yaml\nterminal:\n backend: docker\n docker_run_as_host_user: true # 默认:false\n```\n\n启用后,Hermes 将 `--user $(id -u):$(id -g)` 附加到 `docker run` 命令,使写入绑定挂载目录(`/workspace`、`/root`、`docker_volumes` 中的任何内容)的文件归您的宿主用户所有,而非 root。权衡:容器将无法再 `apt install` 或写入 `/root/.npm` 等 root 拥有的路径 —— 如果您同时需要这两者,请使用 `HOME` 归非 root 用户所有的基础镜像(或在镜像构建时添加所需工具)。\n\n保持 `false`(默认)以获得向后兼容的行为。当您的工作流主要是\"编辑挂载的宿主文件\"且厌倦了 `sudo chown -R` 时,请开启此选项。\n\n### 可选:将启动目录挂载到 `/workspace`\n\nDocker 沙箱默认保持隔离。Hermes **不会**将您当前的宿主工作目录传入容器,除非您明确选择加入。\n\n在 `config.yaml` 中启用:\n\n```yaml\nterminal:\n backend: docker\n docker_mount_cwd_to_workspace: true\n```\n\n启用后:\n- 如果您从 `~/projects/my-app` 启动 Hermes,该宿主目录将绑定挂载到 `/workspace`\n- Docker 后端从 `/workspace` 开始\n- 文件工具和终端命令都能看到相同的挂载项目\n\n禁用时,`/workspace` 保持沙箱所有,除非您通过 `docker_volumes` 显式挂载内容。\n\n安全权衡:\n- `false` 保留沙箱边界\n- `true` 使沙箱直接访问您启动 Hermes 的目录\n\n仅在您有意希望容器处理实时宿主文件时才选择加入。\n\n### 持久 Shell\n\n默认情况下,每条终端命令在其自己的子进程中运行 —— 工作目录、环境变量和 shell 变量在命令之间重置。启用**持久 shell** 后,单个长期运行的 bash 进程在 `execute()` 调用之间保持存活,使状态在命令之间保持。\n\n这对 **SSH 后端**最有用,它还消除了每条命令的连接开销。持久 shell **对 SSH 默认启用**,对本地后端禁用。\n\n```yaml\nterminal:\n persistent_shell: true # 默认 —— 为 SSH 启用持久 shell\n```\n\n禁用:\n\n```bash\nhermes config set terminal.persistent_shell false\n```\n\n**跨命令保持的内容:**\n- 工作目录(`cd /tmp` 对下一条命令生效)\n- 导出的环境变量(`export FOO=bar`)\n- Shell 变量(`MY_VAR=hello`)\n\n**优先级:**\n\n| 级别 | 变量 | 默认值 |\n|-------|----------|---------|\n| 配置 | `terminal.persistent_shell` | `true` |\n| SSH 覆盖 | `TERMINAL_SSH_PERSISTENT` | 遵循配置 |\n| Local 覆盖 | `TERMINAL_LOCAL_PERSISTENT` | `false` |\n\n每个后端的环境变量具有最高优先级。如果您也想在本地后端使用持久 shell:\n\n```bash\nexport TERMINAL_LOCAL_PERSISTENT=true\n```\n\n:::note\n需要 `stdin_data` 或 sudo 的命令会自动回退到单次模式,因为持久 shell 的 stdin 已被 IPC 协议占用。\n:::\n\n有关每个后端的详细信息,请参阅[代码执行](features/code-execution.md)和 [README 的终端部分](features/tools.md)。\n\n## 技能设置\n\n技能可以通过其 SKILL.md frontmatter 声明自己的配置设置。这些是非机密值(路径、偏好、域设置),存储在 `config.yaml` 的 `skills.config` 命名空间下。\n\n```yaml\nskills:\n config:\n myplugin:\n path: ~/myplugin-data # 示例 —— 每个技能定义自己的键\n```\n\n**技能设置的工作原理:**\n\n- `hermes config migrate` 扫描所有已启用的技能,找到未配置的设置,并提供提示\n- `hermes config show` 在\"技能设置\"下显示所有技能设置及其所属技能\n- 技能加载时,其解析的配置值会自动注入到技能上下文中\n\n**手动设置值:**\n\n```bash\nhermes config set skills.config.myplugin.path ~/myplugin-data\n```\n\n有关在您自己的技能中声明配置设置的详细信息,请参阅[创建技能 — 配置设置](/developer-guide/creating-skills#config-settings-configyaml)。\n\n### Agent 创建技能写入的守卫\n\n当 agent 使用 `skill_manage` 创建、编辑、修补或删除技能时,Hermes 可以选择扫描新/更新的内容以查找危险关键字模式(凭据收集、明显的 prompt 注入、数据外泄指令)。扫描器**默认关闭** —— 合法触及 `~/.ssh/` 或提及 `$OPENAI_API_KEY` 的真实 agent 工作流触发启发式规则过于频繁。如果您希望扫描器在 agent 的技能写入落地前提示您,请重新开启:\n\n```yaml\nskills:\n guard_agent_created: true # 默认:false\n```\n\n开启后,任何被标记的 `skill_manage` 写入都会以审批提示的形式出现,并附带扫描器的理由。接受的写入落地;拒绝的写入向 agent 返回解释性错误。\n\n## 内存配置\n\n```yaml\nmemory:\n memory_enabled: true\n user_profile_enabled: true\n memory_char_limit: 2200 # ~800 tokens\n user_char_limit: 1375 # ~500 tokens\n```\n\n## 文件读取安全\n\n控制单次 `read_file` 调用可以返回多少内容。超过限制的读取将被拒绝,并向 agent 返回错误,提示使用 `offset` 和 `limit` 读取较小范围。这可以防止单次读取压缩的 JS 包或大型数据文件时淹没上下文窗口。\n\n```yaml\nfile_read_max_chars: 100000 # 默认 —— ~25-35K tokens\n```\n\n如果您使用具有大上下文窗口的模型并经常读取大文件,请调高此值。对于小上下文模型,请降低以保持读取高效:\n\n```yaml\n# 大上下文模型(200K+)\nfile_read_max_chars: 200000\n\n# 小型本地模型(16K 上下文)\nfile_read_max_chars: 30000\n```\n\nAgent 还会自动去重文件读取 —— 如果同一文件区域被读取两次且文件未更改,则返回轻量级存根而不是重新发送内容。这在上下文压缩后重置,以便 agent 在内容被摘要后可以重新读取文件。\n\n## 工具输出截断限制\n\n三个相关的上限控制工具在 Hermes 截断之前可以返回多少原始输出:\n\n```yaml\ntool_output:\n max_bytes: 50000 # 终端输出上限(字符)\n max_lines: 2000 # read_file 分页上限\n max_line_length: 2000 # read_file 行号视图中的每行上限\n```\n\n- **`max_bytes`** —— 当 `terminal` 命令产生超过此字符数的合并 stdout/stderr 时,Hermes 保留前 40% 和后 60%,并在中间插入 `[OUTPUT TRUNCATED]` 通知。默认 `50000`(典型分词器约 12-15K tokens)。\n- **`max_lines`** —— 单次 `read_file` 调用的 `limit` 参数上限。超过此值的请求将被截断,以防单次读取淹没上下文窗口。默认 `2000`。\n- **`max_line_length`** —— `read_file` 发出行号视图时应用的每行上限。超过此长度的行将被截断为此字符数,后跟 `... [truncated]`。默认 `2000`。\n\n对于具有大上下文窗口且每次调用可以承受更多原始输出的模型,请调高限制。对于小上下文模型,请降低以保持工具结果紧凑:\n\n```yaml\n# 大上下文模型(200K+)\ntool_output:\n max_bytes: 150000\n max_lines: 5000\n\n# 小型本地模型(16K 上下文)\ntool_output:\n max_bytes: 20000\n max_lines: 500\n```\n\n## 全局工具集禁用\n\n要在 CLI 和每个 gateway 平台上统一禁用特定工具集,请在 `agent.disabled_toolsets` 下列出其名称:\n\n```yaml\nagent:\n disabled_toolsets:\n - memory # 隐藏内存工具 + MEMORY_GUIDANCE 注入\n - web # 任何地方都不使用 web_search / web_extract\n```\n\n这在每个平台的工具配置(由 `hermes tools` 写入的 `platform_toolsets`)**之后**应用,因此此处列出的工具集始终被删除 —— 即使平台的已保存配置仍然列出它。当您希望有一个\"到处关闭 X\"的单一开关而不是编辑 `hermes tools` UI 中的 15+ 个平台行时,请使用此选项。\n\n留空列表或省略键不会产生任何效果。\n\n## Git Worktree 隔离\n\n启用隔离的 git worktree,以便在同一仓库上并行运行多个 agent:\n\n```yaml\nworktree: true # 始终创建 worktree(与 hermes -w 相同)\n# worktree: false # 默认 —— 仅在传递 -w 标志时\n```\n\n启用后,每个 CLI 会话在 `.worktrees/` 下创建一个带有自己分支的新 worktree。Agent 可以编辑文件、提交、推送和创建 PR,而不会相互干扰。干净的 worktree 在退出时删除;脏的 worktree 保留以供手动恢复。\n\n您还可以通过仓库根目录中的 `.worktreeinclude` 列出要复制到 worktree 的 gitignore 文件:\n\n```\n# .worktreeinclude\n.env\n.venv/\nnode_modules/\n```\n\n## 上下文压缩\n\nHermes 自动压缩长对话以保持在模型的上下文窗口内。压缩摘要器是一个单独的 LLM 调用 —— 您可以将其指向任何 provider 或端点。\n\n所有压缩设置都在 `config.yaml` 中(无环境变量)。\n\n### 完整参考\n\n```yaml\ncompression:\n enabled: true # 开启/关闭压缩\n threshold: 0.50 # 在上下文限制的此百分比时压缩\n target_ratio: 0.20 # 保留为最近尾部的阈值分数\n protect_last_n: 20 # 保持未压缩的最少最近消息数\n hygiene_hard_message_limit: 5000 # Gateway 安全阀 —— 见下文\n\n# 摘要模型/provider 在 auxiliary: 下配置:\nauxiliary:\n compression:\n model: \"\" # 空 = 使用主聊天模型。覆盖为例如 \"google/gemini-3-flash-preview\" 以获得更便宜/更快的压缩。\n provider: \"auto\" # Provider:\"auto\"、\"openrouter\"、\"nous\"、\"codex\"、\"main\" 等\n base_url: null # 自定义 OpenAI 兼容端点(覆盖 provider)\n```\n\n:::info 旧版配置迁移\n带有 `compression.summary_model`、`compression.summary_provider` 和 `compression.summary_base_url` 的旧版配置在首次加载时自动迁移到 `auxiliary.compression.*`(配置版本 17)。无需手动操作。\n:::\n\n`hygiene_hard_message_limit` 是仅限 gateway 的**预压缩安全阀**。它的存在是为了打破一个死循环:当超大会话的 API 调用持续断开时,gateway 永远收不到 token 使用数据,基于 token 的阈值因此无法触发,于是 transcript 持续增长、断开愈发严重。这个基于消息数的下限仅凭消息数量触发(无论 API 是否失败,消息数始终已知),强制压缩以恢复会话。默认 `5000` —— 远高于任何正常会话,包括做数千次短轮次的大上下文(1M+)模型,它们早就在 token 阈值处压缩了。对于异常平台可调得更高;要强制更积极的压缩则调低。在运行中的 gateway 上编辑此值将在下一条消息时生效(见下文)。\n\n:::tip Gateway 热重载压缩和上下文长度\n从最近的版本开始,在运行中的 gateway 上编辑 `config.yaml` 中的 `model.context_length` 或任何 `compression.*` 键将在下一条消息时生效 —— 无需 gateway 重启、`/reset` 或会话轮换。缓存的 agent 签名包含这些键,因此 gateway 在检测到更改时会透明地重建 agent。API 密钥和工具/技能配置仍需要通常的重载路径。\n:::\n\n### 常见设置\n\n**默认(自动检测)—— 无需配置:**\n```yaml\ncompression:\n enabled: true\n threshold: 0.50\n```\n使用您的主 provider 和主模型。如果您希望在比主聊天模型更便宜的模型上进行压缩,请覆盖每任务(例如 `auxiliary.compression.provider: openrouter` + `model: google/gemini-2.5-flash`)。\n\n**强制特定 provider**(基于 OAuth 或 API 密钥):\n```yaml\nauxiliary:\n compression:\n provider: nous\n model: gemini-3-flash\n```\n适用于任何 provider:`nous`、`openrouter`、`codex`、`anthropic`、`main` 等。\n\n**自定义端点**(自托管、Ollama、zai、DeepSeek 等):\n```yaml\nauxiliary:\n compression:\n model: glm-4.7\n base_url: https://api.z.ai/api/coding/paas/v4\n```\n指向自定义 OpenAI 兼容端点。使用 `OPENAI_API_KEY` 进行认证。\n\n### 三个旋钮的交互方式\n\n| `auxiliary.compression.provider` | `auxiliary.compression.base_url` | 结果 |\n|---------------------|---------------------|--------|\n| `auto`(默认) | 未设置 | 自动检测最佳可用 provider |\n| `nous` / `openrouter` / 等 | 未设置 | 强制使用该 provider,使用其认证 |\n| 任意 | 已设置 | 直接使用自定义端点(忽略 provider) |\n\n:::warning 摘要模型上下文长度要求\n摘要模型**必须**具有至少与您的主 agent 模型一样大的上下文窗口。压缩器将对话的完整中间部分发送给摘要模型 —— 如果该模型的上下文窗口小于主模型的,摘要调用将因上下文长度错误而失败。发生这种情况时,中间轮次将**在没有摘要的情况下被丢弃**,静默丢失对话上下文。如果您覆盖模型,请验证其上下文长度满足或超过您的主模型。\n:::\n\n## 上下文引擎\n\n上下文引擎控制在接近模型 token 限制时如何管理对话。内置的 `compressor` 引擎使用有损摘要(参见[上下文压缩](/developer-guide/context-compression-and-caching))。插件引擎可以用替代策略替换它。\n\n```yaml\ncontext:\n engine: \"compressor\" # 默认 —— 内置有损摘要\n```\n\n使用插件引擎(例如,用于无损上下文管理的 LCM):\n\n```yaml\ncontext:\n engine: \"lcm\" # 必须与插件名称匹配\n```\n\n插件引擎**永远不会自动激活** —— 您必须将 `context.engine` 显式设置为插件名称。可用引擎可以通过 `hermes plugins` → Provider Plugins → Context Engine 浏览和选择。\n\n有关内存插件的类似单选系统,请参阅[内存 Providers](/user-guide/features/memory-providers)。\n\n## 迭代预算\n\n当 agent 在处理具有许多工具调用的复杂任务时,它可能会耗尽其迭代预算(默认:90 轮)。Hermes **不会**在任务中途注入压力警告 —— 早期版本会在预算达到 70%/90% 时警告模型,这会导致模型过早放弃复杂任务,该机制已于 2026 年 4 月移除。\n\n取而代之的是,当预算真正耗尽(90/90)时,Hermes 注入一条消息要求模型收尾,并允许一次**宽限调用**以便其给出最终响应。如果该宽限调用仍未产生文本,则会要求 agent 总结已完成的工作。\n\n```yaml\nagent:\n max_turns: 90 # 每次对话轮次的最大迭代次数(默认:90)\n api_max_retries: 3 # 回退启动前每个 provider 的重试次数(默认:3)\n```\n\n当迭代预算完全耗尽时,CLI 向用户显示通知:`⚠ Iteration budget reached (90/90) — response may be incomplete`。\n\n`agent.api_max_retries` 控制 Hermes 在回退 provider 切换启动**之前**对瞬时错误(速率限制、连接断开、5xx)重试 provider API 调用的次数。默认为 `3` —— 总共四次尝试。如果您配置了[回退 providers](/user-guide/features/fallback-providers) 并希望更快地故障转移,请将其降至 `0`,这样主 provider 上的第一个瞬时错误会立即切换到回退,而不是对不稳定的端点进行重试。\n\n### API 超时\n\nHermes 对流式传输有单独的超时层,以及用于非流式调用的陈旧检测器。陈旧检测器仅在您将其保留为隐式默认值时才会自动调整本地 provider。\n\n| 超时 | 默认值 | 本地 providers | 配置/环境变量 |\n|---------|---------|----------------|--------------|\n| Socket 读取超时 | 120s | 自动提升至 1800s | `HERMES_STREAM_READ_TIMEOUT` |\n| 陈旧流检测 | 180s | 自动禁用 | `HERMES_STREAM_STALE_TIMEOUT` |\n| 陈旧非流检测 | 300s | 保持隐式时自动禁用 | `providers.<id>.stale_timeout_seconds` 或 `HERMES_API_CALL_STALE_TIMEOUT` |\n| API 调用(非流式) | 1800s | 不变 | `providers.<id>.request_timeout_seconds` / `timeout_seconds` 或 `HERMES_API_TIMEOUT` |\n\n**Socket 读取超时**控制 httpx 等待 provider 下一个数据块的时间。本地 LLM 在大上下文上预填充可能需要几分钟才能产生第一个 token,因此当 Hermes 检测到本地端点时,会将此值提升至 30 分钟。如果您显式设置 `HERMES_STREAM_READ_TIMEOUT`,无论端点检测如何,始终使用该值。\n\n**陈旧流检测**终止接收 SSE 保活 ping 但没有实际内容的连接。对于本地 providers,这完全禁用,因为它们在预填充期间不发送保活 ping。\n\n**陈旧非流检测**终止长时间没有响应的非流式调用。默认情况下,Hermes 在本地端点上禁用此功能,以避免长时间预填充期间的误报。如果您显式设置 `providers.<id>.stale_timeout_seconds`、`providers.<id>.models.<model>.stale_timeout_seconds` 或 `HERMES_API_CALL_STALE_TIMEOUT`,即使在本地端点上也会遵守该显式值。\n\n## 上下文压力警告\n\n与迭代预算压力分开,上下文压力跟踪对话距**压缩阈值**有多近 —— 即上下文压缩触发以摘要旧消息的点。这有助于您和 agent 了解对话何时变长。\n\n| 进度 | 级别 | 发生的事情 |\n|----------|-------|-------------|\n| **≥ 60%** 到阈值 | 信息 | CLI 显示青色进度条;gateway 发送信息通知 |\n| **≥ 85%** 到阈值 | 警告 | CLI 显示粗体黄色进度条;gateway 警告压缩即将发生 |\n\n在 CLI 中,上下文压力在工具输出流中显示为进度条:\n\n```\n ◐ context ████████████░░░░░░░░ 62% to compaction 48k threshold (50%) · approaching compaction\n```\n\n在消息平台上,发送纯文本通知:\n\n```\n◐ Context: ████████████░░░░░░░░ 62% to compaction (threshold: 50% of window).\n```\n\n如果自动压缩被禁用,警告会告诉您上下文可能被截断。\n\n上下文压力是自动的 —— 无需配置。它纯粹作为面向用户的通知触发,不修改消息流或向模型上下文注入任何内容。\n\n## 凭据池策略\n\n当您为同一 provider 拥有多个 API 密钥或 OAuth token 时,配置轮换策略:\n\n```yaml\ncredential_pool_strategies:\n openrouter: round_robin # 均匀循环使用密钥\n anthropic: least_used # 始终选择使用最少的密钥\n```\n\n选项:`fill_first`(默认)、`round_robin`、`least_used`、`random`。完整文档请参阅[凭据池](/user-guide/features/credential-pools)。\n\n## Prompt 缓存\n\n当活跃 provider 支持时,Hermes 自动开启跨会话 prompt 缓存 —— 无需用户配置。\n\n对于**原生 Anthropic**、**OpenRouter** 和 **Nous Portal** 上的 Claude,Hermes 在系统提示词和技能块上附加带有 1 小时 TTL(`ttl: \"1h\"`)的 `cache_control` 断点。在新鲜的一小时内首次发送时按完整输入费率计费;同一小时内任何会话的后续发送以折扣缓存读取费率从缓存中提取。这意味着系统提示词、加载的技能内容以及任何长上下文包含的早期部分在第一个小时内跨 `hermes` 会话和分叉子 agent 被重用。\n\nQwen Cloud(阿里巴巴 DashScope)上游将缓存 TTL 限制为 5 分钟,因此 Hermes 在那里使用 5 分钟断点 TTL。其他通过第三方的 Claude 路径(AWS Bedrock、Azure Foundry)回退到 provider 自己的缓存默认值。xAI Grok 使用单独的会话固定对话 ID 机制 —— 参阅 [xAI prompt 缓存](/integrations/providers#xai-grok--responses-api--prompt-caching)。\n\n不存在禁用此功能的旋钮 —— 缓存始终开启,即使在单轮对话中也能节省费用,因为仅系统提示词就占输入 token 数的相当大比例。\n\n## 辅助模型\n\nHermes 使用\"辅助\"模型处理图像分析、网页摘要、浏览器截图分析、会话标题生成和上下文压缩等附带任务。默认情况下(`auxiliary.*.provider: \"auto\"`),Hermes 将每个辅助任务路由到您的**主聊天模型** —— 与您在 `hermes model` 中选择的相同 provider/模型。您无需配置任何内容即可开始,但请注意,在昂贵的推理模型(Opus、MiniMax M2.7 等)上,辅助任务会增加显著成本。如果您希望无论主模型如何都使用便宜且快速的附带任务,请显式设置 `auxiliary.<task>.provider` 和 `auxiliary.<task>.model`(例如,在 OpenRouter 上使用 Gemini Flash 进行视觉和网页提取)。\n\n:::note 为什么 \"auto\" 使用您的主模型\n早期版本将聚合器用户(OpenRouter、Nous Portal)分流到便宜的 provider 端默认值。这令人惊讶 —— 付费购买聚合器订阅的用户会看到不同的模型处理其辅助流量。`auto` 现在对所有人使用主模型,`config.yaml` 中的每任务覆盖仍然优先(见下方[完整辅助配置参考](#full-auxiliary-config-reference))。\n:::\n\n### 交互式配置辅助模型\n\n无需手动编辑 YAML,运行 `hermes model` 并从菜单中选择**\"配置辅助模型\"**。您将获得交互式的每任务选择器:\n\n```\n$ hermes model\n→ Configure auxiliary models\n\n[ ] vision currently: auto / main model\n[ ] web_extract currently: auto / main model\n[ ] title_generation currently: openrouter / google/gemini-3-flash-preview\n[ ] compression currently: auto / main model\n[ ] approval currently: auto / main model\n[ ] triage_specifier currently: auto / main model\n[ ] kanban_decomposer currently: auto / main model\n[ ] profile_describer currently: auto / main model\n```\n\n选择任务,选择 provider(OAuth 流程打开浏览器;API 密钥 provider 提示输入),选择模型。更改持久化到 `config.yaml` 中的 `auxiliary.<task>.*`。与主模型选择器相同的机制 —— 无需学习额外语法。\n\n### 视频教程\n\n<div style={{position: 'relative', width: '100%', aspectRatio: '16 / 9', marginBottom: '1.5rem'}}>\n <iframe\n src=\"https://www.youtube.com/embed/NoF-YajElIM\"\n title=\"Hermes Agent — Auxiliary Models Tutorial\"\n style={{position: 'absolute', top: 0, left: 0, width: '100%', height: '100%', border: 0}}\n allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\"\n allowFullScreen\n />\n</div>\n\n### 通用配置模式\n\nHermes 中的每个模型槽位 —— 辅助任务、压缩、回退 —— 使用相同的三个旋钮:\n\n| 键 | 作用 | 默认值 |\n|-----|-------------|---------|\n| `provider` | 用于认证和路由的 provider | `\"auto\"` |\n| `model` | 请求的模型 | provider 的默认值 |\n| `base_url` | 自定义 OpenAI 兼容端点(覆盖 provider) | 未设置 |\n\n当设置 `base_url` 时,Hermes 忽略 provider 并直接调用该端点(使用 `api_key` 或 `OPENAI_API_KEY` 进行认证)。当仅设置 `provider` 时,Hermes 使用该 provider 的内置认证和基础 URL。\n\n辅助任务的可用 providers:`auto`、`main`,以及[provider 注册表](/reference/environment-variables)中的任何 provider —— `openrouter`、`nous`、`openai-codex`、`copilot`、`copilot-acp`、`anthropic`、`gemini`、`qwen-oauth`、`zai`、`kimi-coding`、`kimi-coding-cn`、`minimax`、`minimax-cn`、`minimax-oauth`、`deepseek`、`nvidia`、`xai`、`xai-oauth`、`ollama-cloud`、`alibaba`、`bedrock`、`huggingface`、`arcee`、`xiaomi`、`kilocode`、`opencode-zen`、`opencode-go`、`azure-foundry` —— 或您 `custom_providers` 列表中任何命名的自定义 provider(例如 `provider: \"beans\"`)。\n\n:::tip MiniMax OAuth\n`minimax-oauth` 通过浏览器 OAuth 登录(无需 API 密钥)。运行 `hermes model` 并选择 **MiniMax (OAuth)** 进行认证。辅助任务自动使用 `MiniMax-M2.7-highspeed`。参阅 [MiniMax OAuth 指南](../guides/minimax-oauth.md)。\n:::\n\n:::tip xAI Grok OAuth\n`xai-oauth` 通过浏览器 OAuth 为 SuperGrok 和 X Premium+ 订阅者登录(无需 API 密钥)。运行 `hermes model` 并选择 **xAI Grok OAuth (SuperGrok / Premium+)** 进行认证。相同的 OAuth token 可重用于每个直接到 xAI 的接口(聊天、辅助任务、TTS、图像生成、视频生成、转录)。参阅 [xAI Grok OAuth 指南](../guides/xai-grok-oauth.md),如果 Hermes 在远程主机上,请参阅 [SSH/远程主机上的 OAuth](../guides/oauth-over-ssh.md)。\n:::\n\n:::warning `\"main\"` 仅用于辅助任务\n`\"main\"` provider 选项表示\"使用我的主 agent 使用的任何 provider\" —— 它仅在 `auxiliary:`、`compression:` 和 `fallback_model:` 配置中有效。它**不是**顶级 `model.provider` 设置的有效值。如果您使用自定义 OpenAI 兼容端点,请在 `model:` 部分设置 `provider: custom`。所有主模型 provider 选项请参阅 [AI Providers](/integrations/providers)。\n:::\n\n### 完整辅助配置参考\n\n```yaml\nauxiliary:\n # 图像分析(vision_analyze 工具 + 浏览器截图)\n vision:\n provider: \"auto\" # \"auto\"、\"openrouter\"、\"nous\"、\"codex\"、\"main\" 等\n model: \"\" # 例如 \"openai/gpt-4o\"、\"google/gemini-2.5-flash\"\n base_url: \"\" # 自定义 OpenAI 兼容端点(覆盖 provider)\n api_key: \"\" # base_url 的 API 密钥(回退到 OPENAI_API_KEY)\n timeout: 120 # 秒 —— LLM API 调用超时;视觉负载需要宽裕的超时\n download_timeout: 30 # 秒 —— 图像 HTTP 下载;慢速连接请增加\n\n # 网页摘要 + 浏览器页面文本提取\n web_extract:\n provider: \"auto\"\n model: \"\" # 例如 \"google/gemini-2.5-flash\"\n base_url: \"\"\n api_key: \"\"\n timeout: 360 # 秒(6 分钟)—— 每次尝试的 LLM 摘要\n\n # 危险命令审批分类器\n approval:\n provider: \"auto\"\n model: \"\"\n base_url: \"\"\n api_key: \"\"\n timeout: 30 # 秒\n\n # 上下文压缩超时(与 compression.* 配置分开)\n compression:\n timeout: 120 # 秒 —— 压缩摘要长对话,需要更多时间\n # fallback_chain: # 可选 —— 发生速率限制/连接故障时尝试的 provider\n # - provider: nous\n # model: deepseek/deepseek-chat\n # - provider: openrouter\n # model: google/gemini-2.5-flash\n # base_url: \"\"\n # api_key: \"\"\n\n # 技能中心 —— 技能匹配和搜索\n skills_hub:\n provider: \"auto\"\n model: \"\"\n base_url: \"\"\n api_key: \"\"\n timeout: 30\n\n # MCP 工具调度\n mcp:\n provider: \"auto\"\n model: \"\"\n base_url: \"\"\n api_key: \"\"\n timeout: 30\n\n # Kanban 分类规格说明器 —— `hermes kanban specify <id>`(或\n # 仪表板上 Triage 列卡片的 ✨ Specify 按钮)使用此\n # 槽位将单行描述扩展为具体规格并将\n # 任务提升到 `todo`。便宜快速的模型在这里效果很好;规格扩展\n # 很短,不需要推理深度。\n triage_specifier:\n provider: \"auto\"\n model: \"\"\n base_url: \"\"\n api_key: \"\"\n timeout: 120\n```\n\n:::tip\n每个辅助任务都有可配置的 `timeout`(秒)。默认值:vision 120s、web_extract 360s、approval 30s、compression 120s。如果您为辅助任务使用慢速本地模型,请增加这些值。Vision 还有单独的 `download_timeout`(默认 30s)用于 HTTP 图像下载 —— 对于慢速连接或自托管图像服务器,请增加此值。\n:::\n\n:::info\n上下文压缩有自己的 `compression:` 块用于阈值,以及 `auxiliary.compression:` 块用于模型/provider 设置 —— 参阅上方的[上下文压缩](#context-compression)。主备用链使用顶层的 `fallback_providers:` 列表 —— 参阅[备用提供商](/integrations/providers#fallback-providers)。三者都遵循相同的 provider/model/base_url 模式。\n:::\n\n### 辅助任务的每任务回退链\n\n每个辅助任务都可以选择性地定义一个 `fallback_chain` —— 一个 provider/model 条目列表,当主要辅助 provider 因速率限制、网络连接问题或付费限制而失败时,Hermes 会尝试使用该列表:\n\n```yaml\nauxiliary:\n compression:\n provider: openrouter\n model: openai/gpt-4o-mini\n fallback_chain:\n - provider: nous\n model: deepseek/deepseek-chat\n - provider: openrouter\n model: google/gemini-2.5-flash\n```\n\n当主要辅助 provider(`openrouter` / `openai/gpt-4o-mini`)返回速率限制、连接超时或需要付费错误时,Hermes 将依次遍历 `fallback_chain`。它会跳过 provider 与已失败 provider 相同的条目,并尝试每个剩余条目,直到有一个成功或该链耗尽。如果所有回退都失败,Hermes 会回退到主 agent 模型作为最终的安全网。\n\n每个条目支持与任何辅助任务配置相同的三个旋钮:\n\n| 键 | 描述 |\n|-----|-------------|\n| `provider` | Provider 名称(`nous`、`openrouter`、`anthropic`、`gemini`、`main` 等) |\n| `model` | 该 provider 的模型名称 |\n| `base_url` | (可选)自定义 OpenAI 兼容端点 |\n\n`fallback_chain` 适用于任何辅助任务 —— `compression`、`vision`、`web_extract`、`approval`、`skills_hub`、`mcp` 等。\n\n### OpenRouter 路由和辅助任务的 Pareto Code\n\n当辅助任务解析到 OpenRouter(显式或通过 `provider: \"main\"` 而您的主 agent 在 OpenRouter 上)时,主 agent 的 `provider_routing` 和 `openrouter.min_coding_score` 设置**不会传播** —— 按设计,每个辅助任务是独立的。要为特定辅助任务设置 OpenRouter provider 偏好或使用 [Pareto Code 路由器](/integrations/providers#openrouter-pareto-code-router),请通过 `extra_body` 按任务设置:\n\n```yaml\nauxiliary:\n compression:\n provider: openrouter\n model: openrouter/pareto-code # 为此任务使用 Pareto Code 路由器\n extra_body:\n provider: # OpenRouter provider 路由偏好\n order: [anthropic, google] # 按此顺序尝试这些 providers\n sort: throughput # 或 \"price\" | \"latency\"\n # only: [anthropic] # 限制到特定 provider\n # ignore: [deepinfra] # 排除特定 providers\n plugins: # OpenRouter Pareto Code 路由器旋钮\n - id: pareto-router\n min_coding_score: 0.5 # 0.0–1.0;越高 = 更强的编码能力\n```\n\n形状与 OpenRouter 在聊天补全请求体中接受的内容一致。Hermes 原样转发整个 `extra_body`,因此 [openrouter.ai/docs](https://openrouter.ai/docs) 中记录的任何其他 OpenRouter 请求体字段都以相同方式工作。\n\n### 更改视觉模型\n\n使用 GPT-4o 而非 Gemini Flash 进行图像分析:\n\n```yaml\nauxiliary:\n vision:\n model: \"openai/gpt-4o\"\n```\n\n或通过环境变量(在 `~/.hermes/.env` 中):\n\n```bash\nAUXILIARY_VISION_MODEL=openai/gpt-4o\n```\n\n### Provider 选项\n\n这些选项适用于**辅助任务配置**(`auxiliary:`、`compression:`、`fallback_model:`),而非您的主 `model.provider` 设置。\n\n| Provider | 描述 | 要求 |\n|----------|-------------|-------------|\n| `\"auto\"` | 最佳可用(默认)。Vision 尝试 OpenRouter → Nous → Codex。 | — |\n| `\"openrouter\"` | 强制 OpenRouter —— 路由到任何模型(Gemini、GPT-4o、Claude 等) | `OPENROUTER_API_KEY` |\n| `\"nous\"` | 强制 Nous Portal | `hermes auth` |\n| `\"codex\"` | 强制 Codex OAuth(ChatGPT 账户)。支持视觉(gpt-5.3-codex)。 | `hermes model` → Codex |\n| `\"minimax-oauth\"` | 强制 MiniMax OAuth(浏览器登录,无需 API 密钥)。辅助任务使用 MiniMax-M2.7-highspeed。 | `hermes model` → MiniMax (OAuth) |\n| `\"xai-oauth\"` | 强制 xAI Grok OAuth(SuperGrok 或 X Premium+ 订阅者的浏览器登录,无需 API 密钥)。相同的 OAuth token 涵盖聊天、TTS、图像、视频和转录。 | `hermes model` → xAI Grok OAuth (SuperGrok / Premium+) |\n| `\"main\"` | 使用您的活跃自定义/主端点。可以来自 `OPENAI_BASE_URL` + `OPENAI_API_KEY` 或通过 `hermes model` / `config.yaml` 保存的自定义端点。适用于 OpenAI、本地模型或任何 OpenAI 兼容 API。**仅限辅助任务 —— 对 `model.provider` 无效。** | 自定义端点凭据 + 基础 URL |\n\n当您希望附带任务绕过默认路由器时,主 provider 目录中的直接 API 密钥 providers 也在这里工作。配置 `GMI_API_KEY` 后,`gmi` 有效:\n\n```yaml\nauxiliary:\n compression:\n provider: \"gmi\"\n model: \"anthropic/claude-opus-4.6\"\n```\n\n对于 GMI 辅助路由,使用 GMI 的 `/v1/models` 端点返回的确切模型 ID。\n\n### 常见设置\n\n**使用直接自定义端点**(比 `provider: \"main\"` 对本地/自托管 API 更清晰):\n```yaml\nauxiliary:\n vision:\n base_url: \"http://localhost:1234/v1\"\n api_key: \"local-key\"\n model: \"qwen2.5-vl\"\n```\n\n`base_url` 优先于 `provider`,因此这是将辅助任务路由到特定端点的最明确方式。对于直接端点覆盖,Hermes 使用配置的 `api_key` 或回退到 `OPENAI_API_KEY`;它不会为该自定义端点重用 `OPENROUTER_API_KEY`。\n\n**使用 OpenAI API 密钥进行视觉:**\n```yaml\n# 在 ~/.hermes/.env 中:\n# OPENAI_BASE_URL=https://api.openai.com/v1\n# OPENAI_API_KEY=sk-...\n\nauxiliary:\n vision:\n provider: \"main\"\n model: \"gpt-4o\" # 或 \"gpt-4o-mini\" 更便宜\n```\n\n**使用 OpenRouter 进行视觉**(路由到任何模型):\n```yaml\nauxiliary:\n vision:\n provider: \"openrouter\"\n model: \"openai/gpt-4o\" # 或 \"google/gemini-2.5-flash\" 等\n```\n\n**使用 Codex OAuth**(ChatGPT Pro/Plus 账户 —— 无需 API 密钥):\n```yaml\nauxiliary:\n vision:\n provider: \"codex\" # 使用您的 ChatGPT OAuth token\n # 模型默认为 gpt-5.3-codex(支持视觉)\n```\n\n**使用 MiniMax OAuth**(浏览器登录,无需 API 密钥):\n```yaml\nmodel:\n default: MiniMax-M2.7\n provider: minimax-oauth\n base_url: https://api.minimax.io/anthropic\n```\n运行 `hermes model` 并选择 **MiniMax (OAuth)** 自动登录并设置此项。对于中国区域,基础 URL 将是 `https://api.minimaxi.com/anthropic`。完整演练请参阅 [MiniMax OAuth 指南](../guides/minimax-oauth.md)。\n\n**使用本地/自托管模型:**\n```yaml\nauxiliary:\n vision:\n provider: \"main\" # 使用您的活跃自定义端点\n model: \"my-local-model\"\n```\n\n`provider: \"main\"` 使用 Hermes 用于普通聊天的任何 provider —— 无论是命名的自定义 provider(例如 `beans`)、内置 provider(如 `openrouter`)还是旧版 `OPENAI_BASE_URL` 端点。\n\n:::tip\n如果您使用 Codex OAuth 作为主模型 provider,视觉会自动工作 —— 无需额外配置。Codex 包含在视觉的自动检测链中。\n:::\n\n:::warning\n**视觉需要多模态模型。** 如果您设置 `provider: \"main\"`,请确保您的端点支持多模态/视觉 —— 否则图像分析将失败。\n:::\n\n### 环境变量(旧版)\n\n辅助模型也可以通过环境变量配置。但是,`config.yaml` 是首选方法 —— 它更易于管理,并支持所有选项,包括 `base_url` 和 `api_key`。\n\n| 设置 | 环境变量 |\n|---------|---------------------|\n| Vision provider | `AUXILIARY_VISION_PROVIDER` |\n| Vision 模型 | `AUXILIARY_VISION_MODEL` |\n| Vision 端点 | `AUXILIARY_VISION_BASE_URL` |\n| Vision API 密钥 | `AUXILIARY_VISION_API_KEY` |\n| Web 提取 provider | `AUXILIARY_WEB_EXTRACT_PROVIDER` |\n| Web 提取模型 | `AUXILIARY_WEB_EXTRACT_MODEL` |\n| Web 提取端点 | `AUXILIARY_WEB_EXTRACT_BASE_URL` |\n| Web 提取 API 密钥 | `AUXILIARY_WEB_EXTRACT_API_KEY` |\n\n压缩和回退模型设置仅限 config.yaml。\n\n:::tip\n运行 `hermes config` 查看您当前的辅助模型设置。覆盖仅在与默认值不同时显示。\n:::\n\n## 推理努力程度\n\n控制模型在响应前进行多少\"思考\":\n\n```yaml\nagent:\n reasoning_effort: \"\" # 空 = 中等(默认)。选项:none、minimal、low、medium、high、xhigh(最大)\n```\n\n未设置时(默认),推理努力程度默认为\"medium\" —— 适合大多数任务的平衡级别。设置值会覆盖它 —— 更高的推理努力程度在复杂任务上提供更好的结果,但代价是更多 token 和延迟。\n\n您也可以在运行时使用 `/reasoning` 命令更改推理努力程度:\n\n```\n/reasoning # 显示当前努力程度和显示状态\n/reasoning high # 将推理努力程度设为 high\n/reasoning none # 禁用推理\n/reasoning show # 在每次响应上方显示模型思考\n/reasoning hide # 隐藏模型思考\n```\n\n## 工具使用强制\n\n某些模型偶尔会将预期操作描述为文本而不是进行工具调用(\"我会运行测试...\"而不是实际调用终端)。工具使用强制会注入系统提示词指导,引导模型实际调用工具。\n\n```yaml\nagent:\n tool_use_enforcement: \"auto\" # \"auto\" | true | false | [\"model-substring\", ...]\n```\n\n| 值 | 行为 |\n|-------|----------|\n| `\"auto\"`(默认) | 对匹配以下模型启用:`gpt`、`codex`、`gemini`、`gemma`、`grok`。对所有其他模型禁用(Claude、DeepSeek、Qwen 等)。 |\n| `true` | 始终启用,无论模型如何。如果您注意到当前模型描述操作而不是执行操作,请使用此选项。 |\n| `false` | 始终禁用,无论模型如何。 |\n| `[\"gpt\", \"codex\", \"qwen\", \"llama\"]` | 仅当模型名称包含列出的子字符串之一时启用(不区分大小写)。 |\n\n### 注入的内容\n\n启用后,系统提示词中可能会添加三层指导:\n\n1. **通用工具使用强制**(所有匹配模型)—— 指示模型立即进行工具调用而不是描述意图,持续工作直到任务完成,永远不要以未来操作的承诺结束轮次。\n\n2. **OpenAI 执行纪律**(仅限 GPT 和 Codex 模型)—— 针对 GPT 特定失败模式的额外指导:在部分结果上放弃工作、跳过先决条件查找、幻觉而不是使用工具、在未验证的情况下宣布\"完成\"。\n\n3. **Google 操作指导**(仅限 Gemini 和 Gemma 模型)—— 简洁性、绝对路径、并行工具调用和编辑前验证模式。\n\n这些对用户透明,仅影响系统提示词。已经可靠使用工具的模型(如 Claude)不需要此指导,这就是为什么 `\"auto\"` 排除它们。\n\n### 何时开启\n\n如果您使用的模型不在默认自动列表中,并注意到它经常描述它*会*做什么而不是实际去做,请设置 `tool_use_enforcement: true` 或将模型子字符串添加到列表中:\n\n```yaml\nagent:\n tool_use_enforcement: [\"gpt\", \"codex\", \"gemini\", \"grok\", \"my-custom-model\"]\n```\n\n## TTS 配置\n\n```yaml\ntts:\n provider: \"edge\" # \"edge\" | \"elevenlabs\" | \"openai\" | \"minimax\" | \"mistral\" | \"gemini\" | \"xai\" | \"neutts\"\n speed: 1.0 # 全局速度倍数(所有 provider 的回退)\n edge:\n voice: \"en-US-AriaNeural\" # 322 种声音,74 种语言\n speed: 1.0 # 速度倍数(转换为速率百分比,例如 1.5 → +50%)\n elevenlabs:\n voice_id: \"pNInz6obpgDQGcFmaJgB\"\n model_id: \"eleven_multilingual_v2\"\n openai:\n model: \"gpt-4o-mini-tts\"\n voice: \"alloy\" # alloy、echo、fable、onyx、nova、shimmer\n speed: 1.0 # 速度倍数(API 限制为 0.25–4.0)\n base_url: \"https://api.openai.com/v1\" # 覆盖 OpenAI 兼容 TTS 端点\n minimax:\n speed: 1.0 # 语音速度倍数\n # base_url: \"\" # 可选:覆盖 OpenAI 兼容 TTS 端点\n mistral:\n model: \"voxtral-mini-tts-2603\"\n voice_id: \"c69964a6-ab8b-4f8a-9465-ec0925096ec8\" # Paul - Neutral(默认)\n gemini:\n model: \"gemini-2.5-flash-preview-tts\" # 或 gemini-2.5-pro-preview-tts\n voice: \"Kore\" # 30 种预置声音:Zephyr、Puck、Kore、Enceladus 等\n xai:\n voice_id: \"eve\" # xAI TTS 声音\n language: \"en\" # ISO 639-1\n sample_rate: 24000\n bit_rate: 128000 # MP3 比特率\n # base_url: \"https://api.x.ai/v1\"\n neutts:\n ref_audio: ''\n ref_text: ''\n model: neuphonic/neutts-air-q4-gguf\n device: cpu\n```\n\n这控制 `text_to_speech` 工具和语音模式中的口语回复(CLI 中的 `/voice tts` 或消息 gateway)。\n\n**速度回退层次:** provider 特定速度(例如 `tts.edge.speed`)→ 全局 `tts.speed` → `1.0` 默认值。设置全局 `tts.speed` 以在所有 provider 上应用统一速度,或按 provider 覆盖以进行精细控制。\n\n## 显示设置\n\n```yaml\ndisplay:\n tool_progress: all # off | new | all | verbose\n tool_progress_command: false # 在消息 gateway 中启用 /verbose 斜杠命令\n platforms: {} # 每平台显示覆盖(见下文)\n tool_progress_overrides: {} # 已弃用 —— 改用 display.platforms\n interim_assistant_messages: true # Gateway:将自然的轮次中 assistant 更新作为单独消息发送\n skin: default # 内置或自定义 CLI 皮肤(参阅 user-guide/features/skins)\n personality: \"kawaii\" # 旧版外观字段,仍在某些摘要中显示\n compact: false # 紧凑输出模式(减少空白)\n resume_display: full # full(恢复时显示之前的消息)| minimal(仅单行)\n bell_on_complete: false # 当 agent 完成时播放终端铃声(适合长任务)\n show_reasoning: false # 在每次响应上方显示模型推理/思考(用 /reasoning show|hide 切换)\n streaming: false # 将 token 实时流式传输到终端\n show_cost: false # 在 CLI 状态栏中显示估计 $ 成本\n timestamps: false # 为 true 时,在 CLI/TUI 记录中为用户和 assistant 标签添加 [HH:MM] 时间戳前缀\n tool_preview_length: 0 # 工具调用预览的最大字符数(0 = 无限制,显示完整路径/命令)\n runtime_footer: # Gateway:在最终回复中附加运行时上下文页脚\n enabled: false\n fields: [\"model\", \"context_pct\", \"cwd\"]\n file_mutation_verifier: true # 当本轮 write_file/patch 调用失败时附加建议性页脚\n language: en # 静态消息的 UI 语言(审批提示、部分 gateway 回复)。en | zh | zh-hant | ja | de | es | fr | tr | uk | af | ko | it | ga | pt | ru | hu\n```\n\n### 文件变更验证器\n\n当 `display.file_mutation_verifier` 为 `true`(默认)时,每当本轮中 `write_file` 或 `patch` 调用失败且从未被对同一路径的成功写入取代时,Hermes 会在 assistant 的最终响应中附加一行建议。这捕获了\"批量并行补丁,一半静默失败,模型总结成功\"这类过度声明,而无需您在每次编辑后手动运行 `git status`。\n\n示例页脚:\n\n```\n⚠️ File-mutation verifier: 3 file(s) were NOT modified this turn despite any wording above that may suggest otherwise. Run `git status` or `read_file` to confirm.\n • concepts/automatic-organization.md — [patch] Could not find match for old_string\n • concepts/lora.md — [patch] Could not find match for old_string\n • concepts/rag-pipeline.md — [patch] Could not find match for old_string\n```\n\n设置 `file_mutation_verifier: false`(或 `HERMES_FILE_MUTATION_VERIFIER=0`)以禁止页脚。验证器仅在轮次结束时有真实失败未解决时触发 —— 在同一轮次内重试失败补丁并成功的模型不会为该文件触发它。\n\n### 静态消息的 UI 语言\n\n`display.language` 设置翻译一小组静态面向用户的消息 —— CLI 审批提示、少数 gateway 斜杠命令回复(例如重启排空通知、\"审批已过期\"、\"目标已清除\")。它**不**翻译 agent 响应、日志行、工具输出、错误回溯或斜杠命令描述 —— 这些保持英文。如果您希望 agent 本身用另一种语言回复,只需在您的提示词或系统消息中告诉它。\n\n支持的值:`en`(默认)、`zh`(简体中文)、`zh-hant`(繁体中文)、`ja`(日语)、`de`(德语)、`es`(西班牙语)、`fr`(法语)、`tr`(土耳其语)、`uk`(乌克兰语)、`af`(南非荷兰语)、`ko`(韩语)、`it`(意大利语)、`ga`(爱尔兰语)、`pt`(葡萄牙语)、`ru`(俄语)、`hu`(匈牙利语)。未知值回退到英文。\n\n您也可以使用 `HERMES_LANGUAGE` 环境变量按会话设置,它会覆盖配置值。\n\n```yaml\ndisplay:\n language: zh # CLI 审批提示以中文显示\n```\n\n| 模式 | 您看到的内容 |\n|------|-------------|\n| `off` | 静默 —— 仅最终响应 |\n| `new` | 仅在工具更改时显示工具指示器 |\n| `all` | 每次工具调用附带简短预览(默认) |\n| `verbose` | 完整参数、结果和调试日志 |\n\n在 CLI 中,使用 `/verbose` 循环切换这些模式。要在消息平台(Telegram、Discord、Slack 等)中使用 `/verbose`,请在上方的 `display` 部分设置 `tool_progress_command: true`。该命令将循环切换模式并保存到配置。\n\n### 运行时元数据页脚(仅限 gateway)\n\n当 `display.runtime_footer.enabled: true` 时,Hermes 在每个 gateway 轮次的**最终**消息中附加一个小型运行时上下文页脚。目前页脚可显示模型、上下文窗口百分比和当前工作目录。默认关闭;如果您的团队希望每个回复都包含这些来源信息,请按 gateway 选择加入。\n\n```yaml\ndisplay:\n runtime_footer:\n enabled: true\n fields: [\"model\", \"context_pct\", \"cwd\"] # 支持字段:model、context_pct、cwd\n```\n\n`/footer` 斜杠命令在任何会话中运行时切换此功能。\n\n附加到 Telegram/Discord/Slack 回复的示例页脚:\n\n```\n— claude-opus-4.7 · 12 tool calls · 2m 14s · $0.042\n```\n\n只有轮次的**最终**消息获得页脚;中间更新保持干净。\n\n### 每平台进度覆盖\n\n不同平台有不同的详细程度需求。例如,Signal 无法编辑消息,因此每次进度更新都会成为单独的消息 —— 很嘈杂。使用 `display.platforms` 设置每平台模式:\n\n```yaml\ndisplay:\n tool_progress: all # 全局默认\n platforms:\n signal:\n tool_progress: 'off' # 在 Signal 上静默进度\n telegram:\n tool_progress: verbose # 在 Telegram 上详细进度\n slack:\n tool_progress: 'off' # 在共享 Slack 工作区中保持安静\n```\n\n没有覆盖的平台回退到全局 `tool_progress` 值。有效平台键:`telegram`、`discord`、`slack`、`signal`、`whatsapp`、`matrix`、`mattermost`、`email`、`sms`、`homeassistant`、`dingtalk`、`feishu`、`wecom`、`weixin`、`bluebubbles`、`qqbot`。旧版 `display.tool_progress_overrides` 键仍可加载以向后兼容,但已弃用,并在首次加载时迁移到 `display.platforms`。\n\n`interim_assistant_messages` 仅限 gateway。启用后,Hermes 将已完成的轮次中 assistant 更新作为单独的聊天消息发送。这与 `tool_progress` 无关,不需要 gateway 流式传输。\n\n## 隐私\n\n```yaml\nprivacy:\n redact_pii: false # 从 LLM 上下文中删除 PII(仅限 gateway)\n```\n\n当 `redact_pii` 为 `true` 时,gateway 在将系统提示词发送到受支持平台上的 LLM 之前,会从中删除个人身份信息:\n\n| 字段 | 处理方式 |\n|-------|-----------|\n| 电话号码(WhatsApp/Signal 上的用户 ID) | 哈希为 `user_<12-char-sha256>` |\n| 用户 ID | 哈希为 `user_<12-char-sha256>` |\n| 聊天 ID | 数字部分哈希,保留平台前缀(`telegram:<hash>`) |\n| 主频道 ID | 数字部分哈希 |\n| 用户名/昵称 | **不受影响**(用户选择的,公开可见) |\n\n**平台支持:** 删除适用于 WhatsApp、Signal 和 Telegram。Discord 和 Slack 被排除,因为它们的提及系统(`<@user_id>`)需要 LLM 上下文中的真实 ID。\n\n哈希是确定性的 —— 同一用户始终映射到同一哈希,因此模型仍然可以在群聊中区分用户。路由和传递在内部使用原始值。\n\n## 语音转文字(STT)\n\n```yaml\nstt:\n provider: \"local\" # \"local\" | \"groq\" | \"openai\" | \"mistral\"\n local:\n model: \"base\" # tiny、base、small、medium、large-v3\n openai:\n model: \"whisper-1\" # whisper-1 | gpt-4o-mini-transcribe | gpt-4o-transcribe\n # model: \"whisper-1\" # 旧版回退键仍受支持\n```\n\nProvider 行为:\n\n- `local` 使用在您机器上运行的 `faster-whisper`。使用 `pip install faster-whisper` 单独安装。\n- `groq` 使用 Groq 的 Whisper 兼容端点,读取 `GROQ_API_KEY`。\n- `openai` 使用 OpenAI 语音 API,读取 `VOICE_TOOLS_OPENAI_KEY`。\n\n如果请求的 provider 不可用,Hermes 按此顺序自动回退:`local` → `groq` → `openai`。\n\nGroq 和 OpenAI 模型覆盖由环境变量驱动:\n\n```bash\nSTT_GROQ_MODEL=whisper-large-v3-turbo\nSTT_OPENAI_MODEL=whisper-1\nGROQ_BASE_URL=https://api.groq.com/openai/v1\nSTT_OPENAI_BASE_URL=https://api.openai.com/v1\n```\n\n## 语音模式(CLI)\n\n```yaml\nvoice:\n record_key: \"ctrl+b\" # CLI 内的按键通话键\n max_recording_seconds: 120 # 长录音的硬停止\n auto_tts: false # /voice on 时自动启用口语回复\n beep_enabled: true # 在 CLI 语音模式中播放录音开始/停止提示音\n silence_threshold: 200 # 语音检测的 RMS 阈值\n silence_duration: 3.0 # 自动停止前的静默秒数\n```\n\n在 CLI 中使用 `/voice on` 启用麦克风模式,使用 `record_key` 开始/停止录音,使用 `/voice tts` 切换口语回复。端到端设置和平台特定行为请参阅[语音模式](/user-guide/features/voice-mode)。\n\n## 流式传输\n\n将 token 实时流式传输到终端或消息平台,而不是等待完整响应。\n\n### CLI 流式传输\n\n```yaml\ndisplay:\n streaming: true # 实时将 token 流式传输到终端\n show_reasoning: true # 同时流式传输推理/思考 token(可选)\n```\n\n启用后,响应在流式传输框内逐 token 出现。工具调用仍然静默捕获。如果 provider 不支持流式传输,它会自动回退到正常显示。\n\n### Gateway 流式传输(Telegram、Discord、Slack)\n\n```yaml\nstreaming:\n enabled: true # 启用渐进式消息编辑\n transport: edit # \"edit\"(渐进式消息编辑)或 \"off\"\n edit_interval: 0.3 # 消息编辑之间的秒数\n buffer_threshold: 40 # 强制编辑刷新前的字符数\n cursor: \" ▉\" # 流式传输期间显示的光标\n fresh_final_after_seconds: 0 # 预览超过此时间时选择发送新的最终消息(Telegram)\n```\n\n启用后,bot 在第一个 token 时发送消息,然后随着更多 token 到来渐进式编辑它。不支持消息编辑的平台(Signal、Email、Home Assistant)在第一次尝试时自动检测 —— 该会话的流式传输被优雅地禁用,不会产生大量消息。\n\n对于不带渐进式 token 编辑的独立自然轮次中 assistant 更新,请设置 `display.interim_assistant_messages: true`。\n\n**溢出处理:** 如果流式传输的文本超过平台的消息长度限制(约 4096 字符),当前消息被最终化,新消息自动开始。\n\n**新的最终消息(Telegram):** Telegram 的 `editMessageText` 保留原始消息时间戳,因此长时间运行的流式回复即使在完成后也会保留第一个 token 的时间戳。设置 `fresh_final_after_seconds > 0` 可选择将旧预览作为全新的最终消息传递,并尽力删除旧预览。默认值为 `0`,始终就地最终化流式回复,避免某些客户端短暂显示重复消息再删除其中一条。\n\n:::note\n主开关 `streaming.enabled` 默认为 `false`——在你启用之前不会有任何流式传输。启用后,是否流式传输按**平台**决定:Telegram 默认带有 `display.platforms.telegram.streaming: true`(流式传输),Discord 为 `display.platforms.discord.streaming: false`(不流式传输)。因此启用流式传输后,Telegram 开箱即用地流式传输,Discord 在你修改其开关之前仍使用整条消息回复。你可以在仪表盘的 **Channels** 开关中或直接在 `~/.hermes/config.yaml` 中调整这些按平台的开关。\n:::\n\n## 群聊会话隔离\n\n控制共享聊天是每个房间保持一个对话还是每个参与者一个对话:\n\n```yaml\ngroup_sessions_per_user: true # true = 群组/频道中每用户隔离,false = 每个聊天一个共享会话\n```\n\n- `true` 是默认和推荐设置。在 Discord 频道、Telegram 群组、Slack 频道和类似共享上下文中,当平台提供用户 ID 时,每个发送者获得自己的会话。\n- `false` 恢复到旧的共享房间行为。如果您明确希望 Hermes 将频道视为一个协作对话,这可能有用,但这也意味着用户共享上下文、token 成本和中断状态。\n- 私信不受影响。Hermes 仍然像往常一样通过聊天/DM ID 键入 DM。\n- 线程与其父频道保持隔离;使用 `true` 时,每个参与者在线程内也获得自己的会话。\n\n有关行为详情和示例,请参阅[会话](/user-guide/sessions)和 [Discord 指南](/user-guide/messaging/discord)。\n\n## 未授权 DM 行为\n\n控制当未知用户发送私信时 Hermes 的行为:\n\n```yaml\nunauthorized_dm_behavior: pair\n\nwhatsapp:\n unauthorized_dm_behavior: ignore\n```\n\n- `pair` 是默认值。Hermes 拒绝访问,但在 DM 中回复一次性配对码。\n- `ignore` 静默丢弃未授权的 DM。\n- 平台部分覆盖全局默认值,因此您可以在广泛范围内保持配对启用,同时使一个平台更安静。\n\n## 快速命令\n\n定义自定义命令,这些命令要么在不调用 LLM 的情况下运行 shell 命令,要么将一个斜杠命令别名为另一个。Exec 快速命令是零 token 的,对于从消息平台(Telegram、Discord 等)进行快速服务器检查或实用脚本很有用。\n\n```yaml\nquick_commands:\n status:\n type: exec\n command: systemctl status hermes-agent\n disk:\n type: exec\n command: df -h /\n update:\n type: exec\n command: cd ~/.hermes/hermes-agent && git pull && pip install -e .\n gpu:\n type: exec\n command: nvidia-smi --query-gpu=name,utilization.gpu,memory.used,memory.total --format=csv,noheader\n restart:\n type: alias\n target: /gateway restart\n```\n\n用法:在 CLI 或任何消息平台中输入 `/status`、`/disk`、`/update`、`/gpu` 或 `/restart`。`exec` 命令在宿主本地运行并直接返回输出 —— 无 LLM 调用,不消耗 token。`alias` 命令重写为配置的斜杠命令目标。\n\n- **30 秒超时** —— 长时间运行的命令被终止并显示错误消息\n- **优先级** —— 快速命令在技能命令之前检查,因此您可以覆盖技能名称\n- **自动补全** —— 快速命令在调度时解析,不显示在内置斜杠命令自动补全表中\n- **类型** —— 支持的类型为 `exec` 和 `alias`;其他类型显示错误\n- **到处可用** —— CLI、Telegram、Discord、Slack、WhatsApp、Signal、Email、Home Assistant\n\n仅字符串的 prompt 快捷方式不是有效的快速命令。对于可重用的 prompt 工作流,请创建技能或别名到现有斜杠命令。\n\n## 人类延迟\n\n在消息平台中模拟类人响应节奏:\n\n```yaml\nhuman_delay:\n mode: \"off\" # off | natural | custom\n min_ms: 800 # 最小延迟(自定义模式)\n max_ms: 2500 # 最大延迟(自定义模式)\n```\n\n## 代码执行\n\n配置 `execute_code` 工具:\n\n```yaml\ncode_execution:\n mode: project # project(默认)| strict\n timeout: 300 # 最大执行时间(秒)\n max_tool_calls: 50 # 代码执行中的最大工具调用次数\n```\n\n**`mode`** 控制脚本的工作目录和 Python 解释器:\n\n- **`project`**(默认)—— 脚本在会话的工作目录中以活跃 virtualenv/conda 环境的 python 运行。项目依赖(`pandas`、`torch`、项目包)和相对路径(`.env`、`./data.csv`)自然解析,与 `terminal()` 看到的一致。\n- **`strict`** —— 脚本在临时暂存目录中以 `sys.executable`(Hermes 自己的 python)运行。最大可重现性,但项目依赖和相对路径不会解析。\n\n环境清理(删除 `*_API_KEY`、`*_TOKEN`、`*_SECRET`、`*_PASSWORD`、`*_CREDENTIAL`、`*_PASSWD`、`*_AUTH`)和工具白名单在两种模式下完全相同 —— 切换模式不会改变安全态势。\n\n## Web 搜索后端\n\n`web_search` 和 `web_extract` 工具支持五种后端 provider。在 `config.yaml` 中或通过 `hermes tools` 配置后端:\n\n```yaml\nweb:\n backend: firecrawl # firecrawl | searxng | parallel | tavily | exa\n\n # 或使用每功能键混合 provider(例如免费搜索 + 付费提取):\n search_backend: \"searxng\"\n extract_backend: \"firecrawl\"\n```\n\n| 后端 | 环境变量 | 搜索 | 提取 |\n|---------|---------|--------|---------|\n| **Firecrawl**(默认) | `FIRECRAWL_API_KEY` | ✔ | ✔ |\n| **SearXNG** | `SEARXNG_URL` | ✔ | — |\n| **Parallel** | `PARALLEL_API_KEY` | ✔ | ✔ |\n| **Tavily** | `TAVILY_API_KEY` | ✔ | ✔ |\n| **Exa** | `EXA_API_KEY` | ✔ | ✔ |\n\n**后端选择:** 如果未设置 `web.backend`,后端从可用的 API 密钥自动检测。如果仅设置了 `SEARXNG_URL`,使用 SearXNG。如果仅设置了 `EXA_API_KEY`,使用 Exa。如果仅设置了 `TAVILY_API_KEY`,使用 Tavily。如果仅设置了 `PARALLEL_API_KEY`,使用 Parallel。否则 Firecrawl 是默认值。\n\n**SearXNG** 是一个免费、自托管、尊重隐私的元搜索引擎,查询 70+ 个搜索引擎。无需 API 密钥 —— 只需将 `SEARXNG_URL` 设置为您的实例(例如 `http://localhost:8080`)。SearXNG 仅限搜索;`web_extract` 需要单独的提取 provider(设置 `web.extract_backend`)。Docker 设置说明请参阅 [Web 搜索设置指南](/user-guide/features/web-search)。\n\n**自托管 Firecrawl:** 设置 `FIRECRAWL_API_URL` 指向您自己的实例。设置自定义 URL 后,API 密钥变为可选(在服务器上设置 `USE_DB_AUTHENTICATION=***` 以禁用认证)。\n\n**Parallel 搜索模式:** 设置 `PARALLEL_SEARCH_MODE` 控制搜索行为 —— `fast`、`one-shot` 或 `agentic`(默认:`agentic`)。\n\n**Exa:** 在 `~/.hermes/.env` 中设置 `EXA_API_KEY`。支持 `category` 过滤(`company`、`research paper`、`news`、`people`、`personal site`、`pdf`)和域名/日期过滤器。\n\n## 浏览器\n\n配置浏览器自动化行为:\n\n```yaml\nbrowser:\n inactivity_timeout: 120 # 自动关闭空闲会话前的秒数\n command_timeout: 30 # 浏览器命令超时(截图、导航等)(秒)\n record_sessions: false # 自动将浏览器会话录制为 WebM 视频到 ~/.hermes/browser_recordings/\n # 可选 CDP 覆盖 —— 设置后,Hermes 直接附加到您自己的\n # Chromium 系浏览器(通过 /browser connect),而不是启动无头浏览器。\n cdp_url: \"\"\n # 对话框监督器 —— 控制当 CDP 后端附加时(Browserbase、本地 Chromium 系\n # 浏览器通过 /browser connect)如何处理原生 JS 对话框(alert/confirm/prompt)。\n # 在 Camofox 和默认本地 agent 浏览器模式下忽略。\n dialog_policy: must_respond # must_respond | auto_dismiss | auto_accept\n dialog_timeout_s: 300 # must_respond 下的安全自动关闭(秒)\n camofox:\n managed_persistence: false # 为 true 时,Camofox 会话跨重启持久化 cookie/登录\n user_id: \"\" # 可选的外部管理 Camofox userId\n session_key: \"\" # Hermes 创建标签页时发送的可选会话密钥\n adopt_existing_tab: false # 在创建新标签页之前重用此身份的现有标签页\n```\n\n**对话框策略:**\n\n- `must_respond`(默认)—— 捕获对话框,在 `browser_snapshot.pending_dialogs` 中显示,等待 agent 调用 `browser_dialog(action=...)`。在 `dialog_timeout_s` 秒内无响应后,对话框被自动关闭以防止页面的 JS 线程永久停滞。\n- `auto_dismiss` —— 捕获,立即关闭。Agent 仍然在事后的 `browser_snapshot.recent_dialogs` 中看到对话框记录,`closed_by=\"auto_policy\"`。\n- `auto_accept` —— 捕获,立即接受。适用于有激进 `beforeunload` 提示的页面。\n\n完整对话框工作流请参阅[浏览器功能页面](./features/browser.md#browser_dialog)。\n\n浏览器工具集支持多个 provider。有关 Browserbase、Browser Use 和本地 Chromium 系 CDP 设置的详细信息,请参阅[浏览器功能页面](/user-guide/features/browser)。\n\n## 时区\n\n使用 IANA 时区字符串覆盖服务器本地时区。影响日志中的时间戳、cron 调度和系统提示词时间注入。\n\n```yaml\ntimezone: \"America/New_York\" # IANA 时区(默认:\"\" = 服务器本地时间)\n```\n\n支持的值:任何 IANA 时区标识符(例如 `America/New_York`、`Europe/London`、`Asia/Kolkata`、`UTC`)。留空或省略以使用服务器本地时间。\n\n## Discord\n\n为消息 gateway 配置 Discord 特定行为:\n\n```yaml\ndiscord:\n require_mention: true # 在服务器频道中需要 @提及才能响应\n free_response_channels: \"\" # 逗号分隔的频道 ID,bot 在这些频道无需 @提及即可响应\n auto_thread: true # 在频道中 @提及时自动创建线程\n```\n\n- `require_mention` —— 为 `true`(默认)时,bot 仅在服务器频道中被 `@BotName` 提及时响应。DM 始终无需提及即可工作。\n- `free_response_channels` —— 逗号分隔的频道 ID 列表,bot 在这些频道对每条消息响应,无需提及。\n- `auto_thread` —— 为 `true`(默认)时,频道中的提及会自动为对话创建线程,保持频道整洁(类似 Slack 线程)。\n\n## 安全\n\n预执行安全扫描和机密脱敏:\n\n```yaml\nsecurity:\n redact_secrets: false # 在工具输出和日志中脱敏 API 密钥模式(默认关闭)\n tirith_enabled: true # 为终端命令启用 Tirith 安全扫描\n tirith_path: \"tirith\" # tirith 二进制文件路径(默认:$PATH 中的 \"tirith\")\n tirith_timeout: 5 # 等待 tirith 扫描的秒数\n tirith_fail_open: true # 如果 tirith 不可用,允许命令执行\n website_blocklist: # 参见下方网站黑名单部分\n enabled: false\n domains: []\n shared_files: []\n```\n\n- `redact_secrets` —— 为 `true` 时,自动检测并脱敏工具输出中看起来像 API 密钥、token 和密码的模式,然后再进入对话上下文和日志。**默认关闭** —— 如果您经常在工具输出中处理真实凭据并希望有安全网,请启用。显式设置为 `true` 以开启。\n- `tirith_enabled` —— 为 `true` 时,终端命令在执行前由 [Tirith](https://github.com/sheeki03/tirith) 扫描以检测潜在危险操作。\n- `tirith_path` —— tirith 二进制文件的路径。如果 tirith 安装在非标准位置,请设置此项。\n- `tirith_timeout` —— 等待 tirith 扫描的最大秒数。如果扫描超时,命令继续执行。\n- `tirith_fail_open` —— 为 `true`(默认)时,如果 tirith 不可用或失败,允许命令执行。设置为 `false` 以在 tirith 无法验证时阻止命令。\n\n## 网站黑名单\n\n阻止 agent 的 web 和浏览器工具访问特定域名:\n\n```yaml\nsecurity:\n website_blocklist:\n enabled: false # 启用 URL 阻止(默认:false)\n domains: # 被阻止的域名模式列表\n - \"*.internal.company.com\"\n - \"admin.example.com\"\n - \"*.local\"\n shared_files: # 从外部文件加载额外规则\n - \"/etc/hermes/blocked-sites.txt\"\n```\n\n启用后,任何匹配被阻止域名模式的 URL 在 web 或浏览器工具执行之前都会被拒绝。这适用于 `web_search`、`web_extract`、`browser_navigate` 以及任何访问 URL 的工具。\n\n域名规则支持:\n- 精确域名:`admin.example.com`\n- 通配符子域名:`*.internal.company.com`(阻止所有子域名)\n- TLD 通配符:`*.local`\n\n共享文件每行包含一条域名规则(空行和 `#` 注释被忽略)。缺失或不可读的文件记录警告,但不禁用其他 web 工具。\n\n策略缓存 30 秒,因此配置更改无需重启即可快速生效。\n\n## 智能审批\n\n控制 Hermes 如何处理潜在危险命令:\n\n```yaml\napprovals:\n mode: manual # manual | smart | off\n```\n\n| 模式 | 行为 |\n|------|----------|\n| `manual`(默认) | 在执行任何被标记的命令之前提示用户。在 CLI 中显示交互式审批对话框。在消息中排队待处理的审批请求。 |\n| `smart` | 使用辅助 LLM 评估被标记的命令是否真正危险。低风险命令以会话级持久性自动批准。真正有风险的命令升级给用户。 |\n| `off` | 跳过所有审批检查。等同于 `HERMES_YOLO_MODE=true`。**谨慎使用。** |\n\n智能模式对于减少审批疲劳特别有用 —— 它让 agent 在安全操作上更自主地工作,同时仍然捕获真正破坏性的命令。\n\n:::warning\n设置 `approvals.mode: off` 会禁用终端命令的所有安全检查。仅在受信任的沙箱环境中使用。\n:::\n\n## 检查点\n\n破坏性文件操作之前的自动文件系统快照。详情请参阅[检查点与回滚](/user-guide/checkpoints-and-rollback)。\n\n```yaml\ncheckpoints:\n enabled: false # 启用自动检查点(也可:hermes chat --checkpoints)。默认:false(选择加入)。\n max_snapshots: 20 # 每个目录保留的最大检查点数(默认:20)\n```\n\n\n## 委托\n\n为委托工具配置子 agent 行为:\n\n```yaml\ndelegation:\n # model: \"google/gemini-3-flash-preview\" # 覆盖模型(空 = 继承父级)\n # provider: \"openrouter\" # 覆盖 provider(空 = 继承父级)\n # base_url: \"http://localhost:1234/v1\" # 直接 OpenAI 兼容端点(优先于 provider)\n # api_key: \"local-key\" # base_url 的 API 密钥(回退到 OPENAI_API_KEY)\n # api_mode: \"\" # base_url 的线路协议:\"chat_completions\"、\"codex_responses\" 或 \"anthropic_messages\"。空 = 从 URL 自动检测(例如 /anthropic 后缀 → anthropic_messages)。对启发式无法检测的非标准端点显式设置。\n max_concurrent_children: 3 # 每批并行子 agent 数(下限 1,无上限)。也可通过 DELEGATION_MAX_CONCURRENT_CHILDREN 环境变量设置。\n max_spawn_depth: 1 # 委托树深度上限(1-3,截断)。1 = 扁平(默认):父级生成无法委托的叶子。2 = 编排器子级可以生成叶子孙级。3 = 三级。\n orchestrator_enabled: true # 全局终止开关。为 false 时,role=\"orchestrator\" 被忽略,每个子级无论 max_spawn_depth 如何都被强制为叶子。\n```\n\n**子 agent provider:model 覆盖:** 默认情况下,子 agent 继承父 agent 的 provider 和模型。设置 `delegation.provider` 和 `delegation.model` 将子 agent 路由到不同的 provider:model 对 —— 例如,在您的主 agent 运行昂贵推理模型时,为范围较窄的子任务使用便宜/快速的模型。\n\n**直接端点覆盖:** 如果您想要明显的自定义端点路径,请设置 `delegation.base_url`、`delegation.api_key` 和 `delegation.model`。这将子 agent 直接发送到该 OpenAI 兼容端点,并优先于 `delegation.provider`。如果省略 `delegation.api_key`,Hermes 仅回退到 `OPENAI_API_KEY`。\n\n**线路协议(`api_mode`):** Hermes 从 `delegation.base_url` 自动检测线路协议(例如以 `/anthropic` 结尾的路径 → `anthropic_messages`;Codex/原生 Anthropic/Kimi-coding 主机名保留其现有检测)。对于启发式无法分类的端点 —— 例如 Azure AI Foundry、MiniMax、Zhipu GLM 或前置 Anthropic 形状后端的 LiteLLM 代理 —— 请将 `delegation.api_mode` 显式设置为 `chat_completions`、`codex_responses` 或 `anthropic_messages` 之一。留空(默认)以保持自动检测。\n\n委托 provider 使用与 CLI/gateway 启动相同的凭据解析。所有配置的 provider 均受支持:`openrouter`、`nous`、`copilot`、`zai`、`kimi-coding`、`minimax`、`minimax-cn`。设置 provider 时,系统自动解析正确的基础 URL、API 密钥和 API 模式 —— 无需手动凭据连接。\n\n**优先级:** 配置中的 `delegation.base_url` → 配置中的 `delegation.provider` → 父 provider(继承)。配置中的 `delegation.model` → 父模型(继承)。仅设置 `model` 而不设置 `provider` 仅更改模型名称,同时保留父级凭据(适用于在同一 provider(如 OpenRouter)内切换模型)。\n\n**宽度和深度:** `max_concurrent_children` 限制每批并行运行的子 agent 数量(默认 `3`,下限 1,无上限)。也可通过 `DELEGATION_MAX_CONCURRENT_CHILDREN` 环境变量设置。当模型提交的 `tasks` 数组超过上限时,`delegate_task` 返回工具错误解释限制,而不是静默截断。`max_spawn_depth` 控制委托树深度(截断到 1-3)。在默认 `1` 时,委托是扁平的:子级无法生成孙级,传递 `role=\"orchestrator\"` 静默降级为 `leaf`。提升到 `2` 使编排器子级可以生成叶子孙级;`3` 用于三级树。Agent 通过 `role=\"orchestrator\"` 按调用选择编排;`orchestrator_enabled: false` 强制每个子级回到叶子,无论如何。成本呈乘法增长 —— 在 `max_spawn_depth: 3` 和 `max_concurrent_children: 3` 时,树可以达到 3×3×3 = 27 个并发叶子 agent。使用模式请参阅[子 Agent 委托 → 深度限制和嵌套编排](features/delegation.md#depth-limit-and-nested-orchestration)。\n\n## 澄清\n\n配置澄清提示行为:\n\n```yaml\nclarify:\n timeout: 120 # 等待用户澄清响应的秒数\n```\n\n## 上下文文件(SOUL.md、AGENTS.md)\n\nHermes 使用两种不同的上下文范围:\n\n| 文件 | 用途 | 范围 |\n|------|---------|-------|\n| `SOUL.md` | **主要 agent 身份** —— 定义 agent 是谁(系统提示词第 #1 槽位) | `~/.hermes/SOUL.md` 或 `$HERMES_HOME/SOUL.md` |\n| `.hermes.md` / `HERMES.md` | 项目特定指令(最高优先级) | 向上走到 git 根目录 |\n| `AGENTS.md` | 项目特定指令、编码规范 | 递归目录遍历 |\n| `CLAUDE.md` | Claude Code 上下文文件(也会检测) | 仅工作目录 |\n| `.cursorrules` | Cursor IDE 规则(也会检测) | 仅工作目录 |\n| `.cursor/rules/*.mdc` | Cursor 规则文件(也会检测) | 仅工作目录 |\n\n- **SOUL.md** 是 agent 的主要身份。它占据系统提示词的第 #1 槽位,完全替换内置的默认身份。编辑它以完全自定义 agent 是谁。\n- 如果 SOUL.md 缺失、为空或无法加载,Hermes 回退到内置默认身份。\n- **项目上下文文件使用优先级系统** —— 仅加载一种类型(第一个匹配优先):`.hermes.md` → `AGENTS.md` → `CLAUDE.md` → `.cursorrules`。SOUL.md 始终独立加载。\n- **AGENTS.md** 是分层的:如果子目录也有 AGENTS.md,所有都会合并。\n- 如果 `SOUL.md` 不存在,Hermes 会自动生成默认的 `SOUL.md`。\n- 所有加载的上下文文件上限为 20,000 字符,并进行智能截断。\n\n另请参阅:\n- [个性与 SOUL.md](/user-guide/features/personality)\n- [上下文文件](/user-guide/features/context-files)\n\n## 工作目录\n\n| 上下文 | 默认值 |\n|---------|---------|\n| **CLI(`hermes`)** | 运行命令的当前目录 |\n| **消息 gateway** | 主目录 `~`(用 `MESSAGING_CWD` 覆盖) |\n| **Docker / Singularity / Modal / SSH** | 容器或远程机器内用户的主目录 |\n\n覆盖工作目录:\n```bash\n# 在 ~/.hermes/.env 或 ~/.hermes/config.yaml 中:\nMESSAGING_CWD=/home/myuser/projects # Gateway 会话\nTERMINAL_CWD=/workspace # 所有终端会话\n```\n"}, "7130d60861a9243301514bff611a9381830d59d8": {"t": 1785767991.9361944, "n": 56895, "h": "02774e845c3d63e1", "c": "---\nsidebar_position: 2\ntitle: \"配置\"\ndescription: \"配置 Hermes Agent — config.yaml、providers、模型、API 密钥等\"\n---\n\n# 配置\n\n所有设置均存储在 `~/.hermes/` 目录中,便于访问。\n\n## 目录结构\n\n```text\n~/.hermes/\n├── config.yaml # 设置(模型、终端、TTS、压缩等)\n├── .env # API 密钥和机密\n├── auth.json # OAuth provider 凭据(Nous Portal 等)\n├── SOUL.md # 主要 agent 身份(系统提示词第 #1 槽位)\n├── memories/ # 持久记忆(MEMORY.md、USER.md)\n├── skills/ # Agent 创建的技能(通过 skill_manage 工具管理)\n├── cron/ # 定时任务\n├── sessions/ # Gateway 会话\n└── logs/ # 日志(errors.log、gateway.log — 机密自动脱敏)\n```\n\n## 管理配置\n\n```bash\nhermes config # 查看当前配置\nhermes config edit # 在编辑器中打开 config.yaml\nhermes config set KEY VAL # 设置特定值\nhermes config check # 检查缺失选项(更新后使用)\nhermes config migrate # 交互式添加缺失选项\n\n# 示例:\nhermes config set model anthropic/claude-opus-4\nhermes config set terminal.backend docker\nhermes config set OPENROUTER_API_KEY sk-or-... # 保存到 .env\n```\n\n:::tip\n`hermes config set` 命令会自动将值路由到正确的文件 —— API 密钥保存到 `.env`,其他所有内容保存到 `config.yaml`。\n:::\n\n## 配置优先级\n\n设置按以下顺序解析(优先级从高到低):\n\n1. **CLI 参数** —— 例如 `hermes chat --model anthropic/claude-sonnet-4`(单次调用覆盖)\n2. **`~/.hermes/config.yaml`** —— 所有非机密设置的主配置文件\n3. **`~/.hermes/.env`** —— 环境变量的回退;机密(API 密钥、token、密码)**必须**放这里\n4. **内置默认值** —— 未设置任何内容时的硬编码安全默认值\n\n:::info 经验法则\n机密(API 密钥、bot token、密码)放入 `.env`。其他所有内容(模型、终端后端、压缩设置、内存限制、工具集)放入 `config.yaml`。当两者都设置时,`config.yaml` 对非机密设置优先。\n:::\n\n## 环境变量替换\n\n可以在 `config.yaml` 中使用 `${VAR_NAME}` 语法引用环境变量:\n\n```yaml\nauxiliary:\n vision:\n api_key: ${GOOGLE_API_KEY}\n base_url: ${CUSTOM_VISION_URL}\n\ndelegation:\n api_key: ${DELEGATION_KEY}\n```\n\n单个值中可以有多个引用:`url: \"${HOST}:${PORT}\"`。如果引用的变量未设置,占位符将保持原样(`${UNDEFINED_VAR}` 保持不变)。仅支持 `${VAR}` 语法 —— 裸 `$VAR` 不会被展开。\n\n关于 AI provider 设置(OpenRouter、Anthropic、Copilot、自定义端点、自托管 LLM、回退模型等),请参阅 [AI Providers](/integrations/providers)。\n\n### Provider 超时\n\n可以为 provider 设置 `providers.<id>.request_timeout_seconds` 作为全局请求超时,以及 `providers.<id>.models.<model>.timeout_seconds` 作为特定模型的覆盖值。适用于每种传输方式(OpenAI-wire、原生 Anthropic、Anthropic 兼容)上的主轮次客户端、回退链、凭据轮换后的重建,以及(对于 OpenAI-wire)每请求超时 kwarg —— 因此配置值优先于旧版 `HERMES_API_TIMEOUT` 环境变量。\n\n还可以设置 `providers.<id>.stale_timeout_seconds` 用于非流式陈旧调用检测器,以及 `providers.<id>.models.<model>.stale_timeout_seconds` 作为特定模型的覆盖值。此值优先于旧版 `HERMES_API_CALL_STALE_TIMEOUT` 环境变量。\n\n不设置这些值将保持旧版默认值(`HERMES_API_TIMEOUT=1800`s、`HERMES_API_CALL_STALE_TIMEOUT=90`s、原生 Anthropic 900s)。隐式的非流式 stale 检测会在本地端点上自动禁用,并且会在超大上下文下自动放宽。目前不适用于 AWS Bedrock(`bedrock_converse` 和 AnthropicBedrock SDK 路径均使用 boto3 及其自身的超时配置)。请参阅 [`cli-config.yaml.example`](https://github.com/NousResearch/hermes-agent/blob/main/cli-config.yaml.example) 中的注释示例。\n\n## 终端后端配置\n\nHermes 支持六种终端后端。每种后端决定 agent 的 shell 命令实际在哪里执行 —— 本地机器、Docker 容器、通过 SSH 的远程服务器、Modal 云沙箱(直接或通过 Nous 托管的 gateway)、Daytona 工作区,或 Singularity/Apptainer 容器。\n\n```yaml\nterminal:\n backend: local # local | docker | ssh | modal | daytona | singularity\n cwd: \".\" # Gateway/cron 工作目录(CLI 始终使用启动目录)\n timeout: 180 # 每条命令的超时时间(秒)\n env_passthrough: [] # 转发到沙箱执行的环境变量名(terminal + execute_code)\n singularity_image: \"docker://nikolaik/python-nodejs:python3.11-nodejs20\" # Singularity 后端的容器镜像\n modal_image: \"nikolaik/python-nodejs:python3.11-nodejs20\" # Modal 后端的容器镜像\n daytona_image: \"nikolaik/python-nodejs:python3.11-nodejs20\" # Daytona 后端的容器镜像\n```\n\n对于 Modal 和 Daytona 等云沙箱,`container_persistent: true` 表示 Hermes 将尝试在沙箱重建后保留文件系统状态。这并不保证相同的活跃沙箱、PID 空间或后台进程之后仍在运行。\n\n### 后端概览\n\n| 后端 | 命令运行位置 | 隔离性 | 最适合 |\n|---------|-------------------|-----------|----------|\n| **local** | 直接在您的机器上 | 无 | 开发、个人使用 |\n| **docker** | 单个持久 Docker 容器(跨会话、`/new`、子 agent 共享) | 完全(命名空间、cap-drop) | 安全沙箱、CI/CD |\n| **ssh** | 通过 SSH 的远程服务器 | 网络边界 | 远程开发、强大硬件 |\n| **modal** | Modal 云沙箱 | 完全(云 VM) | 临时云计算、评估 |\n| **daytona** | Daytona 工作区 | 完全(云容器) | 托管云开发环境 |\n| **singularity** | Singularity/Apptainer 容器 | 命名空间(--containall) | HPC 集群、共享机器 |\n\n### Local 后端\n\n默认后端。命令直接在您的机器上运行,无隔离。无需特殊设置。\n\n```yaml\nterminal:\n backend: local\n```\n\n:::warning\nAgent 拥有与您的用户账户相同的文件系统访问权限。使用 `hermes tools` 禁用不需要的工具,或切换到 Docker 进行沙箱隔离。\n:::\n\n### Docker 后端\n\n在具有安全加固的 Docker 容器内运行命令(所有权限已删除、无权限提升、PID 限制)。\n\n**单个持久容器,而非每条命令一个容器。** Hermes 在首次使用时启动一个长期运行的容器,并通过 `docker exec` 将每个终端、文件和 `execute_code` 调用路由到同一容器中 —— 跨会话、`/new`、`/reset` 和 `delegate_task` 子 agent,贯穿 Hermes 进程的整个生命周期。工作目录更改、已安装的包以及 `/workspace` 中的文件会从一次工具调用延续到下一次,就像本地 shell 一样。容器在关闭时停止并删除。详情请参阅下方的**容器生命周期**。\n\n```yaml\nterminal:\n backend: docker\n docker_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n docker_mount_cwd_to_workspace: false # 将启动目录挂载到 /workspace\n docker_run_as_host_user: false # 参见下方\"以宿主用户身份运行容器\"\n docker_forward_env: # 转发到容器的环境变量\n - \"GITHUB_TOKEN\"\n docker_volumes: # 宿主目录挂载\n - \"/home/user/projects:/workspace/projects\"\n - \"/home/user/data:/data:ro\" # :ro 表示只读\n docker_extra_args: # 附加到 `docker run` 的额外标志\n - \"--gpus=all\"\n - \"--network=host\"\n\n # 资源限制\n container_cpu: 1 # CPU 核心数(0 = 不限制)\n container_memory: 5120 # MB(0 = 不限制)\n container_disk: 51200 # MB(需要 XFS+pquota 上的 overlay2)\n container_persistent: true # 跨会话持久化 /workspace 和 /root\n```\n\n**`terminal.docker_extra_args`**(也可通过 `TERMINAL_DOCKER_EXTRA_ARGS='[\"--gpus=all\"]'` 覆盖)允许传递 Hermes 未作为一级键公开的任意 `docker run` 标志 —— `--gpus`、`--network`、`--add-host`、替代 `--security-opt` 覆盖等。每个条目必须是字符串;该列表最后附加到组装好的 `docker run` 调用中,因此可以在需要时覆盖 Hermes 的默认值。请谨慎使用 —— 与沙箱加固(权限删除、`--user`、workspace 绑定挂载)冲突的标志将悄然削弱隔离性。\n\n**要求:** 已安装并运行 Docker Desktop 或 Docker Engine。Hermes 会探测 `$PATH` 以及常见的 macOS 安装位置(`/usr/local/bin/docker`、`/opt/homebrew/bin/docker`、Docker Desktop 应用包)。开箱即用支持 Podman:设置 `HERMES_DOCKER_BINARY=podman`(或完整路径)以在两者都安装时强制使用它。\n\n**容器生命周期:** Hermes 为每个终端和文件工具调用重用单个长期运行的容器(`docker run -d ... sleep 2h`),跨会话、`/new`、`/reset` 和 `delegate_task` 子 agent,贯穿 Hermes 进程的整个生命周期。命令通过带登录 shell 的 `docker exec` 运行,因此工作目录更改、已安装的包以及 `/workspace` 中的文件都会从一次工具调用延续到下一次。容器在 Hermes 关闭时(或空闲清理回收时)停止并删除。\n\n通过 `delegate_task(tasks=[...])` 生成的并行子 agent 共享这一个容器 —— 并发的 `cd`、环境变量修改以及对同一路径的写入会发生冲突。如果子 agent 需要隔离的沙箱,必须通过 `register_task_env_overrides()` 注册每任务镜像覆盖,RL 和基准测试环境(TerminalBench2、HermesSweEnv 等)会自动为其每任务 Docker 镜像执行此操作。\n\n**安全加固:**\n- `--cap-drop ALL`,仅添加回 `DAC_OVERRIDE`、`CHOWN`、`FOWNER`\n- `--security-opt no-new-privileges`\n- `--pids-limit 256`\n- `/tmp`(512MB)、`/var/tmp`(256MB)、`/run`(64MB)的大小限制 tmpfs\n\n**凭据转发:** `docker_forward_env` 中列出的环境变量首先从您的 shell 环境解析,然后回退到 `~/.hermes/.env`。技能也可以声明 `required_environment_variables`,这些变量会自动合并。\n\n### SSH 后端\n\n通过 SSH 在远程服务器上运行命令。使用 ControlMaster 进行连接复用(5 分钟空闲保活)。默认启用持久 shell —— 状态(cwd、环境变量)在命令之间保持。\n\n```yaml\nterminal:\n backend: ssh\n persistent_shell: true # 保持长期运行的 bash 会话(默认:true)\n```\n\n**必需的环境变量:**\n\n```bash\nTERMINAL_SSH_HOST=my-server.example.com\nTERMINAL_SSH_USER=ubuntu\n```\n\n**可选:**\n\n| 变量 | 默认值 | 描述 |\n|----------|---------|-------------|\n| `TERMINAL_SSH_PORT` | `22` | SSH 端口 |\n| `TERMINAL_SSH_KEY` | (系统默认) | SSH 私钥路径 |\n| `TERMINAL_SSH_PERSISTENT` | `true` | 启用持久 shell |\n\n**工作原理:** 使用 `BatchMode=yes` 和 `StrictHostKeyChecking=accept-new` 在初始化时连接。持久 shell 在远程主机上保持单个 `bash -l` 进程存活,通过临时文件进行通信。需要 `stdin_data` 或 `sudo` 的命令会自动回退到单次模式。\n\n### Modal 后端\n\n在 [Modal](https://modal.com) 云沙箱中运行命令。每个任务获得一个具有可配置 CPU、内存和磁盘的隔离 VM。文件系统可以跨会话快照/恢复。\n\n```yaml\nterminal:\n backend: modal\n container_cpu: 1 # CPU 核心数\n container_memory: 5120 # MB(5GB)\n container_disk: 51200 # MB(50GB)\n container_persistent: true # 快照/恢复文件系统\n```\n\n**必需:** `MODAL_TOKEN_ID` + `MODAL_TOKEN_SECRET` 环境变量,或 `~/.modal.toml` 配置文件。\n\n**持久化:** 启用后,沙箱文件系统在清理时快照,并在下次会话时恢复。快照在 `~/.hermes/modal_snapshots.json` 中跟踪。这保留文件系统状态,而非活跃进程、PID 空间或后台任务。\n\n**凭据文件:** 自动从 `~/.hermes/` 挂载(OAuth token 等),并在每条命令前同步。\n\n### Daytona 后端\n\n在 [Daytona](https://daytona.io) 托管工作区中运行命令。支持停止/恢复以实现持久化。\n\n```yaml\nterminal:\n backend: daytona\n container_cpu: 1 # CPU 核心数\n container_memory: 5120 # MB → 转换为 GiB\n container_disk: 10240 # MB → 转换为 GiB(最大 10 GiB)\n container_persistent: true # 停止/恢复而非删除\n```\n\n**必需:** `DAYTONA_API_KEY` 环境变量。\n\n**持久化:** 启用后,沙箱在清理时停止(而非删除),并在下次会话时恢复。沙箱名称遵循 `hermes-{task_id}` 模式。\n\n**磁盘限制:** Daytona 强制执行 10 GiB 最大值。超过此值的请求将被截断并发出警告。\n\n### Singularity/Apptainer 后端\n\n在 [Singularity/Apptainer](https://apptainer.org) 容器中运行命令。专为 Docker 不可用的 HPC 集群和共享机器设计。\n\n```yaml\nterminal:\n backend: singularity\n singularity_image: \"docker://nikolaik/python-nodejs:python3.11-nodejs20\"\n container_cpu: 1 # CPU 核心数\n container_memory: 5120 # MB\n container_persistent: true # 可写覆盖层跨会话持久化\n```\n\n**要求:** `$PATH` 中有 `apptainer` 或 `singularity` 二进制文件。\n\n**镜像处理:** Docker URL(`docker://...`)自动转换为 SIF 文件并缓存。现有 `.sif` 文件直接使用。\n\n**临时目录:** 按顺序解析:`TERMINAL_SCRATCH_DIR` → `TERMINAL_SANDBOX_DIR/singularity` → `/scratch/$USER/hermes-agent`(HPC 惯例)→ `~/.hermes/sandboxes/singularity`。\n\n**隔离:** 使用 `--containall --no-home` 实现完全命名空间隔离,不挂载宿主 home 目录。\n\n### 常见终端后端问题\n\n如果终端命令立即失败或终端工具报告为已禁用:\n\n- **Local** —— 无特殊要求。入门时最安全的默认选项。\n- **Docker** —— 运行 `docker version` 验证 Docker 是否正常工作。如果失败,修复 Docker 或执行 `hermes config set terminal.backend local`。\n- **SSH** —— `TERMINAL_SSH_HOST` 和 `TERMINAL_SSH_USER` 都必须设置。如果缺少任一项,Hermes 会记录清晰的错误。\n- **Modal** —— 需要 `MODAL_TOKEN_ID` 环境变量或 `~/.modal.toml`。运行 `hermes doctor` 检查。\n- **Daytona** —— 需要 `DAYTONA_API_KEY`。Daytona SDK 处理服务器 URL 配置。\n- **Singularity** —— 需要 `$PATH` 中有 `apptainer` 或 `singularity`。HPC 集群上常见。\n\n如有疑问,将 `terminal.backend` 设回 `local` 并首先验证命令在那里运行。\n\n### 拆卸时远程到宿主文件同步\n\n对于 **SSH**、**Modal** 和 **Daytona** 后端(agent 的工作树位于与运行 Hermes 的宿主不同的机器上),Hermes 跟踪 agent 在远程沙箱中触及的文件,并在会话拆卸/沙箱清理时,将修改的文件**同步回宿主**,存放在 `~/.hermes/cache/remote-syncs/<session-id>/` 下。\n\n- 触发时机:会话关闭、`/new`、`/reset`、gateway 消息超时、子 agent 使用远程后端时 `delegate_task` 子 agent 完成。\n- 覆盖 agent 修改的整个树,而不仅仅是它明确打开的文件。添加、编辑和删除都会被捕获。\n- 远程沙箱可能在您查找时已被拆除;本地 `~/.hermes/cache/remote-syncs/…` 副本是 agent 更改内容的权威记录。\n- 大型二进制输出(模型检查点、原始数据集)按大小限制 —— 同步跳过超过 `file_sync_max_mb`(默认 `100`)的文件。如果您期望更大的工件返回,请调高该值。\n\n```yaml\nterminal:\n file_sync_max_mb: 100 # 默认 —— 同步最大 100 MB 的文件\n file_sync_enabled: true # 默认 —— 设为 false 可完全跳过同步\n```\n\n这是从会话结束后被销毁的临时云沙箱中恢复结果的方式,无需告诉 agent 显式地 `scp` 或 `modal volume put` 每个工件。\n\n### Docker 卷挂载\n\n使用 Docker 后端时,`docker_volumes` 允许您与容器共享宿主目录。每个条目使用标准 Docker `-v` 语法:`host_path:container_path[:options]`。\n\n```yaml\nterminal:\n backend: docker\n docker_volumes:\n - \"/home/user/projects:/workspace/projects\" # 读写(默认)\n - \"/home/user/datasets:/data:ro\" # 只读\n - \"/home/user/.hermes/cache/documents:/output\" # Gateway 可见的导出\n```\n\n适用于:\n- **向 agent 提供文件**(数据集、配置、参考代码)\n- **从 agent 接收文件**(生成的代码、报告、导出)\n- **共享工作区**,您和 agent 都访问相同的文件\n\n如果您使用消息 gateway 并希望 agent 通过 `MEDIA:/...` 发送生成的文件,建议使用专用的宿主可见导出挂载,例如 `/home/user/.hermes/cache/documents:/output`。\n\n- 在 Docker 中将文件写入 `/output/...`\n- 在 `MEDIA:` 中发出**宿主路径**,例如:`MEDIA:/home/user/.hermes/cache/documents/report.txt`\n- **不要**发出 `/workspace/...` 或 `/output/...`,除非该确切路径在宿主上对 gateway 进程也存在\n\n:::warning\nYAML 重复键会静默覆盖之前的键。如果您已有 `docker_volumes:` 块,请将新挂载合并到同一列表中,而不是在文件后面再添加一个 `docker_volumes:` 键。\n:::\n\n也可以通过环境变量设置:`TERMINAL_DOCKER_VOLUMES='[\"/host:/container\"]'`(JSON 数组)。\n\n### Docker 凭据转发\n\n默认情况下,Docker 终端会话不继承任意宿主凭据。如果您需要在容器内使用特定 token,请将其添加到 `terminal.docker_forward_env`。\n\n```yaml\nterminal:\n backend: docker\n docker_forward_env:\n - \"GITHUB_TOKEN\"\n - \"NPM_TOKEN\"\n```\n\nHermes 首先从您当前的 shell 解析每个列出的变量,然后回退到通过 `hermes config set` 保存的 `~/.hermes/.env`。\n\n:::warning\n`docker_forward_env` 中列出的任何内容都会对容器内运行的命令可见。只转发您愿意暴露给终端会话的凭据。\n:::\n\n### 以宿主用户身份运行容器\n\n默认情况下,Docker 容器以 `root`(UID 0)身份运行。在 `/workspace` 或其他绑定挂载中创建的文件在宿主上归 root 所有,因此会话结束后您必须 `sudo chown` 才能从宿主编辑器编辑它们。`terminal.docker_run_as_host_user` 标志解决了这个问题:\n\n```yaml\nterminal:\n backend: docker\n docker_run_as_host_user: true # 默认:false\n```\n\n启用后,Hermes 将 `--user $(id -u):$(id -g)` 附加到 `docker run` 命令,使写入绑定挂载目录(`/workspace`、`/root`、`docker_volumes` 中的任何内容)的文件归您的宿主用户所有,而非 root。权衡:容器将无法再 `apt install` 或写入 `/root/.npm` 等 root 拥有的路径 —— 如果您同时需要这两者,请使用 `HOME` 归非 root 用户所有的基础镜像(或在镜像构建时添加所需工具)。\n\n保持 `false`(默认)以获得向后兼容的行为。当您的工作流主要是\"编辑挂载的宿主文件\"且厌倦了 `sudo chown -R` 时,请开启此选项。\n\n### 可选:将启动目录挂载到 `/workspace`\n\nDocker 沙箱默认保持隔离。Hermes **不会**将您当前的宿主工作目录传入容器,除非您明确选择加入。\n\n在 `config.yaml` 中启用:\n\n```yaml\nterminal:\n backend: docker\n docker_mount_cwd_to_workspace: true\n```\n\n启用后:\n- 如果您从 `~/projects/my-app` 启动 Hermes,该宿主目录将绑定挂载到 `/workspace`\n- Docker 后端从 `/workspace` 开始\n- 文件工具和终端命令都能看到相同的挂载项目\n\n禁用时,`/workspace` 保持沙箱所有,除非您通过 `docker_volumes` 显式挂载内容。\n\n安全权衡:\n- `false` 保留沙箱边界\n- `true` 使沙箱直接访问您启动 Hermes 的目录\n\n仅在您有意希望容器处理实时宿主文件时才选择加入。\n\n### 持久 Shell\n\n默认情况下,每条终端命令在其自己的子进程中运行 —— 工作目录、环境变量和 shell 变量在命令之间重置。启用**持久 shell** 后,单个长期运行的 bash 进程在 `execute()` 调用之间保持存活,使状态在命令之间保持。\n\n这对 **SSH 后端**最有用,它还消除了每条命令的连接开销。持久 shell **对 SSH 默认启用**,对本地后端禁用。\n\n```yaml\nterminal:\n persistent_shell: true # 默认 —— 为 SSH 启用持久 shell\n```\n\n禁用:\n\n```bash\nhermes config set terminal.persistent_shell false\n```\n\n**跨命令保持的内容:**\n- 工作目录(`cd /tmp` 对下一条命令生效)\n- 导出的环境变量(`export FOO=bar`)\n- Shell 变量(`MY_VAR=hello`)\n\n**优先级:**\n\n| 级别 | 变量 | 默认值 |\n|-------|----------|---------|\n| 配置 | `terminal.persistent_shell` | `true` |\n| SSH 覆盖 | `TERMINAL_SSH_PERSISTENT` | 遵循配置 |\n| Local 覆盖 | `TERMINAL_LOCAL_PERSISTENT` | `false` |\n\n每个后端的环境变量具有最高优先级。如果您也想在本地后端使用持久 shell:\n\n```bash\nexport TERMINAL_LOCAL_PERSISTENT=true\n```\n\n:::note\n需要 `stdin_data` 或 sudo 的命令会自动回退到单次模式,因为持久 shell 的 stdin 已被 IPC 协议占用。\n:::\n\n有关每个后端的详细信息,请参阅[代码执行](features/code-execution.md)和 [README 的终端部分](features/tools.md)。\n\n## 技能设置\n\n技能可以通过其 SKILL.md frontmatter 声明自己的配置设置。这些是非机密值(路径、偏好、域设置),存储在 `config.yaml` 的 `skills.config` 命名空间下。\n\n```yaml\nskills:\n config:\n myplugin:\n path: ~/myplugin-data # 示例 —— 每个技能定义自己的键\n```\n\n**技能设置的工作原理:**\n\n- `hermes config migrate` 扫描所有已启用的技能,找到未配置的设置,并提供提示\n- `hermes config show` 在\"技能设置\"下显示所有技能设置及其所属技能\n- 技能加载时,其解析的配置值会自动注入到技能上下文中\n\n**手动设置值:**\n\n```bash\nhermes config set skills.config.myplugin.path ~/myplugin-data\n```\n\n有关在您自己的技能中声明配置设置的详细信息,请参阅[创建技能 — 配置设置](/developer-guide/creating-skills#config-settings-configyaml)。\n\n### Agent 创建技能写入的守卫\n\n当 agent 使用 `skill_manage` 创建、编辑、修补或删除技能时,Hermes 可以选择扫描新/更新的内容以查找危险关键字模式(凭据收集、明显的 prompt 注入、数据外泄指令)。扫描器**默认关闭** —— 合法触及 `~/.ssh/` 或提及 `$OPENAI_API_KEY` 的真实 agent 工作流触发启发式规则过于频繁。如果您希望扫描器在 agent 的技能写入落地前提示您,请重新开启:\n\n```yaml\nskills:\n guard_agent_created: true # 默认:false\n```\n\n开启后,任何被标记的 `skill_manage` 写入都会以审批提示的形式出现,并附带扫描器的理由。接受的写入落地;拒绝的写入向 agent 返回解释性错误。\n\n## 内存配置\n\n```yaml\nmemory:\n memory_enabled: true\n user_profile_enabled: true\n memory_char_limit: 2200 # ~800 tokens\n user_char_limit: 1375 # ~500 tokens\n```\n\n## 文件读取安全\n\n控制单次 `read_file` 调用可以返回多少内容。超过限制的读取将被拒绝,并向 agent 返回错误,提示使用 `offset` 和 `limit` 读取较小范围。这可以防止单次读取压缩的 JS 包或大型数据文件时淹没上下文窗口。\n\n```yaml\nfile_read_max_chars: 100000 # 默认 —— ~25-35K tokens\n```\n\n如果您使用具有大上下文窗口的模型并经常读取大文件,请调高此值。对于小上下文模型,请降低以保持读取高效:\n\n```yaml\n# 大上下文模型(200K+)\nfile_read_max_chars: 200000\n\n# 小型本地模型(16K 上下文)\nfile_read_max_chars: 30000\n```\n\nAgent 还会自动去重文件读取 —— 如果同一文件区域被读取两次且文件未更改,则返回轻量级存根而不是重新发送内容。这在上下文压缩后重置,以便 agent 在内容被摘要后可以重新读取文件。\n\n## 工具输出截断限制\n\n三个相关的上限控制工具在 Hermes 截断之前可以返回多少原始输出:\n\n```yaml\ntool_output:\n max_bytes: 50000 # 终端输出上限(字符)\n max_lines: 2000 # read_file 分页上限\n max_line_length: 2000 # read_file 行号视图中的每行上限\n```\n\n- **`max_bytes`** —— 当 `terminal` 命令产生超过此字符数的合并 stdout/stderr 时,Hermes 保留前 40% 和后 60%,并在中间插入 `[OUTPUT TRUNCATED]` 通知。默认 `50000`(典型分词器约 12-15K tokens)。\n- **`max_lines`** —— 单次 `read_file` 调用的 `limit` 参数上限。超过此值的请求将被截断,以防单次读取淹没上下文窗口。默认 `2000`。\n- **`max_line_length`** —— `read_file` 发出行号视图时应用的每行上限。超过此长度的行将被截断为此字符数,后跟 `... [truncated]`。默认 `2000`。\n\n对于具有大上下文窗口且每次调用可以承受更多原始输出的模型,请调高限制。对于小上下文模型,请降低以保持工具结果紧凑:\n\n```yaml\n# 大上下文模型(200K+)\ntool_output:\n max_bytes: 150000\n max_lines: 5000\n\n# 小型本地模型(16K 上下文)\ntool_output:\n max_bytes: 20000\n max_lines: 500\n```\n\n## 全局工具集禁用\n\n要在 CLI 和每个 gateway 平台上统一禁用特定工具集,请在 `agent.disabled_toolsets` 下列出其名称:\n\n```yaml\nagent:\n disabled_toolsets:\n - memory # 隐藏内存工具 + MEMORY_GUIDANCE 注入\n - web # 任何地方都不使用 web_search / web_extract\n```\n\n这在每个平台的工具配置(由 `hermes tools` 写入的 `platform_toolsets`)**之后**应用,因此此处列出的工具集始终被删除 —— 即使平台的已保存配置仍然列出它。当您希望有一个\"到处关闭 X\"的单一开关而不是编辑 `hermes tools` UI 中的 15+ 个平台行时,请使用此选项。\n\n留空列表或省略键不会产生任何效果。\n\n## Git Worktree 隔离\n\n启用隔离的 git worktree,以便在同一仓库上并行运行多个 agent:\n\n```yaml\nworktree: true # 始终创建 worktree(与 hermes -w 相同)\n# worktree: false # 默认 —— 仅在传递 -w 标志时\n```\n\n启用后,每个 CLI 会话在 `.worktrees/` 下创建一个带有自己分支的新 worktree。Agent 可以编辑文件、提交、推送和创建 PR,而不会相互干扰。干净的 worktree 在退出时删除;脏的 worktree 保留以供手动恢复。\n\n您还可以通过仓库根目录中的 `.worktreeinclude` 列出要复制到 worktree 的 gitignore 文件:\n\n```\n# .worktreeinclude\n.env\n.venv/\nnode_modules/\n```\n\n## 上下文压缩\n\nHermes 自动压缩长对话以保持在模型的上下文窗口内。压缩摘要器是一个单独的 LLM 调用 —— 您可以将其指向任何 provider 或端点。\n\n所有压缩设置都在 `config.yaml` 中(无环境变量)。\n\n### 完整参考\n\n```yaml\ncompression:\n enabled: true # 开启/关闭压缩\n threshold: 0.50 # 在上下文限制的此百分比时压缩\n target_ratio: 0.20 # 保留为最近尾部的阈值分数\n protect_last_n: 20 # 保持未压缩的最少最近消息数\n hygiene_hard_message_limit: 5000 # Gateway 安全阀 —— 见下文\n\n# 摘要模型/provider 在 auxiliary: 下配置:\nauxiliary:\n compression:\n model: \"\" # 空 = 使用主聊天模型。覆盖为例如 \"google/gemini-3-flash-preview\" 以获得更便宜/更快的压缩。\n provider: \"auto\" # Provider:\"auto\"、\"openrouter\"、\"nous\"、\"codex\"、\"main\" 等\n base_url: null # 自定义 OpenAI 兼容端点(覆盖 provider)\n```\n\n:::info 旧版配置迁移\n带有 `compression.summary_model`、`compression.summary_provider` 和 `compression.summary_base_url` 的旧版配置在首次加载时自动迁移到 `auxiliary.compression.*`(配置版本 17)。无需手动操作。\n:::\n\n`hygiene_hard_message_limit` 是仅限 gateway 的**预压缩安全阀**。它的存在是为了打破一个死循环:当超大会话的 API 调用持续断开时,gateway 永远收不到 token 使用数据,基于 token 的阈值因此无法触发,于是 transcript 持续增长、断开愈发严重。这个基于消息数的下限仅凭消息数量触发(无论 API 是否失败,消息数始终已知),强制压缩以恢复会话。默认 `5000` —— 远高于任何正常会话,包括做数千次短轮次的大上下文(1M+)模型,它们早就在 token 阈值处压缩了。对于异常平台可调得更高;要强制更积极的压缩则调低。在运行中的 gateway 上编辑此值将在下一条消息时生效(见下文)。\n\n:::tip Gateway 热重载压缩和上下文长度\n从最近的版本开始,在运行中的 gateway 上编辑 `config.yaml` 中的 `model.context_length` 或任何 `compression.*` 键将在下一条消息时生效 —— 无需 gateway 重启、`/reset` 或会话轮换。缓存的 agent 签名包含这些键,因此 gateway 在检测到更改时会透明地重建 agent。API 密钥和工具/技能配置仍需要通常的重载路径。\n:::\n\n### 常见设置\n\n**默认(自动检测)—— 无需配置:**\n```yaml\ncompression:\n enabled: true\n threshold: 0.50\n```\n使用您的主 provider 和主模型。如果您希望在比主聊天模型更便宜的模型上进行压缩,请覆盖每任务(例如 `auxiliary.compression.provider: openrouter` + `model: google/gemini-2.5-flash`)。\n\n**强制特定 provider**(基于 OAuth 或 API 密钥):\n```yaml\nauxiliary:\n compression:\n provider: nous\n model: gemini-3-flash\n```\n适用于任何 provider:`nous`、`openrouter`、`codex`、`anthropic`、`main` 等。\n\n**自定义端点**(自托管、Ollama、zai、DeepSeek 等):\n```yaml\nauxiliary:\n compression:\n model: glm-4.7\n base_url: https://api.z.ai/api/coding/paas/v4\n```\n指向自定义 OpenAI 兼容端点。使用 `OPENAI_API_KEY` 进行认证。\n\n### 三个旋钮的交互方式\n\n| `auxiliary.compression.provider` | `auxiliary.compression.base_url` | 结果 |\n|---------------------|---------------------|--------|\n| `auto`(默认) | 未设置 | 自动检测最佳可用 provider |\n| `nous` / `openrouter` / 等 | 未设置 | 强制使用该 provider,使用其认证 |\n| 任意 | 已设置 | 直接使用自定义端点(忽略 provider) |\n\n:::warning 摘要模型上下文长度要求\n摘要模型**必须**具有至少与您的主 agent 模型一样大的上下文窗口。压缩器将对话的完整中间部分发送给摘要模型 —— 如果该模型的上下文窗口小于主模型的,摘要调用将因上下文长度错误而失败。发生这种情况时,中间轮次将**在没有摘要的情况下被丢弃**,静默丢失对话上下文。如果您覆盖模型,请验证其上下文长度满足或超过您的主模型。\n:::\n\n## 上下文引擎\n\n上下文引擎控制在接近模型 token 限制时如何管理对话。内置的 `compressor` 引擎使用有损摘要(参见[上下文压缩](/developer-guide/context-compression-and-caching))。插件引擎可以用替代策略替换它。\n\n```yaml\ncontext:\n engine: \"compressor\" # 默认 —— 内置有损摘要\n```\n\n使用插件引擎(例如,用于无损上下文管理的 LCM):\n\n```yaml\ncontext:\n engine: \"lcm\" # 必须与插件名称匹配\n```\n\n插件引擎**永远不会自动激活** —— 您必须将 `context.engine` 显式设置为插件名称。可用引擎可以通过 `hermes plugins` → Provider Plugins → Context Engine 浏览和选择。\n\n有关内存插件的类似单选系统,请参阅[内存 Providers](/user-guide/features/memory-providers)。\n\n## 迭代预算压力\n\n当 agent 在处理具有许多工具调用的复杂任务时,它可能会在没有意识到预算不足的情况下耗尽其迭代预算(默认:90 轮)。预算压力会在模型接近限制时自动发出警告:\n\n| 阈值 | 级别 | 模型看到的内容 |\n|-----------|-------|---------------------|\n| **70%** | 注意 | `[BUDGET: 63/90. 27 iterations left. Start consolidating.]` |\n| **90%** | 警告 | `[BUDGET WARNING: 81/90. Only 9 left. Respond NOW.]` |\n\n警告注入到最后一个工具结果的 JSON 中(作为 `_budget_warning` 字段),而不是作为单独的消息 —— 这保留了 prompt 缓存,不会破坏对话结构。\n\n```yaml\nagent:\n max_turns: 90 # 每次对话轮次的最大迭代次数(默认:90)\n api_max_retries: 3 # 回退启动前每个 provider 的重试次数(默认:3)\n```\n\n预算压力默认启用。Agent 自然地将警告视为工具结果的一部分,鼓励它在耗尽迭代之前整合工作并提供响应。\n\n当迭代预算完全耗尽时,CLI 向用户显示通知:`⚠ Iteration budget reached (90/90) — response may be incomplete`。如果预算在活跃工作期间耗尽,agent 会在停止前生成已完成内容的摘要。\n\n`agent.api_max_retries` 控制 Hermes 在回退 provider 切换启动**之前**对瞬时错误(速率限制、连接断开、5xx)重试 provider API 调用的次数。默认为 `3` —— 总共四次尝试。如果您配置了[回退 providers](/user-guide/features/fallback-providers) 并希望更快地故障转移,请将其降至 `0`,这样主 provider 上的第一个瞬时错误会立即切换到回退,而不是对不稳定的端点进行重试。\n\n### API 超时\n\nHermes 对流式传输有单独的超时层,以及用于非流式调用的陈旧检测器。陈旧检测器仅在您将其保留为隐式默认值时才会自动调整本地 provider。\n\n| 超时 | 默认值 | 本地 providers | 配置/环境变量 |\n|---------|---------|----------------|--------------|\n| Socket 读取超时 | 120s | 自动提升至 1800s | `HERMES_STREAM_READ_TIMEOUT` |\n| 陈旧流检测 | 180s | 自动禁用 | `HERMES_STREAM_STALE_TIMEOUT` |\n| 陈旧非流检测 | 300s | 保持隐式时自动禁用 | `providers.<id>.stale_timeout_seconds` 或 `HERMES_API_CALL_STALE_TIMEOUT` |\n| API 调用(非流式) | 1800s | 不变 | `providers.<id>.request_timeout_seconds` / `timeout_seconds` 或 `HERMES_API_TIMEOUT` |\n\n**Socket 读取超时**控制 httpx 等待 provider 下一个数据块的时间。本地 LLM 在大上下文上预填充可能需要几分钟才能产生第一个 token,因此当 Hermes 检测到本地端点时,会将此值提升至 30 分钟。如果您显式设置 `HERMES_STREAM_READ_TIMEOUT`,无论端点检测如何,始终使用该值。\n\n**陈旧流检测**终止接收 SSE 保活 ping 但没有实际内容的连接。对于本地 providers,这完全禁用,因为它们在预填充期间不发送保活 ping。\n\n**陈旧非流检测**终止长时间没有响应的非流式调用。默认情况下,Hermes 在本地端点上禁用此功能,以避免长时间预填充期间的误报。如果您显式设置 `providers.<id>.stale_timeout_seconds`、`providers.<id>.models.<model>.stale_timeout_seconds` 或 `HERMES_API_CALL_STALE_TIMEOUT`,即使在本地端点上也会遵守该显式值。\n\n## 上下文压力警告\n\n与迭代预算压力分开,上下文压力跟踪对话距**压缩阈值**有多近 —— 即上下文压缩触发以摘要旧消息的点。这有助于您和 agent 了解对话何时变长。\n\n| 进度 | 级别 | 发生的事情 |\n|----------|-------|-------------|\n| **≥ 60%** 到阈值 | 信息 | CLI 显示青色进度条;gateway 发送信息通知 |\n| **≥ 85%** 到阈值 | 警告 | CLI 显示粗体黄色进度条;gateway 警告压缩即将发生 |\n\n在 CLI 中,上下文压力在工具输出流中显示为进度条:\n\n```\n ◐ context ████████████░░░░░░░░ 62% to compaction 48k threshold (50%) · approaching compaction\n```\n\n在消息平台上,发送纯文本通知:\n\n```\n◐ Context: ████████████░░░░░░░░ 62% to compaction (threshold: 50% of window).\n```\n\n如果自动压缩被禁用,警告会告诉您上下文可能被截断。\n\n上下文压力是自动的 —— 无需配置。它纯粹作为面向用户的通知触发,不修改消息流或向模型上下文注入任何内容。\n\n## 凭据池策略\n\n当您为同一 provider 拥有多个 API 密钥或 OAuth token 时,配置轮换策略:\n\n```yaml\ncredential_pool_strategies:\n openrouter: round_robin # 均匀循环使用密钥\n anthropic: least_used # 始终选择使用最少的密钥\n```\n\n选项:`fill_first`(默认)、`round_robin`、`least_used`、`random`。完整文档请参阅[凭据池](/user-guide/features/credential-pools)。\n\n## Prompt 缓存\n\n当活跃 provider 支持时,Hermes 自动开启跨会话 prompt 缓存 —— 无需用户配置。\n\n对于**原生 Anthropic**、**OpenRouter** 和 **Nous Portal** 上的 Claude,Hermes 在系统提示词和技能块上附加带有 1 小时 TTL(`ttl: \"1h\"`)的 `cache_control` 断点。在新鲜的一小时内首次发送时按完整输入费率计费;同一小时内任何会话的后续发送以折扣缓存读取费率从缓存中提取。这意味着系统提示词、加载的技能内容以及任何长上下文包含的早期部分在第一个小时内跨 `hermes` 会话和分叉子 agent 被重用。\n\nQwen Cloud(阿里巴巴 DashScope)上游将缓存 TTL 限制为 5 分钟,因此 Hermes 在那里使用 5 分钟断点 TTL。其他通过第三方的 Claude 路径(AWS Bedrock、Azure Foundry)回退到 provider 自己的缓存默认值。xAI Grok 使用单独的会话固定对话 ID 机制 —— 参阅 [xAI prompt 缓存](/integrations/providers#xai-grok--responses-api--prompt-caching)。\n\n不存在禁用此功能的旋钮 —— 缓存始终开启,即使在单轮对话中也能节省费用,因为仅系统提示词就占输入 token 数的相当大比例。\n\n## 辅助模型\n\nHermes 使用\"辅助\"模型处理图像分析、网页摘要、浏览器截图分析、会话标题生成和上下文压缩等附带任务。默认情况下(`auxiliary.*.provider: \"auto\"`),Hermes 将每个辅助任务路由到您的**主聊天模型** —— 与您在 `hermes model` 中选择的相同 provider/模型。您无需配置任何内容即可开始,但请注意,在昂贵的推理模型(Opus、MiniMax M2.7 等)上,辅助任务会增加显著成本。如果您希望无论主模型如何都使用便宜且快速的附带任务,请显式设置 `auxiliary.<task>.provider` 和 `auxiliary.<task>.model`(例如,在 OpenRouter 上使用 Gemini Flash 进行视觉和网页提取)。\n\n:::note 为什么 \"auto\" 使用您的主模型\n早期版本将聚合器用户(OpenRouter、Nous Portal)分流到便宜的 provider 端默认值。这令人惊讶 —— 付费购买聚合器订阅的用户会看到不同的模型处理其辅助流量。`auto` 现在对所有人使用主模型,`config.yaml` 中的每任务覆盖仍然优先(见下方[完整辅助配置参考](#full-auxiliary-config-reference))。\n:::\n\n### 交互式配置辅助模型\n\n无需手动编辑 YAML,运行 `hermes model` 并从菜单中选择**\"配置辅助模型\"**。您将获得交互式的每任务选择器:\n\n```\n$ hermes model\n→ Configure auxiliary models\n\n[ ] vision currently: auto / main model\n[ ] web_extract currently: auto / main model\n[ ] title_generation currently: openrouter / google/gemini-3-flash-preview\n[ ] compression currently: auto / main model\n[ ] approval currently: auto / main model\n[ ] triage_specifier currently: auto / main model\n[ ] kanban_decomposer currently: auto / main model\n[ ] profile_describer currently: auto / main model\n```\n\n选择任务,选择 provider(OAuth 流程打开浏览器;API 密钥 provider 提示输入),选择模型。更改持久化到 `config.yaml` 中的 `auxiliary.<task>.*`。与主模型选择器相同的机制 —— 无需学习额外语法。\n\n### 视频教程\n\n<div style={{position: 'relative', width: '100%', aspectRatio: '16 / 9', marginBottom: '1.5rem'}}>\n <iframe\n src=\"https://www.youtube.com/embed/NoF-YajElIM\"\n title=\"Hermes Agent — Auxiliary Models Tutorial\"\n style={{position: 'absolute', top: 0, left: 0, width: '100%', height: '100%', border: 0}}\n allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\"\n allowFullScreen\n />\n</div>\n\n### 通用配置模式\n\nHermes 中的每个模型槽位 —— 辅助任务、压缩、回退 —— 使用相同的三个旋钮:\n\n| 键 | 作用 | 默认值 |\n|-----|-------------|---------|\n| `provider` | 用于认证和路由的 provider | `\"auto\"` |\n| `model` | 请求的模型 | provider 的默认值 |\n| `base_url` | 自定义 OpenAI 兼容端点(覆盖 provider) | 未设置 |\n\n当设置 `base_url` 时,Hermes 忽略 provider 并直接调用该端点(使用 `api_key` 或 `OPENAI_API_KEY` 进行认证)。当仅设置 `provider` 时,Hermes 使用该 provider 的内置认证和基础 URL。\n\n辅助任务的可用 providers:`auto`、`main`,以及[provider 注册表](/reference/environment-variables)中的任何 provider —— `openrouter`、`nous`、`openai-codex`、`copilot`、`copilot-acp`、`anthropic`、`gemini`、`qwen-oauth`、`zai`、`kimi-coding`、`kimi-coding-cn`、`minimax`、`minimax-cn`、`minimax-oauth`、`deepseek`、`nvidia`、`xai`、`xai-oauth`、`ollama-cloud`、`alibaba`、`bedrock`、`huggingface`、`arcee`、`xiaomi`、`kilocode`、`opencode-zen`、`opencode-go`、`azure-foundry` —— 或您 `custom_providers` 列表中任何命名的自定义 provider(例如 `provider: \"beans\"`)。\n\n:::tip MiniMax OAuth\n`minimax-oauth` 通过浏览器 OAuth 登录(无需 API 密钥)。运行 `hermes model` 并选择 **MiniMax (OAuth)** 进行认证。辅助任务自动使用 `MiniMax-M2.7-highspeed`。参阅 [MiniMax OAuth 指南](../guides/minimax-oauth.md)。\n:::\n\n:::tip xAI Grok OAuth\n`xai-oauth` 通过浏览器 OAuth 为 SuperGrok 和 X Premium+ 订阅者登录(无需 API 密钥)。运行 `hermes model` 并选择 **xAI Grok OAuth (SuperGrok / Premium+)** 进行认证。相同的 OAuth token 可重用于每个直接到 xAI 的接口(聊天、辅助任务、TTS、图像生成、视频生成、转录)。参阅 [xAI Grok OAuth 指南](../guides/xai-grok-oauth.md),如果 Hermes 在远程主机上,请参阅 [SSH/远程主机上的 OAuth](../guides/oauth-over-ssh.md)。\n:::\n\n:::warning `\"main\"` 仅用于辅助任务\n`\"main\"` provider 选项表示\"使用我的主 agent 使用的任何 provider\" —— 它仅在 `auxiliary:`、`compression:` 和 `fallback_model:` 配置中有效。它**不是**顶级 `model.provider` 设置的有效值。如果您使用自定义 OpenAI 兼容端点,请在 `model:` 部分设置 `provider: custom`。所有主模型 provider 选项请参阅 [AI Providers](/integrations/providers)。\n:::\n\n### 完整辅助配置参考\n\n```yaml\nauxiliary:\n # 图像分析(vision_analyze 工具 + 浏览器截图)\n vision:\n provider: \"auto\" # \"auto\"、\"openrouter\"、\"nous\"、\"codex\"、\"main\" 等\n model: \"\" # 例如 \"openai/gpt-4o\"、\"google/gemini-2.5-flash\"\n base_url: \"\" # 自定义 OpenAI 兼容端点(覆盖 provider)\n api_key: \"\" # base_url 的 API 密钥(回退到 OPENAI_API_KEY)\n timeout: 120 # 秒 —— LLM API 调用超时;视觉负载需要宽裕的超时\n download_timeout: 30 # 秒 —— 图像 HTTP 下载;慢速连接请增加\n\n # 网页摘要 + 浏览器页面文本提取\n web_extract:\n provider: \"auto\"\n model: \"\" # 例如 \"google/gemini-2.5-flash\"\n base_url: \"\"\n api_key: \"\"\n timeout: 360 # 秒(6 分钟)—— 每次尝试的 LLM 摘要\n\n # 危险命令审批分类器\n approval:\n provider: \"auto\"\n model: \"\"\n base_url: \"\"\n api_key: \"\"\n timeout: 30 # 秒\n\n # 上下文压缩超时(与 compression.* 配置分开)\n compression:\n timeout: 120 # 秒 —— 压缩摘要长对话,需要更多时间\n # fallback_chain: # 可选 —— 发生速率限制/连接故障时尝试的 provider\n # - provider: nous\n # model: deepseek/deepseek-chat\n # - provider: openrouter\n # model: google/gemini-2.5-flash\n # base_url: \"\"\n # api_key: \"\"\n\n # 技能中心 —— 技能匹配和搜索\n skills_hub:\n provider: \"auto\"\n model: \"\"\n base_url: \"\"\n api_key: \"\"\n timeout: 30\n\n # MCP 工具调度\n mcp:\n provider: \"auto\"\n model: \"\"\n base_url: \"\"\n api_key: \"\"\n timeout: 30\n\n # Kanban 分类规格说明器 —— `hermes kanban specify <id>`(或\n # 仪表板上 Triage 列卡片的 ✨ Specify 按钮)使用此\n # 槽位将单行描述扩展为具体规格并将\n # 任务提升到 `todo`。便宜快速的模型在这里效果很好;规格扩展\n # 很短,不需要推理深度。\n triage_specifier:\n provider: \"auto\"\n model: \"\"\n base_url: \"\"\n api_key: \"\"\n timeout: 120\n```\n\n:::tip\n每个辅助任务都有可配置的 `timeout`(秒)。默认值:vision 120s、web_extract 360s、approval 30s、compression 120s。如果您为辅助任务使用慢速本地模型,请增加这些值。Vision 还有单独的 `download_timeout`(默认 30s)用于 HTTP 图像下载 —— 对于慢速连接或自托管图像服务器,请增加此值。\n:::\n\n:::info\n上下文压缩有自己的 `compression:` 块用于阈值,以及 `auxiliary.compression:` 块用于模型/provider 设置 —— 参阅上方的[上下文压缩](#context-compression)。主备用链使用顶层的 `fallback_providers:` 列表 —— 参阅[备用提供商](/integrations/providers#fallback-providers)。三者都遵循相同的 provider/model/base_url 模式。\n:::\n\n### 辅助任务的每任务回退链\n\n每个辅助任务都可以选择性地定义一个 `fallback_chain` —— 一个 provider/model 条目列表,当主要辅助 provider 因速率限制、网络连接问题或付费限制而失败时,Hermes 会尝试使用该列表:\n\n```yaml\nauxiliary:\n compression:\n provider: openrouter\n model: openai/gpt-4o-mini\n fallback_chain:\n - provider: nous\n model: deepseek/deepseek-chat\n - provider: openrouter\n model: google/gemini-2.5-flash\n```\n\n当主要辅助 provider(`openrouter` / `openai/gpt-4o-mini`)返回速率限制、连接超时或需要付费错误时,Hermes 将依次遍历 `fallback_chain`。它会跳过 provider 与已失败 provider 相同的条目,并尝试每个剩余条目,直到有一个成功或该链耗尽。如果所有回退都失败,Hermes 会回退到主 agent 模型作为最终的安全网。\n\n每个条目支持与任何辅助任务配置相同的三个旋钮:\n\n| 键 | 描述 |\n|-----|-------------|\n| `provider` | Provider 名称(`nous`、`openrouter`、`anthropic`、`gemini`、`main` 等) |\n| `model` | 该 provider 的模型名称 |\n| `base_url` | (可选)自定义 OpenAI 兼容端点 |\n\n`fallback_chain` 适用于任何辅助任务 —— `compression`、`vision`、`web_extract`、`approval`、`skills_hub`、`mcp` 等。\n\n### OpenRouter 路由和辅助任务的 Pareto Code\n\n当辅助任务解析到 OpenRouter(显式或通过 `provider: \"main\"` 而您的主 agent 在 OpenRouter 上)时,主 agent 的 `provider_routing` 和 `openrouter.min_coding_score` 设置**不会传播** —— 按设计,每个辅助任务是独立的。要为特定辅助任务设置 OpenRouter provider 偏好或使用 [Pareto Code 路由器](/integrations/providers#openrouter-pareto-code-router),请通过 `extra_body` 按任务设置:\n\n```yaml\nauxiliary:\n compression:\n provider: openrouter\n model: openrouter/pareto-code # 为此任务使用 Pareto Code 路由器\n extra_body:\n provider: # OpenRouter provider 路由偏好\n order: [anthropic, google] # 按此顺序尝试这些 providers\n sort: throughput # 或 \"price\" | \"latency\"\n # only: [anthropic] # 限制到特定 provider\n # ignore: [deepinfra] # 排除特定 providers\n plugins: # OpenRouter Pareto Code 路由器旋钮\n - id: pareto-router\n min_coding_score: 0.5 # 0.0–1.0;越高 = 更强的编码能力\n```\n\n形状与 OpenRouter 在聊天补全请求体中接受的内容一致。Hermes 原样转发整个 `extra_body`,因此 [openrouter.ai/docs](https://openrouter.ai/docs) 中记录的任何其他 OpenRouter 请求体字段都以相同方式工作。\n\n### 更改视觉模型\n\n使用 GPT-4o 而非 Gemini Flash 进行图像分析:\n\n```yaml\nauxiliary:\n vision:\n model: \"openai/gpt-4o\"\n```\n\n或通过环境变量(在 `~/.hermes/.env` 中):\n\n```bash\nAUXILIARY_VISION_MODEL=openai/gpt-4o\n```\n\n### Provider 选项\n\n这些选项适用于**辅助任务配置**(`auxiliary:`、`compression:`、`fallback_model:`),而非您的主 `model.provider` 设置。\n\n| Provider | 描述 | 要求 |\n|----------|-------------|-------------|\n| `\"auto\"` | 最佳可用(默认)。Vision 尝试 OpenRouter → Nous → Codex。 | — |\n| `\"openrouter\"` | 强制 OpenRouter —— 路由到任何模型(Gemini、GPT-4o、Claude 等) | `OPENROUTER_API_KEY` |\n| `\"nous\"` | 强制 Nous Portal | `hermes auth` |\n| `\"codex\"` | 强制 Codex OAuth(ChatGPT 账户)。支持视觉(gpt-5.3-codex)。 | `hermes model` → Codex |\n| `\"minimax-oauth\"` | 强制 MiniMax OAuth(浏览器登录,无需 API 密钥)。辅助任务使用 MiniMax-M2.7-highspeed。 | `hermes model` → MiniMax (OAuth) |\n| `\"xai-oauth\"` | 强制 xAI Grok OAuth(SuperGrok 或 X Premium+ 订阅者的浏览器登录,无需 API 密钥)。相同的 OAuth token 涵盖聊天、TTS、图像、视频和转录。 | `hermes model` → xAI Grok OAuth (SuperGrok / Premium+) |\n| `\"main\"` | 使用您的活跃自定义/主端点。可以来自 `OPENAI_BASE_URL` + `OPENAI_API_KEY` 或通过 `hermes model` / `config.yaml` 保存的自定义端点。适用于 OpenAI、本地模型或任何 OpenAI 兼容 API。**仅限辅助任务 —— 对 `model.provider` 无效。** | 自定义端点凭据 + 基础 URL |\n\n当您希望附带任务绕过默认路由器时,主 provider 目录中的直接 API 密钥 providers 也在这里工作。配置 `GMI_API_KEY` 后,`gmi` 有效:\n\n```yaml\nauxiliary:\n compression:\n provider: \"gmi\"\n model: \"anthropic/claude-opus-4.6\"\n```\n\n对于 GMI 辅助路由,使用 GMI 的 `/v1/models` 端点返回的确切模型 ID。\n\n### 常见设置\n\n**使用直接自定义端点**(比 `provider: \"main\"` 对本地/自托管 API 更清晰):\n```yaml\nauxiliary:\n vision:\n base_url: \"http://localhost:1234/v1\"\n api_key: \"local-key\"\n model: \"qwen2.5-vl\"\n```\n\n`base_url` 优先于 `provider`,因此这是将辅助任务路由到特定端点的最明确方式。对于直接端点覆盖,Hermes 使用配置的 `api_key` 或回退到 `OPENAI_API_KEY`;它不会为该自定义端点重用 `OPENROUTER_API_KEY`。\n\n**使用 OpenAI API 密钥进行视觉:**\n```yaml\n# 在 ~/.hermes/.env 中:\n# OPENAI_BASE_URL=https://api.openai.com/v1\n# OPENAI_API_KEY=sk-...\n\nauxiliary:\n vision:\n provider: \"main\"\n model: \"gpt-4o\" # 或 \"gpt-4o-mini\" 更便宜\n```\n\n**使用 OpenRouter 进行视觉**(路由到任何模型):\n```yaml\nauxiliary:\n vision:\n provider: \"openrouter\"\n model: \"openai/gpt-4o\" # 或 \"google/gemini-2.5-flash\" 等\n```\n\n**使用 Codex OAuth**(ChatGPT Pro/Plus 账户 —— 无需 API 密钥):\n```yaml\nauxiliary:\n vision:\n provider: \"codex\" # 使用您的 ChatGPT OAuth token\n # 模型默认为 gpt-5.3-codex(支持视觉)\n```\n\n**使用 MiniMax OAuth**(浏览器登录,无需 API 密钥):\n```yaml\nmodel:\n default: MiniMax-M2.7\n provider: minimax-oauth\n base_url: https://api.minimax.io/anthropic\n```\n运行 `hermes model` 并选择 **MiniMax (OAuth)** 自动登录并设置此项。对于中国区域,基础 URL 将是 `https://api.minimaxi.com/anthropic`。完整演练请参阅 [MiniMax OAuth 指南](../guides/minimax-oauth.md)。\n\n**使用本地/自托管模型:**\n```yaml\nauxiliary:\n vision:\n provider: \"main\" # 使用您的活跃自定义端点\n model: \"my-local-model\"\n```\n\n`provider: \"main\"` 使用 Hermes 用于普通聊天的任何 provider —— 无论是命名的自定义 provider(例如 `beans`)、内置 provider(如 `openrouter`)还是旧版 `OPENAI_BASE_URL` 端点。\n\n:::tip\n如果您使用 Codex OAuth 作为主模型 provider,视觉会自动工作 —— 无需额外配置。Codex 包含在视觉的自动检测链中。\n:::\n\n:::warning\n**视觉需要多模态模型。** 如果您设置 `provider: \"main\"`,请确保您的端点支持多模态/视觉 —— 否则图像分析将失败。\n:::\n\n### 环境变量(旧版)\n\n辅助模型也可以通过环境变量配置。但是,`config.yaml` 是首选方法 —— 它更易于管理,并支持所有选项,包括 `base_url` 和 `api_key`。\n\n| 设置 | 环境变量 |\n|---------|---------------------|\n| Vision provider | `AUXILIARY_VISION_PROVIDER` |\n| Vision 模型 | `AUXILIARY_VISION_MODEL` |\n| Vision 端点 | `AUXILIARY_VISION_BASE_URL` |\n| Vision API 密钥 | `AUXILIARY_VISION_API_KEY` |\n| Web 提取 provider | `AUXILIARY_WEB_EXTRACT_PROVIDER` |\n| Web 提取模型 | `AUXILIARY_WEB_EXTRACT_MODEL` |\n| Web 提取端点 | `AUXILIARY_WEB_EXTRACT_BASE_URL` |\n| Web 提取 API 密钥 | `AUXILIARY_WEB_EXTRACT_API_KEY` |\n\n压缩和回退模型设置仅限 config.yaml。\n\n:::tip\n运行 `hermes config` 查看您当前的辅助模型设置。覆盖仅在与默认值不同时显示。\n:::\n\n## 推理努力程度\n\n控制模型在响应前进行多少\"思考\":\n\n```yaml\nagent:\n reasoning_effort: \"\" # 空 = 中等(默认)。选项:none、minimal、low、medium、high、xhigh(最大)\n```\n\n未设置时(默认),推理努力程度默认为\"medium\" —— 适合大多数任务的平衡级别。设置值会覆盖它 —— 更高的推理努力程度在复杂任务上提供更好的结果,但代价是更多 token 和延迟。\n\n您也可以在运行时使用 `/reasoning` 命令更改推理努力程度:\n\n```\n/reasoning # 显示当前努力程度和显示状态\n/reasoning high # 将推理努力程度设为 high\n/reasoning none # 禁用推理\n/reasoning show # 在每次响应上方显示模型思考\n/reasoning hide # 隐藏模型思考\n```\n\n## 工具使用强制\n\n某些模型偶尔会将预期操作描述为文本而不是进行工具调用(\"我会运行测试...\"而不是实际调用终端)。工具使用强制会注入系统提示词指导,引导模型实际调用工具。\n\n```yaml\nagent:\n tool_use_enforcement: \"auto\" # \"auto\" | true | false | [\"model-substring\", ...]\n```\n\n| 值 | 行为 |\n|-------|----------|\n| `\"auto\"`(默认) | 对匹配以下模型启用:`gpt`、`codex`、`gemini`、`gemma`、`grok`。对所有其他模型禁用(Claude、DeepSeek、Qwen 等)。 |\n| `true` | 始终启用,无论模型如何。如果您注意到当前模型描述操作而不是执行操作,请使用此选项。 |\n| `false` | 始终禁用,无论模型如何。 |\n| `[\"gpt\", \"codex\", \"qwen\", \"llama\"]` | 仅当模型名称包含列出的子字符串之一时启用(不区分大小写)。 |\n\n### 注入的内容\n\n启用后,系统提示词中可能会添加三层指导:\n\n1. **通用工具使用强制**(所有匹配模型)—— 指示模型立即进行工具调用而不是描述意图,持续工作直到任务完成,永远不要以未来操作的承诺结束轮次。\n\n2. **OpenAI 执行纪律**(仅限 GPT 和 Codex 模型)—— 针对 GPT 特定失败模式的额外指导:在部分结果上放弃工作、跳过先决条件查找、幻觉而不是使用工具、在未验证的情况下宣布\"完成\"。\n\n3. **Google 操作指导**(仅限 Gemini 和 Gemma 模型)—— 简洁性、绝对路径、并行工具调用和编辑前验证模式。\n\n这些对用户透明,仅影响系统提示词。已经可靠使用工具的模型(如 Claude)不需要此指导,这就是为什么 `\"auto\"` 排除它们。\n\n### 何时开启\n\n如果您使用的模型不在默认自动列表中,并注意到它经常描述它*会*做什么而不是实际去做,请设置 `tool_use_enforcement: true` 或将模型子字符串添加到列表中:\n\n```yaml\nagent:\n tool_use_enforcement: [\"gpt\", \"codex\", \"gemini\", \"grok\", \"my-custom-model\"]\n```\n\n## TTS 配置\n\n```yaml\ntts:\n provider: \"edge\" # \"edge\" | \"elevenlabs\" | \"openai\" | \"minimax\" | \"mistral\" | \"gemini\" | \"xai\" | \"neutts\"\n speed: 1.0 # 全局速度倍数(所有 provider 的回退)\n edge:\n voice: \"en-US-AriaNeural\" # 322 种声音,74 种语言\n speed: 1.0 # 速度倍数(转换为速率百分比,例如 1.5 → +50%)\n elevenlabs:\n voice_id: \"pNInz6obpgDQGcFmaJgB\"\n model_id: \"eleven_multilingual_v2\"\n openai:\n model: \"gpt-4o-mini-tts\"\n voice: \"alloy\" # alloy、echo、fable、onyx、nova、shimmer\n speed: 1.0 # 速度倍数(API 限制为 0.25–4.0)\n base_url: \"https://api.openai.com/v1\" # 覆盖 OpenAI 兼容 TTS 端点\n minimax:\n speed: 1.0 # 语音速度倍数\n # base_url: \"\" # 可选:覆盖 OpenAI 兼容 TTS 端点\n mistral:\n model: \"voxtral-mini-tts-2603\"\n voice_id: \"c69964a6-ab8b-4f8a-9465-ec0925096ec8\" # Paul - Neutral(默认)\n gemini:\n model: \"gemini-2.5-flash-preview-tts\" # 或 gemini-2.5-pro-preview-tts\n voice: \"Kore\" # 30 种预置声音:Zephyr、Puck、Kore、Enceladus 等\n xai:\n voice_id: \"eve\" # xAI TTS 声音\n language: \"en\" # ISO 639-1\n sample_rate: 24000\n bit_rate: 128000 # MP3 比特率\n # base_url: \"https://api.x.ai/v1\"\n neutts:\n ref_audio: ''\n ref_text: ''\n model: neuphonic/neutts-air-q4-gguf\n device: cpu\n```\n\n这控制 `text_to_speech` 工具和语音模式中的口语回复(CLI 中的 `/voice tts` 或消息 gateway)。\n\n**速度回退层次:** provider 特定速度(例如 `tts.edge.speed`)→ 全局 `tts.speed` → `1.0` 默认值。设置全局 `tts.speed` 以在所有 provider 上应用统一速度,或按 provider 覆盖以进行精细控制。\n\n## 显示设置\n\n```yaml\ndisplay:\n tool_progress: all # off | new | all | verbose\n tool_progress_command: false # 在消息 gateway 中启用 /verbose 斜杠命令\n platforms: {} # 每平台显示覆盖(见下文)\n tool_progress_overrides: {} # 已弃用 —— 改用 display.platforms\n interim_assistant_messages: true # Gateway:将自然的轮次中 assistant 更新作为单独消息发送\n skin: default # 内置或自定义 CLI 皮肤(参阅 user-guide/features/skins)\n personality: \"kawaii\" # 旧版外观字段,仍在某些摘要中显示\n compact: false # 紧凑输出模式(减少空白)\n resume_display: full # full(恢复时显示之前的消息)| minimal(仅单行)\n bell_on_complete: false # 当 agent 完成时播放终端铃声(适合长任务)\n show_reasoning: false # 在每次响应上方显示模型推理/思考(用 /reasoning show|hide 切换)\n streaming: false # 将 token 实时流式传输到终端\n show_cost: false # 在 CLI 状态栏中显示估计 $ 成本\n timestamps: false # 为 true 时,在 CLI/TUI 记录中为用户和 assistant 标签添加 [HH:MM] 时间戳前缀\n tool_preview_length: 0 # 工具调用预览的最大字符数(0 = 无限制,显示完整路径/命令)\n runtime_footer: # Gateway:在最终回复中附加运行时上下文页脚\n enabled: false\n fields: [\"model\", \"context_pct\", \"cwd\"]\n file_mutation_verifier: true # 当本轮 write_file/patch 调用失败时附加建议性页脚\n language: en # 静态消息的 UI 语言(审批提示、部分 gateway 回复)。en | zh | zh-hant | ja | de | es | fr | tr | uk | af | ko | it | ga | pt | ru | hu\n```\n\n### 文件变更验证器\n\n当 `display.file_mutation_verifier` 为 `true`(默认)时,每当本轮中 `write_file` 或 `patch` 调用失败且从未被对同一路径的成功写入取代时,Hermes 会在 assistant 的最终响应中附加一行建议。这捕获了\"批量并行补丁,一半静默失败,模型总结成功\"这类过度声明,而无需您在每次编辑后手动运行 `git status`。\n\n示例页脚:\n\n```\n⚠️ File-mutation verifier: 3 file(s) were NOT modified this turn despite any wording above that may suggest otherwise. Run `git status` or `read_file` to confirm.\n • concepts/automatic-organization.md — [patch] Could not find match for old_string\n • concepts/lora.md — [patch] Could not find match for old_string\n • concepts/rag-pipeline.md — [patch] Could not find match for old_string\n```\n\n设置 `file_mutation_verifier: false`(或 `HERMES_FILE_MUTATION_VERIFIER=0`)以禁止页脚。验证器仅在轮次结束时有真实失败未解决时触发 —— 在同一轮次内重试失败补丁并成功的模型不会为该文件触发它。\n\n### 静态消息的 UI 语言\n\n`display.language` 设置翻译一小组静态面向用户的消息 —— CLI 审批提示、少数 gateway 斜杠命令回复(例如重启排空通知、\"审批已过期\"、\"目标已清除\")。它**不**翻译 agent 响应、日志行、工具输出、错误回溯或斜杠命令描述 —— 这些保持英文。如果您希望 agent 本身用另一种语言回复,只需在您的提示词或系统消息中告诉它。\n\n支持的值:`en`(默认)、`zh`(简体中文)、`zh-hant`(繁体中文)、`ja`(日语)、`de`(德语)、`es`(西班牙语)、`fr`(法语)、`tr`(土耳其语)、`uk`(乌克兰语)、`af`(南非荷兰语)、`ko`(韩语)、`it`(意大利语)、`ga`(爱尔兰语)、`pt`(葡萄牙语)、`ru`(俄语)、`hu`(匈牙利语)。未知值回退到英文。\n\n您也可以使用 `HERMES_LANGUAGE` 环境变量按会话设置,它会覆盖配置值。\n\n```yaml\ndisplay:\n language: zh # CLI 审批提示以中文显示\n```\n\n| 模式 | 您看到的内容 |\n|------|-------------|\n| `off` | 静默 —— 仅最终响应 |\n| `new` | 仅在工具更改时显示工具指示器 |\n| `all` | 每次工具调用附带简短预览(默认) |\n| `verbose` | 完整参数、结果和调试日志 |\n\n在 CLI 中,使用 `/verbose` 循环切换这些模式。要在消息平台(Telegram、Discord、Slack 等)中使用 `/verbose`,请在上方的 `display` 部分设置 `tool_progress_command: true`。该命令将循环切换模式并保存到配置。\n\n### 运行时元数据页脚(仅限 gateway)\n\n当 `display.runtime_footer.enabled: true` 时,Hermes 在每个 gateway 轮次的**最终**消息中附加一个小型运行时上下文页脚。目前页脚可显示模型、上下文窗口百分比和当前工作目录。默认关闭;如果您的团队希望每个回复都包含这些来源信息,请按 gateway 选择加入。\n\n```yaml\ndisplay:\n runtime_footer:\n enabled: true\n fields: [\"model\", \"context_pct\", \"cwd\"] # 支持字段:model、context_pct、cwd\n```\n\n`/footer` 斜杠命令在任何会话中运行时切换此功能。\n\n附加到 Telegram/Discord/Slack 回复的示例页脚:\n\n```\n— claude-opus-4.7 · 12 tool calls · 2m 14s · $0.042\n```\n\n只有轮次的**最终**消息获得页脚;中间更新保持干净。\n\n### 每平台进度覆盖\n\n不同平台有不同的详细程度需求。例如,Signal 无法编辑消息,因此每次进度更新都会成为单独的消息 —— 很嘈杂。使用 `display.platforms` 设置每平台模式:\n\n```yaml\ndisplay:\n tool_progress: all # 全局默认\n platforms:\n signal:\n tool_progress: 'off' # 在 Signal 上静默进度\n telegram:\n tool_progress: verbose # 在 Telegram 上详细进度\n slack:\n tool_progress: 'off' # 在共享 Slack 工作区中保持安静\n```\n\n没有覆盖的平台回退到全局 `tool_progress` 值。有效平台键:`telegram`、`discord`、`slack`、`signal`、`whatsapp`、`matrix`、`mattermost`、`email`、`sms`、`homeassistant`、`dingtalk`、`feishu`、`wecom`、`weixin`、`bluebubbles`、`qqbot`。旧版 `display.tool_progress_overrides` 键仍可加载以向后兼容,但已弃用,并在首次加载时迁移到 `display.platforms`。\n\n`interim_assistant_messages` 仅限 gateway。启用后,Hermes 将已完成的轮次中 assistant 更新作为单独的聊天消息发送。这与 `tool_progress` 无关,不需要 gateway 流式传输。\n\n## 隐私\n\n```yaml\nprivacy:\n redact_pii: false # 从 LLM 上下文中删除 PII(仅限 gateway)\n```\n\n当 `redact_pii` 为 `true` 时,gateway 在将系统提示词发送到受支持平台上的 LLM 之前,会从中删除个人身份信息:\n\n| 字段 | 处理方式 |\n|-------|-----------|\n| 电话号码(WhatsApp/Signal 上的用户 ID) | 哈希为 `user_<12-char-sha256>` |\n| 用户 ID | 哈希为 `user_<12-char-sha256>` |\n| 聊天 ID | 数字部分哈希,保留平台前缀(`telegram:<hash>`) |\n| 主频道 ID | 数字部分哈希 |\n| 用户名/昵称 | **不受影响**(用户选择的,公开可见) |\n\n**平台支持:** 删除适用于 WhatsApp、Signal 和 Telegram。Discord 和 Slack 被排除,因为它们的提及系统(`<@user_id>`)需要 LLM 上下文中的真实 ID。\n\n哈希是确定性的 —— 同一用户始终映射到同一哈希,因此模型仍然可以在群聊中区分用户。路由和传递在内部使用原始值。\n\n## 语音转文字(STT)\n\n```yaml\nstt:\n provider: \"local\" # \"local\" | \"groq\" | \"openai\" | \"mistral\"\n local:\n model: \"base\" # tiny、base、small、medium、large-v3\n openai:\n model: \"whisper-1\" # whisper-1 | gpt-4o-mini-transcribe | gpt-4o-transcribe\n # model: \"whisper-1\" # 旧版回退键仍受支持\n```\n\nProvider 行为:\n\n- `local` 使用在您机器上运行的 `faster-whisper`。使用 `pip install faster-whisper` 单独安装。\n- `groq` 使用 Groq 的 Whisper 兼容端点,读取 `GROQ_API_KEY`。\n- `openai` 使用 OpenAI 语音 API,读取 `VOICE_TOOLS_OPENAI_KEY`。\n\n如果请求的 provider 不可用,Hermes 按此顺序自动回退:`local` → `groq` → `openai`。\n\nGroq 和 OpenAI 模型覆盖由环境变量驱动:\n\n```bash\nSTT_GROQ_MODEL=whisper-large-v3-turbo\nSTT_OPENAI_MODEL=whisper-1\nGROQ_BASE_URL=https://api.groq.com/openai/v1\nSTT_OPENAI_BASE_URL=https://api.openai.com/v1\n```\n\n## 语音模式(CLI)\n\n```yaml\nvoice:\n record_key: \"ctrl+b\" # CLI 内的按键通话键\n max_recording_seconds: 120 # 长录音的硬停止\n auto_tts: false # /voice on 时自动启用口语回复\n beep_enabled: true # 在 CLI 语音模式中播放录音开始/停止提示音\n silence_threshold: 200 # 语音检测的 RMS 阈值\n silence_duration: 3.0 # 自动停止前的静默秒数\n```\n\n在 CLI 中使用 `/voice on` 启用麦克风模式,使用 `record_key` 开始/停止录音,使用 `/voice tts` 切换口语回复。端到端设置和平台特定行为请参阅[语音模式](/user-guide/features/voice-mode)。\n\n## 流式传输\n\n将 token 实时流式传输到终端或消息平台,而不是等待完整响应。\n\n### CLI 流式传输\n\n```yaml\ndisplay:\n streaming: true # 实时将 token 流式传输到终端\n show_reasoning: true # 同时流式传输推理/思考 token(可选)\n```\n\n启用后,响应在流式传输框内逐 token 出现。工具调用仍然静默捕获。如果 provider 不支持流式传输,它会自动回退到正常显示。\n\n### Gateway 流式传输(Telegram、Discord、Slack)\n\n```yaml\nstreaming:\n enabled: true # 启用渐进式消息编辑\n transport: edit # \"edit\"(渐进式消息编辑)或 \"off\"\n edit_interval: 0.3 # 消息编辑之间的秒数\n buffer_threshold: 40 # 强制编辑刷新前的字符数\n cursor: \" ▉\" # 流式传输期间显示的光标\n fresh_final_after_seconds: 0 # 预览超过此时间时选择发送新的最终消息(Telegram)\n```\n\n启用后,bot 在第一个 token 时发送消息,然后随着更多 token 到来渐进式编辑它。不支持消息编辑的平台(Signal、Email、Home Assistant)在第一次尝试时自动检测 —— 该会话的流式传输被优雅地禁用,不会产生大量消息。\n\n对于不带渐进式 token 编辑的独立自然轮次中 assistant 更新,请设置 `display.interim_assistant_messages: true`。\n\n**溢出处理:** 如果流式传输的文本超过平台的消息长度限制(约 4096 字符),当前消息被最终化,新消息自动开始。\n\n**新的最终消息(Telegram):** Telegram 的 `editMessageText` 保留原始消息时间戳,因此长时间运行的流式回复即使在完成后也会保留第一个 token 的时间戳。设置 `fresh_final_after_seconds > 0` 可选择将旧预览作为全新的最终消息传递,并尽力删除旧预览。默认值为 `0`,始终就地最终化流式回复,避免某些客户端短暂显示重复消息再删除其中一条。\n\n:::note\n主开关 `streaming.enabled` 默认为 `false`——在你启用之前不会有任何流式传输。启用后,是否流式传输按**平台**决定:Telegram 默认带有 `display.platforms.telegram.streaming: true`(流式传输),Discord 为 `display.platforms.discord.streaming: false`(不流式传输)。因此启用流式传输后,Telegram 开箱即用地流式传输,Discord 在你修改其开关之前仍使用整条消息回复。你可以在仪表盘的 **Channels** 开关中或直接在 `~/.hermes/config.yaml` 中调整这些按平台的开关。\n:::\n\n## 群聊会话隔离\n\n控制共享聊天是每个房间保持一个对话还是每个参与者一个对话:\n\n```yaml\ngroup_sessions_per_user: true # true = 群组/频道中每用户隔离,false = 每个聊天一个共享会话\n```\n\n- `true` 是默认和推荐设置。在 Discord 频道、Telegram 群组、Slack 频道和类似共享上下文中,当平台提供用户 ID 时,每个发送者获得自己的会话。\n- `false` 恢复到旧的共享房间行为。如果您明确希望 Hermes 将频道视为一个协作对话,这可能有用,但这也意味着用户共享上下文、token 成本和中断状态。\n- 私信不受影响。Hermes 仍然像往常一样通过聊天/DM ID 键入 DM。\n- 线程与其父频道保持隔离;使用 `true` 时,每个参与者在线程内也获得自己的会话。\n\n有关行为详情和示例,请参阅[会话](/user-guide/sessions)和 [Discord 指南](/user-guide/messaging/discord)。\n\n## 未授权 DM 行为\n\n控制当未知用户发送私信时 Hermes 的行为:\n\n```yaml\nunauthorized_dm_behavior: pair\n\nwhatsapp:\n unauthorized_dm_behavior: ignore\n```\n\n- `pair` 是默认值。Hermes 拒绝访问,但在 DM 中回复一次性配对码。\n- `ignore` 静默丢弃未授权的 DM。\n- 平台部分覆盖全局默认值,因此您可以在广泛范围内保持配对启用,同时使一个平台更安静。\n\n## 快速命令\n\n定义自定义命令,这些命令要么在不调用 LLM 的情况下运行 shell 命令,要么将一个斜杠命令别名为另一个。Exec 快速命令是零 token 的,对于从消息平台(Telegram、Discord 等)进行快速服务器检查或实用脚本很有用。\n\n```yaml\nquick_commands:\n status:\n type: exec\n command: systemctl status hermes-agent\n disk:\n type: exec\n command: df -h /\n update:\n type: exec\n command: cd ~/.hermes/hermes-agent && git pull && pip install -e .\n gpu:\n type: exec\n command: nvidia-smi --query-gpu=name,utilization.gpu,memory.used,memory.total --format=csv,noheader\n restart:\n type: alias\n target: /gateway restart\n```\n\n用法:在 CLI 或任何消息平台中输入 `/status`、`/disk`、`/update`、`/gpu` 或 `/restart`。`exec` 命令在宿主本地运行并直接返回输出 —— 无 LLM 调用,不消耗 token。`alias` 命令重写为配置的斜杠命令目标。\n\n- **30 秒超时** —— 长时间运行的命令被终止并显示错误消息\n- **优先级** —— 快速命令在技能命令之前检查,因此您可以覆盖技能名称\n- **自动补全** —— 快速命令在调度时解析,不显示在内置斜杠命令自动补全表中\n- **类型** —— 支持的类型为 `exec` 和 `alias`;其他类型显示错误\n- **到处可用** —— CLI、Telegram、Discord、Slack、WhatsApp、Signal、Email、Home Assistant\n\n仅字符串的 prompt 快捷方式不是有效的快速命令。对于可重用的 prompt 工作流,请创建技能或别名到现有斜杠命令。\n\n## 人类延迟\n\n在消息平台中模拟类人响应节奏:\n\n```yaml\nhuman_delay:\n mode: \"off\" # off | natural | custom\n min_ms: 800 # 最小延迟(自定义模式)\n max_ms: 2500 # 最大延迟(自定义模式)\n```\n\n## 代码执行\n\n配置 `execute_code` 工具:\n\n```yaml\ncode_execution:\n mode: project # project(默认)| strict\n timeout: 300 # 最大执行时间(秒)\n max_tool_calls: 50 # 代码执行中的最大工具调用次数\n```\n\n**`mode`** 控制脚本的工作目录和 Python 解释器:\n\n- **`project`**(默认)—— 脚本在会话的工作目录中以活跃 virtualenv/conda 环境的 python 运行。项目依赖(`pandas`、`torch`、项目包)和相对路径(`.env`、`./data.csv`)自然解析,与 `terminal()` 看到的一致。\n- **`strict`** —— 脚本在临时暂存目录中以 `sys.executable`(Hermes 自己的 python)运行。最大可重现性,但项目依赖和相对路径不会解析。\n\n环境清理(删除 `*_API_KEY`、`*_TOKEN`、`*_SECRET`、`*_PASSWORD`、`*_CREDENTIAL`、`*_PASSWD`、`*_AUTH`)和工具白名单在两种模式下完全相同 —— 切换模式不会改变安全态势。\n\n## Web 搜索后端\n\n`web_search` 和 `web_extract` 工具支持五种后端 provider。在 `config.yaml` 中或通过 `hermes tools` 配置后端:\n\n```yaml\nweb:\n backend: firecrawl # firecrawl | searxng | parallel | tavily | exa\n\n # 或使用每功能键混合 provider(例如免费搜索 + 付费提取):\n search_backend: \"searxng\"\n extract_backend: \"firecrawl\"\n```\n\n| 后端 | 环境变量 | 搜索 | 提取 |\n|---------|---------|--------|---------|\n| **Firecrawl**(默认) | `FIRECRAWL_API_KEY` | ✔ | ✔ |\n| **SearXNG** | `SEARXNG_URL` | ✔ | — |\n| **Parallel** | `PARALLEL_API_KEY` | ✔ | ✔ |\n| **Tavily** | `TAVILY_API_KEY` | ✔ | ✔ |\n| **Exa** | `EXA_API_KEY` | ✔ | ✔ |\n\n**后端选择:** 如果未设置 `web.backend`,后端从可用的 API 密钥自动检测。如果仅设置了 `SEARXNG_URL`,使用 SearXNG。如果仅设置了 `EXA_API_KEY`,使用 Exa。如果仅设置了 `TAVILY_API_KEY`,使用 Tavily。如果仅设置了 `PARALLEL_API_KEY`,使用 Parallel。否则 Firecrawl 是默认值。\n\n**SearXNG** 是一个免费、自托管、尊重隐私的元搜索引擎,查询 70+ 个搜索引擎。无需 API 密钥 —— 只需将 `SEARXNG_URL` 设置为您的实例(例如 `http://localhost:8080`)。SearXNG 仅限搜索;`web_extract` 需要单独的提取 provider(设置 `web.extract_backend`)。Docker 设置说明请参阅 [Web 搜索设置指南](/user-guide/features/web-search)。\n\n**自托管 Firecrawl:** 设置 `FIRECRAWL_API_URL` 指向您自己的实例。设置自定义 URL 后,API 密钥变为可选(在服务器上设置 `USE_DB_AUTHENTICATION=***` 以禁用认证)。\n\n**Parallel 搜索模式:** 设置 `PARALLEL_SEARCH_MODE` 控制搜索行为 —— `fast`、`one-shot` 或 `agentic`(默认:`agentic`)。\n\n**Exa:** 在 `~/.hermes/.env` 中设置 `EXA_API_KEY`。支持 `category` 过滤(`company`、`research paper`、`news`、`people`、`personal site`、`pdf`)和域名/日期过滤器。\n\n## 浏览器\n\n配置浏览器自动化行为:\n\n```yaml\nbrowser:\n inactivity_timeout: 120 # 自动关闭空闲会话前的秒数\n command_timeout: 30 # 浏览器命令超时(截图、导航等)(秒)\n record_sessions: false # 自动将浏览器会话录制为 WebM 视频到 ~/.hermes/browser_recordings/\n # 可选 CDP 覆盖 —— 设置后,Hermes 直接附加到您自己的\n # Chromium 系浏览器(通过 /browser connect),而不是启动无头浏览器。\n cdp_url: \"\"\n # 对话框监督器 —— 控制当 CDP 后端附加时(Browserbase、本地 Chromium 系\n # 浏览器通过 /browser connect)如何处理原生 JS 对话框(alert/confirm/prompt)。\n # 在 Camofox 和默认本地 agent 浏览器模式下忽略。\n dialog_policy: must_respond # must_respond | auto_dismiss | auto_accept\n dialog_timeout_s: 300 # must_respond 下的安全自动关闭(秒)\n camofox:\n managed_persistence: false # 为 true 时,Camofox 会话跨重启持久化 cookie/登录\n user_id: \"\" # 可选的外部管理 Camofox userId\n session_key: \"\" # Hermes 创建标签页时发送的可选会话密钥\n adopt_existing_tab: false # 在创建新标签页之前重用此身份的现有标签页\n```\n\n**对话框策略:**\n\n- `must_respond`(默认)—— 捕获对话框,在 `browser_snapshot.pending_dialogs` 中显示,等待 agent 调用 `browser_dialog(action=...)`。在 `dialog_timeout_s` 秒内无响应后,对话框被自动关闭以防止页面的 JS 线程永久停滞。\n- `auto_dismiss` —— 捕获,立即关闭。Agent 仍然在事后的 `browser_snapshot.recent_dialogs` 中看到对话框记录,`closed_by=\"auto_policy\"`。\n- `auto_accept` —— 捕获,立即接受。适用于有激进 `beforeunload` 提示的页面。\n\n完整对话框工作流请参阅[浏览器功能页面](./features/browser.md#browser_dialog)。\n\n浏览器工具集支持多个 provider。有关 Browserbase、Browser Use 和本地 Chromium 系 CDP 设置的详细信息,请参阅[浏览器功能页面](/user-guide/features/browser)。\n\n## 时区\n\n使用 IANA 时区字符串覆盖服务器本地时区。影响日志中的时间戳、cron 调度和系统提示词时间注入。\n\n```yaml\ntimezone: \"America/New_York\" # IANA 时区(默认:\"\" = 服务器本地时间)\n```\n\n支持的值:任何 IANA 时区标识符(例如 `America/New_York`、`Europe/London`、`Asia/Kolkata`、`UTC`)。留空或省略以使用服务器本地时间。\n\n## Discord\n\n为消息 gateway 配置 Discord 特定行为:\n\n```yaml\ndiscord:\n require_mention: true # 在服务器频道中需要 @提及才能响应\n free_response_channels: \"\" # 逗号分隔的频道 ID,bot 在这些频道无需 @提及即可响应\n auto_thread: true # 在频道中 @提及时自动创建线程\n```\n\n- `require_mention` —— 为 `true`(默认)时,bot 仅在服务器频道中被 `@BotName` 提及时响应。DM 始终无需提及即可工作。\n- `free_response_channels` —— 逗号分隔的频道 ID 列表,bot 在这些频道对每条消息响应,无需提及。\n- `auto_thread` —— 为 `true`(默认)时,频道中的提及会自动为对话创建线程,保持频道整洁(类似 Slack 线程)。\n\n## 安全\n\n预执行安全扫描和机密脱敏:\n\n```yaml\nsecurity:\n redact_secrets: false # 在工具输出和日志中脱敏 API 密钥模式(默认关闭)\n tirith_enabled: true # 为终端命令启用 Tirith 安全扫描\n tirith_path: \"tirith\" # tirith 二进制文件路径(默认:$PATH 中的 \"tirith\")\n tirith_timeout: 5 # 等待 tirith 扫描的秒数\n tirith_fail_open: true # 如果 tirith 不可用,允许命令执行\n website_blocklist: # 参见下方网站黑名单部分\n enabled: false\n domains: []\n shared_files: []\n```\n\n- `redact_secrets` —— 为 `true` 时,自动检测并脱敏工具输出中看起来像 API 密钥、token 和密码的模式,然后再进入对话上下文和日志。**默认关闭** —— 如果您经常在工具输出中处理真实凭据并希望有安全网,请启用。显式设置为 `true` 以开启。\n- `tirith_enabled` —— 为 `true` 时,终端命令在执行前由 [Tirith](https://github.com/sheeki03/tirith) 扫描以检测潜在危险操作。\n- `tirith_path` —— tirith 二进制文件的路径。如果 tirith 安装在非标准位置,请设置此项。\n- `tirith_timeout` —— 等待 tirith 扫描的最大秒数。如果扫描超时,命令继续执行。\n- `tirith_fail_open` —— 为 `true`(默认)时,如果 tirith 不可用或失败,允许命令执行。设置为 `false` 以在 tirith 无法验证时阻止命令。\n\n## 网站黑名单\n\n阻止 agent 的 web 和浏览器工具访问特定域名:\n\n```yaml\nsecurity:\n website_blocklist:\n enabled: false # 启用 URL 阻止(默认:false)\n domains: # 被阻止的域名模式列表\n - \"*.internal.company.com\"\n - \"admin.example.com\"\n - \"*.local\"\n shared_files: # 从外部文件加载额外规则\n - \"/etc/hermes/blocked-sites.txt\"\n```\n\n启用后,任何匹配被阻止域名模式的 URL 在 web 或浏览器工具执行之前都会被拒绝。这适用于 `web_search`、`web_extract`、`browser_navigate` 以及任何访问 URL 的工具。\n\n域名规则支持:\n- 精确域名:`admin.example.com`\n- 通配符子域名:`*.internal.company.com`(阻止所有子域名)\n- TLD 通配符:`*.local`\n\n共享文件每行包含一条域名规则(空行和 `#` 注释被忽略)。缺失或不可读的文件记录警告,但不禁用其他 web 工具。\n\n策略缓存 30 秒,因此配置更改无需重启即可快速生效。\n\n## 智能审批\n\n控制 Hermes 如何处理潜在危险命令:\n\n```yaml\napprovals:\n mode: manual # manual | smart | off\n```\n\n| 模式 | 行为 |\n|------|----------|\n| `manual`(默认) | 在执行任何被标记的命令之前提示用户。在 CLI 中显示交互式审批对话框。在消息中排队待处理的审批请求。 |\n| `smart` | 使用辅助 LLM 评估被标记的命令是否真正危险。低风险命令以会话级持久性自动批准。真正有风险的命令升级给用户。 |\n| `off` | 跳过所有审批检查。等同于 `HERMES_YOLO_MODE=true`。**谨慎使用。** |\n\n智能模式对于减少审批疲劳特别有用 —— 它让 agent 在安全操作上更自主地工作,同时仍然捕获真正破坏性的命令。\n\n:::warning\n设置 `approvals.mode: off` 会禁用终端命令的所有安全检查。仅在受信任的沙箱环境中使用。\n:::\n\n## 检查点\n\n破坏性文件操作之前的自动文件系统快照。详情请参阅[检查点与回滚](/user-guide/checkpoints-and-rollback)。\n\n```yaml\ncheckpoints:\n enabled: false # 启用自动检查点(也可:hermes chat --checkpoints)。默认:false(选择加入)。\n max_snapshots: 20 # 每个目录保留的最大检查点数(默认:20)\n```\n\n\n## 委托\n\n为委托工具配置子 agent 行为:\n\n```yaml\ndelegation:\n # model: \"google/gemini-3-flash-preview\" # 覆盖模型(空 = 继承父级)\n # provider: \"openrouter\" # 覆盖 provider(空 = 继承父级)\n # base_url: \"http://localhost:1234/v1\" # 直接 OpenAI 兼容端点(优先于 provider)\n # api_key: \"local-key\" # base_url 的 API 密钥(回退到 OPENAI_API_KEY)\n # api_mode: \"\" # base_url 的线路协议:\"chat_completions\"、\"codex_responses\" 或 \"anthropic_messages\"。空 = 从 URL 自动检测(例如 /anthropic 后缀 → anthropic_messages)。对启发式无法检测的非标准端点显式设置。\n max_concurrent_children: 3 # 每批并行子 agent 数(下限 1,无上限)。也可通过 DELEGATION_MAX_CONCURRENT_CHILDREN 环境变量设置。\n max_spawn_depth: 1 # 委托树深度上限(1-3,截断)。1 = 扁平(默认):父级生成无法委托的叶子。2 = 编排器子级可以生成叶子孙级。3 = 三级。\n orchestrator_enabled: true # 全局终止开关。为 false 时,role=\"orchestrator\" 被忽略,每个子级无论 max_spawn_depth 如何都被强制为叶子。\n```\n\n**子 agent provider:model 覆盖:** 默认情况下,子 agent 继承父 agent 的 provider 和模型。设置 `delegation.provider` 和 `delegation.model` 将子 agent 路由到不同的 provider:model 对 —— 例如,在您的主 agent 运行昂贵推理模型时,为范围较窄的子任务使用便宜/快速的模型。\n\n**直接端点覆盖:** 如果您想要明显的自定义端点路径,请设置 `delegation.base_url`、`delegation.api_key` 和 `delegation.model`。这将子 agent 直接发送到该 OpenAI 兼容端点,并优先于 `delegation.provider`。如果省略 `delegation.api_key`,Hermes 仅回退到 `OPENAI_API_KEY`。\n\n**线路协议(`api_mode`):** Hermes 从 `delegation.base_url` 自动检测线路协议(例如以 `/anthropic` 结尾的路径 → `anthropic_messages`;Codex/原生 Anthropic/Kimi-coding 主机名保留其现有检测)。对于启发式无法分类的端点 —— 例如 Azure AI Foundry、MiniMax、Zhipu GLM 或前置 Anthropic 形状后端的 LiteLLM 代理 —— 请将 `delegation.api_mode` 显式设置为 `chat_completions`、`codex_responses` 或 `anthropic_messages` 之一。留空(默认)以保持自动检测。\n\n委托 provider 使用与 CLI/gateway 启动相同的凭据解析。所有配置的 provider 均受支持:`openrouter`、`nous`、`copilot`、`zai`、`kimi-coding`、`minimax`、`minimax-cn`。设置 provider 时,系统自动解析正确的基础 URL、API 密钥和 API 模式 —— 无需手动凭据连接。\n\n**优先级:** 配置中的 `delegation.base_url` → 配置中的 `delegation.provider` → 父 provider(继承)。配置中的 `delegation.model` → 父模型(继承)。仅设置 `model` 而不设置 `provider` 仅更改模型名称,同时保留父级凭据(适用于在同一 provider(如 OpenRouter)内切换模型)。\n\n**宽度和深度:** `max_concurrent_children` 限制每批并行运行的子 agent 数量(默认 `3`,下限 1,无上限)。也可通过 `DELEGATION_MAX_CONCURRENT_CHILDREN` 环境变量设置。当模型提交的 `tasks` 数组超过上限时,`delegate_task` 返回工具错误解释限制,而不是静默截断。`max_spawn_depth` 控制委托树深度(截断到 1-3)。在默认 `1` 时,委托是扁平的:子级无法生成孙级,传递 `role=\"orchestrator\"` 静默降级为 `leaf`。提升到 `2` 使编排器子级可以生成叶子孙级;`3` 用于三级树。Agent 通过 `role=\"orchestrator\"` 按调用选择编排;`orchestrator_enabled: false` 强制每个子级回到叶子,无论如何。成本呈乘法增长 —— 在 `max_spawn_depth: 3` 和 `max_concurrent_children: 3` 时,树可以达到 3×3×3 = 27 个并发叶子 agent。使用模式请参阅[子 Agent 委托 → 深度限制和嵌套编排](features/delegation.md#depth-limit-and-nested-orchestration)。\n\n## 澄清\n\n配置澄清提示行为:\n\n```yaml\nclarify:\n timeout: 120 # 等待用户澄清响应的秒数\n```\n\n## 上下文文件(SOUL.md、AGENTS.md)\n\nHermes 使用两种不同的上下文范围:\n\n| 文件 | 用途 | 范围 |\n|------|---------|-------|\n| `SOUL.md` | **主要 agent 身份** —— 定义 agent 是谁(系统提示词第 #1 槽位) | `~/.hermes/SOUL.md` 或 `$HERMES_HOME/SOUL.md` |\n| `.hermes.md` / `HERMES.md` | 项目特定指令(最高优先级) | 向上走到 git 根目录 |\n| `AGENTS.md` | 项目特定指令、编码规范 | 递归目录遍历 |\n| `CLAUDE.md` | Claude Code 上下文文件(也会检测) | 仅工作目录 |\n| `.cursorrules` | Cursor IDE 规则(也会检测) | 仅工作目录 |\n| `.cursor/rules/*.mdc` | Cursor 规则文件(也会检测) | 仅工作目录 |\n\n- **SOUL.md** 是 agent 的主要身份。它占据系统提示词的第 #1 槽位,完全替换内置的默认身份。编辑它以完全自定义 agent 是谁。\n- 如果 SOUL.md 缺失、为空或无法加载,Hermes 回退到内置默认身份。\n- **项目上下文文件使用优先级系统** —— 仅加载一种类型(第一个匹配优先):`.hermes.md` → `AGENTS.md` → `CLAUDE.md` → `.cursorrules`。SOUL.md 始终独立加载。\n- **AGENTS.md** 是分层的:如果子目录也有 AGENTS.md,所有都会合并。\n- 如果 `SOUL.md` 不存在,Hermes 会自动生成默认的 `SOUL.md`。\n- 所有加载的上下文文件上限为 20,000 字符,并进行智能截断。\n\n另请参阅:\n- [个性与 SOUL.md](/user-guide/features/personality)\n- [上下文文件](/user-guide/features/context-files)\n\n## 工作目录\n\n| 上下文 | 默认值 |\n|---------|---------|\n| **CLI(`hermes`)** | 运行命令的当前目录 |\n| **消息 gateway** | 主目录 `~`(用 `MESSAGING_CWD` 覆盖) |\n| **Docker / Singularity / Modal / SSH** | 容器或远程机器内用户的主目录 |\n\n覆盖工作目录:\n```bash\n# 在 ~/.hermes/.env 或 ~/.hermes/config.yaml 中:\nMESSAGING_CWD=/home/myuser/projects # Gateway 会话\nTERMINAL_CWD=/workspace # 所有终端会话\n```\n"}, "03563dabacc144713f9c0827d6045b7a88f13efc": {"t": 1785767995.2146122, "n": 56915, "h": "35225c6b92911059", "c": "---\nsidebar_position: 2\ntitle: \"配置\"\ndescription: \"配置 Hermes Agent — config.yaml、providers、模型、API 密钥等\"\n---\n\n# 配置\n\n所有设置均存储在 `~/.hermes/` 目录中,便于访问。\n\n## 目录结构\n\n```text\n~/.hermes/\n├── config.yaml # 设置(模型、终端、TTS、压缩等)\n├── .env # API 密钥和机密\n├── auth.json # OAuth provider 凭据(Nous Portal 等)\n├── SOUL.md # 主要 agent 身份(系统提示词第 #1 槽位)\n├── memories/ # 持久记忆(MEMORY.md、USER.md)\n├── skills/ # Agent 创建的技能(通过 skill_manage 工具管理)\n├── cron/ # 定时任务\n├── sessions/ # Gateway 会话\n└── logs/ # 日志(errors.log、gateway.log — 机密自动脱敏)\n```\n\n## 管理配置\n\n```bash\nhermes config # 查看当前配置\nhermes config edit # 在编辑器中打开 config.yaml\nhermes config set KEY VAL # 设置特定值\nhermes config check # 检查缺失选项(更新后使用)\nhermes config migrate # 交互式添加缺失选项\n\n# 示例:\nhermes config set model anthropic/claude-opus-4\nhermes config set terminal.backend docker\nhermes config set OPENROUTER_API_KEY sk-or-... # 保存到 .env\n```\n\n:::tip\n`hermes config set` 命令会自动将值路由到正确的文件 —— API 密钥保存到 `.env`,其他所有内容保存到 `config.yaml`。\n:::\n\n## 配置优先级\n\n设置按以下顺序解析(优先级从高到低):\n\n1. **CLI 参数** —— 例如 `hermes chat --model anthropic/claude-sonnet-4`(单次调用覆盖)\n2. **`~/.hermes/config.yaml`** —— 所有非机密设置的主配置文件\n3. **`~/.hermes/.env`** —— 环境变量的回退;机密(API 密钥、token、密码)**必须**放这里\n4. **内置默认值** —— 未设置任何内容时的硬编码安全默认值\n\n:::info 经验法则\n机密(API 密钥、bot token、密码)放入 `.env`。其他所有内容(模型、终端后端、压缩设置、内存限制、工具集)放入 `config.yaml`。当两者都设置时,`config.yaml` 对非机密设置优先。\n:::\n\n## 环境变量替换\n\n可以在 `config.yaml` 中使用 `${VAR_NAME}` 语法引用环境变量:\n\n```yaml\nauxiliary:\n vision:\n api_key: ${GOOGLE_API_KEY}\n base_url: ${CUSTOM_VISION_URL}\n\ndelegation:\n api_key: ${DELEGATION_KEY}\n```\n\n单个值中可以有多个引用:`url: \"${HOST}:${PORT}\"`。如果引用的变量未设置,占位符将保持原样(`${UNDEFINED_VAR}` 保持不变)。仅支持 `${VAR}` 语法 —— 裸 `$VAR` 不会被展开。\n\n关于 AI provider 设置(OpenRouter、Anthropic、Copilot、自定义端点、自托管 LLM、回退模型等),请参阅 [AI Providers](/integrations/providers)。\n\n### Provider 超时\n\n可以为 provider 设置 `providers.<id>.request_timeout_seconds` 作为全局请求超时,以及 `providers.<id>.models.<model>.timeout_seconds` 作为特定模型的覆盖值。适用于每种传输方式(OpenAI-wire、原生 Anthropic、Anthropic 兼容)上的主轮次客户端、回退链、凭据轮换后的重建,以及(对于 OpenAI-wire)每请求超时 kwarg —— 因此配置值优先于旧版 `HERMES_API_TIMEOUT` 环境变量。\n\n还可以设置 `providers.<id>.stale_timeout_seconds` 用于非流式陈旧调用检测器,以及 `providers.<id>.models.<model>.stale_timeout_seconds` 作为特定模型的覆盖值。此值优先于旧版 `HERMES_API_CALL_STALE_TIMEOUT` 环境变量。\n\n不设置这些值将保持旧版默认值(`HERMES_API_TIMEOUT=1800`s、`HERMES_API_CALL_STALE_TIMEOUT=90`s、原生 Anthropic 900s)。隐式的非流式 stale 检测会在本地端点上自动禁用,并且会在超大上下文下自动放宽。目前不适用于 AWS Bedrock(`bedrock_converse` 和 AnthropicBedrock SDK 路径均使用 boto3 及其自身的超时配置)。请参阅 [`cli-config.yaml.example`](https://github.com/NousResearch/hermes-agent/blob/main/cli-config.yaml.example) 中的注释示例。\n\n## 终端后端配置\n\nHermes 支持六种终端后端。每种后端决定 agent 的 shell 命令实际在哪里执行 —— 本地机器、Docker 容器、通过 SSH 的远程服务器、Modal 云沙箱(直接或通过 Nous 托管的 gateway)、Daytona 工作区,或 Singularity/Apptainer 容器。\n\n```yaml\nterminal:\n backend: local # local | docker | ssh | modal | daytona | singularity\n cwd: \".\" # Gateway/cron 工作目录(CLI 始终使用启动目录)\n timeout: 180 # 每条命令的超时时间(秒)\n env_passthrough: [] # 转发到沙箱执行的环境变量名(terminal + execute_code)\n singularity_image: \"docker://nikolaik/python-nodejs:python3.11-nodejs20\" # Singularity 后端的容器镜像\n modal_image: \"nikolaik/python-nodejs:python3.11-nodejs20\" # Modal 后端的容器镜像\n daytona_image: \"nikolaik/python-nodejs:python3.11-nodejs20\" # Daytona 后端的容器镜像\n```\n\n对于 Modal 和 Daytona 等云沙箱,`container_persistent: true` 表示 Hermes 将尝试在沙箱重建后保留文件系统状态。这并不保证相同的活跃沙箱、PID 空间或后台进程之后仍在运行。\n\n### 后端概览\n\n| 后端 | 命令运行位置 | 隔离性 | 最适合 |\n|---------|-------------------|-----------|----------|\n| **local** | 直接在您的机器上 | 无 | 开发、个人使用 |\n| **docker** | 单个持久 Docker 容器(跨会话、`/new`、子 agent 共享) | 完全(命名空间、cap-drop) | 安全沙箱、CI/CD |\n| **ssh** | 通过 SSH 的远程服务器 | 网络边界 | 远程开发、强大硬件 |\n| **modal** | Modal 云沙箱 | 完全(云 VM) | 临时云计算、评估 |\n| **daytona** | Daytona 工作区 | 完全(云容器) | 托管云开发环境 |\n| **singularity** | Singularity/Apptainer 容器 | 命名空间(--containall) | HPC 集群、共享机器 |\n\n### Local 后端\n\n默认后端。命令直接在您的机器上运行,无隔离。无需特殊设置。\n\n```yaml\nterminal:\n backend: local\n```\n\n:::warning\nAgent 拥有与您的用户账户相同的文件系统访问权限。使用 `hermes tools` 禁用不需要的工具,或切换到 Docker 进行沙箱隔离。\n:::\n\n### Docker 后端\n\n在具有安全加固的 Docker 容器内运行命令(所有权限已删除、无权限提升、PID 限制)。\n\n**单个持久容器,而非每条命令一个容器。** Hermes 在首次使用时启动一个长期运行的容器,并通过 `docker exec` 将每个终端、文件和 `execute_code` 调用路由到同一容器中 —— 跨会话、`/new`、`/reset` 和 `delegate_task` 子 agent,贯穿 Hermes 进程的整个生命周期。工作目录更改、已安装的包以及 `/workspace` 中的文件会从一次工具调用延续到下一次,就像本地 shell 一样。容器在关闭时停止并删除。详情请参阅下方的**容器生命周期**。\n\n```yaml\nterminal:\n backend: docker\n docker_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n docker_mount_cwd_to_workspace: false # 将启动目录挂载到 /workspace\n docker_run_as_host_user: false # 参见下方\"以宿主用户身份运行容器\"\n docker_forward_env: # 转发到容器的环境变量\n - \"GITHUB_TOKEN\"\n docker_volumes: # 宿主目录挂载\n - \"/home/user/projects:/workspace/projects\"\n - \"/home/user/data:/data:ro\" # :ro 表示只读\n docker_extra_args: # 附加到 `docker run` 的额外标志\n - \"--gpus=all\"\n - \"--network=host\"\n\n # 资源限制\n container_cpu: 1 # CPU 核心数(0 = 不限制)\n container_memory: 5120 # MB(0 = 不限制)\n container_disk: 51200 # MB(需要 XFS+pquota 上的 overlay2)\n container_persistent: true # 跨会话持久化 /workspace 和 /root\n```\n\n**`terminal.docker_extra_args`**(也可通过 `TERMINAL_DOCKER_EXTRA_ARGS='[\"--gpus=all\"]'` 覆盖)允许传递 Hermes 未作为一级键公开的任意 `docker run` 标志 —— `--gpus`、`--network`、`--add-host`、替代 `--security-opt` 覆盖等。每个条目必须是字符串;该列表最后附加到组装好的 `docker run` 调用中,因此可以在需要时覆盖 Hermes 的默认值。请谨慎使用 —— 与沙箱加固(权限删除、`--user`、workspace 绑定挂载)冲突的标志将悄然削弱隔离性。\n\n**要求:** 已安装并运行 Docker Desktop 或 Docker Engine。Hermes 会探测 `$PATH` 以及常见的 macOS 安装位置(`/usr/local/bin/docker`、`/opt/homebrew/bin/docker`、Docker Desktop 应用包)。开箱即用支持 Podman:设置 `HERMES_DOCKER_BINARY=podman`(或完整路径)以在两者都安装时强制使用它。\n\n**容器生命周期:** Hermes 为每个终端和文件工具调用重用单个长期运行的容器(`docker run -d ... sleep 2h`),跨会话、`/new`、`/reset` 和 `delegate_task` 子 agent,贯穿 Hermes 进程的整个生命周期。命令通过带登录 shell 的 `docker exec` 运行,因此工作目录更改、已安装的包以及 `/workspace` 中的文件都会从一次工具调用延续到下一次。容器在 Hermes 关闭时(或空闲清理回收时)停止并删除。\n\n通过 `delegate_task(tasks=[...])` 生成的并行子 agent 共享这一个容器 —— 并发的 `cd`、环境变量修改以及对同一路径的写入会发生冲突。如果子 agent 需要隔离的沙箱,必须通过 `register_task_env_overrides()` 注册每任务镜像覆盖,RL 和基准测试环境(TerminalBench2、HermesSweEnv 等)会自动为其每任务 Docker 镜像执行此操作。\n\n**安全加固:**\n- `--cap-drop ALL`,仅添加回 `DAC_OVERRIDE`、`CHOWN`、`FOWNER`\n- `--security-opt no-new-privileges`\n- `--pids-limit 256`\n- `/tmp`(512MB)、`/var/tmp`(256MB)、`/run`(64MB)的大小限制 tmpfs\n\n**凭据转发:** `docker_forward_env` 中列出的环境变量首先从您的 shell 环境解析,然后回退到 `~/.hermes/.env`。技能也可以声明 `required_environment_variables`,这些变量会自动合并。\n\n### SSH 后端\n\n通过 SSH 在远程服务器上运行命令。使用 ControlMaster 进行连接复用(5 分钟空闲保活)。默认启用持久 shell —— 状态(cwd、环境变量)在命令之间保持。\n\n```yaml\nterminal:\n backend: ssh\n persistent_shell: true # 保持长期运行的 bash 会话(默认:true)\n```\n\n**必需的环境变量:**\n\n```bash\nTERMINAL_SSH_HOST=my-server.example.com\nTERMINAL_SSH_USER=ubuntu\n```\n\n**可选:**\n\n| 变量 | 默认值 | 描述 |\n|----------|---------|-------------|\n| `TERMINAL_SSH_PORT` | `22` | SSH 端口 |\n| `TERMINAL_SSH_KEY` | (系统默认) | SSH 私钥路径 |\n| `TERMINAL_SSH_PERSISTENT` | `true` | 启用持久 shell |\n\n**工作原理:** 使用 `BatchMode=yes` 和 `StrictHostKeyChecking=accept-new` 在初始化时连接。持久 shell 在远程主机上保持单个 `bash -l` 进程存活,通过临时文件进行通信。需要 `stdin_data` 或 `sudo` 的命令会自动回退到单次模式。\n\n### Modal 后端\n\n在 [Modal](https://modal.com) 云沙箱中运行命令。每个任务获得一个具有可配置 CPU、内存和磁盘的隔离 VM。文件系统可以跨会话快照/恢复。\n\n```yaml\nterminal:\n backend: modal\n container_cpu: 1 # CPU 核心数\n container_memory: 5120 # MB(5GB)\n container_disk: 51200 # MB(50GB)\n container_persistent: true # 快照/恢复文件系统\n```\n\n**必需:** `MODAL_TOKEN_ID` + `MODAL_TOKEN_SECRET` 环境变量,或 `~/.modal.toml` 配置文件。\n\n**持久化:** 启用后,沙箱文件系统在清理时快照,并在下次会话时恢复。快照在 `~/.hermes/modal_snapshots.json` 中跟踪。这保留文件系统状态,而非活跃进程、PID 空间或后台任务。\n\n**凭据文件:** 自动从 `~/.hermes/` 挂载(OAuth token 等),并在每条命令前同步。\n\n### Daytona 后端\n\n在 [Daytona](https://daytona.io) 托管工作区中运行命令。支持停止/恢复以实现持久化。\n\n```yaml\nterminal:\n backend: daytona\n container_cpu: 1 # CPU 核心数\n container_memory: 5120 # MB → 转换为 GiB\n container_disk: 10240 # MB → 转换为 GiB(最大 10 GiB)\n container_persistent: true # 停止/恢复而非删除\n```\n\n**必需:** `DAYTONA_API_KEY` 环境变量。\n\n**持久化:** 启用后,沙箱在清理时停止(而非删除),并在下次会话时恢复。沙箱名称遵循 `hermes-{task_id}` 模式。\n\n**磁盘限制:** Daytona 强制执行 10 GiB 最大值。超过此值的请求将被截断并发出警告。\n\n### Singularity/Apptainer 后端\n\n在 [Singularity/Apptainer](https://apptainer.org) 容器中运行命令。专为 Docker 不可用的 HPC 集群和共享机器设计。\n\n```yaml\nterminal:\n backend: singularity\n singularity_image: \"docker://nikolaik/python-nodejs:python3.11-nodejs20\"\n container_cpu: 1 # CPU 核心数\n container_memory: 5120 # MB\n container_persistent: true # 可写覆盖层跨会话持久化\n```\n\n**要求:** `$PATH` 中有 `apptainer` 或 `singularity` 二进制文件。\n\n**镜像处理:** Docker URL(`docker://...`)自动转换为 SIF 文件并缓存。现有 `.sif` 文件直接使用。\n\n**临时目录:** 按顺序解析:`TERMINAL_SCRATCH_DIR` → `TERMINAL_SANDBOX_DIR/singularity` → `/scratch/$USER/hermes-agent`(HPC 惯例)→ `~/.hermes/sandboxes/singularity`。\n\n**隔离:** 使用 `--containall --no-home` 实现完全命名空间隔离,不挂载宿主 home 目录。\n\n### 常见终端后端问题\n\n如果终端命令立即失败或终端工具报告为已禁用:\n\n- **Local** —— 无特殊要求。入门时最安全的默认选项。\n- **Docker** —— 运行 `docker version` 验证 Docker 是否正常工作。如果失败,修复 Docker 或执行 `hermes config set terminal.backend local`。\n- **SSH** —— `TERMINAL_SSH_HOST` 和 `TERMINAL_SSH_USER` 都必须设置。如果缺少任一项,Hermes 会记录清晰的错误。\n- **Modal** —— 需要 `MODAL_TOKEN_ID` 环境变量或 `~/.modal.toml`。运行 `hermes doctor` 检查。\n- **Daytona** —— 需要 `DAYTONA_API_KEY`。Daytona SDK 处理服务器 URL 配置。\n- **Singularity** —— 需要 `$PATH` 中有 `apptainer` 或 `singularity`。HPC 集群上常见。\n\n如有疑问,将 `terminal.backend` 设回 `local` 并首先验证命令在那里运行。\n\n### 拆卸时远程到宿主文件同步\n\n对于 **SSH**、**Modal** 和 **Daytona** 后端(agent 的工作树位于与运行 Hermes 的宿主不同的机器上),Hermes 跟踪 agent 在远程沙箱中触及的文件,并在会话拆卸/沙箱清理时,将修改的文件**同步回宿主**,存放在 `~/.hermes/cache/remote-syncs/<session-id>/` 下。\n\n- 触发时机:会话关闭、`/new`、`/reset`、gateway 消息超时、子 agent 使用远程后端时 `delegate_task` 子 agent 完成。\n- 覆盖 agent 修改的整个树,而不仅仅是它明确打开的文件。添加、编辑和删除都会被捕获。\n- 远程沙箱可能在您查找时已被拆除;本地 `~/.hermes/cache/remote-syncs/…` 副本是 agent 更改内容的权威记录。\n- 大型二进制输出(模型检查点、原始数据集)按大小限制 —— 同步跳过超过 `file_sync_max_mb`(默认 `100`)的文件。如果您期望更大的工件返回,请调高该值。\n\n```yaml\nterminal:\n file_sync_max_mb: 100 # 默认 —— 同步最大 100 MB 的文件\n file_sync_enabled: true # 默认 —— 设为 false 可完全跳过同步\n```\n\n这是从会话结束后被销毁的临时云沙箱中恢复结果的方式,无需告诉 agent 显式地 `scp` 或 `modal volume put` 每个工件。\n\n### Docker 卷挂载\n\n使用 Docker 后端时,`docker_volumes` 允许您与容器共享宿主目录。每个条目使用标准 Docker `-v` 语法:`host_path:container_path[:options]`。\n\n```yaml\nterminal:\n backend: docker\n docker_volumes:\n - \"/home/user/projects:/workspace/projects\" # 读写(默认)\n - \"/home/user/datasets:/data:ro\" # 只读\n - \"/home/user/.hermes/cache/documents:/output\" # Gateway 可见的导出\n```\n\n适用于:\n- **向 agent 提供文件**(数据集、配置、参考代码)\n- **从 agent 接收文件**(生成的代码、报告、导出)\n- **共享工作区**,您和 agent 都访问相同的文件\n\n如果您使用消息 gateway 并希望 agent 通过 `MEDIA:/...` 发送生成的文件,建议使用专用的宿主可见导出挂载,例如 `/home/user/.hermes/cache/documents:/output`。\n\n- 在 Docker 中将文件写入 `/output/...`\n- 在 `MEDIA:` 中发出**宿主路径**,例如:`MEDIA:/home/user/.hermes/cache/documents/report.txt`\n- **不要**发出 `/workspace/...` 或 `/output/...`,除非该确切路径在宿主上对 gateway 进程也存在\n\n:::warning\nYAML 重复键会静默覆盖之前的键。如果您已有 `docker_volumes:` 块,请将新挂载合并到同一列表中,而不是在文件后面再添加一个 `docker_volumes:` 键。\n:::\n\n也可以通过环境变量设置:`TERMINAL_DOCKER_VOLUMES='[\"/host:/container\"]'`(JSON 数组)。\n\n### Docker 凭据转发\n\n默认情况下,Docker 终端会话不继承任意宿主凭据。如果您需要在容器内使用特定 token,请将其添加到 `terminal.docker_forward_env`。\n\n```yaml\nterminal:\n backend: docker\n docker_forward_env:\n - \"GITHUB_TOKEN\"\n - \"NPM_TOKEN\"\n```\n\nHermes 首先从您当前的 shell 解析每个列出的变量,然后回退到通过 `hermes config set` 保存的 `~/.hermes/.env`。\n\n:::warning\n`docker_forward_env` 中列出的任何内容都会对容器内运行的命令可见。只转发您愿意暴露给终端会话的凭据。\n:::\n\n### 以宿主用户身份运行容器\n\n默认情况下,Docker 容器以 `root`(UID 0)身份运行。在 `/workspace` 或其他绑定挂载中创建的文件在宿主上归 root 所有,因此会话结束后您必须 `sudo chown` 才能从宿主编辑器编辑它们。`terminal.docker_run_as_host_user` 标志解决了这个问题:\n\n```yaml\nterminal:\n backend: docker\n docker_run_as_host_user: true # 默认:false\n```\n\n启用后,Hermes 将 `--user $(id -u):$(id -g)` 附加到 `docker run` 命令,使写入绑定挂载目录(`/workspace`、`/root`、`docker_volumes` 中的任何内容)的文件归您的宿主用户所有,而非 root。权衡:容器将无法再 `apt install` 或写入 `/root/.npm` 等 root 拥有的路径 —— 如果您同时需要这两者,请使用 `HOME` 归非 root 用户所有的基础镜像(或在镜像构建时添加所需工具)。\n\n保持 `false`(默认)以获得向后兼容的行为。当您的工作流主要是\"编辑挂载的宿主文件\"且厌倦了 `sudo chown -R` 时,请开启此选项。\n\n### 可选:将启动目录挂载到 `/workspace`\n\nDocker 沙箱默认保持隔离。Hermes **不会**将您当前的宿主工作目录传入容器,除非您明确选择加入。\n\n在 `config.yaml` 中启用:\n\n```yaml\nterminal:\n backend: docker\n docker_mount_cwd_to_workspace: true\n```\n\n启用后:\n- 如果您从 `~/projects/my-app` 启动 Hermes,该宿主目录将绑定挂载到 `/workspace`\n- Docker 后端从 `/workspace` 开始\n- 文件工具和终端命令都能看到相同的挂载项目\n\n禁用时,`/workspace` 保持沙箱所有,除非您通过 `docker_volumes` 显式挂载内容。\n\n安全权衡:\n- `false` 保留沙箱边界\n- `true` 使沙箱直接访问您启动 Hermes 的目录\n\n仅在您有意希望容器处理实时宿主文件时才选择加入。\n\n### 持久 Shell\n\n默认情况下,每条终端命令在其自己的子进程中运行 —— 工作目录、环境变量和 shell 变量在命令之间重置。启用**持久 shell** 后,单个长期运行的 bash 进程在 `execute()` 调用之间保持存活,使状态在命令之间保持。\n\n这对 **SSH 后端**最有用,它还消除了每条命令的连接开销。持久 shell **对 SSH 默认启用**,对本地后端禁用。\n\n```yaml\nterminal:\n persistent_shell: true # 默认 —— 为 SSH 启用持久 shell\n```\n\n禁用:\n\n```bash\nhermes config set terminal.persistent_shell false\n```\n\n**跨命令保持的内容:**\n- 工作目录(`cd /tmp` 对下一条命令生效)\n- 导出的环境变量(`export FOO=bar`)\n- Shell 变量(`MY_VAR=hello`)\n\n**优先级:**\n\n| 级别 | 变量 | 默认值 |\n|-------|----------|---------|\n| 配置 | `terminal.persistent_shell` | `true` |\n| SSH 覆盖 | `TERMINAL_SSH_PERSISTENT` | 遵循配置 |\n| Local 覆盖 | `TERMINAL_LOCAL_PERSISTENT` | `false` |\n\n每个后端的环境变量具有最高优先级。如果您也想在本地后端使用持久 shell:\n\n```bash\nexport TERMINAL_LOCAL_PERSISTENT=true\n```\n\n:::note\n需要 `stdin_data` 或 sudo 的命令会自动回退到单次模式,因为持久 shell 的 stdin 已被 IPC 协议占用。\n:::\n\n有关每个后端的详细信息,请参阅[代码执行](features/code-execution.md)和 [README 的终端部分](features/tools.md)。\n\n## 技能设置\n\n技能可以通过其 SKILL.md frontmatter 声明自己的配置设置。这些是非机密值(路径、偏好、域设置),存储在 `config.yaml` 的 `skills.config` 命名空间下。\n\n```yaml\nskills:\n config:\n myplugin:\n path: ~/myplugin-data # 示例 —— 每个技能定义自己的键\n```\n\n**技能设置的工作原理:**\n\n- `hermes config migrate` 扫描所有已启用的技能,找到未配置的设置,并提供提示\n- `hermes config show` 在\"技能设置\"下显示所有技能设置及其所属技能\n- 技能加载时,其解析的配置值会自动注入到技能上下文中\n\n**手动设置值:**\n\n```bash\nhermes config set skills.config.myplugin.path ~/myplugin-data\n```\n\n有关在您自己的技能中声明配置设置的详细信息,请参阅[创建技能 — 配置设置](/developer-guide/creating-skills#config-settings-configyaml)。\n\n### Agent 创建技能写入的守卫\n\n当 agent 使用 `skill_manage` 创建、编辑、修补或删除技能时,Hermes 可以选择扫描新/更新的内容以查找危险关键字模式(凭据收集、明显的 prompt 注入、数据外泄指令)。扫描器**默认关闭** —— 合法触及 `~/.ssh/` 或提及 `$OPENAI_API_KEY` 的真实 agent 工作流触发启发式规则过于频繁。如果您希望扫描器在 agent 的技能写入落地前提示您,请重新开启:\n\n```yaml\nskills:\n guard_agent_created: true # 默认:false\n```\n\n开启后,任何被标记的 `skill_manage` 写入都会以审批提示的形式出现,并附带扫描器的理由。接受的写入落地;拒绝的写入向 agent 返回解释性错误。\n\n## 内存配置\n\n```yaml\nmemory:\n memory_enabled: true\n user_profile_enabled: true\n memory_char_limit: 2200 # ~800 tokens\n user_char_limit: 1375 # ~500 tokens\n```\n\n## 文件读取安全\n\n控制单次 `read_file` 调用可以返回多少内容。超过限制的读取将被拒绝,并向 agent 返回错误,提示使用 `offset` 和 `limit` 读取较小范围。这可以防止单次读取压缩的 JS 包或大型数据文件时淹没上下文窗口。\n\n```yaml\nfile_read_max_chars: 100000 # 默认 —— ~25-35K tokens\n```\n\n如果您使用具有大上下文窗口的模型并经常读取大文件,请调高此值。对于小上下文模型,请降低以保持读取高效:\n\n```yaml\n# 大上下文模型(200K+)\nfile_read_max_chars: 200000\n\n# 小型本地模型(16K 上下文)\nfile_read_max_chars: 30000\n```\n\nAgent 还会自动去重文件读取 —— 如果同一文件区域被读取两次且文件未更改,则返回轻量级存根而不是重新发送内容。这在上下文压缩后重置,以便 agent 在内容被摘要后可以重新读取文件。\n\n## 工具输出截断限制\n\n三个相关的上限控制工具在 Hermes 截断之前可以返回多少原始输出:\n\n```yaml\ntool_output:\n max_bytes: 50000 # 终端输出上限(字符)\n max_lines: 2000 # read_file 分页上限\n max_line_length: 2000 # read_file 行号视图中的每行上限\n```\n\n- **`max_bytes`** —— 当 `terminal` 命令产生超过此字符数的合并 stdout/stderr 时,Hermes 保留前 40% 和后 60%,并在中间插入 `[OUTPUT TRUNCATED]` 通知。默认 `50000`(典型分词器约 12-15K tokens)。\n- **`max_lines`** —— 单次 `read_file` 调用的 `limit` 参数上限。超过此值的请求将被截断,以防单次读取淹没上下文窗口。默认 `2000`。\n- **`max_line_length`** —— `read_file` 发出行号视图时应用的每行上限。超过此长度的行将被截断为此字符数,后跟 `... [truncated]`。默认 `2000`。\n\n对于具有大上下文窗口且每次调用可以承受更多原始输出的模型,请调高限制。对于小上下文模型,请降低以保持工具结果紧凑:\n\n```yaml\n# 大上下文模型(200K+)\ntool_output:\n max_bytes: 150000\n max_lines: 5000\n\n# 小型本地模型(16K 上下文)\ntool_output:\n max_bytes: 20000\n max_lines: 500\n```\n\n## 全局工具集禁用\n\n要在 CLI 和每个 gateway 平台上统一禁用特定工具集,请在 `agent.disabled_toolsets` 下列出其名称:\n\n```yaml\nagent:\n disabled_toolsets:\n - memory # 隐藏内存工具 + MEMORY_GUIDANCE 注入\n - web # 任何地方都不使用 web_search / web_extract\n```\n\n这在每个平台的工具配置(由 `hermes tools` 写入的 `platform_toolsets`)**之后**应用,因此此处列出的工具集始终被删除 —— 即使平台的已保存配置仍然列出它。当您希望有一个\"到处关闭 X\"的单一开关而不是编辑 `hermes tools` UI 中的 15+ 个平台行时,请使用此选项。\n\n留空列表或省略键不会产生任何效果。\n\n## Git Worktree 隔离\n\n启用隔离的 git worktree,以便在同一仓库上并行运行多个 agent:\n\n```yaml\nworktree: true # 始终创建 worktree(与 hermes -w 相同)\n# worktree: false # 默认 —— 仅在传递 -w 标志时\n```\n\n启用后,每个 CLI 会话在 `.worktrees/` 下创建一个带有自己分支的新 worktree。Agent 可以编辑文件、提交、推送和创建 PR,而不会相互干扰。干净的 worktree 在退出时删除;脏的 worktree 保留以供手动恢复。\n\n您还可以通过仓库根目录中的 `.worktreeinclude` 列出要复制到 worktree 的 gitignore 文件:\n\n```\n# .worktreeinclude\n.env\n.venv/\nnode_modules/\n```\n\n## 上下文压缩\n\nHermes 自动压缩长对话以保持在模型的上下文窗口内。压缩摘要器是一个单独的 LLM 调用 —— 您可以将其指向任何 provider 或端点。\n\n所有压缩设置都在 `config.yaml` 中(无环境变量)。\n\n### 完整参考\n\n```yaml\ncompression:\n enabled: true # 开启/关闭压缩\n threshold: 0.50 # 在上下文限制的此百分比时压缩\n target_ratio: 0.20 # 保留为最近尾部的阈值分数\n protect_last_n: 20 # 保持未压缩的最少最近消息数\n hygiene_hard_message_limit: 5000 # Gateway 安全阀 —— 见下文\n\n# 摘要模型/provider 在 auxiliary: 下配置:\nauxiliary:\n compression:\n model: \"\" # 空 = 使用主聊天模型。覆盖为例如 \"google/gemini-3-flash-preview\" 以获得更便宜/更快的压缩。\n provider: \"auto\" # Provider:\"auto\"、\"openrouter\"、\"nous\"、\"codex\"、\"main\" 等\n base_url: null # 自定义 OpenAI 兼容端点(覆盖 provider)\n```\n\n:::info 旧版配置迁移\n带有 `compression.summary_model`、`compression.summary_provider` 和 `compression.summary_base_url` 的旧版配置在首次加载时自动迁移到 `auxiliary.compression.*`(配置版本 17)。无需手动操作。\n:::\n\n`hygiene_hard_message_limit` 是仅限 gateway 的**预压缩安全阀**。它的存在是为了打破一个死循环:当超大会话的 API 调用持续断开时,gateway 永远收不到 token 使用数据,基于 token 的阈值因此无法触发,于是 transcript 持续增长、断开愈发严重。这个基于消息数的下限仅凭消息数量触发(无论 API 是否失败,消息数始终已知),强制压缩以恢复会话。默认 `5000` —— 远高于任何正常会话,包括做数千次短轮次的大上下文(1M+)模型,它们早就在 token 阈值处压缩了。对于异常平台可调得更高;要强制更积极的压缩则调低。在运行中的 gateway 上编辑此值将在下一条消息时生效(见下文)。\n\n:::tip Gateway 热重载压缩和上下文长度\n从最近的版本开始,在运行中的 gateway 上编辑 `config.yaml` 中的 `model.context_length` 或任何 `compression.*` 键将在下一条消息时生效 —— 无需 gateway 重启、`/reset` 或会话轮换。缓存的 agent 签名包含这些键,因此 gateway 在检测到更改时会透明地重建 agent。API 密钥和工具/技能配置仍需要通常的重载路径。\n:::\n\n### 常见设置\n\n**默认(自动检测)—— 无需配置:**\n```yaml\ncompression:\n enabled: true\n threshold: 0.50\n```\n使用您的主 provider 和主模型。如果您希望在比主聊天模型更便宜的模型上进行压缩,请覆盖每任务(例如 `auxiliary.compression.provider: openrouter` + `model: google/gemini-2.5-flash`)。\n\n**强制特定 provider**(基于 OAuth 或 API 密钥):\n```yaml\nauxiliary:\n compression:\n provider: nous\n model: gemini-3-flash\n```\n适用于任何 provider:`nous`、`openrouter`、`codex`、`anthropic`、`main` 等。\n\n**自定义端点**(自托管、Ollama、zai、DeepSeek 等):\n```yaml\nauxiliary:\n compression:\n model: glm-4.7\n base_url: https://api.z.ai/api/coding/paas/v4\n```\n指向自定义 OpenAI 兼容端点。使用 `OPENAI_API_KEY` 进行认证。\n\n### 三个旋钮的交互方式\n\n| `auxiliary.compression.provider` | `auxiliary.compression.base_url` | 结果 |\n|---------------------|---------------------|--------|\n| `auto`(默认) | 未设置 | 自动检测最佳可用 provider |\n| `nous` / `openrouter` / 等 | 未设置 | 强制使用该 provider,使用其认证 |\n| 任意 | 已设置 | 直接使用自定义端点(忽略 provider) |\n\n:::warning 摘要模型上下文长度要求\n摘要模型**必须**具有至少与您的主 agent 模型一样大的上下文窗口。压缩器将对话的完整中间部分发送给摘要模型 —— 如果该模型的上下文窗口小于主模型的,摘要调用将因上下文长度错误而失败。发生这种情况时,中间轮次将**在没有摘要的情况下被丢弃**,静默丢失对话上下文。如果您覆盖模型,请验证其上下文长度满足或超过您的主模型。\n:::\n\n## 上下文引擎\n\n上下文引擎控制在接近模型 token 限制时如何管理对话。内置的 `compressor` 引擎使用有损摘要(参见[上下文压缩](/developer-guide/context-compression-and-caching))。插件引擎可以用替代策略替换它。\n\n```yaml\ncontext:\n engine: \"compressor\" # 默认 —— 内置有损摘要\n```\n\n使用插件引擎(例如,用于无损上下文管理的 LCM):\n\n```yaml\ncontext:\n engine: \"lcm\" # 必须与插件名称匹配\n```\n\n插件引擎**永远不会自动激活** —— 您必须将 `context.engine` 显式设置为插件名称。可用引擎可以通过 `hermes plugins` → Provider Plugins → Context Engine 浏览和选择。\n\n有关内存插件的类似单选系统,请参阅[内存 Providers](/user-guide/features/memory-providers)。\n\n## 迭代预算压力\n\n当 agent 在处理具有许多工具调用的复杂任务时,它可能会在没有意识到预算不足的情况下耗尽其迭代预算(默认:90 轮)。预算压力会在模型接近限制时自动发出警告:\n\n| 阈值 | 级别 | 模型看到的内容 |\n|-----------|-------|---------------------|\n| **70%** | 注意 | `[BUDGET: 63/90. 27 iterations left. Start consolidating.]` |\n| **90%** | 警告 | `[BUDGET WARNING: 81/90. Only 9 left. Respond NOW.]` |\n\n警告注入到最后一个工具结果的 JSON 中(作为 `_budget_warning` 字段),而不是作为单独的消息 —— 这保留了 prompt 缓存,不会破坏对话结构。\n\n```yaml\nagent:\n max_turns: 90 # 每次对话轮次的最大迭代次数(默认:90)\n api_max_retries: 3 # 回退启动前每个 provider 的重试次数(默认:3)\n```\n\n预算压力默认启用。Agent 自然地将警告视为工具结果的一部分,鼓励它在耗尽迭代之前整合工作并提供响应。\n\n当迭代预算完全耗尽时,CLI 向用户显示通知:`⚠ Iteration budget reached (90/90) — response may be incomplete`。如果预算在活跃工作期间耗尽,agent 会在停止前生成已完成内容的摘要。\n\n`agent.api_max_retries` 控制 Hermes 在回退 provider 切换启动**之前**对瞬时错误(速率限制、连接断开、5xx)重试 provider API 调用的次数。默认为 `3` —— 总共四次尝试。如果您配置了[回退 providers](/user-guide/features/fallback-providers) 并希望更快地故障转移,请将其降至 `0`,这样主 provider 上的第一个瞬时错误会立即切换到回退,而不是对不稳定的端点进行重试。\n\n### API 超时\n\nHermes 对流式传输有单独的超时层,以及用于非流式调用的陈旧检测器。陈旧检测器仅在您将其保留为隐式默认值时才会自动调整本地 provider。\n\n| 超时 | 默认值 | 本地 providers | 配置/环境变量 |\n|---------|---------|----------------|--------------|\n| Socket 读取超时 | 120s | 自动提升至 1800s | `HERMES_STREAM_READ_TIMEOUT` |\n| 陈旧流检测 | 180s | 自动禁用 | `HERMES_STREAM_STALE_TIMEOUT` |\n| 陈旧非流检测 | 300s | 保持隐式时自动禁用 | `providers.<id>.stale_timeout_seconds` 或 `HERMES_API_CALL_STALE_TIMEOUT` |\n| API 调用(非流式) | 1800s | 不变 | `providers.<id>.request_timeout_seconds` / `timeout_seconds` 或 `HERMES_API_TIMEOUT` |\n\n**Socket 读取超时**控制 httpx 等待 provider 下一个数据块的时间。本地 LLM 在大上下文上预填充可能需要几分钟才能产生第一个 token,因此当 Hermes 检测到本地端点时,会将此值提升至 30 分钟。如果您显式设置 `HERMES_STREAM_READ_TIMEOUT`,无论端点检测如何,始终使用该值。\n\n**陈旧流检测**终止接收 SSE 保活 ping 但没有实际内容的连接。对于本地 providers,这完全禁用,因为它们在预填充期间不发送保活 ping。\n\n**陈旧非流检测**终止长时间没有响应的非流式调用。默认情况下,Hermes 在本地端点上禁用此功能,以避免长时间预填充期间的误报。如果您显式设置 `providers.<id>.stale_timeout_seconds`、`providers.<id>.models.<model>.stale_timeout_seconds` 或 `HERMES_API_CALL_STALE_TIMEOUT`,即使在本地端点上也会遵守该显式值。\n\n## 上下文压力警告\n\n与迭代预算压力分开,上下文压力跟踪对话距**压缩阈值**有多近 —— 即上下文压缩触发以摘要旧消息的点。这有助于您和 agent 了解对话何时变长。\n\n| 进度 | 级别 | 发生的事情 |\n|----------|-------|-------------|\n| **≥ 60%** 到阈值 | 信息 | CLI 显示青色进度条;gateway 发送信息通知 |\n| **≥ 85%** 到阈值 | 警告 | CLI 显示粗体黄色进度条;gateway 警告压缩即将发生 |\n\n在 CLI 中,上下文压力在工具输出流中显示为进度条:\n\n```\n ◐ context ████████████░░░░░░░░ 62% to compaction 48k threshold (50%) · approaching compaction\n```\n\n在消息平台上,发送纯文本通知:\n\n```\n◐ Context: ████████████░░░░░░░░ 62% to compaction (threshold: 50% of window).\n```\n\n如果自动压缩被禁用,警告会告诉您上下文可能被截断。\n\n上下文压力是自动的 —— 无需配置。它纯粹作为面向用户的通知触发,不修改消息流或向模型上下文注入任何内容。\n\n## 凭据池策略\n\n当您为同一 provider 拥有多个 API 密钥或 OAuth token 时,配置轮换策略:\n\n```yaml\ncredential_pool_strategies:\n openrouter: round_robin # 均匀循环使用密钥\n anthropic: least_used # 始终选择使用最少的密钥\n```\n\n选项:`fill_first`(默认)、`round_robin`、`least_used`、`random`。完整文档请参阅[凭据池](/user-guide/features/credential-pools)。\n\n## Prompt 缓存\n\n当活跃 provider 支持时,Hermes 自动开启跨会话 prompt 缓存 —— 无需用户配置。\n\n对于**原生 Anthropic**、**OpenRouter** 和 **Nous Portal** 上的 Claude,Hermes 在系统提示词和技能块上附加带有 1 小时 TTL(`ttl: \"1h\"`)的 `cache_control` 断点。在新鲜的一小时内首次发送时按完整输入费率计费;同一小时内任何会话的后续发送以折扣缓存读取费率从缓存中提取。这意味着系统提示词、加载的技能内容以及任何长上下文包含的早期部分在第一个小时内跨 `hermes` 会话和分叉子 agent 被重用。\n\nQwen Cloud(阿里巴巴 DashScope)上游将缓存 TTL 限制为 5 分钟,因此 Hermes 在那里使用 5 分钟断点 TTL。其他通过第三方的 Claude 路径(AWS Bedrock、Azure Foundry)回退到 provider 自己的缓存默认值。xAI Grok 使用单独的会话固定对话 ID 机制 —— 参阅 [xAI prompt 缓存](/integrations/providers#xai-grok--responses-api--prompt-caching)。\n\n不存在禁用此功能的旋钮 —— 缓存始终开启,即使在单轮对话中也能节省费用,因为仅系统提示词就占输入 token 数的相当大比例。\n\n## 辅助模型\n\nHermes 使用\"辅助\"模型处理图像分析、网页摘要、浏览器截图分析、会话标题生成和上下文压缩等附带任务。默认情况下(`auxiliary.*.provider: \"auto\"`),Hermes 将每个辅助任务路由到您的**主聊天模型** —— 与您在 `hermes model` 中选择的相同 provider/模型。您无需配置任何内容即可开始,但请注意,在昂贵的推理模型(Opus、MiniMax M2.7 等)上,辅助任务会增加显著成本。如果您希望无论主模型如何都使用便宜且快速的附带任务,请显式设置 `auxiliary.<task>.provider` 和 `auxiliary.<task>.model`(例如,在 OpenRouter 上使用 Gemini Flash 进行视觉和网页提取)。\n\n:::note 为什么 \"auto\" 使用您的主模型\n早期版本将聚合器用户(OpenRouter、Nous Portal)分流到便宜的 provider 端默认值。这令人惊讶 —— 付费购买聚合器订阅的用户会看到不同的模型处理其辅助流量。`auto` 现在对所有人使用主模型,`config.yaml` 中的每任务覆盖仍然优先(见下方[完整辅助配置参考](#full-auxiliary-config-reference))。\n:::\n\n### 交互式配置辅助模型\n\n无需手动编辑 YAML,运行 `hermes model` 并从菜单中选择**\"配置辅助模型\"**。您将获得交互式的每任务选择器:\n\n```\n$ hermes model\n→ Configure auxiliary models\n\n[ ] vision currently: auto / main model\n[ ] web_extract currently: auto / main model\n[ ] title_generation currently: openrouter / google/gemini-3-flash-preview\n[ ] compression currently: auto / main model\n[ ] approval currently: auto / main model\n[ ] triage_specifier currently: auto / main model\n[ ] kanban_decomposer currently: auto / main model\n[ ] profile_describer currently: auto / main model\n```\n\n选择任务,选择 provider(OAuth 流程打开浏览器;API 密钥 provider 提示输入),选择模型。更改持久化到 `config.yaml` 中的 `auxiliary.<task>.*`。与主模型选择器相同的机制 —— 无需学习额外语法。\n\n### 视频教程\n\n<div style={{position: 'relative', width: '100%', aspectRatio: '16 / 9', marginBottom: '1.5rem'}}>\n <iframe\n src=\"https://www.youtube.com/embed/NoF-YajElIM\"\n title=\"Hermes Agent — Auxiliary Models Tutorial\"\n style={{position: 'absolute', top: 0, left: 0, width: '100%', height: '100%', border: 0}}\n allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\"\n allowFullScreen\n />\n</div>\n\n### 通用配置模式\n\nHermes 中的每个模型槽位 —— 辅助任务、压缩、回退 —— 使用相同的三个旋钮:\n\n| 键 | 作用 | 默认值 |\n|-----|-------------|---------|\n| `provider` | 用于认证和路由的 provider | `\"auto\"` |\n| `model` | 请求的模型 | provider 的默认值 |\n| `base_url` | 自定义 OpenAI 兼容端点(覆盖 provider) | 未设置 |\n\n当设置 `base_url` 时,Hermes 忽略 provider 并直接调用该端点(使用 `api_key` 或 `OPENAI_API_KEY` 进行认证)。当仅设置 `provider` 时,Hermes 使用该 provider 的内置认证和基础 URL。\n\n辅助任务的可用 providers:`auto`、`main`,以及[provider 注册表](/reference/environment-variables)中的任何 provider —— `openrouter`、`nous`、`openai-codex`、`copilot`、`copilot-acp`、`anthropic`、`gemini`、`google-gemini-cli`、`qwen-oauth`、`zai`、`kimi-coding`、`kimi-coding-cn`、`minimax`、`minimax-cn`、`minimax-oauth`、`deepseek`、`nvidia`、`xai`、`xai-oauth`、`ollama-cloud`、`alibaba`、`bedrock`、`huggingface`、`arcee`、`xiaomi`、`kilocode`、`opencode-zen`、`opencode-go`、`azure-foundry` —— 或您 `custom_providers` 列表中任何命名的自定义 provider(例如 `provider: \"beans\"`)。\n\n:::tip MiniMax OAuth\n`minimax-oauth` 通过浏览器 OAuth 登录(无需 API 密钥)。运行 `hermes model` 并选择 **MiniMax (OAuth)** 进行认证。辅助任务自动使用 `MiniMax-M2.7-highspeed`。参阅 [MiniMax OAuth 指南](../guides/minimax-oauth.md)。\n:::\n\n:::tip xAI Grok OAuth\n`xai-oauth` 通过浏览器 OAuth 为 SuperGrok 和 X Premium+ 订阅者登录(无需 API 密钥)。运行 `hermes model` 并选择 **xAI Grok OAuth (SuperGrok / Premium+)** 进行认证。相同的 OAuth token 可重用于每个直接到 xAI 的接口(聊天、辅助任务、TTS、图像生成、视频生成、转录)。参阅 [xAI Grok OAuth 指南](../guides/xai-grok-oauth.md),如果 Hermes 在远程主机上,请参阅 [SSH/远程主机上的 OAuth](../guides/oauth-over-ssh.md)。\n:::\n\n:::warning `\"main\"` 仅用于辅助任务\n`\"main\"` provider 选项表示\"使用我的主 agent 使用的任何 provider\" —— 它仅在 `auxiliary:`、`compression:` 和 `fallback_model:` 配置中有效。它**不是**顶级 `model.provider` 设置的有效值。如果您使用自定义 OpenAI 兼容端点,请在 `model:` 部分设置 `provider: custom`。所有主模型 provider 选项请参阅 [AI Providers](/integrations/providers)。\n:::\n\n### 完整辅助配置参考\n\n```yaml\nauxiliary:\n # 图像分析(vision_analyze 工具 + 浏览器截图)\n vision:\n provider: \"auto\" # \"auto\"、\"openrouter\"、\"nous\"、\"codex\"、\"main\" 等\n model: \"\" # 例如 \"openai/gpt-4o\"、\"google/gemini-2.5-flash\"\n base_url: \"\" # 自定义 OpenAI 兼容端点(覆盖 provider)\n api_key: \"\" # base_url 的 API 密钥(回退到 OPENAI_API_KEY)\n timeout: 120 # 秒 —— LLM API 调用超时;视觉负载需要宽裕的超时\n download_timeout: 30 # 秒 —— 图像 HTTP 下载;慢速连接请增加\n\n # 网页摘要 + 浏览器页面文本提取\n web_extract:\n provider: \"auto\"\n model: \"\" # 例如 \"google/gemini-2.5-flash\"\n base_url: \"\"\n api_key: \"\"\n timeout: 360 # 秒(6 分钟)—— 每次尝试的 LLM 摘要\n\n # 危险命令审批分类器\n approval:\n provider: \"auto\"\n model: \"\"\n base_url: \"\"\n api_key: \"\"\n timeout: 30 # 秒\n\n # 上下文压缩超时(与 compression.* 配置分开)\n compression:\n timeout: 120 # 秒 —— 压缩摘要长对话,需要更多时间\n # fallback_chain: # 可选 —— 发生速率限制/连接故障时尝试的 provider\n # - provider: nous\n # model: deepseek/deepseek-chat\n # - provider: openrouter\n # model: google/gemini-2.5-flash\n # base_url: \"\"\n # api_key: \"\"\n\n # 技能中心 —— 技能匹配和搜索\n skills_hub:\n provider: \"auto\"\n model: \"\"\n base_url: \"\"\n api_key: \"\"\n timeout: 30\n\n # MCP 工具调度\n mcp:\n provider: \"auto\"\n model: \"\"\n base_url: \"\"\n api_key: \"\"\n timeout: 30\n\n # Kanban 分类规格说明器 —— `hermes kanban specify <id>`(或\n # 仪表板上 Triage 列卡片的 ✨ Specify 按钮)使用此\n # 槽位将单行描述扩展为具体规格并将\n # 任务提升到 `todo`。便宜快速的模型在这里效果很好;规格扩展\n # 很短,不需要推理深度。\n triage_specifier:\n provider: \"auto\"\n model: \"\"\n base_url: \"\"\n api_key: \"\"\n timeout: 120\n```\n\n:::tip\n每个辅助任务都有可配置的 `timeout`(秒)。默认值:vision 120s、web_extract 360s、approval 30s、compression 120s。如果您为辅助任务使用慢速本地模型,请增加这些值。Vision 还有单独的 `download_timeout`(默认 30s)用于 HTTP 图像下载 —— 对于慢速连接或自托管图像服务器,请增加此值。\n:::\n\n:::info\n上下文压缩有自己的 `compression:` 块用于阈值,以及 `auxiliary.compression:` 块用于模型/provider 设置 —— 参阅上方的[上下文压缩](#context-compression)。主备用链使用顶层的 `fallback_providers:` 列表 —— 参阅[备用提供商](/integrations/providers#fallback-providers)。三者都遵循相同的 provider/model/base_url 模式。\n:::\n\n### 辅助任务的每任务回退链\n\n每个辅助任务都可以选择性地定义一个 `fallback_chain` —— 一个 provider/model 条目列表,当主要辅助 provider 因速率限制、网络连接问题或付费限制而失败时,Hermes 会尝试使用该列表:\n\n```yaml\nauxiliary:\n compression:\n provider: openrouter\n model: openai/gpt-4o-mini\n fallback_chain:\n - provider: nous\n model: deepseek/deepseek-chat\n - provider: openrouter\n model: google/gemini-2.5-flash\n```\n\n当主要辅助 provider(`openrouter` / `openai/gpt-4o-mini`)返回速率限制、连接超时或需要付费错误时,Hermes 将依次遍历 `fallback_chain`。它会跳过 provider 与已失败 provider 相同的条目,并尝试每个剩余条目,直到有一个成功或该链耗尽。如果所有回退都失败,Hermes 会回退到主 agent 模型作为最终的安全网。\n\n每个条目支持与任何辅助任务配置相同的三个旋钮:\n\n| 键 | 描述 |\n|-----|-------------|\n| `provider` | Provider 名称(`nous`、`openrouter`、`anthropic`、`gemini`、`main` 等) |\n| `model` | 该 provider 的模型名称 |\n| `base_url` | (可选)自定义 OpenAI 兼容端点 |\n\n`fallback_chain` 适用于任何辅助任务 —— `compression`、`vision`、`web_extract`、`approval`、`skills_hub`、`mcp` 等。\n\n### OpenRouter 路由和辅助任务的 Pareto Code\n\n当辅助任务解析到 OpenRouter(显式或通过 `provider: \"main\"` 而您的主 agent 在 OpenRouter 上)时,主 agent 的 `provider_routing` 和 `openrouter.min_coding_score` 设置**不会传播** —— 按设计,每个辅助任务是独立的。要为特定辅助任务设置 OpenRouter provider 偏好或使用 [Pareto Code 路由器](/integrations/providers#openrouter-pareto-code-router),请通过 `extra_body` 按任务设置:\n\n```yaml\nauxiliary:\n compression:\n provider: openrouter\n model: openrouter/pareto-code # 为此任务使用 Pareto Code 路由器\n extra_body:\n provider: # OpenRouter provider 路由偏好\n order: [anthropic, google] # 按此顺序尝试这些 providers\n sort: throughput # 或 \"price\" | \"latency\"\n # only: [anthropic] # 限制到特定 provider\n # ignore: [deepinfra] # 排除特定 providers\n plugins: # OpenRouter Pareto Code 路由器旋钮\n - id: pareto-router\n min_coding_score: 0.5 # 0.0–1.0;越高 = 更强的编码能力\n```\n\n形状与 OpenRouter 在聊天补全请求体中接受的内容一致。Hermes 原样转发整个 `extra_body`,因此 [openrouter.ai/docs](https://openrouter.ai/docs) 中记录的任何其他 OpenRouter 请求体字段都以相同方式工作。\n\n### 更改视觉模型\n\n使用 GPT-4o 而非 Gemini Flash 进行图像分析:\n\n```yaml\nauxiliary:\n vision:\n model: \"openai/gpt-4o\"\n```\n\n或通过环境变量(在 `~/.hermes/.env` 中):\n\n```bash\nAUXILIARY_VISION_MODEL=openai/gpt-4o\n```\n\n### Provider 选项\n\n这些选项适用于**辅助任务配置**(`auxiliary:`、`compression:`、`fallback_model:`),而非您的主 `model.provider` 设置。\n\n| Provider | 描述 | 要求 |\n|----------|-------------|-------------|\n| `\"auto\"` | 最佳可用(默认)。Vision 尝试 OpenRouter → Nous → Codex。 | — |\n| `\"openrouter\"` | 强制 OpenRouter —— 路由到任何模型(Gemini、GPT-4o、Claude 等) | `OPENROUTER_API_KEY` |\n| `\"nous\"` | 强制 Nous Portal | `hermes auth` |\n| `\"codex\"` | 强制 Codex OAuth(ChatGPT 账户)。支持视觉(gpt-5.3-codex)。 | `hermes model` → Codex |\n| `\"minimax-oauth\"` | 强制 MiniMax OAuth(浏览器登录,无需 API 密钥)。辅助任务使用 MiniMax-M2.7-highspeed。 | `hermes model` → MiniMax (OAuth) |\n| `\"xai-oauth\"` | 强制 xAI Grok OAuth(SuperGrok 或 X Premium+ 订阅者的浏览器登录,无需 API 密钥)。相同的 OAuth token 涵盖聊天、TTS、图像、视频和转录。 | `hermes model` → xAI Grok OAuth (SuperGrok / Premium+) |\n| `\"main\"` | 使用您的活跃自定义/主端点。可以来自 `OPENAI_BASE_URL` + `OPENAI_API_KEY` 或通过 `hermes model` / `config.yaml` 保存的自定义端点。适用于 OpenAI、本地模型或任何 OpenAI 兼容 API。**仅限辅助任务 —— 对 `model.provider` 无效。** | 自定义端点凭据 + 基础 URL |\n\n当您希望附带任务绕过默认路由器时,主 provider 目录中的直接 API 密钥 providers 也在这里工作。配置 `GMI_API_KEY` 后,`gmi` 有效:\n\n```yaml\nauxiliary:\n compression:\n provider: \"gmi\"\n model: \"anthropic/claude-opus-4.6\"\n```\n\n对于 GMI 辅助路由,使用 GMI 的 `/v1/models` 端点返回的确切模型 ID。\n\n### 常见设置\n\n**使用直接自定义端点**(比 `provider: \"main\"` 对本地/自托管 API 更清晰):\n```yaml\nauxiliary:\n vision:\n base_url: \"http://localhost:1234/v1\"\n api_key: \"local-key\"\n model: \"qwen2.5-vl\"\n```\n\n`base_url` 优先于 `provider`,因此这是将辅助任务路由到特定端点的最明确方式。对于直接端点覆盖,Hermes 使用配置的 `api_key` 或回退到 `OPENAI_API_KEY`;它不会为该自定义端点重用 `OPENROUTER_API_KEY`。\n\n**使用 OpenAI API 密钥进行视觉:**\n```yaml\n# 在 ~/.hermes/.env 中:\n# OPENAI_BASE_URL=https://api.openai.com/v1\n# OPENAI_API_KEY=sk-...\n\nauxiliary:\n vision:\n provider: \"main\"\n model: \"gpt-4o\" # 或 \"gpt-4o-mini\" 更便宜\n```\n\n**使用 OpenRouter 进行视觉**(路由到任何模型):\n```yaml\nauxiliary:\n vision:\n provider: \"openrouter\"\n model: \"openai/gpt-4o\" # 或 \"google/gemini-2.5-flash\" 等\n```\n\n**使用 Codex OAuth**(ChatGPT Pro/Plus 账户 —— 无需 API 密钥):\n```yaml\nauxiliary:\n vision:\n provider: \"codex\" # 使用您的 ChatGPT OAuth token\n # 模型默认为 gpt-5.3-codex(支持视觉)\n```\n\n**使用 MiniMax OAuth**(浏览器登录,无需 API 密钥):\n```yaml\nmodel:\n default: MiniMax-M2.7\n provider: minimax-oauth\n base_url: https://api.minimax.io/anthropic\n```\n运行 `hermes model` 并选择 **MiniMax (OAuth)** 自动登录并设置此项。对于中国区域,基础 URL 将是 `https://api.minimaxi.com/anthropic`。完整演练请参阅 [MiniMax OAuth 指南](../guides/minimax-oauth.md)。\n\n**使用本地/自托管模型:**\n```yaml\nauxiliary:\n vision:\n provider: \"main\" # 使用您的活跃自定义端点\n model: \"my-local-model\"\n```\n\n`provider: \"main\"` 使用 Hermes 用于普通聊天的任何 provider —— 无论是命名的自定义 provider(例如 `beans`)、内置 provider(如 `openrouter`)还是旧版 `OPENAI_BASE_URL` 端点。\n\n:::tip\n如果您使用 Codex OAuth 作为主模型 provider,视觉会自动工作 —— 无需额外配置。Codex 包含在视觉的自动检测链中。\n:::\n\n:::warning\n**视觉需要多模态模型。** 如果您设置 `provider: \"main\"`,请确保您的端点支持多模态/视觉 —— 否则图像分析将失败。\n:::\n\n### 环境变量(旧版)\n\n辅助模型也可以通过环境变量配置。但是,`config.yaml` 是首选方法 —— 它更易于管理,并支持所有选项,包括 `base_url` 和 `api_key`。\n\n| 设置 | 环境变量 |\n|---------|---------------------|\n| Vision provider | `AUXILIARY_VISION_PROVIDER` |\n| Vision 模型 | `AUXILIARY_VISION_MODEL` |\n| Vision 端点 | `AUXILIARY_VISION_BASE_URL` |\n| Vision API 密钥 | `AUXILIARY_VISION_API_KEY` |\n| Web 提取 provider | `AUXILIARY_WEB_EXTRACT_PROVIDER` |\n| Web 提取模型 | `AUXILIARY_WEB_EXTRACT_MODEL` |\n| Web 提取端点 | `AUXILIARY_WEB_EXTRACT_BASE_URL` |\n| Web 提取 API 密钥 | `AUXILIARY_WEB_EXTRACT_API_KEY` |\n\n压缩和回退模型设置仅限 config.yaml。\n\n:::tip\n运行 `hermes config` 查看您当前的辅助模型设置。覆盖仅在与默认值不同时显示。\n:::\n\n## 推理努力程度\n\n控制模型在响应前进行多少\"思考\":\n\n```yaml\nagent:\n reasoning_effort: \"\" # 空 = 中等(默认)。选项:none、minimal、low、medium、high、xhigh(最大)\n```\n\n未设置时(默认),推理努力程度默认为\"medium\" —— 适合大多数任务的平衡级别。设置值会覆盖它 —— 更高的推理努力程度在复杂任务上提供更好的结果,但代价是更多 token 和延迟。\n\n您也可以在运行时使用 `/reasoning` 命令更改推理努力程度:\n\n```\n/reasoning # 显示当前努力程度和显示状态\n/reasoning high # 将推理努力程度设为 high\n/reasoning none # 禁用推理\n/reasoning show # 在每次响应上方显示模型思考\n/reasoning hide # 隐藏模型思考\n```\n\n## 工具使用强制\n\n某些模型偶尔会将预期操作描述为文本而不是进行工具调用(\"我会运行测试...\"而不是实际调用终端)。工具使用强制会注入系统提示词指导,引导模型实际调用工具。\n\n```yaml\nagent:\n tool_use_enforcement: \"auto\" # \"auto\" | true | false | [\"model-substring\", ...]\n```\n\n| 值 | 行为 |\n|-------|----------|\n| `\"auto\"`(默认) | 对匹配以下模型启用:`gpt`、`codex`、`gemini`、`gemma`、`grok`。对所有其他模型禁用(Claude、DeepSeek、Qwen 等)。 |\n| `true` | 始终启用,无论模型如何。如果您注意到当前模型描述操作而不是执行操作,请使用此选项。 |\n| `false` | 始终禁用,无论模型如何。 |\n| `[\"gpt\", \"codex\", \"qwen\", \"llama\"]` | 仅当模型名称包含列出的子字符串之一时启用(不区分大小写)。 |\n\n### 注入的内容\n\n启用后,系统提示词中可能会添加三层指导:\n\n1. **通用工具使用强制**(所有匹配模型)—— 指示模型立即进行工具调用而不是描述意图,持续工作直到任务完成,永远不要以未来操作的承诺结束轮次。\n\n2. **OpenAI 执行纪律**(仅限 GPT 和 Codex 模型)—— 针对 GPT 特定失败模式的额外指导:在部分结果上放弃工作、跳过先决条件查找、幻觉而不是使用工具、在未验证的情况下宣布\"完成\"。\n\n3. **Google 操作指导**(仅限 Gemini 和 Gemma 模型)—— 简洁性、绝对路径、并行工具调用和编辑前验证模式。\n\n这些对用户透明,仅影响系统提示词。已经可靠使用工具的模型(如 Claude)不需要此指导,这就是为什么 `\"auto\"` 排除它们。\n\n### 何时开启\n\n如果您使用的模型不在默认自动列表中,并注意到它经常描述它*会*做什么而不是实际去做,请设置 `tool_use_enforcement: true` 或将模型子字符串添加到列表中:\n\n```yaml\nagent:\n tool_use_enforcement: [\"gpt\", \"codex\", \"gemini\", \"grok\", \"my-custom-model\"]\n```\n\n## TTS 配置\n\n```yaml\ntts:\n provider: \"edge\" # \"edge\" | \"elevenlabs\" | \"openai\" | \"minimax\" | \"mistral\" | \"gemini\" | \"xai\" | \"neutts\"\n speed: 1.0 # 全局速度倍数(所有 provider 的回退)\n edge:\n voice: \"en-US-AriaNeural\" # 322 种声音,74 种语言\n speed: 1.0 # 速度倍数(转换为速率百分比,例如 1.5 → +50%)\n elevenlabs:\n voice_id: \"pNInz6obpgDQGcFmaJgB\"\n model_id: \"eleven_multilingual_v2\"\n openai:\n model: \"gpt-4o-mini-tts\"\n voice: \"alloy\" # alloy、echo、fable、onyx、nova、shimmer\n speed: 1.0 # 速度倍数(API 限制为 0.25–4.0)\n base_url: \"https://api.openai.com/v1\" # 覆盖 OpenAI 兼容 TTS 端点\n minimax:\n speed: 1.0 # 语音速度倍数\n # base_url: \"\" # 可选:覆盖 OpenAI 兼容 TTS 端点\n mistral:\n model: \"voxtral-mini-tts-2603\"\n voice_id: \"c69964a6-ab8b-4f8a-9465-ec0925096ec8\" # Paul - Neutral(默认)\n gemini:\n model: \"gemini-2.5-flash-preview-tts\" # 或 gemini-2.5-pro-preview-tts\n voice: \"Kore\" # 30 种预置声音:Zephyr、Puck、Kore、Enceladus 等\n xai:\n voice_id: \"eve\" # xAI TTS 声音\n language: \"en\" # ISO 639-1\n sample_rate: 24000\n bit_rate: 128000 # MP3 比特率\n # base_url: \"https://api.x.ai/v1\"\n neutts:\n ref_audio: ''\n ref_text: ''\n model: neuphonic/neutts-air-q4-gguf\n device: cpu\n```\n\n这控制 `text_to_speech` 工具和语音模式中的口语回复(CLI 中的 `/voice tts` 或消息 gateway)。\n\n**速度回退层次:** provider 特定速度(例如 `tts.edge.speed`)→ 全局 `tts.speed` → `1.0` 默认值。设置全局 `tts.speed` 以在所有 provider 上应用统一速度,或按 provider 覆盖以进行精细控制。\n\n## 显示设置\n\n```yaml\ndisplay:\n tool_progress: all # off | new | all | verbose\n tool_progress_command: false # 在消息 gateway 中启用 /verbose 斜杠命令\n platforms: {} # 每平台显示覆盖(见下文)\n tool_progress_overrides: {} # 已弃用 —— 改用 display.platforms\n interim_assistant_messages: true # Gateway:将自然的轮次中 assistant 更新作为单独消息发送\n skin: default # 内置或自定义 CLI 皮肤(参阅 user-guide/features/skins)\n personality: \"kawaii\" # 旧版外观字段,仍在某些摘要中显示\n compact: false # 紧凑输出模式(减少空白)\n resume_display: full # full(恢复时显示之前的消息)| minimal(仅单行)\n bell_on_complete: false # 当 agent 完成时播放终端铃声(适合长任务)\n show_reasoning: false # 在每次响应上方显示模型推理/思考(用 /reasoning show|hide 切换)\n streaming: false # 将 token 实时流式传输到终端\n show_cost: false # 在 CLI 状态栏中显示估计 $ 成本\n timestamps: false # 为 true 时,在 CLI/TUI 记录中为用户和 assistant 标签添加 [HH:MM] 时间戳前缀\n tool_preview_length: 0 # 工具调用预览的最大字符数(0 = 无限制,显示完整路径/命令)\n runtime_footer: # Gateway:在最终回复中附加运行时上下文页脚\n enabled: false\n fields: [\"model\", \"context_pct\", \"cwd\"]\n file_mutation_verifier: true # 当本轮 write_file/patch 调用失败时附加建议性页脚\n language: en # 静态消息的 UI 语言(审批提示、部分 gateway 回复)。en | zh | zh-hant | ja | de | es | fr | tr | uk | af | ko | it | ga | pt | ru | hu\n```\n\n### 文件变更验证器\n\n当 `display.file_mutation_verifier` 为 `true`(默认)时,每当本轮中 `write_file` 或 `patch` 调用失败且从未被对同一路径的成功写入取代时,Hermes 会在 assistant 的最终响应中附加一行建议。这捕获了\"批量并行补丁,一半静默失败,模型总结成功\"这类过度声明,而无需您在每次编辑后手动运行 `git status`。\n\n示例页脚:\n\n```\n⚠️ File-mutation verifier: 3 file(s) were NOT modified this turn despite any wording above that may suggest otherwise. Run `git status` or `read_file` to confirm.\n • concepts/automatic-organization.md — [patch] Could not find match for old_string\n • concepts/lora.md — [patch] Could not find match for old_string\n • concepts/rag-pipeline.md — [patch] Could not find match for old_string\n```\n\n设置 `file_mutation_verifier: false`(或 `HERMES_FILE_MUTATION_VERIFIER=0`)以禁止页脚。验证器仅在轮次结束时有真实失败未解决时触发 —— 在同一轮次内重试失败补丁并成功的模型不会为该文件触发它。\n\n### 静态消息的 UI 语言\n\n`display.language` 设置翻译一小组静态面向用户的消息 —— CLI 审批提示、少数 gateway 斜杠命令回复(例如重启排空通知、\"审批已过期\"、\"目标已清除\")。它**不**翻译 agent 响应、日志行、工具输出、错误回溯或斜杠命令描述 —— 这些保持英文。如果您希望 agent 本身用另一种语言回复,只需在您的提示词或系统消息中告诉它。\n\n支持的值:`en`(默认)、`zh`(简体中文)、`zh-hant`(繁体中文)、`ja`(日语)、`de`(德语)、`es`(西班牙语)、`fr`(法语)、`tr`(土耳其语)、`uk`(乌克兰语)、`af`(南非荷兰语)、`ko`(韩语)、`it`(意大利语)、`ga`(爱尔兰语)、`pt`(葡萄牙语)、`ru`(俄语)、`hu`(匈牙利语)。未知值回退到英文。\n\n您也可以使用 `HERMES_LANGUAGE` 环境变量按会话设置,它会覆盖配置值。\n\n```yaml\ndisplay:\n language: zh # CLI 审批提示以中文显示\n```\n\n| 模式 | 您看到的内容 |\n|------|-------------|\n| `off` | 静默 —— 仅最终响应 |\n| `new` | 仅在工具更改时显示工具指示器 |\n| `all` | 每次工具调用附带简短预览(默认) |\n| `verbose` | 完整参数、结果和调试日志 |\n\n在 CLI 中,使用 `/verbose` 循环切换这些模式。要在消息平台(Telegram、Discord、Slack 等)中使用 `/verbose`,请在上方的 `display` 部分设置 `tool_progress_command: true`。该命令将循环切换模式并保存到配置。\n\n### 运行时元数据页脚(仅限 gateway)\n\n当 `display.runtime_footer.enabled: true` 时,Hermes 在每个 gateway 轮次的**最终**消息中附加一个小型运行时上下文页脚。目前页脚可显示模型、上下文窗口百分比和当前工作目录。默认关闭;如果您的团队希望每个回复都包含这些来源信息,请按 gateway 选择加入。\n\n```yaml\ndisplay:\n runtime_footer:\n enabled: true\n fields: [\"model\", \"context_pct\", \"cwd\"] # 支持字段:model、context_pct、cwd\n```\n\n`/footer` 斜杠命令在任何会话中运行时切换此功能。\n\n附加到 Telegram/Discord/Slack 回复的示例页脚:\n\n```\n— claude-opus-4.7 · 12 tool calls · 2m 14s · $0.042\n```\n\n只有轮次的**最终**消息获得页脚;中间更新保持干净。\n\n### 每平台进度覆盖\n\n不同平台有不同的详细程度需求。例如,Signal 无法编辑消息,因此每次进度更新都会成为单独的消息 —— 很嘈杂。使用 `display.platforms` 设置每平台模式:\n\n```yaml\ndisplay:\n tool_progress: all # 全局默认\n platforms:\n signal:\n tool_progress: 'off' # 在 Signal 上静默进度\n telegram:\n tool_progress: verbose # 在 Telegram 上详细进度\n slack:\n tool_progress: 'off' # 在共享 Slack 工作区中保持安静\n```\n\n没有覆盖的平台回退到全局 `tool_progress` 值。有效平台键:`telegram`、`discord`、`slack`、`signal`、`whatsapp`、`matrix`、`mattermost`、`email`、`sms`、`homeassistant`、`dingtalk`、`feishu`、`wecom`、`weixin`、`bluebubbles`、`qqbot`。旧版 `display.tool_progress_overrides` 键仍可加载以向后兼容,但已弃用,并在首次加载时迁移到 `display.platforms`。\n\n`interim_assistant_messages` 仅限 gateway。启用后,Hermes 将已完成的轮次中 assistant 更新作为单独的聊天消息发送。这与 `tool_progress` 无关,不需要 gateway 流式传输。\n\n## 隐私\n\n```yaml\nprivacy:\n redact_pii: false # 从 LLM 上下文中删除 PII(仅限 gateway)\n```\n\n当 `redact_pii` 为 `true` 时,gateway 在将系统提示词发送到受支持平台上的 LLM 之前,会从中删除个人身份信息:\n\n| 字段 | 处理方式 |\n|-------|-----------|\n| 电话号码(WhatsApp/Signal 上的用户 ID) | 哈希为 `user_<12-char-sha256>` |\n| 用户 ID | 哈希为 `user_<12-char-sha256>` |\n| 聊天 ID | 数字部分哈希,保留平台前缀(`telegram:<hash>`) |\n| 主频道 ID | 数字部分哈希 |\n| 用户名/昵称 | **不受影响**(用户选择的,公开可见) |\n\n**平台支持:** 删除适用于 WhatsApp、Signal 和 Telegram。Discord 和 Slack 被排除,因为它们的提及系统(`<@user_id>`)需要 LLM 上下文中的真实 ID。\n\n哈希是确定性的 —— 同一用户始终映射到同一哈希,因此模型仍然可以在群聊中区分用户。路由和传递在内部使用原始值。\n\n## 语音转文字(STT)\n\n```yaml\nstt:\n provider: \"local\" # \"local\" | \"groq\" | \"openai\" | \"mistral\"\n local:\n model: \"base\" # tiny、base、small、medium、large-v3\n openai:\n model: \"whisper-1\" # whisper-1 | gpt-4o-mini-transcribe | gpt-4o-transcribe\n # model: \"whisper-1\" # 旧版回退键仍受支持\n```\n\nProvider 行为:\n\n- `local` 使用在您机器上运行的 `faster-whisper`。使用 `pip install faster-whisper` 单独安装。\n- `groq` 使用 Groq 的 Whisper 兼容端点,读取 `GROQ_API_KEY`。\n- `openai` 使用 OpenAI 语音 API,读取 `VOICE_TOOLS_OPENAI_KEY`。\n\n如果请求的 provider 不可用,Hermes 按此顺序自动回退:`local` → `groq` → `openai`。\n\nGroq 和 OpenAI 模型覆盖由环境变量驱动:\n\n```bash\nSTT_GROQ_MODEL=whisper-large-v3-turbo\nSTT_OPENAI_MODEL=whisper-1\nGROQ_BASE_URL=https://api.groq.com/openai/v1\nSTT_OPENAI_BASE_URL=https://api.openai.com/v1\n```\n\n## 语音模式(CLI)\n\n```yaml\nvoice:\n record_key: \"ctrl+b\" # CLI 内的按键通话键\n max_recording_seconds: 120 # 长录音的硬停止\n auto_tts: false # /voice on 时自动启用口语回复\n beep_enabled: true # 在 CLI 语音模式中播放录音开始/停止提示音\n silence_threshold: 200 # 语音检测的 RMS 阈值\n silence_duration: 3.0 # 自动停止前的静默秒数\n```\n\n在 CLI 中使用 `/voice on` 启用麦克风模式,使用 `record_key` 开始/停止录音,使用 `/voice tts` 切换口语回复。端到端设置和平台特定行为请参阅[语音模式](/user-guide/features/voice-mode)。\n\n## 流式传输\n\n将 token 实时流式传输到终端或消息平台,而不是等待完整响应。\n\n### CLI 流式传输\n\n```yaml\ndisplay:\n streaming: true # 实时将 token 流式传输到终端\n show_reasoning: true # 同时流式传输推理/思考 token(可选)\n```\n\n启用后,响应在流式传输框内逐 token 出现。工具调用仍然静默捕获。如果 provider 不支持流式传输,它会自动回退到正常显示。\n\n### Gateway 流式传输(Telegram、Discord、Slack)\n\n```yaml\nstreaming:\n enabled: true # 启用渐进式消息编辑\n transport: edit # \"edit\"(渐进式消息编辑)或 \"off\"\n edit_interval: 0.3 # 消息编辑之间的秒数\n buffer_threshold: 40 # 强制编辑刷新前的字符数\n cursor: \" ▉\" # 流式传输期间显示的光标\n fresh_final_after_seconds: 0 # 预览超过此时间时选择发送新的最终消息(Telegram)\n```\n\n启用后,bot 在第一个 token 时发送消息,然后随着更多 token 到来渐进式编辑它。不支持消息编辑的平台(Signal、Email、Home Assistant)在第一次尝试时自动检测 —— 该会话的流式传输被优雅地禁用,不会产生大量消息。\n\n对于不带渐进式 token 编辑的独立自然轮次中 assistant 更新,请设置 `display.interim_assistant_messages: true`。\n\n**溢出处理:** 如果流式传输的文本超过平台的消息长度限制(约 4096 字符),当前消息被最终化,新消息自动开始。\n\n**新的最终消息(Telegram):** Telegram 的 `editMessageText` 保留原始消息时间戳,因此长时间运行的流式回复即使在完成后也会保留第一个 token 的时间戳。设置 `fresh_final_after_seconds > 0` 可选择将旧预览作为全新的最终消息传递,并尽力删除旧预览。默认值为 `0`,始终就地最终化流式回复,避免某些客户端短暂显示重复消息再删除其中一条。\n\n:::note\n主开关 `streaming.enabled` 默认为 `false`——在你启用之前不会有任何流式传输。启用后,是否流式传输按**平台**决定:Telegram 默认带有 `display.platforms.telegram.streaming: true`(流式传输),Discord 为 `display.platforms.discord.streaming: false`(不流式传输)。因此启用流式传输后,Telegram 开箱即用地流式传输,Discord 在你修改其开关之前仍使用整条消息回复。你可以在仪表盘的 **Channels** 开关中或直接在 `~/.hermes/config.yaml` 中调整这些按平台的开关。\n:::\n\n## 群聊会话隔离\n\n控制共享聊天是每个房间保持一个对话还是每个参与者一个对话:\n\n```yaml\ngroup_sessions_per_user: true # true = 群组/频道中每用户隔离,false = 每个聊天一个共享会话\n```\n\n- `true` 是默认和推荐设置。在 Discord 频道、Telegram 群组、Slack 频道和类似共享上下文中,当平台提供用户 ID 时,每个发送者获得自己的会话。\n- `false` 恢复到旧的共享房间行为。如果您明确希望 Hermes 将频道视为一个协作对话,这可能有用,但这也意味着用户共享上下文、token 成本和中断状态。\n- 私信不受影响。Hermes 仍然像往常一样通过聊天/DM ID 键入 DM。\n- 线程与其父频道保持隔离;使用 `true` 时,每个参与者在线程内也获得自己的会话。\n\n有关行为详情和示例,请参阅[会话](/user-guide/sessions)和 [Discord 指南](/user-guide/messaging/discord)。\n\n## 未授权 DM 行为\n\n控制当未知用户发送私信时 Hermes 的行为:\n\n```yaml\nunauthorized_dm_behavior: pair\n\nwhatsapp:\n unauthorized_dm_behavior: ignore\n```\n\n- `pair` 是默认值。Hermes 拒绝访问,但在 DM 中回复一次性配对码。\n- `ignore` 静默丢弃未授权的 DM。\n- 平台部分覆盖全局默认值,因此您可以在广泛范围内保持配对启用,同时使一个平台更安静。\n\n## 快速命令\n\n定义自定义命令,这些命令要么在不调用 LLM 的情况下运行 shell 命令,要么将一个斜杠命令别名为另一个。Exec 快速命令是零 token 的,对于从消息平台(Telegram、Discord 等)进行快速服务器检查或实用脚本很有用。\n\n```yaml\nquick_commands:\n status:\n type: exec\n command: systemctl status hermes-agent\n disk:\n type: exec\n command: df -h /\n update:\n type: exec\n command: cd ~/.hermes/hermes-agent && git pull && pip install -e .\n gpu:\n type: exec\n command: nvidia-smi --query-gpu=name,utilization.gpu,memory.used,memory.total --format=csv,noheader\n restart:\n type: alias\n target: /gateway restart\n```\n\n用法:在 CLI 或任何消息平台中输入 `/status`、`/disk`、`/update`、`/gpu` 或 `/restart`。`exec` 命令在宿主本地运行并直接返回输出 —— 无 LLM 调用,不消耗 token。`alias` 命令重写为配置的斜杠命令目标。\n\n- **30 秒超时** —— 长时间运行的命令被终止并显示错误消息\n- **优先级** —— 快速命令在技能命令之前检查,因此您可以覆盖技能名称\n- **自动补全** —— 快速命令在调度时解析,不显示在内置斜杠命令自动补全表中\n- **类型** —— 支持的类型为 `exec` 和 `alias`;其他类型显示错误\n- **到处可用** —— CLI、Telegram、Discord、Slack、WhatsApp、Signal、Email、Home Assistant\n\n仅字符串的 prompt 快捷方式不是有效的快速命令。对于可重用的 prompt 工作流,请创建技能或别名到现有斜杠命令。\n\n## 人类延迟\n\n在消息平台中模拟类人响应节奏:\n\n```yaml\nhuman_delay:\n mode: \"off\" # off | natural | custom\n min_ms: 800 # 最小延迟(自定义模式)\n max_ms: 2500 # 最大延迟(自定义模式)\n```\n\n## 代码执行\n\n配置 `execute_code` 工具:\n\n```yaml\ncode_execution:\n mode: project # project(默认)| strict\n timeout: 300 # 最大执行时间(秒)\n max_tool_calls: 50 # 代码执行中的最大工具调用次数\n```\n\n**`mode`** 控制脚本的工作目录和 Python 解释器:\n\n- **`project`**(默认)—— 脚本在会话的工作目录中以活跃 virtualenv/conda 环境的 python 运行。项目依赖(`pandas`、`torch`、项目包)和相对路径(`.env`、`./data.csv`)自然解析,与 `terminal()` 看到的一致。\n- **`strict`** —— 脚本在临时暂存目录中以 `sys.executable`(Hermes 自己的 python)运行。最大可重现性,但项目依赖和相对路径不会解析。\n\n环境清理(删除 `*_API_KEY`、`*_TOKEN`、`*_SECRET`、`*_PASSWORD`、`*_CREDENTIAL`、`*_PASSWD`、`*_AUTH`)和工具白名单在两种模式下完全相同 —— 切换模式不会改变安全态势。\n\n## Web 搜索后端\n\n`web_search` 和 `web_extract` 工具支持五种后端 provider。在 `config.yaml` 中或通过 `hermes tools` 配置后端:\n\n```yaml\nweb:\n backend: firecrawl # firecrawl | searxng | parallel | tavily | exa\n\n # 或使用每功能键混合 provider(例如免费搜索 + 付费提取):\n search_backend: \"searxng\"\n extract_backend: \"firecrawl\"\n```\n\n| 后端 | 环境变量 | 搜索 | 提取 |\n|---------|---------|--------|---------|\n| **Firecrawl**(默认) | `FIRECRAWL_API_KEY` | ✔ | ✔ |\n| **SearXNG** | `SEARXNG_URL` | ✔ | — |\n| **Parallel** | `PARALLEL_API_KEY` | ✔ | ✔ |\n| **Tavily** | `TAVILY_API_KEY` | ✔ | ✔ |\n| **Exa** | `EXA_API_KEY` | ✔ | ✔ |\n\n**后端选择:** 如果未设置 `web.backend`,后端从可用的 API 密钥自动检测。如果仅设置了 `SEARXNG_URL`,使用 SearXNG。如果仅设置了 `EXA_API_KEY`,使用 Exa。如果仅设置了 `TAVILY_API_KEY`,使用 Tavily。如果仅设置了 `PARALLEL_API_KEY`,使用 Parallel。否则 Firecrawl 是默认值。\n\n**SearXNG** 是一个免费、自托管、尊重隐私的元搜索引擎,查询 70+ 个搜索引擎。无需 API 密钥 —— 只需将 `SEARXNG_URL` 设置为您的实例(例如 `http://localhost:8080`)。SearXNG 仅限搜索;`web_extract` 需要单独的提取 provider(设置 `web.extract_backend`)。Docker 设置说明请参阅 [Web 搜索设置指南](/user-guide/features/web-search)。\n\n**自托管 Firecrawl:** 设置 `FIRECRAWL_API_URL` 指向您自己的实例。设置自定义 URL 后,API 密钥变为可选(在服务器上设置 `USE_DB_AUTHENTICATION=***` 以禁用认证)。\n\n**Parallel 搜索模式:** 设置 `PARALLEL_SEARCH_MODE` 控制搜索行为 —— `fast`、`one-shot` 或 `agentic`(默认:`agentic`)。\n\n**Exa:** 在 `~/.hermes/.env` 中设置 `EXA_API_KEY`。支持 `category` 过滤(`company`、`research paper`、`news`、`people`、`personal site`、`pdf`)和域名/日期过滤器。\n\n## 浏览器\n\n配置浏览器自动化行为:\n\n```yaml\nbrowser:\n inactivity_timeout: 120 # 自动关闭空闲会话前的秒数\n command_timeout: 30 # 浏览器命令超时(截图、导航等)(秒)\n record_sessions: false # 自动将浏览器会话录制为 WebM 视频到 ~/.hermes/browser_recordings/\n # 可选 CDP 覆盖 —— 设置后,Hermes 直接附加到您自己的\n # Chromium 系浏览器(通过 /browser connect),而不是启动无头浏览器。\n cdp_url: \"\"\n # 对话框监督器 —— 控制当 CDP 后端附加时(Browserbase、本地 Chromium 系\n # 浏览器通过 /browser connect)如何处理原生 JS 对话框(alert/confirm/prompt)。\n # 在 Camofox 和默认本地 agent 浏览器模式下忽略。\n dialog_policy: must_respond # must_respond | auto_dismiss | auto_accept\n dialog_timeout_s: 300 # must_respond 下的安全自动关闭(秒)\n camofox:\n managed_persistence: false # 为 true 时,Camofox 会话跨重启持久化 cookie/登录\n user_id: \"\" # 可选的外部管理 Camofox userId\n session_key: \"\" # Hermes 创建标签页时发送的可选会话密钥\n adopt_existing_tab: false # 在创建新标签页之前重用此身份的现有标签页\n```\n\n**对话框策略:**\n\n- `must_respond`(默认)—— 捕获对话框,在 `browser_snapshot.pending_dialogs` 中显示,等待 agent 调用 `browser_dialog(action=...)`。在 `dialog_timeout_s` 秒内无响应后,对话框被自动关闭以防止页面的 JS 线程永久停滞。\n- `auto_dismiss` —— 捕获,立即关闭。Agent 仍然在事后的 `browser_snapshot.recent_dialogs` 中看到对话框记录,`closed_by=\"auto_policy\"`。\n- `auto_accept` —— 捕获,立即接受。适用于有激进 `beforeunload` 提示的页面。\n\n完整对话框工作流请参阅[浏览器功能页面](./features/browser.md#browser_dialog)。\n\n浏览器工具集支持多个 provider。有关 Browserbase、Browser Use 和本地 Chromium 系 CDP 设置的详细信息,请参阅[浏览器功能页面](/user-guide/features/browser)。\n\n## 时区\n\n使用 IANA 时区字符串覆盖服务器本地时区。影响日志中的时间戳、cron 调度和系统提示词时间注入。\n\n```yaml\ntimezone: \"America/New_York\" # IANA 时区(默认:\"\" = 服务器本地时间)\n```\n\n支持的值:任何 IANA 时区标识符(例如 `America/New_York`、`Europe/London`、`Asia/Kolkata`、`UTC`)。留空或省略以使用服务器本地时间。\n\n## Discord\n\n为消息 gateway 配置 Discord 特定行为:\n\n```yaml\ndiscord:\n require_mention: true # 在服务器频道中需要 @提及才能响应\n free_response_channels: \"\" # 逗号分隔的频道 ID,bot 在这些频道无需 @提及即可响应\n auto_thread: true # 在频道中 @提及时自动创建线程\n```\n\n- `require_mention` —— 为 `true`(默认)时,bot 仅在服务器频道中被 `@BotName` 提及时响应。DM 始终无需提及即可工作。\n- `free_response_channels` —— 逗号分隔的频道 ID 列表,bot 在这些频道对每条消息响应,无需提及。\n- `auto_thread` —— 为 `true`(默认)时,频道中的提及会自动为对话创建线程,保持频道整洁(类似 Slack 线程)。\n\n## 安全\n\n预执行安全扫描和机密脱敏:\n\n```yaml\nsecurity:\n redact_secrets: false # 在工具输出和日志中脱敏 API 密钥模式(默认关闭)\n tirith_enabled: true # 为终端命令启用 Tirith 安全扫描\n tirith_path: \"tirith\" # tirith 二进制文件路径(默认:$PATH 中的 \"tirith\")\n tirith_timeout: 5 # 等待 tirith 扫描的秒数\n tirith_fail_open: true # 如果 tirith 不可用,允许命令执行\n website_blocklist: # 参见下方网站黑名单部分\n enabled: false\n domains: []\n shared_files: []\n```\n\n- `redact_secrets` —— 为 `true` 时,自动检测并脱敏工具输出中看起来像 API 密钥、token 和密码的模式,然后再进入对话上下文和日志。**默认关闭** —— 如果您经常在工具输出中处理真实凭据并希望有安全网,请启用。显式设置为 `true` 以开启。\n- `tirith_enabled` —— 为 `true` 时,终端命令在执行前由 [Tirith](https://github.com/sheeki03/tirith) 扫描以检测潜在危险操作。\n- `tirith_path` —— tirith 二进制文件的路径。如果 tirith 安装在非标准位置,请设置此项。\n- `tirith_timeout` —— 等待 tirith 扫描的最大秒数。如果扫描超时,命令继续执行。\n- `tirith_fail_open` —— 为 `true`(默认)时,如果 tirith 不可用或失败,允许命令执行。设置为 `false` 以在 tirith 无法验证时阻止命令。\n\n## 网站黑名单\n\n阻止 agent 的 web 和浏览器工具访问特定域名:\n\n```yaml\nsecurity:\n website_blocklist:\n enabled: false # 启用 URL 阻止(默认:false)\n domains: # 被阻止的域名模式列表\n - \"*.internal.company.com\"\n - \"admin.example.com\"\n - \"*.local\"\n shared_files: # 从外部文件加载额外规则\n - \"/etc/hermes/blocked-sites.txt\"\n```\n\n启用后,任何匹配被阻止域名模式的 URL 在 web 或浏览器工具执行之前都会被拒绝。这适用于 `web_search`、`web_extract`、`browser_navigate` 以及任何访问 URL 的工具。\n\n域名规则支持:\n- 精确域名:`admin.example.com`\n- 通配符子域名:`*.internal.company.com`(阻止所有子域名)\n- TLD 通配符:`*.local`\n\n共享文件每行包含一条域名规则(空行和 `#` 注释被忽略)。缺失或不可读的文件记录警告,但不禁用其他 web 工具。\n\n策略缓存 30 秒,因此配置更改无需重启即可快速生效。\n\n## 智能审批\n\n控制 Hermes 如何处理潜在危险命令:\n\n```yaml\napprovals:\n mode: manual # manual | smart | off\n```\n\n| 模式 | 行为 |\n|------|----------|\n| `manual`(默认) | 在执行任何被标记的命令之前提示用户。在 CLI 中显示交互式审批对话框。在消息中排队待处理的审批请求。 |\n| `smart` | 使用辅助 LLM 评估被标记的命令是否真正危险。低风险命令以会话级持久性自动批准。真正有风险的命令升级给用户。 |\n| `off` | 跳过所有审批检查。等同于 `HERMES_YOLO_MODE=true`。**谨慎使用。** |\n\n智能模式对于减少审批疲劳特别有用 —— 它让 agent 在安全操作上更自主地工作,同时仍然捕获真正破坏性的命令。\n\n:::warning\n设置 `approvals.mode: off` 会禁用终端命令的所有安全检查。仅在受信任的沙箱环境中使用。\n:::\n\n## 检查点\n\n破坏性文件操作之前的自动文件系统快照。详情请参阅[检查点与回滚](/user-guide/checkpoints-and-rollback)。\n\n```yaml\ncheckpoints:\n enabled: false # 启用自动检查点(也可:hermes chat --checkpoints)。默认:false(选择加入)。\n max_snapshots: 20 # 每个目录保留的最大检查点数(默认:20)\n```\n\n\n## 委托\n\n为委托工具配置子 agent 行为:\n\n```yaml\ndelegation:\n # model: \"google/gemini-3-flash-preview\" # 覆盖模型(空 = 继承父级)\n # provider: \"openrouter\" # 覆盖 provider(空 = 继承父级)\n # base_url: \"http://localhost:1234/v1\" # 直接 OpenAI 兼容端点(优先于 provider)\n # api_key: \"local-key\" # base_url 的 API 密钥(回退到 OPENAI_API_KEY)\n # api_mode: \"\" # base_url 的线路协议:\"chat_completions\"、\"codex_responses\" 或 \"anthropic_messages\"。空 = 从 URL 自动检测(例如 /anthropic 后缀 → anthropic_messages)。对启发式无法检测的非标准端点显式设置。\n max_concurrent_children: 3 # 每批并行子 agent 数(下限 1,无上限)。也可通过 DELEGATION_MAX_CONCURRENT_CHILDREN 环境变量设置。\n max_spawn_depth: 1 # 委托树深度上限(1-3,截断)。1 = 扁平(默认):父级生成无法委托的叶子。2 = 编排器子级可以生成叶子孙级。3 = 三级。\n orchestrator_enabled: true # 全局终止开关。为 false 时,role=\"orchestrator\" 被忽略,每个子级无论 max_spawn_depth 如何都被强制为叶子。\n```\n\n**子 agent provider:model 覆盖:** 默认情况下,子 agent 继承父 agent 的 provider 和模型。设置 `delegation.provider` 和 `delegation.model` 将子 agent 路由到不同的 provider:model 对 —— 例如,在您的主 agent 运行昂贵推理模型时,为范围较窄的子任务使用便宜/快速的模型。\n\n**直接端点覆盖:** 如果您想要明显的自定义端点路径,请设置 `delegation.base_url`、`delegation.api_key` 和 `delegation.model`。这将子 agent 直接发送到该 OpenAI 兼容端点,并优先于 `delegation.provider`。如果省略 `delegation.api_key`,Hermes 仅回退到 `OPENAI_API_KEY`。\n\n**线路协议(`api_mode`):** Hermes 从 `delegation.base_url` 自动检测线路协议(例如以 `/anthropic` 结尾的路径 → `anthropic_messages`;Codex/原生 Anthropic/Kimi-coding 主机名保留其现有检测)。对于启发式无法分类的端点 —— 例如 Azure AI Foundry、MiniMax、Zhipu GLM 或前置 Anthropic 形状后端的 LiteLLM 代理 —— 请将 `delegation.api_mode` 显式设置为 `chat_completions`、`codex_responses` 或 `anthropic_messages` 之一。留空(默认)以保持自动检测。\n\n委托 provider 使用与 CLI/gateway 启动相同的凭据解析。所有配置的 provider 均受支持:`openrouter`、`nous`、`copilot`、`zai`、`kimi-coding`、`minimax`、`minimax-cn`。设置 provider 时,系统自动解析正确的基础 URL、API 密钥和 API 模式 —— 无需手动凭据连接。\n\n**优先级:** 配置中的 `delegation.base_url` → 配置中的 `delegation.provider` → 父 provider(继承)。配置中的 `delegation.model` → 父模型(继承)。仅设置 `model` 而不设置 `provider` 仅更改模型名称,同时保留父级凭据(适用于在同一 provider(如 OpenRouter)内切换模型)。\n\n**宽度和深度:** `max_concurrent_children` 限制每批并行运行的子 agent 数量(默认 `3`,下限 1,无上限)。也可通过 `DELEGATION_MAX_CONCURRENT_CHILDREN` 环境变量设置。当模型提交的 `tasks` 数组超过上限时,`delegate_task` 返回工具错误解释限制,而不是静默截断。`max_spawn_depth` 控制委托树深度(截断到 1-3)。在默认 `1` 时,委托是扁平的:子级无法生成孙级,传递 `role=\"orchestrator\"` 静默降级为 `leaf`。提升到 `2` 使编排器子级可以生成叶子孙级;`3` 用于三级树。Agent 通过 `role=\"orchestrator\"` 按调用选择编排;`orchestrator_enabled: false` 强制每个子级回到叶子,无论如何。成本呈乘法增长 —— 在 `max_spawn_depth: 3` 和 `max_concurrent_children: 3` 时,树可以达到 3×3×3 = 27 个并发叶子 agent。使用模式请参阅[子 Agent 委托 → 深度限制和嵌套编排](features/delegation.md#depth-limit-and-nested-orchestration)。\n\n## 澄清\n\n配置澄清提示行为:\n\n```yaml\nclarify:\n timeout: 120 # 等待用户澄清响应的秒数\n```\n\n## 上下文文件(SOUL.md、AGENTS.md)\n\nHermes 使用两种不同的上下文范围:\n\n| 文件 | 用途 | 范围 |\n|------|---------|-------|\n| `SOUL.md` | **主要 agent 身份** —— 定义 agent 是谁(系统提示词第 #1 槽位) | `~/.hermes/SOUL.md` 或 `$HERMES_HOME/SOUL.md` |\n| `.hermes.md` / `HERMES.md` | 项目特定指令(最高优先级) | 向上走到 git 根目录 |\n| `AGENTS.md` | 项目特定指令、编码规范 | 递归目录遍历 |\n| `CLAUDE.md` | Claude Code 上下文文件(也会检测) | 仅工作目录 |\n| `.cursorrules` | Cursor IDE 规则(也会检测) | 仅工作目录 |\n| `.cursor/rules/*.mdc` | Cursor 规则文件(也会检测) | 仅工作目录 |\n\n- **SOUL.md** 是 agent 的主要身份。它占据系统提示词的第 #1 槽位,完全替换内置的默认身份。编辑它以完全自定义 agent 是谁。\n- 如果 SOUL.md 缺失、为空或无法加载,Hermes 回退到内置默认身份。\n- **项目上下文文件使用优先级系统** —— 仅加载一种类型(第一个匹配优先):`.hermes.md` → `AGENTS.md` → `CLAUDE.md` → `.cursorrules`。SOUL.md 始终独立加载。\n- **AGENTS.md** 是分层的:如果子目录也有 AGENTS.md,所有都会合并。\n- 如果 `SOUL.md` 不存在,Hermes 会自动生成默认的 `SOUL.md`。\n- 所有加载的上下文文件上限为 20,000 字符,并进行智能截断。\n\n另请参阅:\n- [个性与 SOUL.md](/user-guide/features/personality)\n- [上下文文件](/user-guide/features/context-files)\n\n## 工作目录\n\n| 上下文 | 默认值 |\n|---------|---------|\n| **CLI(`hermes`)** | 运行命令的当前目录 |\n| **消息 gateway** | 主目录 `~`(用 `MESSAGING_CWD` 覆盖) |\n| **Docker / Singularity / Modal / SSH** | 容器或远程机器内用户的主目录 |\n\n覆盖工作目录:\n```bash\n# 在 ~/.hermes/.env 或 ~/.hermes/config.yaml 中:\nMESSAGING_CWD=/home/myuser/projects # Gateway 会话\nTERMINAL_CWD=/workspace # 所有终端会话\n```\n"}, "defeda8c559f47b9d29cb3a7b5d8e3c1984e1552": {"t": 1785767998.4609683, "n": 56796, "h": "811315eb15c6eb3b", "c": "---\nsidebar_position: 2\ntitle: \"配置\"\ndescription: \"配置 Hermes Agent — config.yaml、providers、模型、API 密钥等\"\n---\n\n# 配置\n\n所有设置均存储在 `~/.hermes/` 目录中,便于访问。\n\n## 目录结构\n\n```text\n~/.hermes/\n├── config.yaml # 设置(模型、终端、TTS、压缩等)\n├── .env # API 密钥和机密\n├── auth.json # OAuth provider 凭据(Nous Portal 等)\n├── SOUL.md # 主要 agent 身份(系统提示词第 #1 槽位)\n├── memories/ # 持久记忆(MEMORY.md、USER.md)\n├── skills/ # Agent 创建的技能(通过 skill_manage 工具管理)\n├── cron/ # 定时任务\n├── sessions/ # Gateway 会话\n└── logs/ # 日志(errors.log、gateway.log — 机密自动脱敏)\n```\n\n## 管理配置\n\n```bash\nhermes config # 查看当前配置\nhermes config edit # 在编辑器中打开 config.yaml\nhermes config set KEY VAL # 设置特定值\nhermes config check # 检查缺失选项(更新后使用)\nhermes config migrate # 交互式添加缺失选项\n\n# 示例:\nhermes config set model anthropic/claude-opus-4\nhermes config set terminal.backend docker\nhermes config set OPENROUTER_API_KEY sk-or-... # 保存到 .env\n```\n\n:::tip\n`hermes config set` 命令会自动将值路由到正确的文件 —— API 密钥保存到 `.env`,其他所有内容保存到 `config.yaml`。\n:::\n\n## 配置优先级\n\n设置按以下顺序解析(优先级从高到低):\n\n1. **CLI 参数** —— 例如 `hermes chat --model anthropic/claude-sonnet-4`(单次调用覆盖)\n2. **`~/.hermes/config.yaml`** —— 所有非机密设置的主配置文件\n3. **`~/.hermes/.env`** —— 环境变量的回退;机密(API 密钥、token、密码)**必须**放这里\n4. **内置默认值** —— 未设置任何内容时的硬编码安全默认值\n\n:::info 经验法则\n机密(API 密钥、bot token、密码)放入 `.env`。其他所有内容(模型、终端后端、压缩设置、内存限制、工具集)放入 `config.yaml`。当两者都设置时,`config.yaml` 对非机密设置优先。\n:::\n\n## 环境变量替换\n\n可以在 `config.yaml` 中使用 `${VAR_NAME}` 语法引用环境变量:\n\n```yaml\nauxiliary:\n vision:\n api_key: ${GOOGLE_API_KEY}\n base_url: ${CUSTOM_VISION_URL}\n\ndelegation:\n api_key: ${DELEGATION_KEY}\n```\n\n单个值中可以有多个引用:`url: \"${HOST}:${PORT}\"`。如果引用的变量未设置,占位符将保持原样(`${UNDEFINED_VAR}` 保持不变)。仅支持 `${VAR}` 语法 —— 裸 `$VAR` 不会被展开。\n\n关于 AI provider 设置(OpenRouter、Anthropic、Copilot、自定义端点、自托管 LLM、回退模型等),请参阅 [AI Providers](/integrations/providers)。\n\n### Provider 超时\n\n可以为 provider 设置 `providers.<id>.request_timeout_seconds` 作为全局请求超时,以及 `providers.<id>.models.<model>.timeout_seconds` 作为特定模型的覆盖值。适用于每种传输方式(OpenAI-wire、原生 Anthropic、Anthropic 兼容)上的主轮次客户端、回退链、凭据轮换后的重建,以及(对于 OpenAI-wire)每请求超时 kwarg —— 因此配置值优先于旧版 `HERMES_API_TIMEOUT` 环境变量。\n\n还可以设置 `providers.<id>.stale_timeout_seconds` 用于非流式陈旧调用检测器,以及 `providers.<id>.models.<model>.stale_timeout_seconds` 作为特定模型的覆盖值。此值优先于旧版 `HERMES_API_CALL_STALE_TIMEOUT` 环境变量。\n\n不设置这些值将保持旧版默认值(`HERMES_API_TIMEOUT=1800`s、`HERMES_API_CALL_STALE_TIMEOUT=90`s、原生 Anthropic 900s)。隐式的非流式 stale 检测会在本地端点上自动禁用,并且会在超大上下文下自动放宽。目前不适用于 AWS Bedrock(`bedrock_converse` 和 AnthropicBedrock SDK 路径均使用 boto3 及其自身的超时配置)。请参阅 [`cli-config.yaml.example`](https://github.com/NousResearch/hermes-agent/blob/main/cli-config.yaml.example) 中的注释示例。\n\n## 终端后端配置\n\nHermes 支持六种终端后端。每种后端决定 agent 的 shell 命令实际在哪里执行 —— 本地机器、Docker 容器、通过 SSH 的远程服务器、Modal 云沙箱(直接或通过 Nous 托管的 gateway)、Daytona 工作区,或 Singularity/Apptainer 容器。\n\n```yaml\nterminal:\n backend: local # local | docker | ssh | modal | daytona | singularity\n cwd: \".\" # Gateway/cron 工作目录(CLI 始终使用启动目录)\n timeout: 180 # 每条命令的超时时间(秒)\n env_passthrough: [] # 转发到沙箱执行的环境变量名(terminal + execute_code)\n singularity_image: \"docker://nikolaik/python-nodejs:python3.11-nodejs20\" # Singularity 后端的容器镜像\n modal_image: \"nikolaik/python-nodejs:python3.11-nodejs20\" # Modal 后端的容器镜像\n daytona_image: \"nikolaik/python-nodejs:python3.11-nodejs20\" # Daytona 后端的容器镜像\n```\n\n对于 Modal 和 Daytona 等云沙箱,`container_persistent: true` 表示 Hermes 将尝试在沙箱重建后保留文件系统状态。这并不保证相同的活跃沙箱、PID 空间或后台进程之后仍在运行。\n\n### 后端概览\n\n| 后端 | 命令运行位置 | 隔离性 | 最适合 |\n|---------|-------------------|-----------|----------|\n| **local** | 直接在您的机器上 | 无 | 开发、个人使用 |\n| **docker** | 单个持久 Docker 容器(跨会话、`/new`、子 agent 共享) | 完全(命名空间、cap-drop) | 安全沙箱、CI/CD |\n| **ssh** | 通过 SSH 的远程服务器 | 网络边界 | 远程开发、强大硬件 |\n| **modal** | Modal 云沙箱 | 完全(云 VM) | 临时云计算、评估 |\n| **daytona** | Daytona 工作区 | 完全(云容器) | 托管云开发环境 |\n| **singularity** | Singularity/Apptainer 容器 | 命名空间(--containall) | HPC 集群、共享机器 |\n\n### Local 后端\n\n默认后端。命令直接在您的机器上运行,无隔离。无需特殊设置。\n\n```yaml\nterminal:\n backend: local\n```\n\n:::warning\nAgent 拥有与您的用户账户相同的文件系统访问权限。使用 `hermes tools` 禁用不需要的工具,或切换到 Docker 进行沙箱隔离。\n:::\n\n### Docker 后端\n\n在具有安全加固的 Docker 容器内运行命令(所有权限已删除、无权限提升、PID 限制)。\n\n**单个持久容器,而非每条命令一个容器。** Hermes 在首次使用时启动一个长期运行的容器,并通过 `docker exec` 将每个终端、文件和 `execute_code` 调用路由到同一容器中 —— 跨会话、`/new`、`/reset` 和 `delegate_task` 子 agent,贯穿 Hermes 进程的整个生命周期。工作目录更改、已安装的包以及 `/workspace` 中的文件会从一次工具调用延续到下一次,就像本地 shell 一样。容器在关闭时停止并删除。详情请参阅下方的**容器生命周期**。\n\n```yaml\nterminal:\n backend: docker\n docker_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n docker_mount_cwd_to_workspace: false # 将启动目录挂载到 /workspace\n docker_run_as_host_user: false # 参见下方\"以宿主用户身份运行容器\"\n docker_forward_env: # 转发到容器的环境变量\n - \"GITHUB_TOKEN\"\n docker_volumes: # 宿主目录挂载\n - \"/home/user/projects:/workspace/projects\"\n - \"/home/user/data:/data:ro\" # :ro 表示只读\n docker_extra_args: # 附加到 `docker run` 的额外标志\n - \"--gpus=all\"\n - \"--network=host\"\n\n # 资源限制\n container_cpu: 1 # CPU 核心数(0 = 不限制)\n container_memory: 5120 # MB(0 = 不限制)\n container_disk: 51200 # MB(需要 XFS+pquota 上的 overlay2)\n container_persistent: true # 跨会话持久化 /workspace 和 /root\n```\n\n**`terminal.docker_extra_args`**(也可通过 `TERMINAL_DOCKER_EXTRA_ARGS='[\"--gpus=all\"]'` 覆盖)允许传递 Hermes 未作为一级键公开的任意 `docker run` 标志 —— `--gpus`、`--network`、`--add-host`、替代 `--security-opt` 覆盖等。每个条目必须是字符串;该列表最后附加到组装好的 `docker run` 调用中,因此可以在需要时覆盖 Hermes 的默认值。请谨慎使用 —— 与沙箱加固(权限删除、`--user`、workspace 绑定挂载)冲突的标志将悄然削弱隔离性。\n\n**要求:** 已安装并运行 Docker Desktop 或 Docker Engine。Hermes 会探测 `$PATH` 以及常见的 macOS 安装位置(`/usr/local/bin/docker`、`/opt/homebrew/bin/docker`、Docker Desktop 应用包)。开箱即用支持 Podman:设置 `HERMES_DOCKER_BINARY=podman`(或完整路径)以在两者都安装时强制使用它。\n\n**容器生命周期:** Hermes 为每个终端和文件工具调用重用单个长期运行的容器(`docker run -d ... sleep 2h`),跨会话、`/new`、`/reset` 和 `delegate_task` 子 agent,贯穿 Hermes 进程的整个生命周期。命令通过带登录 shell 的 `docker exec` 运行,因此工作目录更改、已安装的包以及 `/workspace` 中的文件都会从一次工具调用延续到下一次。容器在 Hermes 关闭时(或空闲清理回收时)停止并删除。\n\n通过 `delegate_task(tasks=[...])` 生成的并行子 agent 共享这一个容器 —— 并发的 `cd`、环境变量修改以及对同一路径的写入会发生冲突。如果子 agent 需要隔离的沙箱,必须通过 `register_task_env_overrides()` 注册每任务镜像覆盖,RL 和基准测试环境(TerminalBench2、HermesSweEnv 等)会自动为其每任务 Docker 镜像执行此操作。\n\n**安全加固:**\n- `--cap-drop ALL`,仅添加回 `DAC_OVERRIDE`、`CHOWN`、`FOWNER`\n- `--security-opt no-new-privileges`\n- `--pids-limit 256`\n- `/tmp`(512MB)、`/var/tmp`(256MB)、`/run`(64MB)的大小限制 tmpfs\n\n**凭据转发:** `docker_forward_env` 中列出的环境变量首先从您的 shell 环境解析,然后回退到 `~/.hermes/.env`。技能也可以声明 `required_environment_variables`,这些变量会自动合并。\n\n### SSH 后端\n\n通过 SSH 在远程服务器上运行命令。使用 ControlMaster 进行连接复用(5 分钟空闲保活)。默认启用持久 shell —— 状态(cwd、环境变量)在命令之间保持。\n\n```yaml\nterminal:\n backend: ssh\n persistent_shell: true # 保持长期运行的 bash 会话(默认:true)\n```\n\n**必需的环境变量:**\n\n```bash\nTERMINAL_SSH_HOST=my-server.example.com\nTERMINAL_SSH_USER=ubuntu\n```\n\n**可选:**\n\n| 变量 | 默认值 | 描述 |\n|----------|---------|-------------|\n| `TERMINAL_SSH_PORT` | `22` | SSH 端口 |\n| `TERMINAL_SSH_KEY` | (系统默认) | SSH 私钥路径 |\n| `TERMINAL_SSH_PERSISTENT` | `true` | 启用持久 shell |\n\n**工作原理:** 使用 `BatchMode=yes` 和 `StrictHostKeyChecking=accept-new` 在初始化时连接。持久 shell 在远程主机上保持单个 `bash -l` 进程存活,通过临时文件进行通信。需要 `stdin_data` 或 `sudo` 的命令会自动回退到单次模式。\n\n### Modal 后端\n\n在 [Modal](https://modal.com) 云沙箱中运行命令。每个任务获得一个具有可配置 CPU、内存和磁盘的隔离 VM。文件系统可以跨会话快照/恢复。\n\n```yaml\nterminal:\n backend: modal\n container_cpu: 1 # CPU 核心数\n container_memory: 5120 # MB(5GB)\n container_disk: 51200 # MB(50GB)\n container_persistent: true # 快照/恢复文件系统\n```\n\n**必需:** `MODAL_TOKEN_ID` + `MODAL_TOKEN_SECRET` 环境变量,或 `~/.modal.toml` 配置文件。\n\n**持久化:** 启用后,沙箱文件系统在清理时快照,并在下次会话时恢复。快照在 `~/.hermes/modal_snapshots.json` 中跟踪。这保留文件系统状态,而非活跃进程、PID 空间或后台任务。\n\n**凭据文件:** 自动从 `~/.hermes/` 挂载(OAuth token 等),并在每条命令前同步。\n\n### Daytona 后端\n\n在 [Daytona](https://daytona.io) 托管工作区中运行命令。支持停止/恢复以实现持久化。\n\n```yaml\nterminal:\n backend: daytona\n container_cpu: 1 # CPU 核心数\n container_memory: 5120 # MB → 转换为 GiB\n container_disk: 10240 # MB → 转换为 GiB(最大 10 GiB)\n container_persistent: true # 停止/恢复而非删除\n```\n\n**必需:** `DAYTONA_API_KEY` 环境变量。\n\n**持久化:** 启用后,沙箱在清理时停止(而非删除),并在下次会话时恢复。沙箱名称遵循 `hermes-{task_id}` 模式。\n\n**磁盘限制:** Daytona 强制执行 10 GiB 最大值。超过此值的请求将被截断并发出警告。\n\n### Singularity/Apptainer 后端\n\n在 [Singularity/Apptainer](https://apptainer.org) 容器中运行命令。专为 Docker 不可用的 HPC 集群和共享机器设计。\n\n```yaml\nterminal:\n backend: singularity\n singularity_image: \"docker://nikolaik/python-nodejs:python3.11-nodejs20\"\n container_cpu: 1 # CPU 核心数\n container_memory: 5120 # MB\n container_persistent: true # 可写覆盖层跨会话持久化\n```\n\n**要求:** `$PATH` 中有 `apptainer` 或 `singularity` 二进制文件。\n\n**镜像处理:** Docker URL(`docker://...`)自动转换为 SIF 文件并缓存。现有 `.sif` 文件直接使用。\n\n**临时目录:** 按顺序解析:`TERMINAL_SCRATCH_DIR` → `TERMINAL_SANDBOX_DIR/singularity` → `/scratch/$USER/hermes-agent`(HPC 惯例)→ `~/.hermes/sandboxes/singularity`。\n\n**隔离:** 使用 `--containall --no-home` 实现完全命名空间隔离,不挂载宿主 home 目录。\n\n### 常见终端后端问题\n\n如果终端命令立即失败或终端工具报告为已禁用:\n\n- **Local** —— 无特殊要求。入门时最安全的默认选项。\n- **Docker** —— 运行 `docker version` 验证 Docker 是否正常工作。如果失败,修复 Docker 或执行 `hermes config set terminal.backend local`。\n- **SSH** —— `TERMINAL_SSH_HOST` 和 `TERMINAL_SSH_USER` 都必须设置。如果缺少任一项,Hermes 会记录清晰的错误。\n- **Modal** —— 需要 `MODAL_TOKEN_ID` 环境变量或 `~/.modal.toml`。运行 `hermes doctor` 检查。\n- **Daytona** —— 需要 `DAYTONA_API_KEY`。Daytona SDK 处理服务器 URL 配置。\n- **Singularity** —— 需要 `$PATH` 中有 `apptainer` 或 `singularity`。HPC 集群上常见。\n\n如有疑问,将 `terminal.backend` 设回 `local` 并首先验证命令在那里运行。\n\n### 拆卸时远程到宿主文件同步\n\n对于 **SSH**、**Modal** 和 **Daytona** 后端(agent 的工作树位于与运行 Hermes 的宿主不同的机器上),Hermes 跟踪 agent 在远程沙箱中触及的文件,并在会话拆卸/沙箱清理时,将修改的文件**同步回宿主**,存放在 `~/.hermes/cache/remote-syncs/<session-id>/` 下。\n\n- 触发时机:会话关闭、`/new`、`/reset`、gateway 消息超时、子 agent 使用远程后端时 `delegate_task` 子 agent 完成。\n- 覆盖 agent 修改的整个树,而不仅仅是它明确打开的文件。添加、编辑和删除都会被捕获。\n- 远程沙箱可能在您查找时已被拆除;本地 `~/.hermes/cache/remote-syncs/…` 副本是 agent 更改内容的权威记录。\n- 大型二进制输出(模型检查点、原始数据集)按大小限制 —— 同步跳过超过 `file_sync_max_mb`(默认 `100`)的文件。如果您期望更大的工件返回,请调高该值。\n\n```yaml\nterminal:\n file_sync_max_mb: 100 # 默认 —— 同步最大 100 MB 的文件\n file_sync_enabled: true # 默认 —— 设为 false 可完全跳过同步\n```\n\n这是从会话结束后被销毁的临时云沙箱中恢复结果的方式,无需告诉 agent 显式地 `scp` 或 `modal volume put` 每个工件。\n\n### Docker 卷挂载\n\n使用 Docker 后端时,`docker_volumes` 允许您与容器共享宿主目录。每个条目使用标准 Docker `-v` 语法:`host_path:container_path[:options]`。\n\n```yaml\nterminal:\n backend: docker\n docker_volumes:\n - \"/home/user/projects:/workspace/projects\" # 读写(默认)\n - \"/home/user/datasets:/data:ro\" # 只读\n - \"/home/user/.hermes/cache/documents:/output\" # Gateway 可见的导出\n```\n\n适用于:\n- **向 agent 提供文件**(数据集、配置、参考代码)\n- **从 agent 接收文件**(生成的代码、报告、导出)\n- **共享工作区**,您和 agent 都访问相同的文件\n\n如果您使用消息 gateway 并希望 agent 通过 `MEDIA:/...` 发送生成的文件,建议使用专用的宿主可见导出挂载,例如 `/home/user/.hermes/cache/documents:/output`。\n\n- 在 Docker 中将文件写入 `/output/...`\n- 在 `MEDIA:` 中发出**宿主路径**,例如:`MEDIA:/home/user/.hermes/cache/documents/report.txt`\n- **不要**发出 `/workspace/...` 或 `/output/...`,除非该确切路径在宿主上对 gateway 进程也存在\n\n:::warning\nYAML 重复键会静默覆盖之前的键。如果您已有 `docker_volumes:` 块,请将新挂载合并到同一列表中,而不是在文件后面再添加一个 `docker_volumes:` 键。\n:::\n\n也可以通过环境变量设置:`TERMINAL_DOCKER_VOLUMES='[\"/host:/container\"]'`(JSON 数组)。\n\n### Docker 凭据转发\n\n默认情况下,Docker 终端会话不继承任意宿主凭据。如果您需要在容器内使用特定 token,请将其添加到 `terminal.docker_forward_env`。\n\n```yaml\nterminal:\n backend: docker\n docker_forward_env:\n - \"GITHUB_TOKEN\"\n - \"NPM_TOKEN\"\n```\n\nHermes 首先从您当前的 shell 解析每个列出的变量,然后回退到通过 `hermes config set` 保存的 `~/.hermes/.env`。\n\n:::warning\n`docker_forward_env` 中列出的任何内容都会对容器内运行的命令可见。只转发您愿意暴露给终端会话的凭据。\n:::\n\n### 以宿主用户身份运行容器\n\n默认情况下,Docker 容器以 `root`(UID 0)身份运行。在 `/workspace` 或其他绑定挂载中创建的文件在宿主上归 root 所有,因此会话结束后您必须 `sudo chown` 才能从宿主编辑器编辑它们。`terminal.docker_run_as_host_user` 标志解决了这个问题:\n\n```yaml\nterminal:\n backend: docker\n docker_run_as_host_user: true # 默认:false\n```\n\n启用后,Hermes 将 `--user $(id -u):$(id -g)` 附加到 `docker run` 命令,使写入绑定挂载目录(`/workspace`、`/root`、`docker_volumes` 中的任何内容)的文件归您的宿主用户所有,而非 root。权衡:容器将无法再 `apt install` 或写入 `/root/.npm` 等 root 拥有的路径 —— 如果您同时需要这两者,请使用 `HOME` 归非 root 用户所有的基础镜像(或在镜像构建时添加所需工具)。\n\n保持 `false`(默认)以获得向后兼容的行为。当您的工作流主要是\"编辑挂载的宿主文件\"且厌倦了 `sudo chown -R` 时,请开启此选项。\n\n### 可选:将启动目录挂载到 `/workspace`\n\nDocker 沙箱默认保持隔离。Hermes **不会**将您当前的宿主工作目录传入容器,除非您明确选择加入。\n\n在 `config.yaml` 中启用:\n\n```yaml\nterminal:\n backend: docker\n docker_mount_cwd_to_workspace: true\n```\n\n启用后:\n- 如果您从 `~/projects/my-app` 启动 Hermes,该宿主目录将绑定挂载到 `/workspace`\n- Docker 后端从 `/workspace` 开始\n- 文件工具和终端命令都能看到相同的挂载项目\n\n禁用时,`/workspace` 保持沙箱所有,除非您通过 `docker_volumes` 显式挂载内容。\n\n安全权衡:\n- `false` 保留沙箱边界\n- `true` 使沙箱直接访问您启动 Hermes 的目录\n\n仅在您有意希望容器处理实时宿主文件时才选择加入。\n\n### 持久 Shell\n\n默认情况下,每条终端命令在其自己的子进程中运行 —— 工作目录、环境变量和 shell 变量在命令之间重置。启用**持久 shell** 后,单个长期运行的 bash 进程在 `execute()` 调用之间保持存活,使状态在命令之间保持。\n\n这对 **SSH 后端**最有用,它还消除了每条命令的连接开销。持久 shell **对 SSH 默认启用**,对本地后端禁用。\n\n```yaml\nterminal:\n persistent_shell: true # 默认 —— 为 SSH 启用持久 shell\n```\n\n禁用:\n\n```bash\nhermes config set terminal.persistent_shell false\n```\n\n**跨命令保持的内容:**\n- 工作目录(`cd /tmp` 对下一条命令生效)\n- 导出的环境变量(`export FOO=bar`)\n- Shell 变量(`MY_VAR=hello`)\n\n**优先级:**\n\n| 级别 | 变量 | 默认值 |\n|-------|----------|---------|\n| 配置 | `terminal.persistent_shell` | `true` |\n| SSH 覆盖 | `TERMINAL_SSH_PERSISTENT` | 遵循配置 |\n| Local 覆盖 | `TERMINAL_LOCAL_PERSISTENT` | `false` |\n\n每个后端的环境变量具有最高优先级。如果您也想在本地后端使用持久 shell:\n\n```bash\nexport TERMINAL_LOCAL_PERSISTENT=true\n```\n\n:::note\n需要 `stdin_data` 或 sudo 的命令会自动回退到单次模式,因为持久 shell 的 stdin 已被 IPC 协议占用。\n:::\n\n有关每个后端的详细信息,请参阅[代码执行](features/code-execution.md)和 [README 的终端部分](features/tools.md)。\n\n## 技能设置\n\n技能可以通过其 SKILL.md frontmatter 声明自己的配置设置。这些是非机密值(路径、偏好、域设置),存储在 `config.yaml` 的 `skills.config` 命名空间下。\n\n```yaml\nskills:\n config:\n myplugin:\n path: ~/myplugin-data # 示例 —— 每个技能定义自己的键\n```\n\n**技能设置的工作原理:**\n\n- `hermes config migrate` 扫描所有已启用的技能,找到未配置的设置,并提供提示\n- `hermes config show` 在\"技能设置\"下显示所有技能设置及其所属技能\n- 技能加载时,其解析的配置值会自动注入到技能上下文中\n\n**手动设置值:**\n\n```bash\nhermes config set skills.config.myplugin.path ~/myplugin-data\n```\n\n有关在您自己的技能中声明配置设置的详细信息,请参阅[创建技能 — 配置设置](/developer-guide/creating-skills#config-settings-configyaml)。\n\n### Agent 创建技能写入的守卫\n\n当 agent 使用 `skill_manage` 创建、编辑、修补或删除技能时,Hermes 可以选择扫描新/更新的内容以查找危险关键字模式(凭据收集、明显的 prompt 注入、数据外泄指令)。扫描器**默认关闭** —— 合法触及 `~/.ssh/` 或提及 `$OPENAI_API_KEY` 的真实 agent 工作流触发启发式规则过于频繁。如果您希望扫描器在 agent 的技能写入落地前提示您,请重新开启:\n\n```yaml\nskills:\n guard_agent_created: true # 默认:false\n```\n\n开启后,任何被标记的 `skill_manage` 写入都会以审批提示的形式出现,并附带扫描器的理由。接受的写入落地;拒绝的写入向 agent 返回解释性错误。\n\n## 内存配置\n\n```yaml\nmemory:\n memory_enabled: true\n user_profile_enabled: true\n memory_char_limit: 2200 # ~800 tokens\n user_char_limit: 1375 # ~500 tokens\n```\n\n## 文件读取安全\n\n控制单次 `read_file` 调用可以返回多少内容。超过限制的读取将被拒绝,并向 agent 返回错误,提示使用 `offset` 和 `limit` 读取较小范围。这可以防止单次读取压缩的 JS 包或大型数据文件时淹没上下文窗口。\n\n```yaml\nfile_read_max_chars: 100000 # 默认 —— ~25-35K tokens\n```\n\n如果您使用具有大上下文窗口的模型并经常读取大文件,请调高此值。对于小上下文模型,请降低以保持读取高效:\n\n```yaml\n# 大上下文模型(200K+)\nfile_read_max_chars: 200000\n\n# 小型本地模型(16K 上下文)\nfile_read_max_chars: 30000\n```\n\nAgent 还会自动去重文件读取 —— 如果同一文件区域被读取两次且文件未更改,则返回轻量级存根而不是重新发送内容。这在上下文压缩后重置,以便 agent 在内容被摘要后可以重新读取文件。\n\n## 工具输出截断限制\n\n三个相关的上限控制工具在 Hermes 截断之前可以返回多少原始输出:\n\n```yaml\ntool_output:\n max_bytes: 50000 # 终端输出上限(字符)\n max_lines: 2000 # read_file 分页上限\n max_line_length: 2000 # read_file 行号视图中的每行上限\n```\n\n- **`max_bytes`** —— 当 `terminal` 命令产生超过此字符数的合并 stdout/stderr 时,Hermes 保留前 40% 和后 60%,并在中间插入 `[OUTPUT TRUNCATED]` 通知。默认 `50000`(典型分词器约 12-15K tokens)。\n- **`max_lines`** —— 单次 `read_file` 调用的 `limit` 参数上限。超过此值的请求将被截断,以防单次读取淹没上下文窗口。默认 `2000`。\n- **`max_line_length`** —— `read_file` 发出行号视图时应用的每行上限。超过此长度的行将被截断为此字符数,后跟 `... [truncated]`。默认 `2000`。\n\n对于具有大上下文窗口且每次调用可以承受更多原始输出的模型,请调高限制。对于小上下文模型,请降低以保持工具结果紧凑:\n\n```yaml\n# 大上下文模型(200K+)\ntool_output:\n max_bytes: 150000\n max_lines: 5000\n\n# 小型本地模型(16K 上下文)\ntool_output:\n max_bytes: 20000\n max_lines: 500\n```\n\n## 全局工具集禁用\n\n要在 CLI 和每个 gateway 平台上统一禁用特定工具集,请在 `agent.disabled_toolsets` 下列出其名称:\n\n```yaml\nagent:\n disabled_toolsets:\n - memory # 隐藏内存工具 + MEMORY_GUIDANCE 注入\n - web # 任何地方都不使用 web_search / web_extract\n```\n\n这在每个平台的工具配置(由 `hermes tools` 写入的 `platform_toolsets`)**之后**应用,因此此处列出的工具集始终被删除 —— 即使平台的已保存配置仍然列出它。当您希望有一个\"到处关闭 X\"的单一开关而不是编辑 `hermes tools` UI 中的 15+ 个平台行时,请使用此选项。\n\n留空列表或省略键不会产生任何效果。\n\n## Git Worktree 隔离\n\n启用隔离的 git worktree,以便在同一仓库上并行运行多个 agent:\n\n```yaml\nworktree: true # 始终创建 worktree(与 hermes -w 相同)\n# worktree: false # 默认 —— 仅在传递 -w 标志时\n```\n\n启用后,每个 CLI 会话在 `.worktrees/` 下创建一个带有自己分支的新 worktree。Agent 可以编辑文件、提交、推送和创建 PR,而不会相互干扰。干净的 worktree 在退出时删除;脏的 worktree 保留以供手动恢复。\n\n您还可以通过仓库根目录中的 `.worktreeinclude` 列出要复制到 worktree 的 gitignore 文件:\n\n```\n# .worktreeinclude\n.env\n.venv/\nnode_modules/\n```\n\n## 上下文压缩\n\nHermes 自动压缩长对话以保持在模型的上下文窗口内。压缩摘要器是一个单独的 LLM 调用 —— 您可以将其指向任何 provider 或端点。\n\n所有压缩设置都在 `config.yaml` 中(无环境变量)。\n\n### 完整参考\n\n```yaml\ncompression:\n enabled: true # 开启/关闭压缩\n threshold: 0.50 # 在上下文限制的此百分比时压缩\n target_ratio: 0.20 # 保留为最近尾部的阈值分数\n protect_last_n: 20 # 保持未压缩的最少最近消息数\n hygiene_hard_message_limit: 400 # Gateway 安全阀 —— 见下文\n\n# 摘要模型/provider 在 auxiliary: 下配置:\nauxiliary:\n compression:\n model: \"\" # 空 = 使用主聊天模型。覆盖为例如 \"google/gemini-3-flash-preview\" 以获得更便宜/更快的压缩。\n provider: \"auto\" # Provider:\"auto\"、\"openrouter\"、\"nous\"、\"codex\"、\"main\" 等\n base_url: null # 自定义 OpenAI 兼容端点(覆盖 provider)\n```\n\n:::info 旧版配置迁移\n带有 `compression.summary_model`、`compression.summary_provider` 和 `compression.summary_base_url` 的旧版配置在首次加载时自动迁移到 `auxiliary.compression.*`(配置版本 17)。无需手动操作。\n:::\n\n`hygiene_hard_message_limit` 是仅限 gateway 的**预压缩安全阀**。拥有数千条消息的失控会话可能在正常的上下文百分比阈值触发之前就达到模型上下文限制;当消息数超过此上限时,Hermes 强制压缩,无论 token 使用情况如何。默认 `400` —— 对于非常长的会话正常的平台,请调高;要强制更积极的压缩,请降低。在运行中的 gateway 上编辑此值将在下一条消息时生效(见下文)。\n\n:::tip Gateway 热重载压缩和上下文长度\n从最近的版本开始,在运行中的 gateway 上编辑 `config.yaml` 中的 `model.context_length` 或任何 `compression.*` 键将在下一条消息时生效 —— 无需 gateway 重启、`/reset` 或会话轮换。缓存的 agent 签名包含这些键,因此 gateway 在检测到更改时会透明地重建 agent。API 密钥和工具/技能配置仍需要通常的重载路径。\n:::\n\n### 常见设置\n\n**默认(自动检测)—— 无需配置:**\n```yaml\ncompression:\n enabled: true\n threshold: 0.50\n```\n使用您的主 provider 和主模型。如果您希望在比主聊天模型更便宜的模型上进行压缩,请覆盖每任务(例如 `auxiliary.compression.provider: openrouter` + `model: google/gemini-2.5-flash`)。\n\n**强制特定 provider**(基于 OAuth 或 API 密钥):\n```yaml\nauxiliary:\n compression:\n provider: nous\n model: gemini-3-flash\n```\n适用于任何 provider:`nous`、`openrouter`、`codex`、`anthropic`、`main` 等。\n\n**自定义端点**(自托管、Ollama、zai、DeepSeek 等):\n```yaml\nauxiliary:\n compression:\n model: glm-4.7\n base_url: https://api.z.ai/api/coding/paas/v4\n```\n指向自定义 OpenAI 兼容端点。使用 `OPENAI_API_KEY` 进行认证。\n\n### 三个旋钮的交互方式\n\n| `auxiliary.compression.provider` | `auxiliary.compression.base_url` | 结果 |\n|---------------------|---------------------|--------|\n| `auto`(默认) | 未设置 | 自动检测最佳可用 provider |\n| `nous` / `openrouter` / 等 | 未设置 | 强制使用该 provider,使用其认证 |\n| 任意 | 已设置 | 直接使用自定义端点(忽略 provider) |\n\n:::warning 摘要模型上下文长度要求\n摘要模型**必须**具有至少与您的主 agent 模型一样大的上下文窗口。压缩器将对话的完整中间部分发送给摘要模型 —— 如果该模型的上下文窗口小于主模型的,摘要调用将因上下文长度错误而失败。发生这种情况时,中间轮次将**在没有摘要的情况下被丢弃**,静默丢失对话上下文。如果您覆盖模型,请验证其上下文长度满足或超过您的主模型。\n:::\n\n## 上下文引擎\n\n上下文引擎控制在接近模型 token 限制时如何管理对话。内置的 `compressor` 引擎使用有损摘要(参见[上下文压缩](/developer-guide/context-compression-and-caching))。插件引擎可以用替代策略替换它。\n\n```yaml\ncontext:\n engine: \"compressor\" # 默认 —— 内置有损摘要\n```\n\n使用插件引擎(例如,用于无损上下文管理的 LCM):\n\n```yaml\ncontext:\n engine: \"lcm\" # 必须与插件名称匹配\n```\n\n插件引擎**永远不会自动激活** —— 您必须将 `context.engine` 显式设置为插件名称。可用引擎可以通过 `hermes plugins` → Provider Plugins → Context Engine 浏览和选择。\n\n有关内存插件的类似单选系统,请参阅[内存 Providers](/user-guide/features/memory-providers)。\n\n## 迭代预算压力\n\n当 agent 在处理具有许多工具调用的复杂任务时,它可能会在没有意识到预算不足的情况下耗尽其迭代预算(默认:90 轮)。预算压力会在模型接近限制时自动发出警告:\n\n| 阈值 | 级别 | 模型看到的内容 |\n|-----------|-------|---------------------|\n| **70%** | 注意 | `[BUDGET: 63/90. 27 iterations left. Start consolidating.]` |\n| **90%** | 警告 | `[BUDGET WARNING: 81/90. Only 9 left. Respond NOW.]` |\n\n警告注入到最后一个工具结果的 JSON 中(作为 `_budget_warning` 字段),而不是作为单独的消息 —— 这保留了 prompt 缓存,不会破坏对话结构。\n\n```yaml\nagent:\n max_turns: 90 # 每次对话轮次的最大迭代次数(默认:90)\n api_max_retries: 3 # 回退启动前每个 provider 的重试次数(默认:3)\n```\n\n预算压力默认启用。Agent 自然地将警告视为工具结果的一部分,鼓励它在耗尽迭代之前整合工作并提供响应。\n\n当迭代预算完全耗尽时,CLI 向用户显示通知:`⚠ Iteration budget reached (90/90) — response may be incomplete`。如果预算在活跃工作期间耗尽,agent 会在停止前生成已完成内容的摘要。\n\n`agent.api_max_retries` 控制 Hermes 在回退 provider 切换启动**之前**对瞬时错误(速率限制、连接断开、5xx)重试 provider API 调用的次数。默认为 `3` —— 总共四次尝试。如果您配置了[回退 providers](/user-guide/features/fallback-providers) 并希望更快地故障转移,请将其降至 `0`,这样主 provider 上的第一个瞬时错误会立即切换到回退,而不是对不稳定的端点进行重试。\n\n### API 超时\n\nHermes 对流式传输有单独的超时层,以及用于非流式调用的陈旧检测器。陈旧检测器仅在您将其保留为隐式默认值时才会自动调整本地 provider。\n\n| 超时 | 默认值 | 本地 providers | 配置/环境变量 |\n|---------|---------|----------------|--------------|\n| Socket 读取超时 | 120s | 自动提升至 1800s | `HERMES_STREAM_READ_TIMEOUT` |\n| 陈旧流检测 | 180s | 自动禁用 | `HERMES_STREAM_STALE_TIMEOUT` |\n| 陈旧非流检测 | 300s | 保持隐式时自动禁用 | `providers.<id>.stale_timeout_seconds` 或 `HERMES_API_CALL_STALE_TIMEOUT` |\n| API 调用(非流式) | 1800s | 不变 | `providers.<id>.request_timeout_seconds` / `timeout_seconds` 或 `HERMES_API_TIMEOUT` |\n\n**Socket 读取超时**控制 httpx 等待 provider 下一个数据块的时间。本地 LLM 在大上下文上预填充可能需要几分钟才能产生第一个 token,因此当 Hermes 检测到本地端点时,会将此值提升至 30 分钟。如果您显式设置 `HERMES_STREAM_READ_TIMEOUT`,无论端点检测如何,始终使用该值。\n\n**陈旧流检测**终止接收 SSE 保活 ping 但没有实际内容的连接。对于本地 providers,这完全禁用,因为它们在预填充期间不发送保活 ping。\n\n**陈旧非流检测**终止长时间没有响应的非流式调用。默认情况下,Hermes 在本地端点上禁用此功能,以避免长时间预填充期间的误报。如果您显式设置 `providers.<id>.stale_timeout_seconds`、`providers.<id>.models.<model>.stale_timeout_seconds` 或 `HERMES_API_CALL_STALE_TIMEOUT`,即使在本地端点上也会遵守该显式值。\n\n## 上下文压力警告\n\n与迭代预算压力分开,上下文压力跟踪对话距**压缩阈值**有多近 —— 即上下文压缩触发以摘要旧消息的点。这有助于您和 agent 了解对话何时变长。\n\n| 进度 | 级别 | 发生的事情 |\n|----------|-------|-------------|\n| **≥ 60%** 到阈值 | 信息 | CLI 显示青色进度条;gateway 发送信息通知 |\n| **≥ 85%** 到阈值 | 警告 | CLI 显示粗体黄色进度条;gateway 警告压缩即将发生 |\n\n在 CLI 中,上下文压力在工具输出流中显示为进度条:\n\n```\n ◐ context ████████████░░░░░░░░ 62% to compaction 48k threshold (50%) · approaching compaction\n```\n\n在消息平台上,发送纯文本通知:\n\n```\n◐ Context: ████████████░░░░░░░░ 62% to compaction (threshold: 50% of window).\n```\n\n如果自动压缩被禁用,警告会告诉您上下文可能被截断。\n\n上下文压力是自动的 —— 无需配置。它纯粹作为面向用户的通知触发,不修改消息流或向模型上下文注入任何内容。\n\n## 凭据池策略\n\n当您为同一 provider 拥有多个 API 密钥或 OAuth token 时,配置轮换策略:\n\n```yaml\ncredential_pool_strategies:\n openrouter: round_robin # 均匀循环使用密钥\n anthropic: least_used # 始终选择使用最少的密钥\n```\n\n选项:`fill_first`(默认)、`round_robin`、`least_used`、`random`。完整文档请参阅[凭据池](/user-guide/features/credential-pools)。\n\n## Prompt 缓存\n\n当活跃 provider 支持时,Hermes 自动开启跨会话 prompt 缓存 —— 无需用户配置。\n\n对于**原生 Anthropic**、**OpenRouter** 和 **Nous Portal** 上的 Claude,Hermes 在系统提示词和技能块上附加带有 1 小时 TTL(`ttl: \"1h\"`)的 `cache_control` 断点。在新鲜的一小时内首次发送时按完整输入费率计费;同一小时内任何会话的后续发送以折扣缓存读取费率从缓存中提取。这意味着系统提示词、加载的技能内容以及任何长上下文包含的早期部分在第一个小时内跨 `hermes` 会话和分叉子 agent 被重用。\n\nQwen Cloud(阿里巴巴 DashScope)上游将缓存 TTL 限制为 5 分钟,因此 Hermes 在那里使用 5 分钟断点 TTL。其他通过第三方的 Claude 路径(AWS Bedrock、Azure Foundry)回退到 provider 自己的缓存默认值。xAI Grok 使用单独的会话固定对话 ID 机制 —— 参阅 [xAI prompt 缓存](/integrations/providers#xai-grok--responses-api--prompt-caching)。\n\n不存在禁用此功能的旋钮 —— 缓存始终开启,即使在单轮对话中也能节省费用,因为仅系统提示词就占输入 token 数的相当大比例。\n\n## 辅助模型\n\nHermes 使用\"辅助\"模型处理图像分析、网页摘要、浏览器截图分析、会话标题生成和上下文压缩等附带任务。默认情况下(`auxiliary.*.provider: \"auto\"`),Hermes 将每个辅助任务路由到您的**主聊天模型** —— 与您在 `hermes model` 中选择的相同 provider/模型。您无需配置任何内容即可开始,但请注意,在昂贵的推理模型(Opus、MiniMax M2.7 等)上,辅助任务会增加显著成本。如果您希望无论主模型如何都使用便宜且快速的附带任务,请显式设置 `auxiliary.<task>.provider` 和 `auxiliary.<task>.model`(例如,在 OpenRouter 上使用 Gemini Flash 进行视觉和网页提取)。\n\n:::note 为什么 \"auto\" 使用您的主模型\n早期版本将聚合器用户(OpenRouter、Nous Portal)分流到便宜的 provider 端默认值。这令人惊讶 —— 付费购买聚合器订阅的用户会看到不同的模型处理其辅助流量。`auto` 现在对所有人使用主模型,`config.yaml` 中的每任务覆盖仍然优先(见下方[完整辅助配置参考](#full-auxiliary-config-reference))。\n:::\n\n### 交互式配置辅助模型\n\n无需手动编辑 YAML,运行 `hermes model` 并从菜单中选择**\"配置辅助模型\"**。您将获得交互式的每任务选择器:\n\n```\n$ hermes model\n→ Configure auxiliary models\n\n[ ] vision currently: auto / main model\n[ ] web_extract currently: auto / main model\n[ ] title_generation currently: openrouter / google/gemini-3-flash-preview\n[ ] compression currently: auto / main model\n[ ] approval currently: auto / main model\n[ ] triage_specifier currently: auto / main model\n[ ] kanban_decomposer currently: auto / main model\n[ ] profile_describer currently: auto / main model\n```\n\n选择任务,选择 provider(OAuth 流程打开浏览器;API 密钥 provider 提示输入),选择模型。更改持久化到 `config.yaml` 中的 `auxiliary.<task>.*`。与主模型选择器相同的机制 —— 无需学习额外语法。\n\n### 视频教程\n\n<div style={{position: 'relative', width: '100%', aspectRatio: '16 / 9', marginBottom: '1.5rem'}}>\n <iframe\n src=\"https://www.youtube.com/embed/NoF-YajElIM\"\n title=\"Hermes Agent — Auxiliary Models Tutorial\"\n style={{position: 'absolute', top: 0, left: 0, width: '100%', height: '100%', border: 0}}\n allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\"\n allowFullScreen\n />\n</div>\n\n### 通用配置模式\n\nHermes 中的每个模型槽位 —— 辅助任务、压缩、回退 —— 使用相同的三个旋钮:\n\n| 键 | 作用 | 默认值 |\n|-----|-------------|---------|\n| `provider` | 用于认证和路由的 provider | `\"auto\"` |\n| `model` | 请求的模型 | provider 的默认值 |\n| `base_url` | 自定义 OpenAI 兼容端点(覆盖 provider) | 未设置 |\n\n当设置 `base_url` 时,Hermes 忽略 provider 并直接调用该端点(使用 `api_key` 或 `OPENAI_API_KEY` 进行认证)。当仅设置 `provider` 时,Hermes 使用该 provider 的内置认证和基础 URL。\n\n辅助任务的可用 providers:`auto`、`main`,以及[provider 注册表](/reference/environment-variables)中的任何 provider —— `openrouter`、`nous`、`openai-codex`、`copilot`、`copilot-acp`、`anthropic`、`gemini`、`google-gemini-cli`、`qwen-oauth`、`zai`、`kimi-coding`、`kimi-coding-cn`、`minimax`、`minimax-cn`、`minimax-oauth`、`deepseek`、`nvidia`、`xai`、`xai-oauth`、`ollama-cloud`、`alibaba`、`bedrock`、`huggingface`、`arcee`、`xiaomi`、`kilocode`、`opencode-zen`、`opencode-go`、`azure-foundry` —— 或您 `custom_providers` 列表中任何命名的自定义 provider(例如 `provider: \"beans\"`)。\n\n:::tip MiniMax OAuth\n`minimax-oauth` 通过浏览器 OAuth 登录(无需 API 密钥)。运行 `hermes model` 并选择 **MiniMax (OAuth)** 进行认证。辅助任务自动使用 `MiniMax-M2.7-highspeed`。参阅 [MiniMax OAuth 指南](../guides/minimax-oauth.md)。\n:::\n\n:::tip xAI Grok OAuth\n`xai-oauth` 通过浏览器 OAuth 为 SuperGrok 和 X Premium+ 订阅者登录(无需 API 密钥)。运行 `hermes model` 并选择 **xAI Grok OAuth (SuperGrok / Premium+)** 进行认证。相同的 OAuth token 可重用于每个直接到 xAI 的接口(聊天、辅助任务、TTS、图像生成、视频生成、转录)。参阅 [xAI Grok OAuth 指南](../guides/xai-grok-oauth.md),如果 Hermes 在远程主机上,请参阅 [SSH/远程主机上的 OAuth](../guides/oauth-over-ssh.md)。\n:::\n\n:::warning `\"main\"` 仅用于辅助任务\n`\"main\"` provider 选项表示\"使用我的主 agent 使用的任何 provider\" —— 它仅在 `auxiliary:`、`compression:` 和 `fallback_model:` 配置中有效。它**不是**顶级 `model.provider` 设置的有效值。如果您使用自定义 OpenAI 兼容端点,请在 `model:` 部分设置 `provider: custom`。所有主模型 provider 选项请参阅 [AI Providers](/integrations/providers)。\n:::\n\n### 完整辅助配置参考\n\n```yaml\nauxiliary:\n # 图像分析(vision_analyze 工具 + 浏览器截图)\n vision:\n provider: \"auto\" # \"auto\"、\"openrouter\"、\"nous\"、\"codex\"、\"main\" 等\n model: \"\" # 例如 \"openai/gpt-4o\"、\"google/gemini-2.5-flash\"\n base_url: \"\" # 自定义 OpenAI 兼容端点(覆盖 provider)\n api_key: \"\" # base_url 的 API 密钥(回退到 OPENAI_API_KEY)\n timeout: 120 # 秒 —— LLM API 调用超时;视觉负载需要宽裕的超时\n download_timeout: 30 # 秒 —— 图像 HTTP 下载;慢速连接请增加\n\n # 网页摘要 + 浏览器页面文本提取\n web_extract:\n provider: \"auto\"\n model: \"\" # 例如 \"google/gemini-2.5-flash\"\n base_url: \"\"\n api_key: \"\"\n timeout: 360 # 秒(6 分钟)—— 每次尝试的 LLM 摘要\n\n # 危险命令审批分类器\n approval:\n provider: \"auto\"\n model: \"\"\n base_url: \"\"\n api_key: \"\"\n timeout: 30 # 秒\n\n # 上下文压缩超时(与 compression.* 配置分开)\n compression:\n timeout: 120 # 秒 —— 压缩摘要长对话,需要更多时间\n # fallback_chain: # 可选 —— 发生速率限制/连接故障时尝试的 provider\n # - provider: nous\n # model: deepseek/deepseek-chat\n # - provider: openrouter\n # model: google/gemini-2.5-flash\n # base_url: \"\"\n # api_key: \"\"\n\n # 技能中心 —— 技能匹配和搜索\n skills_hub:\n provider: \"auto\"\n model: \"\"\n base_url: \"\"\n api_key: \"\"\n timeout: 30\n\n # MCP 工具调度\n mcp:\n provider: \"auto\"\n model: \"\"\n base_url: \"\"\n api_key: \"\"\n timeout: 30\n\n # Kanban 分类规格说明器 —— `hermes kanban specify <id>`(或\n # 仪表板上 Triage 列卡片的 ✨ Specify 按钮)使用此\n # 槽位将单行描述扩展为具体规格并将\n # 任务提升到 `todo`。便宜快速的模型在这里效果很好;规格扩展\n # 很短,不需要推理深度。\n triage_specifier:\n provider: \"auto\"\n model: \"\"\n base_url: \"\"\n api_key: \"\"\n timeout: 120\n```\n\n:::tip\n每个辅助任务都有可配置的 `timeout`(秒)。默认值:vision 120s、web_extract 360s、approval 30s、compression 120s。如果您为辅助任务使用慢速本地模型,请增加这些值。Vision 还有单独的 `download_timeout`(默认 30s)用于 HTTP 图像下载 —— 对于慢速连接或自托管图像服务器,请增加此值。\n:::\n\n:::info\n上下文压缩有自己的 `compression:` 块用于阈值,以及 `auxiliary.compression:` 块用于模型/provider 设置 —— 参阅上方的[上下文压缩](#context-compression)。主备用链使用顶层的 `fallback_providers:` 列表 —— 参阅[备用提供商](/integrations/providers#fallback-providers)。三者都遵循相同的 provider/model/base_url 模式。\n:::\n\n### 辅助任务的每任务回退链\n\n每个辅助任务都可以选择性地定义一个 `fallback_chain` —— 一个 provider/model 条目列表,当主要辅助 provider 因速率限制、网络连接问题或付费限制而失败时,Hermes 会尝试使用该列表:\n\n```yaml\nauxiliary:\n compression:\n provider: openrouter\n model: openai/gpt-4o-mini\n fallback_chain:\n - provider: nous\n model: deepseek/deepseek-chat\n - provider: openrouter\n model: google/gemini-2.5-flash\n```\n\n当主要辅助 provider(`openrouter` / `openai/gpt-4o-mini`)返回速率限制、连接超时或需要付费错误时,Hermes 将依次遍历 `fallback_chain`。它会跳过 provider 与已失败 provider 相同的条目,并尝试每个剩余条目,直到有一个成功或该链耗尽。如果所有回退都失败,Hermes 会回退到主 agent 模型作为最终的安全网。\n\n每个条目支持与任何辅助任务配置相同的三个旋钮:\n\n| 键 | 描述 |\n|-----|-------------|\n| `provider` | Provider 名称(`nous`、`openrouter`、`anthropic`、`gemini`、`main` 等) |\n| `model` | 该 provider 的模型名称 |\n| `base_url` | (可选)自定义 OpenAI 兼容端点 |\n\n`fallback_chain` 适用于任何辅助任务 —— `compression`、`vision`、`web_extract`、`approval`、`skills_hub`、`mcp` 等。\n\n### OpenRouter 路由和辅助任务的 Pareto Code\n\n当辅助任务解析到 OpenRouter(显式或通过 `provider: \"main\"` 而您的主 agent 在 OpenRouter 上)时,主 agent 的 `provider_routing` 和 `openrouter.min_coding_score` 设置**不会传播** —— 按设计,每个辅助任务是独立的。要为特定辅助任务设置 OpenRouter provider 偏好或使用 [Pareto Code 路由器](/integrations/providers#openrouter-pareto-code-router),请通过 `extra_body` 按任务设置:\n\n```yaml\nauxiliary:\n compression:\n provider: openrouter\n model: openrouter/pareto-code # 为此任务使用 Pareto Code 路由器\n extra_body:\n provider: # OpenRouter provider 路由偏好\n order: [anthropic, google] # 按此顺序尝试这些 providers\n sort: throughput # 或 \"price\" | \"latency\"\n # only: [anthropic] # 限制到特定 provider\n # ignore: [deepinfra] # 排除特定 providers\n plugins: # OpenRouter Pareto Code 路由器旋钮\n - id: pareto-router\n min_coding_score: 0.5 # 0.0–1.0;越高 = 更强的编码能力\n```\n\n形状与 OpenRouter 在聊天补全请求体中接受的内容一致。Hermes 原样转发整个 `extra_body`,因此 [openrouter.ai/docs](https://openrouter.ai/docs) 中记录的任何其他 OpenRouter 请求体字段都以相同方式工作。\n\n### 更改视觉模型\n\n使用 GPT-4o 而非 Gemini Flash 进行图像分析:\n\n```yaml\nauxiliary:\n vision:\n model: \"openai/gpt-4o\"\n```\n\n或通过环境变量(在 `~/.hermes/.env` 中):\n\n```bash\nAUXILIARY_VISION_MODEL=openai/gpt-4o\n```\n\n### Provider 选项\n\n这些选项适用于**辅助任务配置**(`auxiliary:`、`compression:`、`fallback_model:`),而非您的主 `model.provider` 设置。\n\n| Provider | 描述 | 要求 |\n|----------|-------------|-------------|\n| `\"auto\"` | 最佳可用(默认)。Vision 尝试 OpenRouter → Nous → Codex。 | — |\n| `\"openrouter\"` | 强制 OpenRouter —— 路由到任何模型(Gemini、GPT-4o、Claude 等) | `OPENROUTER_API_KEY` |\n| `\"nous\"` | 强制 Nous Portal | `hermes auth` |\n| `\"codex\"` | 强制 Codex OAuth(ChatGPT 账户)。支持视觉(gpt-5.3-codex)。 | `hermes model` → Codex |\n| `\"minimax-oauth\"` | 强制 MiniMax OAuth(浏览器登录,无需 API 密钥)。辅助任务使用 MiniMax-M2.7-highspeed。 | `hermes model` → MiniMax (OAuth) |\n| `\"xai-oauth\"` | 强制 xAI Grok OAuth(SuperGrok 或 X Premium+ 订阅者的浏览器登录,无需 API 密钥)。相同的 OAuth token 涵盖聊天、TTS、图像、视频和转录。 | `hermes model` → xAI Grok OAuth (SuperGrok / Premium+) |\n| `\"main\"` | 使用您的活跃自定义/主端点。可以来自 `OPENAI_BASE_URL` + `OPENAI_API_KEY` 或通过 `hermes model` / `config.yaml` 保存的自定义端点。适用于 OpenAI、本地模型或任何 OpenAI 兼容 API。**仅限辅助任务 —— 对 `model.provider` 无效。** | 自定义端点凭据 + 基础 URL |\n\n当您希望附带任务绕过默认路由器时,主 provider 目录中的直接 API 密钥 providers 也在这里工作。配置 `GMI_API_KEY` 后,`gmi` 有效:\n\n```yaml\nauxiliary:\n compression:\n provider: \"gmi\"\n model: \"anthropic/claude-opus-4.6\"\n```\n\n对于 GMI 辅助路由,使用 GMI 的 `/v1/models` 端点返回的确切模型 ID。\n\n### 常见设置\n\n**使用直接自定义端点**(比 `provider: \"main\"` 对本地/自托管 API 更清晰):\n```yaml\nauxiliary:\n vision:\n base_url: \"http://localhost:1234/v1\"\n api_key: \"local-key\"\n model: \"qwen2.5-vl\"\n```\n\n`base_url` 优先于 `provider`,因此这是将辅助任务路由到特定端点的最明确方式。对于直接端点覆盖,Hermes 使用配置的 `api_key` 或回退到 `OPENAI_API_KEY`;它不会为该自定义端点重用 `OPENROUTER_API_KEY`。\n\n**使用 OpenAI API 密钥进行视觉:**\n```yaml\n# 在 ~/.hermes/.env 中:\n# OPENAI_BASE_URL=https://api.openai.com/v1\n# OPENAI_API_KEY=sk-...\n\nauxiliary:\n vision:\n provider: \"main\"\n model: \"gpt-4o\" # 或 \"gpt-4o-mini\" 更便宜\n```\n\n**使用 OpenRouter 进行视觉**(路由到任何模型):\n```yaml\nauxiliary:\n vision:\n provider: \"openrouter\"\n model: \"openai/gpt-4o\" # 或 \"google/gemini-2.5-flash\" 等\n```\n\n**使用 Codex OAuth**(ChatGPT Pro/Plus 账户 —— 无需 API 密钥):\n```yaml\nauxiliary:\n vision:\n provider: \"codex\" # 使用您的 ChatGPT OAuth token\n # 模型默认为 gpt-5.3-codex(支持视觉)\n```\n\n**使用 MiniMax OAuth**(浏览器登录,无需 API 密钥):\n```yaml\nmodel:\n default: MiniMax-M2.7\n provider: minimax-oauth\n base_url: https://api.minimax.io/anthropic\n```\n运行 `hermes model` 并选择 **MiniMax (OAuth)** 自动登录并设置此项。对于中国区域,基础 URL 将是 `https://api.minimaxi.com/anthropic`。完整演练请参阅 [MiniMax OAuth 指南](../guides/minimax-oauth.md)。\n\n**使用本地/自托管模型:**\n```yaml\nauxiliary:\n vision:\n provider: \"main\" # 使用您的活跃自定义端点\n model: \"my-local-model\"\n```\n\n`provider: \"main\"` 使用 Hermes 用于普通聊天的任何 provider —— 无论是命名的自定义 provider(例如 `beans`)、内置 provider(如 `openrouter`)还是旧版 `OPENAI_BASE_URL` 端点。\n\n:::tip\n如果您使用 Codex OAuth 作为主模型 provider,视觉会自动工作 —— 无需额外配置。Codex 包含在视觉的自动检测链中。\n:::\n\n:::warning\n**视觉需要多模态模型。** 如果您设置 `provider: \"main\"`,请确保您的端点支持多模态/视觉 —— 否则图像分析将失败。\n:::\n\n### 环境变量(旧版)\n\n辅助模型也可以通过环境变量配置。但是,`config.yaml` 是首选方法 —— 它更易于管理,并支持所有选项,包括 `base_url` 和 `api_key`。\n\n| 设置 | 环境变量 |\n|---------|---------------------|\n| Vision provider | `AUXILIARY_VISION_PROVIDER` |\n| Vision 模型 | `AUXILIARY_VISION_MODEL` |\n| Vision 端点 | `AUXILIARY_VISION_BASE_URL` |\n| Vision API 密钥 | `AUXILIARY_VISION_API_KEY` |\n| Web 提取 provider | `AUXILIARY_WEB_EXTRACT_PROVIDER` |\n| Web 提取模型 | `AUXILIARY_WEB_EXTRACT_MODEL` |\n| Web 提取端点 | `AUXILIARY_WEB_EXTRACT_BASE_URL` |\n| Web 提取 API 密钥 | `AUXILIARY_WEB_EXTRACT_API_KEY` |\n\n压缩和回退模型设置仅限 config.yaml。\n\n:::tip\n运行 `hermes config` 查看您当前的辅助模型设置。覆盖仅在与默认值不同时显示。\n:::\n\n## 推理努力程度\n\n控制模型在响应前进行多少\"思考\":\n\n```yaml\nagent:\n reasoning_effort: \"\" # 空 = 中等(默认)。选项:none、minimal、low、medium、high、xhigh(最大)\n```\n\n未设置时(默认),推理努力程度默认为\"medium\" —— 适合大多数任务的平衡级别。设置值会覆盖它 —— 更高的推理努力程度在复杂任务上提供更好的结果,但代价是更多 token 和延迟。\n\n您也可以在运行时使用 `/reasoning` 命令更改推理努力程度:\n\n```\n/reasoning # 显示当前努力程度和显示状态\n/reasoning high # 将推理努力程度设为 high\n/reasoning none # 禁用推理\n/reasoning show # 在每次响应上方显示模型思考\n/reasoning hide # 隐藏模型思考\n```\n\n## 工具使用强制\n\n某些模型偶尔会将预期操作描述为文本而不是进行工具调用(\"我会运行测试...\"而不是实际调用终端)。工具使用强制会注入系统提示词指导,引导模型实际调用工具。\n\n```yaml\nagent:\n tool_use_enforcement: \"auto\" # \"auto\" | true | false | [\"model-substring\", ...]\n```\n\n| 值 | 行为 |\n|-------|----------|\n| `\"auto\"`(默认) | 对匹配以下模型启用:`gpt`、`codex`、`gemini`、`gemma`、`grok`。对所有其他模型禁用(Claude、DeepSeek、Qwen 等)。 |\n| `true` | 始终启用,无论模型如何。如果您注意到当前模型描述操作而不是执行操作,请使用此选项。 |\n| `false` | 始终禁用,无论模型如何。 |\n| `[\"gpt\", \"codex\", \"qwen\", \"llama\"]` | 仅当模型名称包含列出的子字符串之一时启用(不区分大小写)。 |\n\n### 注入的内容\n\n启用后,系统提示词中可能会添加三层指导:\n\n1. **通用工具使用强制**(所有匹配模型)—— 指示模型立即进行工具调用而不是描述意图,持续工作直到任务完成,永远不要以未来操作的承诺结束轮次。\n\n2. **OpenAI 执行纪律**(仅限 GPT 和 Codex 模型)—— 针对 GPT 特定失败模式的额外指导:在部分结果上放弃工作、跳过先决条件查找、幻觉而不是使用工具、在未验证的情况下宣布\"完成\"。\n\n3. **Google 操作指导**(仅限 Gemini 和 Gemma 模型)—— 简洁性、绝对路径、并行工具调用和编辑前验证模式。\n\n这些对用户透明,仅影响系统提示词。已经可靠使用工具的模型(如 Claude)不需要此指导,这就是为什么 `\"auto\"` 排除它们。\n\n### 何时开启\n\n如果您使用的模型不在默认自动列表中,并注意到它经常描述它*会*做什么而不是实际去做,请设置 `tool_use_enforcement: true` 或将模型子字符串添加到列表中:\n\n```yaml\nagent:\n tool_use_enforcement: [\"gpt\", \"codex\", \"gemini\", \"grok\", \"my-custom-model\"]\n```\n\n## TTS 配置\n\n```yaml\ntts:\n provider: \"edge\" # \"edge\" | \"elevenlabs\" | \"openai\" | \"minimax\" | \"mistral\" | \"gemini\" | \"xai\" | \"neutts\"\n speed: 1.0 # 全局速度倍数(所有 provider 的回退)\n edge:\n voice: \"en-US-AriaNeural\" # 322 种声音,74 种语言\n speed: 1.0 # 速度倍数(转换为速率百分比,例如 1.5 → +50%)\n elevenlabs:\n voice_id: \"pNInz6obpgDQGcFmaJgB\"\n model_id: \"eleven_multilingual_v2\"\n openai:\n model: \"gpt-4o-mini-tts\"\n voice: \"alloy\" # alloy、echo、fable、onyx、nova、shimmer\n speed: 1.0 # 速度倍数(API 限制为 0.25–4.0)\n base_url: \"https://api.openai.com/v1\" # 覆盖 OpenAI 兼容 TTS 端点\n minimax:\n speed: 1.0 # 语音速度倍数\n # base_url: \"\" # 可选:覆盖 OpenAI 兼容 TTS 端点\n mistral:\n model: \"voxtral-mini-tts-2603\"\n voice_id: \"c69964a6-ab8b-4f8a-9465-ec0925096ec8\" # Paul - Neutral(默认)\n gemini:\n model: \"gemini-2.5-flash-preview-tts\" # 或 gemini-2.5-pro-preview-tts\n voice: \"Kore\" # 30 种预置声音:Zephyr、Puck、Kore、Enceladus 等\n xai:\n voice_id: \"eve\" # xAI TTS 声音\n language: \"en\" # ISO 639-1\n sample_rate: 24000\n bit_rate: 128000 # MP3 比特率\n # base_url: \"https://api.x.ai/v1\"\n neutts:\n ref_audio: ''\n ref_text: ''\n model: neuphonic/neutts-air-q4-gguf\n device: cpu\n```\n\n这控制 `text_to_speech` 工具和语音模式中的口语回复(CLI 中的 `/voice tts` 或消息 gateway)。\n\n**速度回退层次:** provider 特定速度(例如 `tts.edge.speed`)→ 全局 `tts.speed` → `1.0` 默认值。设置全局 `tts.speed` 以在所有 provider 上应用统一速度,或按 provider 覆盖以进行精细控制。\n\n## 显示设置\n\n```yaml\ndisplay:\n tool_progress: all # off | new | all | verbose\n tool_progress_command: false # 在消息 gateway 中启用 /verbose 斜杠命令\n platforms: {} # 每平台显示覆盖(见下文)\n tool_progress_overrides: {} # 已弃用 —— 改用 display.platforms\n interim_assistant_messages: true # Gateway:将自然的轮次中 assistant 更新作为单独消息发送\n skin: default # 内置或自定义 CLI 皮肤(参阅 user-guide/features/skins)\n personality: \"kawaii\" # 旧版外观字段,仍在某些摘要中显示\n compact: false # 紧凑输出模式(减少空白)\n resume_display: full # full(恢复时显示之前的消息)| minimal(仅单行)\n bell_on_complete: false # 当 agent 完成时播放终端铃声(适合长任务)\n show_reasoning: false # 在每次响应上方显示模型推理/思考(用 /reasoning show|hide 切换)\n streaming: false # 将 token 实时流式传输到终端\n show_cost: false # 在 CLI 状态栏中显示估计 $ 成本\n timestamps: false # 为 true 时,在 CLI/TUI 记录中为用户和 assistant 标签添加 [HH:MM] 时间戳前缀\n tool_preview_length: 0 # 工具调用预览的最大字符数(0 = 无限制,显示完整路径/命令)\n runtime_footer: # Gateway:在最终回复中附加运行时上下文页脚\n enabled: false\n fields: [\"model\", \"context_pct\", \"cwd\"]\n file_mutation_verifier: true # 当本轮 write_file/patch 调用失败时附加建议性页脚\n language: en # 静态消息的 UI 语言(审批提示、部分 gateway 回复)。en | zh | zh-hant | ja | de | es | fr | tr | uk | af | ko | it | ga | pt | ru | hu\n```\n\n### 文件变更验证器\n\n当 `display.file_mutation_verifier` 为 `true`(默认)时,每当本轮中 `write_file` 或 `patch` 调用失败且从未被对同一路径的成功写入取代时,Hermes 会在 assistant 的最终响应中附加一行建议。这捕获了\"批量并行补丁,一半静默失败,模型总结成功\"这类过度声明,而无需您在每次编辑后手动运行 `git status`。\n\n示例页脚:\n\n```\n⚠️ File-mutation verifier: 3 file(s) were NOT modified this turn despite any wording above that may suggest otherwise. Run `git status` or `read_file` to confirm.\n • concepts/automatic-organization.md — [patch] Could not find match for old_string\n • concepts/lora.md — [patch] Could not find match for old_string\n • concepts/rag-pipeline.md — [patch] Could not find match for old_string\n```\n\n设置 `file_mutation_verifier: false`(或 `HERMES_FILE_MUTATION_VERIFIER=0`)以禁止页脚。验证器仅在轮次结束时有真实失败未解决时触发 —— 在同一轮次内重试失败补丁并成功的模型不会为该文件触发它。\n\n### 静态消息的 UI 语言\n\n`display.language` 设置翻译一小组静态面向用户的消息 —— CLI 审批提示、少数 gateway 斜杠命令回复(例如重启排空通知、\"审批已过期\"、\"目标已清除\")。它**不**翻译 agent 响应、日志行、工具输出、错误回溯或斜杠命令描述 —— 这些保持英文。如果您希望 agent 本身用另一种语言回复,只需在您的提示词或系统消息中告诉它。\n\n支持的值:`en`(默认)、`zh`(简体中文)、`zh-hant`(繁体中文)、`ja`(日语)、`de`(德语)、`es`(西班牙语)、`fr`(法语)、`tr`(土耳其语)、`uk`(乌克兰语)、`af`(南非荷兰语)、`ko`(韩语)、`it`(意大利语)、`ga`(爱尔兰语)、`pt`(葡萄牙语)、`ru`(俄语)、`hu`(匈牙利语)。未知值回退到英文。\n\n您也可以使用 `HERMES_LANGUAGE` 环境变量按会话设置,它会覆盖配置值。\n\n```yaml\ndisplay:\n language: zh # CLI 审批提示以中文显示\n```\n\n| 模式 | 您看到的内容 |\n|------|-------------|\n| `off` | 静默 —— 仅最终响应 |\n| `new` | 仅在工具更改时显示工具指示器 |\n| `all` | 每次工具调用附带简短预览(默认) |\n| `verbose` | 完整参数、结果和调试日志 |\n\n在 CLI 中,使用 `/verbose` 循环切换这些模式。要在消息平台(Telegram、Discord、Slack 等)中使用 `/verbose`,请在上方的 `display` 部分设置 `tool_progress_command: true`。该命令将循环切换模式并保存到配置。\n\n### 运行时元数据页脚(仅限 gateway)\n\n当 `display.runtime_footer.enabled: true` 时,Hermes 在每个 gateway 轮次的**最终**消息中附加一个小型运行时上下文页脚。目前页脚可显示模型、上下文窗口百分比和当前工作目录。默认关闭;如果您的团队希望每个回复都包含这些来源信息,请按 gateway 选择加入。\n\n```yaml\ndisplay:\n runtime_footer:\n enabled: true\n fields: [\"model\", \"context_pct\", \"cwd\"] # 支持字段:model、context_pct、cwd\n```\n\n`/footer` 斜杠命令在任何会话中运行时切换此功能。\n\n附加到 Telegram/Discord/Slack 回复的示例页脚:\n\n```\n— claude-opus-4.7 · 12 tool calls · 2m 14s · $0.042\n```\n\n只有轮次的**最终**消息获得页脚;中间更新保持干净。\n\n### 每平台进度覆盖\n\n不同平台有不同的详细程度需求。例如,Signal 无法编辑消息,因此每次进度更新都会成为单独的消息 —— 很嘈杂。使用 `display.platforms` 设置每平台模式:\n\n```yaml\ndisplay:\n tool_progress: all # 全局默认\n platforms:\n signal:\n tool_progress: 'off' # 在 Signal 上静默进度\n telegram:\n tool_progress: verbose # 在 Telegram 上详细进度\n slack:\n tool_progress: 'off' # 在共享 Slack 工作区中保持安静\n```\n\n没有覆盖的平台回退到全局 `tool_progress` 值。有效平台键:`telegram`、`discord`、`slack`、`signal`、`whatsapp`、`matrix`、`mattermost`、`email`、`sms`、`homeassistant`、`dingtalk`、`feishu`、`wecom`、`weixin`、`bluebubbles`、`qqbot`。旧版 `display.tool_progress_overrides` 键仍可加载以向后兼容,但已弃用,并在首次加载时迁移到 `display.platforms`。\n\n`interim_assistant_messages` 仅限 gateway。启用后,Hermes 将已完成的轮次中 assistant 更新作为单独的聊天消息发送。这与 `tool_progress` 无关,不需要 gateway 流式传输。\n\n## 隐私\n\n```yaml\nprivacy:\n redact_pii: false # 从 LLM 上下文中删除 PII(仅限 gateway)\n```\n\n当 `redact_pii` 为 `true` 时,gateway 在将系统提示词发送到受支持平台上的 LLM 之前,会从中删除个人身份信息:\n\n| 字段 | 处理方式 |\n|-------|-----------|\n| 电话号码(WhatsApp/Signal 上的用户 ID) | 哈希为 `user_<12-char-sha256>` |\n| 用户 ID | 哈希为 `user_<12-char-sha256>` |\n| 聊天 ID | 数字部分哈希,保留平台前缀(`telegram:<hash>`) |\n| 主频道 ID | 数字部分哈希 |\n| 用户名/昵称 | **不受影响**(用户选择的,公开可见) |\n\n**平台支持:** 删除适用于 WhatsApp、Signal 和 Telegram。Discord 和 Slack 被排除,因为它们的提及系统(`<@user_id>`)需要 LLM 上下文中的真实 ID。\n\n哈希是确定性的 —— 同一用户始终映射到同一哈希,因此模型仍然可以在群聊中区分用户。路由和传递在内部使用原始值。\n\n## 语音转文字(STT)\n\n```yaml\nstt:\n provider: \"local\" # \"local\" | \"groq\" | \"openai\" | \"mistral\"\n local:\n model: \"base\" # tiny、base、small、medium、large-v3\n openai:\n model: \"whisper-1\" # whisper-1 | gpt-4o-mini-transcribe | gpt-4o-transcribe\n # model: \"whisper-1\" # 旧版回退键仍受支持\n```\n\nProvider 行为:\n\n- `local` 使用在您机器上运行的 `faster-whisper`。使用 `pip install faster-whisper` 单独安装。\n- `groq` 使用 Groq 的 Whisper 兼容端点,读取 `GROQ_API_KEY`。\n- `openai` 使用 OpenAI 语音 API,读取 `VOICE_TOOLS_OPENAI_KEY`。\n\n如果请求的 provider 不可用,Hermes 按此顺序自动回退:`local` → `groq` → `openai`。\n\nGroq 和 OpenAI 模型覆盖由环境变量驱动:\n\n```bash\nSTT_GROQ_MODEL=whisper-large-v3-turbo\nSTT_OPENAI_MODEL=whisper-1\nGROQ_BASE_URL=https://api.groq.com/openai/v1\nSTT_OPENAI_BASE_URL=https://api.openai.com/v1\n```\n\n## 语音模式(CLI)\n\n```yaml\nvoice:\n record_key: \"ctrl+b\" # CLI 内的按键通话键\n max_recording_seconds: 120 # 长录音的硬停止\n auto_tts: false # /voice on 时自动启用口语回复\n beep_enabled: true # 在 CLI 语音模式中播放录音开始/停止提示音\n silence_threshold: 200 # 语音检测的 RMS 阈值\n silence_duration: 3.0 # 自动停止前的静默秒数\n```\n\n在 CLI 中使用 `/voice on` 启用麦克风模式,使用 `record_key` 开始/停止录音,使用 `/voice tts` 切换口语回复。端到端设置和平台特定行为请参阅[语音模式](/user-guide/features/voice-mode)。\n\n## 流式传输\n\n将 token 实时流式传输到终端或消息平台,而不是等待完整响应。\n\n### CLI 流式传输\n\n```yaml\ndisplay:\n streaming: true # 实时将 token 流式传输到终端\n show_reasoning: true # 同时流式传输推理/思考 token(可选)\n```\n\n启用后,响应在流式传输框内逐 token 出现。工具调用仍然静默捕获。如果 provider 不支持流式传输,它会自动回退到正常显示。\n\n### Gateway 流式传输(Telegram、Discord、Slack)\n\n```yaml\nstreaming:\n enabled: true # 启用渐进式消息编辑\n transport: edit # \"edit\"(渐进式消息编辑)或 \"off\"\n edit_interval: 0.3 # 消息编辑之间的秒数\n buffer_threshold: 40 # 强制编辑刷新前的字符数\n cursor: \" ▉\" # 流式传输期间显示的光标\n fresh_final_after_seconds: 0 # 预览超过此时间时选择发送新的最终消息(Telegram)\n```\n\n启用后,bot 在第一个 token 时发送消息,然后随着更多 token 到来渐进式编辑它。不支持消息编辑的平台(Signal、Email、Home Assistant)在第一次尝试时自动检测 —— 该会话的流式传输被优雅地禁用,不会产生大量消息。\n\n对于不带渐进式 token 编辑的独立自然轮次中 assistant 更新,请设置 `display.interim_assistant_messages: true`。\n\n**溢出处理:** 如果流式传输的文本超过平台的消息长度限制(约 4096 字符),当前消息被最终化,新消息自动开始。\n\n**新的最终消息(Telegram):** Telegram 的 `editMessageText` 保留原始消息时间戳,因此长时间运行的流式回复即使在完成后也会保留第一个 token 的时间戳。设置 `fresh_final_after_seconds > 0` 可选择将旧预览作为全新的最终消息传递,并尽力删除旧预览。默认值为 `0`,始终就地最终化流式回复,避免某些客户端短暂显示重复消息再删除其中一条。\n\n:::note\n主开关 `streaming.enabled` 默认为 `false`——在你启用之前不会有任何流式传输。启用后,是否流式传输按**平台**决定:Telegram 默认带有 `display.platforms.telegram.streaming: true`(流式传输),Discord 为 `display.platforms.discord.streaming: false`(不流式传输)。因此启用流式传输后,Telegram 开箱即用地流式传输,Discord 在你修改其开关之前仍使用整条消息回复。你可以在仪表盘的 **Channels** 开关中或直接在 `~/.hermes/config.yaml` 中调整这些按平台的开关。\n:::\n\n## 群聊会话隔离\n\n控制共享聊天是每个房间保持一个对话还是每个参与者一个对话:\n\n```yaml\ngroup_sessions_per_user: true # true = 群组/频道中每用户隔离,false = 每个聊天一个共享会话\n```\n\n- `true` 是默认和推荐设置。在 Discord 频道、Telegram 群组、Slack 频道和类似共享上下文中,当平台提供用户 ID 时,每个发送者获得自己的会话。\n- `false` 恢复到旧的共享房间行为。如果您明确希望 Hermes 将频道视为一个协作对话,这可能有用,但这也意味着用户共享上下文、token 成本和中断状态。\n- 私信不受影响。Hermes 仍然像往常一样通过聊天/DM ID 键入 DM。\n- 线程与其父频道保持隔离;使用 `true` 时,每个参与者在线程内也获得自己的会话。\n\n有关行为详情和示例,请参阅[会话](/user-guide/sessions)和 [Discord 指南](/user-guide/messaging/discord)。\n\n## 未授权 DM 行为\n\n控制当未知用户发送私信时 Hermes 的行为:\n\n```yaml\nunauthorized_dm_behavior: pair\n\nwhatsapp:\n unauthorized_dm_behavior: ignore\n```\n\n- `pair` 是默认值。Hermes 拒绝访问,但在 DM 中回复一次性配对码。\n- `ignore` 静默丢弃未授权的 DM。\n- 平台部分覆盖全局默认值,因此您可以在广泛范围内保持配对启用,同时使一个平台更安静。\n\n## 快速命令\n\n定义自定义命令,这些命令要么在不调用 LLM 的情况下运行 shell 命令,要么将一个斜杠命令别名为另一个。Exec 快速命令是零 token 的,对于从消息平台(Telegram、Discord 等)进行快速服务器检查或实用脚本很有用。\n\n```yaml\nquick_commands:\n status:\n type: exec\n command: systemctl status hermes-agent\n disk:\n type: exec\n command: df -h /\n update:\n type: exec\n command: cd ~/.hermes/hermes-agent && git pull && pip install -e .\n gpu:\n type: exec\n command: nvidia-smi --query-gpu=name,utilization.gpu,memory.used,memory.total --format=csv,noheader\n restart:\n type: alias\n target: /gateway restart\n```\n\n用法:在 CLI 或任何消息平台中输入 `/status`、`/disk`、`/update`、`/gpu` 或 `/restart`。`exec` 命令在宿主本地运行并直接返回输出 —— 无 LLM 调用,不消耗 token。`alias` 命令重写为配置的斜杠命令目标。\n\n- **30 秒超时** —— 长时间运行的命令被终止并显示错误消息\n- **优先级** —— 快速命令在技能命令之前检查,因此您可以覆盖技能名称\n- **自动补全** —— 快速命令在调度时解析,不显示在内置斜杠命令自动补全表中\n- **类型** —— 支持的类型为 `exec` 和 `alias`;其他类型显示错误\n- **到处可用** —— CLI、Telegram、Discord、Slack、WhatsApp、Signal、Email、Home Assistant\n\n仅字符串的 prompt 快捷方式不是有效的快速命令。对于可重用的 prompt 工作流,请创建技能或别名到现有斜杠命令。\n\n## 人类延迟\n\n在消息平台中模拟类人响应节奏:\n\n```yaml\nhuman_delay:\n mode: \"off\" # off | natural | custom\n min_ms: 800 # 最小延迟(自定义模式)\n max_ms: 2500 # 最大延迟(自定义模式)\n```\n\n## 代码执行\n\n配置 `execute_code` 工具:\n\n```yaml\ncode_execution:\n mode: project # project(默认)| strict\n timeout: 300 # 最大执行时间(秒)\n max_tool_calls: 50 # 代码执行中的最大工具调用次数\n```\n\n**`mode`** 控制脚本的工作目录和 Python 解释器:\n\n- **`project`**(默认)—— 脚本在会话的工作目录中以活跃 virtualenv/conda 环境的 python 运行。项目依赖(`pandas`、`torch`、项目包)和相对路径(`.env`、`./data.csv`)自然解析,与 `terminal()` 看到的一致。\n- **`strict`** —— 脚本在临时暂存目录中以 `sys.executable`(Hermes 自己的 python)运行。最大可重现性,但项目依赖和相对路径不会解析。\n\n环境清理(删除 `*_API_KEY`、`*_TOKEN`、`*_SECRET`、`*_PASSWORD`、`*_CREDENTIAL`、`*_PASSWD`、`*_AUTH`)和工具白名单在两种模式下完全相同 —— 切换模式不会改变安全态势。\n\n## Web 搜索后端\n\n`web_search` 和 `web_extract` 工具支持五种后端 provider。在 `config.yaml` 中或通过 `hermes tools` 配置后端:\n\n```yaml\nweb:\n backend: firecrawl # firecrawl | searxng | parallel | tavily | exa\n\n # 或使用每功能键混合 provider(例如免费搜索 + 付费提取):\n search_backend: \"searxng\"\n extract_backend: \"firecrawl\"\n```\n\n| 后端 | 环境变量 | 搜索 | 提取 |\n|---------|---------|--------|---------|\n| **Firecrawl**(默认) | `FIRECRAWL_API_KEY` | ✔ | ✔ |\n| **SearXNG** | `SEARXNG_URL` | ✔ | — |\n| **Parallel** | `PARALLEL_API_KEY` | ✔ | ✔ |\n| **Tavily** | `TAVILY_API_KEY` | ✔ | ✔ |\n| **Exa** | `EXA_API_KEY` | ✔ | ✔ |\n\n**后端选择:** 如果未设置 `web.backend`,后端从可用的 API 密钥自动检测。如果仅设置了 `SEARXNG_URL`,使用 SearXNG。如果仅设置了 `EXA_API_KEY`,使用 Exa。如果仅设置了 `TAVILY_API_KEY`,使用 Tavily。如果仅设置了 `PARALLEL_API_KEY`,使用 Parallel。否则 Firecrawl 是默认值。\n\n**SearXNG** 是一个免费、自托管、尊重隐私的元搜索引擎,查询 70+ 个搜索引擎。无需 API 密钥 —— 只需将 `SEARXNG_URL` 设置为您的实例(例如 `http://localhost:8080`)。SearXNG 仅限搜索;`web_extract` 需要单独的提取 provider(设置 `web.extract_backend`)。Docker 设置说明请参阅 [Web 搜索设置指南](/user-guide/features/web-search)。\n\n**自托管 Firecrawl:** 设置 `FIRECRAWL_API_URL` 指向您自己的实例。设置自定义 URL 后,API 密钥变为可选(在服务器上设置 `USE_DB_AUTHENTICATION=***` 以禁用认证)。\n\n**Parallel 搜索模式:** 设置 `PARALLEL_SEARCH_MODE` 控制搜索行为 —— `fast`、`one-shot` 或 `agentic`(默认:`agentic`)。\n\n**Exa:** 在 `~/.hermes/.env` 中设置 `EXA_API_KEY`。支持 `category` 过滤(`company`、`research paper`、`news`、`people`、`personal site`、`pdf`)和域名/日期过滤器。\n\n## 浏览器\n\n配置浏览器自动化行为:\n\n```yaml\nbrowser:\n inactivity_timeout: 120 # 自动关闭空闲会话前的秒数\n command_timeout: 30 # 浏览器命令超时(截图、导航等)(秒)\n record_sessions: false # 自动将浏览器会话录制为 WebM 视频到 ~/.hermes/browser_recordings/\n # 可选 CDP 覆盖 —— 设置后,Hermes 直接附加到您自己的\n # Chromium 系浏览器(通过 /browser connect),而不是启动无头浏览器。\n cdp_url: \"\"\n # 对话框监督器 —— 控制当 CDP 后端附加时(Browserbase、本地 Chromium 系\n # 浏览器通过 /browser connect)如何处理原生 JS 对话框(alert/confirm/prompt)。\n # 在 Camofox 和默认本地 agent 浏览器模式下忽略。\n dialog_policy: must_respond # must_respond | auto_dismiss | auto_accept\n dialog_timeout_s: 300 # must_respond 下的安全自动关闭(秒)\n camofox:\n managed_persistence: false # 为 true 时,Camofox 会话跨重启持久化 cookie/登录\n user_id: \"\" # 可选的外部管理 Camofox userId\n session_key: \"\" # Hermes 创建标签页时发送的可选会话密钥\n adopt_existing_tab: false # 在创建新标签页之前重用此身份的现有标签页\n```\n\n**对话框策略:**\n\n- `must_respond`(默认)—— 捕获对话框,在 `browser_snapshot.pending_dialogs` 中显示,等待 agent 调用 `browser_dialog(action=...)`。在 `dialog_timeout_s` 秒内无响应后,对话框被自动关闭以防止页面的 JS 线程永久停滞。\n- `auto_dismiss` —— 捕获,立即关闭。Agent 仍然在事后的 `browser_snapshot.recent_dialogs` 中看到对话框记录,`closed_by=\"auto_policy\"`。\n- `auto_accept` —— 捕获,立即接受。适用于有激进 `beforeunload` 提示的页面。\n\n完整对话框工作流请参阅[浏览器功能页面](./features/browser.md#browser_dialog)。\n\n浏览器工具集支持多个 provider。有关 Browserbase、Browser Use 和本地 Chromium 系 CDP 设置的详细信息,请参阅[浏览器功能页面](/user-guide/features/browser)。\n\n## 时区\n\n使用 IANA 时区字符串覆盖服务器本地时区。影响日志中的时间戳、cron 调度和系统提示词时间注入。\n\n```yaml\ntimezone: \"America/New_York\" # IANA 时区(默认:\"\" = 服务器本地时间)\n```\n\n支持的值:任何 IANA 时区标识符(例如 `America/New_York`、`Europe/London`、`Asia/Kolkata`、`UTC`)。留空或省略以使用服务器本地时间。\n\n## Discord\n\n为消息 gateway 配置 Discord 特定行为:\n\n```yaml\ndiscord:\n require_mention: true # 在服务器频道中需要 @提及才能响应\n free_response_channels: \"\" # 逗号分隔的频道 ID,bot 在这些频道无需 @提及即可响应\n auto_thread: true # 在频道中 @提及时自动创建线程\n```\n\n- `require_mention` —— 为 `true`(默认)时,bot 仅在服务器频道中被 `@BotName` 提及时响应。DM 始终无需提及即可工作。\n- `free_response_channels` —— 逗号分隔的频道 ID 列表,bot 在这些频道对每条消息响应,无需提及。\n- `auto_thread` —— 为 `true`(默认)时,频道中的提及会自动为对话创建线程,保持频道整洁(类似 Slack 线程)。\n\n## 安全\n\n预执行安全扫描和机密脱敏:\n\n```yaml\nsecurity:\n redact_secrets: false # 在工具输出和日志中脱敏 API 密钥模式(默认关闭)\n tirith_enabled: true # 为终端命令启用 Tirith 安全扫描\n tirith_path: \"tirith\" # tirith 二进制文件路径(默认:$PATH 中的 \"tirith\")\n tirith_timeout: 5 # 等待 tirith 扫描的秒数\n tirith_fail_open: true # 如果 tirith 不可用,允许命令执行\n website_blocklist: # 参见下方网站黑名单部分\n enabled: false\n domains: []\n shared_files: []\n```\n\n- `redact_secrets` —— 为 `true` 时,自动检测并脱敏工具输出中看起来像 API 密钥、token 和密码的模式,然后再进入对话上下文和日志。**默认关闭** —— 如果您经常在工具输出中处理真实凭据并希望有安全网,请启用。显式设置为 `true` 以开启。\n- `tirith_enabled` —— 为 `true` 时,终端命令在执行前由 [Tirith](https://github.com/sheeki03/tirith) 扫描以检测潜在危险操作。\n- `tirith_path` —— tirith 二进制文件的路径。如果 tirith 安装在非标准位置,请设置此项。\n- `tirith_timeout` —— 等待 tirith 扫描的最大秒数。如果扫描超时,命令继续执行。\n- `tirith_fail_open` —— 为 `true`(默认)时,如果 tirith 不可用或失败,允许命令执行。设置为 `false` 以在 tirith 无法验证时阻止命令。\n\n## 网站黑名单\n\n阻止 agent 的 web 和浏览器工具访问特定域名:\n\n```yaml\nsecurity:\n website_blocklist:\n enabled: false # 启用 URL 阻止(默认:false)\n domains: # 被阻止的域名模式列表\n - \"*.internal.company.com\"\n - \"admin.example.com\"\n - \"*.local\"\n shared_files: # 从外部文件加载额外规则\n - \"/etc/hermes/blocked-sites.txt\"\n```\n\n启用后,任何匹配被阻止域名模式的 URL 在 web 或浏览器工具执行之前都会被拒绝。这适用于 `web_search`、`web_extract`、`browser_navigate` 以及任何访问 URL 的工具。\n\n域名规则支持:\n- 精确域名:`admin.example.com`\n- 通配符子域名:`*.internal.company.com`(阻止所有子域名)\n- TLD 通配符:`*.local`\n\n共享文件每行包含一条域名规则(空行和 `#` 注释被忽略)。缺失或不可读的文件记录警告,但不禁用其他 web 工具。\n\n策略缓存 30 秒,因此配置更改无需重启即可快速生效。\n\n## 智能审批\n\n控制 Hermes 如何处理潜在危险命令:\n\n```yaml\napprovals:\n mode: manual # manual | smart | off\n```\n\n| 模式 | 行为 |\n|------|----------|\n| `manual`(默认) | 在执行任何被标记的命令之前提示用户。在 CLI 中显示交互式审批对话框。在消息中排队待处理的审批请求。 |\n| `smart` | 使用辅助 LLM 评估被标记的命令是否真正危险。低风险命令以会话级持久性自动批准。真正有风险的命令升级给用户。 |\n| `off` | 跳过所有审批检查。等同于 `HERMES_YOLO_MODE=true`。**谨慎使用。** |\n\n智能模式对于减少审批疲劳特别有用 —— 它让 agent 在安全操作上更自主地工作,同时仍然捕获真正破坏性的命令。\n\n:::warning\n设置 `approvals.mode: off` 会禁用终端命令的所有安全检查。仅在受信任的沙箱环境中使用。\n:::\n\n## 检查点\n\n破坏性文件操作之前的自动文件系统快照。详情请参阅[检查点与回滚](/user-guide/checkpoints-and-rollback)。\n\n```yaml\ncheckpoints:\n enabled: false # 启用自动检查点(也可:hermes chat --checkpoints)。默认:false(选择加入)。\n max_snapshots: 20 # 每个目录保留的最大检查点数(默认:20)\n```\n\n\n## 委托\n\n为委托工具配置子 agent 行为:\n\n```yaml\ndelegation:\n # model: \"google/gemini-3-flash-preview\" # 覆盖模型(空 = 继承父级)\n # provider: \"openrouter\" # 覆盖 provider(空 = 继承父级)\n # base_url: \"http://localhost:1234/v1\" # 直接 OpenAI 兼容端点(优先于 provider)\n # api_key: \"local-key\" # base_url 的 API 密钥(回退到 OPENAI_API_KEY)\n # api_mode: \"\" # base_url 的线路协议:\"chat_completions\"、\"codex_responses\" 或 \"anthropic_messages\"。空 = 从 URL 自动检测(例如 /anthropic 后缀 → anthropic_messages)。对启发式无法检测的非标准端点显式设置。\n max_concurrent_children: 3 # 每批并行子 agent 数(下限 1,无上限)。也可通过 DELEGATION_MAX_CONCURRENT_CHILDREN 环境变量设置。\n max_spawn_depth: 1 # 委托树深度上限(1-3,截断)。1 = 扁平(默认):父级生成无法委托的叶子。2 = 编排器子级可以生成叶子孙级。3 = 三级。\n orchestrator_enabled: true # 全局终止开关。为 false 时,role=\"orchestrator\" 被忽略,每个子级无论 max_spawn_depth 如何都被强制为叶子。\n```\n\n**子 agent provider:model 覆盖:** 默认情况下,子 agent 继承父 agent 的 provider 和模型。设置 `delegation.provider` 和 `delegation.model` 将子 agent 路由到不同的 provider:model 对 —— 例如,在您的主 agent 运行昂贵推理模型时,为范围较窄的子任务使用便宜/快速的模型。\n\n**直接端点覆盖:** 如果您想要明显的自定义端点路径,请设置 `delegation.base_url`、`delegation.api_key` 和 `delegation.model`。这将子 agent 直接发送到该 OpenAI 兼容端点,并优先于 `delegation.provider`。如果省略 `delegation.api_key`,Hermes 仅回退到 `OPENAI_API_KEY`。\n\n**线路协议(`api_mode`):** Hermes 从 `delegation.base_url` 自动检测线路协议(例如以 `/anthropic` 结尾的路径 → `anthropic_messages`;Codex/原生 Anthropic/Kimi-coding 主机名保留其现有检测)。对于启发式无法分类的端点 —— 例如 Azure AI Foundry、MiniMax、Zhipu GLM 或前置 Anthropic 形状后端的 LiteLLM 代理 —— 请将 `delegation.api_mode` 显式设置为 `chat_completions`、`codex_responses` 或 `anthropic_messages` 之一。留空(默认)以保持自动检测。\n\n委托 provider 使用与 CLI/gateway 启动相同的凭据解析。所有配置的 provider 均受支持:`openrouter`、`nous`、`copilot`、`zai`、`kimi-coding`、`minimax`、`minimax-cn`。设置 provider 时,系统自动解析正确的基础 URL、API 密钥和 API 模式 —— 无需手动凭据连接。\n\n**优先级:** 配置中的 `delegation.base_url` → 配置中的 `delegation.provider` → 父 provider(继承)。配置中的 `delegation.model` → 父模型(继承)。仅设置 `model` 而不设置 `provider` 仅更改模型名称,同时保留父级凭据(适用于在同一 provider(如 OpenRouter)内切换模型)。\n\n**宽度和深度:** `max_concurrent_children` 限制每批并行运行的子 agent 数量(默认 `3`,下限 1,无上限)。也可通过 `DELEGATION_MAX_CONCURRENT_CHILDREN` 环境变量设置。当模型提交的 `tasks` 数组超过上限时,`delegate_task` 返回工具错误解释限制,而不是静默截断。`max_spawn_depth` 控制委托树深度(截断到 1-3)。在默认 `1` 时,委托是扁平的:子级无法生成孙级,传递 `role=\"orchestrator\"` 静默降级为 `leaf`。提升到 `2` 使编排器子级可以生成叶子孙级;`3` 用于三级树。Agent 通过 `role=\"orchestrator\"` 按调用选择编排;`orchestrator_enabled: false` 强制每个子级回到叶子,无论如何。成本呈乘法增长 —— 在 `max_spawn_depth: 3` 和 `max_concurrent_children: 3` 时,树可以达到 3×3×3 = 27 个并发叶子 agent。使用模式请参阅[子 Agent 委托 → 深度限制和嵌套编排](features/delegation.md#depth-limit-and-nested-orchestration)。\n\n## 澄清\n\n配置澄清提示行为:\n\n```yaml\nclarify:\n timeout: 120 # 等待用户澄清响应的秒数\n```\n\n## 上下文文件(SOUL.md、AGENTS.md)\n\nHermes 使用两种不同的上下文范围:\n\n| 文件 | 用途 | 范围 |\n|------|---------|-------|\n| `SOUL.md` | **主要 agent 身份** —— 定义 agent 是谁(系统提示词第 #1 槽位) | `~/.hermes/SOUL.md` 或 `$HERMES_HOME/SOUL.md` |\n| `.hermes.md` / `HERMES.md` | 项目特定指令(最高优先级) | 向上走到 git 根目录 |\n| `AGENTS.md` | 项目特定指令、编码规范 | 递归目录遍历 |\n| `CLAUDE.md` | Claude Code 上下文文件(也会检测) | 仅工作目录 |\n| `.cursorrules` | Cursor IDE 规则(也会检测) | 仅工作目录 |\n| `.cursor/rules/*.mdc` | Cursor 规则文件(也会检测) | 仅工作目录 |\n\n- **SOUL.md** 是 agent 的主要身份。它占据系统提示词的第 #1 槽位,完全替换内置的默认身份。编辑它以完全自定义 agent 是谁。\n- 如果 SOUL.md 缺失、为空或无法加载,Hermes 回退到内置默认身份。\n- **项目上下文文件使用优先级系统** —— 仅加载一种类型(第一个匹配优先):`.hermes.md` → `AGENTS.md` → `CLAUDE.md` → `.cursorrules`。SOUL.md 始终独立加载。\n- **AGENTS.md** 是分层的:如果子目录也有 AGENTS.md,所有都会合并。\n- 如果 `SOUL.md` 不存在,Hermes 会自动生成默认的 `SOUL.md`。\n- 所有加载的上下文文件上限为 20,000 字符,并进行智能截断。\n\n另请参阅:\n- [个性与 SOUL.md](/user-guide/features/personality)\n- [上下文文件](/user-guide/features/context-files)\n\n## 工作目录\n\n| 上下文 | 默认值 |\n|---------|---------|\n| **CLI(`hermes`)** | 运行命令的当前目录 |\n| **消息 gateway** | 主目录 `~`(用 `MESSAGING_CWD` 覆盖) |\n| **Docker / Singularity / Modal / SSH** | 容器或远程机器内用户的主目录 |\n\n覆盖工作目录:\n```bash\n# 在 ~/.hermes/.env 或 ~/.hermes/config.yaml 中:\nMESSAGING_CWD=/home/myuser/projects # Gateway 会话\nTERMINAL_CWD=/workspace # 所有终端会话\n```\n"}, "cc30e0b659d47cc449b8e8df0129715973d445b6": {"t": 1785768001.0825818, "n": 56755, "h": "2242792eaa50077e", "c": "---\nsidebar_position: 2\ntitle: \"配置\"\ndescription: \"配置 Hermes Agent — config.yaml、providers、模型、API 密钥等\"\n---\n\n# 配置\n\n所有设置均存储在 `~/.hermes/` 目录中,便于访问。\n\n## 目录结构\n\n```text\n~/.hermes/\n├── config.yaml # 设置(模型、终端、TTS、压缩等)\n├── .env # API 密钥和机密\n├── auth.json # OAuth provider 凭据(Nous Portal 等)\n├── SOUL.md # 主要 agent 身份(系统提示词第 #1 槽位)\n├── memories/ # 持久记忆(MEMORY.md、USER.md)\n├── skills/ # Agent 创建的技能(通过 skill_manage 工具管理)\n├── cron/ # 定时任务\n├── sessions/ # Gateway 会话\n└── logs/ # 日志(errors.log、gateway.log — 机密自动脱敏)\n```\n\n## 管理配置\n\n```bash\nhermes config # 查看当前配置\nhermes config edit # 在编辑器中打开 config.yaml\nhermes config set KEY VAL # 设置特定值\nhermes config check # 检查缺失选项(更新后使用)\nhermes config migrate # 交互式添加缺失选项\n\n# 示例:\nhermes config set model anthropic/claude-opus-4\nhermes config set terminal.backend docker\nhermes config set OPENROUTER_API_KEY sk-or-... # 保存到 .env\n```\n\n:::tip\n`hermes config set` 命令会自动将值路由到正确的文件 —— API 密钥保存到 `.env`,其他所有内容保存到 `config.yaml`。\n:::\n\n## 配置优先级\n\n设置按以下顺序解析(优先级从高到低):\n\n1. **CLI 参数** —— 例如 `hermes chat --model anthropic/claude-sonnet-4`(单次调用覆盖)\n2. **`~/.hermes/config.yaml`** —— 所有非机密设置的主配置文件\n3. **`~/.hermes/.env`** —— 环境变量的回退;机密(API 密钥、token、密码)**必须**放这里\n4. **内置默认值** —— 未设置任何内容时的硬编码安全默认值\n\n:::info 经验法则\n机密(API 密钥、bot token、密码)放入 `.env`。其他所有内容(模型、终端后端、压缩设置、内存限制、工具集)放入 `config.yaml`。当两者都设置时,`config.yaml` 对非机密设置优先。\n:::\n\n## 环境变量替换\n\n可以在 `config.yaml` 中使用 `${VAR_NAME}` 语法引用环境变量:\n\n```yaml\nauxiliary:\n vision:\n api_key: ${GOOGLE_API_KEY}\n base_url: ${CUSTOM_VISION_URL}\n\ndelegation:\n api_key: ${DELEGATION_KEY}\n```\n\n单个值中可以有多个引用:`url: \"${HOST}:${PORT}\"`。如果引用的变量未设置,占位符将保持原样(`${UNDEFINED_VAR}` 保持不变)。仅支持 `${VAR}` 语法 —— 裸 `$VAR` 不会被展开。\n\n关于 AI provider 设置(OpenRouter、Anthropic、Copilot、自定义端点、自托管 LLM、回退模型等),请参阅 [AI Providers](/integrations/providers)。\n\n### Provider 超时\n\n可以为 provider 设置 `providers.<id>.request_timeout_seconds` 作为全局请求超时,以及 `providers.<id>.models.<model>.timeout_seconds` 作为特定模型的覆盖值。适用于每种传输方式(OpenAI-wire、原生 Anthropic、Anthropic 兼容)上的主轮次客户端、回退链、凭据轮换后的重建,以及(对于 OpenAI-wire)每请求超时 kwarg —— 因此配置值优先于旧版 `HERMES_API_TIMEOUT` 环境变量。\n\n还可以设置 `providers.<id>.stale_timeout_seconds` 用于非流式陈旧调用检测器,以及 `providers.<id>.models.<model>.stale_timeout_seconds` 作为特定模型的覆盖值。此值优先于旧版 `HERMES_API_CALL_STALE_TIMEOUT` 环境变量。\n\n不设置这些值将保持旧版默认值(`HERMES_API_TIMEOUT=1800`s、`HERMES_API_CALL_STALE_TIMEOUT=300`s、原生 Anthropic 900s)。目前不适用于 AWS Bedrock(`bedrock_converse` 和 AnthropicBedrock SDK 路径均使用 boto3 及其自身的超时配置)。请参阅 [`cli-config.yaml.example`](https://github.com/NousResearch/hermes-agent/blob/main/cli-config.yaml.example) 中的注释示例。\n\n## 终端后端配置\n\nHermes 支持六种终端后端。每种后端决定 agent 的 shell 命令实际在哪里执行 —— 本地机器、Docker 容器、通过 SSH 的远程服务器、Modal 云沙箱(直接或通过 Nous 托管的 gateway)、Daytona 工作区,或 Singularity/Apptainer 容器。\n\n```yaml\nterminal:\n backend: local # local | docker | ssh | modal | daytona | singularity\n cwd: \".\" # Gateway/cron 工作目录(CLI 始终使用启动目录)\n timeout: 180 # 每条命令的超时时间(秒)\n env_passthrough: [] # 转发到沙箱执行的环境变量名(terminal + execute_code)\n singularity_image: \"docker://nikolaik/python-nodejs:python3.11-nodejs20\" # Singularity 后端的容器镜像\n modal_image: \"nikolaik/python-nodejs:python3.11-nodejs20\" # Modal 后端的容器镜像\n daytona_image: \"nikolaik/python-nodejs:python3.11-nodejs20\" # Daytona 后端的容器镜像\n```\n\n对于 Modal 和 Daytona 等云沙箱,`container_persistent: true` 表示 Hermes 将尝试在沙箱重建后保留文件系统状态。这并不保证相同的活跃沙箱、PID 空间或后台进程之后仍在运行。\n\n### 后端概览\n\n| 后端 | 命令运行位置 | 隔离性 | 最适合 |\n|---------|-------------------|-----------|----------|\n| **local** | 直接在您的机器上 | 无 | 开发、个人使用 |\n| **docker** | 单个持久 Docker 容器(跨会话、`/new`、子 agent 共享) | 完全(命名空间、cap-drop) | 安全沙箱、CI/CD |\n| **ssh** | 通过 SSH 的远程服务器 | 网络边界 | 远程开发、强大硬件 |\n| **modal** | Modal 云沙箱 | 完全(云 VM) | 临时云计算、评估 |\n| **daytona** | Daytona 工作区 | 完全(云容器) | 托管云开发环境 |\n| **singularity** | Singularity/Apptainer 容器 | 命名空间(--containall) | HPC 集群、共享机器 |\n\n### Local 后端\n\n默认后端。命令直接在您的机器上运行,无隔离。无需特殊设置。\n\n```yaml\nterminal:\n backend: local\n```\n\n:::warning\nAgent 拥有与您的用户账户相同的文件系统访问权限。使用 `hermes tools` 禁用不需要的工具,或切换到 Docker 进行沙箱隔离。\n:::\n\n### Docker 后端\n\n在具有安全加固的 Docker 容器内运行命令(所有权限已删除、无权限提升、PID 限制)。\n\n**单个持久容器,而非每条命令一个容器。** Hermes 在首次使用时启动一个长期运行的容器,并通过 `docker exec` 将每个终端、文件和 `execute_code` 调用路由到同一容器中 —— 跨会话、`/new`、`/reset` 和 `delegate_task` 子 agent,贯穿 Hermes 进程的整个生命周期。工作目录更改、已安装的包以及 `/workspace` 中的文件会从一次工具调用延续到下一次,就像本地 shell 一样。容器在关闭时停止并删除。详情请参阅下方的**容器生命周期**。\n\n```yaml\nterminal:\n backend: docker\n docker_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n docker_mount_cwd_to_workspace: false # 将启动目录挂载到 /workspace\n docker_run_as_host_user: false # 参见下方\"以宿主用户身份运行容器\"\n docker_forward_env: # 转发到容器的环境变量\n - \"GITHUB_TOKEN\"\n docker_volumes: # 宿主目录挂载\n - \"/home/user/projects:/workspace/projects\"\n - \"/home/user/data:/data:ro\" # :ro 表示只读\n docker_extra_args: # 附加到 `docker run` 的额外标志\n - \"--gpus=all\"\n - \"--network=host\"\n\n # 资源限制\n container_cpu: 1 # CPU 核心数(0 = 不限制)\n container_memory: 5120 # MB(0 = 不限制)\n container_disk: 51200 # MB(需要 XFS+pquota 上的 overlay2)\n container_persistent: true # 跨会话持久化 /workspace 和 /root\n```\n\n**`terminal.docker_extra_args`**(也可通过 `TERMINAL_DOCKER_EXTRA_ARGS='[\"--gpus=all\"]'` 覆盖)允许传递 Hermes 未作为一级键公开的任意 `docker run` 标志 —— `--gpus`、`--network`、`--add-host`、替代 `--security-opt` 覆盖等。每个条目必须是字符串;该列表最后附加到组装好的 `docker run` 调用中,因此可以在需要时覆盖 Hermes 的默认值。请谨慎使用 —— 与沙箱加固(权限删除、`--user`、workspace 绑定挂载)冲突的标志将悄然削弱隔离性。\n\n**要求:** 已安装并运行 Docker Desktop 或 Docker Engine。Hermes 会探测 `$PATH` 以及常见的 macOS 安装位置(`/usr/local/bin/docker`、`/opt/homebrew/bin/docker`、Docker Desktop 应用包)。开箱即用支持 Podman:设置 `HERMES_DOCKER_BINARY=podman`(或完整路径)以在两者都安装时强制使用它。\n\n**容器生命周期:** Hermes 为每个终端和文件工具调用重用单个长期运行的容器(`docker run -d ... sleep 2h`),跨会话、`/new`、`/reset` 和 `delegate_task` 子 agent,贯穿 Hermes 进程的整个生命周期。命令通过带登录 shell 的 `docker exec` 运行,因此工作目录更改、已安装的包以及 `/workspace` 中的文件都会从一次工具调用延续到下一次。容器在 Hermes 关闭时(或空闲清理回收时)停止并删除。\n\n通过 `delegate_task(tasks=[...])` 生成的并行子 agent 共享这一个容器 —— 并发的 `cd`、环境变量修改以及对同一路径的写入会发生冲突。如果子 agent 需要隔离的沙箱,必须通过 `register_task_env_overrides()` 注册每任务镜像覆盖,RL 和基准测试环境(TerminalBench2、HermesSweEnv 等)会自动为其每任务 Docker 镜像执行此操作。\n\n**安全加固:**\n- `--cap-drop ALL`,仅添加回 `DAC_OVERRIDE`、`CHOWN`、`FOWNER`\n- `--security-opt no-new-privileges`\n- `--pids-limit 256`\n- `/tmp`(512MB)、`/var/tmp`(256MB)、`/run`(64MB)的大小限制 tmpfs\n\n**凭据转发:** `docker_forward_env` 中列出的环境变量首先从您的 shell 环境解析,然后回退到 `~/.hermes/.env`。技能也可以声明 `required_environment_variables`,这些变量会自动合并。\n\n### SSH 后端\n\n通过 SSH 在远程服务器上运行命令。使用 ControlMaster 进行连接复用(5 分钟空闲保活)。默认启用持久 shell —— 状态(cwd、环境变量)在命令之间保持。\n\n```yaml\nterminal:\n backend: ssh\n persistent_shell: true # 保持长期运行的 bash 会话(默认:true)\n```\n\n**必需的环境变量:**\n\n```bash\nTERMINAL_SSH_HOST=my-server.example.com\nTERMINAL_SSH_USER=ubuntu\n```\n\n**可选:**\n\n| 变量 | 默认值 | 描述 |\n|----------|---------|-------------|\n| `TERMINAL_SSH_PORT` | `22` | SSH 端口 |\n| `TERMINAL_SSH_KEY` | (系统默认) | SSH 私钥路径 |\n| `TERMINAL_SSH_PERSISTENT` | `true` | 启用持久 shell |\n\n**工作原理:** 使用 `BatchMode=yes` 和 `StrictHostKeyChecking=accept-new` 在初始化时连接。持久 shell 在远程主机上保持单个 `bash -l` 进程存活,通过临时文件进行通信。需要 `stdin_data` 或 `sudo` 的命令会自动回退到单次模式。\n\n### Modal 后端\n\n在 [Modal](https://modal.com) 云沙箱中运行命令。每个任务获得一个具有可配置 CPU、内存和磁盘的隔离 VM。文件系统可以跨会话快照/恢复。\n\n```yaml\nterminal:\n backend: modal\n container_cpu: 1 # CPU 核心数\n container_memory: 5120 # MB(5GB)\n container_disk: 51200 # MB(50GB)\n container_persistent: true # 快照/恢复文件系统\n```\n\n**必需:** `MODAL_TOKEN_ID` + `MODAL_TOKEN_SECRET` 环境变量,或 `~/.modal.toml` 配置文件。\n\n**持久化:** 启用后,沙箱文件系统在清理时快照,并在下次会话时恢复。快照在 `~/.hermes/modal_snapshots.json` 中跟踪。这保留文件系统状态,而非活跃进程、PID 空间或后台任务。\n\n**凭据文件:** 自动从 `~/.hermes/` 挂载(OAuth token 等),并在每条命令前同步。\n\n### Daytona 后端\n\n在 [Daytona](https://daytona.io) 托管工作区中运行命令。支持停止/恢复以实现持久化。\n\n```yaml\nterminal:\n backend: daytona\n container_cpu: 1 # CPU 核心数\n container_memory: 5120 # MB → 转换为 GiB\n container_disk: 10240 # MB → 转换为 GiB(最大 10 GiB)\n container_persistent: true # 停止/恢复而非删除\n```\n\n**必需:** `DAYTONA_API_KEY` 环境变量。\n\n**持久化:** 启用后,沙箱在清理时停止(而非删除),并在下次会话时恢复。沙箱名称遵循 `hermes-{task_id}` 模式。\n\n**磁盘限制:** Daytona 强制执行 10 GiB 最大值。超过此值的请求将被截断并发出警告。\n\n### Singularity/Apptainer 后端\n\n在 [Singularity/Apptainer](https://apptainer.org) 容器中运行命令。专为 Docker 不可用的 HPC 集群和共享机器设计。\n\n```yaml\nterminal:\n backend: singularity\n singularity_image: \"docker://nikolaik/python-nodejs:python3.11-nodejs20\"\n container_cpu: 1 # CPU 核心数\n container_memory: 5120 # MB\n container_persistent: true # 可写覆盖层跨会话持久化\n```\n\n**要求:** `$PATH` 中有 `apptainer` 或 `singularity` 二进制文件。\n\n**镜像处理:** Docker URL(`docker://...`)自动转换为 SIF 文件并缓存。现有 `.sif` 文件直接使用。\n\n**临时目录:** 按顺序解析:`TERMINAL_SCRATCH_DIR` → `TERMINAL_SANDBOX_DIR/singularity` → `/scratch/$USER/hermes-agent`(HPC 惯例)→ `~/.hermes/sandboxes/singularity`。\n\n**隔离:** 使用 `--containall --no-home` 实现完全命名空间隔离,不挂载宿主 home 目录。\n\n### 常见终端后端问题\n\n如果终端命令立即失败或终端工具报告为已禁用:\n\n- **Local** —— 无特殊要求。入门时最安全的默认选项。\n- **Docker** —— 运行 `docker version` 验证 Docker 是否正常工作。如果失败,修复 Docker 或执行 `hermes config set terminal.backend local`。\n- **SSH** —— `TERMINAL_SSH_HOST` 和 `TERMINAL_SSH_USER` 都必须设置。如果缺少任一项,Hermes 会记录清晰的错误。\n- **Modal** —— 需要 `MODAL_TOKEN_ID` 环境变量或 `~/.modal.toml`。运行 `hermes doctor` 检查。\n- **Daytona** —— 需要 `DAYTONA_API_KEY`。Daytona SDK 处理服务器 URL 配置。\n- **Singularity** —— 需要 `$PATH` 中有 `apptainer` 或 `singularity`。HPC 集群上常见。\n\n如有疑问,将 `terminal.backend` 设回 `local` 并首先验证命令在那里运行。\n\n### 拆卸时远程到宿主文件同步\n\n对于 **SSH**、**Modal** 和 **Daytona** 后端(agent 的工作树位于与运行 Hermes 的宿主不同的机器上),Hermes 跟踪 agent 在远程沙箱中触及的文件,并在会话拆卸/沙箱清理时,将修改的文件**同步回宿主**,存放在 `~/.hermes/cache/remote-syncs/<session-id>/` 下。\n\n- 触发时机:会话关闭、`/new`、`/reset`、gateway 消息超时、子 agent 使用远程后端时 `delegate_task` 子 agent 完成。\n- 覆盖 agent 修改的整个树,而不仅仅是它明确打开的文件。添加、编辑和删除都会被捕获。\n- 远程沙箱可能在您查找时已被拆除;本地 `~/.hermes/cache/remote-syncs/…` 副本是 agent 更改内容的权威记录。\n- 大型二进制输出(模型检查点、原始数据集)按大小限制 —— 同步跳过超过 `file_sync_max_mb`(默认 `100`)的文件。如果您期望更大的工件返回,请调高该值。\n\n```yaml\nterminal:\n file_sync_max_mb: 100 # 默认 —— 同步最大 100 MB 的文件\n file_sync_enabled: true # 默认 —— 设为 false 可完全跳过同步\n```\n\n这是从会话结束后被销毁的临时云沙箱中恢复结果的方式,无需告诉 agent 显式地 `scp` 或 `modal volume put` 每个工件。\n\n### Docker 卷挂载\n\n使用 Docker 后端时,`docker_volumes` 允许您与容器共享宿主目录。每个条目使用标准 Docker `-v` 语法:`host_path:container_path[:options]`。\n\n```yaml\nterminal:\n backend: docker\n docker_volumes:\n - \"/home/user/projects:/workspace/projects\" # 读写(默认)\n - \"/home/user/datasets:/data:ro\" # 只读\n - \"/home/user/.hermes/cache/documents:/output\" # Gateway 可见的导出\n```\n\n适用于:\n- **向 agent 提供文件**(数据集、配置、参考代码)\n- **从 agent 接收文件**(生成的代码、报告、导出)\n- **共享工作区**,您和 agent 都访问相同的文件\n\n如果您使用消息 gateway 并希望 agent 通过 `MEDIA:/...` 发送生成的文件,建议使用专用的宿主可见导出挂载,例如 `/home/user/.hermes/cache/documents:/output`。\n\n- 在 Docker 中将文件写入 `/output/...`\n- 在 `MEDIA:` 中发出**宿主路径**,例如:`MEDIA:/home/user/.hermes/cache/documents/report.txt`\n- **不要**发出 `/workspace/...` 或 `/output/...`,除非该确切路径在宿主上对 gateway 进程也存在\n\n:::warning\nYAML 重复键会静默覆盖之前的键。如果您已有 `docker_volumes:` 块,请将新挂载合并到同一列表中,而不是在文件后面再添加一个 `docker_volumes:` 键。\n:::\n\n也可以通过环境变量设置:`TERMINAL_DOCKER_VOLUMES='[\"/host:/container\"]'`(JSON 数组)。\n\n### Docker 凭据转发\n\n默认情况下,Docker 终端会话不继承任意宿主凭据。如果您需要在容器内使用特定 token,请将其添加到 `terminal.docker_forward_env`。\n\n```yaml\nterminal:\n backend: docker\n docker_forward_env:\n - \"GITHUB_TOKEN\"\n - \"NPM_TOKEN\"\n```\n\nHermes 首先从您当前的 shell 解析每个列出的变量,然后回退到通过 `hermes config set` 保存的 `~/.hermes/.env`。\n\n:::warning\n`docker_forward_env` 中列出的任何内容都会对容器内运行的命令可见。只转发您愿意暴露给终端会话的凭据。\n:::\n\n### 以宿主用户身份运行容器\n\n默认情况下,Docker 容器以 `root`(UID 0)身份运行。在 `/workspace` 或其他绑定挂载中创建的文件在宿主上归 root 所有,因此会话结束后您必须 `sudo chown` 才能从宿主编辑器编辑它们。`terminal.docker_run_as_host_user` 标志解决了这个问题:\n\n```yaml\nterminal:\n backend: docker\n docker_run_as_host_user: true # 默认:false\n```\n\n启用后,Hermes 将 `--user $(id -u):$(id -g)` 附加到 `docker run` 命令,使写入绑定挂载目录(`/workspace`、`/root`、`docker_volumes` 中的任何内容)的文件归您的宿主用户所有,而非 root。权衡:容器将无法再 `apt install` 或写入 `/root/.npm` 等 root 拥有的路径 —— 如果您同时需要这两者,请使用 `HOME` 归非 root 用户所有的基础镜像(或在镜像构建时添加所需工具)。\n\n保持 `false`(默认)以获得向后兼容的行为。当您的工作流主要是\"编辑挂载的宿主文件\"且厌倦了 `sudo chown -R` 时,请开启此选项。\n\n### 可选:将启动目录挂载到 `/workspace`\n\nDocker 沙箱默认保持隔离。Hermes **不会**将您当前的宿主工作目录传入容器,除非您明确选择加入。\n\n在 `config.yaml` 中启用:\n\n```yaml\nterminal:\n backend: docker\n docker_mount_cwd_to_workspace: true\n```\n\n启用后:\n- 如果您从 `~/projects/my-app` 启动 Hermes,该宿主目录将绑定挂载到 `/workspace`\n- Docker 后端从 `/workspace` 开始\n- 文件工具和终端命令都能看到相同的挂载项目\n\n禁用时,`/workspace` 保持沙箱所有,除非您通过 `docker_volumes` 显式挂载内容。\n\n安全权衡:\n- `false` 保留沙箱边界\n- `true` 使沙箱直接访问您启动 Hermes 的目录\n\n仅在您有意希望容器处理实时宿主文件时才选择加入。\n\n### 持久 Shell\n\n默认情况下,每条终端命令在其自己的子进程中运行 —— 工作目录、环境变量和 shell 变量在命令之间重置。启用**持久 shell** 后,单个长期运行的 bash 进程在 `execute()` 调用之间保持存活,使状态在命令之间保持。\n\n这对 **SSH 后端**最有用,它还消除了每条命令的连接开销。持久 shell **对 SSH 默认启用**,对本地后端禁用。\n\n```yaml\nterminal:\n persistent_shell: true # 默认 —— 为 SSH 启用持久 shell\n```\n\n禁用:\n\n```bash\nhermes config set terminal.persistent_shell false\n```\n\n**跨命令保持的内容:**\n- 工作目录(`cd /tmp` 对下一条命令生效)\n- 导出的环境变量(`export FOO=bar`)\n- Shell 变量(`MY_VAR=hello`)\n\n**优先级:**\n\n| 级别 | 变量 | 默认值 |\n|-------|----------|---------|\n| 配置 | `terminal.persistent_shell` | `true` |\n| SSH 覆盖 | `TERMINAL_SSH_PERSISTENT` | 遵循配置 |\n| Local 覆盖 | `TERMINAL_LOCAL_PERSISTENT` | `false` |\n\n每个后端的环境变量具有最高优先级。如果您也想在本地后端使用持久 shell:\n\n```bash\nexport TERMINAL_LOCAL_PERSISTENT=true\n```\n\n:::note\n需要 `stdin_data` 或 sudo 的命令会自动回退到单次模式,因为持久 shell 的 stdin 已被 IPC 协议占用。\n:::\n\n有关每个后端的详细信息,请参阅[代码执行](features/code-execution.md)和 [README 的终端部分](features/tools.md)。\n\n## 技能设置\n\n技能可以通过其 SKILL.md frontmatter 声明自己的配置设置。这些是非机密值(路径、偏好、域设置),存储在 `config.yaml` 的 `skills.config` 命名空间下。\n\n```yaml\nskills:\n config:\n myplugin:\n path: ~/myplugin-data # 示例 —— 每个技能定义自己的键\n```\n\n**技能设置的工作原理:**\n\n- `hermes config migrate` 扫描所有已启用的技能,找到未配置的设置,并提供提示\n- `hermes config show` 在\"技能设置\"下显示所有技能设置及其所属技能\n- 技能加载时,其解析的配置值会自动注入到技能上下文中\n\n**手动设置值:**\n\n```bash\nhermes config set skills.config.myplugin.path ~/myplugin-data\n```\n\n有关在您自己的技能中声明配置设置的详细信息,请参阅[创建技能 — 配置设置](/developer-guide/creating-skills#config-settings-configyaml)。\n\n### Agent 创建技能写入的守卫\n\n当 agent 使用 `skill_manage` 创建、编辑、修补或删除技能时,Hermes 可以选择扫描新/更新的内容以查找危险关键字模式(凭据收集、明显的 prompt 注入、数据外泄指令)。扫描器**默认关闭** —— 合法触及 `~/.ssh/` 或提及 `$OPENAI_API_KEY` 的真实 agent 工作流触发启发式规则过于频繁。如果您希望扫描器在 agent 的技能写入落地前提示您,请重新开启:\n\n```yaml\nskills:\n guard_agent_created: true # 默认:false\n```\n\n开启后,任何被标记的 `skill_manage` 写入都会以审批提示的形式出现,并附带扫描器的理由。接受的写入落地;拒绝的写入向 agent 返回解释性错误。\n\n## 内存配置\n\n```yaml\nmemory:\n memory_enabled: true\n user_profile_enabled: true\n memory_char_limit: 2200 # ~800 tokens\n user_char_limit: 1375 # ~500 tokens\n```\n\n## 文件读取安全\n\n控制单次 `read_file` 调用可以返回多少内容。超过限制的读取将被拒绝,并向 agent 返回错误,提示使用 `offset` 和 `limit` 读取较小范围。这可以防止单次读取压缩的 JS 包或大型数据文件时淹没上下文窗口。\n\n```yaml\nfile_read_max_chars: 100000 # 默认 —— ~25-35K tokens\n```\n\n如果您使用具有大上下文窗口的模型并经常读取大文件,请调高此值。对于小上下文模型,请降低以保持读取高效:\n\n```yaml\n# 大上下文模型(200K+)\nfile_read_max_chars: 200000\n\n# 小型本地模型(16K 上下文)\nfile_read_max_chars: 30000\n```\n\nAgent 还会自动去重文件读取 —— 如果同一文件区域被读取两次且文件未更改,则返回轻量级存根而不是重新发送内容。这在上下文压缩后重置,以便 agent 在内容被摘要后可以重新读取文件。\n\n## 工具输出截断限制\n\n三个相关的上限控制工具在 Hermes 截断之前可以返回多少原始输出:\n\n```yaml\ntool_output:\n max_bytes: 50000 # 终端输出上限(字符)\n max_lines: 2000 # read_file 分页上限\n max_line_length: 2000 # read_file 行号视图中的每行上限\n```\n\n- **`max_bytes`** —— 当 `terminal` 命令产生超过此字符数的合并 stdout/stderr 时,Hermes 保留前 40% 和后 60%,并在中间插入 `[OUTPUT TRUNCATED]` 通知。默认 `50000`(典型分词器约 12-15K tokens)。\n- **`max_lines`** —— 单次 `read_file` 调用的 `limit` 参数上限。超过此值的请求将被截断,以防单次读取淹没上下文窗口。默认 `2000`。\n- **`max_line_length`** —— `read_file` 发出行号视图时应用的每行上限。超过此长度的行将被截断为此字符数,后跟 `... [truncated]`。默认 `2000`。\n\n对于具有大上下文窗口且每次调用可以承受更多原始输出的模型,请调高限制。对于小上下文模型,请降低以保持工具结果紧凑:\n\n```yaml\n# 大上下文模型(200K+)\ntool_output:\n max_bytes: 150000\n max_lines: 5000\n\n# 小型本地模型(16K 上下文)\ntool_output:\n max_bytes: 20000\n max_lines: 500\n```\n\n## 全局工具集禁用\n\n要在 CLI 和每个 gateway 平台上统一禁用特定工具集,请在 `agent.disabled_toolsets` 下列出其名称:\n\n```yaml\nagent:\n disabled_toolsets:\n - memory # 隐藏内存工具 + MEMORY_GUIDANCE 注入\n - web # 任何地方都不使用 web_search / web_extract\n```\n\n这在每个平台的工具配置(由 `hermes tools` 写入的 `platform_toolsets`)**之后**应用,因此此处列出的工具集始终被删除 —— 即使平台的已保存配置仍然列出它。当您希望有一个\"到处关闭 X\"的单一开关而不是编辑 `hermes tools` UI 中的 15+ 个平台行时,请使用此选项。\n\n留空列表或省略键不会产生任何效果。\n\n## Git Worktree 隔离\n\n启用隔离的 git worktree,以便在同一仓库上并行运行多个 agent:\n\n```yaml\nworktree: true # 始终创建 worktree(与 hermes -w 相同)\n# worktree: false # 默认 —— 仅在传递 -w 标志时\n```\n\n启用后,每个 CLI 会话在 `.worktrees/` 下创建一个带有自己分支的新 worktree。Agent 可以编辑文件、提交、推送和创建 PR,而不会相互干扰。干净的 worktree 在退出时删除;脏的 worktree 保留以供手动恢复。\n\n您还可以通过仓库根目录中的 `.worktreeinclude` 列出要复制到 worktree 的 gitignore 文件:\n\n```\n# .worktreeinclude\n.env\n.venv/\nnode_modules/\n```\n\n## 上下文压缩\n\nHermes 自动压缩长对话以保持在模型的上下文窗口内。压缩摘要器是一个单独的 LLM 调用 —— 您可以将其指向任何 provider 或端点。\n\n所有压缩设置都在 `config.yaml` 中(无环境变量)。\n\n### 完整参考\n\n```yaml\ncompression:\n enabled: true # 开启/关闭压缩\n threshold: 0.50 # 在上下文限制的此百分比时压缩\n target_ratio: 0.20 # 保留为最近尾部的阈值分数\n protect_last_n: 20 # 保持未压缩的最少最近消息数\n hygiene_hard_message_limit: 400 # Gateway 安全阀 —— 见下文\n\n# 摘要模型/provider 在 auxiliary: 下配置:\nauxiliary:\n compression:\n model: \"\" # 空 = 使用主聊天模型。覆盖为例如 \"google/gemini-3-flash-preview\" 以获得更便宜/更快的压缩。\n provider: \"auto\" # Provider:\"auto\"、\"openrouter\"、\"nous\"、\"codex\"、\"main\" 等\n base_url: null # 自定义 OpenAI 兼容端点(覆盖 provider)\n```\n\n:::info 旧版配置迁移\n带有 `compression.summary_model`、`compression.summary_provider` 和 `compression.summary_base_url` 的旧版配置在首次加载时自动迁移到 `auxiliary.compression.*`(配置版本 17)。无需手动操作。\n:::\n\n`hygiene_hard_message_limit` 是仅限 gateway 的**预压缩安全阀**。拥有数千条消息的失控会话可能在正常的上下文百分比阈值触发之前就达到模型上下文限制;当消息数超过此上限时,Hermes 强制压缩,无论 token 使用情况如何。默认 `400` —— 对于非常长的会话正常的平台,请调高;要强制更积极的压缩,请降低。在运行中的 gateway 上编辑此值将在下一条消息时生效(见下文)。\n\n:::tip Gateway 热重载压缩和上下文长度\n从最近的版本开始,在运行中的 gateway 上编辑 `config.yaml` 中的 `model.context_length` 或任何 `compression.*` 键将在下一条消息时生效 —— 无需 gateway 重启、`/reset` 或会话轮换。缓存的 agent 签名包含这些键,因此 gateway 在检测到更改时会透明地重建 agent。API 密钥和工具/技能配置仍需要通常的重载路径。\n:::\n\n### 常见设置\n\n**默认(自动检测)—— 无需配置:**\n```yaml\ncompression:\n enabled: true\n threshold: 0.50\n```\n使用您的主 provider 和主模型。如果您希望在比主聊天模型更便宜的模型上进行压缩,请覆盖每任务(例如 `auxiliary.compression.provider: openrouter` + `model: google/gemini-2.5-flash`)。\n\n**强制特定 provider**(基于 OAuth 或 API 密钥):\n```yaml\nauxiliary:\n compression:\n provider: nous\n model: gemini-3-flash\n```\n适用于任何 provider:`nous`、`openrouter`、`codex`、`anthropic`、`main` 等。\n\n**自定义端点**(自托管、Ollama、zai、DeepSeek 等):\n```yaml\nauxiliary:\n compression:\n model: glm-4.7\n base_url: https://api.z.ai/api/coding/paas/v4\n```\n指向自定义 OpenAI 兼容端点。使用 `OPENAI_API_KEY` 进行认证。\n\n### 三个旋钮的交互方式\n\n| `auxiliary.compression.provider` | `auxiliary.compression.base_url` | 结果 |\n|---------------------|---------------------|--------|\n| `auto`(默认) | 未设置 | 自动检测最佳可用 provider |\n| `nous` / `openrouter` / 等 | 未设置 | 强制使用该 provider,使用其认证 |\n| 任意 | 已设置 | 直接使用自定义端点(忽略 provider) |\n\n:::warning 摘要模型上下文长度要求\n摘要模型**必须**具有至少与您的主 agent 模型一样大的上下文窗口。压缩器将对话的完整中间部分发送给摘要模型 —— 如果该模型的上下文窗口小于主模型的,摘要调用将因上下文长度错误而失败。发生这种情况时,中间轮次将**在没有摘要的情况下被丢弃**,静默丢失对话上下文。如果您覆盖模型,请验证其上下文长度满足或超过您的主模型。\n:::\n\n## 上下文引擎\n\n上下文引擎控制在接近模型 token 限制时如何管理对话。内置的 `compressor` 引擎使用有损摘要(参见[上下文压缩](/developer-guide/context-compression-and-caching))。插件引擎可以用替代策略替换它。\n\n```yaml\ncontext:\n engine: \"compressor\" # 默认 —— 内置有损摘要\n```\n\n使用插件引擎(例如,用于无损上下文管理的 LCM):\n\n```yaml\ncontext:\n engine: \"lcm\" # 必须与插件名称匹配\n```\n\n插件引擎**永远不会自动激活** —— 您必须将 `context.engine` 显式设置为插件名称。可用引擎可以通过 `hermes plugins` → Provider Plugins → Context Engine 浏览和选择。\n\n有关内存插件的类似单选系统,请参阅[内存 Providers](/user-guide/features/memory-providers)。\n\n## 迭代预算压力\n\n当 agent 在处理具有许多工具调用的复杂任务时,它可能会在没有意识到预算不足的情况下耗尽其迭代预算(默认:90 轮)。预算压力会在模型接近限制时自动发出警告:\n\n| 阈值 | 级别 | 模型看到的内容 |\n|-----------|-------|---------------------|\n| **70%** | 注意 | `[BUDGET: 63/90. 27 iterations left. Start consolidating.]` |\n| **90%** | 警告 | `[BUDGET WARNING: 81/90. Only 9 left. Respond NOW.]` |\n\n警告注入到最后一个工具结果的 JSON 中(作为 `_budget_warning` 字段),而不是作为单独的消息 —— 这保留了 prompt 缓存,不会破坏对话结构。\n\n```yaml\nagent:\n max_turns: 90 # 每次对话轮次的最大迭代次数(默认:90)\n api_max_retries: 3 # 回退启动前每个 provider 的重试次数(默认:3)\n```\n\n预算压力默认启用。Agent 自然地将警告视为工具结果的一部分,鼓励它在耗尽迭代之前整合工作并提供响应。\n\n当迭代预算完全耗尽时,CLI 向用户显示通知:`⚠ Iteration budget reached (90/90) — response may be incomplete`。如果预算在活跃工作期间耗尽,agent 会在停止前生成已完成内容的摘要。\n\n`agent.api_max_retries` 控制 Hermes 在回退 provider 切换启动**之前**对瞬时错误(速率限制、连接断开、5xx)重试 provider API 调用的次数。默认为 `3` —— 总共四次尝试。如果您配置了[回退 providers](/user-guide/features/fallback-providers) 并希望更快地故障转移,请将其降至 `0`,这样主 provider 上的第一个瞬时错误会立即切换到回退,而不是对不稳定的端点进行重试。\n\n### API 超时\n\nHermes 对流式传输有单独的超时层,以及用于非流式调用的陈旧检测器。陈旧检测器仅在您将其保留为隐式默认值时才会自动调整本地 provider。\n\n| 超时 | 默认值 | 本地 providers | 配置/环境变量 |\n|---------|---------|----------------|--------------|\n| Socket 读取超时 | 120s | 自动提升至 1800s | `HERMES_STREAM_READ_TIMEOUT` |\n| 陈旧流检测 | 180s | 自动禁用 | `HERMES_STREAM_STALE_TIMEOUT` |\n| 陈旧非流检测 | 300s | 保持隐式时自动禁用 | `providers.<id>.stale_timeout_seconds` 或 `HERMES_API_CALL_STALE_TIMEOUT` |\n| API 调用(非流式) | 1800s | 不变 | `providers.<id>.request_timeout_seconds` / `timeout_seconds` 或 `HERMES_API_TIMEOUT` |\n\n**Socket 读取超时**控制 httpx 等待 provider 下一个数据块的时间。本地 LLM 在大上下文上预填充可能需要几分钟才能产生第一个 token,因此当 Hermes 检测到本地端点时,会将此值提升至 30 分钟。如果您显式设置 `HERMES_STREAM_READ_TIMEOUT`,无论端点检测如何,始终使用该值。\n\n**陈旧流检测**终止接收 SSE 保活 ping 但没有实际内容的连接。对于本地 providers,这完全禁用,因为它们在预填充期间不发送保活 ping。\n\n**陈旧非流检测**终止长时间没有响应的非流式调用。默认情况下,Hermes 在本地端点上禁用此功能,以避免长时间预填充期间的误报。如果您显式设置 `providers.<id>.stale_timeout_seconds`、`providers.<id>.models.<model>.stale_timeout_seconds` 或 `HERMES_API_CALL_STALE_TIMEOUT`,即使在本地端点上也会遵守该显式值。\n\n## 上下文压力警告\n\n与迭代预算压力分开,上下文压力跟踪对话距**压缩阈值**有多近 —— 即上下文压缩触发以摘要旧消息的点。这有助于您和 agent 了解对话何时变长。\n\n| 进度 | 级别 | 发生的事情 |\n|----------|-------|-------------|\n| **≥ 60%** 到阈值 | 信息 | CLI 显示青色进度条;gateway 发送信息通知 |\n| **≥ 85%** 到阈值 | 警告 | CLI 显示粗体黄色进度条;gateway 警告压缩即将发生 |\n\n在 CLI 中,上下文压力在工具输出流中显示为进度条:\n\n```\n ◐ context ████████████░░░░░░░░ 62% to compaction 48k threshold (50%) · approaching compaction\n```\n\n在消息平台上,发送纯文本通知:\n\n```\n◐ Context: ████████████░░░░░░░░ 62% to compaction (threshold: 50% of window).\n```\n\n如果自动压缩被禁用,警告会告诉您上下文可能被截断。\n\n上下文压力是自动的 —— 无需配置。它纯粹作为面向用户的通知触发,不修改消息流或向模型上下文注入任何内容。\n\n## 凭据池策略\n\n当您为同一 provider 拥有多个 API 密钥或 OAuth token 时,配置轮换策略:\n\n```yaml\ncredential_pool_strategies:\n openrouter: round_robin # 均匀循环使用密钥\n anthropic: least_used # 始终选择使用最少的密钥\n```\n\n选项:`fill_first`(默认)、`round_robin`、`least_used`、`random`。完整文档请参阅[凭据池](/user-guide/features/credential-pools)。\n\n## Prompt 缓存\n\n当活跃 provider 支持时,Hermes 自动开启跨会话 prompt 缓存 —— 无需用户配置。\n\n对于**原生 Anthropic**、**OpenRouter** 和 **Nous Portal** 上的 Claude,Hermes 在系统提示词和技能块上附加带有 1 小时 TTL(`ttl: \"1h\"`)的 `cache_control` 断点。在新鲜的一小时内首次发送时按完整输入费率计费;同一小时内任何会话的后续发送以折扣缓存读取费率从缓存中提取。这意味着系统提示词、加载的技能内容以及任何长上下文包含的早期部分在第一个小时内跨 `hermes` 会话和分叉子 agent 被重用。\n\nQwen Cloud(阿里巴巴 DashScope)上游将缓存 TTL 限制为 5 分钟,因此 Hermes 在那里使用 5 分钟断点 TTL。其他通过第三方的 Claude 路径(AWS Bedrock、Azure Foundry)回退到 provider 自己的缓存默认值。xAI Grok 使用单独的会话固定对话 ID 机制 —— 参阅 [xAI prompt 缓存](/integrations/providers#xai-grok--responses-api--prompt-caching)。\n\n不存在禁用此功能的旋钮 —— 缓存始终开启,即使在单轮对话中也能节省费用,因为仅系统提示词就占输入 token 数的相当大比例。\n\n## 辅助模型\n\nHermes 使用\"辅助\"模型处理图像分析、网页摘要、浏览器截图分析、会话标题生成和上下文压缩等附带任务。默认情况下(`auxiliary.*.provider: \"auto\"`),Hermes 将每个辅助任务路由到您的**主聊天模型** —— 与您在 `hermes model` 中选择的相同 provider/模型。您无需配置任何内容即可开始,但请注意,在昂贵的推理模型(Opus、MiniMax M2.7 等)上,辅助任务会增加显著成本。如果您希望无论主模型如何都使用便宜且快速的附带任务,请显式设置 `auxiliary.<task>.provider` 和 `auxiliary.<task>.model`(例如,在 OpenRouter 上使用 Gemini Flash 进行视觉和网页提取)。\n\n:::note 为什么 \"auto\" 使用您的主模型\n早期版本将聚合器用户(OpenRouter、Nous Portal)分流到便宜的 provider 端默认值。这令人惊讶 —— 付费购买聚合器订阅的用户会看到不同的模型处理其辅助流量。`auto` 现在对所有人使用主模型,`config.yaml` 中的每任务覆盖仍然优先(见下方[完整辅助配置参考](#full-auxiliary-config-reference))。\n:::\n\n### 交互式配置辅助模型\n\n无需手动编辑 YAML,运行 `hermes model` 并从菜单中选择**\"配置辅助模型\"**。您将获得交互式的每任务选择器:\n\n```\n$ hermes model\n→ Configure auxiliary models\n\n[ ] vision currently: auto / main model\n[ ] web_extract currently: auto / main model\n[ ] title_generation currently: openrouter / google/gemini-3-flash-preview\n[ ] compression currently: auto / main model\n[ ] approval currently: auto / main model\n[ ] triage_specifier currently: auto / main model\n[ ] kanban_decomposer currently: auto / main model\n[ ] profile_describer currently: auto / main model\n```\n\n选择任务,选择 provider(OAuth 流程打开浏览器;API 密钥 provider 提示输入),选择模型。更改持久化到 `config.yaml` 中的 `auxiliary.<task>.*`。与主模型选择器相同的机制 —— 无需学习额外语法。\n\n### 视频教程\n\n<div style={{position: 'relative', width: '100%', aspectRatio: '16 / 9', marginBottom: '1.5rem'}}>\n <iframe\n src=\"https://www.youtube.com/embed/NoF-YajElIM\"\n title=\"Hermes Agent — Auxiliary Models Tutorial\"\n style={{position: 'absolute', top: 0, left: 0, width: '100%', height: '100%', border: 0}}\n allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\"\n allowFullScreen\n />\n</div>\n\n### 通用配置模式\n\nHermes 中的每个模型槽位 —— 辅助任务、压缩、回退 —— 使用相同的三个旋钮:\n\n| 键 | 作用 | 默认值 |\n|-----|-------------|---------|\n| `provider` | 用于认证和路由的 provider | `\"auto\"` |\n| `model` | 请求的模型 | provider 的默认值 |\n| `base_url` | 自定义 OpenAI 兼容端点(覆盖 provider) | 未设置 |\n\n当设置 `base_url` 时,Hermes 忽略 provider 并直接调用该端点(使用 `api_key` 或 `OPENAI_API_KEY` 进行认证)。当仅设置 `provider` 时,Hermes 使用该 provider 的内置认证和基础 URL。\n\n辅助任务的可用 providers:`auto`、`main`,以及[provider 注册表](/reference/environment-variables)中的任何 provider —— `openrouter`、`nous`、`openai-codex`、`copilot`、`copilot-acp`、`anthropic`、`gemini`、`google-gemini-cli`、`qwen-oauth`、`zai`、`kimi-coding`、`kimi-coding-cn`、`minimax`、`minimax-cn`、`minimax-oauth`、`deepseek`、`nvidia`、`xai`、`xai-oauth`、`ollama-cloud`、`alibaba`、`bedrock`、`huggingface`、`arcee`、`xiaomi`、`kilocode`、`opencode-zen`、`opencode-go`、`azure-foundry` —— 或您 `custom_providers` 列表中任何命名的自定义 provider(例如 `provider: \"beans\"`)。\n\n:::tip MiniMax OAuth\n`minimax-oauth` 通过浏览器 OAuth 登录(无需 API 密钥)。运行 `hermes model` 并选择 **MiniMax (OAuth)** 进行认证。辅助任务自动使用 `MiniMax-M2.7-highspeed`。参阅 [MiniMax OAuth 指南](../guides/minimax-oauth.md)。\n:::\n\n:::tip xAI Grok OAuth\n`xai-oauth` 通过浏览器 OAuth 为 SuperGrok 和 X Premium+ 订阅者登录(无需 API 密钥)。运行 `hermes model` 并选择 **xAI Grok OAuth (SuperGrok / Premium+)** 进行认证。相同的 OAuth token 可重用于每个直接到 xAI 的接口(聊天、辅助任务、TTS、图像生成、视频生成、转录)。参阅 [xAI Grok OAuth 指南](../guides/xai-grok-oauth.md),如果 Hermes 在远程主机上,请参阅 [SSH/远程主机上的 OAuth](../guides/oauth-over-ssh.md)。\n:::\n\n:::warning `\"main\"` 仅用于辅助任务\n`\"main\"` provider 选项表示\"使用我的主 agent 使用的任何 provider\" —— 它仅在 `auxiliary:`、`compression:` 和 `fallback_model:` 配置中有效。它**不是**顶级 `model.provider` 设置的有效值。如果您使用自定义 OpenAI 兼容端点,请在 `model:` 部分设置 `provider: custom`。所有主模型 provider 选项请参阅 [AI Providers](/integrations/providers)。\n:::\n\n### 完整辅助配置参考\n\n```yaml\nauxiliary:\n # 图像分析(vision_analyze 工具 + 浏览器截图)\n vision:\n provider: \"auto\" # \"auto\"、\"openrouter\"、\"nous\"、\"codex\"、\"main\" 等\n model: \"\" # 例如 \"openai/gpt-4o\"、\"google/gemini-2.5-flash\"\n base_url: \"\" # 自定义 OpenAI 兼容端点(覆盖 provider)\n api_key: \"\" # base_url 的 API 密钥(回退到 OPENAI_API_KEY)\n timeout: 120 # 秒 —— LLM API 调用超时;视觉负载需要宽裕的超时\n download_timeout: 30 # 秒 —— 图像 HTTP 下载;慢速连接请增加\n\n # 网页摘要 + 浏览器页面文本提取\n web_extract:\n provider: \"auto\"\n model: \"\" # 例如 \"google/gemini-2.5-flash\"\n base_url: \"\"\n api_key: \"\"\n timeout: 360 # 秒(6 分钟)—— 每次尝试的 LLM 摘要\n\n # 危险命令审批分类器\n approval:\n provider: \"auto\"\n model: \"\"\n base_url: \"\"\n api_key: \"\"\n timeout: 30 # 秒\n\n # 上下文压缩超时(与 compression.* 配置分开)\n compression:\n timeout: 120 # 秒 —— 压缩摘要长对话,需要更多时间\n # fallback_chain: # 可选 —— 发生速率限制/连接故障时尝试的 provider\n # - provider: nous\n # model: deepseek/deepseek-chat\n # - provider: openrouter\n # model: google/gemini-2.5-flash\n # base_url: \"\"\n # api_key: \"\"\n\n # 技能中心 —— 技能匹配和搜索\n skills_hub:\n provider: \"auto\"\n model: \"\"\n base_url: \"\"\n api_key: \"\"\n timeout: 30\n\n # MCP 工具调度\n mcp:\n provider: \"auto\"\n model: \"\"\n base_url: \"\"\n api_key: \"\"\n timeout: 30\n\n # Kanban 分类规格说明器 —— `hermes kanban specify <id>`(或\n # 仪表板上 Triage 列卡片的 ✨ Specify 按钮)使用此\n # 槽位将单行描述扩展为具体规格并将\n # 任务提升到 `todo`。便宜快速的模型在这里效果很好;规格扩展\n # 很短,不需要推理深度。\n triage_specifier:\n provider: \"auto\"\n model: \"\"\n base_url: \"\"\n api_key: \"\"\n timeout: 120\n```\n\n:::tip\n每个辅助任务都有可配置的 `timeout`(秒)。默认值:vision 120s、web_extract 360s、approval 30s、compression 120s。如果您为辅助任务使用慢速本地模型,请增加这些值。Vision 还有单独的 `download_timeout`(默认 30s)用于 HTTP 图像下载 —— 对于慢速连接或自托管图像服务器,请增加此值。\n:::\n\n:::info\n上下文压缩有自己的 `compression:` 块用于阈值,以及 `auxiliary.compression:` 块用于模型/provider 设置 —— 参阅上方的[上下文压缩](#context-compression)。主备用链使用顶层的 `fallback_providers:` 列表 —— 参阅[备用提供商](/integrations/providers#fallback-providers)。三者都遵循相同的 provider/model/base_url 模式。\n:::\n\n### 辅助任务的每任务回退链\n\n每个辅助任务都可以选择性地定义一个 `fallback_chain` —— 一个 provider/model 条目列表,当主要辅助 provider 因速率限制、网络连接问题或付费限制而失败时,Hermes 会尝试使用该列表:\n\n```yaml\nauxiliary:\n compression:\n provider: openrouter\n model: openai/gpt-4o-mini\n fallback_chain:\n - provider: nous\n model: deepseek/deepseek-chat\n - provider: openrouter\n model: google/gemini-2.5-flash\n```\n\n当主要辅助 provider(`openrouter` / `openai/gpt-4o-mini`)返回速率限制、连接超时或需要付费错误时,Hermes 将依次遍历 `fallback_chain`。它会跳过 provider 与已失败 provider 相同的条目,并尝试每个剩余条目,直到有一个成功或该链耗尽。如果所有回退都失败,Hermes 会回退到主 agent 模型作为最终的安全网。\n\n每个条目支持与任何辅助任务配置相同的三个旋钮:\n\n| 键 | 描述 |\n|-----|-------------|\n| `provider` | Provider 名称(`nous`、`openrouter`、`anthropic`、`gemini`、`main` 等) |\n| `model` | 该 provider 的模型名称 |\n| `base_url` | (可选)自定义 OpenAI 兼容端点 |\n\n`fallback_chain` 适用于任何辅助任务 —— `compression`、`vision`、`web_extract`、`approval`、`skills_hub`、`mcp` 等。\n\n### OpenRouter 路由和辅助任务的 Pareto Code\n\n当辅助任务解析到 OpenRouter(显式或通过 `provider: \"main\"` 而您的主 agent 在 OpenRouter 上)时,主 agent 的 `provider_routing` 和 `openrouter.min_coding_score` 设置**不会传播** —— 按设计,每个辅助任务是独立的。要为特定辅助任务设置 OpenRouter provider 偏好或使用 [Pareto Code 路由器](/integrations/providers#openrouter-pareto-code-router),请通过 `extra_body` 按任务设置:\n\n```yaml\nauxiliary:\n compression:\n provider: openrouter\n model: openrouter/pareto-code # 为此任务使用 Pareto Code 路由器\n extra_body:\n provider: # OpenRouter provider 路由偏好\n order: [anthropic, google] # 按此顺序尝试这些 providers\n sort: throughput # 或 \"price\" | \"latency\"\n # only: [anthropic] # 限制到特定 provider\n # ignore: [deepinfra] # 排除特定 providers\n plugins: # OpenRouter Pareto Code 路由器旋钮\n - id: pareto-router\n min_coding_score: 0.5 # 0.0–1.0;越高 = 更强的编码能力\n```\n\n形状与 OpenRouter 在聊天补全请求体中接受的内容一致。Hermes 原样转发整个 `extra_body`,因此 [openrouter.ai/docs](https://openrouter.ai/docs) 中记录的任何其他 OpenRouter 请求体字段都以相同方式工作。\n\n### 更改视觉模型\n\n使用 GPT-4o 而非 Gemini Flash 进行图像分析:\n\n```yaml\nauxiliary:\n vision:\n model: \"openai/gpt-4o\"\n```\n\n或通过环境变量(在 `~/.hermes/.env` 中):\n\n```bash\nAUXILIARY_VISION_MODEL=openai/gpt-4o\n```\n\n### Provider 选项\n\n这些选项适用于**辅助任务配置**(`auxiliary:`、`compression:`、`fallback_model:`),而非您的主 `model.provider` 设置。\n\n| Provider | 描述 | 要求 |\n|----------|-------------|-------------|\n| `\"auto\"` | 最佳可用(默认)。Vision 尝试 OpenRouter → Nous → Codex。 | — |\n| `\"openrouter\"` | 强制 OpenRouter —— 路由到任何模型(Gemini、GPT-4o、Claude 等) | `OPENROUTER_API_KEY` |\n| `\"nous\"` | 强制 Nous Portal | `hermes auth` |\n| `\"codex\"` | 强制 Codex OAuth(ChatGPT 账户)。支持视觉(gpt-5.3-codex)。 | `hermes model` → Codex |\n| `\"minimax-oauth\"` | 强制 MiniMax OAuth(浏览器登录,无需 API 密钥)。辅助任务使用 MiniMax-M2.7-highspeed。 | `hermes model` → MiniMax (OAuth) |\n| `\"xai-oauth\"` | 强制 xAI Grok OAuth(SuperGrok 或 X Premium+ 订阅者的浏览器登录,无需 API 密钥)。相同的 OAuth token 涵盖聊天、TTS、图像、视频和转录。 | `hermes model` → xAI Grok OAuth (SuperGrok / Premium+) |\n| `\"main\"` | 使用您的活跃自定义/主端点。可以来自 `OPENAI_BASE_URL` + `OPENAI_API_KEY` 或通过 `hermes model` / `config.yaml` 保存的自定义端点。适用于 OpenAI、本地模型或任何 OpenAI 兼容 API。**仅限辅助任务 —— 对 `model.provider` 无效。** | 自定义端点凭据 + 基础 URL |\n\n当您希望附带任务绕过默认路由器时,主 provider 目录中的直接 API 密钥 providers 也在这里工作。配置 `GMI_API_KEY` 后,`gmi` 有效:\n\n```yaml\nauxiliary:\n compression:\n provider: \"gmi\"\n model: \"anthropic/claude-opus-4.6\"\n```\n\n对于 GMI 辅助路由,使用 GMI 的 `/v1/models` 端点返回的确切模型 ID。\n\n### 常见设置\n\n**使用直接自定义端点**(比 `provider: \"main\"` 对本地/自托管 API 更清晰):\n```yaml\nauxiliary:\n vision:\n base_url: \"http://localhost:1234/v1\"\n api_key: \"local-key\"\n model: \"qwen2.5-vl\"\n```\n\n`base_url` 优先于 `provider`,因此这是将辅助任务路由到特定端点的最明确方式。对于直接端点覆盖,Hermes 使用配置的 `api_key` 或回退到 `OPENAI_API_KEY`;它不会为该自定义端点重用 `OPENROUTER_API_KEY`。\n\n**使用 OpenAI API 密钥进行视觉:**\n```yaml\n# 在 ~/.hermes/.env 中:\n# OPENAI_BASE_URL=https://api.openai.com/v1\n# OPENAI_API_KEY=sk-...\n\nauxiliary:\n vision:\n provider: \"main\"\n model: \"gpt-4o\" # 或 \"gpt-4o-mini\" 更便宜\n```\n\n**使用 OpenRouter 进行视觉**(路由到任何模型):\n```yaml\nauxiliary:\n vision:\n provider: \"openrouter\"\n model: \"openai/gpt-4o\" # 或 \"google/gemini-2.5-flash\" 等\n```\n\n**使用 Codex OAuth**(ChatGPT Pro/Plus 账户 —— 无需 API 密钥):\n```yaml\nauxiliary:\n vision:\n provider: \"codex\" # 使用您的 ChatGPT OAuth token\n # 模型默认为 gpt-5.3-codex(支持视觉)\n```\n\n**使用 MiniMax OAuth**(浏览器登录,无需 API 密钥):\n```yaml\nmodel:\n default: MiniMax-M2.7\n provider: minimax-oauth\n base_url: https://api.minimax.io/anthropic\n```\n运行 `hermes model` 并选择 **MiniMax (OAuth)** 自动登录并设置此项。对于中国区域,基础 URL 将是 `https://api.minimaxi.com/anthropic`。完整演练请参阅 [MiniMax OAuth 指南](../guides/minimax-oauth.md)。\n\n**使用本地/自托管模型:**\n```yaml\nauxiliary:\n vision:\n provider: \"main\" # 使用您的活跃自定义端点\n model: \"my-local-model\"\n```\n\n`provider: \"main\"` 使用 Hermes 用于普通聊天的任何 provider —— 无论是命名的自定义 provider(例如 `beans`)、内置 provider(如 `openrouter`)还是旧版 `OPENAI_BASE_URL` 端点。\n\n:::tip\n如果您使用 Codex OAuth 作为主模型 provider,视觉会自动工作 —— 无需额外配置。Codex 包含在视觉的自动检测链中。\n:::\n\n:::warning\n**视觉需要多模态模型。** 如果您设置 `provider: \"main\"`,请确保您的端点支持多模态/视觉 —— 否则图像分析将失败。\n:::\n\n### 环境变量(旧版)\n\n辅助模型也可以通过环境变量配置。但是,`config.yaml` 是首选方法 —— 它更易于管理,并支持所有选项,包括 `base_url` 和 `api_key`。\n\n| 设置 | 环境变量 |\n|---------|---------------------|\n| Vision provider | `AUXILIARY_VISION_PROVIDER` |\n| Vision 模型 | `AUXILIARY_VISION_MODEL` |\n| Vision 端点 | `AUXILIARY_VISION_BASE_URL` |\n| Vision API 密钥 | `AUXILIARY_VISION_API_KEY` |\n| Web 提取 provider | `AUXILIARY_WEB_EXTRACT_PROVIDER` |\n| Web 提取模型 | `AUXILIARY_WEB_EXTRACT_MODEL` |\n| Web 提取端点 | `AUXILIARY_WEB_EXTRACT_BASE_URL` |\n| Web 提取 API 密钥 | `AUXILIARY_WEB_EXTRACT_API_KEY` |\n\n压缩和回退模型设置仅限 config.yaml。\n\n:::tip\n运行 `hermes config` 查看您当前的辅助模型设置。覆盖仅在与默认值不同时显示。\n:::\n\n## 推理努力程度\n\n控制模型在响应前进行多少\"思考\":\n\n```yaml\nagent:\n reasoning_effort: \"\" # 空 = 中等(默认)。选项:none、minimal、low、medium、high、xhigh(最大)\n```\n\n未设置时(默认),推理努力程度默认为\"medium\" —— 适合大多数任务的平衡级别。设置值会覆盖它 —— 更高的推理努力程度在复杂任务上提供更好的结果,但代价是更多 token 和延迟。\n\n您也可以在运行时使用 `/reasoning` 命令更改推理努力程度:\n\n```\n/reasoning # 显示当前努力程度和显示状态\n/reasoning high # 将推理努力程度设为 high\n/reasoning none # 禁用推理\n/reasoning show # 在每次响应上方显示模型思考\n/reasoning hide # 隐藏模型思考\n```\n\n## 工具使用强制\n\n某些模型偶尔会将预期操作描述为文本而不是进行工具调用(\"我会运行测试...\"而不是实际调用终端)。工具使用强制会注入系统提示词指导,引导模型实际调用工具。\n\n```yaml\nagent:\n tool_use_enforcement: \"auto\" # \"auto\" | true | false | [\"model-substring\", ...]\n```\n\n| 值 | 行为 |\n|-------|----------|\n| `\"auto\"`(默认) | 对匹配以下模型启用:`gpt`、`codex`、`gemini`、`gemma`、`grok`。对所有其他模型禁用(Claude、DeepSeek、Qwen 等)。 |\n| `true` | 始终启用,无论模型如何。如果您注意到当前模型描述操作而不是执行操作,请使用此选项。 |\n| `false` | 始终禁用,无论模型如何。 |\n| `[\"gpt\", \"codex\", \"qwen\", \"llama\"]` | 仅当模型名称包含列出的子字符串之一时启用(不区分大小写)。 |\n\n### 注入的内容\n\n启用后,系统提示词中可能会添加三层指导:\n\n1. **通用工具使用强制**(所有匹配模型)—— 指示模型立即进行工具调用而不是描述意图,持续工作直到任务完成,永远不要以未来操作的承诺结束轮次。\n\n2. **OpenAI 执行纪律**(仅限 GPT 和 Codex 模型)—— 针对 GPT 特定失败模式的额外指导:在部分结果上放弃工作、跳过先决条件查找、幻觉而不是使用工具、在未验证的情况下宣布\"完成\"。\n\n3. **Google 操作指导**(仅限 Gemini 和 Gemma 模型)—— 简洁性、绝对路径、并行工具调用和编辑前验证模式。\n\n这些对用户透明,仅影响系统提示词。已经可靠使用工具的模型(如 Claude)不需要此指导,这就是为什么 `\"auto\"` 排除它们。\n\n### 何时开启\n\n如果您使用的模型不在默认自动列表中,并注意到它经常描述它*会*做什么而不是实际去做,请设置 `tool_use_enforcement: true` 或将模型子字符串添加到列表中:\n\n```yaml\nagent:\n tool_use_enforcement: [\"gpt\", \"codex\", \"gemini\", \"grok\", \"my-custom-model\"]\n```\n\n## TTS 配置\n\n```yaml\ntts:\n provider: \"edge\" # \"edge\" | \"elevenlabs\" | \"openai\" | \"minimax\" | \"mistral\" | \"gemini\" | \"xai\" | \"neutts\"\n speed: 1.0 # 全局速度倍数(所有 provider 的回退)\n edge:\n voice: \"en-US-AriaNeural\" # 322 种声音,74 种语言\n speed: 1.0 # 速度倍数(转换为速率百分比,例如 1.5 → +50%)\n elevenlabs:\n voice_id: \"pNInz6obpgDQGcFmaJgB\"\n model_id: \"eleven_multilingual_v2\"\n openai:\n model: \"gpt-4o-mini-tts\"\n voice: \"alloy\" # alloy、echo、fable、onyx、nova、shimmer\n speed: 1.0 # 速度倍数(API 限制为 0.25–4.0)\n base_url: \"https://api.openai.com/v1\" # 覆盖 OpenAI 兼容 TTS 端点\n minimax:\n speed: 1.0 # 语音速度倍数\n # base_url: \"\" # 可选:覆盖 OpenAI 兼容 TTS 端点\n mistral:\n model: \"voxtral-mini-tts-2603\"\n voice_id: \"c69964a6-ab8b-4f8a-9465-ec0925096ec8\" # Paul - Neutral(默认)\n gemini:\n model: \"gemini-2.5-flash-preview-tts\" # 或 gemini-2.5-pro-preview-tts\n voice: \"Kore\" # 30 种预置声音:Zephyr、Puck、Kore、Enceladus 等\n xai:\n voice_id: \"eve\" # xAI TTS 声音\n language: \"en\" # ISO 639-1\n sample_rate: 24000\n bit_rate: 128000 # MP3 比特率\n # base_url: \"https://api.x.ai/v1\"\n neutts:\n ref_audio: ''\n ref_text: ''\n model: neuphonic/neutts-air-q4-gguf\n device: cpu\n```\n\n这控制 `text_to_speech` 工具和语音模式中的口语回复(CLI 中的 `/voice tts` 或消息 gateway)。\n\n**速度回退层次:** provider 特定速度(例如 `tts.edge.speed`)→ 全局 `tts.speed` → `1.0` 默认值。设置全局 `tts.speed` 以在所有 provider 上应用统一速度,或按 provider 覆盖以进行精细控制。\n\n## 显示设置\n\n```yaml\ndisplay:\n tool_progress: all # off | new | all | verbose\n tool_progress_command: false # 在消息 gateway 中启用 /verbose 斜杠命令\n platforms: {} # 每平台显示覆盖(见下文)\n tool_progress_overrides: {} # 已弃用 —— 改用 display.platforms\n interim_assistant_messages: true # Gateway:将自然的轮次中 assistant 更新作为单独消息发送\n skin: default # 内置或自定义 CLI 皮肤(参阅 user-guide/features/skins)\n personality: \"kawaii\" # 旧版外观字段,仍在某些摘要中显示\n compact: false # 紧凑输出模式(减少空白)\n resume_display: full # full(恢复时显示之前的消息)| minimal(仅单行)\n bell_on_complete: false # 当 agent 完成时播放终端铃声(适合长任务)\n show_reasoning: false # 在每次响应上方显示模型推理/思考(用 /reasoning show|hide 切换)\n streaming: false # 将 token 实时流式传输到终端\n show_cost: false # 在 CLI 状态栏中显示估计 $ 成本\n timestamps: false # 为 true 时,在 CLI/TUI 记录中为用户和 assistant 标签添加 [HH:MM] 时间戳前缀\n tool_preview_length: 0 # 工具调用预览的最大字符数(0 = 无限制,显示完整路径/命令)\n runtime_footer: # Gateway:在最终回复中附加运行时上下文页脚\n enabled: false\n fields: [\"model\", \"context_pct\", \"cwd\"]\n file_mutation_verifier: true # 当本轮 write_file/patch 调用失败时附加建议性页脚\n language: en # 静态消息的 UI 语言(审批提示、部分 gateway 回复)。en | zh | zh-hant | ja | de | es | fr | tr | uk | af | ko | it | ga | pt | ru | hu\n```\n\n### 文件变更验证器\n\n当 `display.file_mutation_verifier` 为 `true`(默认)时,每当本轮中 `write_file` 或 `patch` 调用失败且从未被对同一路径的成功写入取代时,Hermes 会在 assistant 的最终响应中附加一行建议。这捕获了\"批量并行补丁,一半静默失败,模型总结成功\"这类过度声明,而无需您在每次编辑后手动运行 `git status`。\n\n示例页脚:\n\n```\n⚠️ File-mutation verifier: 3 file(s) were NOT modified this turn despite any wording above that may suggest otherwise. Run `git status` or `read_file` to confirm.\n • concepts/automatic-organization.md — [patch] Could not find match for old_string\n • concepts/lora.md — [patch] Could not find match for old_string\n • concepts/rag-pipeline.md — [patch] Could not find match for old_string\n```\n\n设置 `file_mutation_verifier: false`(或 `HERMES_FILE_MUTATION_VERIFIER=0`)以禁止页脚。验证器仅在轮次结束时有真实失败未解决时触发 —— 在同一轮次内重试失败补丁并成功的模型不会为该文件触发它。\n\n### 静态消息的 UI 语言\n\n`display.language` 设置翻译一小组静态面向用户的消息 —— CLI 审批提示、少数 gateway 斜杠命令回复(例如重启排空通知、\"审批已过期\"、\"目标已清除\")。它**不**翻译 agent 响应、日志行、工具输出、错误回溯或斜杠命令描述 —— 这些保持英文。如果您希望 agent 本身用另一种语言回复,只需在您的提示词或系统消息中告诉它。\n\n支持的值:`en`(默认)、`zh`(简体中文)、`zh-hant`(繁体中文)、`ja`(日语)、`de`(德语)、`es`(西班牙语)、`fr`(法语)、`tr`(土耳其语)、`uk`(乌克兰语)、`af`(南非荷兰语)、`ko`(韩语)、`it`(意大利语)、`ga`(爱尔兰语)、`pt`(葡萄牙语)、`ru`(俄语)、`hu`(匈牙利语)。未知值回退到英文。\n\n您也可以使用 `HERMES_LANGUAGE` 环境变量按会话设置,它会覆盖配置值。\n\n```yaml\ndisplay:\n language: zh # CLI 审批提示以中文显示\n```\n\n| 模式 | 您看到的内容 |\n|------|-------------|\n| `off` | 静默 —— 仅最终响应 |\n| `new` | 仅在工具更改时显示工具指示器 |\n| `all` | 每次工具调用附带简短预览(默认) |\n| `verbose` | 完整参数、结果和调试日志 |\n\n在 CLI 中,使用 `/verbose` 循环切换这些模式。要在消息平台(Telegram、Discord、Slack 等)中使用 `/verbose`,请在上方的 `display` 部分设置 `tool_progress_command: true`。该命令将循环切换模式并保存到配置。\n\n### 运行时元数据页脚(仅限 gateway)\n\n当 `display.runtime_footer.enabled: true` 时,Hermes 在每个 gateway 轮次的**最终**消息中附加一个小型运行时上下文页脚。目前页脚可显示模型、上下文窗口百分比和当前工作目录。默认关闭;如果您的团队希望每个回复都包含这些来源信息,请按 gateway 选择加入。\n\n```yaml\ndisplay:\n runtime_footer:\n enabled: true\n fields: [\"model\", \"context_pct\", \"cwd\"] # 支持字段:model、context_pct、cwd\n```\n\n`/footer` 斜杠命令在任何会话中运行时切换此功能。\n\n附加到 Telegram/Discord/Slack 回复的示例页脚:\n\n```\n— claude-opus-4.7 · 12 tool calls · 2m 14s · $0.042\n```\n\n只有轮次的**最终**消息获得页脚;中间更新保持干净。\n\n### 每平台进度覆盖\n\n不同平台有不同的详细程度需求。例如,Signal 无法编辑消息,因此每次进度更新都会成为单独的消息 —— 很嘈杂。使用 `display.platforms` 设置每平台模式:\n\n```yaml\ndisplay:\n tool_progress: all # 全局默认\n platforms:\n signal:\n tool_progress: 'off' # 在 Signal 上静默进度\n telegram:\n tool_progress: verbose # 在 Telegram 上详细进度\n slack:\n tool_progress: 'off' # 在共享 Slack 工作区中保持安静\n```\n\n没有覆盖的平台回退到全局 `tool_progress` 值。有效平台键:`telegram`、`discord`、`slack`、`signal`、`whatsapp`、`matrix`、`mattermost`、`email`、`sms`、`homeassistant`、`dingtalk`、`feishu`、`wecom`、`weixin`、`bluebubbles`、`qqbot`。旧版 `display.tool_progress_overrides` 键仍可加载以向后兼容,但已弃用,并在首次加载时迁移到 `display.platforms`。\n\n`interim_assistant_messages` 仅限 gateway。启用后,Hermes 将已完成的轮次中 assistant 更新作为单独的聊天消息发送。这与 `tool_progress` 无关,不需要 gateway 流式传输。\n\n## 隐私\n\n```yaml\nprivacy:\n redact_pii: false # 从 LLM 上下文中删除 PII(仅限 gateway)\n```\n\n当 `redact_pii` 为 `true` 时,gateway 在将系统提示词发送到受支持平台上的 LLM 之前,会从中删除个人身份信息:\n\n| 字段 | 处理方式 |\n|-------|-----------|\n| 电话号码(WhatsApp/Signal 上的用户 ID) | 哈希为 `user_<12-char-sha256>` |\n| 用户 ID | 哈希为 `user_<12-char-sha256>` |\n| 聊天 ID | 数字部分哈希,保留平台前缀(`telegram:<hash>`) |\n| 主频道 ID | 数字部分哈希 |\n| 用户名/昵称 | **不受影响**(用户选择的,公开可见) |\n\n**平台支持:** 删除适用于 WhatsApp、Signal 和 Telegram。Discord 和 Slack 被排除,因为它们的提及系统(`<@user_id>`)需要 LLM 上下文中的真实 ID。\n\n哈希是确定性的 —— 同一用户始终映射到同一哈希,因此模型仍然可以在群聊中区分用户。路由和传递在内部使用原始值。\n\n## 语音转文字(STT)\n\n```yaml\nstt:\n provider: \"local\" # \"local\" | \"groq\" | \"openai\" | \"mistral\"\n local:\n model: \"base\" # tiny、base、small、medium、large-v3\n openai:\n model: \"whisper-1\" # whisper-1 | gpt-4o-mini-transcribe | gpt-4o-transcribe\n # model: \"whisper-1\" # 旧版回退键仍受支持\n```\n\nProvider 行为:\n\n- `local` 使用在您机器上运行的 `faster-whisper`。使用 `pip install faster-whisper` 单独安装。\n- `groq` 使用 Groq 的 Whisper 兼容端点,读取 `GROQ_API_KEY`。\n- `openai` 使用 OpenAI 语音 API,读取 `VOICE_TOOLS_OPENAI_KEY`。\n\n如果请求的 provider 不可用,Hermes 按此顺序自动回退:`local` → `groq` → `openai`。\n\nGroq 和 OpenAI 模型覆盖由环境变量驱动:\n\n```bash\nSTT_GROQ_MODEL=whisper-large-v3-turbo\nSTT_OPENAI_MODEL=whisper-1\nGROQ_BASE_URL=https://api.groq.com/openai/v1\nSTT_OPENAI_BASE_URL=https://api.openai.com/v1\n```\n\n## 语音模式(CLI)\n\n```yaml\nvoice:\n record_key: \"ctrl+b\" # CLI 内的按键通话键\n max_recording_seconds: 120 # 长录音的硬停止\n auto_tts: false # /voice on 时自动启用口语回复\n beep_enabled: true # 在 CLI 语音模式中播放录音开始/停止提示音\n silence_threshold: 200 # 语音检测的 RMS 阈值\n silence_duration: 3.0 # 自动停止前的静默秒数\n```\n\n在 CLI 中使用 `/voice on` 启用麦克风模式,使用 `record_key` 开始/停止录音,使用 `/voice tts` 切换口语回复。端到端设置和平台特定行为请参阅[语音模式](/user-guide/features/voice-mode)。\n\n## 流式传输\n\n将 token 实时流式传输到终端或消息平台,而不是等待完整响应。\n\n### CLI 流式传输\n\n```yaml\ndisplay:\n streaming: true # 实时将 token 流式传输到终端\n show_reasoning: true # 同时流式传输推理/思考 token(可选)\n```\n\n启用后,响应在流式传输框内逐 token 出现。工具调用仍然静默捕获。如果 provider 不支持流式传输,它会自动回退到正常显示。\n\n### Gateway 流式传输(Telegram、Discord、Slack)\n\n```yaml\nstreaming:\n enabled: true # 启用渐进式消息编辑\n transport: edit # \"edit\"(渐进式消息编辑)或 \"off\"\n edit_interval: 0.3 # 消息编辑之间的秒数\n buffer_threshold: 40 # 强制编辑刷新前的字符数\n cursor: \" ▉\" # 流式传输期间显示的光标\n fresh_final_after_seconds: 0 # 预览超过此时间时选择发送新的最终消息(Telegram)\n```\n\n启用后,bot 在第一个 token 时发送消息,然后随着更多 token 到来渐进式编辑它。不支持消息编辑的平台(Signal、Email、Home Assistant)在第一次尝试时自动检测 —— 该会话的流式传输被优雅地禁用,不会产生大量消息。\n\n对于不带渐进式 token 编辑的独立自然轮次中 assistant 更新,请设置 `display.interim_assistant_messages: true`。\n\n**溢出处理:** 如果流式传输的文本超过平台的消息长度限制(约 4096 字符),当前消息被最终化,新消息自动开始。\n\n**新的最终消息(Telegram):** Telegram 的 `editMessageText` 保留原始消息时间戳,因此长时间运行的流式回复即使在完成后也会保留第一个 token 的时间戳。设置 `fresh_final_after_seconds > 0` 可选择将旧预览作为全新的最终消息传递,并尽力删除旧预览。默认值为 `0`,始终就地最终化流式回复,避免某些客户端短暂显示重复消息再删除其中一条。\n\n:::note\n主开关 `streaming.enabled` 默认为 `false`——在你启用之前不会有任何流式传输。启用后,是否流式传输按**平台**决定:Telegram 默认带有 `display.platforms.telegram.streaming: true`(流式传输),Discord 为 `display.platforms.discord.streaming: false`(不流式传输)。因此启用流式传输后,Telegram 开箱即用地流式传输,Discord 在你修改其开关之前仍使用整条消息回复。你可以在仪表盘的 **Channels** 开关中或直接在 `~/.hermes/config.yaml` 中调整这些按平台的开关。\n:::\n\n## 群聊会话隔离\n\n控制共享聊天是每个房间保持一个对话还是每个参与者一个对话:\n\n```yaml\ngroup_sessions_per_user: true # true = 群组/频道中每用户隔离,false = 每个聊天一个共享会话\n```\n\n- `true` 是默认和推荐设置。在 Discord 频道、Telegram 群组、Slack 频道和类似共享上下文中,当平台提供用户 ID 时,每个发送者获得自己的会话。\n- `false` 恢复到旧的共享房间行为。如果您明确希望 Hermes 将频道视为一个协作对话,这可能有用,但这也意味着用户共享上下文、token 成本和中断状态。\n- 私信不受影响。Hermes 仍然像往常一样通过聊天/DM ID 键入 DM。\n- 线程与其父频道保持隔离;使用 `true` 时,每个参与者在线程内也获得自己的会话。\n\n有关行为详情和示例,请参阅[会话](/user-guide/sessions)和 [Discord 指南](/user-guide/messaging/discord)。\n\n## 未授权 DM 行为\n\n控制当未知用户发送私信时 Hermes 的行为:\n\n```yaml\nunauthorized_dm_behavior: pair\n\nwhatsapp:\n unauthorized_dm_behavior: ignore\n```\n\n- `pair` 是默认值。Hermes 拒绝访问,但在 DM 中回复一次性配对码。\n- `ignore` 静默丢弃未授权的 DM。\n- 平台部分覆盖全局默认值,因此您可以在广泛范围内保持配对启用,同时使一个平台更安静。\n\n## 快速命令\n\n定义自定义命令,这些命令要么在不调用 LLM 的情况下运行 shell 命令,要么将一个斜杠命令别名为另一个。Exec 快速命令是零 token 的,对于从消息平台(Telegram、Discord 等)进行快速服务器检查或实用脚本很有用。\n\n```yaml\nquick_commands:\n status:\n type: exec\n command: systemctl status hermes-agent\n disk:\n type: exec\n command: df -h /\n update:\n type: exec\n command: cd ~/.hermes/hermes-agent && git pull && pip install -e .\n gpu:\n type: exec\n command: nvidia-smi --query-gpu=name,utilization.gpu,memory.used,memory.total --format=csv,noheader\n restart:\n type: alias\n target: /gateway restart\n```\n\n用法:在 CLI 或任何消息平台中输入 `/status`、`/disk`、`/update`、`/gpu` 或 `/restart`。`exec` 命令在宿主本地运行并直接返回输出 —— 无 LLM 调用,不消耗 token。`alias` 命令重写为配置的斜杠命令目标。\n\n- **30 秒超时** —— 长时间运行的命令被终止并显示错误消息\n- **优先级** —— 快速命令在技能命令之前检查,因此您可以覆盖技能名称\n- **自动补全** —— 快速命令在调度时解析,不显示在内置斜杠命令自动补全表中\n- **类型** —— 支持的类型为 `exec` 和 `alias`;其他类型显示错误\n- **到处可用** —— CLI、Telegram、Discord、Slack、WhatsApp、Signal、Email、Home Assistant\n\n仅字符串的 prompt 快捷方式不是有效的快速命令。对于可重用的 prompt 工作流,请创建技能或别名到现有斜杠命令。\n\n## 人类延迟\n\n在消息平台中模拟类人响应节奏:\n\n```yaml\nhuman_delay:\n mode: \"off\" # off | natural | custom\n min_ms: 800 # 最小延迟(自定义模式)\n max_ms: 2500 # 最大延迟(自定义模式)\n```\n\n## 代码执行\n\n配置 `execute_code` 工具:\n\n```yaml\ncode_execution:\n mode: project # project(默认)| strict\n timeout: 300 # 最大执行时间(秒)\n max_tool_calls: 50 # 代码执行中的最大工具调用次数\n```\n\n**`mode`** 控制脚本的工作目录和 Python 解释器:\n\n- **`project`**(默认)—— 脚本在会话的工作目录中以活跃 virtualenv/conda 环境的 python 运行。项目依赖(`pandas`、`torch`、项目包)和相对路径(`.env`、`./data.csv`)自然解析,与 `terminal()` 看到的一致。\n- **`strict`** —— 脚本在临时暂存目录中以 `sys.executable`(Hermes 自己的 python)运行。最大可重现性,但项目依赖和相对路径不会解析。\n\n环境清理(删除 `*_API_KEY`、`*_TOKEN`、`*_SECRET`、`*_PASSWORD`、`*_CREDENTIAL`、`*_PASSWD`、`*_AUTH`)和工具白名单在两种模式下完全相同 —— 切换模式不会改变安全态势。\n\n## Web 搜索后端\n\n`web_search` 和 `web_extract` 工具支持五种后端 provider。在 `config.yaml` 中或通过 `hermes tools` 配置后端:\n\n```yaml\nweb:\n backend: firecrawl # firecrawl | searxng | parallel | tavily | exa\n\n # 或使用每功能键混合 provider(例如免费搜索 + 付费提取):\n search_backend: \"searxng\"\n extract_backend: \"firecrawl\"\n```\n\n| 后端 | 环境变量 | 搜索 | 提取 |\n|---------|---------|--------|---------|\n| **Firecrawl**(默认) | `FIRECRAWL_API_KEY` | ✔ | ✔ |\n| **SearXNG** | `SEARXNG_URL` | ✔ | — |\n| **Parallel** | `PARALLEL_API_KEY` | ✔ | ✔ |\n| **Tavily** | `TAVILY_API_KEY` | ✔ | ✔ |\n| **Exa** | `EXA_API_KEY` | ✔ | ✔ |\n\n**后端选择:** 如果未设置 `web.backend`,后端从可用的 API 密钥自动检测。如果仅设置了 `SEARXNG_URL`,使用 SearXNG。如果仅设置了 `EXA_API_KEY`,使用 Exa。如果仅设置了 `TAVILY_API_KEY`,使用 Tavily。如果仅设置了 `PARALLEL_API_KEY`,使用 Parallel。否则 Firecrawl 是默认值。\n\n**SearXNG** 是一个免费、自托管、尊重隐私的元搜索引擎,查询 70+ 个搜索引擎。无需 API 密钥 —— 只需将 `SEARXNG_URL` 设置为您的实例(例如 `http://localhost:8080`)。SearXNG 仅限搜索;`web_extract` 需要单独的提取 provider(设置 `web.extract_backend`)。Docker 设置说明请参阅 [Web 搜索设置指南](/user-guide/features/web-search)。\n\n**自托管 Firecrawl:** 设置 `FIRECRAWL_API_URL` 指向您自己的实例。设置自定义 URL 后,API 密钥变为可选(在服务器上设置 `USE_DB_AUTHENTICATION=***` 以禁用认证)。\n\n**Parallel 搜索模式:** 设置 `PARALLEL_SEARCH_MODE` 控制搜索行为 —— `fast`、`one-shot` 或 `agentic`(默认:`agentic`)。\n\n**Exa:** 在 `~/.hermes/.env` 中设置 `EXA_API_KEY`。支持 `category` 过滤(`company`、`research paper`、`news`、`people`、`personal site`、`pdf`)和域名/日期过滤器。\n\n## 浏览器\n\n配置浏览器自动化行为:\n\n```yaml\nbrowser:\n inactivity_timeout: 120 # 自动关闭空闲会话前的秒数\n command_timeout: 30 # 浏览器命令超时(截图、导航等)(秒)\n record_sessions: false # 自动将浏览器会话录制为 WebM 视频到 ~/.hermes/browser_recordings/\n # 可选 CDP 覆盖 —— 设置后,Hermes 直接附加到您自己的\n # Chromium 系浏览器(通过 /browser connect),而不是启动无头浏览器。\n cdp_url: \"\"\n # 对话框监督器 —— 控制当 CDP 后端附加时(Browserbase、本地 Chromium 系\n # 浏览器通过 /browser connect)如何处理原生 JS 对话框(alert/confirm/prompt)。\n # 在 Camofox 和默认本地 agent 浏览器模式下忽略。\n dialog_policy: must_respond # must_respond | auto_dismiss | auto_accept\n dialog_timeout_s: 300 # must_respond 下的安全自动关闭(秒)\n camofox:\n managed_persistence: false # 为 true 时,Camofox 会话跨重启持久化 cookie/登录\n user_id: \"\" # 可选的外部管理 Camofox userId\n session_key: \"\" # Hermes 创建标签页时发送的可选会话密钥\n adopt_existing_tab: false # 在创建新标签页之前重用此身份的现有标签页\n```\n\n**对话框策略:**\n\n- `must_respond`(默认)—— 捕获对话框,在 `browser_snapshot.pending_dialogs` 中显示,等待 agent 调用 `browser_dialog(action=...)`。在 `dialog_timeout_s` 秒内无响应后,对话框被自动关闭以防止页面的 JS 线程永久停滞。\n- `auto_dismiss` —— 捕获,立即关闭。Agent 仍然在事后的 `browser_snapshot.recent_dialogs` 中看到对话框记录,`closed_by=\"auto_policy\"`。\n- `auto_accept` —— 捕获,立即接受。适用于有激进 `beforeunload` 提示的页面。\n\n完整对话框工作流请参阅[浏览器功能页面](./features/browser.md#browser_dialog)。\n\n浏览器工具集支持多个 provider。有关 Browserbase、Browser Use 和本地 Chromium 系 CDP 设置的详细信息,请参阅[浏览器功能页面](/user-guide/features/browser)。\n\n## 时区\n\n使用 IANA 时区字符串覆盖服务器本地时区。影响日志中的时间戳、cron 调度和系统提示词时间注入。\n\n```yaml\ntimezone: \"America/New_York\" # IANA 时区(默认:\"\" = 服务器本地时间)\n```\n\n支持的值:任何 IANA 时区标识符(例如 `America/New_York`、`Europe/London`、`Asia/Kolkata`、`UTC`)。留空或省略以使用服务器本地时间。\n\n## Discord\n\n为消息 gateway 配置 Discord 特定行为:\n\n```yaml\ndiscord:\n require_mention: true # 在服务器频道中需要 @提及才能响应\n free_response_channels: \"\" # 逗号分隔的频道 ID,bot 在这些频道无需 @提及即可响应\n auto_thread: true # 在频道中 @提及时自动创建线程\n```\n\n- `require_mention` —— 为 `true`(默认)时,bot 仅在服务器频道中被 `@BotName` 提及时响应。DM 始终无需提及即可工作。\n- `free_response_channels` —— 逗号分隔的频道 ID 列表,bot 在这些频道对每条消息响应,无需提及。\n- `auto_thread` —— 为 `true`(默认)时,频道中的提及会自动为对话创建线程,保持频道整洁(类似 Slack 线程)。\n\n## 安全\n\n预执行安全扫描和机密脱敏:\n\n```yaml\nsecurity:\n redact_secrets: false # 在工具输出和日志中脱敏 API 密钥模式(默认关闭)\n tirith_enabled: true # 为终端命令启用 Tirith 安全扫描\n tirith_path: \"tirith\" # tirith 二进制文件路径(默认:$PATH 中的 \"tirith\")\n tirith_timeout: 5 # 等待 tirith 扫描的秒数\n tirith_fail_open: true # 如果 tirith 不可用,允许命令执行\n website_blocklist: # 参见下方网站黑名单部分\n enabled: false\n domains: []\n shared_files: []\n```\n\n- `redact_secrets` —— 为 `true` 时,自动检测并脱敏工具输出中看起来像 API 密钥、token 和密码的模式,然后再进入对话上下文和日志。**默认关闭** —— 如果您经常在工具输出中处理真实凭据并希望有安全网,请启用。显式设置为 `true` 以开启。\n- `tirith_enabled` —— 为 `true` 时,终端命令在执行前由 [Tirith](https://github.com/sheeki03/tirith) 扫描以检测潜在危险操作。\n- `tirith_path` —— tirith 二进制文件的路径。如果 tirith 安装在非标准位置,请设置此项。\n- `tirith_timeout` —— 等待 tirith 扫描的最大秒数。如果扫描超时,命令继续执行。\n- `tirith_fail_open` —— 为 `true`(默认)时,如果 tirith 不可用或失败,允许命令执行。设置为 `false` 以在 tirith 无法验证时阻止命令。\n\n## 网站黑名单\n\n阻止 agent 的 web 和浏览器工具访问特定域名:\n\n```yaml\nsecurity:\n website_blocklist:\n enabled: false # 启用 URL 阻止(默认:false)\n domains: # 被阻止的域名模式列表\n - \"*.internal.company.com\"\n - \"admin.example.com\"\n - \"*.local\"\n shared_files: # 从外部文件加载额外规则\n - \"/etc/hermes/blocked-sites.txt\"\n```\n\n启用后,任何匹配被阻止域名模式的 URL 在 web 或浏览器工具执行之前都会被拒绝。这适用于 `web_search`、`web_extract`、`browser_navigate` 以及任何访问 URL 的工具。\n\n域名规则支持:\n- 精确域名:`admin.example.com`\n- 通配符子域名:`*.internal.company.com`(阻止所有子域名)\n- TLD 通配符:`*.local`\n\n共享文件每行包含一条域名规则(空行和 `#` 注释被忽略)。缺失或不可读的文件记录警告,但不禁用其他 web 工具。\n\n策略缓存 30 秒,因此配置更改无需重启即可快速生效。\n\n## 智能审批\n\n控制 Hermes 如何处理潜在危险命令:\n\n```yaml\napprovals:\n mode: manual # manual | smart | off\n```\n\n| 模式 | 行为 |\n|------|----------|\n| `manual`(默认) | 在执行任何被标记的命令之前提示用户。在 CLI 中显示交互式审批对话框。在消息中排队待处理的审批请求。 |\n| `smart` | 使用辅助 LLM 评估被标记的命令是否真正危险。低风险命令以会话级持久性自动批准。真正有风险的命令升级给用户。 |\n| `off` | 跳过所有审批检查。等同于 `HERMES_YOLO_MODE=true`。**谨慎使用。** |\n\n智能模式对于减少审批疲劳特别有用 —— 它让 agent 在安全操作上更自主地工作,同时仍然捕获真正破坏性的命令。\n\n:::warning\n设置 `approvals.mode: off` 会禁用终端命令的所有安全检查。仅在受信任的沙箱环境中使用。\n:::\n\n## 检查点\n\n破坏性文件操作之前的自动文件系统快照。详情请参阅[检查点与回滚](/user-guide/checkpoints-and-rollback)。\n\n```yaml\ncheckpoints:\n enabled: false # 启用自动检查点(也可:hermes chat --checkpoints)。默认:false(选择加入)。\n max_snapshots: 20 # 每个目录保留的最大检查点数(默认:20)\n```\n\n\n## 委托\n\n为委托工具配置子 agent 行为:\n\n```yaml\ndelegation:\n # model: \"google/gemini-3-flash-preview\" # 覆盖模型(空 = 继承父级)\n # provider: \"openrouter\" # 覆盖 provider(空 = 继承父级)\n # base_url: \"http://localhost:1234/v1\" # 直接 OpenAI 兼容端点(优先于 provider)\n # api_key: \"local-key\" # base_url 的 API 密钥(回退到 OPENAI_API_KEY)\n # api_mode: \"\" # base_url 的线路协议:\"chat_completions\"、\"codex_responses\" 或 \"anthropic_messages\"。空 = 从 URL 自动检测(例如 /anthropic 后缀 → anthropic_messages)。对启发式无法检测的非标准端点显式设置。\n max_concurrent_children: 3 # 每批并行子 agent 数(下限 1,无上限)。也可通过 DELEGATION_MAX_CONCURRENT_CHILDREN 环境变量设置。\n max_spawn_depth: 1 # 委托树深度上限(1-3,截断)。1 = 扁平(默认):父级生成无法委托的叶子。2 = 编排器子级可以生成叶子孙级。3 = 三级。\n orchestrator_enabled: true # 全局终止开关。为 false 时,role=\"orchestrator\" 被忽略,每个子级无论 max_spawn_depth 如何都被强制为叶子。\n```\n\n**子 agent provider:model 覆盖:** 默认情况下,子 agent 继承父 agent 的 provider 和模型。设置 `delegation.provider` 和 `delegation.model` 将子 agent 路由到不同的 provider:model 对 —— 例如,在您的主 agent 运行昂贵推理模型时,为范围较窄的子任务使用便宜/快速的模型。\n\n**直接端点覆盖:** 如果您想要明显的自定义端点路径,请设置 `delegation.base_url`、`delegation.api_key` 和 `delegation.model`。这将子 agent 直接发送到该 OpenAI 兼容端点,并优先于 `delegation.provider`。如果省略 `delegation.api_key`,Hermes 仅回退到 `OPENAI_API_KEY`。\n\n**线路协议(`api_mode`):** Hermes 从 `delegation.base_url` 自动检测线路协议(例如以 `/anthropic` 结尾的路径 → `anthropic_messages`;Codex/原生 Anthropic/Kimi-coding 主机名保留其现有检测)。对于启发式无法分类的端点 —— 例如 Azure AI Foundry、MiniMax、Zhipu GLM 或前置 Anthropic 形状后端的 LiteLLM 代理 —— 请将 `delegation.api_mode` 显式设置为 `chat_completions`、`codex_responses` 或 `anthropic_messages` 之一。留空(默认)以保持自动检测。\n\n委托 provider 使用与 CLI/gateway 启动相同的凭据解析。所有配置的 provider 均受支持:`openrouter`、`nous`、`copilot`、`zai`、`kimi-coding`、`minimax`、`minimax-cn`。设置 provider 时,系统自动解析正确的基础 URL、API 密钥和 API 模式 —— 无需手动凭据连接。\n\n**优先级:** 配置中的 `delegation.base_url` → 配置中的 `delegation.provider` → 父 provider(继承)。配置中的 `delegation.model` → 父模型(继承)。仅设置 `model` 而不设置 `provider` 仅更改模型名称,同时保留父级凭据(适用于在同一 provider(如 OpenRouter)内切换模型)。\n\n**宽度和深度:** `max_concurrent_children` 限制每批并行运行的子 agent 数量(默认 `3`,下限 1,无上限)。也可通过 `DELEGATION_MAX_CONCURRENT_CHILDREN` 环境变量设置。当模型提交的 `tasks` 数组超过上限时,`delegate_task` 返回工具错误解释限制,而不是静默截断。`max_spawn_depth` 控制委托树深度(截断到 1-3)。在默认 `1` 时,委托是扁平的:子级无法生成孙级,传递 `role=\"orchestrator\"` 静默降级为 `leaf`。提升到 `2` 使编排器子级可以生成叶子孙级;`3` 用于三级树。Agent 通过 `role=\"orchestrator\"` 按调用选择编排;`orchestrator_enabled: false` 强制每个子级回到叶子,无论如何。成本呈乘法增长 —— 在 `max_spawn_depth: 3` 和 `max_concurrent_children: 3` 时,树可以达到 3×3×3 = 27 个并发叶子 agent。使用模式请参阅[子 Agent 委托 → 深度限制和嵌套编排](features/delegation.md#depth-limit-and-nested-orchestration)。\n\n## 澄清\n\n配置澄清提示行为:\n\n```yaml\nclarify:\n timeout: 120 # 等待用户澄清响应的秒数\n```\n\n## 上下文文件(SOUL.md、AGENTS.md)\n\nHermes 使用两种不同的上下文范围:\n\n| 文件 | 用途 | 范围 |\n|------|---------|-------|\n| `SOUL.md` | **主要 agent 身份** —— 定义 agent 是谁(系统提示词第 #1 槽位) | `~/.hermes/SOUL.md` 或 `$HERMES_HOME/SOUL.md` |\n| `.hermes.md` / `HERMES.md` | 项目特定指令(最高优先级) | 向上走到 git 根目录 |\n| `AGENTS.md` | 项目特定指令、编码规范 | 递归目录遍历 |\n| `CLAUDE.md` | Claude Code 上下文文件(也会检测) | 仅工作目录 |\n| `.cursorrules` | Cursor IDE 规则(也会检测) | 仅工作目录 |\n| `.cursor/rules/*.mdc` | Cursor 规则文件(也会检测) | 仅工作目录 |\n\n- **SOUL.md** 是 agent 的主要身份。它占据系统提示词的第 #1 槽位,完全替换内置的默认身份。编辑它以完全自定义 agent 是谁。\n- 如果 SOUL.md 缺失、为空或无法加载,Hermes 回退到内置默认身份。\n- **项目上下文文件使用优先级系统** —— 仅加载一种类型(第一个匹配优先):`.hermes.md` → `AGENTS.md` → `CLAUDE.md` → `.cursorrules`。SOUL.md 始终独立加载。\n- **AGENTS.md** 是分层的:如果子目录也有 AGENTS.md,所有都会合并。\n- 如果 `SOUL.md` 不存在,Hermes 会自动生成默认的 `SOUL.md`。\n- 所有加载的上下文文件上限为 20,000 字符,并进行智能截断。\n\n另请参阅:\n- [个性与 SOUL.md](/user-guide/features/personality)\n- [上下文文件](/user-guide/features/context-files)\n\n## 工作目录\n\n| 上下文 | 默认值 |\n|---------|---------|\n| **CLI(`hermes`)** | 运行命令的当前目录 |\n| **消息 gateway** | 主目录 `~`(用 `MESSAGING_CWD` 覆盖) |\n| **Docker / Singularity / Modal / SSH** | 容器或远程机器内用户的主目录 |\n\n覆盖工作目录:\n```bash\n# 在 ~/.hermes/.env 或 ~/.hermes/config.yaml 中:\nMESSAGING_CWD=/home/myuser/projects # Gateway 会话\nTERMINAL_CWD=/workspace # 所有终端会话\n```\n"}, "a1f51feb72b4526df85fbd9cbdaf9b9627397cba": {"t": 1785768003.1588912, "n": 55611, "h": "d87cd11f9f831f4a", "c": "---\nsidebar_position: 2\ntitle: \"配置\"\ndescription: \"配置 Hermes Agent — config.yaml、providers、模型、API 密钥等\"\n---\n\n# 配置\n\n所有设置均存储在 `~/.hermes/` 目录中,便于访问。\n\n## 目录结构\n\n```text\n~/.hermes/\n├── config.yaml # 设置(模型、终端、TTS、压缩等)\n├── .env # API 密钥和机密\n├── auth.json # OAuth provider 凭据(Nous Portal 等)\n├── SOUL.md # 主要 agent 身份(系统提示词第 #1 槽位)\n├── memories/ # 持久记忆(MEMORY.md、USER.md)\n├── skills/ # Agent 创建的技能(通过 skill_manage 工具管理)\n├── cron/ # 定时任务\n├── sessions/ # Gateway 会话\n└── logs/ # 日志(errors.log、gateway.log — 机密自动脱敏)\n```\n\n## 管理配置\n\n```bash\nhermes config # 查看当前配置\nhermes config edit # 在编辑器中打开 config.yaml\nhermes config set KEY VAL # 设置特定值\nhermes config check # 检查缺失选项(更新后使用)\nhermes config migrate # 交互式添加缺失选项\n\n# 示例:\nhermes config set model anthropic/claude-opus-4\nhermes config set terminal.backend docker\nhermes config set OPENROUTER_API_KEY sk-or-... # 保存到 .env\n```\n\n:::tip\n`hermes config set` 命令会自动将值路由到正确的文件 —— API 密钥保存到 `.env`,其他所有内容保存到 `config.yaml`。\n:::\n\n## 配置优先级\n\n设置按以下顺序解析(优先级从高到低):\n\n1. **CLI 参数** —— 例如 `hermes chat --model anthropic/claude-sonnet-4`(单次调用覆盖)\n2. **`~/.hermes/config.yaml`** —— 所有非机密设置的主配置文件\n3. **`~/.hermes/.env`** —— 环境变量的回退;机密(API 密钥、token、密码)**必须**放这里\n4. **内置默认值** —— 未设置任何内容时的硬编码安全默认值\n\n:::info 经验法则\n机密(API 密钥、bot token、密码)放入 `.env`。其他所有内容(模型、终端后端、压缩设置、内存限制、工具集)放入 `config.yaml`。当两者都设置时,`config.yaml` 对非机密设置优先。\n:::\n\n## 环境变量替换\n\n可以在 `config.yaml` 中使用 `${VAR_NAME}` 语法引用环境变量:\n\n```yaml\nauxiliary:\n vision:\n api_key: ${GOOGLE_API_KEY}\n base_url: ${CUSTOM_VISION_URL}\n\ndelegation:\n api_key: ${DELEGATION_KEY}\n```\n\n单个值中可以有多个引用:`url: \"${HOST}:${PORT}\"`。如果引用的变量未设置,占位符将保持原样(`${UNDEFINED_VAR}` 保持不变)。仅支持 `${VAR}` 语法 —— 裸 `$VAR` 不会被展开。\n\n关于 AI provider 设置(OpenRouter、Anthropic、Copilot、自定义端点、自托管 LLM、回退模型等),请参阅 [AI Providers](/integrations/providers)。\n\n### Provider 超时\n\n可以为 provider 设置 `providers.<id>.request_timeout_seconds` 作为全局请求超时,以及 `providers.<id>.models.<model>.timeout_seconds` 作为特定模型的覆盖值。适用于每种传输方式(OpenAI-wire、原生 Anthropic、Anthropic 兼容)上的主轮次客户端、回退链、凭据轮换后的重建,以及(对于 OpenAI-wire)每请求超时 kwarg —— 因此配置值优先于旧版 `HERMES_API_TIMEOUT` 环境变量。\n\n还可以设置 `providers.<id>.stale_timeout_seconds` 用于非流式陈旧调用检测器,以及 `providers.<id>.models.<model>.stale_timeout_seconds` 作为特定模型的覆盖值。此值优先于旧版 `HERMES_API_CALL_STALE_TIMEOUT` 环境变量。\n\n不设置这些值将保持旧版默认值(`HERMES_API_TIMEOUT=1800`s、`HERMES_API_CALL_STALE_TIMEOUT=300`s、原生 Anthropic 900s)。目前不适用于 AWS Bedrock(`bedrock_converse` 和 AnthropicBedrock SDK 路径均使用 boto3 及其自身的超时配置)。请参阅 [`cli-config.yaml.example`](https://github.com/NousResearch/hermes-agent/blob/main/cli-config.yaml.example) 中的注释示例。\n\n## 终端后端配置\n\nHermes 支持六种终端后端。每种后端决定 agent 的 shell 命令实际在哪里执行 —— 本地机器、Docker 容器、通过 SSH 的远程服务器、Modal 云沙箱(直接或通过 Nous 托管的 gateway)、Daytona 工作区,或 Singularity/Apptainer 容器。\n\n```yaml\nterminal:\n backend: local # local | docker | ssh | modal | daytona | singularity\n cwd: \".\" # Gateway/cron 工作目录(CLI 始终使用启动目录)\n timeout: 180 # 每条命令的超时时间(秒)\n env_passthrough: [] # 转发到沙箱执行的环境变量名(terminal + execute_code)\n singularity_image: \"docker://nikolaik/python-nodejs:python3.11-nodejs20\" # Singularity 后端的容器镜像\n modal_image: \"nikolaik/python-nodejs:python3.11-nodejs20\" # Modal 后端的容器镜像\n daytona_image: \"nikolaik/python-nodejs:python3.11-nodejs20\" # Daytona 后端的容器镜像\n```\n\n对于 Modal 和 Daytona 等云沙箱,`container_persistent: true` 表示 Hermes 将尝试在沙箱重建后保留文件系统状态。这并不保证相同的活跃沙箱、PID 空间或后台进程之后仍在运行。\n\n### 后端概览\n\n| 后端 | 命令运行位置 | 隔离性 | 最适合 |\n|---------|-------------------|-----------|----------|\n| **local** | 直接在您的机器上 | 无 | 开发、个人使用 |\n| **docker** | 单个持久 Docker 容器(跨会话、`/new`、子 agent 共享) | 完全(命名空间、cap-drop) | 安全沙箱、CI/CD |\n| **ssh** | 通过 SSH 的远程服务器 | 网络边界 | 远程开发、强大硬件 |\n| **modal** | Modal 云沙箱 | 完全(云 VM) | 临时云计算、评估 |\n| **daytona** | Daytona 工作区 | 完全(云容器) | 托管云开发环境 |\n| **singularity** | Singularity/Apptainer 容器 | 命名空间(--containall) | HPC 集群、共享机器 |\n\n### Local 后端\n\n默认后端。命令直接在您的机器上运行,无隔离。无需特殊设置。\n\n```yaml\nterminal:\n backend: local\n```\n\n:::warning\nAgent 拥有与您的用户账户相同的文件系统访问权限。使用 `hermes tools` 禁用不需要的工具,或切换到 Docker 进行沙箱隔离。\n:::\n\n### Docker 后端\n\n在具有安全加固的 Docker 容器内运行命令(所有权限已删除、无权限提升、PID 限制)。\n\n**单个持久容器,而非每条命令一个容器。** Hermes 在首次使用时启动一个长期运行的容器,并通过 `docker exec` 将每个终端、文件和 `execute_code` 调用路由到同一容器中 —— 跨会话、`/new`、`/reset` 和 `delegate_task` 子 agent,贯穿 Hermes 进程的整个生命周期。工作目录更改、已安装的包以及 `/workspace` 中的文件会从一次工具调用延续到下一次,就像本地 shell 一样。容器在关闭时停止并删除。详情请参阅下方的**容器生命周期**。\n\n```yaml\nterminal:\n backend: docker\n docker_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n docker_mount_cwd_to_workspace: false # 将启动目录挂载到 /workspace\n docker_run_as_host_user: false # 参见下方\"以宿主用户身份运行容器\"\n docker_forward_env: # 转发到容器的环境变量\n - \"GITHUB_TOKEN\"\n docker_volumes: # 宿主目录挂载\n - \"/home/user/projects:/workspace/projects\"\n - \"/home/user/data:/data:ro\" # :ro 表示只读\n docker_extra_args: # 附加到 `docker run` 的额外标志\n - \"--gpus=all\"\n - \"--network=host\"\n\n # 资源限制\n container_cpu: 1 # CPU 核心数(0 = 不限制)\n container_memory: 5120 # MB(0 = 不限制)\n container_disk: 51200 # MB(需要 XFS+pquota 上的 overlay2)\n container_persistent: true # 跨会话持久化 /workspace 和 /root\n```\n\n**`terminal.docker_extra_args`**(也可通过 `TERMINAL_DOCKER_EXTRA_ARGS='[\"--gpus=all\"]'` 覆盖)允许传递 Hermes 未作为一级键公开的任意 `docker run` 标志 —— `--gpus`、`--network`、`--add-host`、替代 `--security-opt` 覆盖等。每个条目必须是字符串;该列表最后附加到组装好的 `docker run` 调用中,因此可以在需要时覆盖 Hermes 的默认值。请谨慎使用 —— 与沙箱加固(权限删除、`--user`、workspace 绑定挂载)冲突的标志将悄然削弱隔离性。\n\n**要求:** 已安装并运行 Docker Desktop 或 Docker Engine。Hermes 会探测 `$PATH` 以及常见的 macOS 安装位置(`/usr/local/bin/docker`、`/opt/homebrew/bin/docker`、Docker Desktop 应用包)。开箱即用支持 Podman:设置 `HERMES_DOCKER_BINARY=podman`(或完整路径)以在两者都安装时强制使用它。\n\n**容器生命周期:** Hermes 为每个终端和文件工具调用重用单个长期运行的容器(`docker run -d ... sleep 2h`),跨会话、`/new`、`/reset` 和 `delegate_task` 子 agent,贯穿 Hermes 进程的整个生命周期。命令通过带登录 shell 的 `docker exec` 运行,因此工作目录更改、已安装的包以及 `/workspace` 中的文件都会从一次工具调用延续到下一次。容器在 Hermes 关闭时(或空闲清理回收时)停止并删除。\n\n通过 `delegate_task(tasks=[...])` 生成的并行子 agent 共享这一个容器 —— 并发的 `cd`、环境变量修改以及对同一路径的写入会发生冲突。如果子 agent 需要隔离的沙箱,必须通过 `register_task_env_overrides()` 注册每任务镜像覆盖,RL 和基准测试环境(TerminalBench2、HermesSweEnv 等)会自动为其每任务 Docker 镜像执行此操作。\n\n**安全加固:**\n- `--cap-drop ALL`,仅添加回 `DAC_OVERRIDE`、`CHOWN`、`FOWNER`\n- `--security-opt no-new-privileges`\n- `--pids-limit 256`\n- `/tmp`(512MB)、`/var/tmp`(256MB)、`/run`(64MB)的大小限制 tmpfs\n\n**凭据转发:** `docker_forward_env` 中列出的环境变量首先从您的 shell 环境解析,然后回退到 `~/.hermes/.env`。技能也可以声明 `required_environment_variables`,这些变量会自动合并。\n\n### SSH 后端\n\n通过 SSH 在远程服务器上运行命令。使用 ControlMaster 进行连接复用(5 分钟空闲保活)。默认启用持久 shell —— 状态(cwd、环境变量)在命令之间保持。\n\n```yaml\nterminal:\n backend: ssh\n persistent_shell: true # 保持长期运行的 bash 会话(默认:true)\n```\n\n**必需的环境变量:**\n\n```bash\nTERMINAL_SSH_HOST=my-server.example.com\nTERMINAL_SSH_USER=ubuntu\n```\n\n**可选:**\n\n| 变量 | 默认值 | 描述 |\n|----------|---------|-------------|\n| `TERMINAL_SSH_PORT` | `22` | SSH 端口 |\n| `TERMINAL_SSH_KEY` | (系统默认) | SSH 私钥路径 |\n| `TERMINAL_SSH_PERSISTENT` | `true` | 启用持久 shell |\n\n**工作原理:** 使用 `BatchMode=yes` 和 `StrictHostKeyChecking=accept-new` 在初始化时连接。持久 shell 在远程主机上保持单个 `bash -l` 进程存活,通过临时文件进行通信。需要 `stdin_data` 或 `sudo` 的命令会自动回退到单次模式。\n\n### Modal 后端\n\n在 [Modal](https://modal.com) 云沙箱中运行命令。每个任务获得一个具有可配置 CPU、内存和磁盘的隔离 VM。文件系统可以跨会话快照/恢复。\n\n```yaml\nterminal:\n backend: modal\n container_cpu: 1 # CPU 核心数\n container_memory: 5120 # MB(5GB)\n container_disk: 51200 # MB(50GB)\n container_persistent: true # 快照/恢复文件系统\n```\n\n**必需:** `MODAL_TOKEN_ID` + `MODAL_TOKEN_SECRET` 环境变量,或 `~/.modal.toml` 配置文件。\n\n**持久化:** 启用后,沙箱文件系统在清理时快照,并在下次会话时恢复。快照在 `~/.hermes/modal_snapshots.json` 中跟踪。这保留文件系统状态,而非活跃进程、PID 空间或后台任务。\n\n**凭据文件:** 自动从 `~/.hermes/` 挂载(OAuth token 等),并在每条命令前同步。\n\n### Daytona 后端\n\n在 [Daytona](https://daytona.io) 托管工作区中运行命令。支持停止/恢复以实现持久化。\n\n```yaml\nterminal:\n backend: daytona\n container_cpu: 1 # CPU 核心数\n container_memory: 5120 # MB → 转换为 GiB\n container_disk: 10240 # MB → 转换为 GiB(最大 10 GiB)\n container_persistent: true # 停止/恢复而非删除\n```\n\n**必需:** `DAYTONA_API_KEY` 环境变量。\n\n**持久化:** 启用后,沙箱在清理时停止(而非删除),并在下次会话时恢复。沙箱名称遵循 `hermes-{task_id}` 模式。\n\n**磁盘限制:** Daytona 强制执行 10 GiB 最大值。超过此值的请求将被截断并发出警告。\n\n### Singularity/Apptainer 后端\n\n在 [Singularity/Apptainer](https://apptainer.org) 容器中运行命令。专为 Docker 不可用的 HPC 集群和共享机器设计。\n\n```yaml\nterminal:\n backend: singularity\n singularity_image: \"docker://nikolaik/python-nodejs:python3.11-nodejs20\"\n container_cpu: 1 # CPU 核心数\n container_memory: 5120 # MB\n container_persistent: true # 可写覆盖层跨会话持久化\n```\n\n**要求:** `$PATH` 中有 `apptainer` 或 `singularity` 二进制文件。\n\n**镜像处理:** Docker URL(`docker://...`)自动转换为 SIF 文件并缓存。现有 `.sif` 文件直接使用。\n\n**临时目录:** 按顺序解析:`TERMINAL_SCRATCH_DIR` → `TERMINAL_SANDBOX_DIR/singularity` → `/scratch/$USER/hermes-agent`(HPC 惯例)→ `~/.hermes/sandboxes/singularity`。\n\n**隔离:** 使用 `--containall --no-home` 实现完全命名空间隔离,不挂载宿主 home 目录。\n\n### 常见终端后端问题\n\n如果终端命令立即失败或终端工具报告为已禁用:\n\n- **Local** —— 无特殊要求。入门时最安全的默认选项。\n- **Docker** —— 运行 `docker version` 验证 Docker 是否正常工作。如果失败,修复 Docker 或执行 `hermes config set terminal.backend local`。\n- **SSH** —— `TERMINAL_SSH_HOST` 和 `TERMINAL_SSH_USER` 都必须设置。如果缺少任一项,Hermes 会记录清晰的错误。\n- **Modal** —— 需要 `MODAL_TOKEN_ID` 环境变量或 `~/.modal.toml`。运行 `hermes doctor` 检查。\n- **Daytona** —— 需要 `DAYTONA_API_KEY`。Daytona SDK 处理服务器 URL 配置。\n- **Singularity** —— 需要 `$PATH` 中有 `apptainer` 或 `singularity`。HPC 集群上常见。\n\n如有疑问,将 `terminal.backend` 设回 `local` 并首先验证命令在那里运行。\n\n### 拆卸时远程到宿主文件同步\n\n对于 **SSH**、**Modal** 和 **Daytona** 后端(agent 的工作树位于与运行 Hermes 的宿主不同的机器上),Hermes 跟踪 agent 在远程沙箱中触及的文件,并在会话拆卸/沙箱清理时,将修改的文件**同步回宿主**,存放在 `~/.hermes/cache/remote-syncs/<session-id>/` 下。\n\n- 触发时机:会话关闭、`/new`、`/reset`、gateway 消息超时、子 agent 使用远程后端时 `delegate_task` 子 agent 完成。\n- 覆盖 agent 修改的整个树,而不仅仅是它明确打开的文件。添加、编辑和删除都会被捕获。\n- 远程沙箱可能在您查找时已被拆除;本地 `~/.hermes/cache/remote-syncs/…` 副本是 agent 更改内容的权威记录。\n- 大型二进制输出(模型检查点、原始数据集)按大小限制 —— 同步跳过超过 `file_sync_max_mb`(默认 `100`)的文件。如果您期望更大的工件返回,请调高该值。\n\n```yaml\nterminal:\n file_sync_max_mb: 100 # 默认 —— 同步最大 100 MB 的文件\n file_sync_enabled: true # 默认 —— 设为 false 可完全跳过同步\n```\n\n这是从会话结束后被销毁的临时云沙箱中恢复结果的方式,无需告诉 agent 显式地 `scp` 或 `modal volume put` 每个工件。\n\n### Docker 卷挂载\n\n使用 Docker 后端时,`docker_volumes` 允许您与容器共享宿主目录。每个条目使用标准 Docker `-v` 语法:`host_path:container_path[:options]`。\n\n```yaml\nterminal:\n backend: docker\n docker_volumes:\n - \"/home/user/projects:/workspace/projects\" # 读写(默认)\n - \"/home/user/datasets:/data:ro\" # 只读\n - \"/home/user/.hermes/cache/documents:/output\" # Gateway 可见的导出\n```\n\n适用于:\n- **向 agent 提供文件**(数据集、配置、参考代码)\n- **从 agent 接收文件**(生成的代码、报告、导出)\n- **共享工作区**,您和 agent 都访问相同的文件\n\n如果您使用消息 gateway 并希望 agent 通过 `MEDIA:/...` 发送生成的文件,建议使用专用的宿主可见导出挂载,例如 `/home/user/.hermes/cache/documents:/output`。\n\n- 在 Docker 中将文件写入 `/output/...`\n- 在 `MEDIA:` 中发出**宿主路径**,例如:`MEDIA:/home/user/.hermes/cache/documents/report.txt`\n- **不要**发出 `/workspace/...` 或 `/output/...`,除非该确切路径在宿主上对 gateway 进程也存在\n\n:::warning\nYAML 重复键会静默覆盖之前的键。如果您已有 `docker_volumes:` 块,请将新挂载合并到同一列表中,而不是在文件后面再添加一个 `docker_volumes:` 键。\n:::\n\n也可以通过环境变量设置:`TERMINAL_DOCKER_VOLUMES='[\"/host:/container\"]'`(JSON 数组)。\n\n### Docker 凭据转发\n\n默认情况下,Docker 终端会话不继承任意宿主凭据。如果您需要在容器内使用特定 token,请将其添加到 `terminal.docker_forward_env`。\n\n```yaml\nterminal:\n backend: docker\n docker_forward_env:\n - \"GITHUB_TOKEN\"\n - \"NPM_TOKEN\"\n```\n\nHermes 首先从您当前的 shell 解析每个列出的变量,然后回退到通过 `hermes config set` 保存的 `~/.hermes/.env`。\n\n:::warning\n`docker_forward_env` 中列出的任何内容都会对容器内运行的命令可见。只转发您愿意暴露给终端会话的凭据。\n:::\n\n### 以宿主用户身份运行容器\n\n默认情况下,Docker 容器以 `root`(UID 0)身份运行。在 `/workspace` 或其他绑定挂载中创建的文件在宿主上归 root 所有,因此会话结束后您必须 `sudo chown` 才能从宿主编辑器编辑它们。`terminal.docker_run_as_host_user` 标志解决了这个问题:\n\n```yaml\nterminal:\n backend: docker\n docker_run_as_host_user: true # 默认:false\n```\n\n启用后,Hermes 将 `--user $(id -u):$(id -g)` 附加到 `docker run` 命令,使写入绑定挂载目录(`/workspace`、`/root`、`docker_volumes` 中的任何内容)的文件归您的宿主用户所有,而非 root。权衡:容器将无法再 `apt install` 或写入 `/root/.npm` 等 root 拥有的路径 —— 如果您同时需要这两者,请使用 `HOME` 归非 root 用户所有的基础镜像(或在镜像构建时添加所需工具)。\n\n保持 `false`(默认)以获得向后兼容的行为。当您的工作流主要是\"编辑挂载的宿主文件\"且厌倦了 `sudo chown -R` 时,请开启此选项。\n\n### 可选:将启动目录挂载到 `/workspace`\n\nDocker 沙箱默认保持隔离。Hermes **不会**将您当前的宿主工作目录传入容器,除非您明确选择加入。\n\n在 `config.yaml` 中启用:\n\n```yaml\nterminal:\n backend: docker\n docker_mount_cwd_to_workspace: true\n```\n\n启用后:\n- 如果您从 `~/projects/my-app` 启动 Hermes,该宿主目录将绑定挂载到 `/workspace`\n- Docker 后端从 `/workspace` 开始\n- 文件工具和终端命令都能看到相同的挂载项目\n\n禁用时,`/workspace` 保持沙箱所有,除非您通过 `docker_volumes` 显式挂载内容。\n\n安全权衡:\n- `false` 保留沙箱边界\n- `true` 使沙箱直接访问您启动 Hermes 的目录\n\n仅在您有意希望容器处理实时宿主文件时才选择加入。\n\n### 持久 Shell\n\n默认情况下,每条终端命令在其自己的子进程中运行 —— 工作目录、环境变量和 shell 变量在命令之间重置。启用**持久 shell** 后,单个长期运行的 bash 进程在 `execute()` 调用之间保持存活,使状态在命令之间保持。\n\n这对 **SSH 后端**最有用,它还消除了每条命令的连接开销。持久 shell **对 SSH 默认启用**,对本地后端禁用。\n\n```yaml\nterminal:\n persistent_shell: true # 默认 —— 为 SSH 启用持久 shell\n```\n\n禁用:\n\n```bash\nhermes config set terminal.persistent_shell false\n```\n\n**跨命令保持的内容:**\n- 工作目录(`cd /tmp` 对下一条命令生效)\n- 导出的环境变量(`export FOO=bar`)\n- Shell 变量(`MY_VAR=hello`)\n\n**优先级:**\n\n| 级别 | 变量 | 默认值 |\n|-------|----------|---------|\n| 配置 | `terminal.persistent_shell` | `true` |\n| SSH 覆盖 | `TERMINAL_SSH_PERSISTENT` | 遵循配置 |\n| Local 覆盖 | `TERMINAL_LOCAL_PERSISTENT` | `false` |\n\n每个后端的环境变量具有最高优先级。如果您也想在本地后端使用持久 shell:\n\n```bash\nexport TERMINAL_LOCAL_PERSISTENT=true\n```\n\n:::note\n需要 `stdin_data` 或 sudo 的命令会自动回退到单次模式,因为持久 shell 的 stdin 已被 IPC 协议占用。\n:::\n\n有关每个后端的详细信息,请参阅[代码执行](features/code-execution.md)和 [README 的终端部分](features/tools.md)。\n\n## 技能设置\n\n技能可以通过其 SKILL.md frontmatter 声明自己的配置设置。这些是非机密值(路径、偏好、域设置),存储在 `config.yaml` 的 `skills.config` 命名空间下。\n\n```yaml\nskills:\n config:\n myplugin:\n path: ~/myplugin-data # 示例 —— 每个技能定义自己的键\n```\n\n**技能设置的工作原理:**\n\n- `hermes config migrate` 扫描所有已启用的技能,找到未配置的设置,并提供提示\n- `hermes config show` 在\"技能设置\"下显示所有技能设置及其所属技能\n- 技能加载时,其解析的配置值会自动注入到技能上下文中\n\n**手动设置值:**\n\n```bash\nhermes config set skills.config.myplugin.path ~/myplugin-data\n```\n\n有关在您自己的技能中声明配置设置的详细信息,请参阅[创建技能 — 配置设置](/developer-guide/creating-skills#config-settings-configyaml)。\n\n### Agent 创建技能写入的守卫\n\n当 agent 使用 `skill_manage` 创建、编辑、修补或删除技能时,Hermes 可以选择扫描新/更新的内容以查找危险关键字模式(凭据收集、明显的 prompt 注入、数据外泄指令)。扫描器**默认关闭** —— 合法触及 `~/.ssh/` 或提及 `$OPENAI_API_KEY` 的真实 agent 工作流触发启发式规则过于频繁。如果您希望扫描器在 agent 的技能写入落地前提示您,请重新开启:\n\n```yaml\nskills:\n guard_agent_created: true # 默认:false\n```\n\n开启后,任何被标记的 `skill_manage` 写入都会以审批提示的形式出现,并附带扫描器的理由。接受的写入落地;拒绝的写入向 agent 返回解释性错误。\n\n## 内存配置\n\n```yaml\nmemory:\n memory_enabled: true\n user_profile_enabled: true\n memory_char_limit: 2200 # ~800 tokens\n user_char_limit: 1375 # ~500 tokens\n```\n\n## 文件读取安全\n\n控制单次 `read_file` 调用可以返回多少内容。超过限制的读取将被拒绝,并向 agent 返回错误,提示使用 `offset` 和 `limit` 读取较小范围。这可以防止单次读取压缩的 JS 包或大型数据文件时淹没上下文窗口。\n\n```yaml\nfile_read_max_chars: 100000 # 默认 —— ~25-35K tokens\n```\n\n如果您使用具有大上下文窗口的模型并经常读取大文件,请调高此值。对于小上下文模型,请降低以保持读取高效:\n\n```yaml\n# 大上下文模型(200K+)\nfile_read_max_chars: 200000\n\n# 小型本地模型(16K 上下文)\nfile_read_max_chars: 30000\n```\n\nAgent 还会自动去重文件读取 —— 如果同一文件区域被读取两次且文件未更改,则返回轻量级存根而不是重新发送内容。这在上下文压缩后重置,以便 agent 在内容被摘要后可以重新读取文件。\n\n## 工具输出截断限制\n\n三个相关的上限控制工具在 Hermes 截断之前可以返回多少原始输出:\n\n```yaml\ntool_output:\n max_bytes: 50000 # 终端输出上限(字符)\n max_lines: 2000 # read_file 分页上限\n max_line_length: 2000 # read_file 行号视图中的每行上限\n```\n\n- **`max_bytes`** —— 当 `terminal` 命令产生超过此字符数的合并 stdout/stderr 时,Hermes 保留前 40% 和后 60%,并在中间插入 `[OUTPUT TRUNCATED]` 通知。默认 `50000`(典型分词器约 12-15K tokens)。\n- **`max_lines`** —— 单次 `read_file` 调用的 `limit` 参数上限。超过此值的请求将被截断,以防单次读取淹没上下文窗口。默认 `2000`。\n- **`max_line_length`** —— `read_file` 发出行号视图时应用的每行上限。超过此长度的行将被截断为此字符数,后跟 `... [truncated]`。默认 `2000`。\n\n对于具有大上下文窗口且每次调用可以承受更多原始输出的模型,请调高限制。对于小上下文模型,请降低以保持工具结果紧凑:\n\n```yaml\n# 大上下文模型(200K+)\ntool_output:\n max_bytes: 150000\n max_lines: 5000\n\n# 小型本地模型(16K 上下文)\ntool_output:\n max_bytes: 20000\n max_lines: 500\n```\n\n## 全局工具集禁用\n\n要在 CLI 和每个 gateway 平台上统一禁用特定工具集,请在 `agent.disabled_toolsets` 下列出其名称:\n\n```yaml\nagent:\n disabled_toolsets:\n - memory # 隐藏内存工具 + MEMORY_GUIDANCE 注入\n - web # 任何地方都不使用 web_search / web_extract\n```\n\n这在每个平台的工具配置(由 `hermes tools` 写入的 `platform_toolsets`)**之后**应用,因此此处列出的工具集始终被删除 —— 即使平台的已保存配置仍然列出它。当您希望有一个\"到处关闭 X\"的单一开关而不是编辑 `hermes tools` UI 中的 15+ 个平台行时,请使用此选项。\n\n留空列表或省略键不会产生任何效果。\n\n## Git Worktree 隔离\n\n启用隔离的 git worktree,以便在同一仓库上并行运行多个 agent:\n\n```yaml\nworktree: true # 始终创建 worktree(与 hermes -w 相同)\n# worktree: false # 默认 —— 仅在传递 -w 标志时\n```\n\n启用后,每个 CLI 会话在 `.worktrees/` 下创建一个带有自己分支的新 worktree。Agent 可以编辑文件、提交、推送和创建 PR,而不会相互干扰。干净的 worktree 在退出时删除;脏的 worktree 保留以供手动恢复。\n\n您还可以通过仓库根目录中的 `.worktreeinclude` 列出要复制到 worktree 的 gitignore 文件:\n\n```\n# .worktreeinclude\n.env\n.venv/\nnode_modules/\n```\n\n## 上下文压缩\n\nHermes 自动压缩长对话以保持在模型的上下文窗口内。压缩摘要器是一个单独的 LLM 调用 —— 您可以将其指向任何 provider 或端点。\n\n所有压缩设置都在 `config.yaml` 中(无环境变量)。\n\n### 完整参考\n\n```yaml\ncompression:\n enabled: true # 开启/关闭压缩\n threshold: 0.50 # 在上下文限制的此百分比时压缩\n target_ratio: 0.20 # 保留为最近尾部的阈值分数\n protect_last_n: 20 # 保持未压缩的最少最近消息数\n hygiene_hard_message_limit: 400 # Gateway 安全阀 —— 见下文\n\n# 摘要模型/provider 在 auxiliary: 下配置:\nauxiliary:\n compression:\n model: \"\" # 空 = 使用主聊天模型。覆盖为例如 \"google/gemini-3-flash-preview\" 以获得更便宜/更快的压缩。\n provider: \"auto\" # Provider:\"auto\"、\"openrouter\"、\"nous\"、\"codex\"、\"main\" 等\n base_url: null # 自定义 OpenAI 兼容端点(覆盖 provider)\n```\n\n:::info 旧版配置迁移\n带有 `compression.summary_model`、`compression.summary_provider` 和 `compression.summary_base_url` 的旧版配置在首次加载时自动迁移到 `auxiliary.compression.*`(配置版本 17)。无需手动操作。\n:::\n\n`hygiene_hard_message_limit` 是仅限 gateway 的**预压缩安全阀**。拥有数千条消息的失控会话可能在正常的上下文百分比阈值触发之前就达到模型上下文限制;当消息数超过此上限时,Hermes 强制压缩,无论 token 使用情况如何。默认 `400` —— 对于非常长的会话正常的平台,请调高;要强制更积极的压缩,请降低。在运行中的 gateway 上编辑此值将在下一条消息时生效(见下文)。\n\n:::tip Gateway 热重载压缩和上下文长度\n从最近的版本开始,在运行中的 gateway 上编辑 `config.yaml` 中的 `model.context_length` 或任何 `compression.*` 键将在下一条消息时生效 —— 无需 gateway 重启、`/reset` 或会话轮换。缓存的 agent 签名包含这些键,因此 gateway 在检测到更改时会透明地重建 agent。API 密钥和工具/技能配置仍需要通常的重载路径。\n:::\n\n### 常见设置\n\n**默认(自动检测)—— 无需配置:**\n```yaml\ncompression:\n enabled: true\n threshold: 0.50\n```\n使用您的主 provider 和主模型。如果您希望在比主聊天模型更便宜的模型上进行压缩,请覆盖每任务(例如 `auxiliary.compression.provider: openrouter` + `model: google/gemini-2.5-flash`)。\n\n**强制特定 provider**(基于 OAuth 或 API 密钥):\n```yaml\nauxiliary:\n compression:\n provider: nous\n model: gemini-3-flash\n```\n适用于任何 provider:`nous`、`openrouter`、`codex`、`anthropic`、`main` 等。\n\n**自定义端点**(自托管、Ollama、zai、DeepSeek 等):\n```yaml\nauxiliary:\n compression:\n model: glm-4.7\n base_url: https://api.z.ai/api/coding/paas/v4\n```\n指向自定义 OpenAI 兼容端点。使用 `OPENAI_API_KEY` 进行认证。\n\n### 三个旋钮的交互方式\n\n| `auxiliary.compression.provider` | `auxiliary.compression.base_url` | 结果 |\n|---------------------|---------------------|--------|\n| `auto`(默认) | 未设置 | 自动检测最佳可用 provider |\n| `nous` / `openrouter` / 等 | 未设置 | 强制使用该 provider,使用其认证 |\n| 任意 | 已设置 | 直接使用自定义端点(忽略 provider) |\n\n:::warning 摘要模型上下文长度要求\n摘要模型**必须**具有至少与您的主 agent 模型一样大的上下文窗口。压缩器将对话的完整中间部分发送给摘要模型 —— 如果该模型的上下文窗口小于主模型的,摘要调用将因上下文长度错误而失败。发生这种情况时,中间轮次将**在没有摘要的情况下被丢弃**,静默丢失对话上下文。如果您覆盖模型,请验证其上下文长度满足或超过您的主模型。\n:::\n\n## 上下文引擎\n\n上下文引擎控制在接近模型 token 限制时如何管理对话。内置的 `compressor` 引擎使用有损摘要(参见[上下文压缩](/developer-guide/context-compression-and-caching))。插件引擎可以用替代策略替换它。\n\n```yaml\ncontext:\n engine: \"compressor\" # 默认 —— 内置有损摘要\n```\n\n使用插件引擎(例如,用于无损上下文管理的 LCM):\n\n```yaml\ncontext:\n engine: \"lcm\" # 必须与插件名称匹配\n```\n\n插件引擎**永远不会自动激活** —— 您必须将 `context.engine` 显式设置为插件名称。可用引擎可以通过 `hermes plugins` → Provider Plugins → Context Engine 浏览和选择。\n\n有关内存插件的类似单选系统,请参阅[内存 Providers](/user-guide/features/memory-providers)。\n\n## 迭代预算压力\n\n当 agent 在处理具有许多工具调用的复杂任务时,它可能会在没有意识到预算不足的情况下耗尽其迭代预算(默认:90 轮)。预算压力会在模型接近限制时自动发出警告:\n\n| 阈值 | 级别 | 模型看到的内容 |\n|-----------|-------|---------------------|\n| **70%** | 注意 | `[BUDGET: 63/90. 27 iterations left. Start consolidating.]` |\n| **90%** | 警告 | `[BUDGET WARNING: 81/90. Only 9 left. Respond NOW.]` |\n\n警告注入到最后一个工具结果的 JSON 中(作为 `_budget_warning` 字段),而不是作为单独的消息 —— 这保留了 prompt 缓存,不会破坏对话结构。\n\n```yaml\nagent:\n max_turns: 90 # 每次对话轮次的最大迭代次数(默认:90)\n api_max_retries: 3 # 回退启动前每个 provider 的重试次数(默认:3)\n```\n\n预算压力默认启用。Agent 自然地将警告视为工具结果的一部分,鼓励它在耗尽迭代之前整合工作并提供响应。\n\n当迭代预算完全耗尽时,CLI 向用户显示通知:`⚠ Iteration budget reached (90/90) — response may be incomplete`。如果预算在活跃工作期间耗尽,agent 会在停止前生成已完成内容的摘要。\n\n`agent.api_max_retries` 控制 Hermes 在回退 provider 切换启动**之前**对瞬时错误(速率限制、连接断开、5xx)重试 provider API 调用的次数。默认为 `3` —— 总共四次尝试。如果您配置了[回退 providers](/user-guide/features/fallback-providers) 并希望更快地故障转移,请将其降至 `0`,这样主 provider 上的第一个瞬时错误会立即切换到回退,而不是对不稳定的端点进行重试。\n\n### API 超时\n\nHermes 对流式传输有单独的超时层,以及用于非流式调用的陈旧检测器。陈旧检测器仅在您将其保留为隐式默认值时才会自动调整本地 provider。\n\n| 超时 | 默认值 | 本地 providers | 配置/环境变量 |\n|---------|---------|----------------|--------------|\n| Socket 读取超时 | 120s | 自动提升至 1800s | `HERMES_STREAM_READ_TIMEOUT` |\n| 陈旧流检测 | 180s | 自动禁用 | `HERMES_STREAM_STALE_TIMEOUT` |\n| 陈旧非流检测 | 300s | 保持隐式时自动禁用 | `providers.<id>.stale_timeout_seconds` 或 `HERMES_API_CALL_STALE_TIMEOUT` |\n| API 调用(非流式) | 1800s | 不变 | `providers.<id>.request_timeout_seconds` / `timeout_seconds` 或 `HERMES_API_TIMEOUT` |\n\n**Socket 读取超时**控制 httpx 等待 provider 下一个数据块的时间。本地 LLM 在大上下文上预填充可能需要几分钟才能产生第一个 token,因此当 Hermes 检测到本地端点时,会将此值提升至 30 分钟。如果您显式设置 `HERMES_STREAM_READ_TIMEOUT`,无论端点检测如何,始终使用该值。\n\n**陈旧流检测**终止接收 SSE 保活 ping 但没有实际内容的连接。对于本地 providers,这完全禁用,因为它们在预填充期间不发送保活 ping。\n\n**陈旧非流检测**终止长时间没有响应的非流式调用。默认情况下,Hermes 在本地端点上禁用此功能,以避免长时间预填充期间的误报。如果您显式设置 `providers.<id>.stale_timeout_seconds`、`providers.<id>.models.<model>.stale_timeout_seconds` 或 `HERMES_API_CALL_STALE_TIMEOUT`,即使在本地端点上也会遵守该显式值。\n\n## 上下文压力警告\n\n与迭代预算压力分开,上下文压力跟踪对话距**压缩阈值**有多近 —— 即上下文压缩触发以摘要旧消息的点。这有助于您和 agent 了解对话何时变长。\n\n| 进度 | 级别 | 发生的事情 |\n|----------|-------|-------------|\n| **≥ 60%** 到阈值 | 信息 | CLI 显示青色进度条;gateway 发送信息通知 |\n| **≥ 85%** 到阈值 | 警告 | CLI 显示粗体黄色进度条;gateway 警告压缩即将发生 |\n\n在 CLI 中,上下文压力在工具输出流中显示为进度条:\n\n```\n ◐ context ████████████░░░░░░░░ 62% to compaction 48k threshold (50%) · approaching compaction\n```\n\n在消息平台上,发送纯文本通知:\n\n```\n◐ Context: ████████████░░░░░░░░ 62% to compaction (threshold: 50% of window).\n```\n\n如果自动压缩被禁用,警告会告诉您上下文可能被截断。\n\n上下文压力是自动的 —— 无需配置。它纯粹作为面向用户的通知触发,不修改消息流或向模型上下文注入任何内容。\n\n## 凭据池策略\n\n当您为同一 provider 拥有多个 API 密钥或 OAuth token 时,配置轮换策略:\n\n```yaml\ncredential_pool_strategies:\n openrouter: round_robin # 均匀循环使用密钥\n anthropic: least_used # 始终选择使用最少的密钥\n```\n\n选项:`fill_first`(默认)、`round_robin`、`least_used`、`random`。完整文档请参阅[凭据池](/user-guide/features/credential-pools)。\n\n## Prompt 缓存\n\n当活跃 provider 支持时,Hermes 自动开启跨会话 prompt 缓存 —— 无需用户配置。\n\n对于**原生 Anthropic**、**OpenRouter** 和 **Nous Portal** 上的 Claude,Hermes 在系统提示词和技能块上附加带有 1 小时 TTL(`ttl: \"1h\"`)的 `cache_control` 断点。在新鲜的一小时内首次发送时按完整输入费率计费;同一小时内任何会话的后续发送以折扣缓存读取费率从缓存中提取。这意味着系统提示词、加载的技能内容以及任何长上下文包含的早期部分在第一个小时内跨 `hermes` 会话和分叉子 agent 被重用。\n\nQwen Cloud(阿里巴巴 DashScope)上游将缓存 TTL 限制为 5 分钟,因此 Hermes 在那里使用 5 分钟断点 TTL。其他通过第三方的 Claude 路径(AWS Bedrock、Azure Foundry)回退到 provider 自己的缓存默认值。xAI Grok 使用单独的会话固定对话 ID 机制 —— 参阅 [xAI prompt 缓存](/integrations/providers#xai-grok--responses-api--prompt-caching)。\n\n不存在禁用此功能的旋钮 —— 缓存始终开启,即使在单轮对话中也能节省费用,因为仅系统提示词就占输入 token 数的相当大比例。\n\n## 辅助模型\n\nHermes 使用\"辅助\"模型处理图像分析、网页摘要、浏览器截图分析、会话标题生成和上下文压缩等附带任务。默认情况下(`auxiliary.*.provider: \"auto\"`),Hermes 将每个辅助任务路由到您的**主聊天模型** —— 与您在 `hermes model` 中选择的相同 provider/模型。您无需配置任何内容即可开始,但请注意,在昂贵的推理模型(Opus、MiniMax M2.7 等)上,辅助任务会增加显著成本。如果您希望无论主模型如何都使用便宜且快速的附带任务,请显式设置 `auxiliary.<task>.provider` 和 `auxiliary.<task>.model`(例如,在 OpenRouter 上使用 Gemini Flash 进行视觉和网页提取)。\n\n:::note 为什么 \"auto\" 使用您的主模型\n早期版本将聚合器用户(OpenRouter、Nous Portal)分流到便宜的 provider 端默认值。这令人惊讶 —— 付费购买聚合器订阅的用户会看到不同的模型处理其辅助流量。`auto` 现在对所有人使用主模型,`config.yaml` 中的每任务覆盖仍然优先(见下方[完整辅助配置参考](#full-auxiliary-config-reference))。\n:::\n\n### 交互式配置辅助模型\n\n无需手动编辑 YAML,运行 `hermes model` 并从菜单中选择**\"配置辅助模型\"**。您将获得交互式的每任务选择器:\n\n```\n$ hermes model\n→ Configure auxiliary models\n\n[ ] vision currently: auto / main model\n[ ] web_extract currently: auto / main model\n[ ] title_generation currently: openrouter / google/gemini-3-flash-preview\n[ ] compression currently: auto / main model\n[ ] approval currently: auto / main model\n[ ] triage_specifier currently: auto / main model\n[ ] kanban_decomposer currently: auto / main model\n[ ] profile_describer currently: auto / main model\n```\n\n选择任务,选择 provider(OAuth 流程打开浏览器;API 密钥 provider 提示输入),选择模型。更改持久化到 `config.yaml` 中的 `auxiliary.<task>.*`。与主模型选择器相同的机制 —— 无需学习额外语法。\n\n### 视频教程\n\n<div style={{position: 'relative', width: '100%', aspectRatio: '16 / 9', marginBottom: '1.5rem'}}>\n <iframe\n src=\"https://www.youtube.com/embed/NoF-YajElIM\"\n title=\"Hermes Agent — Auxiliary Models Tutorial\"\n style={{position: 'absolute', top: 0, left: 0, width: '100%', height: '100%', border: 0}}\n allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\"\n allowFullScreen\n />\n</div>\n\n### 通用配置模式\n\nHermes 中的每个模型槽位 —— 辅助任务、压缩、回退 —— 使用相同的三个旋钮:\n\n| 键 | 作用 | 默认值 |\n|-----|-------------|---------|\n| `provider` | 用于认证和路由的 provider | `\"auto\"` |\n| `model` | 请求的模型 | provider 的默认值 |\n| `base_url` | 自定义 OpenAI 兼容端点(覆盖 provider) | 未设置 |\n\n当设置 `base_url` 时,Hermes 忽略 provider 并直接调用该端点(使用 `api_key` 或 `OPENAI_API_KEY` 进行认证)。当仅设置 `provider` 时,Hermes 使用该 provider 的内置认证和基础 URL。\n\n辅助任务的可用 providers:`auto`、`main`,以及[provider 注册表](/reference/environment-variables)中的任何 provider —— `openrouter`、`nous`、`openai-codex`、`copilot`、`copilot-acp`、`anthropic`、`gemini`、`google-gemini-cli`、`qwen-oauth`、`zai`、`kimi-coding`、`kimi-coding-cn`、`minimax`、`minimax-cn`、`minimax-oauth`、`deepseek`、`nvidia`、`xai`、`xai-oauth`、`ollama-cloud`、`alibaba`、`bedrock`、`huggingface`、`arcee`、`xiaomi`、`kilocode`、`opencode-zen`、`opencode-go`、`azure-foundry` —— 或您 `custom_providers` 列表中任何命名的自定义 provider(例如 `provider: \"beans\"`)。\n\n:::tip MiniMax OAuth\n`minimax-oauth` 通过浏览器 OAuth 登录(无需 API 密钥)。运行 `hermes model` 并选择 **MiniMax (OAuth)** 进行认证。辅助任务自动使用 `MiniMax-M2.7-highspeed`。参阅 [MiniMax OAuth 指南](../guides/minimax-oauth.md)。\n:::\n\n:::tip xAI Grok OAuth\n`xai-oauth` 通过浏览器 OAuth 为 SuperGrok 和 X Premium+ 订阅者登录(无需 API 密钥)。运行 `hermes model` 并选择 **xAI Grok OAuth (SuperGrok / Premium+)** 进行认证。相同的 OAuth token 可重用于每个直接到 xAI 的接口(聊天、辅助任务、TTS、图像生成、视频生成、转录)。参阅 [xAI Grok OAuth 指南](../guides/xai-grok-oauth.md),如果 Hermes 在远程主机上,请参阅 [SSH/远程主机上的 OAuth](../guides/oauth-over-ssh.md)。\n:::\n\n:::warning `\"main\"` 仅用于辅助任务\n`\"main\"` provider 选项表示\"使用我的主 agent 使用的任何 provider\" —— 它仅在 `auxiliary:`、`compression:` 和 `fallback_model:` 配置中有效。它**不是**顶级 `model.provider` 设置的有效值。如果您使用自定义 OpenAI 兼容端点,请在 `model:` 部分设置 `provider: custom`。所有主模型 provider 选项请参阅 [AI Providers](/integrations/providers)。\n:::\n\n### 完整辅助配置参考\n\n```yaml\nauxiliary:\n # 图像分析(vision_analyze 工具 + 浏览器截图)\n vision:\n provider: \"auto\" # \"auto\"、\"openrouter\"、\"nous\"、\"codex\"、\"main\" 等\n model: \"\" # 例如 \"openai/gpt-4o\"、\"google/gemini-2.5-flash\"\n base_url: \"\" # 自定义 OpenAI 兼容端点(覆盖 provider)\n api_key: \"\" # base_url 的 API 密钥(回退到 OPENAI_API_KEY)\n timeout: 120 # 秒 —— LLM API 调用超时;视觉负载需要宽裕的超时\n download_timeout: 30 # 秒 —— 图像 HTTP 下载;慢速连接请增加\n\n # 网页摘要 + 浏览器页面文本提取\n web_extract:\n provider: \"auto\"\n model: \"\" # 例如 \"google/gemini-2.5-flash\"\n base_url: \"\"\n api_key: \"\"\n timeout: 360 # 秒(6 分钟)—— 每次尝试的 LLM 摘要\n\n # 危险命令审批分类器\n approval:\n provider: \"auto\"\n model: \"\"\n base_url: \"\"\n api_key: \"\"\n timeout: 30 # 秒\n\n # 上下文压缩超时(与 compression.* 配置分开)\n compression:\n timeout: 120 # 秒 —— 压缩摘要长对话,需要更多时间\n\n # 技能中心 —— 技能匹配和搜索\n skills_hub:\n provider: \"auto\"\n model: \"\"\n base_url: \"\"\n api_key: \"\"\n timeout: 30\n\n # MCP 工具调度\n mcp:\n provider: \"auto\"\n model: \"\"\n base_url: \"\"\n api_key: \"\"\n timeout: 30\n\n # Kanban 分类规格说明器 —— `hermes kanban specify <id>`(或\n # 仪表板上 Triage 列卡片的 ✨ Specify 按钮)使用此\n # 槽位将单行描述扩展为具体规格并将\n # 任务提升到 `todo`。便宜快速的模型在这里效果很好;规格扩展\n # 很短,不需要推理深度。\n triage_specifier:\n provider: \"auto\"\n model: \"\"\n base_url: \"\"\n api_key: \"\"\n timeout: 120\n```\n\n:::tip\n每个辅助任务都有可配置的 `timeout`(秒)。默认值:vision 120s、web_extract 360s、approval 30s、compression 120s。如果您为辅助任务使用慢速本地模型,请增加这些值。Vision 还有单独的 `download_timeout`(默认 30s)用于 HTTP 图像下载 —— 对于慢速连接或自托管图像服务器,请增加此值。\n:::\n\n:::info\n上下文压缩有自己的 `compression:` 块用于阈值,以及 `auxiliary.compression:` 块用于模型/provider 设置 —— 参阅上方的[上下文压缩](#context-compression)。回退模型使用 `fallback_model:` 块 —— 参阅[回退模型](/integrations/providers#fallback-model)。三者都遵循相同的 provider/model/base_url 模式。\n:::\n\n### OpenRouter 路由和辅助任务的 Pareto Code\n\n当辅助任务解析到 OpenRouter(显式或通过 `provider: \"main\"` 而您的主 agent 在 OpenRouter 上)时,主 agent 的 `provider_routing` 和 `openrouter.min_coding_score` 设置**不会传播** —— 按设计,每个辅助任务是独立的。要为特定辅助任务设置 OpenRouter provider 偏好或使用 [Pareto Code 路由器](/integrations/providers#openrouter-pareto-code-router),请通过 `extra_body` 按任务设置:\n\n```yaml\nauxiliary:\n compression:\n provider: openrouter\n model: openrouter/pareto-code # 为此任务使用 Pareto Code 路由器\n extra_body:\n provider: # OpenRouter provider 路由偏好\n order: [anthropic, google] # 按此顺序尝试这些 providers\n sort: throughput # 或 \"price\" | \"latency\"\n # only: [anthropic] # 限制到特定 provider\n # ignore: [deepinfra] # 排除特定 providers\n plugins: # OpenRouter Pareto Code 路由器旋钮\n - id: pareto-router\n min_coding_score: 0.5 # 0.0–1.0;越高 = 更强的编码能力\n```\n\n形状与 OpenRouter 在聊天补全请求体中接受的内容一致。Hermes 原样转发整个 `extra_body`,因此 [openrouter.ai/docs](https://openrouter.ai/docs) 中记录的任何其他 OpenRouter 请求体字段都以相同方式工作。\n\n### 更改视觉模型\n\n使用 GPT-4o 而非 Gemini Flash 进行图像分析:\n\n```yaml\nauxiliary:\n vision:\n model: \"openai/gpt-4o\"\n```\n\n或通过环境变量(在 `~/.hermes/.env` 中):\n\n```bash\nAUXILIARY_VISION_MODEL=openai/gpt-4o\n```\n\n### Provider 选项\n\n这些选项适用于**辅助任务配置**(`auxiliary:`、`compression:`、`fallback_model:`),而非您的主 `model.provider` 设置。\n\n| Provider | 描述 | 要求 |\n|----------|-------------|-------------|\n| `\"auto\"` | 最佳可用(默认)。Vision 尝试 OpenRouter → Nous → Codex。 | — |\n| `\"openrouter\"` | 强制 OpenRouter —— 路由到任何模型(Gemini、GPT-4o、Claude 等) | `OPENROUTER_API_KEY` |\n| `\"nous\"` | 强制 Nous Portal | `hermes auth` |\n| `\"codex\"` | 强制 Codex OAuth(ChatGPT 账户)。支持视觉(gpt-5.3-codex)。 | `hermes model` → Codex |\n| `\"minimax-oauth\"` | 强制 MiniMax OAuth(浏览器登录,无需 API 密钥)。辅助任务使用 MiniMax-M2.7-highspeed。 | `hermes model` → MiniMax (OAuth) |\n| `\"xai-oauth\"` | 强制 xAI Grok OAuth(SuperGrok 或 X Premium+ 订阅者的浏览器登录,无需 API 密钥)。相同的 OAuth token 涵盖聊天、TTS、图像、视频和转录。 | `hermes model` → xAI Grok OAuth (SuperGrok / Premium+) |\n| `\"main\"` | 使用您的活跃自定义/主端点。可以来自 `OPENAI_BASE_URL` + `OPENAI_API_KEY` 或通过 `hermes model` / `config.yaml` 保存的自定义端点。适用于 OpenAI、本地模型或任何 OpenAI 兼容 API。**仅限辅助任务 —— 对 `model.provider` 无效。** | 自定义端点凭据 + 基础 URL |\n\n当您希望附带任务绕过默认路由器时,主 provider 目录中的直接 API 密钥 providers 也在这里工作。配置 `GMI_API_KEY` 后,`gmi` 有效:\n\n```yaml\nauxiliary:\n compression:\n provider: \"gmi\"\n model: \"anthropic/claude-opus-4.6\"\n```\n\n对于 GMI 辅助路由,使用 GMI 的 `/v1/models` 端点返回的确切模型 ID。\n\n### 常见设置\n\n**使用直接自定义端点**(比 `provider: \"main\"` 对本地/自托管 API 更清晰):\n```yaml\nauxiliary:\n vision:\n base_url: \"http://localhost:1234/v1\"\n api_key: \"local-key\"\n model: \"qwen2.5-vl\"\n```\n\n`base_url` 优先于 `provider`,因此这是将辅助任务路由到特定端点的最明确方式。对于直接端点覆盖,Hermes 使用配置的 `api_key` 或回退到 `OPENAI_API_KEY`;它不会为该自定义端点重用 `OPENROUTER_API_KEY`。\n\n**使用 OpenAI API 密钥进行视觉:**\n```yaml\n# 在 ~/.hermes/.env 中:\n# OPENAI_BASE_URL=https://api.openai.com/v1\n# OPENAI_API_KEY=sk-...\n\nauxiliary:\n vision:\n provider: \"main\"\n model: \"gpt-4o\" # 或 \"gpt-4o-mini\" 更便宜\n```\n\n**使用 OpenRouter 进行视觉**(路由到任何模型):\n```yaml\nauxiliary:\n vision:\n provider: \"openrouter\"\n model: \"openai/gpt-4o\" # 或 \"google/gemini-2.5-flash\" 等\n```\n\n**使用 Codex OAuth**(ChatGPT Pro/Plus 账户 —— 无需 API 密钥):\n```yaml\nauxiliary:\n vision:\n provider: \"codex\" # 使用您的 ChatGPT OAuth token\n # 模型默认为 gpt-5.3-codex(支持视觉)\n```\n\n**使用 MiniMax OAuth**(浏览器登录,无需 API 密钥):\n```yaml\nmodel:\n default: MiniMax-M2.7\n provider: minimax-oauth\n base_url: https://api.minimax.io/anthropic\n```\n运行 `hermes model` 并选择 **MiniMax (OAuth)** 自动登录并设置此项。对于中国区域,基础 URL 将是 `https://api.minimaxi.com/anthropic`。完整演练请参阅 [MiniMax OAuth 指南](../guides/minimax-oauth.md)。\n\n**使用本地/自托管模型:**\n```yaml\nauxiliary:\n vision:\n provider: \"main\" # 使用您的活跃自定义端点\n model: \"my-local-model\"\n```\n\n`provider: \"main\"` 使用 Hermes 用于普通聊天的任何 provider —— 无论是命名的自定义 provider(例如 `beans`)、内置 provider(如 `openrouter`)还是旧版 `OPENAI_BASE_URL` 端点。\n\n:::tip\n如果您使用 Codex OAuth 作为主模型 provider,视觉会自动工作 —— 无需额外配置。Codex 包含在视觉的自动检测链中。\n:::\n\n:::warning\n**视觉需要多模态模型。** 如果您设置 `provider: \"main\"`,请确保您的端点支持多模态/视觉 —— 否则图像分析将失败。\n:::\n\n### 环境变量(旧版)\n\n辅助模型也可以通过环境变量配置。但是,`config.yaml` 是首选方法 —— 它更易于管理,并支持所有选项,包括 `base_url` 和 `api_key`。\n\n| 设置 | 环境变量 |\n|---------|---------------------|\n| Vision provider | `AUXILIARY_VISION_PROVIDER` |\n| Vision 模型 | `AUXILIARY_VISION_MODEL` |\n| Vision 端点 | `AUXILIARY_VISION_BASE_URL` |\n| Vision API 密钥 | `AUXILIARY_VISION_API_KEY` |\n| Web 提取 provider | `AUXILIARY_WEB_EXTRACT_PROVIDER` |\n| Web 提取模型 | `AUXILIARY_WEB_EXTRACT_MODEL` |\n| Web 提取端点 | `AUXILIARY_WEB_EXTRACT_BASE_URL` |\n| Web 提取 API 密钥 | `AUXILIARY_WEB_EXTRACT_API_KEY` |\n\n压缩和回退模型设置仅限 config.yaml。\n\n:::tip\n运行 `hermes config` 查看您当前的辅助模型设置。覆盖仅在与默认值不同时显示。\n:::\n\n## 推理努力程度\n\n控制模型在响应前进行多少\"思考\":\n\n```yaml\nagent:\n reasoning_effort: \"\" # 空 = 中等(默认)。选项:none、minimal、low、medium、high、xhigh(最大)\n```\n\n未设置时(默认),推理努力程度默认为\"medium\" —— 适合大多数任务的平衡级别。设置值会覆盖它 —— 更高的推理努力程度在复杂任务上提供更好的结果,但代价是更多 token 和延迟。\n\n您也可以在运行时使用 `/reasoning` 命令更改推理努力程度:\n\n```\n/reasoning # 显示当前努力程度和显示状态\n/reasoning high # 将推理努力程度设为 high\n/reasoning none # 禁用推理\n/reasoning show # 在每次响应上方显示模型思考\n/reasoning hide # 隐藏模型思考\n```\n\n## 工具使用强制\n\n某些模型偶尔会将预期操作描述为文本而不是进行工具调用(\"我会运行测试...\"而不是实际调用终端)。工具使用强制会注入系统提示词指导,引导模型实际调用工具。\n\n```yaml\nagent:\n tool_use_enforcement: \"auto\" # \"auto\" | true | false | [\"model-substring\", ...]\n```\n\n| 值 | 行为 |\n|-------|----------|\n| `\"auto\"`(默认) | 对匹配以下模型启用:`gpt`、`codex`、`gemini`、`gemma`、`grok`。对所有其他模型禁用(Claude、DeepSeek、Qwen 等)。 |\n| `true` | 始终启用,无论模型如何。如果您注意到当前模型描述操作而不是执行操作,请使用此选项。 |\n| `false` | 始终禁用,无论模型如何。 |\n| `[\"gpt\", \"codex\", \"qwen\", \"llama\"]` | 仅当模型名称包含列出的子字符串之一时启用(不区分大小写)。 |\n\n### 注入的内容\n\n启用后,系统提示词中可能会添加三层指导:\n\n1. **通用工具使用强制**(所有匹配模型)—— 指示模型立即进行工具调用而不是描述意图,持续工作直到任务完成,永远不要以未来操作的承诺结束轮次。\n\n2. **OpenAI 执行纪律**(仅限 GPT 和 Codex 模型)—— 针对 GPT 特定失败模式的额外指导:在部分结果上放弃工作、跳过先决条件查找、幻觉而不是使用工具、在未验证的情况下宣布\"完成\"。\n\n3. **Google 操作指导**(仅限 Gemini 和 Gemma 模型)—— 简洁性、绝对路径、并行工具调用和编辑前验证模式。\n\n这些对用户透明,仅影响系统提示词。已经可靠使用工具的模型(如 Claude)不需要此指导,这就是为什么 `\"auto\"` 排除它们。\n\n### 何时开启\n\n如果您使用的模型不在默认自动列表中,并注意到它经常描述它*会*做什么而不是实际去做,请设置 `tool_use_enforcement: true` 或将模型子字符串添加到列表中:\n\n```yaml\nagent:\n tool_use_enforcement: [\"gpt\", \"codex\", \"gemini\", \"grok\", \"my-custom-model\"]\n```\n\n## TTS 配置\n\n```yaml\ntts:\n provider: \"edge\" # \"edge\" | \"elevenlabs\" | \"openai\" | \"minimax\" | \"mistral\" | \"gemini\" | \"xai\" | \"neutts\"\n speed: 1.0 # 全局速度倍数(所有 provider 的回退)\n edge:\n voice: \"en-US-AriaNeural\" # 322 种声音,74 种语言\n speed: 1.0 # 速度倍数(转换为速率百分比,例如 1.5 → +50%)\n elevenlabs:\n voice_id: \"pNInz6obpgDQGcFmaJgB\"\n model_id: \"eleven_multilingual_v2\"\n openai:\n model: \"gpt-4o-mini-tts\"\n voice: \"alloy\" # alloy、echo、fable、onyx、nova、shimmer\n speed: 1.0 # 速度倍数(API 限制为 0.25–4.0)\n base_url: \"https://api.openai.com/v1\" # 覆盖 OpenAI 兼容 TTS 端点\n minimax:\n speed: 1.0 # 语音速度倍数\n # base_url: \"\" # 可选:覆盖 OpenAI 兼容 TTS 端点\n mistral:\n model: \"voxtral-mini-tts-2603\"\n voice_id: \"c69964a6-ab8b-4f8a-9465-ec0925096ec8\" # Paul - Neutral(默认)\n gemini:\n model: \"gemini-2.5-flash-preview-tts\" # 或 gemini-2.5-pro-preview-tts\n voice: \"Kore\" # 30 种预置声音:Zephyr、Puck、Kore、Enceladus 等\n xai:\n voice_id: \"eve\" # xAI TTS 声音\n language: \"en\" # ISO 639-1\n sample_rate: 24000\n bit_rate: 128000 # MP3 比特率\n # base_url: \"https://api.x.ai/v1\"\n neutts:\n ref_audio: ''\n ref_text: ''\n model: neuphonic/neutts-air-q4-gguf\n device: cpu\n```\n\n这控制 `text_to_speech` 工具和语音模式中的口语回复(CLI 中的 `/voice tts` 或消息 gateway)。\n\n**速度回退层次:** provider 特定速度(例如 `tts.edge.speed`)→ 全局 `tts.speed` → `1.0` 默认值。设置全局 `tts.speed` 以在所有 provider 上应用统一速度,或按 provider 覆盖以进行精细控制。\n\n## 显示设置\n\n```yaml\ndisplay:\n tool_progress: all # off | new | all | verbose\n tool_progress_command: false # 在消息 gateway 中启用 /verbose 斜杠命令\n platforms: {} # 每平台显示覆盖(见下文)\n tool_progress_overrides: {} # 已弃用 —— 改用 display.platforms\n interim_assistant_messages: true # Gateway:将自然的轮次中 assistant 更新作为单独消息发送\n skin: default # 内置或自定义 CLI 皮肤(参阅 user-guide/features/skins)\n personality: \"kawaii\" # 旧版外观字段,仍在某些摘要中显示\n compact: false # 紧凑输出模式(减少空白)\n resume_display: full # full(恢复时显示之前的消息)| minimal(仅单行)\n bell_on_complete: false # 当 agent 完成时播放终端铃声(适合长任务)\n show_reasoning: false # 在每次响应上方显示模型推理/思考(用 /reasoning show|hide 切换)\n streaming: false # 将 token 实时流式传输到终端\n show_cost: false # 在 CLI 状态栏中显示估计 $ 成本\n timestamps: false # 为 true 时,在 CLI/TUI 记录中为用户和 assistant 标签添加 [HH:MM] 时间戳前缀\n tool_preview_length: 0 # 工具调用预览的最大字符数(0 = 无限制,显示完整路径/命令)\n runtime_footer: # Gateway:在最终回复中附加运行时上下文页脚\n enabled: false\n fields: [\"model\", \"context_pct\", \"cwd\"]\n file_mutation_verifier: true # 当本轮 write_file/patch 调用失败时附加建议性页脚\n language: en # 静态消息的 UI 语言(审批提示、部分 gateway 回复)。en | zh | zh-hant | ja | de | es | fr | tr | uk | af | ko | it | ga | pt | ru | hu\n```\n\n### 文件变更验证器\n\n当 `display.file_mutation_verifier` 为 `true`(默认)时,每当本轮中 `write_file` 或 `patch` 调用失败且从未被对同一路径的成功写入取代时,Hermes 会在 assistant 的最终响应中附加一行建议。这捕获了\"批量并行补丁,一半静默失败,模型总结成功\"这类过度声明,而无需您在每次编辑后手动运行 `git status`。\n\n示例页脚:\n\n```\n⚠️ File-mutation verifier: 3 file(s) were NOT modified this turn despite any wording above that may suggest otherwise. Run `git status` or `read_file` to confirm.\n • concepts/automatic-organization.md — [patch] Could not find match for old_string\n • concepts/lora.md — [patch] Could not find match for old_string\n • concepts/rag-pipeline.md — [patch] Could not find match for old_string\n```\n\n设置 `file_mutation_verifier: false`(或 `HERMES_FILE_MUTATION_VERIFIER=0`)以禁止页脚。验证器仅在轮次结束时有真实失败未解决时触发 —— 在同一轮次内重试失败补丁并成功的模型不会为该文件触发它。\n\n### 静态消息的 UI 语言\n\n`display.language` 设置翻译一小组静态面向用户的消息 —— CLI 审批提示、少数 gateway 斜杠命令回复(例如重启排空通知、\"审批已过期\"、\"目标已清除\")。它**不**翻译 agent 响应、日志行、工具输出、错误回溯或斜杠命令描述 —— 这些保持英文。如果您希望 agent 本身用另一种语言回复,只需在您的提示词或系统消息中告诉它。\n\n支持的值:`en`(默认)、`zh`(简体中文)、`zh-hant`(繁体中文)、`ja`(日语)、`de`(德语)、`es`(西班牙语)、`fr`(法语)、`tr`(土耳其语)、`uk`(乌克兰语)、`af`(南非荷兰语)、`ko`(韩语)、`it`(意大利语)、`ga`(爱尔兰语)、`pt`(葡萄牙语)、`ru`(俄语)、`hu`(匈牙利语)。未知值回退到英文。\n\n您也可以使用 `HERMES_LANGUAGE` 环境变量按会话设置,它会覆盖配置值。\n\n```yaml\ndisplay:\n language: zh # CLI 审批提示以中文显示\n```\n\n| 模式 | 您看到的内容 |\n|------|-------------|\n| `off` | 静默 —— 仅最终响应 |\n| `new` | 仅在工具更改时显示工具指示器 |\n| `all` | 每次工具调用附带简短预览(默认) |\n| `verbose` | 完整参数、结果和调试日志 |\n\n在 CLI 中,使用 `/verbose` 循环切换这些模式。要在消息平台(Telegram、Discord、Slack 等)中使用 `/verbose`,请在上方的 `display` 部分设置 `tool_progress_command: true`。该命令将循环切换模式并保存到配置。\n\n### 运行时元数据页脚(仅限 gateway)\n\n当 `display.runtime_footer.enabled: true` 时,Hermes 在每个 gateway 轮次的**最终**消息中附加一个小型运行时上下文页脚。目前页脚可显示模型、上下文窗口百分比和当前工作目录。默认关闭;如果您的团队希望每个回复都包含这些来源信息,请按 gateway 选择加入。\n\n```yaml\ndisplay:\n runtime_footer:\n enabled: true\n fields: [\"model\", \"context_pct\", \"cwd\"] # 支持字段:model、context_pct、cwd\n```\n\n`/footer` 斜杠命令在任何会话中运行时切换此功能。\n\n附加到 Telegram/Discord/Slack 回复的示例页脚:\n\n```\n— claude-opus-4.7 · 12 tool calls · 2m 14s · $0.042\n```\n\n只有轮次的**最终**消息获得页脚;中间更新保持干净。\n\n### 每平台进度覆盖\n\n不同平台有不同的详细程度需求。例如,Signal 无法编辑消息,因此每次进度更新都会成为单独的消息 —— 很嘈杂。使用 `display.platforms` 设置每平台模式:\n\n```yaml\ndisplay:\n tool_progress: all # 全局默认\n platforms:\n signal:\n tool_progress: 'off' # 在 Signal 上静默进度\n telegram:\n tool_progress: verbose # 在 Telegram 上详细进度\n slack:\n tool_progress: 'off' # 在共享 Slack 工作区中保持安静\n```\n\n没有覆盖的平台回退到全局 `tool_progress` 值。有效平台键:`telegram`、`discord`、`slack`、`signal`、`whatsapp`、`matrix`、`mattermost`、`email`、`sms`、`homeassistant`、`dingtalk`、`feishu`、`wecom`、`weixin`、`bluebubbles`、`qqbot`。旧版 `display.tool_progress_overrides` 键仍可加载以向后兼容,但已弃用,并在首次加载时迁移到 `display.platforms`。\n\n`interim_assistant_messages` 仅限 gateway。启用后,Hermes 将已完成的轮次中 assistant 更新作为单独的聊天消息发送。这与 `tool_progress` 无关,不需要 gateway 流式传输。\n\n## 隐私\n\n```yaml\nprivacy:\n redact_pii: false # 从 LLM 上下文中删除 PII(仅限 gateway)\n```\n\n当 `redact_pii` 为 `true` 时,gateway 在将系统提示词发送到受支持平台上的 LLM 之前,会从中删除个人身份信息:\n\n| 字段 | 处理方式 |\n|-------|-----------|\n| 电话号码(WhatsApp/Signal 上的用户 ID) | 哈希为 `user_<12-char-sha256>` |\n| 用户 ID | 哈希为 `user_<12-char-sha256>` |\n| 聊天 ID | 数字部分哈希,保留平台前缀(`telegram:<hash>`) |\n| 主频道 ID | 数字部分哈希 |\n| 用户名/昵称 | **不受影响**(用户选择的,公开可见) |\n\n**平台支持:** 删除适用于 WhatsApp、Signal 和 Telegram。Discord 和 Slack 被排除,因为它们的提及系统(`<@user_id>`)需要 LLM 上下文中的真实 ID。\n\n哈希是确定性的 —— 同一用户始终映射到同一哈希,因此模型仍然可以在群聊中区分用户。路由和传递在内部使用原始值。\n\n## 语音转文字(STT)\n\n```yaml\nstt:\n provider: \"local\" # \"local\" | \"groq\" | \"openai\" | \"mistral\"\n local:\n model: \"base\" # tiny、base、small、medium、large-v3\n openai:\n model: \"whisper-1\" # whisper-1 | gpt-4o-mini-transcribe | gpt-4o-transcribe\n # model: \"whisper-1\" # 旧版回退键仍受支持\n```\n\nProvider 行为:\n\n- `local` 使用在您机器上运行的 `faster-whisper`。使用 `pip install faster-whisper` 单独安装。\n- `groq` 使用 Groq 的 Whisper 兼容端点,读取 `GROQ_API_KEY`。\n- `openai` 使用 OpenAI 语音 API,读取 `VOICE_TOOLS_OPENAI_KEY`。\n\n如果请求的 provider 不可用,Hermes 按此顺序自动回退:`local` → `groq` → `openai`。\n\nGroq 和 OpenAI 模型覆盖由环境变量驱动:\n\n```bash\nSTT_GROQ_MODEL=whisper-large-v3-turbo\nSTT_OPENAI_MODEL=whisper-1\nGROQ_BASE_URL=https://api.groq.com/openai/v1\nSTT_OPENAI_BASE_URL=https://api.openai.com/v1\n```\n\n## 语音模式(CLI)\n\n```yaml\nvoice:\n record_key: \"ctrl+b\" # CLI 内的按键通话键\n max_recording_seconds: 120 # 长录音的硬停止\n auto_tts: false # /voice on 时自动启用口语回复\n beep_enabled: true # 在 CLI 语音模式中播放录音开始/停止提示音\n silence_threshold: 200 # 语音检测的 RMS 阈值\n silence_duration: 3.0 # 自动停止前的静默秒数\n```\n\n在 CLI 中使用 `/voice on` 启用麦克风模式,使用 `record_key` 开始/停止录音,使用 `/voice tts` 切换口语回复。端到端设置和平台特定行为请参阅[语音模式](/user-guide/features/voice-mode)。\n\n## 流式传输\n\n将 token 实时流式传输到终端或消息平台,而不是等待完整响应。\n\n### CLI 流式传输\n\n```yaml\ndisplay:\n streaming: true # 实时将 token 流式传输到终端\n show_reasoning: true # 同时流式传输推理/思考 token(可选)\n```\n\n启用后,响应在流式传输框内逐 token 出现。工具调用仍然静默捕获。如果 provider 不支持流式传输,它会自动回退到正常显示。\n\n### Gateway 流式传输(Telegram、Discord、Slack)\n\n```yaml\nstreaming:\n enabled: true # 启用渐进式消息编辑\n transport: edit # \"edit\"(渐进式消息编辑)或 \"off\"\n edit_interval: 0.3 # 消息编辑之间的秒数\n buffer_threshold: 40 # 强制编辑刷新前的字符数\n cursor: \" ▉\" # 流式传输期间显示的光标\n fresh_final_after_seconds: 0 # 预览超过此时间时选择发送新的最终消息(Telegram)\n```\n\n启用后,bot 在第一个 token 时发送消息,然后随着更多 token 到来渐进式编辑它。不支持消息编辑的平台(Signal、Email、Home Assistant)在第一次尝试时自动检测 —— 该会话的流式传输被优雅地禁用,不会产生大量消息。\n\n对于不带渐进式 token 编辑的独立自然轮次中 assistant 更新,请设置 `display.interim_assistant_messages: true`。\n\n**溢出处理:** 如果流式传输的文本超过平台的消息长度限制(约 4096 字符),当前消息被最终化,新消息自动开始。\n\n**新的最终消息(Telegram):** Telegram 的 `editMessageText` 保留原始消息时间戳,因此长时间运行的流式回复即使在完成后也会保留第一个 token 的时间戳。设置 `fresh_final_after_seconds > 0` 可选择将旧预览作为全新的最终消息传递,并尽力删除旧预览。默认值为 `0`,始终就地最终化流式回复,避免某些客户端短暂显示重复消息再删除其中一条。\n\n:::note\n主开关 `streaming.enabled` 默认为 `false`——在你启用之前不会有任何流式传输。启用后,是否流式传输按**平台**决定:Telegram 默认带有 `display.platforms.telegram.streaming: true`(流式传输),Discord 为 `display.platforms.discord.streaming: false`(不流式传输)。因此启用流式传输后,Telegram 开箱即用地流式传输,Discord 在你修改其开关之前仍使用整条消息回复。你可以在仪表盘的 **Channels** 开关中或直接在 `~/.hermes/config.yaml` 中调整这些按平台的开关。\n:::\n\n## 群聊会话隔离\n\n控制共享聊天是每个房间保持一个对话还是每个参与者一个对话:\n\n```yaml\ngroup_sessions_per_user: true # true = 群组/频道中每用户隔离,false = 每个聊天一个共享会话\n```\n\n- `true` 是默认和推荐设置。在 Discord 频道、Telegram 群组、Slack 频道和类似共享上下文中,当平台提供用户 ID 时,每个发送者获得自己的会话。\n- `false` 恢复到旧的共享房间行为。如果您明确希望 Hermes 将频道视为一个协作对话,这可能有用,但这也意味着用户共享上下文、token 成本和中断状态。\n- 私信不受影响。Hermes 仍然像往常一样通过聊天/DM ID 键入 DM。\n- 线程与其父频道保持隔离;使用 `true` 时,每个参与者在线程内也获得自己的会话。\n\n有关行为详情和示例,请参阅[会话](/user-guide/sessions)和 [Discord 指南](/user-guide/messaging/discord)。\n\n## 未授权 DM 行为\n\n控制当未知用户发送私信时 Hermes 的行为:\n\n```yaml\nunauthorized_dm_behavior: pair\n\nwhatsapp:\n unauthorized_dm_behavior: ignore\n```\n\n- `pair` 是默认值。Hermes 拒绝访问,但在 DM 中回复一次性配对码。\n- `ignore` 静默丢弃未授权的 DM。\n- 平台部分覆盖全局默认值,因此您可以在广泛范围内保持配对启用,同时使一个平台更安静。\n\n## 快速命令\n\n定义自定义命令,这些命令要么在不调用 LLM 的情况下运行 shell 命令,要么将一个斜杠命令别名为另一个。Exec 快速命令是零 token 的,对于从消息平台(Telegram、Discord 等)进行快速服务器检查或实用脚本很有用。\n\n```yaml\nquick_commands:\n status:\n type: exec\n command: systemctl status hermes-agent\n disk:\n type: exec\n command: df -h /\n update:\n type: exec\n command: cd ~/.hermes/hermes-agent && git pull && pip install -e .\n gpu:\n type: exec\n command: nvidia-smi --query-gpu=name,utilization.gpu,memory.used,memory.total --format=csv,noheader\n restart:\n type: alias\n target: /gateway restart\n```\n\n用法:在 CLI 或任何消息平台中输入 `/status`、`/disk`、`/update`、`/gpu` 或 `/restart`。`exec` 命令在宿主本地运行并直接返回输出 —— 无 LLM 调用,不消耗 token。`alias` 命令重写为配置的斜杠命令目标。\n\n- **30 秒超时** —— 长时间运行的命令被终止并显示错误消息\n- **优先级** —— 快速命令在技能命令之前检查,因此您可以覆盖技能名称\n- **自动补全** —— 快速命令在调度时解析,不显示在内置斜杠命令自动补全表中\n- **类型** —— 支持的类型为 `exec` 和 `alias`;其他类型显示错误\n- **到处可用** —— CLI、Telegram、Discord、Slack、WhatsApp、Signal、Email、Home Assistant\n\n仅字符串的 prompt 快捷方式不是有效的快速命令。对于可重用的 prompt 工作流,请创建技能或别名到现有斜杠命令。\n\n## 人类延迟\n\n在消息平台中模拟类人响应节奏:\n\n```yaml\nhuman_delay:\n mode: \"off\" # off | natural | custom\n min_ms: 800 # 最小延迟(自定义模式)\n max_ms: 2500 # 最大延迟(自定义模式)\n```\n\n## 代码执行\n\n配置 `execute_code` 工具:\n\n```yaml\ncode_execution:\n mode: project # project(默认)| strict\n timeout: 300 # 最大执行时间(秒)\n max_tool_calls: 50 # 代码执行中的最大工具调用次数\n```\n\n**`mode`** 控制脚本的工作目录和 Python 解释器:\n\n- **`project`**(默认)—— 脚本在会话的工作目录中以活跃 virtualenv/conda 环境的 python 运行。项目依赖(`pandas`、`torch`、项目包)和相对路径(`.env`、`./data.csv`)自然解析,与 `terminal()` 看到的一致。\n- **`strict`** —— 脚本在临时暂存目录中以 `sys.executable`(Hermes 自己的 python)运行。最大可重现性,但项目依赖和相对路径不会解析。\n\n环境清理(删除 `*_API_KEY`、`*_TOKEN`、`*_SECRET`、`*_PASSWORD`、`*_CREDENTIAL`、`*_PASSWD`、`*_AUTH`)和工具白名单在两种模式下完全相同 —— 切换模式不会改变安全态势。\n\n## Web 搜索后端\n\n`web_search` 和 `web_extract` 工具支持五种后端 provider。在 `config.yaml` 中或通过 `hermes tools` 配置后端:\n\n```yaml\nweb:\n backend: firecrawl # firecrawl | searxng | parallel | tavily | exa\n\n # 或使用每功能键混合 provider(例如免费搜索 + 付费提取):\n search_backend: \"searxng\"\n extract_backend: \"firecrawl\"\n```\n\n| 后端 | 环境变量 | 搜索 | 提取 |\n|---------|---------|--------|---------|\n| **Firecrawl**(默认) | `FIRECRAWL_API_KEY` | ✔ | ✔ |\n| **SearXNG** | `SEARXNG_URL` | ✔ | — |\n| **Parallel** | `PARALLEL_API_KEY` | ✔ | ✔ |\n| **Tavily** | `TAVILY_API_KEY` | ✔ | ✔ |\n| **Exa** | `EXA_API_KEY` | ✔ | ✔ |\n\n**后端选择:** 如果未设置 `web.backend`,后端从可用的 API 密钥自动检测。如果仅设置了 `SEARXNG_URL`,使用 SearXNG。如果仅设置了 `EXA_API_KEY`,使用 Exa。如果仅设置了 `TAVILY_API_KEY`,使用 Tavily。如果仅设置了 `PARALLEL_API_KEY`,使用 Parallel。否则 Firecrawl 是默认值。\n\n**SearXNG** 是一个免费、自托管、尊重隐私的元搜索引擎,查询 70+ 个搜索引擎。无需 API 密钥 —— 只需将 `SEARXNG_URL` 设置为您的实例(例如 `http://localhost:8080`)。SearXNG 仅限搜索;`web_extract` 需要单独的提取 provider(设置 `web.extract_backend`)。Docker 设置说明请参阅 [Web 搜索设置指南](/user-guide/features/web-search)。\n\n**自托管 Firecrawl:** 设置 `FIRECRAWL_API_URL` 指向您自己的实例。设置自定义 URL 后,API 密钥变为可选(在服务器上设置 `USE_DB_AUTHENTICATION=***` 以禁用认证)。\n\n**Parallel 搜索模式:** 设置 `PARALLEL_SEARCH_MODE` 控制搜索行为 —— `fast`、`one-shot` 或 `agentic`(默认:`agentic`)。\n\n**Exa:** 在 `~/.hermes/.env` 中设置 `EXA_API_KEY`。支持 `category` 过滤(`company`、`research paper`、`news`、`people`、`personal site`、`pdf`)和域名/日期过滤器。\n\n## 浏览器\n\n配置浏览器自动化行为:\n\n```yaml\nbrowser:\n inactivity_timeout: 120 # 自动关闭空闲会话前的秒数\n command_timeout: 30 # 浏览器命令超时(截图、导航等)(秒)\n record_sessions: false # 自动将浏览器会话录制为 WebM 视频到 ~/.hermes/browser_recordings/\n # 可选 CDP 覆盖 —— 设置后,Hermes 直接附加到您自己的\n # Chromium 系浏览器(通过 /browser connect),而不是启动无头浏览器。\n cdp_url: \"\"\n # 对话框监督器 —— 控制当 CDP 后端附加时(Browserbase、本地 Chromium 系\n # 浏览器通过 /browser connect)如何处理原生 JS 对话框(alert/confirm/prompt)。\n # 在 Camofox 和默认本地 agent 浏览器模式下忽略。\n dialog_policy: must_respond # must_respond | auto_dismiss | auto_accept\n dialog_timeout_s: 300 # must_respond 下的安全自动关闭(秒)\n camofox:\n managed_persistence: false # 为 true 时,Camofox 会话跨重启持久化 cookie/登录\n user_id: \"\" # 可选的外部管理 Camofox userId\n session_key: \"\" # Hermes 创建标签页时发送的可选会话密钥\n adopt_existing_tab: false # 在创建新标签页之前重用此身份的现有标签页\n```\n\n**对话框策略:**\n\n- `must_respond`(默认)—— 捕获对话框,在 `browser_snapshot.pending_dialogs` 中显示,等待 agent 调用 `browser_dialog(action=...)`。在 `dialog_timeout_s` 秒内无响应后,对话框被自动关闭以防止页面的 JS 线程永久停滞。\n- `auto_dismiss` —— 捕获,立即关闭。Agent 仍然在事后的 `browser_snapshot.recent_dialogs` 中看到对话框记录,`closed_by=\"auto_policy\"`。\n- `auto_accept` —— 捕获,立即接受。适用于有激进 `beforeunload` 提示的页面。\n\n完整对话框工作流请参阅[浏览器功能页面](./features/browser.md#browser_dialog)。\n\n浏览器工具集支持多个 provider。有关 Browserbase、Browser Use 和本地 Chromium 系 CDP 设置的详细信息,请参阅[浏览器功能页面](/user-guide/features/browser)。\n\n## 时区\n\n使用 IANA 时区字符串覆盖服务器本地时区。影响日志中的时间戳、cron 调度和系统提示词时间注入。\n\n```yaml\ntimezone: \"America/New_York\" # IANA 时区(默认:\"\" = 服务器本地时间)\n```\n\n支持的值:任何 IANA 时区标识符(例如 `America/New_York`、`Europe/London`、`Asia/Kolkata`、`UTC`)。留空或省略以使用服务器本地时间。\n\n## Discord\n\n为消息 gateway 配置 Discord 特定行为:\n\n```yaml\ndiscord:\n require_mention: true # 在服务器频道中需要 @提及才能响应\n free_response_channels: \"\" # 逗号分隔的频道 ID,bot 在这些频道无需 @提及即可响应\n auto_thread: true # 在频道中 @提及时自动创建线程\n```\n\n- `require_mention` —— 为 `true`(默认)时,bot 仅在服务器频道中被 `@BotName` 提及时响应。DM 始终无需提及即可工作。\n- `free_response_channels` —— 逗号分隔的频道 ID 列表,bot 在这些频道对每条消息响应,无需提及。\n- `auto_thread` —— 为 `true`(默认)时,频道中的提及会自动为对话创建线程,保持频道整洁(类似 Slack 线程)。\n\n## 安全\n\n预执行安全扫描和机密脱敏:\n\n```yaml\nsecurity:\n redact_secrets: false # 在工具输出和日志中脱敏 API 密钥模式(默认关闭)\n tirith_enabled: true # 为终端命令启用 Tirith 安全扫描\n tirith_path: \"tirith\" # tirith 二进制文件路径(默认:$PATH 中的 \"tirith\")\n tirith_timeout: 5 # 等待 tirith 扫描的秒数\n tirith_fail_open: true # 如果 tirith 不可用,允许命令执行\n website_blocklist: # 参见下方网站黑名单部分\n enabled: false\n domains: []\n shared_files: []\n```\n\n- `redact_secrets` —— 为 `true` 时,自动检测并脱敏工具输出中看起来像 API 密钥、token 和密码的模式,然后再进入对话上下文和日志。**默认关闭** —— 如果您经常在工具输出中处理真实凭据并希望有安全网,请启用。显式设置为 `true` 以开启。\n- `tirith_enabled` —— 为 `true` 时,终端命令在执行前由 [Tirith](https://github.com/sheeki03/tirith) 扫描以检测潜在危险操作。\n- `tirith_path` —— tirith 二进制文件的路径。如果 tirith 安装在非标准位置,请设置此项。\n- `tirith_timeout` —— 等待 tirith 扫描的最大秒数。如果扫描超时,命令继续执行。\n- `tirith_fail_open` —— 为 `true`(默认)时,如果 tirith 不可用或失败,允许命令执行。设置为 `false` 以在 tirith 无法验证时阻止命令。\n\n## 网站黑名单\n\n阻止 agent 的 web 和浏览器工具访问特定域名:\n\n```yaml\nsecurity:\n website_blocklist:\n enabled: false # 启用 URL 阻止(默认:false)\n domains: # 被阻止的域名模式列表\n - \"*.internal.company.com\"\n - \"admin.example.com\"\n - \"*.local\"\n shared_files: # 从外部文件加载额外规则\n - \"/etc/hermes/blocked-sites.txt\"\n```\n\n启用后,任何匹配被阻止域名模式的 URL 在 web 或浏览器工具执行之前都会被拒绝。这适用于 `web_search`、`web_extract`、`browser_navigate` 以及任何访问 URL 的工具。\n\n域名规则支持:\n- 精确域名:`admin.example.com`\n- 通配符子域名:`*.internal.company.com`(阻止所有子域名)\n- TLD 通配符:`*.local`\n\n共享文件每行包含一条域名规则(空行和 `#` 注释被忽略)。缺失或不可读的文件记录警告,但不禁用其他 web 工具。\n\n策略缓存 30 秒,因此配置更改无需重启即可快速生效。\n\n## 智能审批\n\n控制 Hermes 如何处理潜在危险命令:\n\n```yaml\napprovals:\n mode: manual # manual | smart | off\n```\n\n| 模式 | 行为 |\n|------|----------|\n| `manual`(默认) | 在执行任何被标记的命令之前提示用户。在 CLI 中显示交互式审批对话框。在消息中排队待处理的审批请求。 |\n| `smart` | 使用辅助 LLM 评估被标记的命令是否真正危险。低风险命令以会话级持久性自动批准。真正有风险的命令升级给用户。 |\n| `off` | 跳过所有审批检查。等同于 `HERMES_YOLO_MODE=true`。**谨慎使用。** |\n\n智能模式对于减少审批疲劳特别有用 —— 它让 agent 在安全操作上更自主地工作,同时仍然捕获真正破坏性的命令。\n\n:::warning\n设置 `approvals.mode: off` 会禁用终端命令的所有安全检查。仅在受信任的沙箱环境中使用。\n:::\n\n## 检查点\n\n破坏性文件操作之前的自动文件系统快照。详情请参阅[检查点与回滚](/user-guide/checkpoints-and-rollback)。\n\n```yaml\ncheckpoints:\n enabled: false # 启用自动检查点(也可:hermes chat --checkpoints)。默认:false(选择加入)。\n max_snapshots: 20 # 每个目录保留的最大检查点数(默认:20)\n```\n\n\n## 委托\n\n为委托工具配置子 agent 行为:\n\n```yaml\ndelegation:\n # model: \"google/gemini-3-flash-preview\" # 覆盖模型(空 = 继承父级)\n # provider: \"openrouter\" # 覆盖 provider(空 = 继承父级)\n # base_url: \"http://localhost:1234/v1\" # 直接 OpenAI 兼容端点(优先于 provider)\n # api_key: \"local-key\" # base_url 的 API 密钥(回退到 OPENAI_API_KEY)\n # api_mode: \"\" # base_url 的线路协议:\"chat_completions\"、\"codex_responses\" 或 \"anthropic_messages\"。空 = 从 URL 自动检测(例如 /anthropic 后缀 → anthropic_messages)。对启发式无法检测的非标准端点显式设置。\n max_concurrent_children: 3 # 每批并行子 agent 数(下限 1,无上限)。也可通过 DELEGATION_MAX_CONCURRENT_CHILDREN 环境变量设置。\n max_spawn_depth: 1 # 委托树深度上限(1-3,截断)。1 = 扁平(默认):父级生成无法委托的叶子。2 = 编排器子级可以生成叶子孙级。3 = 三级。\n orchestrator_enabled: true # 全局终止开关。为 false 时,role=\"orchestrator\" 被忽略,每个子级无论 max_spawn_depth 如何都被强制为叶子。\n```\n\n**子 agent provider:model 覆盖:** 默认情况下,子 agent 继承父 agent 的 provider 和模型。设置 `delegation.provider` 和 `delegation.model` 将子 agent 路由到不同的 provider:model 对 —— 例如,在您的主 agent 运行昂贵推理模型时,为范围较窄的子任务使用便宜/快速的模型。\n\n**直接端点覆盖:** 如果您想要明显的自定义端点路径,请设置 `delegation.base_url`、`delegation.api_key` 和 `delegation.model`。这将子 agent 直接发送到该 OpenAI 兼容端点,并优先于 `delegation.provider`。如果省略 `delegation.api_key`,Hermes 仅回退到 `OPENAI_API_KEY`。\n\n**线路协议(`api_mode`):** Hermes 从 `delegation.base_url` 自动检测线路协议(例如以 `/anthropic` 结尾的路径 → `anthropic_messages`;Codex/原生 Anthropic/Kimi-coding 主机名保留其现有检测)。对于启发式无法分类的端点 —— 例如 Azure AI Foundry、MiniMax、Zhipu GLM 或前置 Anthropic 形状后端的 LiteLLM 代理 —— 请将 `delegation.api_mode` 显式设置为 `chat_completions`、`codex_responses` 或 `anthropic_messages` 之一。留空(默认)以保持自动检测。\n\n委托 provider 使用与 CLI/gateway 启动相同的凭据解析。所有配置的 provider 均受支持:`openrouter`、`nous`、`copilot`、`zai`、`kimi-coding`、`minimax`、`minimax-cn`。设置 provider 时,系统自动解析正确的基础 URL、API 密钥和 API 模式 —— 无需手动凭据连接。\n\n**优先级:** 配置中的 `delegation.base_url` → 配置中的 `delegation.provider` → 父 provider(继承)。配置中的 `delegation.model` → 父模型(继承)。仅设置 `model` 而不设置 `provider` 仅更改模型名称,同时保留父级凭据(适用于在同一 provider(如 OpenRouter)内切换模型)。\n\n**宽度和深度:** `max_concurrent_children` 限制每批并行运行的子 agent 数量(默认 `3`,下限 1,无上限)。也可通过 `DELEGATION_MAX_CONCURRENT_CHILDREN` 环境变量设置。当模型提交的 `tasks` 数组超过上限时,`delegate_task` 返回工具错误解释限制,而不是静默截断。`max_spawn_depth` 控制委托树深度(截断到 1-3)。在默认 `1` 时,委托是扁平的:子级无法生成孙级,传递 `role=\"orchestrator\"` 静默降级为 `leaf`。提升到 `2` 使编排器子级可以生成叶子孙级;`3` 用于三级树。Agent 通过 `role=\"orchestrator\"` 按调用选择编排;`orchestrator_enabled: false` 强制每个子级回到叶子,无论如何。成本呈乘法增长 —— 在 `max_spawn_depth: 3` 和 `max_concurrent_children: 3` 时,树可以达到 3×3×3 = 27 个并发叶子 agent。使用模式请参阅[子 Agent 委托 → 深度限制和嵌套编排](features/delegation.md#depth-limit-and-nested-orchestration)。\n\n## 澄清\n\n配置澄清提示行为:\n\n```yaml\nclarify:\n timeout: 120 # 等待用户澄清响应的秒数\n```\n\n## 上下文文件(SOUL.md、AGENTS.md)\n\nHermes 使用两种不同的上下文范围:\n\n| 文件 | 用途 | 范围 |\n|------|---------|-------|\n| `SOUL.md` | **主要 agent 身份** —— 定义 agent 是谁(系统提示词第 #1 槽位) | `~/.hermes/SOUL.md` 或 `$HERMES_HOME/SOUL.md` |\n| `.hermes.md` / `HERMES.md` | 项目特定指令(最高优先级) | 向上走到 git 根目录 |\n| `AGENTS.md` | 项目特定指令、编码规范 | 递归目录遍历 |\n| `CLAUDE.md` | Claude Code 上下文文件(也会检测) | 仅工作目录 |\n| `.cursorrules` | Cursor IDE 规则(也会检测) | 仅工作目录 |\n| `.cursor/rules/*.mdc` | Cursor 规则文件(也会检测) | 仅工作目录 |\n\n- **SOUL.md** 是 agent 的主要身份。它占据系统提示词的第 #1 槽位,完全替换内置的默认身份。编辑它以完全自定义 agent 是谁。\n- 如果 SOUL.md 缺失、为空或无法加载,Hermes 回退到内置默认身份。\n- **项目上下文文件使用优先级系统** —— 仅加载一种类型(第一个匹配优先):`.hermes.md` → `AGENTS.md` → `CLAUDE.md` → `.cursorrules`。SOUL.md 始终独立加载。\n- **AGENTS.md** 是分层的:如果子目录也有 AGENTS.md,所有都会合并。\n- 如果 `SOUL.md` 不存在,Hermes 会自动生成默认的 `SOUL.md`。\n- 所有加载的上下文文件上限为 20,000 字符,并进行智能截断。\n\n另请参阅:\n- [个性与 SOUL.md](/user-guide/features/personality)\n- [上下文文件](/user-guide/features/context-files)\n\n## 工作目录\n\n| 上下文 | 默认值 |\n|---------|---------|\n| **CLI(`hermes`)** | 运行命令的当前目录 |\n| **消息 gateway** | 主目录 `~`(用 `MESSAGING_CWD` 覆盖) |\n| **Docker / Singularity / Modal / SSH** | 容器或远程机器内用户的主目录 |\n\n覆盖工作目录:\n```bash\n# 在 ~/.hermes/.env 或 ~/.hermes/config.yaml 中:\nMESSAGING_CWD=/home/myuser/projects # Gateway 会话\nTERMINAL_CWD=/workspace # 所有终端会话\n```\n"}, "f5c3fc319cde79aea3a904a7afe9c311e6fc79dc": {"t": 1785768005.4091556, "n": 55633, "h": "119da03c9354674a", "c": "---\nsidebar_position: 2\ntitle: \"配置\"\ndescription: \"配置 Hermes Agent — config.yaml、providers、模型、API 密钥等\"\n---\n\n# 配置\n\n所有设置均存储在 `~/.hermes/` 目录中,便于访问。\n\n## 目录结构\n\n```text\n~/.hermes/\n├── config.yaml # 设置(模型、终端、TTS、压缩等)\n├── .env # API 密钥和机密\n├── auth.json # OAuth provider 凭据(Nous Portal 等)\n├── SOUL.md # 主要 agent 身份(系统提示词第 #1 槽位)\n├── memories/ # 持久记忆(MEMORY.md、USER.md)\n├── skills/ # Agent 创建的技能(通过 skill_manage 工具管理)\n├── cron/ # 定时任务\n├── sessions/ # Gateway 会话\n└── logs/ # 日志(errors.log、gateway.log — 机密自动脱敏)\n```\n\n## 管理配置\n\n```bash\nhermes config # 查看当前配置\nhermes config edit # 在编辑器中打开 config.yaml\nhermes config set KEY VAL # 设置特定值\nhermes config check # 检查缺失选项(更新后使用)\nhermes config migrate # 交互式添加缺失选项\n\n# 示例:\nhermes config set model anthropic/claude-opus-4\nhermes config set terminal.backend docker\nhermes config set OPENROUTER_API_KEY sk-or-... # 保存到 .env\n```\n\n:::tip\n`hermes config set` 命令会自动将值路由到正确的文件 —— API 密钥保存到 `.env`,其他所有内容保存到 `config.yaml`。\n:::\n\n## 配置优先级\n\n设置按以下顺序解析(优先级从高到低):\n\n1. **CLI 参数** —— 例如 `hermes chat --model anthropic/claude-sonnet-4`(单次调用覆盖)\n2. **`~/.hermes/config.yaml`** —— 所有非机密设置的主配置文件\n3. **`~/.hermes/.env`** —— 环境变量的回退;机密(API 密钥、token、密码)**必须**放这里\n4. **内置默认值** —— 未设置任何内容时的硬编码安全默认值\n\n:::info 经验法则\n机密(API 密钥、bot token、密码)放入 `.env`。其他所有内容(模型、终端后端、压缩设置、内存限制、工具集)放入 `config.yaml`。当两者都设置时,`config.yaml` 对非机密设置优先。\n:::\n\n## 环境变量替换\n\n可以在 `config.yaml` 中使用 `${VAR_NAME}` 语法引用环境变量:\n\n```yaml\nauxiliary:\n vision:\n api_key: ${GOOGLE_API_KEY}\n base_url: ${CUSTOM_VISION_URL}\n\ndelegation:\n api_key: ${DELEGATION_KEY}\n```\n\n单个值中可以有多个引用:`url: \"${HOST}:${PORT}\"`。如果引用的变量未设置,占位符将保持原样(`${UNDEFINED_VAR}` 保持不变)。仅支持 `${VAR}` 语法 —— 裸 `$VAR` 不会被展开。\n\n关于 AI provider 设置(OpenRouter、Anthropic、Copilot、自定义端点、自托管 LLM、回退模型等),请参阅 [AI Providers](/integrations/providers)。\n\n### Provider 超时\n\n可以为 provider 设置 `providers.<id>.request_timeout_seconds` 作为全局请求超时,以及 `providers.<id>.models.<model>.timeout_seconds` 作为特定模型的覆盖值。适用于每种传输方式(OpenAI-wire、原生 Anthropic、Anthropic 兼容)上的主轮次客户端、回退链、凭据轮换后的重建,以及(对于 OpenAI-wire)每请求超时 kwarg —— 因此配置值优先于旧版 `HERMES_API_TIMEOUT` 环境变量。\n\n还可以设置 `providers.<id>.stale_timeout_seconds` 用于非流式陈旧调用检测器,以及 `providers.<id>.models.<model>.stale_timeout_seconds` 作为特定模型的覆盖值。此值优先于旧版 `HERMES_API_CALL_STALE_TIMEOUT` 环境变量。\n\n不设置这些值将保持旧版默认值(`HERMES_API_TIMEOUT=1800`s、`HERMES_API_CALL_STALE_TIMEOUT=300`s、原生 Anthropic 900s)。目前不适用于 AWS Bedrock(`bedrock_converse` 和 AnthropicBedrock SDK 路径均使用 boto3 及其自身的超时配置)。请参阅 [`cli-config.yaml.example`](https://github.com/NousResearch/hermes-agent/blob/main/cli-config.yaml.example) 中的注释示例。\n\n## 终端后端配置\n\nHermes 支持六种终端后端。每种后端决定 agent 的 shell 命令实际在哪里执行 —— 本地机器、Docker 容器、通过 SSH 的远程服务器、Modal 云沙箱(直接或通过 Nous 托管的 gateway)、Daytona 工作区,或 Singularity/Apptainer 容器。\n\n```yaml\nterminal:\n backend: local # local | docker | ssh | modal | daytona | singularity\n cwd: \".\" # Gateway/cron 工作目录(CLI 始终使用启动目录)\n timeout: 180 # 每条命令的超时时间(秒)\n env_passthrough: [] # 转发到沙箱执行的环境变量名(terminal + execute_code)\n singularity_image: \"docker://nikolaik/python-nodejs:python3.11-nodejs20\" # Singularity 后端的容器镜像\n modal_image: \"nikolaik/python-nodejs:python3.11-nodejs20\" # Modal 后端的容器镜像\n daytona_image: \"nikolaik/python-nodejs:python3.11-nodejs20\" # Daytona 后端的容器镜像\n```\n\n对于 Modal 和 Daytona 等云沙箱,`container_persistent: true` 表示 Hermes 将尝试在沙箱重建后保留文件系统状态。这并不保证相同的活跃沙箱、PID 空间或后台进程之后仍在运行。\n\n### 后端概览\n\n| 后端 | 命令运行位置 | 隔离性 | 最适合 |\n|---------|-------------------|-----------|----------|\n| **local** | 直接在您的机器上 | 无 | 开发、个人使用 |\n| **docker** | 单个持久 Docker 容器(跨会话、`/new`、子 agent 共享) | 完全(命名空间、cap-drop) | 安全沙箱、CI/CD |\n| **ssh** | 通过 SSH 的远程服务器 | 网络边界 | 远程开发、强大硬件 |\n| **modal** | Modal 云沙箱 | 完全(云 VM) | 临时云计算、评估 |\n| **daytona** | Daytona 工作区 | 完全(云容器) | 托管云开发环境 |\n| **singularity** | Singularity/Apptainer 容器 | 命名空间(--containall) | HPC 集群、共享机器 |\n\n### Local 后端\n\n默认后端。命令直接在您的机器上运行,无隔离。无需特殊设置。\n\n```yaml\nterminal:\n backend: local\n```\n\n:::warning\nAgent 拥有与您的用户账户相同的文件系统访问权限。使用 `hermes tools` 禁用不需要的工具,或切换到 Docker 进行沙箱隔离。\n:::\n\n### Docker 后端\n\n在具有安全加固的 Docker 容器内运行命令(所有权限已删除、无权限提升、PID 限制)。\n\n**单个持久容器,而非每条命令一个容器。** Hermes 在首次使用时启动一个长期运行的容器,并通过 `docker exec` 将每个终端、文件和 `execute_code` 调用路由到同一容器中 —— 跨会话、`/new`、`/reset` 和 `delegate_task` 子 agent,贯穿 Hermes 进程的整个生命周期。工作目录更改、已安装的包以及 `/workspace` 中的文件会从一次工具调用延续到下一次,就像本地 shell 一样。容器在关闭时停止并删除。详情请参阅下方的**容器生命周期**。\n\n```yaml\nterminal:\n backend: docker\n docker_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n docker_mount_cwd_to_workspace: false # 将启动目录挂载到 /workspace\n docker_run_as_host_user: false # 参见下方\"以宿主用户身份运行容器\"\n docker_forward_env: # 转发到容器的环境变量\n - \"GITHUB_TOKEN\"\n docker_volumes: # 宿主目录挂载\n - \"/home/user/projects:/workspace/projects\"\n - \"/home/user/data:/data:ro\" # :ro 表示只读\n docker_extra_args: # 附加到 `docker run` 的额外标志\n - \"--gpus=all\"\n - \"--network=host\"\n\n # 资源限制\n container_cpu: 1 # CPU 核心数(0 = 不限制)\n container_memory: 5120 # MB(0 = 不限制)\n container_disk: 51200 # MB(需要 XFS+pquota 上的 overlay2)\n container_persistent: true # 跨会话持久化 /workspace 和 /root\n```\n\n**`terminal.docker_extra_args`**(也可通过 `TERMINAL_DOCKER_EXTRA_ARGS='[\"--gpus=all\"]'` 覆盖)允许传递 Hermes 未作为一级键公开的任意 `docker run` 标志 —— `--gpus`、`--network`、`--add-host`、替代 `--security-opt` 覆盖等。每个条目必须是字符串;该列表最后附加到组装好的 `docker run` 调用中,因此可以在需要时覆盖 Hermes 的默认值。请谨慎使用 —— 与沙箱加固(权限删除、`--user`、workspace 绑定挂载)冲突的标志将悄然削弱隔离性。\n\n**要求:** 已安装并运行 Docker Desktop 或 Docker Engine。Hermes 会探测 `$PATH` 以及常见的 macOS 安装位置(`/usr/local/bin/docker`、`/opt/homebrew/bin/docker`、Docker Desktop 应用包)。开箱即用支持 Podman:设置 `HERMES_DOCKER_BINARY=podman`(或完整路径)以在两者都安装时强制使用它。\n\n**容器生命周期:** Hermes 为每个终端和文件工具调用重用单个长期运行的容器(`docker run -d ... sleep 2h`),跨会话、`/new`、`/reset` 和 `delegate_task` 子 agent,贯穿 Hermes 进程的整个生命周期。命令通过带登录 shell 的 `docker exec` 运行,因此工作目录更改、已安装的包以及 `/workspace` 中的文件都会从一次工具调用延续到下一次。容器在 Hermes 关闭时(或空闲清理回收时)停止并删除。\n\n通过 `delegate_task(tasks=[...])` 生成的并行子 agent 共享这一个容器 —— 并发的 `cd`、环境变量修改以及对同一路径的写入会发生冲突。如果子 agent 需要隔离的沙箱,必须通过 `register_task_env_overrides()` 注册每任务镜像覆盖,RL 和基准测试环境(TerminalBench2、HermesSweEnv 等)会自动为其每任务 Docker 镜像执行此操作。\n\n**安全加固:**\n- `--cap-drop ALL`,仅添加回 `DAC_OVERRIDE`、`CHOWN`、`FOWNER`\n- `--security-opt no-new-privileges`\n- `--pids-limit 256`\n- `/tmp`(512MB)、`/var/tmp`(256MB)、`/run`(64MB)的大小限制 tmpfs\n\n**凭据转发:** `docker_forward_env` 中列出的环境变量首先从您的 shell 环境解析,然后回退到 `~/.hermes/.env`。技能也可以声明 `required_environment_variables`,这些变量会自动合并。\n\n### SSH 后端\n\n通过 SSH 在远程服务器上运行命令。使用 ControlMaster 进行连接复用(5 分钟空闲保活)。默认启用持久 shell —— 状态(cwd、环境变量)在命令之间保持。\n\n```yaml\nterminal:\n backend: ssh\n persistent_shell: true # 保持长期运行的 bash 会话(默认:true)\n```\n\n**必需的环境变量:**\n\n```bash\nTERMINAL_SSH_HOST=my-server.example.com\nTERMINAL_SSH_USER=ubuntu\n```\n\n**可选:**\n\n| 变量 | 默认值 | 描述 |\n|----------|---------|-------------|\n| `TERMINAL_SSH_PORT` | `22` | SSH 端口 |\n| `TERMINAL_SSH_KEY` | (系统默认) | SSH 私钥路径 |\n| `TERMINAL_SSH_PERSISTENT` | `true` | 启用持久 shell |\n\n**工作原理:** 使用 `BatchMode=yes` 和 `StrictHostKeyChecking=accept-new` 在初始化时连接。持久 shell 在远程主机上保持单个 `bash -l` 进程存活,通过临时文件进行通信。需要 `stdin_data` 或 `sudo` 的命令会自动回退到单次模式。\n\n### Modal 后端\n\n在 [Modal](https://modal.com) 云沙箱中运行命令。每个任务获得一个具有可配置 CPU、内存和磁盘的隔离 VM。文件系统可以跨会话快照/恢复。\n\n```yaml\nterminal:\n backend: modal\n container_cpu: 1 # CPU 核心数\n container_memory: 5120 # MB(5GB)\n container_disk: 51200 # MB(50GB)\n container_persistent: true # 快照/恢复文件系统\n```\n\n**必需:** `MODAL_TOKEN_ID` + `MODAL_TOKEN_SECRET` 环境变量,或 `~/.modal.toml` 配置文件。\n\n**持久化:** 启用后,沙箱文件系统在清理时快照,并在下次会话时恢复。快照在 `~/.hermes/modal_snapshots.json` 中跟踪。这保留文件系统状态,而非活跃进程、PID 空间或后台任务。\n\n**凭据文件:** 自动从 `~/.hermes/` 挂载(OAuth token 等),并在每条命令前同步。\n\n### Daytona 后端\n\n在 [Daytona](https://daytona.io) 托管工作区中运行命令。支持停止/恢复以实现持久化。\n\n```yaml\nterminal:\n backend: daytona\n container_cpu: 1 # CPU 核心数\n container_memory: 5120 # MB → 转换为 GiB\n container_disk: 10240 # MB → 转换为 GiB(最大 10 GiB)\n container_persistent: true # 停止/恢复而非删除\n```\n\n**必需:** `DAYTONA_API_KEY` 环境变量。\n\n**持久化:** 启用后,沙箱在清理时停止(而非删除),并在下次会话时恢复。沙箱名称遵循 `hermes-{task_id}` 模式。\n\n**磁盘限制:** Daytona 强制执行 10 GiB 最大值。超过此值的请求将被截断并发出警告。\n\n### Singularity/Apptainer 后端\n\n在 [Singularity/Apptainer](https://apptainer.org) 容器中运行命令。专为 Docker 不可用的 HPC 集群和共享机器设计。\n\n```yaml\nterminal:\n backend: singularity\n singularity_image: \"docker://nikolaik/python-nodejs:python3.11-nodejs20\"\n container_cpu: 1 # CPU 核心数\n container_memory: 5120 # MB\n container_persistent: true # 可写覆盖层跨会话持久化\n```\n\n**要求:** `$PATH` 中有 `apptainer` 或 `singularity` 二进制文件。\n\n**镜像处理:** Docker URL(`docker://...`)自动转换为 SIF 文件并缓存。现有 `.sif` 文件直接使用。\n\n**临时目录:** 按顺序解析:`TERMINAL_SCRATCH_DIR` → `TERMINAL_SANDBOX_DIR/singularity` → `/scratch/$USER/hermes-agent`(HPC 惯例)→ `~/.hermes/sandboxes/singularity`。\n\n**隔离:** 使用 `--containall --no-home` 实现完全命名空间隔离,不挂载宿主 home 目录。\n\n### 常见终端后端问题\n\n如果终端命令立即失败或终端工具报告为已禁用:\n\n- **Local** —— 无特殊要求。入门时最安全的默认选项。\n- **Docker** —— 运行 `docker version` 验证 Docker 是否正常工作。如果失败,修复 Docker 或执行 `hermes config set terminal.backend local`。\n- **SSH** —— `TERMINAL_SSH_HOST` 和 `TERMINAL_SSH_USER` 都必须设置。如果缺少任一项,Hermes 会记录清晰的错误。\n- **Modal** —— 需要 `MODAL_TOKEN_ID` 环境变量或 `~/.modal.toml`。运行 `hermes doctor` 检查。\n- **Daytona** —— 需要 `DAYTONA_API_KEY`。Daytona SDK 处理服务器 URL 配置。\n- **Singularity** —— 需要 `$PATH` 中有 `apptainer` 或 `singularity`。HPC 集群上常见。\n\n如有疑问,将 `terminal.backend` 设回 `local` 并首先验证命令在那里运行。\n\n### 拆卸时远程到宿主文件同步\n\n对于 **SSH**、**Modal** 和 **Daytona** 后端(agent 的工作树位于与运行 Hermes 的宿主不同的机器上),Hermes 跟踪 agent 在远程沙箱中触及的文件,并在会话拆卸/沙箱清理时,将修改的文件**同步回宿主**,存放在 `~/.hermes/cache/remote-syncs/<session-id>/` 下。\n\n- 触发时机:会话关闭、`/new`、`/reset`、gateway 消息超时、子 agent 使用远程后端时 `delegate_task` 子 agent 完成。\n- 覆盖 agent 修改的整个树,而不仅仅是它明确打开的文件。添加、编辑和删除都会被捕获。\n- 远程沙箱可能在您查找时已被拆除;本地 `~/.hermes/cache/remote-syncs/…` 副本是 agent 更改内容的权威记录。\n- 大型二进制输出(模型检查点、原始数据集)按大小限制 —— 同步跳过超过 `file_sync_max_mb`(默认 `100`)的文件。如果您期望更大的工件返回,请调高该值。\n\n```yaml\nterminal:\n file_sync_max_mb: 100 # 默认 —— 同步最大 100 MB 的文件\n file_sync_enabled: true # 默认 —— 设为 false 可完全跳过同步\n```\n\n这是从会话结束后被销毁的临时云沙箱中恢复结果的方式,无需告诉 agent 显式地 `scp` 或 `modal volume put` 每个工件。\n\n### Docker 卷挂载\n\n使用 Docker 后端时,`docker_volumes` 允许您与容器共享宿主目录。每个条目使用标准 Docker `-v` 语法:`host_path:container_path[:options]`。\n\n```yaml\nterminal:\n backend: docker\n docker_volumes:\n - \"/home/user/projects:/workspace/projects\" # 读写(默认)\n - \"/home/user/datasets:/data:ro\" # 只读\n - \"/home/user/.hermes/cache/documents:/output\" # Gateway 可见的导出\n```\n\n适用于:\n- **向 agent 提供文件**(数据集、配置、参考代码)\n- **从 agent 接收文件**(生成的代码、报告、导出)\n- **共享工作区**,您和 agent 都访问相同的文件\n\n如果您使用消息 gateway 并希望 agent 通过 `MEDIA:/...` 发送生成的文件,建议使用专用的宿主可见导出挂载,例如 `/home/user/.hermes/cache/documents:/output`。\n\n- 在 Docker 中将文件写入 `/output/...`\n- 在 `MEDIA:` 中发出**宿主路径**,例如:`MEDIA:/home/user/.hermes/cache/documents/report.txt`\n- **不要**发出 `/workspace/...` 或 `/output/...`,除非该确切路径在宿主上对 gateway 进程也存在\n\n:::warning\nYAML 重复键会静默覆盖之前的键。如果您已有 `docker_volumes:` 块,请将新挂载合并到同一列表中,而不是在文件后面再添加一个 `docker_volumes:` 键。\n:::\n\n也可以通过环境变量设置:`TERMINAL_DOCKER_VOLUMES='[\"/host:/container\"]'`(JSON 数组)。\n\n### Docker 凭据转发\n\n默认情况下,Docker 终端会话不继承任意宿主凭据。如果您需要在容器内使用特定 token,请将其添加到 `terminal.docker_forward_env`。\n\n```yaml\nterminal:\n backend: docker\n docker_forward_env:\n - \"GITHUB_TOKEN\"\n - \"NPM_TOKEN\"\n```\n\nHermes 首先从您当前的 shell 解析每个列出的变量,然后回退到通过 `hermes config set` 保存的 `~/.hermes/.env`。\n\n:::warning\n`docker_forward_env` 中列出的任何内容都会对容器内运行的命令可见。只转发您愿意暴露给终端会话的凭据。\n:::\n\n### 以宿主用户身份运行容器\n\n默认情况下,Docker 容器以 `root`(UID 0)身份运行。在 `/workspace` 或其他绑定挂载中创建的文件在宿主上归 root 所有,因此会话结束后您必须 `sudo chown` 才能从宿主编辑器编辑它们。`terminal.docker_run_as_host_user` 标志解决了这个问题:\n\n```yaml\nterminal:\n backend: docker\n docker_run_as_host_user: true # 默认:false\n```\n\n启用后,Hermes 将 `--user $(id -u):$(id -g)` 附加到 `docker run` 命令,使写入绑定挂载目录(`/workspace`、`/root`、`docker_volumes` 中的任何内容)的文件归您的宿主用户所有,而非 root。权衡:容器将无法再 `apt install` 或写入 `/root/.npm` 等 root 拥有的路径 —— 如果您同时需要这两者,请使用 `HOME` 归非 root 用户所有的基础镜像(或在镜像构建时添加所需工具)。\n\n保持 `false`(默认)以获得向后兼容的行为。当您的工作流主要是\"编辑挂载的宿主文件\"且厌倦了 `sudo chown -R` 时,请开启此选项。\n\n### 可选:将启动目录挂载到 `/workspace`\n\nDocker 沙箱默认保持隔离。Hermes **不会**将您当前的宿主工作目录传入容器,除非您明确选择加入。\n\n在 `config.yaml` 中启用:\n\n```yaml\nterminal:\n backend: docker\n docker_mount_cwd_to_workspace: true\n```\n\n启用后:\n- 如果您从 `~/projects/my-app` 启动 Hermes,该宿主目录将绑定挂载到 `/workspace`\n- Docker 后端从 `/workspace` 开始\n- 文件工具和终端命令都能看到相同的挂载项目\n\n禁用时,`/workspace` 保持沙箱所有,除非您通过 `docker_volumes` 显式挂载内容。\n\n安全权衡:\n- `false` 保留沙箱边界\n- `true` 使沙箱直接访问您启动 Hermes 的目录\n\n仅在您有意希望容器处理实时宿主文件时才选择加入。\n\n### 持久 Shell\n\n默认情况下,每条终端命令在其自己的子进程中运行 —— 工作目录、环境变量和 shell 变量在命令之间重置。启用**持久 shell** 后,单个长期运行的 bash 进程在 `execute()` 调用之间保持存活,使状态在命令之间保持。\n\n这对 **SSH 后端**最有用,它还消除了每条命令的连接开销。持久 shell **对 SSH 默认启用**,对本地后端禁用。\n\n```yaml\nterminal:\n persistent_shell: true # 默认 —— 为 SSH 启用持久 shell\n```\n\n禁用:\n\n```bash\nhermes config set terminal.persistent_shell false\n```\n\n**跨命令保持的内容:**\n- 工作目录(`cd /tmp` 对下一条命令生效)\n- 导出的环境变量(`export FOO=bar`)\n- Shell 变量(`MY_VAR=hello`)\n\n**优先级:**\n\n| 级别 | 变量 | 默认值 |\n|-------|----------|---------|\n| 配置 | `terminal.persistent_shell` | `true` |\n| SSH 覆盖 | `TERMINAL_SSH_PERSISTENT` | 遵循配置 |\n| Local 覆盖 | `TERMINAL_LOCAL_PERSISTENT` | `false` |\n\n每个后端的环境变量具有最高优先级。如果您也想在本地后端使用持久 shell:\n\n```bash\nexport TERMINAL_LOCAL_PERSISTENT=true\n```\n\n:::note\n需要 `stdin_data` 或 sudo 的命令会自动回退到单次模式,因为持久 shell 的 stdin 已被 IPC 协议占用。\n:::\n\n有关每个后端的详细信息,请参阅[代码执行](features/code-execution.md)和 [README 的终端部分](features/tools.md)。\n\n## 技能设置\n\n技能可以通过其 SKILL.md frontmatter 声明自己的配置设置。这些是非机密值(路径、偏好、域设置),存储在 `config.yaml` 的 `skills.config` 命名空间下。\n\n```yaml\nskills:\n config:\n myplugin:\n path: ~/myplugin-data # 示例 —— 每个技能定义自己的键\n```\n\n**技能设置的工作原理:**\n\n- `hermes config migrate` 扫描所有已启用的技能,找到未配置的设置,并提供提示\n- `hermes config show` 在\"技能设置\"下显示所有技能设置及其所属技能\n- 技能加载时,其解析的配置值会自动注入到技能上下文中\n\n**手动设置值:**\n\n```bash\nhermes config set skills.config.myplugin.path ~/myplugin-data\n```\n\n有关在您自己的技能中声明配置设置的详细信息,请参阅[创建技能 — 配置设置](/developer-guide/creating-skills#config-settings-configyaml)。\n\n### Agent 创建技能写入的守卫\n\n当 agent 使用 `skill_manage` 创建、编辑、修补或删除技能时,Hermes 可以选择扫描新/更新的内容以查找危险关键字模式(凭据收集、明显的 prompt 注入、数据外泄指令)。扫描器**默认关闭** —— 合法触及 `~/.ssh/` 或提及 `$OPENAI_API_KEY` 的真实 agent 工作流触发启发式规则过于频繁。如果您希望扫描器在 agent 的技能写入落地前提示您,请重新开启:\n\n```yaml\nskills:\n guard_agent_created: true # 默认:false\n```\n\n开启后,任何被标记的 `skill_manage` 写入都会以审批提示的形式出现,并附带扫描器的理由。接受的写入落地;拒绝的写入向 agent 返回解释性错误。\n\n## 内存配置\n\n```yaml\nmemory:\n memory_enabled: true\n user_profile_enabled: true\n memory_char_limit: 2200 # ~800 tokens\n user_char_limit: 1375 # ~500 tokens\n```\n\n## 文件读取安全\n\n控制单次 `read_file` 调用可以返回多少内容。超过限制的读取将被拒绝,并向 agent 返回错误,提示使用 `offset` 和 `limit` 读取较小范围。这可以防止单次读取压缩的 JS 包或大型数据文件时淹没上下文窗口。\n\n```yaml\nfile_read_max_chars: 100000 # 默认 —— ~25-35K tokens\n```\n\n如果您使用具有大上下文窗口的模型并经常读取大文件,请调高此值。对于小上下文模型,请降低以保持读取高效:\n\n```yaml\n# 大上下文模型(200K+)\nfile_read_max_chars: 200000\n\n# 小型本地模型(16K 上下文)\nfile_read_max_chars: 30000\n```\n\nAgent 还会自动去重文件读取 —— 如果同一文件区域被读取两次且文件未更改,则返回轻量级存根而不是重新发送内容。这在上下文压缩后重置,以便 agent 在内容被摘要后可以重新读取文件。\n\n## 工具输出截断限制\n\n三个相关的上限控制工具在 Hermes 截断之前可以返回多少原始输出:\n\n```yaml\ntool_output:\n max_bytes: 50000 # 终端输出上限(字符)\n max_lines: 2000 # read_file 分页上限\n max_line_length: 2000 # read_file 行号视图中的每行上限\n```\n\n- **`max_bytes`** —— 当 `terminal` 命令产生超过此字符数的合并 stdout/stderr 时,Hermes 保留前 40% 和后 60%,并在中间插入 `[OUTPUT TRUNCATED]` 通知。默认 `50000`(典型分词器约 12-15K tokens)。\n- **`max_lines`** —— 单次 `read_file` 调用的 `limit` 参数上限。超过此值的请求将被截断,以防单次读取淹没上下文窗口。默认 `2000`。\n- **`max_line_length`** —— `read_file` 发出行号视图时应用的每行上限。超过此长度的行将被截断为此字符数,后跟 `... [truncated]`。默认 `2000`。\n\n对于具有大上下文窗口且每次调用可以承受更多原始输出的模型,请调高限制。对于小上下文模型,请降低以保持工具结果紧凑:\n\n```yaml\n# 大上下文模型(200K+)\ntool_output:\n max_bytes: 150000\n max_lines: 5000\n\n# 小型本地模型(16K 上下文)\ntool_output:\n max_bytes: 20000\n max_lines: 500\n```\n\n## 全局工具集禁用\n\n要在 CLI 和每个 gateway 平台上统一禁用特定工具集,请在 `agent.disabled_toolsets` 下列出其名称:\n\n```yaml\nagent:\n disabled_toolsets:\n - memory # 隐藏内存工具 + MEMORY_GUIDANCE 注入\n - web # 任何地方都不使用 web_search / web_extract\n```\n\n这在每个平台的工具配置(由 `hermes tools` 写入的 `platform_toolsets`)**之后**应用,因此此处列出的工具集始终被删除 —— 即使平台的已保存配置仍然列出它。当您希望有一个\"到处关闭 X\"的单一开关而不是编辑 `hermes tools` UI 中的 15+ 个平台行时,请使用此选项。\n\n留空列表或省略键不会产生任何效果。\n\n## Git Worktree 隔离\n\n启用隔离的 git worktree,以便在同一仓库上并行运行多个 agent:\n\n```yaml\nworktree: true # 始终创建 worktree(与 hermes -w 相同)\n# worktree: false # 默认 —— 仅在传递 -w 标志时\n```\n\n启用后,每个 CLI 会话在 `.worktrees/` 下创建一个带有自己分支的新 worktree。Agent 可以编辑文件、提交、推送和创建 PR,而不会相互干扰。干净的 worktree 在退出时删除;脏的 worktree 保留以供手动恢复。\n\n您还可以通过仓库根目录中的 `.worktreeinclude` 列出要复制到 worktree 的 gitignore 文件:\n\n```\n# .worktreeinclude\n.env\n.venv/\nnode_modules/\n```\n\n## 上下文压缩\n\nHermes 自动压缩长对话以保持在模型的上下文窗口内。压缩摘要器是一个单独的 LLM 调用 —— 您可以将其指向任何 provider 或端点。\n\n所有压缩设置都在 `config.yaml` 中(无环境变量)。\n\n### 完整参考\n\n```yaml\ncompression:\n enabled: true # 开启/关闭压缩\n threshold: 0.50 # 在上下文限制的此百分比时压缩\n target_ratio: 0.20 # 保留为最近尾部的阈值分数\n protect_last_n: 20 # 保持未压缩的最少最近消息数\n hygiene_hard_message_limit: 400 # Gateway 安全阀 —— 见下文\n\n# 摘要模型/provider 在 auxiliary: 下配置:\nauxiliary:\n compression:\n model: \"\" # 空 = 使用主聊天模型。覆盖为例如 \"google/gemini-3-flash-preview\" 以获得更便宜/更快的压缩。\n provider: \"auto\" # Provider:\"auto\"、\"openrouter\"、\"nous\"、\"codex\"、\"main\" 等\n base_url: null # 自定义 OpenAI 兼容端点(覆盖 provider)\n```\n\n:::info 旧版配置迁移\n带有 `compression.summary_model`、`compression.summary_provider` 和 `compression.summary_base_url` 的旧版配置在首次加载时自动迁移到 `auxiliary.compression.*`(配置版本 17)。无需手动操作。\n:::\n\n`hygiene_hard_message_limit` 是仅限 gateway 的**预压缩安全阀**。拥有数千条消息的失控会话可能在正常的上下文百分比阈值触发之前就达到模型上下文限制;当消息数超过此上限时,Hermes 强制压缩,无论 token 使用情况如何。默认 `400` —— 对于非常长的会话正常的平台,请调高;要强制更积极的压缩,请降低。在运行中的 gateway 上编辑此值将在下一条消息时生效(见下文)。\n\n:::tip Gateway 热重载压缩和上下文长度\n从最近的版本开始,在运行中的 gateway 上编辑 `config.yaml` 中的 `model.context_length` 或任何 `compression.*` 键将在下一条消息时生效 —— 无需 gateway 重启、`/reset` 或会话轮换。缓存的 agent 签名包含这些键,因此 gateway 在检测到更改时会透明地重建 agent。API 密钥和工具/技能配置仍需要通常的重载路径。\n:::\n\n### 常见设置\n\n**默认(自动检测)—— 无需配置:**\n```yaml\ncompression:\n enabled: true\n threshold: 0.50\n```\n使用您的主 provider 和主模型。如果您希望在比主聊天模型更便宜的模型上进行压缩,请覆盖每任务(例如 `auxiliary.compression.provider: openrouter` + `model: google/gemini-2.5-flash`)。\n\n**强制特定 provider**(基于 OAuth 或 API 密钥):\n```yaml\nauxiliary:\n compression:\n provider: nous\n model: gemini-3-flash\n```\n适用于任何 provider:`nous`、`openrouter`、`codex`、`anthropic`、`main` 等。\n\n**自定义端点**(自托管、Ollama、zai、DeepSeek 等):\n```yaml\nauxiliary:\n compression:\n model: glm-4.7\n base_url: https://api.z.ai/api/coding/paas/v4\n```\n指向自定义 OpenAI 兼容端点。使用 `OPENAI_API_KEY` 进行认证。\n\n### 三个旋钮的交互方式\n\n| `auxiliary.compression.provider` | `auxiliary.compression.base_url` | 结果 |\n|---------------------|---------------------|--------|\n| `auto`(默认) | 未设置 | 自动检测最佳可用 provider |\n| `nous` / `openrouter` / 等 | 未设置 | 强制使用该 provider,使用其认证 |\n| 任意 | 已设置 | 直接使用自定义端点(忽略 provider) |\n\n:::warning 摘要模型上下文长度要求\n摘要模型**必须**具有至少与您的主 agent 模型一样大的上下文窗口。压缩器将对话的完整中间部分发送给摘要模型 —— 如果该模型的上下文窗口小于主模型的,摘要调用将因上下文长度错误而失败。发生这种情况时,中间轮次将**在没有摘要的情况下被丢弃**,静默丢失对话上下文。如果您覆盖模型,请验证其上下文长度满足或超过您的主模型。\n:::\n\n## 上下文引擎\n\n上下文引擎控制在接近模型 token 限制时如何管理对话。内置的 `compressor` 引擎使用有损摘要(参见[上下文压缩](/developer-guide/context-compression-and-caching))。插件引擎可以用替代策略替换它。\n\n```yaml\ncontext:\n engine: \"compressor\" # 默认 —— 内置有损摘要\n```\n\n使用插件引擎(例如,用于无损上下文管理的 LCM):\n\n```yaml\ncontext:\n engine: \"lcm\" # 必须与插件名称匹配\n```\n\n插件引擎**永远不会自动激活** —— 您必须将 `context.engine` 显式设置为插件名称。可用引擎可以通过 `hermes plugins` → Provider Plugins → Context Engine 浏览和选择。\n\n有关内存插件的类似单选系统,请参阅[内存 Providers](/user-guide/features/memory-providers)。\n\n## 迭代预算压力\n\n当 agent 在处理具有许多工具调用的复杂任务时,它可能会在没有意识到预算不足的情况下耗尽其迭代预算(默认:90 轮)。预算压力会在模型接近限制时自动发出警告:\n\n| 阈值 | 级别 | 模型看到的内容 |\n|-----------|-------|---------------------|\n| **70%** | 注意 | `[BUDGET: 63/90. 27 iterations left. Start consolidating.]` |\n| **90%** | 警告 | `[BUDGET WARNING: 81/90. Only 9 left. Respond NOW.]` |\n\n警告注入到最后一个工具结果的 JSON 中(作为 `_budget_warning` 字段),而不是作为单独的消息 —— 这保留了 prompt 缓存,不会破坏对话结构。\n\n```yaml\nagent:\n max_turns: 90 # 每次对话轮次的最大迭代次数(默认:90)\n api_max_retries: 3 # 回退启动前每个 provider 的重试次数(默认:3)\n```\n\n预算压力默认启用。Agent 自然地将警告视为工具结果的一部分,鼓励它在耗尽迭代之前整合工作并提供响应。\n\n当迭代预算完全耗尽时,CLI 向用户显示通知:`⚠ Iteration budget reached (90/90) — response may be incomplete`。如果预算在活跃工作期间耗尽,agent 会在停止前生成已完成内容的摘要。\n\n`agent.api_max_retries` 控制 Hermes 在回退 provider 切换启动**之前**对瞬时错误(速率限制、连接断开、5xx)重试 provider API 调用的次数。默认为 `3` —— 总共四次尝试。如果您配置了[回退 providers](/user-guide/features/fallback-providers) 并希望更快地故障转移,请将其降至 `0`,这样主 provider 上的第一个瞬时错误会立即切换到回退,而不是对不稳定的端点进行重试。\n\n### API 超时\n\nHermes 对流式传输有单独的超时层,以及用于非流式调用的陈旧检测器。陈旧检测器仅在您将其保留为隐式默认值时才会自动调整本地 provider。\n\n| 超时 | 默认值 | 本地 providers | 配置/环境变量 |\n|---------|---------|----------------|--------------|\n| Socket 读取超时 | 120s | 自动提升至 1800s | `HERMES_STREAM_READ_TIMEOUT` |\n| 陈旧流检测 | 180s | 自动禁用 | `HERMES_STREAM_STALE_TIMEOUT` |\n| 陈旧非流检测 | 300s | 保持隐式时自动禁用 | `providers.<id>.stale_timeout_seconds` 或 `HERMES_API_CALL_STALE_TIMEOUT` |\n| API 调用(非流式) | 1800s | 不变 | `providers.<id>.request_timeout_seconds` / `timeout_seconds` 或 `HERMES_API_TIMEOUT` |\n\n**Socket 读取超时**控制 httpx 等待 provider 下一个数据块的时间。本地 LLM 在大上下文上预填充可能需要几分钟才能产生第一个 token,因此当 Hermes 检测到本地端点时,会将此值提升至 30 分钟。如果您显式设置 `HERMES_STREAM_READ_TIMEOUT`,无论端点检测如何,始终使用该值。\n\n**陈旧流检测**终止接收 SSE 保活 ping 但没有实际内容的连接。对于本地 providers,这完全禁用,因为它们在预填充期间不发送保活 ping。\n\n**陈旧非流检测**终止长时间没有响应的非流式调用。默认情况下,Hermes 在本地端点上禁用此功能,以避免长时间预填充期间的误报。如果您显式设置 `providers.<id>.stale_timeout_seconds`、`providers.<id>.models.<model>.stale_timeout_seconds` 或 `HERMES_API_CALL_STALE_TIMEOUT`,即使在本地端点上也会遵守该显式值。\n\n## 上下文压力警告\n\n与迭代预算压力分开,上下文压力跟踪对话距**压缩阈值**有多近 —— 即上下文压缩触发以摘要旧消息的点。这有助于您和 agent 了解对话何时变长。\n\n| 进度 | 级别 | 发生的事情 |\n|----------|-------|-------------|\n| **≥ 60%** 到阈值 | 信息 | CLI 显示青色进度条;gateway 发送信息通知 |\n| **≥ 85%** 到阈值 | 警告 | CLI 显示粗体黄色进度条;gateway 警告压缩即将发生 |\n\n在 CLI 中,上下文压力在工具输出流中显示为进度条:\n\n```\n ◐ context ████████████░░░░░░░░ 62% to compaction 48k threshold (50%) · approaching compaction\n```\n\n在消息平台上,发送纯文本通知:\n\n```\n◐ Context: ████████████░░░░░░░░ 62% to compaction (threshold: 50% of window).\n```\n\n如果自动压缩被禁用,警告会告诉您上下文可能被截断。\n\n上下文压力是自动的 —— 无需配置。它纯粹作为面向用户的通知触发,不修改消息流或向模型上下文注入任何内容。\n\n## 凭据池策略\n\n当您为同一 provider 拥有多个 API 密钥或 OAuth token 时,配置轮换策略:\n\n```yaml\ncredential_pool_strategies:\n openrouter: round_robin # 均匀循环使用密钥\n anthropic: least_used # 始终选择使用最少的密钥\n```\n\n选项:`fill_first`(默认)、`round_robin`、`least_used`、`random`。完整文档请参阅[凭据池](/user-guide/features/credential-pools)。\n\n## Prompt 缓存\n\n当活跃 provider 支持时,Hermes 自动开启跨会话 prompt 缓存 —— 无需用户配置。\n\n对于**原生 Anthropic**、**OpenRouter** 和 **Nous Portal** 上的 Claude,Hermes 在系统提示词和技能块上附加带有 1 小时 TTL(`ttl: \"1h\"`)的 `cache_control` 断点。在新鲜的一小时内首次发送时按完整输入费率计费;同一小时内任何会话的后续发送以折扣缓存读取费率从缓存中提取。这意味着系统提示词、加载的技能内容以及任何长上下文包含的早期部分在第一个小时内跨 `hermes` 会话和分叉子 agent 被重用。\n\nQwen Cloud(阿里巴巴 DashScope)上游将缓存 TTL 限制为 5 分钟,因此 Hermes 在那里使用 5 分钟断点 TTL。其他通过第三方的 Claude 路径(AWS Bedrock、Azure Foundry)回退到 provider 自己的缓存默认值。xAI Grok 使用单独的会话固定对话 ID 机制 —— 参阅 [xAI prompt 缓存](/integrations/providers#xai-grok--responses-api--prompt-caching)。\n\n不存在禁用此功能的旋钮 —— 缓存始终开启,即使在单轮对话中也能节省费用,因为仅系统提示词就占输入 token 数的相当大比例。\n\n## 辅助模型\n\nHermes 使用\"辅助\"模型处理图像分析、网页摘要、浏览器截图分析、会话标题生成和上下文压缩等附带任务。默认情况下(`auxiliary.*.provider: \"auto\"`),Hermes 将每个辅助任务路由到您的**主聊天模型** —— 与您在 `hermes model` 中选择的相同 provider/模型。您无需配置任何内容即可开始,但请注意,在昂贵的推理模型(Opus、MiniMax M2.7 等)上,辅助任务会增加显著成本。如果您希望无论主模型如何都使用便宜且快速的附带任务,请显式设置 `auxiliary.<task>.provider` 和 `auxiliary.<task>.model`(例如,在 OpenRouter 上使用 Gemini Flash 进行视觉和网页提取)。\n\n:::note 为什么 \"auto\" 使用您的主模型\n早期版本将聚合器用户(OpenRouter、Nous Portal)分流到便宜的 provider 端默认值。这令人惊讶 —— 付费购买聚合器订阅的用户会看到不同的模型处理其辅助流量。`auto` 现在对所有人使用主模型,`config.yaml` 中的每任务覆盖仍然优先(见下方[完整辅助配置参考](#full-auxiliary-config-reference))。\n:::\n\n### 交互式配置辅助模型\n\n无需手动编辑 YAML,运行 `hermes model` 并从菜单中选择**\"配置辅助模型\"**。您将获得交互式的每任务选择器:\n\n```\n$ hermes model\n→ Configure auxiliary models\n\n[ ] vision currently: auto / main model\n[ ] web_extract currently: auto / main model\n[ ] title_generation currently: openrouter / google/gemini-3-flash-preview\n[ ] compression currently: auto / main model\n[ ] approval currently: auto / main model\n[ ] triage_specifier currently: auto / main model\n[ ] kanban_decomposer currently: auto / main model\n[ ] profile_describer currently: auto / main model\n```\n\n选择任务,选择 provider(OAuth 流程打开浏览器;API 密钥 provider 提示输入),选择模型。更改持久化到 `config.yaml` 中的 `auxiliary.<task>.*`。与主模型选择器相同的机制 —— 无需学习额外语法。\n\n### 视频教程\n\n<div style={{position: 'relative', width: '100%', aspectRatio: '16 / 9', marginBottom: '1.5rem'}}>\n <iframe\n src=\"https://www.youtube.com/embed/NoF-YajElIM\"\n title=\"Hermes Agent — Auxiliary Models Tutorial\"\n style={{position: 'absolute', top: 0, left: 0, width: '100%', height: '100%', border: 0}}\n allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\"\n allowFullScreen\n />\n</div>\n\n### 通用配置模式\n\nHermes 中的每个模型槽位 —— 辅助任务、压缩、回退 —— 使用相同的三个旋钮:\n\n| 键 | 作用 | 默认值 |\n|-----|-------------|---------|\n| `provider` | 用于认证和路由的 provider | `\"auto\"` |\n| `model` | 请求的模型 | provider 的默认值 |\n| `base_url` | 自定义 OpenAI 兼容端点(覆盖 provider) | 未设置 |\n\n当设置 `base_url` 时,Hermes 忽略 provider 并直接调用该端点(使用 `api_key` 或 `OPENAI_API_KEY` 进行认证)。当仅设置 `provider` 时,Hermes 使用该 provider 的内置认证和基础 URL。\n\n辅助任务的可用 providers:`auto`、`main`,以及[provider 注册表](/reference/environment-variables)中的任何 provider —— `openrouter`、`nous`、`openai-codex`、`copilot`、`copilot-acp`、`anthropic`、`gemini`、`google-gemini-cli`、`qwen-oauth`、`zai`、`kimi-coding`、`kimi-coding-cn`、`minimax`、`minimax-cn`、`minimax-oauth`、`deepseek`、`nvidia`、`xai`、`xai-oauth`、`ollama-cloud`、`alibaba`、`bedrock`、`huggingface`、`arcee`、`xiaomi`、`kilocode`、`opencode-zen`、`opencode-go`、`azure-foundry` —— 或您 `custom_providers` 列表中任何命名的自定义 provider(例如 `provider: \"beans\"`)。\n\n:::tip MiniMax OAuth\n`minimax-oauth` 通过浏览器 OAuth 登录(无需 API 密钥)。运行 `hermes model` 并选择 **MiniMax (OAuth)** 进行认证。辅助任务自动使用 `MiniMax-M2.7-highspeed`。参阅 [MiniMax OAuth 指南](../guides/minimax-oauth.md)。\n:::\n\n:::tip xAI Grok OAuth\n`xai-oauth` 通过浏览器 OAuth 为 SuperGrok 和 X Premium+ 订阅者登录(无需 API 密钥)。运行 `hermes model` 并选择 **xAI Grok OAuth (SuperGrok / Premium+)** 进行认证。相同的 OAuth token 可重用于每个直接到 xAI 的接口(聊天、辅助任务、TTS、图像生成、视频生成、转录)。参阅 [xAI Grok OAuth 指南](../guides/xai-grok-oauth.md),如果 Hermes 在远程主机上,请参阅 [SSH/远程主机上的 OAuth](../guides/oauth-over-ssh.md)。\n:::\n\n:::warning `\"main\"` 仅用于辅助任务\n`\"main\"` provider 选项表示\"使用我的主 agent 使用的任何 provider\" —— 它仅在 `auxiliary:`、`compression:` 和 `fallback_model:` 配置中有效。它**不是**顶级 `model.provider` 设置的有效值。如果您使用自定义 OpenAI 兼容端点,请在 `model:` 部分设置 `provider: custom`。所有主模型 provider 选项请参阅 [AI Providers](/integrations/providers)。\n:::\n\n### 完整辅助配置参考\n\n```yaml\nauxiliary:\n # 图像分析(vision_analyze 工具 + 浏览器截图)\n vision:\n provider: \"auto\" # \"auto\"、\"openrouter\"、\"nous\"、\"codex\"、\"main\" 等\n model: \"\" # 例如 \"openai/gpt-4o\"、\"google/gemini-2.5-flash\"\n base_url: \"\" # 自定义 OpenAI 兼容端点(覆盖 provider)\n api_key: \"\" # base_url 的 API 密钥(回退到 OPENAI_API_KEY)\n timeout: 120 # 秒 —— LLM API 调用超时;视觉负载需要宽裕的超时\n download_timeout: 30 # 秒 —— 图像 HTTP 下载;慢速连接请增加\n\n # 网页摘要 + 浏览器页面文本提取\n web_extract:\n provider: \"auto\"\n model: \"\" # 例如 \"google/gemini-2.5-flash\"\n base_url: \"\"\n api_key: \"\"\n timeout: 360 # 秒(6 分钟)—— 每次尝试的 LLM 摘要\n\n # 危险命令审批分类器\n approval:\n provider: \"auto\"\n model: \"\"\n base_url: \"\"\n api_key: \"\"\n timeout: 30 # 秒\n\n # 上下文压缩超时(与 compression.* 配置分开)\n compression:\n timeout: 120 # 秒 —— 压缩摘要长对话,需要更多时间\n\n # 技能中心 —— 技能匹配和搜索\n skills_hub:\n provider: \"auto\"\n model: \"\"\n base_url: \"\"\n api_key: \"\"\n timeout: 30\n\n # MCP 工具调度\n mcp:\n provider: \"auto\"\n model: \"\"\n base_url: \"\"\n api_key: \"\"\n timeout: 30\n\n # Kanban 分类规格说明器 —— `hermes kanban specify <id>`(或\n # 仪表板上 Triage 列卡片的 ✨ Specify 按钮)使用此\n # 槽位将单行描述扩展为具体规格并将\n # 任务提升到 `todo`。便宜快速的模型在这里效果很好;规格扩展\n # 很短,不需要推理深度。\n triage_specifier:\n provider: \"auto\"\n model: \"\"\n base_url: \"\"\n api_key: \"\"\n timeout: 120\n```\n\n:::tip\n每个辅助任务都有可配置的 `timeout`(秒)。默认值:vision 120s、web_extract 360s、approval 30s、compression 120s。如果您为辅助任务使用慢速本地模型,请增加这些值。Vision 还有单独的 `download_timeout`(默认 30s)用于 HTTP 图像下载 —— 对于慢速连接或自托管图像服务器,请增加此值。\n:::\n\n:::info\n上下文压缩有自己的 `compression:` 块用于阈值,以及 `auxiliary.compression:` 块用于模型/provider 设置 —— 参阅上方的[上下文压缩](#context-compression)。回退模型使用 `fallback_model:` 块 —— 参阅[回退模型](/integrations/providers#fallback-model)。三者都遵循相同的 provider/model/base_url 模式。\n:::\n\n### OpenRouter 路由和辅助任务的 Pareto Code\n\n当辅助任务解析到 OpenRouter(显式或通过 `provider: \"main\"` 而您的主 agent 在 OpenRouter 上)时,主 agent 的 `provider_routing` 和 `openrouter.min_coding_score` 设置**不会传播** —— 按设计,每个辅助任务是独立的。要为特定辅助任务设置 OpenRouter provider 偏好或使用 [Pareto Code 路由器](/integrations/providers#openrouter-pareto-code-router),请通过 `extra_body` 按任务设置:\n\n```yaml\nauxiliary:\n compression:\n provider: openrouter\n model: openrouter/pareto-code # 为此任务使用 Pareto Code 路由器\n extra_body:\n provider: # OpenRouter provider 路由偏好\n order: [anthropic, google] # 按此顺序尝试这些 providers\n sort: throughput # 或 \"price\" | \"latency\"\n # only: [anthropic] # 限制到特定 provider\n # ignore: [deepinfra] # 排除特定 providers\n plugins: # OpenRouter Pareto Code 路由器旋钮\n - id: pareto-router\n min_coding_score: 0.5 # 0.0–1.0;越高 = 更强的编码能力\n```\n\n形状与 OpenRouter 在聊天补全请求体中接受的内容一致。Hermes 原样转发整个 `extra_body`,因此 [openrouter.ai/docs](https://openrouter.ai/docs) 中记录的任何其他 OpenRouter 请求体字段都以相同方式工作。\n\n### 更改视觉模型\n\n使用 GPT-4o 而非 Gemini Flash 进行图像分析:\n\n```yaml\nauxiliary:\n vision:\n model: \"openai/gpt-4o\"\n```\n\n或通过环境变量(在 `~/.hermes/.env` 中):\n\n```bash\nAUXILIARY_VISION_MODEL=openai/gpt-4o\n```\n\n### Provider 选项\n\n这些选项适用于**辅助任务配置**(`auxiliary:`、`compression:`、`fallback_model:`),而非您的主 `model.provider` 设置。\n\n| Provider | 描述 | 要求 |\n|----------|-------------|-------------|\n| `\"auto\"` | 最佳可用(默认)。Vision 尝试 OpenRouter → Nous → Codex。 | — |\n| `\"openrouter\"` | 强制 OpenRouter —— 路由到任何模型(Gemini、GPT-4o、Claude 等) | `OPENROUTER_API_KEY` |\n| `\"nous\"` | 强制 Nous Portal | `hermes auth` |\n| `\"codex\"` | 强制 Codex OAuth(ChatGPT 账户)。支持视觉(gpt-5.3-codex)。 | `hermes model` → Codex |\n| `\"minimax-oauth\"` | 强制 MiniMax OAuth(浏览器登录,无需 API 密钥)。辅助任务使用 MiniMax-M2.7-highspeed。 | `hermes model` → MiniMax (OAuth) |\n| `\"xai-oauth\"` | 强制 xAI Grok OAuth(SuperGrok 或 X Premium+ 订阅者的浏览器登录,无需 API 密钥)。相同的 OAuth token 涵盖聊天、TTS、图像、视频和转录。 | `hermes model` → xAI Grok OAuth (SuperGrok / Premium+) |\n| `\"main\"` | 使用您的活跃自定义/主端点。可以来自 `OPENAI_BASE_URL` + `OPENAI_API_KEY` 或通过 `hermes model` / `config.yaml` 保存的自定义端点。适用于 OpenAI、本地模型或任何 OpenAI 兼容 API。**仅限辅助任务 —— 对 `model.provider` 无效。** | 自定义端点凭据 + 基础 URL |\n\n当您希望附带任务绕过默认路由器时,主 provider 目录中的直接 API 密钥 providers 也在这里工作。配置 `GMI_API_KEY` 后,`gmi` 有效:\n\n```yaml\nauxiliary:\n compression:\n provider: \"gmi\"\n model: \"anthropic/claude-opus-4.6\"\n```\n\n对于 GMI 辅助路由,使用 GMI 的 `/v1/models` 端点返回的确切模型 ID。\n\n### 常见设置\n\n**使用直接自定义端点**(比 `provider: \"main\"` 对本地/自托管 API 更清晰):\n```yaml\nauxiliary:\n vision:\n base_url: \"http://localhost:1234/v1\"\n api_key: \"local-key\"\n model: \"qwen2.5-vl\"\n```\n\n`base_url` 优先于 `provider`,因此这是将辅助任务路由到特定端点的最明确方式。对于直接端点覆盖,Hermes 使用配置的 `api_key` 或回退到 `OPENAI_API_KEY`;它不会为该自定义端点重用 `OPENROUTER_API_KEY`。\n\n**使用 OpenAI API 密钥进行视觉:**\n```yaml\n# 在 ~/.hermes/.env 中:\n# OPENAI_BASE_URL=https://api.openai.com/v1\n# OPENAI_API_KEY=sk-...\n\nauxiliary:\n vision:\n provider: \"main\"\n model: \"gpt-4o\" # 或 \"gpt-4o-mini\" 更便宜\n```\n\n**使用 OpenRouter 进行视觉**(路由到任何模型):\n```yaml\nauxiliary:\n vision:\n provider: \"openrouter\"\n model: \"openai/gpt-4o\" # 或 \"google/gemini-2.5-flash\" 等\n```\n\n**使用 Codex OAuth**(ChatGPT Pro/Plus 账户 —— 无需 API 密钥):\n```yaml\nauxiliary:\n vision:\n provider: \"codex\" # 使用您的 ChatGPT OAuth token\n # 模型默认为 gpt-5.3-codex(支持视觉)\n```\n\n**使用 MiniMax OAuth**(浏览器登录,无需 API 密钥):\n```yaml\nmodel:\n default: MiniMax-M2.7\n provider: minimax-oauth\n base_url: https://api.minimax.io/anthropic\n```\n运行 `hermes model` 并选择 **MiniMax (OAuth)** 自动登录并设置此项。对于中国区域,基础 URL 将是 `https://api.minimaxi.com/anthropic`。完整演练请参阅 [MiniMax OAuth 指南](../guides/minimax-oauth.md)。\n\n**使用本地/自托管模型:**\n```yaml\nauxiliary:\n vision:\n provider: \"main\" # 使用您的活跃自定义端点\n model: \"my-local-model\"\n```\n\n`provider: \"main\"` 使用 Hermes 用于普通聊天的任何 provider —— 无论是命名的自定义 provider(例如 `beans`)、内置 provider(如 `openrouter`)还是旧版 `OPENAI_BASE_URL` 端点。\n\n:::tip\n如果您使用 Codex OAuth 作为主模型 provider,视觉会自动工作 —— 无需额外配置。Codex 包含在视觉的自动检测链中。\n:::\n\n:::warning\n**视觉需要多模态模型。** 如果您设置 `provider: \"main\"`,请确保您的端点支持多模态/视觉 —— 否则图像分析将失败。\n:::\n\n### 环境变量(旧版)\n\n辅助模型也可以通过环境变量配置。但是,`config.yaml` 是首选方法 —— 它更易于管理,并支持所有选项,包括 `base_url` 和 `api_key`。\n\n| 设置 | 环境变量 |\n|---------|---------------------|\n| Vision provider | `AUXILIARY_VISION_PROVIDER` |\n| Vision 模型 | `AUXILIARY_VISION_MODEL` |\n| Vision 端点 | `AUXILIARY_VISION_BASE_URL` |\n| Vision API 密钥 | `AUXILIARY_VISION_API_KEY` |\n| Web 提取 provider | `AUXILIARY_WEB_EXTRACT_PROVIDER` |\n| Web 提取模型 | `AUXILIARY_WEB_EXTRACT_MODEL` |\n| Web 提取端点 | `AUXILIARY_WEB_EXTRACT_BASE_URL` |\n| Web 提取 API 密钥 | `AUXILIARY_WEB_EXTRACT_API_KEY` |\n\n压缩和回退模型设置仅限 config.yaml。\n\n:::tip\n运行 `hermes config` 查看您当前的辅助模型设置。覆盖仅在与默认值不同时显示。\n:::\n\n## 推理努力程度\n\n控制模型在响应前进行多少\"思考\":\n\n```yaml\nagent:\n reasoning_effort: \"\" # 空 = 中等(默认)。选项:none、minimal、low、medium、high、xhigh(最大)\n```\n\n未设置时(默认),推理努力程度默认为\"medium\" —— 适合大多数任务的平衡级别。设置值会覆盖它 —— 更高的推理努力程度在复杂任务上提供更好的结果,但代价是更多 token 和延迟。\n\n您也可以在运行时使用 `/reasoning` 命令更改推理努力程度:\n\n```\n/reasoning # 显示当前努力程度和显示状态\n/reasoning high # 将推理努力程度设为 high\n/reasoning none # 禁用推理\n/reasoning show # 在每次响应上方显示模型思考\n/reasoning hide # 隐藏模型思考\n```\n\n## 工具使用强制\n\n某些模型偶尔会将预期操作描述为文本而不是进行工具调用(\"我会运行测试...\"而不是实际调用终端)。工具使用强制会注入系统提示词指导,引导模型实际调用工具。\n\n```yaml\nagent:\n tool_use_enforcement: \"auto\" # \"auto\" | true | false | [\"model-substring\", ...]\n```\n\n| 值 | 行为 |\n|-------|----------|\n| `\"auto\"`(默认) | 对匹配以下模型启用:`gpt`、`codex`、`gemini`、`gemma`、`grok`。对所有其他模型禁用(Claude、DeepSeek、Qwen 等)。 |\n| `true` | 始终启用,无论模型如何。如果您注意到当前模型描述操作而不是执行操作,请使用此选项。 |\n| `false` | 始终禁用,无论模型如何。 |\n| `[\"gpt\", \"codex\", \"qwen\", \"llama\"]` | 仅当模型名称包含列出的子字符串之一时启用(不区分大小写)。 |\n\n### 注入的内容\n\n启用后,系统提示词中可能会添加三层指导:\n\n1. **通用工具使用强制**(所有匹配模型)—— 指示模型立即进行工具调用而不是描述意图,持续工作直到任务完成,永远不要以未来操作的承诺结束轮次。\n\n2. **OpenAI 执行纪律**(仅限 GPT 和 Codex 模型)—— 针对 GPT 特定失败模式的额外指导:在部分结果上放弃工作、跳过先决条件查找、幻觉而不是使用工具、在未验证的情况下宣布\"完成\"。\n\n3. **Google 操作指导**(仅限 Gemini 和 Gemma 模型)—— 简洁性、绝对路径、并行工具调用和编辑前验证模式。\n\n这些对用户透明,仅影响系统提示词。已经可靠使用工具的模型(如 Claude)不需要此指导,这就是为什么 `\"auto\"` 排除它们。\n\n### 何时开启\n\n如果您使用的模型不在默认自动列表中,并注意到它经常描述它*会*做什么而不是实际去做,请设置 `tool_use_enforcement: true` 或将模型子字符串添加到列表中:\n\n```yaml\nagent:\n tool_use_enforcement: [\"gpt\", \"codex\", \"gemini\", \"grok\", \"my-custom-model\"]\n```\n\n## TTS 配置\n\n```yaml\ntts:\n provider: \"edge\" # \"edge\" | \"elevenlabs\" | \"openai\" | \"minimax\" | \"mistral\" | \"gemini\" | \"xai\" | \"neutts\"\n speed: 1.0 # 全局速度倍数(所有 provider 的回退)\n edge:\n voice: \"en-US-AriaNeural\" # 322 种声音,74 种语言\n speed: 1.0 # 速度倍数(转换为速率百分比,例如 1.5 → +50%)\n elevenlabs:\n voice_id: \"pNInz6obpgDQGcFmaJgB\"\n model_id: \"eleven_multilingual_v2\"\n openai:\n model: \"gpt-4o-mini-tts\"\n voice: \"alloy\" # alloy、echo、fable、onyx、nova、shimmer\n speed: 1.0 # 速度倍数(API 限制为 0.25–4.0)\n base_url: \"https://api.openai.com/v1\" # 覆盖 OpenAI 兼容 TTS 端点\n minimax:\n speed: 1.0 # 语音速度倍数\n # base_url: \"\" # 可选:覆盖 OpenAI 兼容 TTS 端点\n mistral:\n model: \"voxtral-mini-tts-2603\"\n voice_id: \"c69964a6-ab8b-4f8a-9465-ec0925096ec8\" # Paul - Neutral(默认)\n gemini:\n model: \"gemini-2.5-flash-preview-tts\" # 或 gemini-2.5-pro-preview-tts\n voice: \"Kore\" # 30 种预置声音:Zephyr、Puck、Kore、Enceladus 等\n xai:\n voice_id: \"eve\" # xAI TTS 声音\n language: \"en\" # ISO 639-1\n sample_rate: 24000\n bit_rate: 128000 # MP3 比特率\n # base_url: \"https://api.x.ai/v1\"\n neutts:\n ref_audio: ''\n ref_text: ''\n model: neuphonic/neutts-air-q4-gguf\n device: cpu\n```\n\n这控制 `text_to_speech` 工具和语音模式中的口语回复(CLI 中的 `/voice tts` 或消息 gateway)。\n\n**速度回退层次:** provider 特定速度(例如 `tts.edge.speed`)→ 全局 `tts.speed` → `1.0` 默认值。设置全局 `tts.speed` 以在所有 provider 上应用统一速度,或按 provider 覆盖以进行精细控制。\n\n## 显示设置\n\n```yaml\ndisplay:\n tool_progress: all # off | new | all | verbose\n tool_progress_command: false # 在消息 gateway 中启用 /verbose 斜杠命令\n platforms: {} # 每平台显示覆盖(见下文)\n tool_progress_overrides: {} # 已弃用 —— 改用 display.platforms\n interim_assistant_messages: true # Gateway:将自然的轮次中 assistant 更新作为单独消息发送\n skin: default # 内置或自定义 CLI 皮肤(参阅 user-guide/features/skins)\n personality: \"kawaii\" # 旧版外观字段,仍在某些摘要中显示\n compact: false # 紧凑输出模式(减少空白)\n resume_display: full # full(恢复时显示之前的消息)| minimal(仅单行)\n bell_on_complete: false # 当 agent 完成时播放终端铃声(适合长任务)\n show_reasoning: false # 在每次响应上方显示模型推理/思考(用 /reasoning show|hide 切换)\n streaming: false # 将 token 实时流式传输到终端\n show_cost: false # 在 CLI 状态栏中显示估计 $ 成本\n timestamps: false # 为 true 时,在 CLI/TUI 记录中为用户和 assistant 标签添加 [HH:MM] 时间戳前缀\n tool_preview_length: 0 # 工具调用预览的最大字符数(0 = 无限制,显示完整路径/命令)\n runtime_footer: # Gateway:在最终回复中附加运行时上下文页脚\n enabled: false\n fields: [\"model\", \"context_pct\", \"cwd\"]\n file_mutation_verifier: true # 当本轮 write_file/patch 调用失败时附加建议性页脚\n language: en # 静态消息的 UI 语言(审批提示、部分 gateway 回复)。en | zh | zh-hant | ja | de | es | fr | tr | uk | af | ko | it | ga | pt | ru | hu\n```\n\n### 文件变更验证器\n\n当 `display.file_mutation_verifier` 为 `true`(默认)时,每当本轮中 `write_file` 或 `patch` 调用失败且从未被对同一路径的成功写入取代时,Hermes 会在 assistant 的最终响应中附加一行建议。这捕获了\"批量并行补丁,一半静默失败,模型总结成功\"这类过度声明,而无需您在每次编辑后手动运行 `git status`。\n\n示例页脚:\n\n```\n⚠️ File-mutation verifier: 3 file(s) were NOT modified this turn despite any wording above that may suggest otherwise. Run `git status` or `read_file` to confirm.\n • concepts/automatic-organization.md — [patch] Could not find match for old_string\n • concepts/lora.md — [patch] Could not find match for old_string\n • concepts/rag-pipeline.md — [patch] Could not find match for old_string\n```\n\n设置 `file_mutation_verifier: false`(或 `HERMES_FILE_MUTATION_VERIFIER=0`)以禁止页脚。验证器仅在轮次结束时有真实失败未解决时触发 —— 在同一轮次内重试失败补丁并成功的模型不会为该文件触发它。\n\n### 静态消息的 UI 语言\n\n`display.language` 设置翻译一小组静态面向用户的消息 —— CLI 审批提示、少数 gateway 斜杠命令回复(例如重启排空通知、\"审批已过期\"、\"目标已清除\")。它**不**翻译 agent 响应、日志行、工具输出、错误回溯或斜杠命令描述 —— 这些保持英文。如果您希望 agent 本身用另一种语言回复,只需在您的提示词或系统消息中告诉它。\n\n支持的值:`en`(默认)、`zh`(简体中文)、`zh-hant`(繁体中文)、`ja`(日语)、`de`(德语)、`es`(西班牙语)、`fr`(法语)、`tr`(土耳其语)、`uk`(乌克兰语)、`af`(南非荷兰语)、`ko`(韩语)、`it`(意大利语)、`ga`(爱尔兰语)、`pt`(葡萄牙语)、`ru`(俄语)、`hu`(匈牙利语)。未知值回退到英文。\n\n您也可以使用 `HERMES_LANGUAGE` 环境变量按会话设置,它会覆盖配置值。\n\n```yaml\ndisplay:\n language: zh # CLI 审批提示以中文显示\n```\n\n| 模式 | 您看到的内容 |\n|------|-------------|\n| `off` | 静默 —— 仅最终响应 |\n| `new` | 仅在工具更改时显示工具指示器 |\n| `all` | 每次工具调用附带简短预览(默认) |\n| `verbose` | 完整参数、结果和调试日志 |\n\n在 CLI 中,使用 `/verbose` 循环切换这些模式。要在消息平台(Telegram、Discord、Slack 等)中使用 `/verbose`,请在上方的 `display` 部分设置 `tool_progress_command: true`。该命令将循环切换模式并保存到配置。\n\n### 运行时元数据页脚(仅限 gateway)\n\n当 `display.runtime_footer.enabled: true` 时,Hermes 在每个 gateway 轮次的**最终**消息中附加一个小型运行时上下文页脚。目前页脚可显示模型、上下文窗口百分比和当前工作目录。默认关闭;如果您的团队希望每个回复都包含这些来源信息,请按 gateway 选择加入。\n\n```yaml\ndisplay:\n runtime_footer:\n enabled: true\n fields: [\"model\", \"context_pct\", \"cwd\"] # 支持字段:model、context_pct、cwd\n```\n\n`/footer` 斜杠命令在任何会话中运行时切换此功能。\n\n附加到 Telegram/Discord/Slack 回复的示例页脚:\n\n```\n— claude-opus-4.7 · 12 tool calls · 2m 14s · $0.042\n```\n\n只有轮次的**最终**消息获得页脚;中间更新保持干净。\n\n### 每平台进度覆盖\n\n不同平台有不同的详细程度需求。例如,Signal 无法编辑消息,因此每次进度更新都会成为单独的消息 —— 很嘈杂。使用 `display.platforms` 设置每平台模式:\n\n```yaml\ndisplay:\n tool_progress: all # 全局默认\n platforms:\n signal:\n tool_progress: 'off' # 在 Signal 上静默进度\n telegram:\n tool_progress: verbose # 在 Telegram 上详细进度\n slack:\n tool_progress: 'off' # 在共享 Slack 工作区中保持安静\n```\n\n没有覆盖的平台回退到全局 `tool_progress` 值。有效平台键:`telegram`、`discord`、`slack`、`signal`、`whatsapp`、`matrix`、`mattermost`、`email`、`sms`、`homeassistant`、`dingtalk`、`feishu`、`wecom`、`weixin`、`bluebubbles`、`qqbot`。旧版 `display.tool_progress_overrides` 键仍可加载以向后兼容,但已弃用,并在首次加载时迁移到 `display.platforms`。\n\n`interim_assistant_messages` 仅限 gateway。启用后,Hermes 将已完成的轮次中 assistant 更新作为单独的聊天消息发送。这与 `tool_progress` 无关,不需要 gateway 流式传输。\n\n## 隐私\n\n```yaml\nprivacy:\n redact_pii: false # 从 LLM 上下文中删除 PII(仅限 gateway)\n```\n\n当 `redact_pii` 为 `true` 时,gateway 在将系统提示词发送到受支持平台上的 LLM 之前,会从中删除个人身份信息:\n\n| 字段 | 处理方式 |\n|-------|-----------|\n| 电话号码(WhatsApp/Signal 上的用户 ID) | 哈希为 `user_<12-char-sha256>` |\n| 用户 ID | 哈希为 `user_<12-char-sha256>` |\n| 聊天 ID | 数字部分哈希,保留平台前缀(`telegram:<hash>`) |\n| 主频道 ID | 数字部分哈希 |\n| 用户名/昵称 | **不受影响**(用户选择的,公开可见) |\n\n**平台支持:** 删除适用于 WhatsApp、Signal 和 Telegram。Discord 和 Slack 被排除,因为它们的提及系统(`<@user_id>`)需要 LLM 上下文中的真实 ID。\n\n哈希是确定性的 —— 同一用户始终映射到同一哈希,因此模型仍然可以在群聊中区分用户。路由和传递在内部使用原始值。\n\n## 语音转文字(STT)\n\n```yaml\nstt:\n provider: \"local\" # \"local\" | \"groq\" | \"openai\" | \"mistral\"\n local:\n model: \"base\" # tiny、base、small、medium、large-v3\n openai:\n model: \"whisper-1\" # whisper-1 | gpt-4o-mini-transcribe | gpt-4o-transcribe\n # model: \"whisper-1\" # 旧版回退键仍受支持\n```\n\nProvider 行为:\n\n- `local` 使用在您机器上运行的 `faster-whisper`。使用 `pip install faster-whisper` 单独安装。\n- `groq` 使用 Groq 的 Whisper 兼容端点,读取 `GROQ_API_KEY`。\n- `openai` 使用 OpenAI 语音 API,读取 `VOICE_TOOLS_OPENAI_KEY`。\n\n如果请求的 provider 不可用,Hermes 按此顺序自动回退:`local` → `groq` → `openai`。\n\nGroq 和 OpenAI 模型覆盖由环境变量驱动:\n\n```bash\nSTT_GROQ_MODEL=whisper-large-v3-turbo\nSTT_OPENAI_MODEL=whisper-1\nGROQ_BASE_URL=https://api.groq.com/openai/v1\nSTT_OPENAI_BASE_URL=https://api.openai.com/v1\n```\n\n## 语音模式(CLI)\n\n```yaml\nvoice:\n record_key: \"ctrl+b\" # CLI 内的按键通话键\n max_recording_seconds: 120 # 长录音的硬停止\n auto_tts: false # /voice on 时自动启用口语回复\n beep_enabled: true # 在 CLI 语音模式中播放录音开始/停止提示音\n silence_threshold: 200 # 语音检测的 RMS 阈值\n silence_duration: 3.0 # 自动停止前的静默秒数\n```\n\n在 CLI 中使用 `/voice on` 启用麦克风模式,使用 `record_key` 开始/停止录音,使用 `/voice tts` 切换口语回复。端到端设置和平台特定行为请参阅[语音模式](/user-guide/features/voice-mode)。\n\n## 流式传输\n\n将 token 实时流式传输到终端或消息平台,而不是等待完整响应。\n\n### CLI 流式传输\n\n```yaml\ndisplay:\n streaming: true # 实时将 token 流式传输到终端\n show_reasoning: true # 同时流式传输推理/思考 token(可选)\n```\n\n启用后,响应在流式传输框内逐 token 出现。工具调用仍然静默捕获。如果 provider 不支持流式传输,它会自动回退到正常显示。\n\n### Gateway 流式传输(Telegram、Discord、Slack)\n\n```yaml\nstreaming:\n enabled: true # 启用渐进式消息编辑\n transport: edit # \"edit\"(渐进式消息编辑)或 \"off\"\n edit_interval: 0.3 # 消息编辑之间的秒数\n buffer_threshold: 40 # 强制编辑刷新前的字符数\n cursor: \" ▉\" # 流式传输期间显示的光标\n fresh_final_after_seconds: 60 # 当预览超过此时间时发送新的最终消息(Telegram);0 = 始终就地编辑\n```\n\n启用后,bot 在第一个 token 时发送消息,然后随着更多 token 到来渐进式编辑它。不支持消息编辑的平台(Signal、Email、Home Assistant)在第一次尝试时自动检测 —— 该会话的流式传输被优雅地禁用,不会产生大量消息。\n\n对于不带渐进式 token 编辑的独立自然轮次中 assistant 更新,请设置 `display.interim_assistant_messages: true`。\n\n**溢出处理:** 如果流式传输的文本超过平台的消息长度限制(约 4096 字符),当前消息被最终化,新消息自动开始。\n\n**新的最终消息(Telegram):** Telegram 的 `editMessageText` 保留原始消息时间戳,因此长时间运行的流式回复即使在完成后也会保留第一个 token 的时间戳。当 `fresh_final_after_seconds > 0`(默认 `60`)时,完成的回复作为全新消息传递(尽力删除旧预览),以便 Telegram 的可见时间戳反映完成时间。短预览仍然就地最终化。设置为 `0` 以始终就地编辑。\n\n:::note\n主开关 `streaming.enabled` 默认为 `false`——在你启用之前不会有任何流式传输。启用后,是否流式传输按**平台**决定:Telegram 默认带有 `display.platforms.telegram.streaming: true`(流式传输),Discord 为 `display.platforms.discord.streaming: false`(不流式传输)。因此启用流式传输后,Telegram 开箱即用地流式传输,Discord 在你修改其开关之前仍使用整条消息回复。你可以在仪表盘的 **Channels** 开关中或直接在 `~/.hermes/config.yaml` 中调整这些按平台的开关。\n:::\n\n## 群聊会话隔离\n\n控制共享聊天是每个房间保持一个对话还是每个参与者一个对话:\n\n```yaml\ngroup_sessions_per_user: true # true = 群组/频道中每用户隔离,false = 每个聊天一个共享会话\n```\n\n- `true` 是默认和推荐设置。在 Discord 频道、Telegram 群组、Slack 频道和类似共享上下文中,当平台提供用户 ID 时,每个发送者获得自己的会话。\n- `false` 恢复到旧的共享房间行为。如果您明确希望 Hermes 将频道视为一个协作对话,这可能有用,但这也意味着用户共享上下文、token 成本和中断状态。\n- 私信不受影响。Hermes 仍然像往常一样通过聊天/DM ID 键入 DM。\n- 线程与其父频道保持隔离;使用 `true` 时,每个参与者在线程内也获得自己的会话。\n\n有关行为详情和示例,请参阅[会话](/user-guide/sessions)和 [Discord 指南](/user-guide/messaging/discord)。\n\n## 未授权 DM 行为\n\n控制当未知用户发送私信时 Hermes 的行为:\n\n```yaml\nunauthorized_dm_behavior: pair\n\nwhatsapp:\n unauthorized_dm_behavior: ignore\n```\n\n- `pair` 是默认值。Hermes 拒绝访问,但在 DM 中回复一次性配对码。\n- `ignore` 静默丢弃未授权的 DM。\n- 平台部分覆盖全局默认值,因此您可以在广泛范围内保持配对启用,同时使一个平台更安静。\n\n## 快速命令\n\n定义自定义命令,这些命令要么在不调用 LLM 的情况下运行 shell 命令,要么将一个斜杠命令别名为另一个。Exec 快速命令是零 token 的,对于从消息平台(Telegram、Discord 等)进行快速服务器检查或实用脚本很有用。\n\n```yaml\nquick_commands:\n status:\n type: exec\n command: systemctl status hermes-agent\n disk:\n type: exec\n command: df -h /\n update:\n type: exec\n command: cd ~/.hermes/hermes-agent && git pull && pip install -e .\n gpu:\n type: exec\n command: nvidia-smi --query-gpu=name,utilization.gpu,memory.used,memory.total --format=csv,noheader\n restart:\n type: alias\n target: /gateway restart\n```\n\n用法:在 CLI 或任何消息平台中输入 `/status`、`/disk`、`/update`、`/gpu` 或 `/restart`。`exec` 命令在宿主本地运行并直接返回输出 —— 无 LLM 调用,不消耗 token。`alias` 命令重写为配置的斜杠命令目标。\n\n- **30 秒超时** —— 长时间运行的命令被终止并显示错误消息\n- **优先级** —— 快速命令在技能命令之前检查,因此您可以覆盖技能名称\n- **自动补全** —— 快速命令在调度时解析,不显示在内置斜杠命令自动补全表中\n- **类型** —— 支持的类型为 `exec` 和 `alias`;其他类型显示错误\n- **到处可用** —— CLI、Telegram、Discord、Slack、WhatsApp、Signal、Email、Home Assistant\n\n仅字符串的 prompt 快捷方式不是有效的快速命令。对于可重用的 prompt 工作流,请创建技能或别名到现有斜杠命令。\n\n## 人类延迟\n\n在消息平台中模拟类人响应节奏:\n\n```yaml\nhuman_delay:\n mode: \"off\" # off | natural | custom\n min_ms: 800 # 最小延迟(自定义模式)\n max_ms: 2500 # 最大延迟(自定义模式)\n```\n\n## 代码执行\n\n配置 `execute_code` 工具:\n\n```yaml\ncode_execution:\n mode: project # project(默认)| strict\n timeout: 300 # 最大执行时间(秒)\n max_tool_calls: 50 # 代码执行中的最大工具调用次数\n```\n\n**`mode`** 控制脚本的工作目录和 Python 解释器:\n\n- **`project`**(默认)—— 脚本在会话的工作目录中以活跃 virtualenv/conda 环境的 python 运行。项目依赖(`pandas`、`torch`、项目包)和相对路径(`.env`、`./data.csv`)自然解析,与 `terminal()` 看到的一致。\n- **`strict`** —— 脚本在临时暂存目录中以 `sys.executable`(Hermes 自己的 python)运行。最大可重现性,但项目依赖和相对路径不会解析。\n\n环境清理(删除 `*_API_KEY`、`*_TOKEN`、`*_SECRET`、`*_PASSWORD`、`*_CREDENTIAL`、`*_PASSWD`、`*_AUTH`)和工具白名单在两种模式下完全相同 —— 切换模式不会改变安全态势。\n\n## Web 搜索后端\n\n`web_search` 和 `web_extract` 工具支持五种后端 provider。在 `config.yaml` 中或通过 `hermes tools` 配置后端:\n\n```yaml\nweb:\n backend: firecrawl # firecrawl | searxng | parallel | tavily | exa\n\n # 或使用每功能键混合 provider(例如免费搜索 + 付费提取):\n search_backend: \"searxng\"\n extract_backend: \"firecrawl\"\n```\n\n| 后端 | 环境变量 | 搜索 | 提取 |\n|---------|---------|--------|---------|\n| **Firecrawl**(默认) | `FIRECRAWL_API_KEY` | ✔ | ✔ |\n| **SearXNG** | `SEARXNG_URL` | ✔ | — |\n| **Parallel** | `PARALLEL_API_KEY` | ✔ | ✔ |\n| **Tavily** | `TAVILY_API_KEY` | ✔ | ✔ |\n| **Exa** | `EXA_API_KEY` | ✔ | ✔ |\n\n**后端选择:** 如果未设置 `web.backend`,后端从可用的 API 密钥自动检测。如果仅设置了 `SEARXNG_URL`,使用 SearXNG。如果仅设置了 `EXA_API_KEY`,使用 Exa。如果仅设置了 `TAVILY_API_KEY`,使用 Tavily。如果仅设置了 `PARALLEL_API_KEY`,使用 Parallel。否则 Firecrawl 是默认值。\n\n**SearXNG** 是一个免费、自托管、尊重隐私的元搜索引擎,查询 70+ 个搜索引擎。无需 API 密钥 —— 只需将 `SEARXNG_URL` 设置为您的实例(例如 `http://localhost:8080`)。SearXNG 仅限搜索;`web_extract` 需要单独的提取 provider(设置 `web.extract_backend`)。Docker 设置说明请参阅 [Web 搜索设置指南](/user-guide/features/web-search)。\n\n**自托管 Firecrawl:** 设置 `FIRECRAWL_API_URL` 指向您自己的实例。设置自定义 URL 后,API 密钥变为可选(在服务器上设置 `USE_DB_AUTHENTICATION=***` 以禁用认证)。\n\n**Parallel 搜索模式:** 设置 `PARALLEL_SEARCH_MODE` 控制搜索行为 —— `fast`、`one-shot` 或 `agentic`(默认:`agentic`)。\n\n**Exa:** 在 `~/.hermes/.env` 中设置 `EXA_API_KEY`。支持 `category` 过滤(`company`、`research paper`、`news`、`people`、`personal site`、`pdf`)和域名/日期过滤器。\n\n## 浏览器\n\n配置浏览器自动化行为:\n\n```yaml\nbrowser:\n inactivity_timeout: 120 # 自动关闭空闲会话前的秒数\n command_timeout: 30 # 浏览器命令超时(截图、导航等)(秒)\n record_sessions: false # 自动将浏览器会话录制为 WebM 视频到 ~/.hermes/browser_recordings/\n # 可选 CDP 覆盖 —— 设置后,Hermes 直接附加到您自己的\n # Chromium 系浏览器(通过 /browser connect),而不是启动无头浏览器。\n cdp_url: \"\"\n # 对话框监督器 —— 控制当 CDP 后端附加时(Browserbase、本地 Chromium 系\n # 浏览器通过 /browser connect)如何处理原生 JS 对话框(alert/confirm/prompt)。\n # 在 Camofox 和默认本地 agent 浏览器模式下忽略。\n dialog_policy: must_respond # must_respond | auto_dismiss | auto_accept\n dialog_timeout_s: 300 # must_respond 下的安全自动关闭(秒)\n camofox:\n managed_persistence: false # 为 true 时,Camofox 会话跨重启持久化 cookie/登录\n user_id: \"\" # 可选的外部管理 Camofox userId\n session_key: \"\" # Hermes 创建标签页时发送的可选会话密钥\n adopt_existing_tab: false # 在创建新标签页之前重用此身份的现有标签页\n```\n\n**对话框策略:**\n\n- `must_respond`(默认)—— 捕获对话框,在 `browser_snapshot.pending_dialogs` 中显示,等待 agent 调用 `browser_dialog(action=...)`。在 `dialog_timeout_s` 秒内无响应后,对话框被自动关闭以防止页面的 JS 线程永久停滞。\n- `auto_dismiss` —— 捕获,立即关闭。Agent 仍然在事后的 `browser_snapshot.recent_dialogs` 中看到对话框记录,`closed_by=\"auto_policy\"`。\n- `auto_accept` —— 捕获,立即接受。适用于有激进 `beforeunload` 提示的页面。\n\n完整对话框工作流请参阅[浏览器功能页面](./features/browser.md#browser_dialog)。\n\n浏览器工具集支持多个 provider。有关 Browserbase、Browser Use 和本地 Chromium 系 CDP 设置的详细信息,请参阅[浏览器功能页面](/user-guide/features/browser)。\n\n## 时区\n\n使用 IANA 时区字符串覆盖服务器本地时区。影响日志中的时间戳、cron 调度和系统提示词时间注入。\n\n```yaml\ntimezone: \"America/New_York\" # IANA 时区(默认:\"\" = 服务器本地时间)\n```\n\n支持的值:任何 IANA 时区标识符(例如 `America/New_York`、`Europe/London`、`Asia/Kolkata`、`UTC`)。留空或省略以使用服务器本地时间。\n\n## Discord\n\n为消息 gateway 配置 Discord 特定行为:\n\n```yaml\ndiscord:\n require_mention: true # 在服务器频道中需要 @提及才能响应\n free_response_channels: \"\" # 逗号分隔的频道 ID,bot 在这些频道无需 @提及即可响应\n auto_thread: true # 在频道中 @提及时自动创建线程\n```\n\n- `require_mention` —— 为 `true`(默认)时,bot 仅在服务器频道中被 `@BotName` 提及时响应。DM 始终无需提及即可工作。\n- `free_response_channels` —— 逗号分隔的频道 ID 列表,bot 在这些频道对每条消息响应,无需提及。\n- `auto_thread` —— 为 `true`(默认)时,频道中的提及会自动为对话创建线程,保持频道整洁(类似 Slack 线程)。\n\n## 安全\n\n预执行安全扫描和机密脱敏:\n\n```yaml\nsecurity:\n redact_secrets: false # 在工具输出和日志中脱敏 API 密钥模式(默认关闭)\n tirith_enabled: true # 为终端命令启用 Tirith 安全扫描\n tirith_path: \"tirith\" # tirith 二进制文件路径(默认:$PATH 中的 \"tirith\")\n tirith_timeout: 5 # 等待 tirith 扫描的秒数\n tirith_fail_open: true # 如果 tirith 不可用,允许命令执行\n website_blocklist: # 参见下方网站黑名单部分\n enabled: false\n domains: []\n shared_files: []\n```\n\n- `redact_secrets` —— 为 `true` 时,自动检测并脱敏工具输出中看起来像 API 密钥、token 和密码的模式,然后再进入对话上下文和日志。**默认关闭** —— 如果您经常在工具输出中处理真实凭据并希望有安全网,请启用。显式设置为 `true` 以开启。\n- `tirith_enabled` —— 为 `true` 时,终端命令在执行前由 [Tirith](https://github.com/sheeki03/tirith) 扫描以检测潜在危险操作。\n- `tirith_path` —— tirith 二进制文件的路径。如果 tirith 安装在非标准位置,请设置此项。\n- `tirith_timeout` —— 等待 tirith 扫描的最大秒数。如果扫描超时,命令继续执行。\n- `tirith_fail_open` —— 为 `true`(默认)时,如果 tirith 不可用或失败,允许命令执行。设置为 `false` 以在 tirith 无法验证时阻止命令。\n\n## 网站黑名单\n\n阻止 agent 的 web 和浏览器工具访问特定域名:\n\n```yaml\nsecurity:\n website_blocklist:\n enabled: false # 启用 URL 阻止(默认:false)\n domains: # 被阻止的域名模式列表\n - \"*.internal.company.com\"\n - \"admin.example.com\"\n - \"*.local\"\n shared_files: # 从外部文件加载额外规则\n - \"/etc/hermes/blocked-sites.txt\"\n```\n\n启用后,任何匹配被阻止域名模式的 URL 在 web 或浏览器工具执行之前都会被拒绝。这适用于 `web_search`、`web_extract`、`browser_navigate` 以及任何访问 URL 的工具。\n\n域名规则支持:\n- 精确域名:`admin.example.com`\n- 通配符子域名:`*.internal.company.com`(阻止所有子域名)\n- TLD 通配符:`*.local`\n\n共享文件每行包含一条域名规则(空行和 `#` 注释被忽略)。缺失或不可读的文件记录警告,但不禁用其他 web 工具。\n\n策略缓存 30 秒,因此配置更改无需重启即可快速生效。\n\n## 智能审批\n\n控制 Hermes 如何处理潜在危险命令:\n\n```yaml\napprovals:\n mode: manual # manual | smart | off\n```\n\n| 模式 | 行为 |\n|------|----------|\n| `manual`(默认) | 在执行任何被标记的命令之前提示用户。在 CLI 中显示交互式审批对话框。在消息中排队待处理的审批请求。 |\n| `smart` | 使用辅助 LLM 评估被标记的命令是否真正危险。低风险命令以会话级持久性自动批准。真正有风险的命令升级给用户。 |\n| `off` | 跳过所有审批检查。等同于 `HERMES_YOLO_MODE=true`。**谨慎使用。** |\n\n智能模式对于减少审批疲劳特别有用 —— 它让 agent 在安全操作上更自主地工作,同时仍然捕获真正破坏性的命令。\n\n:::warning\n设置 `approvals.mode: off` 会禁用终端命令的所有安全检查。仅在受信任的沙箱环境中使用。\n:::\n\n## 检查点\n\n破坏性文件操作之前的自动文件系统快照。详情请参阅[检查点与回滚](/user-guide/checkpoints-and-rollback)。\n\n```yaml\ncheckpoints:\n enabled: false # 启用自动检查点(也可:hermes chat --checkpoints)。默认:false(选择加入)。\n max_snapshots: 20 # 每个目录保留的最大检查点数(默认:20)\n```\n\n\n## 委托\n\n为委托工具配置子 agent 行为:\n\n```yaml\ndelegation:\n # model: \"google/gemini-3-flash-preview\" # 覆盖模型(空 = 继承父级)\n # provider: \"openrouter\" # 覆盖 provider(空 = 继承父级)\n # base_url: \"http://localhost:1234/v1\" # 直接 OpenAI 兼容端点(优先于 provider)\n # api_key: \"local-key\" # base_url 的 API 密钥(回退到 OPENAI_API_KEY)\n # api_mode: \"\" # base_url 的线路协议:\"chat_completions\"、\"codex_responses\" 或 \"anthropic_messages\"。空 = 从 URL 自动检测(例如 /anthropic 后缀 → anthropic_messages)。对启发式无法检测的非标准端点显式设置。\n max_concurrent_children: 3 # 每批并行子 agent 数(下限 1,无上限)。也可通过 DELEGATION_MAX_CONCURRENT_CHILDREN 环境变量设置。\n max_spawn_depth: 1 # 委托树深度上限(1-3,截断)。1 = 扁平(默认):父级生成无法委托的叶子。2 = 编排器子级可以生成叶子孙级。3 = 三级。\n orchestrator_enabled: true # 全局终止开关。为 false 时,role=\"orchestrator\" 被忽略,每个子级无论 max_spawn_depth 如何都被强制为叶子。\n```\n\n**子 agent provider:model 覆盖:** 默认情况下,子 agent 继承父 agent 的 provider 和模型。设置 `delegation.provider` 和 `delegation.model` 将子 agent 路由到不同的 provider:model 对 —— 例如,在您的主 agent 运行昂贵推理模型时,为范围较窄的子任务使用便宜/快速的模型。\n\n**直接端点覆盖:** 如果您想要明显的自定义端点路径,请设置 `delegation.base_url`、`delegation.api_key` 和 `delegation.model`。这将子 agent 直接发送到该 OpenAI 兼容端点,并优先于 `delegation.provider`。如果省略 `delegation.api_key`,Hermes 仅回退到 `OPENAI_API_KEY`。\n\n**线路协议(`api_mode`):** Hermes 从 `delegation.base_url` 自动检测线路协议(例如以 `/anthropic` 结尾的路径 → `anthropic_messages`;Codex/原生 Anthropic/Kimi-coding 主机名保留其现有检测)。对于启发式无法分类的端点 —— 例如 Azure AI Foundry、MiniMax、Zhipu GLM 或前置 Anthropic 形状后端的 LiteLLM 代理 —— 请将 `delegation.api_mode` 显式设置为 `chat_completions`、`codex_responses` 或 `anthropic_messages` 之一。留空(默认)以保持自动检测。\n\n委托 provider 使用与 CLI/gateway 启动相同的凭据解析。所有配置的 provider 均受支持:`openrouter`、`nous`、`copilot`、`zai`、`kimi-coding`、`minimax`、`minimax-cn`。设置 provider 时,系统自动解析正确的基础 URL、API 密钥和 API 模式 —— 无需手动凭据连接。\n\n**优先级:** 配置中的 `delegation.base_url` → 配置中的 `delegation.provider` → 父 provider(继承)。配置中的 `delegation.model` → 父模型(继承)。仅设置 `model` 而不设置 `provider` 仅更改模型名称,同时保留父级凭据(适用于在同一 provider(如 OpenRouter)内切换模型)。\n\n**宽度和深度:** `max_concurrent_children` 限制每批并行运行的子 agent 数量(默认 `3`,下限 1,无上限)。也可通过 `DELEGATION_MAX_CONCURRENT_CHILDREN` 环境变量设置。当模型提交的 `tasks` 数组超过上限时,`delegate_task` 返回工具错误解释限制,而不是静默截断。`max_spawn_depth` 控制委托树深度(截断到 1-3)。在默认 `1` 时,委托是扁平的:子级无法生成孙级,传递 `role=\"orchestrator\"` 静默降级为 `leaf`。提升到 `2` 使编排器子级可以生成叶子孙级;`3` 用于三级树。Agent 通过 `role=\"orchestrator\"` 按调用选择编排;`orchestrator_enabled: false` 强制每个子级回到叶子,无论如何。成本呈乘法增长 —— 在 `max_spawn_depth: 3` 和 `max_concurrent_children: 3` 时,树可以达到 3×3×3 = 27 个并发叶子 agent。使用模式请参阅[子 Agent 委托 → 深度限制和嵌套编排](features/delegation.md#depth-limit-and-nested-orchestration)。\n\n## 澄清\n\n配置澄清提示行为:\n\n```yaml\nclarify:\n timeout: 120 # 等待用户澄清响应的秒数\n```\n\n## 上下文文件(SOUL.md、AGENTS.md)\n\nHermes 使用两种不同的上下文范围:\n\n| 文件 | 用途 | 范围 |\n|------|---------|-------|\n| `SOUL.md` | **主要 agent 身份** —— 定义 agent 是谁(系统提示词第 #1 槽位) | `~/.hermes/SOUL.md` 或 `$HERMES_HOME/SOUL.md` |\n| `.hermes.md` / `HERMES.md` | 项目特定指令(最高优先级) | 向上走到 git 根目录 |\n| `AGENTS.md` | 项目特定指令、编码规范 | 递归目录遍历 |\n| `CLAUDE.md` | Claude Code 上下文文件(也会检测) | 仅工作目录 |\n| `.cursorrules` | Cursor IDE 规则(也会检测) | 仅工作目录 |\n| `.cursor/rules/*.mdc` | Cursor 规则文件(也会检测) | 仅工作目录 |\n\n- **SOUL.md** 是 agent 的主要身份。它占据系统提示词的第 #1 槽位,完全替换内置的默认身份。编辑它以完全自定义 agent 是谁。\n- 如果 SOUL.md 缺失、为空或无法加载,Hermes 回退到内置默认身份。\n- **项目上下文文件使用优先级系统** —— 仅加载一种类型(第一个匹配优先):`.hermes.md` → `AGENTS.md` → `CLAUDE.md` → `.cursorrules`。SOUL.md 始终独立加载。\n- **AGENTS.md** 是分层的:如果子目录也有 AGENTS.md,所有都会合并。\n- 如果 `SOUL.md` 不存在,Hermes 会自动生成默认的 `SOUL.md`。\n- 所有加载的上下文文件上限为 20,000 字符,并进行智能截断。\n\n另请参阅:\n- [个性与 SOUL.md](/user-guide/features/personality)\n- [上下文文件](/user-guide/features/context-files)\n\n## 工作目录\n\n| 上下文 | 默认值 |\n|---------|---------|\n| **CLI(`hermes`)** | 运行命令的当前目录 |\n| **消息 gateway** | 主目录 `~`(用 `MESSAGING_CWD` 覆盖) |\n| **Docker / Singularity / Modal / SSH** | 容器或远程机器内用户的主目录 |\n\n覆盖工作目录:\n```bash\n# 在 ~/.hermes/.env 或 ~/.hermes/config.yaml 中:\nMESSAGING_CWD=/home/myuser/projects # Gateway 会话\nTERMINAL_CWD=/workspace # 所有终端会话\n```\n"}, "8b23b2bc0130d3c74b5893b3f5cd21857c0a58d7": {"t": 1785768007.1817322, "n": 55270, "h": "ee0db6466ba34f2b", "c": "---\nsidebar_position: 2\ntitle: \"配置\"\ndescription: \"配置 Hermes Agent — config.yaml、providers、模型、API 密钥等\"\n---\n\n# 配置\n\n所有设置均存储在 `~/.hermes/` 目录中,便于访问。\n\n## 目录结构\n\n```text\n~/.hermes/\n├── config.yaml # 设置(模型、终端、TTS、压缩等)\n├── .env # API 密钥和机密\n├── auth.json # OAuth provider 凭据(Nous Portal 等)\n├── SOUL.md # 主要 agent 身份(系统提示词第 #1 槽位)\n├── memories/ # 持久记忆(MEMORY.md、USER.md)\n├── skills/ # Agent 创建的技能(通过 skill_manage 工具管理)\n├── cron/ # 定时任务\n├── sessions/ # Gateway 会话\n└── logs/ # 日志(errors.log、gateway.log — 机密自动脱敏)\n```\n\n## 管理配置\n\n```bash\nhermes config # 查看当前配置\nhermes config edit # 在编辑器中打开 config.yaml\nhermes config set KEY VAL # 设置特定值\nhermes config check # 检查缺失选项(更新后使用)\nhermes config migrate # 交互式添加缺失选项\n\n# 示例:\nhermes config set model anthropic/claude-opus-4\nhermes config set terminal.backend docker\nhermes config set OPENROUTER_API_KEY sk-or-... # 保存到 .env\n```\n\n:::tip\n`hermes config set` 命令会自动将值路由到正确的文件 —— API 密钥保存到 `.env`,其他所有内容保存到 `config.yaml`。\n:::\n\n## 配置优先级\n\n设置按以下顺序解析(优先级从高到低):\n\n1. **CLI 参数** —— 例如 `hermes chat --model anthropic/claude-sonnet-4`(单次调用覆盖)\n2. **`~/.hermes/config.yaml`** —— 所有非机密设置的主配置文件\n3. **`~/.hermes/.env`** —— 环境变量的回退;机密(API 密钥、token、密码)**必须**放这里\n4. **内置默认值** —— 未设置任何内容时的硬编码安全默认值\n\n:::info 经验法则\n机密(API 密钥、bot token、密码)放入 `.env`。其他所有内容(模型、终端后端、压缩设置、内存限制、工具集)放入 `config.yaml`。当两者都设置时,`config.yaml` 对非机密设置优先。\n:::\n\n## 环境变量替换\n\n可以在 `config.yaml` 中使用 `${VAR_NAME}` 语法引用环境变量:\n\n```yaml\nauxiliary:\n vision:\n api_key: ${GOOGLE_API_KEY}\n base_url: ${CUSTOM_VISION_URL}\n\ndelegation:\n api_key: ${DELEGATION_KEY}\n```\n\n单个值中可以有多个引用:`url: \"${HOST}:${PORT}\"`。如果引用的变量未设置,占位符将保持原样(`${UNDEFINED_VAR}` 保持不变)。仅支持 `${VAR}` 语法 —— 裸 `$VAR` 不会被展开。\n\n关于 AI provider 设置(OpenRouter、Anthropic、Copilot、自定义端点、自托管 LLM、回退模型等),请参阅 [AI Providers](/integrations/providers)。\n\n### Provider 超时\n\n可以为 provider 设置 `providers.<id>.request_timeout_seconds` 作为全局请求超时,以及 `providers.<id>.models.<model>.timeout_seconds` 作为特定模型的覆盖值。适用于每种传输方式(OpenAI-wire、原生 Anthropic、Anthropic 兼容)上的主轮次客户端、回退链、凭据轮换后的重建,以及(对于 OpenAI-wire)每请求超时 kwarg —— 因此配置值优先于旧版 `HERMES_API_TIMEOUT` 环境变量。\n\n还可以设置 `providers.<id>.stale_timeout_seconds` 用于非流式陈旧调用检测器,以及 `providers.<id>.models.<model>.stale_timeout_seconds` 作为特定模型的覆盖值。此值优先于旧版 `HERMES_API_CALL_STALE_TIMEOUT` 环境变量。\n\n不设置这些值将保持旧版默认值(`HERMES_API_TIMEOUT=1800`s、`HERMES_API_CALL_STALE_TIMEOUT=300`s、原生 Anthropic 900s)。目前不适用于 AWS Bedrock(`bedrock_converse` 和 AnthropicBedrock SDK 路径均使用 boto3 及其自身的超时配置)。请参阅 [`cli-config.yaml.example`](https://github.com/NousResearch/hermes-agent/blob/main/cli-config.yaml.example) 中的注释示例。\n\n## 终端后端配置\n\nHermes 支持六种终端后端。每种后端决定 agent 的 shell 命令实际在哪里执行 —— 本地机器、Docker 容器、通过 SSH 的远程服务器、Modal 云沙箱(直接或通过 Nous 托管的 gateway)、Daytona 工作区,或 Singularity/Apptainer 容器。\n\n```yaml\nterminal:\n backend: local # local | docker | ssh | modal | daytona | singularity\n cwd: \".\" # Gateway/cron 工作目录(CLI 始终使用启动目录)\n timeout: 180 # 每条命令的超时时间(秒)\n env_passthrough: [] # 转发到沙箱执行的环境变量名(terminal + execute_code)\n singularity_image: \"docker://nikolaik/python-nodejs:python3.11-nodejs20\" # Singularity 后端的容器镜像\n modal_image: \"nikolaik/python-nodejs:python3.11-nodejs20\" # Modal 后端的容器镜像\n daytona_image: \"nikolaik/python-nodejs:python3.11-nodejs20\" # Daytona 后端的容器镜像\n```\n\n对于 Modal 和 Daytona 等云沙箱,`container_persistent: true` 表示 Hermes 将尝试在沙箱重建后保留文件系统状态。这并不保证相同的活跃沙箱、PID 空间或后台进程之后仍在运行。\n\n### 后端概览\n\n| 后端 | 命令运行位置 | 隔离性 | 最适合 |\n|---------|-------------------|-----------|----------|\n| **local** | 直接在您的机器上 | 无 | 开发、个人使用 |\n| **docker** | 单个持久 Docker 容器(跨会话、`/new`、子 agent 共享) | 完全(命名空间、cap-drop) | 安全沙箱、CI/CD |\n| **ssh** | 通过 SSH 的远程服务器 | 网络边界 | 远程开发、强大硬件 |\n| **modal** | Modal 云沙箱 | 完全(云 VM) | 临时云计算、评估 |\n| **daytona** | Daytona 工作区 | 完全(云容器) | 托管云开发环境 |\n| **singularity** | Singularity/Apptainer 容器 | 命名空间(--containall) | HPC 集群、共享机器 |\n\n### Local 后端\n\n默认后端。命令直接在您的机器上运行,无隔离。无需特殊设置。\n\n```yaml\nterminal:\n backend: local\n```\n\n:::warning\nAgent 拥有与您的用户账户相同的文件系统访问权限。使用 `hermes tools` 禁用不需要的工具,或切换到 Docker 进行沙箱隔离。\n:::\n\n### Docker 后端\n\n在具有安全加固的 Docker 容器内运行命令(所有权限已删除、无权限提升、PID 限制)。\n\n**单个持久容器,而非每条命令一个容器。** Hermes 在首次使用时启动一个长期运行的容器,并通过 `docker exec` 将每个终端、文件和 `execute_code` 调用路由到同一容器中 —— 跨会话、`/new`、`/reset` 和 `delegate_task` 子 agent,贯穿 Hermes 进程的整个生命周期。工作目录更改、已安装的包以及 `/workspace` 中的文件会从一次工具调用延续到下一次,就像本地 shell 一样。容器在关闭时停止并删除。详情请参阅下方的**容器生命周期**。\n\n```yaml\nterminal:\n backend: docker\n docker_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n docker_mount_cwd_to_workspace: false # 将启动目录挂载到 /workspace\n docker_run_as_host_user: false # 参见下方\"以宿主用户身份运行容器\"\n docker_forward_env: # 转发到容器的环境变量\n - \"GITHUB_TOKEN\"\n docker_volumes: # 宿主目录挂载\n - \"/home/user/projects:/workspace/projects\"\n - \"/home/user/data:/data:ro\" # :ro 表示只读\n docker_extra_args: # 附加到 `docker run` 的额外标志\n - \"--gpus=all\"\n - \"--network=host\"\n\n # 资源限制\n container_cpu: 1 # CPU 核心数(0 = 不限制)\n container_memory: 5120 # MB(0 = 不限制)\n container_disk: 51200 # MB(需要 XFS+pquota 上的 overlay2)\n container_persistent: true # 跨会话持久化 /workspace 和 /root\n```\n\n**`terminal.docker_extra_args`**(也可通过 `TERMINAL_DOCKER_EXTRA_ARGS='[\"--gpus=all\"]'` 覆盖)允许传递 Hermes 未作为一级键公开的任意 `docker run` 标志 —— `--gpus`、`--network`、`--add-host`、替代 `--security-opt` 覆盖等。每个条目必须是字符串;该列表最后附加到组装好的 `docker run` 调用中,因此可以在需要时覆盖 Hermes 的默认值。请谨慎使用 —— 与沙箱加固(权限删除、`--user`、workspace 绑定挂载)冲突的标志将悄然削弱隔离性。\n\n**要求:** 已安装并运行 Docker Desktop 或 Docker Engine。Hermes 会探测 `$PATH` 以及常见的 macOS 安装位置(`/usr/local/bin/docker`、`/opt/homebrew/bin/docker`、Docker Desktop 应用包)。开箱即用支持 Podman:设置 `HERMES_DOCKER_BINARY=podman`(或完整路径)以在两者都安装时强制使用它。\n\n**容器生命周期:** Hermes 为每个终端和文件工具调用重用单个长期运行的容器(`docker run -d ... sleep 2h`),跨会话、`/new`、`/reset` 和 `delegate_task` 子 agent,贯穿 Hermes 进程的整个生命周期。命令通过带登录 shell 的 `docker exec` 运行,因此工作目录更改、已安装的包以及 `/workspace` 中的文件都会从一次工具调用延续到下一次。容器在 Hermes 关闭时(或空闲清理回收时)停止并删除。\n\n通过 `delegate_task(tasks=[...])` 生成的并行子 agent 共享这一个容器 —— 并发的 `cd`、环境变量修改以及对同一路径的写入会发生冲突。如果子 agent 需要隔离的沙箱,必须通过 `register_task_env_overrides()` 注册每任务镜像覆盖,RL 和基准测试环境(TerminalBench2、HermesSweEnv 等)会自动为其每任务 Docker 镜像执行此操作。\n\n**安全加固:**\n- `--cap-drop ALL`,仅添加回 `DAC_OVERRIDE`、`CHOWN`、`FOWNER`\n- `--security-opt no-new-privileges`\n- `--pids-limit 256`\n- `/tmp`(512MB)、`/var/tmp`(256MB)、`/run`(64MB)的大小限制 tmpfs\n\n**凭据转发:** `docker_forward_env` 中列出的环境变量首先从您的 shell 环境解析,然后回退到 `~/.hermes/.env`。技能也可以声明 `required_environment_variables`,这些变量会自动合并。\n\n### SSH 后端\n\n通过 SSH 在远程服务器上运行命令。使用 ControlMaster 进行连接复用(5 分钟空闲保活)。默认启用持久 shell —— 状态(cwd、环境变量)在命令之间保持。\n\n```yaml\nterminal:\n backend: ssh\n persistent_shell: true # 保持长期运行的 bash 会话(默认:true)\n```\n\n**必需的环境变量:**\n\n```bash\nTERMINAL_SSH_HOST=my-server.example.com\nTERMINAL_SSH_USER=ubuntu\n```\n\n**可选:**\n\n| 变量 | 默认值 | 描述 |\n|----------|---------|-------------|\n| `TERMINAL_SSH_PORT` | `22` | SSH 端口 |\n| `TERMINAL_SSH_KEY` | (系统默认) | SSH 私钥路径 |\n| `TERMINAL_SSH_PERSISTENT` | `true` | 启用持久 shell |\n\n**工作原理:** 使用 `BatchMode=yes` 和 `StrictHostKeyChecking=accept-new` 在初始化时连接。持久 shell 在远程主机上保持单个 `bash -l` 进程存活,通过临时文件进行通信。需要 `stdin_data` 或 `sudo` 的命令会自动回退到单次模式。\n\n### Modal 后端\n\n在 [Modal](https://modal.com) 云沙箱中运行命令。每个任务获得一个具有可配置 CPU、内存和磁盘的隔离 VM。文件系统可以跨会话快照/恢复。\n\n```yaml\nterminal:\n backend: modal\n container_cpu: 1 # CPU 核心数\n container_memory: 5120 # MB(5GB)\n container_disk: 51200 # MB(50GB)\n container_persistent: true # 快照/恢复文件系统\n```\n\n**必需:** `MODAL_TOKEN_ID` + `MODAL_TOKEN_SECRET` 环境变量,或 `~/.modal.toml` 配置文件。\n\n**持久化:** 启用后,沙箱文件系统在清理时快照,并在下次会话时恢复。快照在 `~/.hermes/modal_snapshots.json` 中跟踪。这保留文件系统状态,而非活跃进程、PID 空间或后台任务。\n\n**凭据文件:** 自动从 `~/.hermes/` 挂载(OAuth token 等),并在每条命令前同步。\n\n### Daytona 后端\n\n在 [Daytona](https://daytona.io) 托管工作区中运行命令。支持停止/恢复以实现持久化。\n\n```yaml\nterminal:\n backend: daytona\n container_cpu: 1 # CPU 核心数\n container_memory: 5120 # MB → 转换为 GiB\n container_disk: 10240 # MB → 转换为 GiB(最大 10 GiB)\n container_persistent: true # 停止/恢复而非删除\n```\n\n**必需:** `DAYTONA_API_KEY` 环境变量。\n\n**持久化:** 启用后,沙箱在清理时停止(而非删除),并在下次会话时恢复。沙箱名称遵循 `hermes-{task_id}` 模式。\n\n**磁盘限制:** Daytona 强制执行 10 GiB 最大值。超过此值的请求将被截断并发出警告。\n\n### Singularity/Apptainer 后端\n\n在 [Singularity/Apptainer](https://apptainer.org) 容器中运行命令。专为 Docker 不可用的 HPC 集群和共享机器设计。\n\n```yaml\nterminal:\n backend: singularity\n singularity_image: \"docker://nikolaik/python-nodejs:python3.11-nodejs20\"\n container_cpu: 1 # CPU 核心数\n container_memory: 5120 # MB\n container_persistent: true # 可写覆盖层跨会话持久化\n```\n\n**要求:** `$PATH` 中有 `apptainer` 或 `singularity` 二进制文件。\n\n**镜像处理:** Docker URL(`docker://...`)自动转换为 SIF 文件并缓存。现有 `.sif` 文件直接使用。\n\n**临时目录:** 按顺序解析:`TERMINAL_SCRATCH_DIR` → `TERMINAL_SANDBOX_DIR/singularity` → `/scratch/$USER/hermes-agent`(HPC 惯例)→ `~/.hermes/sandboxes/singularity`。\n\n**隔离:** 使用 `--containall --no-home` 实现完全命名空间隔离,不挂载宿主 home 目录。\n\n### 常见终端后端问题\n\n如果终端命令立即失败或终端工具报告为已禁用:\n\n- **Local** —— 无特殊要求。入门时最安全的默认选项。\n- **Docker** —— 运行 `docker version` 验证 Docker 是否正常工作。如果失败,修复 Docker 或执行 `hermes config set terminal.backend local`。\n- **SSH** —— `TERMINAL_SSH_HOST` 和 `TERMINAL_SSH_USER` 都必须设置。如果缺少任一项,Hermes 会记录清晰的错误。\n- **Modal** —— 需要 `MODAL_TOKEN_ID` 环境变量或 `~/.modal.toml`。运行 `hermes doctor` 检查。\n- **Daytona** —— 需要 `DAYTONA_API_KEY`。Daytona SDK 处理服务器 URL 配置。\n- **Singularity** —— 需要 `$PATH` 中有 `apptainer` 或 `singularity`。HPC 集群上常见。\n\n如有疑问,将 `terminal.backend` 设回 `local` 并首先验证命令在那里运行。\n\n### 拆卸时远程到宿主文件同步\n\n对于 **SSH**、**Modal** 和 **Daytona** 后端(agent 的工作树位于与运行 Hermes 的宿主不同的机器上),Hermes 跟踪 agent 在远程沙箱中触及的文件,并在会话拆卸/沙箱清理时,将修改的文件**同步回宿主**,存放在 `~/.hermes/cache/remote-syncs/<session-id>/` 下。\n\n- 触发时机:会话关闭、`/new`、`/reset`、gateway 消息超时、子 agent 使用远程后端时 `delegate_task` 子 agent 完成。\n- 覆盖 agent 修改的整个树,而不仅仅是它明确打开的文件。添加、编辑和删除都会被捕获。\n- 远程沙箱可能在您查找时已被拆除;本地 `~/.hermes/cache/remote-syncs/…` 副本是 agent 更改内容的权威记录。\n- 大型二进制输出(模型检查点、原始数据集)按大小限制 —— 同步跳过超过 `file_sync_max_mb`(默认 `100`)的文件。如果您期望更大的工件返回,请调高该值。\n\n```yaml\nterminal:\n file_sync_max_mb: 100 # 默认 —— 同步最大 100 MB 的文件\n file_sync_enabled: true # 默认 —— 设为 false 可完全跳过同步\n```\n\n这是从会话结束后被销毁的临时云沙箱中恢复结果的方式,无需告诉 agent 显式地 `scp` 或 `modal volume put` 每个工件。\n\n### Docker 卷挂载\n\n使用 Docker 后端时,`docker_volumes` 允许您与容器共享宿主目录。每个条目使用标准 Docker `-v` 语法:`host_path:container_path[:options]`。\n\n```yaml\nterminal:\n backend: docker\n docker_volumes:\n - \"/home/user/projects:/workspace/projects\" # 读写(默认)\n - \"/home/user/datasets:/data:ro\" # 只读\n - \"/home/user/.hermes/cache/documents:/output\" # Gateway 可见的导出\n```\n\n适用于:\n- **向 agent 提供文件**(数据集、配置、参考代码)\n- **从 agent 接收文件**(生成的代码、报告、导出)\n- **共享工作区**,您和 agent 都访问相同的文件\n\n如果您使用消息 gateway 并希望 agent 通过 `MEDIA:/...` 发送生成的文件,建议使用专用的宿主可见导出挂载,例如 `/home/user/.hermes/cache/documents:/output`。\n\n- 在 Docker 中将文件写入 `/output/...`\n- 在 `MEDIA:` 中发出**宿主路径**,例如:`MEDIA:/home/user/.hermes/cache/documents/report.txt`\n- **不要**发出 `/workspace/...` 或 `/output/...`,除非该确切路径在宿主上对 gateway 进程也存在\n\n:::warning\nYAML 重复键会静默覆盖之前的键。如果您已有 `docker_volumes:` 块,请将新挂载合并到同一列表中,而不是在文件后面再添加一个 `docker_volumes:` 键。\n:::\n\n也可以通过环境变量设置:`TERMINAL_DOCKER_VOLUMES='[\"/host:/container\"]'`(JSON 数组)。\n\n### Docker 凭据转发\n\n默认情况下,Docker 终端会话不继承任意宿主凭据。如果您需要在容器内使用特定 token,请将其添加到 `terminal.docker_forward_env`。\n\n```yaml\nterminal:\n backend: docker\n docker_forward_env:\n - \"GITHUB_TOKEN\"\n - \"NPM_TOKEN\"\n```\n\nHermes 首先从您当前的 shell 解析每个列出的变量,然后回退到通过 `hermes config set` 保存的 `~/.hermes/.env`。\n\n:::warning\n`docker_forward_env` 中列出的任何内容都会对容器内运行的命令可见。只转发您愿意暴露给终端会话的凭据。\n:::\n\n### 以宿主用户身份运行容器\n\n默认情况下,Docker 容器以 `root`(UID 0)身份运行。在 `/workspace` 或其他绑定挂载中创建的文件在宿主上归 root 所有,因此会话结束后您必须 `sudo chown` 才能从宿主编辑器编辑它们。`terminal.docker_run_as_host_user` 标志解决了这个问题:\n\n```yaml\nterminal:\n backend: docker\n docker_run_as_host_user: true # 默认:false\n```\n\n启用后,Hermes 将 `--user $(id -u):$(id -g)` 附加到 `docker run` 命令,使写入绑定挂载目录(`/workspace`、`/root`、`docker_volumes` 中的任何内容)的文件归您的宿主用户所有,而非 root。权衡:容器将无法再 `apt install` 或写入 `/root/.npm` 等 root 拥有的路径 —— 如果您同时需要这两者,请使用 `HOME` 归非 root 用户所有的基础镜像(或在镜像构建时添加所需工具)。\n\n保持 `false`(默认)以获得向后兼容的行为。当您的工作流主要是\"编辑挂载的宿主文件\"且厌倦了 `sudo chown -R` 时,请开启此选项。\n\n### 可选:将启动目录挂载到 `/workspace`\n\nDocker 沙箱默认保持隔离。Hermes **不会**将您当前的宿主工作目录传入容器,除非您明确选择加入。\n\n在 `config.yaml` 中启用:\n\n```yaml\nterminal:\n backend: docker\n docker_mount_cwd_to_workspace: true\n```\n\n启用后:\n- 如果您从 `~/projects/my-app` 启动 Hermes,该宿主目录将绑定挂载到 `/workspace`\n- Docker 后端从 `/workspace` 开始\n- 文件工具和终端命令都能看到相同的挂载项目\n\n禁用时,`/workspace` 保持沙箱所有,除非您通过 `docker_volumes` 显式挂载内容。\n\n安全权衡:\n- `false` 保留沙箱边界\n- `true` 使沙箱直接访问您启动 Hermes 的目录\n\n仅在您有意希望容器处理实时宿主文件时才选择加入。\n\n### 持久 Shell\n\n默认情况下,每条终端命令在其自己的子进程中运行 —— 工作目录、环境变量和 shell 变量在命令之间重置。启用**持久 shell** 后,单个长期运行的 bash 进程在 `execute()` 调用之间保持存活,使状态在命令之间保持。\n\n这对 **SSH 后端**最有用,它还消除了每条命令的连接开销。持久 shell **对 SSH 默认启用**,对本地后端禁用。\n\n```yaml\nterminal:\n persistent_shell: true # 默认 —— 为 SSH 启用持久 shell\n```\n\n禁用:\n\n```bash\nhermes config set terminal.persistent_shell false\n```\n\n**跨命令保持的内容:**\n- 工作目录(`cd /tmp` 对下一条命令生效)\n- 导出的环境变量(`export FOO=bar`)\n- Shell 变量(`MY_VAR=hello`)\n\n**优先级:**\n\n| 级别 | 变量 | 默认值 |\n|-------|----------|---------|\n| 配置 | `terminal.persistent_shell` | `true` |\n| SSH 覆盖 | `TERMINAL_SSH_PERSISTENT` | 遵循配置 |\n| Local 覆盖 | `TERMINAL_LOCAL_PERSISTENT` | `false` |\n\n每个后端的环境变量具有最高优先级。如果您也想在本地后端使用持久 shell:\n\n```bash\nexport TERMINAL_LOCAL_PERSISTENT=true\n```\n\n:::note\n需要 `stdin_data` 或 sudo 的命令会自动回退到单次模式,因为持久 shell 的 stdin 已被 IPC 协议占用。\n:::\n\n有关每个后端的详细信息,请参阅[代码执行](features/code-execution.md)和 [README 的终端部分](features/tools.md)。\n\n## 技能设置\n\n技能可以通过其 SKILL.md frontmatter 声明自己的配置设置。这些是非机密值(路径、偏好、域设置),存储在 `config.yaml` 的 `skills.config` 命名空间下。\n\n```yaml\nskills:\n config:\n myplugin:\n path: ~/myplugin-data # 示例 —— 每个技能定义自己的键\n```\n\n**技能设置的工作原理:**\n\n- `hermes config migrate` 扫描所有已启用的技能,找到未配置的设置,并提供提示\n- `hermes config show` 在\"技能设置\"下显示所有技能设置及其所属技能\n- 技能加载时,其解析的配置值会自动注入到技能上下文中\n\n**手动设置值:**\n\n```bash\nhermes config set skills.config.myplugin.path ~/myplugin-data\n```\n\n有关在您自己的技能中声明配置设置的详细信息,请参阅[创建技能 — 配置设置](/developer-guide/creating-skills#config-settings-configyaml)。\n\n### Agent 创建技能写入的守卫\n\n当 agent 使用 `skill_manage` 创建、编辑、修补或删除技能时,Hermes 可以选择扫描新/更新的内容以查找危险关键字模式(凭据收集、明显的 prompt 注入、数据外泄指令)。扫描器**默认关闭** —— 合法触及 `~/.ssh/` 或提及 `$OPENAI_API_KEY` 的真实 agent 工作流触发启发式规则过于频繁。如果您希望扫描器在 agent 的技能写入落地前提示您,请重新开启:\n\n```yaml\nskills:\n guard_agent_created: true # 默认:false\n```\n\n开启后,任何被标记的 `skill_manage` 写入都会以审批提示的形式出现,并附带扫描器的理由。接受的写入落地;拒绝的写入向 agent 返回解释性错误。\n\n## 内存配置\n\n```yaml\nmemory:\n memory_enabled: true\n user_profile_enabled: true\n memory_char_limit: 2200 # ~800 tokens\n user_char_limit: 1375 # ~500 tokens\n```\n\n## 文件读取安全\n\n控制单次 `read_file` 调用可以返回多少内容。超过限制的读取将被拒绝,并向 agent 返回错误,提示使用 `offset` 和 `limit` 读取较小范围。这可以防止单次读取压缩的 JS 包或大型数据文件时淹没上下文窗口。\n\n```yaml\nfile_read_max_chars: 100000 # 默认 —— ~25-35K tokens\n```\n\n如果您使用具有大上下文窗口的模型并经常读取大文件,请调高此值。对于小上下文模型,请降低以保持读取高效:\n\n```yaml\n# 大上下文模型(200K+)\nfile_read_max_chars: 200000\n\n# 小型本地模型(16K 上下文)\nfile_read_max_chars: 30000\n```\n\nAgent 还会自动去重文件读取 —— 如果同一文件区域被读取两次且文件未更改,则返回轻量级存根而不是重新发送内容。这在上下文压缩后重置,以便 agent 在内容被摘要后可以重新读取文件。\n\n## 工具输出截断限制\n\n三个相关的上限控制工具在 Hermes 截断之前可以返回多少原始输出:\n\n```yaml\ntool_output:\n max_bytes: 50000 # 终端输出上限(字符)\n max_lines: 2000 # read_file 分页上限\n max_line_length: 2000 # read_file 行号视图中的每行上限\n```\n\n- **`max_bytes`** —— 当 `terminal` 命令产生超过此字符数的合并 stdout/stderr 时,Hermes 保留前 40% 和后 60%,并在中间插入 `[OUTPUT TRUNCATED]` 通知。默认 `50000`(典型分词器约 12-15K tokens)。\n- **`max_lines`** —— 单次 `read_file` 调用的 `limit` 参数上限。超过此值的请求将被截断,以防单次读取淹没上下文窗口。默认 `2000`。\n- **`max_line_length`** —— `read_file` 发出行号视图时应用的每行上限。超过此长度的行将被截断为此字符数,后跟 `... [truncated]`。默认 `2000`。\n\n对于具有大上下文窗口且每次调用可以承受更多原始输出的模型,请调高限制。对于小上下文模型,请降低以保持工具结果紧凑:\n\n```yaml\n# 大上下文模型(200K+)\ntool_output:\n max_bytes: 150000\n max_lines: 5000\n\n# 小型本地模型(16K 上下文)\ntool_output:\n max_bytes: 20000\n max_lines: 500\n```\n\n## 全局工具集禁用\n\n要在 CLI 和每个 gateway 平台上统一禁用特定工具集,请在 `agent.disabled_toolsets` 下列出其名称:\n\n```yaml\nagent:\n disabled_toolsets:\n - memory # 隐藏内存工具 + MEMORY_GUIDANCE 注入\n - web # 任何地方都不使用 web_search / web_extract\n```\n\n这在每个平台的工具配置(由 `hermes tools` 写入的 `platform_toolsets`)**之后**应用,因此此处列出的工具集始终被删除 —— 即使平台的已保存配置仍然列出它。当您希望有一个\"到处关闭 X\"的单一开关而不是编辑 `hermes tools` UI 中的 15+ 个平台行时,请使用此选项。\n\n留空列表或省略键不会产生任何效果。\n\n## Git Worktree 隔离\n\n启用隔离的 git worktree,以便在同一仓库上并行运行多个 agent:\n\n```yaml\nworktree: true # 始终创建 worktree(与 hermes -w 相同)\n# worktree: false # 默认 —— 仅在传递 -w 标志时\n```\n\n启用后,每个 CLI 会话在 `.worktrees/` 下创建一个带有自己分支的新 worktree。Agent 可以编辑文件、提交、推送和创建 PR,而不会相互干扰。干净的 worktree 在退出时删除;脏的 worktree 保留以供手动恢复。\n\n您还可以通过仓库根目录中的 `.worktreeinclude` 列出要复制到 worktree 的 gitignore 文件:\n\n```\n# .worktreeinclude\n.env\n.venv/\nnode_modules/\n```\n\n## 上下文压缩\n\nHermes 自动压缩长对话以保持在模型的上下文窗口内。压缩摘要器是一个单独的 LLM 调用 —— 您可以将其指向任何 provider 或端点。\n\n所有压缩设置都在 `config.yaml` 中(无环境变量)。\n\n### 完整参考\n\n```yaml\ncompression:\n enabled: true # 开启/关闭压缩\n threshold: 0.50 # 在上下文限制的此百分比时压缩\n target_ratio: 0.20 # 保留为最近尾部的阈值分数\n protect_last_n: 20 # 保持未压缩的最少最近消息数\n hygiene_hard_message_limit: 400 # Gateway 安全阀 —— 见下文\n\n# 摘要模型/provider 在 auxiliary: 下配置:\nauxiliary:\n compression:\n model: \"\" # 空 = 使用主聊天模型。覆盖为例如 \"google/gemini-3-flash-preview\" 以获得更便宜/更快的压缩。\n provider: \"auto\" # Provider:\"auto\"、\"openrouter\"、\"nous\"、\"codex\"、\"main\" 等\n base_url: null # 自定义 OpenAI 兼容端点(覆盖 provider)\n```\n\n:::info 旧版配置迁移\n带有 `compression.summary_model`、`compression.summary_provider` 和 `compression.summary_base_url` 的旧版配置在首次加载时自动迁移到 `auxiliary.compression.*`(配置版本 17)。无需手动操作。\n:::\n\n`hygiene_hard_message_limit` 是仅限 gateway 的**预压缩安全阀**。拥有数千条消息的失控会话可能在正常的上下文百分比阈值触发之前就达到模型上下文限制;当消息数超过此上限时,Hermes 强制压缩,无论 token 使用情况如何。默认 `400` —— 对于非常长的会话正常的平台,请调高;要强制更积极的压缩,请降低。在运行中的 gateway 上编辑此值将在下一条消息时生效(见下文)。\n\n:::tip Gateway 热重载压缩和上下文长度\n从最近的版本开始,在运行中的 gateway 上编辑 `config.yaml` 中的 `model.context_length` 或任何 `compression.*` 键将在下一条消息时生效 —— 无需 gateway 重启、`/reset` 或会话轮换。缓存的 agent 签名包含这些键,因此 gateway 在检测到更改时会透明地重建 agent。API 密钥和工具/技能配置仍需要通常的重载路径。\n:::\n\n### 常见设置\n\n**默认(自动检测)—— 无需配置:**\n```yaml\ncompression:\n enabled: true\n threshold: 0.50\n```\n使用您的主 provider 和主模型。如果您希望在比主聊天模型更便宜的模型上进行压缩,请覆盖每任务(例如 `auxiliary.compression.provider: openrouter` + `model: google/gemini-2.5-flash`)。\n\n**强制特定 provider**(基于 OAuth 或 API 密钥):\n```yaml\nauxiliary:\n compression:\n provider: nous\n model: gemini-3-flash\n```\n适用于任何 provider:`nous`、`openrouter`、`codex`、`anthropic`、`main` 等。\n\n**自定义端点**(自托管、Ollama、zai、DeepSeek 等):\n```yaml\nauxiliary:\n compression:\n model: glm-4.7\n base_url: https://api.z.ai/api/coding/paas/v4\n```\n指向自定义 OpenAI 兼容端点。使用 `OPENAI_API_KEY` 进行认证。\n\n### 三个旋钮的交互方式\n\n| `auxiliary.compression.provider` | `auxiliary.compression.base_url` | 结果 |\n|---------------------|---------------------|--------|\n| `auto`(默认) | 未设置 | 自动检测最佳可用 provider |\n| `nous` / `openrouter` / 等 | 未设置 | 强制使用该 provider,使用其认证 |\n| 任意 | 已设置 | 直接使用自定义端点(忽略 provider) |\n\n:::warning 摘要模型上下文长度要求\n摘要模型**必须**具有至少与您的主 agent 模型一样大的上下文窗口。压缩器将对话的完整中间部分发送给摘要模型 —— 如果该模型的上下文窗口小于主模型的,摘要调用将因上下文长度错误而失败。发生这种情况时,中间轮次将**在没有摘要的情况下被丢弃**,静默丢失对话上下文。如果您覆盖模型,请验证其上下文长度满足或超过您的主模型。\n:::\n\n## 上下文引擎\n\n上下文引擎控制在接近模型 token 限制时如何管理对话。内置的 `compressor` 引擎使用有损摘要(参见[上下文压缩](/developer-guide/context-compression-and-caching))。插件引擎可以用替代策略替换它。\n\n```yaml\ncontext:\n engine: \"compressor\" # 默认 —— 内置有损摘要\n```\n\n使用插件引擎(例如,用于无损上下文管理的 LCM):\n\n```yaml\ncontext:\n engine: \"lcm\" # 必须与插件名称匹配\n```\n\n插件引擎**永远不会自动激活** —— 您必须将 `context.engine` 显式设置为插件名称。可用引擎可以通过 `hermes plugins` → Provider Plugins → Context Engine 浏览和选择。\n\n有关内存插件的类似单选系统,请参阅[内存 Providers](/user-guide/features/memory-providers)。\n\n## 迭代预算压力\n\n当 agent 在处理具有许多工具调用的复杂任务时,它可能会在没有意识到预算不足的情况下耗尽其迭代预算(默认:90 轮)。预算压力会在模型接近限制时自动发出警告:\n\n| 阈值 | 级别 | 模型看到的内容 |\n|-----------|-------|---------------------|\n| **70%** | 注意 | `[BUDGET: 63/90. 27 iterations left. Start consolidating.]` |\n| **90%** | 警告 | `[BUDGET WARNING: 81/90. Only 9 left. Respond NOW.]` |\n\n警告注入到最后一个工具结果的 JSON 中(作为 `_budget_warning` 字段),而不是作为单独的消息 —— 这保留了 prompt 缓存,不会破坏对话结构。\n\n```yaml\nagent:\n max_turns: 90 # 每次对话轮次的最大迭代次数(默认:90)\n api_max_retries: 3 # 回退启动前每个 provider 的重试次数(默认:3)\n```\n\n预算压力默认启用。Agent 自然地将警告视为工具结果的一部分,鼓励它在耗尽迭代之前整合工作并提供响应。\n\n当迭代预算完全耗尽时,CLI 向用户显示通知:`⚠ Iteration budget reached (90/90) — response may be incomplete`。如果预算在活跃工作期间耗尽,agent 会在停止前生成已完成内容的摘要。\n\n`agent.api_max_retries` 控制 Hermes 在回退 provider 切换启动**之前**对瞬时错误(速率限制、连接断开、5xx)重试 provider API 调用的次数。默认为 `3` —— 总共四次尝试。如果您配置了[回退 providers](/user-guide/features/fallback-providers) 并希望更快地故障转移,请将其降至 `0`,这样主 provider 上的第一个瞬时错误会立即切换到回退,而不是对不稳定的端点进行重试。\n\n### API 超时\n\nHermes 对流式传输有单独的超时层,以及用于非流式调用的陈旧检测器。陈旧检测器仅在您将其保留为隐式默认值时才会自动调整本地 provider。\n\n| 超时 | 默认值 | 本地 providers | 配置/环境变量 |\n|---------|---------|----------------|--------------|\n| Socket 读取超时 | 120s | 自动提升至 1800s | `HERMES_STREAM_READ_TIMEOUT` |\n| 陈旧流检测 | 180s | 自动禁用 | `HERMES_STREAM_STALE_TIMEOUT` |\n| 陈旧非流检测 | 300s | 保持隐式时自动禁用 | `providers.<id>.stale_timeout_seconds` 或 `HERMES_API_CALL_STALE_TIMEOUT` |\n| API 调用(非流式) | 1800s | 不变 | `providers.<id>.request_timeout_seconds` / `timeout_seconds` 或 `HERMES_API_TIMEOUT` |\n\n**Socket 读取超时**控制 httpx 等待 provider 下一个数据块的时间。本地 LLM 在大上下文上预填充可能需要几分钟才能产生第一个 token,因此当 Hermes 检测到本地端点时,会将此值提升至 30 分钟。如果您显式设置 `HERMES_STREAM_READ_TIMEOUT`,无论端点检测如何,始终使用该值。\n\n**陈旧流检测**终止接收 SSE 保活 ping 但没有实际内容的连接。对于本地 providers,这完全禁用,因为它们在预填充期间不发送保活 ping。\n\n**陈旧非流检测**终止长时间没有响应的非流式调用。默认情况下,Hermes 在本地端点上禁用此功能,以避免长时间预填充期间的误报。如果您显式设置 `providers.<id>.stale_timeout_seconds`、`providers.<id>.models.<model>.stale_timeout_seconds` 或 `HERMES_API_CALL_STALE_TIMEOUT`,即使在本地端点上也会遵守该显式值。\n\n## 上下文压力警告\n\n与迭代预算压力分开,上下文压力跟踪对话距**压缩阈值**有多近 —— 即上下文压缩触发以摘要旧消息的点。这有助于您和 agent 了解对话何时变长。\n\n| 进度 | 级别 | 发生的事情 |\n|----------|-------|-------------|\n| **≥ 60%** 到阈值 | 信息 | CLI 显示青色进度条;gateway 发送信息通知 |\n| **≥ 85%** 到阈值 | 警告 | CLI 显示粗体黄色进度条;gateway 警告压缩即将发生 |\n\n在 CLI 中,上下文压力在工具输出流中显示为进度条:\n\n```\n ◐ context ████████████░░░░░░░░ 62% to compaction 48k threshold (50%) · approaching compaction\n```\n\n在消息平台上,发送纯文本通知:\n\n```\n◐ Context: ████████████░░░░░░░░ 62% to compaction (threshold: 50% of window).\n```\n\n如果自动压缩被禁用,警告会告诉您上下文可能被截断。\n\n上下文压力是自动的 —— 无需配置。它纯粹作为面向用户的通知触发,不修改消息流或向模型上下文注入任何内容。\n\n## 凭据池策略\n\n当您为同一 provider 拥有多个 API 密钥或 OAuth token 时,配置轮换策略:\n\n```yaml\ncredential_pool_strategies:\n openrouter: round_robin # 均匀循环使用密钥\n anthropic: least_used # 始终选择使用最少的密钥\n```\n\n选项:`fill_first`(默认)、`round_robin`、`least_used`、`random`。完整文档请参阅[凭据池](/user-guide/features/credential-pools)。\n\n## Prompt 缓存\n\n当活跃 provider 支持时,Hermes 自动开启跨会话 prompt 缓存 —— 无需用户配置。\n\n对于**原生 Anthropic**、**OpenRouter** 和 **Nous Portal** 上的 Claude,Hermes 在系统提示词和技能块上附加带有 1 小时 TTL(`ttl: \"1h\"`)的 `cache_control` 断点。在新鲜的一小时内首次发送时按完整输入费率计费;同一小时内任何会话的后续发送以折扣缓存读取费率从缓存中提取。这意味着系统提示词、加载的技能内容以及任何长上下文包含的早期部分在第一个小时内跨 `hermes` 会话和分叉子 agent 被重用。\n\nQwen Cloud(阿里巴巴 DashScope)上游将缓存 TTL 限制为 5 分钟,因此 Hermes 在那里使用 5 分钟断点 TTL。其他通过第三方的 Claude 路径(AWS Bedrock、Azure Foundry)回退到 provider 自己的缓存默认值。xAI Grok 使用单独的会话固定对话 ID 机制 —— 参阅 [xAI prompt 缓存](/integrations/providers#xai-grok--responses-api--prompt-caching)。\n\n不存在禁用此功能的旋钮 —— 缓存始终开启,即使在单轮对话中也能节省费用,因为仅系统提示词就占输入 token 数的相当大比例。\n\n## 辅助模型\n\nHermes 使用\"辅助\"模型处理图像分析、网页摘要、浏览器截图分析、会话标题生成和上下文压缩等附带任务。默认情况下(`auxiliary.*.provider: \"auto\"`),Hermes 将每个辅助任务路由到您的**主聊天模型** —— 与您在 `hermes model` 中选择的相同 provider/模型。您无需配置任何内容即可开始,但请注意,在昂贵的推理模型(Opus、MiniMax M2.7 等)上,辅助任务会增加显著成本。如果您希望无论主模型如何都使用便宜且快速的附带任务,请显式设置 `auxiliary.<task>.provider` 和 `auxiliary.<task>.model`(例如,在 OpenRouter 上使用 Gemini Flash 进行视觉和网页提取)。\n\n:::note 为什么 \"auto\" 使用您的主模型\n早期版本将聚合器用户(OpenRouter、Nous Portal)分流到便宜的 provider 端默认值。这令人惊讶 —— 付费购买聚合器订阅的用户会看到不同的模型处理其辅助流量。`auto` 现在对所有人使用主模型,`config.yaml` 中的每任务覆盖仍然优先(见下方[完整辅助配置参考](#full-auxiliary-config-reference))。\n:::\n\n### 交互式配置辅助模型\n\n无需手动编辑 YAML,运行 `hermes model` 并从菜单中选择**\"配置辅助模型\"**。您将获得交互式的每任务选择器:\n\n```\n$ hermes model\n→ Configure auxiliary models\n\n[ ] vision currently: auto / main model\n[ ] web_extract currently: auto / main model\n[ ] title_generation currently: openrouter / google/gemini-3-flash-preview\n[ ] compression currently: auto / main model\n[ ] approval currently: auto / main model\n[ ] triage_specifier currently: auto / main model\n[ ] kanban_decomposer currently: auto / main model\n[ ] profile_describer currently: auto / main model\n```\n\n选择任务,选择 provider(OAuth 流程打开浏览器;API 密钥 provider 提示输入),选择模型。更改持久化到 `config.yaml` 中的 `auxiliary.<task>.*`。与主模型选择器相同的机制 —— 无需学习额外语法。\n\n### 视频教程\n\n<div style={{position: 'relative', width: '100%', aspectRatio: '16 / 9', marginBottom: '1.5rem'}}>\n <iframe\n src=\"https://www.youtube.com/embed/NoF-YajElIM\"\n title=\"Hermes Agent — Auxiliary Models Tutorial\"\n style={{position: 'absolute', top: 0, left: 0, width: '100%', height: '100%', border: 0}}\n allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\"\n allowFullScreen\n />\n</div>\n\n### 通用配置模式\n\nHermes 中的每个模型槽位 —— 辅助任务、压缩、回退 —— 使用相同的三个旋钮:\n\n| 键 | 作用 | 默认值 |\n|-----|-------------|---------|\n| `provider` | 用于认证和路由的 provider | `\"auto\"` |\n| `model` | 请求的模型 | provider 的默认值 |\n| `base_url` | 自定义 OpenAI 兼容端点(覆盖 provider) | 未设置 |\n\n当设置 `base_url` 时,Hermes 忽略 provider 并直接调用该端点(使用 `api_key` 或 `OPENAI_API_KEY` 进行认证)。当仅设置 `provider` 时,Hermes 使用该 provider 的内置认证和基础 URL。\n\n辅助任务的可用 providers:`auto`、`main`,以及[provider 注册表](/reference/environment-variables)中的任何 provider —— `openrouter`、`nous`、`openai-codex`、`copilot`、`copilot-acp`、`anthropic`、`gemini`、`google-gemini-cli`、`qwen-oauth`、`zai`、`kimi-coding`、`kimi-coding-cn`、`minimax`、`minimax-cn`、`minimax-oauth`、`deepseek`、`nvidia`、`xai`、`xai-oauth`、`ollama-cloud`、`alibaba`、`bedrock`、`huggingface`、`arcee`、`xiaomi`、`kilocode`、`opencode-zen`、`opencode-go`、`azure-foundry` —— 或您 `custom_providers` 列表中任何命名的自定义 provider(例如 `provider: \"beans\"`)。\n\n:::tip MiniMax OAuth\n`minimax-oauth` 通过浏览器 OAuth 登录(无需 API 密钥)。运行 `hermes model` 并选择 **MiniMax (OAuth)** 进行认证。辅助任务自动使用 `MiniMax-M2.7-highspeed`。参阅 [MiniMax OAuth 指南](../guides/minimax-oauth.md)。\n:::\n\n:::tip xAI Grok OAuth\n`xai-oauth` 通过浏览器 OAuth 为 SuperGrok 和 X Premium+ 订阅者登录(无需 API 密钥)。运行 `hermes model` 并选择 **xAI Grok OAuth (SuperGrok / Premium+)** 进行认证。相同的 OAuth token 可重用于每个直接到 xAI 的接口(聊天、辅助任务、TTS、图像生成、视频生成、转录)。参阅 [xAI Grok OAuth 指南](../guides/xai-grok-oauth.md),如果 Hermes 在远程主机上,请参阅 [SSH/远程主机上的 OAuth](../guides/oauth-over-ssh.md)。\n:::\n\n:::warning `\"main\"` 仅用于辅助任务\n`\"main\"` provider 选项表示\"使用我的主 agent 使用的任何 provider\" —— 它仅在 `auxiliary:`、`compression:` 和 `fallback_model:` 配置中有效。它**不是**顶级 `model.provider` 设置的有效值。如果您使用自定义 OpenAI 兼容端点,请在 `model:` 部分设置 `provider: custom`。所有主模型 provider 选项请参阅 [AI Providers](/integrations/providers)。\n:::\n\n### 完整辅助配置参考\n\n```yaml\nauxiliary:\n # 图像分析(vision_analyze 工具 + 浏览器截图)\n vision:\n provider: \"auto\" # \"auto\"、\"openrouter\"、\"nous\"、\"codex\"、\"main\" 等\n model: \"\" # 例如 \"openai/gpt-4o\"、\"google/gemini-2.5-flash\"\n base_url: \"\" # 自定义 OpenAI 兼容端点(覆盖 provider)\n api_key: \"\" # base_url 的 API 密钥(回退到 OPENAI_API_KEY)\n timeout: 120 # 秒 —— LLM API 调用超时;视觉负载需要宽裕的超时\n download_timeout: 30 # 秒 —— 图像 HTTP 下载;慢速连接请增加\n\n # 网页摘要 + 浏览器页面文本提取\n web_extract:\n provider: \"auto\"\n model: \"\" # 例如 \"google/gemini-2.5-flash\"\n base_url: \"\"\n api_key: \"\"\n timeout: 360 # 秒(6 分钟)—— 每次尝试的 LLM 摘要\n\n # 危险命令审批分类器\n approval:\n provider: \"auto\"\n model: \"\"\n base_url: \"\"\n api_key: \"\"\n timeout: 30 # 秒\n\n # 上下文压缩超时(与 compression.* 配置分开)\n compression:\n timeout: 120 # 秒 —— 压缩摘要长对话,需要更多时间\n\n # 技能中心 —— 技能匹配和搜索\n skills_hub:\n provider: \"auto\"\n model: \"\"\n base_url: \"\"\n api_key: \"\"\n timeout: 30\n\n # MCP 工具调度\n mcp:\n provider: \"auto\"\n model: \"\"\n base_url: \"\"\n api_key: \"\"\n timeout: 30\n\n # Kanban 分类规格说明器 —— `hermes kanban specify <id>`(或\n # 仪表板上 Triage 列卡片的 ✨ Specify 按钮)使用此\n # 槽位将单行描述扩展为具体规格并将\n # 任务提升到 `todo`。便宜快速的模型在这里效果很好;规格扩展\n # 很短,不需要推理深度。\n triage_specifier:\n provider: \"auto\"\n model: \"\"\n base_url: \"\"\n api_key: \"\"\n timeout: 120\n```\n\n:::tip\n每个辅助任务都有可配置的 `timeout`(秒)。默认值:vision 120s、web_extract 360s、approval 30s、compression 120s。如果您为辅助任务使用慢速本地模型,请增加这些值。Vision 还有单独的 `download_timeout`(默认 30s)用于 HTTP 图像下载 —— 对于慢速连接或自托管图像服务器,请增加此值。\n:::\n\n:::info\n上下文压缩有自己的 `compression:` 块用于阈值,以及 `auxiliary.compression:` 块用于模型/provider 设置 —— 参阅上方的[上下文压缩](#context-compression)。回退模型使用 `fallback_model:` 块 —— 参阅[回退模型](/integrations/providers#fallback-model)。三者都遵循相同的 provider/model/base_url 模式。\n:::\n\n### OpenRouter 路由和辅助任务的 Pareto Code\n\n当辅助任务解析到 OpenRouter(显式或通过 `provider: \"main\"` 而您的主 agent 在 OpenRouter 上)时,主 agent 的 `provider_routing` 和 `openrouter.min_coding_score` 设置**不会传播** —— 按设计,每个辅助任务是独立的。要为特定辅助任务设置 OpenRouter provider 偏好或使用 [Pareto Code 路由器](/integrations/providers#openrouter-pareto-code-router),请通过 `extra_body` 按任务设置:\n\n```yaml\nauxiliary:\n compression:\n provider: openrouter\n model: openrouter/pareto-code # 为此任务使用 Pareto Code 路由器\n extra_body:\n provider: # OpenRouter provider 路由偏好\n order: [anthropic, google] # 按此顺序尝试这些 providers\n sort: throughput # 或 \"price\" | \"latency\"\n # only: [anthropic] # 限制到特定 provider\n # ignore: [deepinfra] # 排除特定 providers\n plugins: # OpenRouter Pareto Code 路由器旋钮\n - id: pareto-router\n min_coding_score: 0.5 # 0.0–1.0;越高 = 更强的编码能力\n```\n\n形状与 OpenRouter 在聊天补全请求体中接受的内容一致。Hermes 原样转发整个 `extra_body`,因此 [openrouter.ai/docs](https://openrouter.ai/docs) 中记录的任何其他 OpenRouter 请求体字段都以相同方式工作。\n\n### 更改视觉模型\n\n使用 GPT-4o 而非 Gemini Flash 进行图像分析:\n\n```yaml\nauxiliary:\n vision:\n model: \"openai/gpt-4o\"\n```\n\n或通过环境变量(在 `~/.hermes/.env` 中):\n\n```bash\nAUXILIARY_VISION_MODEL=openai/gpt-4o\n```\n\n### Provider 选项\n\n这些选项适用于**辅助任务配置**(`auxiliary:`、`compression:`、`fallback_model:`),而非您的主 `model.provider` 设置。\n\n| Provider | 描述 | 要求 |\n|----------|-------------|-------------|\n| `\"auto\"` | 最佳可用(默认)。Vision 尝试 OpenRouter → Nous → Codex。 | — |\n| `\"openrouter\"` | 强制 OpenRouter —— 路由到任何模型(Gemini、GPT-4o、Claude 等) | `OPENROUTER_API_KEY` |\n| `\"nous\"` | 强制 Nous Portal | `hermes auth` |\n| `\"codex\"` | 强制 Codex OAuth(ChatGPT 账户)。支持视觉(gpt-5.3-codex)。 | `hermes model` → Codex |\n| `\"minimax-oauth\"` | 强制 MiniMax OAuth(浏览器登录,无需 API 密钥)。辅助任务使用 MiniMax-M2.7-highspeed。 | `hermes model` → MiniMax (OAuth) |\n| `\"xai-oauth\"` | 强制 xAI Grok OAuth(SuperGrok 或 X Premium+ 订阅者的浏览器登录,无需 API 密钥)。相同的 OAuth token 涵盖聊天、TTS、图像、视频和转录。 | `hermes model` → xAI Grok OAuth (SuperGrok / Premium+) |\n| `\"main\"` | 使用您的活跃自定义/主端点。可以来自 `OPENAI_BASE_URL` + `OPENAI_API_KEY` 或通过 `hermes model` / `config.yaml` 保存的自定义端点。适用于 OpenAI、本地模型或任何 OpenAI 兼容 API。**仅限辅助任务 —— 对 `model.provider` 无效。** | 自定义端点凭据 + 基础 URL |\n\n当您希望附带任务绕过默认路由器时,主 provider 目录中的直接 API 密钥 providers 也在这里工作。配置 `GMI_API_KEY` 后,`gmi` 有效:\n\n```yaml\nauxiliary:\n compression:\n provider: \"gmi\"\n model: \"anthropic/claude-opus-4.6\"\n```\n\n对于 GMI 辅助路由,使用 GMI 的 `/v1/models` 端点返回的确切模型 ID。\n\n### 常见设置\n\n**使用直接自定义端点**(比 `provider: \"main\"` 对本地/自托管 API 更清晰):\n```yaml\nauxiliary:\n vision:\n base_url: \"http://localhost:1234/v1\"\n api_key: \"local-key\"\n model: \"qwen2.5-vl\"\n```\n\n`base_url` 优先于 `provider`,因此这是将辅助任务路由到特定端点的最明确方式。对于直接端点覆盖,Hermes 使用配置的 `api_key` 或回退到 `OPENAI_API_KEY`;它不会为该自定义端点重用 `OPENROUTER_API_KEY`。\n\n**使用 OpenAI API 密钥进行视觉:**\n```yaml\n# 在 ~/.hermes/.env 中:\n# OPENAI_BASE_URL=https://api.openai.com/v1\n# OPENAI_API_KEY=sk-...\n\nauxiliary:\n vision:\n provider: \"main\"\n model: \"gpt-4o\" # 或 \"gpt-4o-mini\" 更便宜\n```\n\n**使用 OpenRouter 进行视觉**(路由到任何模型):\n```yaml\nauxiliary:\n vision:\n provider: \"openrouter\"\n model: \"openai/gpt-4o\" # 或 \"google/gemini-2.5-flash\" 等\n```\n\n**使用 Codex OAuth**(ChatGPT Pro/Plus 账户 —— 无需 API 密钥):\n```yaml\nauxiliary:\n vision:\n provider: \"codex\" # 使用您的 ChatGPT OAuth token\n # 模型默认为 gpt-5.3-codex(支持视觉)\n```\n\n**使用 MiniMax OAuth**(浏览器登录,无需 API 密钥):\n```yaml\nmodel:\n default: MiniMax-M2.7\n provider: minimax-oauth\n base_url: https://api.minimax.io/anthropic\n```\n运行 `hermes model` 并选择 **MiniMax (OAuth)** 自动登录并设置此项。对于中国区域,基础 URL 将是 `https://api.minimaxi.com/anthropic`。完整演练请参阅 [MiniMax OAuth 指南](../guides/minimax-oauth.md)。\n\n**使用本地/自托管模型:**\n```yaml\nauxiliary:\n vision:\n provider: \"main\" # 使用您的活跃自定义端点\n model: \"my-local-model\"\n```\n\n`provider: \"main\"` 使用 Hermes 用于普通聊天的任何 provider —— 无论是命名的自定义 provider(例如 `beans`)、内置 provider(如 `openrouter`)还是旧版 `OPENAI_BASE_URL` 端点。\n\n:::tip\n如果您使用 Codex OAuth 作为主模型 provider,视觉会自动工作 —— 无需额外配置。Codex 包含在视觉的自动检测链中。\n:::\n\n:::warning\n**视觉需要多模态模型。** 如果您设置 `provider: \"main\"`,请确保您的端点支持多模态/视觉 —— 否则图像分析将失败。\n:::\n\n### 环境变量(旧版)\n\n辅助模型也可以通过环境变量配置。但是,`config.yaml` 是首选方法 —— 它更易于管理,并支持所有选项,包括 `base_url` 和 `api_key`。\n\n| 设置 | 环境变量 |\n|---------|---------------------|\n| Vision provider | `AUXILIARY_VISION_PROVIDER` |\n| Vision 模型 | `AUXILIARY_VISION_MODEL` |\n| Vision 端点 | `AUXILIARY_VISION_BASE_URL` |\n| Vision API 密钥 | `AUXILIARY_VISION_API_KEY` |\n| Web 提取 provider | `AUXILIARY_WEB_EXTRACT_PROVIDER` |\n| Web 提取模型 | `AUXILIARY_WEB_EXTRACT_MODEL` |\n| Web 提取端点 | `AUXILIARY_WEB_EXTRACT_BASE_URL` |\n| Web 提取 API 密钥 | `AUXILIARY_WEB_EXTRACT_API_KEY` |\n\n压缩和回退模型设置仅限 config.yaml。\n\n:::tip\n运行 `hermes config` 查看您当前的辅助模型设置。覆盖仅在与默认值不同时显示。\n:::\n\n## 推理努力程度\n\n控制模型在响应前进行多少\"思考\":\n\n```yaml\nagent:\n reasoning_effort: \"\" # 空 = 中等(默认)。选项:none、minimal、low、medium、high、xhigh(最大)\n```\n\n未设置时(默认),推理努力程度默认为\"medium\" —— 适合大多数任务的平衡级别。设置值会覆盖它 —— 更高的推理努力程度在复杂任务上提供更好的结果,但代价是更多 token 和延迟。\n\n您也可以在运行时使用 `/reasoning` 命令更改推理努力程度:\n\n```\n/reasoning # 显示当前努力程度和显示状态\n/reasoning high # 将推理努力程度设为 high\n/reasoning none # 禁用推理\n/reasoning show # 在每次响应上方显示模型思考\n/reasoning hide # 隐藏模型思考\n```\n\n## 工具使用强制\n\n某些模型偶尔会将预期操作描述为文本而不是进行工具调用(\"我会运行测试...\"而不是实际调用终端)。工具使用强制会注入系统提示词指导,引导模型实际调用工具。\n\n```yaml\nagent:\n tool_use_enforcement: \"auto\" # \"auto\" | true | false | [\"model-substring\", ...]\n```\n\n| 值 | 行为 |\n|-------|----------|\n| `\"auto\"`(默认) | 对匹配以下模型启用:`gpt`、`codex`、`gemini`、`gemma`、`grok`。对所有其他模型禁用(Claude、DeepSeek、Qwen 等)。 |\n| `true` | 始终启用,无论模型如何。如果您注意到当前模型描述操作而不是执行操作,请使用此选项。 |\n| `false` | 始终禁用,无论模型如何。 |\n| `[\"gpt\", \"codex\", \"qwen\", \"llama\"]` | 仅当模型名称包含列出的子字符串之一时启用(不区分大小写)。 |\n\n### 注入的内容\n\n启用后,系统提示词中可能会添加三层指导:\n\n1. **通用工具使用强制**(所有匹配模型)—— 指示模型立即进行工具调用而不是描述意图,持续工作直到任务完成,永远不要以未来操作的承诺结束轮次。\n\n2. **OpenAI 执行纪律**(仅限 GPT 和 Codex 模型)—— 针对 GPT 特定失败模式的额外指导:在部分结果上放弃工作、跳过先决条件查找、幻觉而不是使用工具、在未验证的情况下宣布\"完成\"。\n\n3. **Google 操作指导**(仅限 Gemini 和 Gemma 模型)—— 简洁性、绝对路径、并行工具调用和编辑前验证模式。\n\n这些对用户透明,仅影响系统提示词。已经可靠使用工具的模型(如 Claude)不需要此指导,这就是为什么 `\"auto\"` 排除它们。\n\n### 何时开启\n\n如果您使用的模型不在默认自动列表中,并注意到它经常描述它*会*做什么而不是实际去做,请设置 `tool_use_enforcement: true` 或将模型子字符串添加到列表中:\n\n```yaml\nagent:\n tool_use_enforcement: [\"gpt\", \"codex\", \"gemini\", \"grok\", \"my-custom-model\"]\n```\n\n## TTS 配置\n\n```yaml\ntts:\n provider: \"edge\" # \"edge\" | \"elevenlabs\" | \"openai\" | \"minimax\" | \"mistral\" | \"gemini\" | \"xai\" | \"neutts\"\n speed: 1.0 # 全局速度倍数(所有 provider 的回退)\n edge:\n voice: \"en-US-AriaNeural\" # 322 种声音,74 种语言\n speed: 1.0 # 速度倍数(转换为速率百分比,例如 1.5 → +50%)\n elevenlabs:\n voice_id: \"pNInz6obpgDQGcFmaJgB\"\n model_id: \"eleven_multilingual_v2\"\n openai:\n model: \"gpt-4o-mini-tts\"\n voice: \"alloy\" # alloy、echo、fable、onyx、nova、shimmer\n speed: 1.0 # 速度倍数(API 限制为 0.25–4.0)\n base_url: \"https://api.openai.com/v1\" # 覆盖 OpenAI 兼容 TTS 端点\n minimax:\n speed: 1.0 # 语音速度倍数\n # base_url: \"\" # 可选:覆盖 OpenAI 兼容 TTS 端点\n mistral:\n model: \"voxtral-mini-tts-2603\"\n voice_id: \"c69964a6-ab8b-4f8a-9465-ec0925096ec8\" # Paul - Neutral(默认)\n gemini:\n model: \"gemini-2.5-flash-preview-tts\" # 或 gemini-2.5-pro-preview-tts\n voice: \"Kore\" # 30 种预置声音:Zephyr、Puck、Kore、Enceladus 等\n xai:\n voice_id: \"eve\" # xAI TTS 声音\n language: \"en\" # ISO 639-1\n sample_rate: 24000\n bit_rate: 128000 # MP3 比特率\n # base_url: \"https://api.x.ai/v1\"\n neutts:\n ref_audio: ''\n ref_text: ''\n model: neuphonic/neutts-air-q4-gguf\n device: cpu\n```\n\n这控制 `text_to_speech` 工具和语音模式中的口语回复(CLI 中的 `/voice tts` 或消息 gateway)。\n\n**速度回退层次:** provider 特定速度(例如 `tts.edge.speed`)→ 全局 `tts.speed` → `1.0` 默认值。设置全局 `tts.speed` 以在所有 provider 上应用统一速度,或按 provider 覆盖以进行精细控制。\n\n## 显示设置\n\n```yaml\ndisplay:\n tool_progress: all # off | new | all | verbose\n tool_progress_command: false # 在消息 gateway 中启用 /verbose 斜杠命令\n platforms: {} # 每平台显示覆盖(见下文)\n tool_progress_overrides: {} # 已弃用 —— 改用 display.platforms\n interim_assistant_messages: true # Gateway:将自然的轮次中 assistant 更新作为单独消息发送\n skin: default # 内置或自定义 CLI 皮肤(参阅 user-guide/features/skins)\n personality: \"kawaii\" # 旧版外观字段,仍在某些摘要中显示\n compact: false # 紧凑输出模式(减少空白)\n resume_display: full # full(恢复时显示之前的消息)| minimal(仅单行)\n bell_on_complete: false # 当 agent 完成时播放终端铃声(适合长任务)\n show_reasoning: false # 在每次响应上方显示模型推理/思考(用 /reasoning show|hide 切换)\n streaming: false # 将 token 实时流式传输到终端\n show_cost: false # 在 CLI 状态栏中显示估计 $ 成本\n timestamps: false # 为 true 时,在 CLI/TUI 记录中为用户和 assistant 标签添加 [HH:MM] 时间戳前缀\n tool_preview_length: 0 # 工具调用预览的最大字符数(0 = 无限制,显示完整路径/命令)\n runtime_footer: # Gateway:在最终回复中附加运行时上下文页脚\n enabled: false\n fields: [\"model\", \"context_pct\", \"cwd\"]\n file_mutation_verifier: true # 当本轮 write_file/patch 调用失败时附加建议性页脚\n language: en # 静态消息的 UI 语言(审批提示、部分 gateway 回复)。en | zh | zh-hant | ja | de | es | fr | tr | uk | af | ko | it | ga | pt | ru | hu\n```\n\n### 文件变更验证器\n\n当 `display.file_mutation_verifier` 为 `true`(默认)时,每当本轮中 `write_file` 或 `patch` 调用失败且从未被对同一路径的成功写入取代时,Hermes 会在 assistant 的最终响应中附加一行建议。这捕获了\"批量并行补丁,一半静默失败,模型总结成功\"这类过度声明,而无需您在每次编辑后手动运行 `git status`。\n\n示例页脚:\n\n```\n⚠️ File-mutation verifier: 3 file(s) were NOT modified this turn despite any wording above that may suggest otherwise. Run `git status` or `read_file` to confirm.\n • concepts/automatic-organization.md — [patch] Could not find match for old_string\n • concepts/lora.md — [patch] Could not find match for old_string\n • concepts/rag-pipeline.md — [patch] Could not find match for old_string\n```\n\n设置 `file_mutation_verifier: false`(或 `HERMES_FILE_MUTATION_VERIFIER=0`)以禁止页脚。验证器仅在轮次结束时有真实失败未解决时触发 —— 在同一轮次内重试失败补丁并成功的模型不会为该文件触发它。\n\n### 静态消息的 UI 语言\n\n`display.language` 设置翻译一小组静态面向用户的消息 —— CLI 审批提示、少数 gateway 斜杠命令回复(例如重启排空通知、\"审批已过期\"、\"目标已清除\")。它**不**翻译 agent 响应、日志行、工具输出、错误回溯或斜杠命令描述 —— 这些保持英文。如果您希望 agent 本身用另一种语言回复,只需在您的提示词或系统消息中告诉它。\n\n支持的值:`en`(默认)、`zh`(简体中文)、`ja`(日语)、`de`(德语)、`es`(西班牙语)、`fr`(法语)、`tr`(土耳其语)、`uk`(乌克兰语)。未知值回退到英文。\n\n您也可以使用 `HERMES_LANGUAGE` 环境变量按会话设置,它会覆盖配置值。\n\n```yaml\ndisplay:\n language: zh # CLI 审批提示以中文显示\n```\n\n| 模式 | 您看到的内容 |\n|------|-------------|\n| `off` | 静默 —— 仅最终响应 |\n| `new` | 仅在工具更改时显示工具指示器 |\n| `all` | 每次工具调用附带简短预览(默认) |\n| `verbose` | 完整参数、结果和调试日志 |\n\n在 CLI 中,使用 `/verbose` 循环切换这些模式。要在消息平台(Telegram、Discord、Slack 等)中使用 `/verbose`,请在上方的 `display` 部分设置 `tool_progress_command: true`。该命令将循环切换模式并保存到配置。\n\n### 运行时元数据页脚(仅限 gateway)\n\n当 `display.runtime_footer.enabled: true` 时,Hermes 在每个 gateway 轮次的**最终**消息中附加一个小型运行时上下文页脚。目前页脚可显示模型、上下文窗口百分比和当前工作目录。默认关闭;如果您的团队希望每个回复都包含这些来源信息,请按 gateway 选择加入。\n\n```yaml\ndisplay:\n runtime_footer:\n enabled: true\n fields: [\"model\", \"context_pct\", \"cwd\"] # 支持字段:model、context_pct、cwd\n```\n\n`/footer` 斜杠命令在任何会话中运行时切换此功能。\n\n附加到 Telegram/Discord/Slack 回复的示例页脚:\n\n```\n— claude-opus-4.7 · 12 tool calls · 2m 14s · $0.042\n```\n\n只有轮次的**最终**消息获得页脚;中间更新保持干净。\n\n### 每平台进度覆盖\n\n不同平台有不同的详细程度需求。例如,Signal 无法编辑消息,因此每次进度更新都会成为单独的消息 —— 很嘈杂。使用 `display.platforms` 设置每平台模式:\n\n```yaml\ndisplay:\n tool_progress: all # 全局默认\n platforms:\n signal:\n tool_progress: 'off' # 在 Signal 上静默进度\n telegram:\n tool_progress: verbose # 在 Telegram 上详细进度\n slack:\n tool_progress: 'off' # 在共享 Slack 工作区中保持安静\n```\n\n没有覆盖的平台回退到全局 `tool_progress` 值。有效平台键:`telegram`、`discord`、`slack`、`signal`、`whatsapp`、`matrix`、`mattermost`、`email`、`sms`、`homeassistant`、`dingtalk`、`feishu`、`wecom`、`weixin`、`bluebubbles`、`qqbot`。旧版 `display.tool_progress_overrides` 键仍可加载以向后兼容,但已弃用,并在首次加载时迁移到 `display.platforms`。\n\n`interim_assistant_messages` 仅限 gateway。启用后,Hermes 将已完成的轮次中 assistant 更新作为单独的聊天消息发送。这与 `tool_progress` 无关,不需要 gateway 流式传输。\n\n## 隐私\n\n```yaml\nprivacy:\n redact_pii: false # 从 LLM 上下文中删除 PII(仅限 gateway)\n```\n\n当 `redact_pii` 为 `true` 时,gateway 在将系统提示词发送到受支持平台上的 LLM 之前,会从中删除个人身份信息:\n\n| 字段 | 处理方式 |\n|-------|-----------|\n| 电话号码(WhatsApp/Signal 上的用户 ID) | 哈希为 `user_<12-char-sha256>` |\n| 用户 ID | 哈希为 `user_<12-char-sha256>` |\n| 聊天 ID | 数字部分哈希,保留平台前缀(`telegram:<hash>`) |\n| 主频道 ID | 数字部分哈希 |\n| 用户名/昵称 | **不受影响**(用户选择的,公开可见) |\n\n**平台支持:** 删除适用于 WhatsApp、Signal 和 Telegram。Discord 和 Slack 被排除,因为它们的提及系统(`<@user_id>`)需要 LLM 上下文中的真实 ID。\n\n哈希是确定性的 —— 同一用户始终映射到同一哈希,因此模型仍然可以在群聊中区分用户。路由和传递在内部使用原始值。\n\n## 语音转文字(STT)\n\n```yaml\nstt:\n provider: \"local\" # \"local\" | \"groq\" | \"openai\" | \"mistral\"\n local:\n model: \"base\" # tiny、base、small、medium、large-v3\n openai:\n model: \"whisper-1\" # whisper-1 | gpt-4o-mini-transcribe | gpt-4o-transcribe\n # model: \"whisper-1\" # 旧版回退键仍受支持\n```\n\nProvider 行为:\n\n- `local` 使用在您机器上运行的 `faster-whisper`。使用 `pip install faster-whisper` 单独安装。\n- `groq` 使用 Groq 的 Whisper 兼容端点,读取 `GROQ_API_KEY`。\n- `openai` 使用 OpenAI 语音 API,读取 `VOICE_TOOLS_OPENAI_KEY`。\n\n如果请求的 provider 不可用,Hermes 按此顺序自动回退:`local` → `groq` → `openai`。\n\nGroq 和 OpenAI 模型覆盖由环境变量驱动:\n\n```bash\nSTT_GROQ_MODEL=whisper-large-v3-turbo\nSTT_OPENAI_MODEL=whisper-1\nGROQ_BASE_URL=https://api.groq.com/openai/v1\nSTT_OPENAI_BASE_URL=https://api.openai.com/v1\n```\n\n## 语音模式(CLI)\n\n```yaml\nvoice:\n record_key: \"ctrl+b\" # CLI 内的按键通话键\n max_recording_seconds: 120 # 长录音的硬停止\n auto_tts: false # /voice on 时自动启用口语回复\n beep_enabled: true # 在 CLI 语音模式中播放录音开始/停止提示音\n silence_threshold: 200 # 语音检测的 RMS 阈值\n silence_duration: 3.0 # 自动停止前的静默秒数\n```\n\n在 CLI 中使用 `/voice on` 启用麦克风模式,使用 `record_key` 开始/停止录音,使用 `/voice tts` 切换口语回复。端到端设置和平台特定行为请参阅[语音模式](/user-guide/features/voice-mode)。\n\n## 流式传输\n\n将 token 实时流式传输到终端或消息平台,而不是等待完整响应。\n\n### CLI 流式传输\n\n```yaml\ndisplay:\n streaming: true # 实时将 token 流式传输到终端\n show_reasoning: true # 同时流式传输推理/思考 token(可选)\n```\n\n启用后,响应在流式传输框内逐 token 出现。工具调用仍然静默捕获。如果 provider 不支持流式传输,它会自动回退到正常显示。\n\n### Gateway 流式传输(Telegram、Discord、Slack)\n\n```yaml\nstreaming:\n enabled: true # 启用渐进式消息编辑\n transport: edit # \"edit\"(渐进式消息编辑)或 \"off\"\n edit_interval: 0.3 # 消息编辑之间的秒数\n buffer_threshold: 40 # 强制编辑刷新前的字符数\n cursor: \" ▉\" # 流式传输期间显示的光标\n fresh_final_after_seconds: 60 # 当预览超过此时间时发送新的最终消息(Telegram);0 = 始终就地编辑\n```\n\n启用后,bot 在第一个 token 时发送消息,然后随着更多 token 到来渐进式编辑它。不支持消息编辑的平台(Signal、Email、Home Assistant)在第一次尝试时自动检测 —— 该会话的流式传输被优雅地禁用,不会产生大量消息。\n\n对于不带渐进式 token 编辑的独立自然轮次中 assistant 更新,请设置 `display.interim_assistant_messages: true`。\n\n**溢出处理:** 如果流式传输的文本超过平台的消息长度限制(约 4096 字符),当前消息被最终化,新消息自动开始。\n\n**新的最终消息(Telegram):** Telegram 的 `editMessageText` 保留原始消息时间戳,因此长时间运行的流式回复即使在完成后也会保留第一个 token 的时间戳。当 `fresh_final_after_seconds > 0`(默认 `60`)时,完成的回复作为全新消息传递(尽力删除旧预览),以便 Telegram 的可见时间戳反映完成时间。短预览仍然就地最终化。设置为 `0` 以始终就地编辑。\n\n:::note\n流式传输默认禁用。在 `~/.hermes/config.yaml` 中启用以尝试流式传输 UX。\n:::\n\n## 群聊会话隔离\n\n控制共享聊天是每个房间保持一个对话还是每个参与者一个对话:\n\n```yaml\ngroup_sessions_per_user: true # true = 群组/频道中每用户隔离,false = 每个聊天一个共享会话\n```\n\n- `true` 是默认和推荐设置。在 Discord 频道、Telegram 群组、Slack 频道和类似共享上下文中,当平台提供用户 ID 时,每个发送者获得自己的会话。\n- `false` 恢复到旧的共享房间行为。如果您明确希望 Hermes 将频道视为一个协作对话,这可能有用,但这也意味着用户共享上下文、token 成本和中断状态。\n- 私信不受影响。Hermes 仍然像往常一样通过聊天/DM ID 键入 DM。\n- 线程与其父频道保持隔离;使用 `true` 时,每个参与者在线程内也获得自己的会话。\n\n有关行为详情和示例,请参阅[会话](/user-guide/sessions)和 [Discord 指南](/user-guide/messaging/discord)。\n\n## 未授权 DM 行为\n\n控制当未知用户发送私信时 Hermes 的行为:\n\n```yaml\nunauthorized_dm_behavior: pair\n\nwhatsapp:\n unauthorized_dm_behavior: ignore\n```\n\n- `pair` 是默认值。Hermes 拒绝访问,但在 DM 中回复一次性配对码。\n- `ignore` 静默丢弃未授权的 DM。\n- 平台部分覆盖全局默认值,因此您可以在广泛范围内保持配对启用,同时使一个平台更安静。\n\n## 快速命令\n\n定义自定义命令,这些命令要么在不调用 LLM 的情况下运行 shell 命令,要么将一个斜杠命令别名为另一个。Exec 快速命令是零 token 的,对于从消息平台(Telegram、Discord 等)进行快速服务器检查或实用脚本很有用。\n\n```yaml\nquick_commands:\n status:\n type: exec\n command: systemctl status hermes-agent\n disk:\n type: exec\n command: df -h /\n update:\n type: exec\n command: cd ~/.hermes/hermes-agent && git pull && pip install -e .\n gpu:\n type: exec\n command: nvidia-smi --query-gpu=name,utilization.gpu,memory.used,memory.total --format=csv,noheader\n restart:\n type: alias\n target: /gateway restart\n```\n\n用法:在 CLI 或任何消息平台中输入 `/status`、`/disk`、`/update`、`/gpu` 或 `/restart`。`exec` 命令在宿主本地运行并直接返回输出 —— 无 LLM 调用,不消耗 token。`alias` 命令重写为配置的斜杠命令目标。\n\n- **30 秒超时** —— 长时间运行的命令被终止并显示错误消息\n- **优先级** —— 快速命令在技能命令之前检查,因此您可以覆盖技能名称\n- **自动补全** —— 快速命令在调度时解析,不显示在内置斜杠命令自动补全表中\n- **类型** —— 支持的类型为 `exec` 和 `alias`;其他类型显示错误\n- **到处可用** —— CLI、Telegram、Discord、Slack、WhatsApp、Signal、Email、Home Assistant\n\n仅字符串的 prompt 快捷方式不是有效的快速命令。对于可重用的 prompt 工作流,请创建技能或别名到现有斜杠命令。\n\n## 人类延迟\n\n在消息平台中模拟类人响应节奏:\n\n```yaml\nhuman_delay:\n mode: \"off\" # off | natural | custom\n min_ms: 800 # 最小延迟(自定义模式)\n max_ms: 2500 # 最大延迟(自定义模式)\n```\n\n## 代码执行\n\n配置 `execute_code` 工具:\n\n```yaml\ncode_execution:\n mode: project # project(默认)| strict\n timeout: 300 # 最大执行时间(秒)\n max_tool_calls: 50 # 代码执行中的最大工具调用次数\n```\n\n**`mode`** 控制脚本的工作目录和 Python 解释器:\n\n- **`project`**(默认)—— 脚本在会话的工作目录中以活跃 virtualenv/conda 环境的 python 运行。项目依赖(`pandas`、`torch`、项目包)和相对路径(`.env`、`./data.csv`)自然解析,与 `terminal()` 看到的一致。\n- **`strict`** —— 脚本在临时暂存目录中以 `sys.executable`(Hermes 自己的 python)运行。最大可重现性,但项目依赖和相对路径不会解析。\n\n环境清理(删除 `*_API_KEY`、`*_TOKEN`、`*_SECRET`、`*_PASSWORD`、`*_CREDENTIAL`、`*_PASSWD`、`*_AUTH`)和工具白名单在两种模式下完全相同 —— 切换模式不会改变安全态势。\n\n## Web 搜索后端\n\n`web_search` 和 `web_extract` 工具支持五种后端 provider。在 `config.yaml` 中或通过 `hermes tools` 配置后端:\n\n```yaml\nweb:\n backend: firecrawl # firecrawl | searxng | parallel | tavily | exa\n\n # 或使用每功能键混合 provider(例如免费搜索 + 付费提取):\n search_backend: \"searxng\"\n extract_backend: \"firecrawl\"\n```\n\n| 后端 | 环境变量 | 搜索 | 提取 |\n|---------|---------|--------|---------|\n| **Firecrawl**(默认) | `FIRECRAWL_API_KEY` | ✔ | ✔ |\n| **SearXNG** | `SEARXNG_URL` | ✔ | — |\n| **Parallel** | `PARALLEL_API_KEY` | ✔ | ✔ |\n| **Tavily** | `TAVILY_API_KEY` | ✔ | ✔ |\n| **Exa** | `EXA_API_KEY` | ✔ | ✔ |\n\n**后端选择:** 如果未设置 `web.backend`,后端从可用的 API 密钥自动检测。如果仅设置了 `SEARXNG_URL`,使用 SearXNG。如果仅设置了 `EXA_API_KEY`,使用 Exa。如果仅设置了 `TAVILY_API_KEY`,使用 Tavily。如果仅设置了 `PARALLEL_API_KEY`,使用 Parallel。否则 Firecrawl 是默认值。\n\n**SearXNG** 是一个免费、自托管、尊重隐私的元搜索引擎,查询 70+ 个搜索引擎。无需 API 密钥 —— 只需将 `SEARXNG_URL` 设置为您的实例(例如 `http://localhost:8080`)。SearXNG 仅限搜索;`web_extract` 需要单独的提取 provider(设置 `web.extract_backend`)。Docker 设置说明请参阅 [Web 搜索设置指南](/user-guide/features/web-search)。\n\n**自托管 Firecrawl:** 设置 `FIRECRAWL_API_URL` 指向您自己的实例。设置自定义 URL 后,API 密钥变为可选(在服务器上设置 `USE_DB_AUTHENTICATION=***` 以禁用认证)。\n\n**Parallel 搜索模式:** 设置 `PARALLEL_SEARCH_MODE` 控制搜索行为 —— `fast`、`one-shot` 或 `agentic`(默认:`agentic`)。\n\n**Exa:** 在 `~/.hermes/.env` 中设置 `EXA_API_KEY`。支持 `category` 过滤(`company`、`research paper`、`news`、`people`、`personal site`、`pdf`)和域名/日期过滤器。\n\n## 浏览器\n\n配置浏览器自动化行为:\n\n```yaml\nbrowser:\n inactivity_timeout: 120 # 自动关闭空闲会话前的秒数\n command_timeout: 30 # 浏览器命令超时(截图、导航等)(秒)\n record_sessions: false # 自动将浏览器会话录制为 WebM 视频到 ~/.hermes/browser_recordings/\n # 可选 CDP 覆盖 —— 设置后,Hermes 直接附加到您自己的\n # Chromium 系浏览器(通过 /browser connect),而不是启动无头浏览器。\n cdp_url: \"\"\n # 对话框监督器 —— 控制当 CDP 后端附加时(Browserbase、本地 Chromium 系\n # 浏览器通过 /browser connect)如何处理原生 JS 对话框(alert/confirm/prompt)。\n # 在 Camofox 和默认本地 agent 浏览器模式下忽略。\n dialog_policy: must_respond # must_respond | auto_dismiss | auto_accept\n dialog_timeout_s: 300 # must_respond 下的安全自动关闭(秒)\n camofox:\n managed_persistence: false # 为 true 时,Camofox 会话跨重启持久化 cookie/登录\n user_id: \"\" # 可选的外部管理 Camofox userId\n session_key: \"\" # Hermes 创建标签页时发送的可选会话密钥\n adopt_existing_tab: false # 在创建新标签页之前重用此身份的现有标签页\n```\n\n**对话框策略:**\n\n- `must_respond`(默认)—— 捕获对话框,在 `browser_snapshot.pending_dialogs` 中显示,等待 agent 调用 `browser_dialog(action=...)`。在 `dialog_timeout_s` 秒内无响应后,对话框被自动关闭以防止页面的 JS 线程永久停滞。\n- `auto_dismiss` —— 捕获,立即关闭。Agent 仍然在事后的 `browser_snapshot.recent_dialogs` 中看到对话框记录,`closed_by=\"auto_policy\"`。\n- `auto_accept` —— 捕获,立即接受。适用于有激进 `beforeunload` 提示的页面。\n\n完整对话框工作流请参阅[浏览器功能页面](./features/browser.md#browser_dialog)。\n\n浏览器工具集支持多个 provider。有关 Browserbase、Browser Use 和本地 Chromium 系 CDP 设置的详细信息,请参阅[浏览器功能页面](/user-guide/features/browser)。\n\n## 时区\n\n使用 IANA 时区字符串覆盖服务器本地时区。影响日志中的时间戳、cron 调度和系统提示词时间注入。\n\n```yaml\ntimezone: \"America/New_York\" # IANA 时区(默认:\"\" = 服务器本地时间)\n```\n\n支持的值:任何 IANA 时区标识符(例如 `America/New_York`、`Europe/London`、`Asia/Kolkata`、`UTC`)。留空或省略以使用服务器本地时间。\n\n## Discord\n\n为消息 gateway 配置 Discord 特定行为:\n\n```yaml\ndiscord:\n require_mention: true # 在服务器频道中需要 @提及才能响应\n free_response_channels: \"\" # 逗号分隔的频道 ID,bot 在这些频道无需 @提及即可响应\n auto_thread: true # 在频道中 @提及时自动创建线程\n```\n\n- `require_mention` —— 为 `true`(默认)时,bot 仅在服务器频道中被 `@BotName` 提及时响应。DM 始终无需提及即可工作。\n- `free_response_channels` —— 逗号分隔的频道 ID 列表,bot 在这些频道对每条消息响应,无需提及。\n- `auto_thread` —— 为 `true`(默认)时,频道中的提及会自动为对话创建线程,保持频道整洁(类似 Slack 线程)。\n\n## 安全\n\n预执行安全扫描和机密脱敏:\n\n```yaml\nsecurity:\n redact_secrets: false # 在工具输出和日志中脱敏 API 密钥模式(默认关闭)\n tirith_enabled: true # 为终端命令启用 Tirith 安全扫描\n tirith_path: \"tirith\" # tirith 二进制文件路径(默认:$PATH 中的 \"tirith\")\n tirith_timeout: 5 # 等待 tirith 扫描的秒数\n tirith_fail_open: true # 如果 tirith 不可用,允许命令执行\n website_blocklist: # 参见下方网站黑名单部分\n enabled: false\n domains: []\n shared_files: []\n```\n\n- `redact_secrets` —— 为 `true` 时,自动检测并脱敏工具输出中看起来像 API 密钥、token 和密码的模式,然后再进入对话上下文和日志。**默认关闭** —— 如果您经常在工具输出中处理真实凭据并希望有安全网,请启用。显式设置为 `true` 以开启。\n- `tirith_enabled` —— 为 `true` 时,终端命令在执行前由 [Tirith](https://github.com/sheeki03/tirith) 扫描以检测潜在危险操作。\n- `tirith_path` —— tirith 二进制文件的路径。如果 tirith 安装在非标准位置,请设置此项。\n- `tirith_timeout` —— 等待 tirith 扫描的最大秒数。如果扫描超时,命令继续执行。\n- `tirith_fail_open` —— 为 `true`(默认)时,如果 tirith 不可用或失败,允许命令执行。设置为 `false` 以在 tirith 无法验证时阻止命令。\n\n## 网站黑名单\n\n阻止 agent 的 web 和浏览器工具访问特定域名:\n\n```yaml\nsecurity:\n website_blocklist:\n enabled: false # 启用 URL 阻止(默认:false)\n domains: # 被阻止的域名模式列表\n - \"*.internal.company.com\"\n - \"admin.example.com\"\n - \"*.local\"\n shared_files: # 从外部文件加载额外规则\n - \"/etc/hermes/blocked-sites.txt\"\n```\n\n启用后,任何匹配被阻止域名模式的 URL 在 web 或浏览器工具执行之前都会被拒绝。这适用于 `web_search`、`web_extract`、`browser_navigate` 以及任何访问 URL 的工具。\n\n域名规则支持:\n- 精确域名:`admin.example.com`\n- 通配符子域名:`*.internal.company.com`(阻止所有子域名)\n- TLD 通配符:`*.local`\n\n共享文件每行包含一条域名规则(空行和 `#` 注释被忽略)。缺失或不可读的文件记录警告,但不禁用其他 web 工具。\n\n策略缓存 30 秒,因此配置更改无需重启即可快速生效。\n\n## 智能审批\n\n控制 Hermes 如何处理潜在危险命令:\n\n```yaml\napprovals:\n mode: manual # manual | smart | off\n```\n\n| 模式 | 行为 |\n|------|----------|\n| `manual`(默认) | 在执行任何被标记的命令之前提示用户。在 CLI 中显示交互式审批对话框。在消息中排队待处理的审批请求。 |\n| `smart` | 使用辅助 LLM 评估被标记的命令是否真正危险。低风险命令以会话级持久性自动批准。真正有风险的命令升级给用户。 |\n| `off` | 跳过所有审批检查。等同于 `HERMES_YOLO_MODE=true`。**谨慎使用。** |\n\n智能模式对于减少审批疲劳特别有用 —— 它让 agent 在安全操作上更自主地工作,同时仍然捕获真正破坏性的命令。\n\n:::warning\n设置 `approvals.mode: off` 会禁用终端命令的所有安全检查。仅在受信任的沙箱环境中使用。\n:::\n\n## 检查点\n\n破坏性文件操作之前的自动文件系统快照。详情请参阅[检查点与回滚](/user-guide/checkpoints-and-rollback)。\n\n```yaml\ncheckpoints:\n enabled: false # 启用自动检查点(也可:hermes chat --checkpoints)。默认:false(选择加入)。\n max_snapshots: 20 # 每个目录保留的最大检查点数(默认:20)\n```\n\n\n## 委托\n\n为委托工具配置子 agent 行为:\n\n```yaml\ndelegation:\n # model: \"google/gemini-3-flash-preview\" # 覆盖模型(空 = 继承父级)\n # provider: \"openrouter\" # 覆盖 provider(空 = 继承父级)\n # base_url: \"http://localhost:1234/v1\" # 直接 OpenAI 兼容端点(优先于 provider)\n # api_key: \"local-key\" # base_url 的 API 密钥(回退到 OPENAI_API_KEY)\n # api_mode: \"\" # base_url 的线路协议:\"chat_completions\"、\"codex_responses\" 或 \"anthropic_messages\"。空 = 从 URL 自动检测(例如 /anthropic 后缀 → anthropic_messages)。对启发式无法检测的非标准端点显式设置。\n max_concurrent_children: 3 # 每批并行子 agent 数(下限 1,无上限)。也可通过 DELEGATION_MAX_CONCURRENT_CHILDREN 环境变量设置。\n max_spawn_depth: 1 # 委托树深度上限(1-3,截断)。1 = 扁平(默认):父级生成无法委托的叶子。2 = 编排器子级可以生成叶子孙级。3 = 三级。\n orchestrator_enabled: true # 全局终止开关。为 false 时,role=\"orchestrator\" 被忽略,每个子级无论 max_spawn_depth 如何都被强制为叶子。\n```\n\n**子 agent provider:model 覆盖:** 默认情况下,子 agent 继承父 agent 的 provider 和模型。设置 `delegation.provider` 和 `delegation.model` 将子 agent 路由到不同的 provider:model 对 —— 例如,在您的主 agent 运行昂贵推理模型时,为范围较窄的子任务使用便宜/快速的模型。\n\n**直接端点覆盖:** 如果您想要明显的自定义端点路径,请设置 `delegation.base_url`、`delegation.api_key` 和 `delegation.model`。这将子 agent 直接发送到该 OpenAI 兼容端点,并优先于 `delegation.provider`。如果省略 `delegation.api_key`,Hermes 仅回退到 `OPENAI_API_KEY`。\n\n**线路协议(`api_mode`):** Hermes 从 `delegation.base_url` 自动检测线路协议(例如以 `/anthropic` 结尾的路径 → `anthropic_messages`;Codex/原生 Anthropic/Kimi-coding 主机名保留其现有检测)。对于启发式无法分类的端点 —— 例如 Azure AI Foundry、MiniMax、Zhipu GLM 或前置 Anthropic 形状后端的 LiteLLM 代理 —— 请将 `delegation.api_mode` 显式设置为 `chat_completions`、`codex_responses` 或 `anthropic_messages` 之一。留空(默认)以保持自动检测。\n\n委托 provider 使用与 CLI/gateway 启动相同的凭据解析。所有配置的 provider 均受支持:`openrouter`、`nous`、`copilot`、`zai`、`kimi-coding`、`minimax`、`minimax-cn`。设置 provider 时,系统自动解析正确的基础 URL、API 密钥和 API 模式 —— 无需手动凭据连接。\n\n**优先级:** 配置中的 `delegation.base_url` → 配置中的 `delegation.provider` → 父 provider(继承)。配置中的 `delegation.model` → 父模型(继承)。仅设置 `model` 而不设置 `provider` 仅更改模型名称,同时保留父级凭据(适用于在同一 provider(如 OpenRouter)内切换模型)。\n\n**宽度和深度:** `max_concurrent_children` 限制每批并行运行的子 agent 数量(默认 `3`,下限 1,无上限)。也可通过 `DELEGATION_MAX_CONCURRENT_CHILDREN` 环境变量设置。当模型提交的 `tasks` 数组超过上限时,`delegate_task` 返回工具错误解释限制,而不是静默截断。`max_spawn_depth` 控制委托树深度(截断到 1-3)。在默认 `1` 时,委托是扁平的:子级无法生成孙级,传递 `role=\"orchestrator\"` 静默降级为 `leaf`。提升到 `2` 使编排器子级可以生成叶子孙级;`3` 用于三级树。Agent 通过 `role=\"orchestrator\"` 按调用选择编排;`orchestrator_enabled: false` 强制每个子级回到叶子,无论如何。成本呈乘法增长 —— 在 `max_spawn_depth: 3` 和 `max_concurrent_children: 3` 时,树可以达到 3×3×3 = 27 个并发叶子 agent。使用模式请参阅[子 Agent 委托 → 深度限制和嵌套编排](features/delegation.md#depth-limit-and-nested-orchestration)。\n\n## 澄清\n\n配置澄清提示行为:\n\n```yaml\nclarify:\n timeout: 120 # 等待用户澄清响应的秒数\n```\n\n## 上下文文件(SOUL.md、AGENTS.md)\n\nHermes 使用两种不同的上下文范围:\n\n| 文件 | 用途 | 范围 |\n|------|---------|-------|\n| `SOUL.md` | **主要 agent 身份** —— 定义 agent 是谁(系统提示词第 #1 槽位) | `~/.hermes/SOUL.md` 或 `$HERMES_HOME/SOUL.md` |\n| `.hermes.md` / `HERMES.md` | 项目特定指令(最高优先级) | 向上走到 git 根目录 |\n| `AGENTS.md` | 项目特定指令、编码规范 | 递归目录遍历 |\n| `CLAUDE.md` | Claude Code 上下文文件(也会检测) | 仅工作目录 |\n| `.cursorrules` | Cursor IDE 规则(也会检测) | 仅工作目录 |\n| `.cursor/rules/*.mdc` | Cursor 规则文件(也会检测) | 仅工作目录 |\n\n- **SOUL.md** 是 agent 的主要身份。它占据系统提示词的第 #1 槽位,完全替换内置的默认身份。编辑它以完全自定义 agent 是谁。\n- 如果 SOUL.md 缺失、为空或无法加载,Hermes 回退到内置默认身份。\n- **项目上下文文件使用优先级系统** —— 仅加载一种类型(第一个匹配优先):`.hermes.md` → `AGENTS.md` → `CLAUDE.md` → `.cursorrules`。SOUL.md 始终独立加载。\n- **AGENTS.md** 是分层的:如果子目录也有 AGENTS.md,所有都会合并。\n- 如果 `SOUL.md` 不存在,Hermes 会自动生成默认的 `SOUL.md`。\n- 所有加载的上下文文件上限为 20,000 字符,并进行智能截断。\n\n另请参阅:\n- [个性与 SOUL.md](/user-guide/features/personality)\n- [上下文文件](/user-guide/features/context-files)\n\n## 工作目录\n\n| 上下文 | 默认值 |\n|---------|---------|\n| **CLI(`hermes`)** | 运行命令的当前目录 |\n| **消息 gateway** | 主目录 `~`(用 `MESSAGING_CWD` 覆盖) |\n| **Docker / Singularity / Modal / SSH** | 容器或远程机器内用户的主目录 |\n\n覆盖工作目录:\n```bash\n# 在 ~/.hermes/.env 或 ~/.hermes/config.yaml 中:\nMESSAGING_CWD=/home/myuser/projects # Gateway 会话\nTERMINAL_CWD=/workspace # 所有终端会话\n```\n"}, "5e1f793430ccab74808b9f7019e071ea3c638381": {"t": 1785768009.2440252, "n": 55311, "h": "8d0e61106310da42", "c": "---\nsidebar_position: 2\ntitle: \"配置\"\ndescription: \"配置 Hermes Agent — config.yaml、providers、模型、API 密钥等\"\n---\n\n# 配置\n\n所有设置均存储在 `~/.hermes/` 目录中,便于访问。\n\n## 目录结构\n\n```text\n~/.hermes/\n├── config.yaml # 设置(模型、终端、TTS、压缩等)\n├── .env # API 密钥和机密\n├── auth.json # OAuth provider 凭据(Nous Portal 等)\n├── SOUL.md # 主要 agent 身份(系统提示词第 #1 槽位)\n├── memories/ # 持久记忆(MEMORY.md、USER.md)\n├── skills/ # Agent 创建的技能(通过 skill_manage 工具管理)\n├── cron/ # 定时任务\n├── sessions/ # Gateway 会话\n└── logs/ # 日志(errors.log、gateway.log — 机密自动脱敏)\n```\n\n## 管理配置\n\n```bash\nhermes config # 查看当前配置\nhermes config edit # 在编辑器中打开 config.yaml\nhermes config set KEY VAL # 设置特定值\nhermes config check # 检查缺失选项(更新后使用)\nhermes config migrate # 交互式添加缺失选项\n\n# 示例:\nhermes config set model anthropic/claude-opus-4\nhermes config set terminal.backend docker\nhermes config set OPENROUTER_API_KEY sk-or-... # 保存到 .env\n```\n\n:::tip\n`hermes config set` 命令会自动将值路由到正确的文件 —— API 密钥保存到 `.env`,其他所有内容保存到 `config.yaml`。\n:::\n\n## 配置优先级\n\n设置按以下顺序解析(优先级从高到低):\n\n1. **CLI 参数** —— 例如 `hermes chat --model anthropic/claude-sonnet-4`(单次调用覆盖)\n2. **`~/.hermes/config.yaml`** —— 所有非机密设置的主配置文件\n3. **`~/.hermes/.env`** —— 环境变量的回退;机密(API 密钥、token、密码)**必须**放这里\n4. **内置默认值** —— 未设置任何内容时的硬编码安全默认值\n\n:::info 经验法则\n机密(API 密钥、bot token、密码)放入 `.env`。其他所有内容(模型、终端后端、压缩设置、内存限制、工具集)放入 `config.yaml`。当两者都设置时,`config.yaml` 对非机密设置优先。\n:::\n\n## 环境变量替换\n\n可以在 `config.yaml` 中使用 `${VAR_NAME}` 语法引用环境变量:\n\n```yaml\nauxiliary:\n vision:\n api_key: ${GOOGLE_API_KEY}\n base_url: ${CUSTOM_VISION_URL}\n\ndelegation:\n api_key: ${DELEGATION_KEY}\n```\n\n单个值中可以有多个引用:`url: \"${HOST}:${PORT}\"`。如果引用的变量未设置,占位符将保持原样(`${UNDEFINED_VAR}` 保持不变)。仅支持 `${VAR}` 语法 —— 裸 `$VAR` 不会被展开。\n\n关于 AI provider 设置(OpenRouter、Anthropic、Copilot、自定义端点、自托管 LLM、回退模型等),请参阅 [AI Providers](/integrations/providers)。\n\n### Provider 超时\n\n可以为 provider 设置 `providers.<id>.request_timeout_seconds` 作为全局请求超时,以及 `providers.<id>.models.<model>.timeout_seconds` 作为特定模型的覆盖值。适用于每种传输方式(OpenAI-wire、原生 Anthropic、Anthropic 兼容)上的主轮次客户端、回退链、凭据轮换后的重建,以及(对于 OpenAI-wire)每请求超时 kwarg —— 因此配置值优先于旧版 `HERMES_API_TIMEOUT` 环境变量。\n\n还可以设置 `providers.<id>.stale_timeout_seconds` 用于非流式陈旧调用检测器,以及 `providers.<id>.models.<model>.stale_timeout_seconds` 作为特定模型的覆盖值。此值优先于旧版 `HERMES_API_CALL_STALE_TIMEOUT` 环境变量。\n\n不设置这些值将保持旧版默认值(`HERMES_API_TIMEOUT=1800`s、`HERMES_API_CALL_STALE_TIMEOUT=300`s、原生 Anthropic 900s)。目前不适用于 AWS Bedrock(`bedrock_converse` 和 AnthropicBedrock SDK 路径均使用 boto3 及其自身的超时配置)。请参阅 [`cli-config.yaml.example`](https://github.com/NousResearch/hermes-agent/blob/main/cli-config.yaml.example) 中的注释示例。\n\n## 终端后端配置\n\nHermes 支持六种终端后端。每种后端决定 agent 的 shell 命令实际在哪里执行 —— 本地机器、Docker 容器、通过 SSH 的远程服务器、Modal 云沙箱(直接或通过 Nous 托管的 gateway)、Daytona 工作区,或 Singularity/Apptainer 容器。\n\n```yaml\nterminal:\n backend: local # local | docker | ssh | modal | daytona | singularity\n cwd: \".\" # Gateway/cron 工作目录(CLI 始终使用启动目录)\n timeout: 180 # 每条命令的超时时间(秒)\n env_passthrough: [] # 转发到沙箱执行的环境变量名(terminal + execute_code)\n singularity_image: \"docker://nikolaik/python-nodejs:python3.11-nodejs20\" # Singularity 后端的容器镜像\n modal_image: \"nikolaik/python-nodejs:python3.11-nodejs20\" # Modal 后端的容器镜像\n daytona_image: \"nikolaik/python-nodejs:python3.11-nodejs20\" # Daytona 后端的容器镜像\n```\n\n对于 Modal 和 Daytona 等云沙箱,`container_persistent: true` 表示 Hermes 将尝试在沙箱重建后保留文件系统状态。这并不保证相同的活跃沙箱、PID 空间或后台进程之后仍在运行。\n\n### 后端概览\n\n| 后端 | 命令运行位置 | 隔离性 | 最适合 |\n|---------|-------------------|-----------|----------|\n| **local** | 直接在您的机器上 | 无 | 开发、个人使用 |\n| **docker** | 单个持久 Docker 容器(跨会话、`/new`、子 agent 共享) | 完全(命名空间、cap-drop) | 安全沙箱、CI/CD |\n| **ssh** | 通过 SSH 的远程服务器 | 网络边界 | 远程开发、强大硬件 |\n| **modal** | Modal 云沙箱 | 完全(云 VM) | 临时云计算、评估 |\n| **daytona** | Daytona 工作区 | 完全(云容器) | 托管云开发环境 |\n| **singularity** | Singularity/Apptainer 容器 | 命名空间(--containall) | HPC 集群、共享机器 |\n\n### Local 后端\n\n默认后端。命令直接在您的机器上运行,无隔离。无需特殊设置。\n\n```yaml\nterminal:\n backend: local\n```\n\n:::warning\nAgent 拥有与您的用户账户相同的文件系统访问权限。使用 `hermes tools` 禁用不需要的工具,或切换到 Docker 进行沙箱隔离。\n:::\n\n### Docker 后端\n\n在具有安全加固的 Docker 容器内运行命令(所有权限已删除、无权限提升、PID 限制)。\n\n**单个持久容器,而非每条命令一个容器。** Hermes 在首次使用时启动一个长期运行的容器,并通过 `docker exec` 将每个终端、文件和 `execute_code` 调用路由到同一容器中 —— 跨会话、`/new`、`/reset` 和 `delegate_task` 子 agent,贯穿 Hermes 进程的整个生命周期。工作目录更改、已安装的包以及 `/workspace` 中的文件会从一次工具调用延续到下一次,就像本地 shell 一样。容器在关闭时停止并删除。详情请参阅下方的**容器生命周期**。\n\n```yaml\nterminal:\n backend: docker\n docker_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n docker_mount_cwd_to_workspace: false # 将启动目录挂载到 /workspace\n docker_run_as_host_user: false # 参见下方\"以宿主用户身份运行容器\"\n docker_forward_env: # 转发到容器的环境变量\n - \"GITHUB_TOKEN\"\n docker_volumes: # 宿主目录挂载\n - \"/home/user/projects:/workspace/projects\"\n - \"/home/user/data:/data:ro\" # :ro 表示只读\n docker_extra_args: # 附加到 `docker run` 的额外标志\n - \"--gpus=all\"\n - \"--network=host\"\n\n # 资源限制\n container_cpu: 1 # CPU 核心数(0 = 不限制)\n container_memory: 5120 # MB(0 = 不限制)\n container_disk: 51200 # MB(需要 XFS+pquota 上的 overlay2)\n container_persistent: true # 跨会话持久化 /workspace 和 /root\n```\n\n**`terminal.docker_extra_args`**(也可通过 `TERMINAL_DOCKER_EXTRA_ARGS='[\"--gpus=all\"]'` 覆盖)允许传递 Hermes 未作为一级键公开的任意 `docker run` 标志 —— `--gpus`、`--network`、`--add-host`、替代 `--security-opt` 覆盖等。每个条目必须是字符串;该列表最后附加到组装好的 `docker run` 调用中,因此可以在需要时覆盖 Hermes 的默认值。请谨慎使用 —— 与沙箱加固(权限删除、`--user`、workspace 绑定挂载)冲突的标志将悄然削弱隔离性。\n\n**要求:** 已安装并运行 Docker Desktop 或 Docker Engine。Hermes 会探测 `$PATH` 以及常见的 macOS 安装位置(`/usr/local/bin/docker`、`/opt/homebrew/bin/docker`、Docker Desktop 应用包)。开箱即用支持 Podman:设置 `HERMES_DOCKER_BINARY=podman`(或完整路径)以在两者都安装时强制使用它。\n\n**容器生命周期:** Hermes 为每个终端和文件工具调用重用单个长期运行的容器(`docker run -d ... sleep 2h`),跨会话、`/new`、`/reset` 和 `delegate_task` 子 agent,贯穿 Hermes 进程的整个生命周期。命令通过带登录 shell 的 `docker exec` 运行,因此工作目录更改、已安装的包以及 `/workspace` 中的文件都会从一次工具调用延续到下一次。容器在 Hermes 关闭时(或空闲清理回收时)停止并删除。\n\n通过 `delegate_task(tasks=[...])` 生成的并行子 agent 共享这一个容器 —— 并发的 `cd`、环境变量修改以及对同一路径的写入会发生冲突。如果子 agent 需要隔离的沙箱,必须通过 `register_task_env_overrides()` 注册每任务镜像覆盖,RL 和基准测试环境(TerminalBench2、HermesSweEnv 等)会自动为其每任务 Docker 镜像执行此操作。\n\n**安全加固:**\n- `--cap-drop ALL`,仅添加回 `DAC_OVERRIDE`、`CHOWN`、`FOWNER`\n- `--security-opt no-new-privileges`\n- `--pids-limit 256`\n- `/tmp`(512MB)、`/var/tmp`(256MB)、`/run`(64MB)的大小限制 tmpfs\n\n**凭据转发:** `docker_forward_env` 中列出的环境变量首先从您的 shell 环境解析,然后回退到 `~/.hermes/.env`。技能也可以声明 `required_environment_variables`,这些变量会自动合并。\n\n### SSH 后端\n\n通过 SSH 在远程服务器上运行命令。使用 ControlMaster 进行连接复用(5 分钟空闲保活)。默认启用持久 shell —— 状态(cwd、环境变量)在命令之间保持。\n\n```yaml\nterminal:\n backend: ssh\n persistent_shell: true # 保持长期运行的 bash 会话(默认:true)\n```\n\n**必需的环境变量:**\n\n```bash\nTERMINAL_SSH_HOST=my-server.example.com\nTERMINAL_SSH_USER=ubuntu\n```\n\n**可选:**\n\n| 变量 | 默认值 | 描述 |\n|----------|---------|-------------|\n| `TERMINAL_SSH_PORT` | `22` | SSH 端口 |\n| `TERMINAL_SSH_KEY` | (系统默认) | SSH 私钥路径 |\n| `TERMINAL_SSH_PERSISTENT` | `true` | 启用持久 shell |\n\n**工作原理:** 使用 `BatchMode=yes` 和 `StrictHostKeyChecking=accept-new` 在初始化时连接。持久 shell 在远程主机上保持单个 `bash -l` 进程存活,通过临时文件进行通信。需要 `stdin_data` 或 `sudo` 的命令会自动回退到单次模式。\n\n### Modal 后端\n\n在 [Modal](https://modal.com) 云沙箱中运行命令。每个任务获得一个具有可配置 CPU、内存和磁盘的隔离 VM。文件系统可以跨会话快照/恢复。\n\n```yaml\nterminal:\n backend: modal\n container_cpu: 1 # CPU 核心数\n container_memory: 5120 # MB(5GB)\n container_disk: 51200 # MB(50GB)\n container_persistent: true # 快照/恢复文件系统\n```\n\n**必需:** `MODAL_TOKEN_ID` + `MODAL_TOKEN_SECRET` 环境变量,或 `~/.modal.toml` 配置文件。\n\n**持久化:** 启用后,沙箱文件系统在清理时快照,并在下次会话时恢复。快照在 `~/.hermes/modal_snapshots.json` 中跟踪。这保留文件系统状态,而非活跃进程、PID 空间或后台任务。\n\n**凭据文件:** 自动从 `~/.hermes/` 挂载(OAuth token 等),并在每条命令前同步。\n\n### Daytona 后端\n\n在 [Daytona](https://daytona.io) 托管工作区中运行命令。支持停止/恢复以实现持久化。\n\n```yaml\nterminal:\n backend: daytona\n container_cpu: 1 # CPU 核心数\n container_memory: 5120 # MB → 转换为 GiB\n container_disk: 10240 # MB → 转换为 GiB(最大 10 GiB)\n container_persistent: true # 停止/恢复而非删除\n```\n\n**必需:** `DAYTONA_API_KEY` 环境变量。\n\n**持久化:** 启用后,沙箱在清理时停止(而非删除),并在下次会话时恢复。沙箱名称遵循 `hermes-{task_id}` 模式。\n\n**磁盘限制:** Daytona 强制执行 10 GiB 最大值。超过此值的请求将被截断并发出警告。\n\n### Singularity/Apptainer 后端\n\n在 [Singularity/Apptainer](https://apptainer.org) 容器中运行命令。专为 Docker 不可用的 HPC 集群和共享机器设计。\n\n```yaml\nterminal:\n backend: singularity\n singularity_image: \"docker://nikolaik/python-nodejs:python3.11-nodejs20\"\n container_cpu: 1 # CPU 核心数\n container_memory: 5120 # MB\n container_persistent: true # 可写覆盖层跨会话持久化\n```\n\n**要求:** `$PATH` 中有 `apptainer` 或 `singularity` 二进制文件。\n\n**镜像处理:** Docker URL(`docker://...`)自动转换为 SIF 文件并缓存。现有 `.sif` 文件直接使用。\n\n**临时目录:** 按顺序解析:`TERMINAL_SCRATCH_DIR` → `TERMINAL_SANDBOX_DIR/singularity` → `/scratch/$USER/hermes-agent`(HPC 惯例)→ `~/.hermes/sandboxes/singularity`。\n\n**隔离:** 使用 `--containall --no-home` 实现完全命名空间隔离,不挂载宿主 home 目录。\n\n### 常见终端后端问题\n\n如果终端命令立即失败或终端工具报告为已禁用:\n\n- **Local** —— 无特殊要求。入门时最安全的默认选项。\n- **Docker** —— 运行 `docker version` 验证 Docker 是否正常工作。如果失败,修复 Docker 或执行 `hermes config set terminal.backend local`。\n- **SSH** —— `TERMINAL_SSH_HOST` 和 `TERMINAL_SSH_USER` 都必须设置。如果缺少任一项,Hermes 会记录清晰的错误。\n- **Modal** —— 需要 `MODAL_TOKEN_ID` 环境变量或 `~/.modal.toml`。运行 `hermes doctor` 检查。\n- **Daytona** —— 需要 `DAYTONA_API_KEY`。Daytona SDK 处理服务器 URL 配置。\n- **Singularity** —— 需要 `$PATH` 中有 `apptainer` 或 `singularity`。HPC 集群上常见。\n\n如有疑问,将 `terminal.backend` 设回 `local` 并首先验证命令在那里运行。\n\n### 拆卸时远程到宿主文件同步\n\n对于 **SSH**、**Modal** 和 **Daytona** 后端(agent 的工作树位于与运行 Hermes 的宿主不同的机器上),Hermes 跟踪 agent 在远程沙箱中触及的文件,并在会话拆卸/沙箱清理时,将修改的文件**同步回宿主**,存放在 `~/.hermes/cache/remote-syncs/<session-id>/` 下。\n\n- 触发时机:会话关闭、`/new`、`/reset`、gateway 消息超时、子 agent 使用远程后端时 `delegate_task` 子 agent 完成。\n- 覆盖 agent 修改的整个树,而不仅仅是它明确打开的文件。添加、编辑和删除都会被捕获。\n- 远程沙箱可能在您查找时已被拆除;本地 `~/.hermes/cache/remote-syncs/…` 副本是 agent 更改内容的权威记录。\n- 大型二进制输出(模型检查点、原始数据集)按大小限制 —— 同步跳过超过 `file_sync_max_mb`(默认 `100`)的文件。如果您期望更大的工件返回,请调高该值。\n\n```yaml\nterminal:\n file_sync_max_mb: 100 # 默认 —— 同步最大 100 MB 的文件\n file_sync_enabled: true # 默认 —— 设为 false 可完全跳过同步\n```\n\n这是从会话结束后被销毁的临时云沙箱中恢复结果的方式,无需告诉 agent 显式地 `scp` 或 `modal volume put` 每个工件。\n\n### Docker 卷挂载\n\n使用 Docker 后端时,`docker_volumes` 允许您与容器共享宿主目录。每个条目使用标准 Docker `-v` 语法:`host_path:container_path[:options]`。\n\n```yaml\nterminal:\n backend: docker\n docker_volumes:\n - \"/home/user/projects:/workspace/projects\" # 读写(默认)\n - \"/home/user/datasets:/data:ro\" # 只读\n - \"/home/user/.hermes/cache/documents:/output\" # Gateway 可见的导出\n```\n\n适用于:\n- **向 agent 提供文件**(数据集、配置、参考代码)\n- **从 agent 接收文件**(生成的代码、报告、导出)\n- **共享工作区**,您和 agent 都访问相同的文件\n\n如果您使用消息 gateway 并希望 agent 通过 `MEDIA:/...` 发送生成的文件,建议使用专用的宿主可见导出挂载,例如 `/home/user/.hermes/cache/documents:/output`。\n\n- 在 Docker 中将文件写入 `/output/...`\n- 在 `MEDIA:` 中发出**宿主路径**,例如:`MEDIA:/home/user/.hermes/cache/documents/report.txt`\n- **不要**发出 `/workspace/...` 或 `/output/...`,除非该确切路径在宿主上对 gateway 进程也存在\n\n:::warning\nYAML 重复键会静默覆盖之前的键。如果您已有 `docker_volumes:` 块,请将新挂载合并到同一列表中,而不是在文件后面再添加一个 `docker_volumes:` 键。\n:::\n\n也可以通过环境变量设置:`TERMINAL_DOCKER_VOLUMES='[\"/host:/container\"]'`(JSON 数组)。\n\n### Docker 凭据转发\n\n默认情况下,Docker 终端会话不继承任意宿主凭据。如果您需要在容器内使用特定 token,请将其添加到 `terminal.docker_forward_env`。\n\n```yaml\nterminal:\n backend: docker\n docker_forward_env:\n - \"GITHUB_TOKEN\"\n - \"NPM_TOKEN\"\n```\n\nHermes 首先从您当前的 shell 解析每个列出的变量,然后回退到通过 `hermes config set` 保存的 `~/.hermes/.env`。\n\n:::warning\n`docker_forward_env` 中列出的任何内容都会对容器内运行的命令可见。只转发您愿意暴露给终端会话的凭据。\n:::\n\n### 以宿主用户身份运行容器\n\n默认情况下,Docker 容器以 `root`(UID 0)身份运行。在 `/workspace` 或其他绑定挂载中创建的文件在宿主上归 root 所有,因此会话结束后您必须 `sudo chown` 才能从宿主编辑器编辑它们。`terminal.docker_run_as_host_user` 标志解决了这个问题:\n\n```yaml\nterminal:\n backend: docker\n docker_run_as_host_user: true # 默认:false\n```\n\n启用后,Hermes 将 `--user $(id -u):$(id -g)` 附加到 `docker run` 命令,使写入绑定挂载目录(`/workspace`、`/root`、`docker_volumes` 中的任何内容)的文件归您的宿主用户所有,而非 root。权衡:容器将无法再 `apt install` 或写入 `/root/.npm` 等 root 拥有的路径 —— 如果您同时需要这两者,请使用 `HOME` 归非 root 用户所有的基础镜像(或在镜像构建时添加所需工具)。\n\n保持 `false`(默认)以获得向后兼容的行为。当您的工作流主要是\"编辑挂载的宿主文件\"且厌倦了 `sudo chown -R` 时,请开启此选项。\n\n### 可选:将启动目录挂载到 `/workspace`\n\nDocker 沙箱默认保持隔离。Hermes **不会**将您当前的宿主工作目录传入容器,除非您明确选择加入。\n\n在 `config.yaml` 中启用:\n\n```yaml\nterminal:\n backend: docker\n docker_mount_cwd_to_workspace: true\n```\n\n启用后:\n- 如果您从 `~/projects/my-app` 启动 Hermes,该宿主目录将绑定挂载到 `/workspace`\n- Docker 后端从 `/workspace` 开始\n- 文件工具和终端命令都能看到相同的挂载项目\n\n禁用时,`/workspace` 保持沙箱所有,除非您通过 `docker_volumes` 显式挂载内容。\n\n安全权衡:\n- `false` 保留沙箱边界\n- `true` 使沙箱直接访问您启动 Hermes 的目录\n\n仅在您有意希望容器处理实时宿主文件时才选择加入。\n\n### 持久 Shell\n\n默认情况下,每条终端命令在其自己的子进程中运行 —— 工作目录、环境变量和 shell 变量在命令之间重置。启用**持久 shell** 后,单个长期运行的 bash 进程在 `execute()` 调用之间保持存活,使状态在命令之间保持。\n\n这对 **SSH 后端**最有用,它还消除了每条命令的连接开销。持久 shell **对 SSH 默认启用**,对本地后端禁用。\n\n```yaml\nterminal:\n persistent_shell: true # 默认 —— 为 SSH 启用持久 shell\n```\n\n禁用:\n\n```bash\nhermes config set terminal.persistent_shell false\n```\n\n**跨命令保持的内容:**\n- 工作目录(`cd /tmp` 对下一条命令生效)\n- 导出的环境变量(`export FOO=bar`)\n- Shell 变量(`MY_VAR=hello`)\n\n**优先级:**\n\n| 级别 | 变量 | 默认值 |\n|-------|----------|---------|\n| 配置 | `terminal.persistent_shell` | `true` |\n| SSH 覆盖 | `TERMINAL_SSH_PERSISTENT` | 遵循配置 |\n| Local 覆盖 | `TERMINAL_LOCAL_PERSISTENT` | `false` |\n\n每个后端的环境变量具有最高优先级。如果您也想在本地后端使用持久 shell:\n\n```bash\nexport TERMINAL_LOCAL_PERSISTENT=true\n```\n\n:::note\n需要 `stdin_data` 或 sudo 的命令会自动回退到单次模式,因为持久 shell 的 stdin 已被 IPC 协议占用。\n:::\n\n有关每个后端的详细信息,请参阅[代码执行](features/code-execution.md)和 [README 的终端部分](features/tools.md)。\n\n## 技能设置\n\n技能可以通过其 SKILL.md frontmatter 声明自己的配置设置。这些是非机密值(路径、偏好、域设置),存储在 `config.yaml` 的 `skills.config` 命名空间下。\n\n```yaml\nskills:\n config:\n myplugin:\n path: ~/myplugin-data # 示例 —— 每个技能定义自己的键\n```\n\n**技能设置的工作原理:**\n\n- `hermes config migrate` 扫描所有已启用的技能,找到未配置的设置,并提供提示\n- `hermes config show` 在\"技能设置\"下显示所有技能设置及其所属技能\n- 技能加载时,其解析的配置值会自动注入到技能上下文中\n\n**手动设置值:**\n\n```bash\nhermes config set skills.config.myplugin.path ~/myplugin-data\n```\n\n有关在您自己的技能中声明配置设置的详细信息,请参阅[创建技能 — 配置设置](/developer-guide/creating-skills#config-settings-configyaml)。\n\n### Agent 创建技能写入的守卫\n\n当 agent 使用 `skill_manage` 创建、编辑、修补或删除技能时,Hermes 可以选择扫描新/更新的内容以查找危险关键字模式(凭据收集、明显的 prompt 注入、数据外泄指令)。扫描器**默认关闭** —— 合法触及 `~/.ssh/` 或提及 `$OPENAI_API_KEY` 的真实 agent 工作流触发启发式规则过于频繁。如果您希望扫描器在 agent 的技能写入落地前提示您,请重新开启:\n\n```yaml\nskills:\n guard_agent_created: true # 默认:false\n```\n\n开启后,任何被标记的 `skill_manage` 写入都会以审批提示的形式出现,并附带扫描器的理由。接受的写入落地;拒绝的写入向 agent 返回解释性错误。\n\n## 内存配置\n\n```yaml\nmemory:\n memory_enabled: true\n user_profile_enabled: true\n memory_char_limit: 2200 # ~800 tokens\n user_char_limit: 1375 # ~500 tokens\n```\n\n## 文件读取安全\n\n控制单次 `read_file` 调用可以返回多少内容。超过限制的读取将被拒绝,并向 agent 返回错误,提示使用 `offset` 和 `limit` 读取较小范围。这可以防止单次读取压缩的 JS 包或大型数据文件时淹没上下文窗口。\n\n```yaml\nfile_read_max_chars: 100000 # 默认 —— ~25-35K tokens\n```\n\n如果您使用具有大上下文窗口的模型并经常读取大文件,请调高此值。对于小上下文模型,请降低以保持读取高效:\n\n```yaml\n# 大上下文模型(200K+)\nfile_read_max_chars: 200000\n\n# 小型本地模型(16K 上下文)\nfile_read_max_chars: 30000\n```\n\nAgent 还会自动去重文件读取 —— 如果同一文件区域被读取两次且文件未更改,则返回轻量级存根而不是重新发送内容。这在上下文压缩后重置,以便 agent 在内容被摘要后可以重新读取文件。\n\n## 工具输出截断限制\n\n三个相关的上限控制工具在 Hermes 截断之前可以返回多少原始输出:\n\n```yaml\ntool_output:\n max_bytes: 50000 # 终端输出上限(字符)\n max_lines: 2000 # read_file 分页上限\n max_line_length: 2000 # read_file 行号视图中的每行上限\n```\n\n- **`max_bytes`** —— 当 `terminal` 命令产生超过此字符数的合并 stdout/stderr 时,Hermes 保留前 40% 和后 60%,并在中间插入 `[OUTPUT TRUNCATED]` 通知。默认 `50000`(典型分词器约 12-15K tokens)。\n- **`max_lines`** —— 单次 `read_file` 调用的 `limit` 参数上限。超过此值的请求将被截断,以防单次读取淹没上下文窗口。默认 `2000`。\n- **`max_line_length`** —— `read_file` 发出行号视图时应用的每行上限。超过此长度的行将被截断为此字符数,后跟 `... [truncated]`。默认 `2000`。\n\n对于具有大上下文窗口且每次调用可以承受更多原始输出的模型,请调高限制。对于小上下文模型,请降低以保持工具结果紧凑:\n\n```yaml\n# 大上下文模型(200K+)\ntool_output:\n max_bytes: 150000\n max_lines: 5000\n\n# 小型本地模型(16K 上下文)\ntool_output:\n max_bytes: 20000\n max_lines: 500\n```\n\n## 全局工具集禁用\n\n要在 CLI 和每个 gateway 平台上统一禁用特定工具集,请在 `agent.disabled_toolsets` 下列出其名称:\n\n```yaml\nagent:\n disabled_toolsets:\n - memory # 隐藏内存工具 + MEMORY_GUIDANCE 注入\n - web # 任何地方都不使用 web_search / web_extract\n```\n\n这在每个平台的工具配置(由 `hermes tools` 写入的 `platform_toolsets`)**之后**应用,因此此处列出的工具集始终被删除 —— 即使平台的已保存配置仍然列出它。当您希望有一个\"到处关闭 X\"的单一开关而不是编辑 `hermes tools` UI 中的 15+ 个平台行时,请使用此选项。\n\n留空列表或省略键不会产生任何效果。\n\n## Git Worktree 隔离\n\n启用隔离的 git worktree,以便在同一仓库上并行运行多个 agent:\n\n```yaml\nworktree: true # 始终创建 worktree(与 hermes -w 相同)\n# worktree: false # 默认 —— 仅在传递 -w 标志时\n```\n\n启用后,每个 CLI 会话在 `.worktrees/` 下创建一个带有自己分支的新 worktree。Agent 可以编辑文件、提交、推送和创建 PR,而不会相互干扰。干净的 worktree 在退出时删除;脏的 worktree 保留以供手动恢复。\n\n您还可以通过仓库根目录中的 `.worktreeinclude` 列出要复制到 worktree 的 gitignore 文件:\n\n```\n# .worktreeinclude\n.env\n.venv/\nnode_modules/\n```\n\n## 上下文压缩\n\nHermes 自动压缩长对话以保持在模型的上下文窗口内。压缩摘要器是一个单独的 LLM 调用 —— 您可以将其指向任何 provider 或端点。\n\n所有压缩设置都在 `config.yaml` 中(无环境变量)。\n\n### 完整参考\n\n```yaml\ncompression:\n enabled: true # 开启/关闭压缩\n threshold: 0.50 # 在上下文限制的此百分比时压缩\n target_ratio: 0.20 # 保留为最近尾部的阈值分数\n protect_last_n: 20 # 保持未压缩的最少最近消息数\n hygiene_hard_message_limit: 400 # Gateway 安全阀 —— 见下文\n\n# 摘要模型/provider 在 auxiliary: 下配置:\nauxiliary:\n compression:\n model: \"\" # 空 = 使用主聊天模型。覆盖为例如 \"google/gemini-3-flash-preview\" 以获得更便宜/更快的压缩。\n provider: \"auto\" # Provider:\"auto\"、\"openrouter\"、\"nous\"、\"codex\"、\"main\" 等\n base_url: null # 自定义 OpenAI 兼容端点(覆盖 provider)\n```\n\n:::info 旧版配置迁移\n带有 `compression.summary_model`、`compression.summary_provider` 和 `compression.summary_base_url` 的旧版配置在首次加载时自动迁移到 `auxiliary.compression.*`(配置版本 17)。无需手动操作。\n:::\n\n`hygiene_hard_message_limit` 是仅限 gateway 的**预压缩安全阀**。拥有数千条消息的失控会话可能在正常的上下文百分比阈值触发之前就达到模型上下文限制;当消息数超过此上限时,Hermes 强制压缩,无论 token 使用情况如何。默认 `400` —— 对于非常长的会话正常的平台,请调高;要强制更积极的压缩,请降低。在运行中的 gateway 上编辑此值将在下一条消息时生效(见下文)。\n\n:::tip Gateway 热重载压缩和上下文长度\n从最近的版本开始,在运行中的 gateway 上编辑 `config.yaml` 中的 `model.context_length` 或任何 `compression.*` 键将在下一条消息时生效 —— 无需 gateway 重启、`/reset` 或会话轮换。缓存的 agent 签名包含这些键,因此 gateway 在检测到更改时会透明地重建 agent。API 密钥和工具/技能配置仍需要通常的重载路径。\n:::\n\n### 常见设置\n\n**默认(自动检测)—— 无需配置:**\n```yaml\ncompression:\n enabled: true\n threshold: 0.50\n```\n使用您的主 provider 和主模型。如果您希望在比主聊天模型更便宜的模型上进行压缩,请覆盖每任务(例如 `auxiliary.compression.provider: openrouter` + `model: google/gemini-2.5-flash`)。\n\n**强制特定 provider**(基于 OAuth 或 API 密钥):\n```yaml\nauxiliary:\n compression:\n provider: nous\n model: gemini-3-flash\n```\n适用于任何 provider:`nous`、`openrouter`、`codex`、`anthropic`、`main` 等。\n\n**自定义端点**(自托管、Ollama、zai、DeepSeek 等):\n```yaml\nauxiliary:\n compression:\n model: glm-4.7\n base_url: https://api.z.ai/api/coding/paas/v4\n```\n指向自定义 OpenAI 兼容端点。使用 `OPENAI_API_KEY` 进行认证。\n\n### 三个旋钮的交互方式\n\n| `auxiliary.compression.provider` | `auxiliary.compression.base_url` | 结果 |\n|---------------------|---------------------|--------|\n| `auto`(默认) | 未设置 | 自动检测最佳可用 provider |\n| `nous` / `openrouter` / 等 | 未设置 | 强制使用该 provider,使用其认证 |\n| 任意 | 已设置 | 直接使用自定义端点(忽略 provider) |\n\n:::warning 摘要模型上下文长度要求\n摘要模型**必须**具有至少与您的主 agent 模型一样大的上下文窗口。压缩器将对话的完整中间部分发送给摘要模型 —— 如果该模型的上下文窗口小于主模型的,摘要调用将因上下文长度错误而失败。发生这种情况时,中间轮次将**在没有摘要的情况下被丢弃**,静默丢失对话上下文。如果您覆盖模型,请验证其上下文长度满足或超过您的主模型。\n:::\n\n## 上下文引擎\n\n上下文引擎控制在接近模型 token 限制时如何管理对话。内置的 `compressor` 引擎使用有损摘要(参见[上下文压缩](/developer-guide/context-compression-and-caching))。插件引擎可以用替代策略替换它。\n\n```yaml\ncontext:\n engine: \"compressor\" # 默认 —— 内置有损摘要\n```\n\n使用插件引擎(例如,用于无损上下文管理的 LCM):\n\n```yaml\ncontext:\n engine: \"lcm\" # 必须与插件名称匹配\n```\n\n插件引擎**永远不会自动激活** —— 您必须将 `context.engine` 显式设置为插件名称。可用引擎可以通过 `hermes plugins` → Provider Plugins → Context Engine 浏览和选择。\n\n有关内存插件的类似单选系统,请参阅[内存 Providers](/user-guide/features/memory-providers)。\n\n## 迭代预算压力\n\n当 agent 在处理具有许多工具调用的复杂任务时,它可能会在没有意识到预算不足的情况下耗尽其迭代预算(默认:90 轮)。预算压力会在模型接近限制时自动发出警告:\n\n| 阈值 | 级别 | 模型看到的内容 |\n|-----------|-------|---------------------|\n| **70%** | 注意 | `[BUDGET: 63/90. 27 iterations left. Start consolidating.]` |\n| **90%** | 警告 | `[BUDGET WARNING: 81/90. Only 9 left. Respond NOW.]` |\n\n警告注入到最后一个工具结果的 JSON 中(作为 `_budget_warning` 字段),而不是作为单独的消息 —— 这保留了 prompt 缓存,不会破坏对话结构。\n\n```yaml\nagent:\n max_turns: 90 # 每次对话轮次的最大迭代次数(默认:90)\n api_max_retries: 3 # 回退启动前每个 provider 的重试次数(默认:3)\n```\n\n预算压力默认启用。Agent 自然地将警告视为工具结果的一部分,鼓励它在耗尽迭代之前整合工作并提供响应。\n\n当迭代预算完全耗尽时,CLI 向用户显示通知:`⚠ Iteration budget reached (90/90) — response may be incomplete`。如果预算在活跃工作期间耗尽,agent 会在停止前生成已完成内容的摘要。\n\n`agent.api_max_retries` 控制 Hermes 在回退 provider 切换启动**之前**对瞬时错误(速率限制、连接断开、5xx)重试 provider API 调用的次数。默认为 `3` —— 总共四次尝试。如果您配置了[回退 providers](/user-guide/features/fallback-providers) 并希望更快地故障转移,请将其降至 `0`,这样主 provider 上的第一个瞬时错误会立即切换到回退,而不是对不稳定的端点进行重试。\n\n### API 超时\n\nHermes 对流式传输有单独的超时层,以及用于非流式调用的陈旧检测器。陈旧检测器仅在您将其保留为隐式默认值时才会自动调整本地 provider。\n\n| 超时 | 默认值 | 本地 providers | 配置/环境变量 |\n|---------|---------|----------------|--------------|\n| Socket 读取超时 | 120s | 自动提升至 1800s | `HERMES_STREAM_READ_TIMEOUT` |\n| 陈旧流检测 | 180s | 自动禁用 | `HERMES_STREAM_STALE_TIMEOUT` |\n| 陈旧非流检测 | 300s | 保持隐式时自动禁用 | `providers.<id>.stale_timeout_seconds` 或 `HERMES_API_CALL_STALE_TIMEOUT` |\n| API 调用(非流式) | 1800s | 不变 | `providers.<id>.request_timeout_seconds` / `timeout_seconds` 或 `HERMES_API_TIMEOUT` |\n\n**Socket 读取超时**控制 httpx 等待 provider 下一个数据块的时间。本地 LLM 在大上下文上预填充可能需要几分钟才能产生第一个 token,因此当 Hermes 检测到本地端点时,会将此值提升至 30 分钟。如果您显式设置 `HERMES_STREAM_READ_TIMEOUT`,无论端点检测如何,始终使用该值。\n\n**陈旧流检测**终止接收 SSE 保活 ping 但没有实际内容的连接。对于本地 providers,这完全禁用,因为它们在预填充期间不发送保活 ping。\n\n**陈旧非流检测**终止长时间没有响应的非流式调用。默认情况下,Hermes 在本地端点上禁用此功能,以避免长时间预填充期间的误报。如果您显式设置 `providers.<id>.stale_timeout_seconds`、`providers.<id>.models.<model>.stale_timeout_seconds` 或 `HERMES_API_CALL_STALE_TIMEOUT`,即使在本地端点上也会遵守该显式值。\n\n## 上下文压力警告\n\n与迭代预算压力分开,上下文压力跟踪对话距**压缩阈值**有多近 —— 即上下文压缩触发以摘要旧消息的点。这有助于您和 agent 了解对话何时变长。\n\n| 进度 | 级别 | 发生的事情 |\n|----------|-------|-------------|\n| **≥ 60%** 到阈值 | 信息 | CLI 显示青色进度条;gateway 发送信息通知 |\n| **≥ 85%** 到阈值 | 警告 | CLI 显示粗体黄色进度条;gateway 警告压缩即将发生 |\n\n在 CLI 中,上下文压力在工具输出流中显示为进度条:\n\n```\n ◐ context ████████████░░░░░░░░ 62% to compaction 48k threshold (50%) · approaching compaction\n```\n\n在消息平台上,发送纯文本通知:\n\n```\n◐ Context: ████████████░░░░░░░░ 62% to compaction (threshold: 50% of window).\n```\n\n如果自动压缩被禁用,警告会告诉您上下文可能被截断。\n\n上下文压力是自动的 —— 无需配置。它纯粹作为面向用户的通知触发,不修改消息流或向模型上下文注入任何内容。\n\n## 凭据池策略\n\n当您为同一 provider 拥有多个 API 密钥或 OAuth token 时,配置轮换策略:\n\n```yaml\ncredential_pool_strategies:\n openrouter: round_robin # 均匀循环使用密钥\n anthropic: least_used # 始终选择使用最少的密钥\n```\n\n选项:`fill_first`(默认)、`round_robin`、`least_used`、`random`。完整文档请参阅[凭据池](/user-guide/features/credential-pools)。\n\n## Prompt 缓存\n\n当活跃 provider 支持时,Hermes 自动开启跨会话 prompt 缓存 —— 无需用户配置。\n\n对于**原生 Anthropic**、**OpenRouter** 和 **Nous Portal** 上的 Claude,Hermes 在系统提示词和技能块上附加带有 1 小时 TTL(`ttl: \"1h\"`)的 `cache_control` 断点。在新鲜的一小时内首次发送时按完整输入费率计费;同一小时内任何会话的后续发送以折扣缓存读取费率从缓存中提取。这意味着系统提示词、加载的技能内容以及任何长上下文包含的早期部分在第一个小时内跨 `hermes` 会话和分叉子 agent 被重用。\n\nQwen Cloud(阿里巴巴 DashScope)上游将缓存 TTL 限制为 5 分钟,因此 Hermes 在那里使用 5 分钟断点 TTL。其他通过第三方的 Claude 路径(AWS Bedrock、Azure Foundry)回退到 provider 自己的缓存默认值。xAI Grok 使用单独的会话固定对话 ID 机制 —— 参阅 [xAI prompt 缓存](/integrations/providers#xai-grok--responses-api--prompt-caching)。\n\n不存在禁用此功能的旋钮 —— 缓存始终开启,即使在单轮对话中也能节省费用,因为仅系统提示词就占输入 token 数的相当大比例。\n\n## 辅助模型\n\nHermes 使用\"辅助\"模型处理图像分析、网页摘要、浏览器截图分析、会话标题生成和上下文压缩等附带任务。默认情况下(`auxiliary.*.provider: \"auto\"`),Hermes 将每个辅助任务路由到您的**主聊天模型** —— 与您在 `hermes model` 中选择的相同 provider/模型。您无需配置任何内容即可开始,但请注意,在昂贵的推理模型(Opus、MiniMax M2.7 等)上,辅助任务会增加显著成本。如果您希望无论主模型如何都使用便宜且快速的附带任务,请显式设置 `auxiliary.<task>.provider` 和 `auxiliary.<task>.model`(例如,在 OpenRouter 上使用 Gemini Flash 进行视觉和网页提取)。\n\n:::note 为什么 \"auto\" 使用您的主模型\n早期版本将聚合器用户(OpenRouter、Nous Portal)分流到便宜的 provider 端默认值。这令人惊讶 —— 付费购买聚合器订阅的用户会看到不同的模型处理其辅助流量。`auto` 现在对所有人使用主模型,`config.yaml` 中的每任务覆盖仍然优先(见下方[完整辅助配置参考](#full-auxiliary-config-reference))。\n:::\n\n### 交互式配置辅助模型\n\n无需手动编辑 YAML,运行 `hermes model` 并从菜单中选择**\"配置辅助模型\"**。您将获得交互式的每任务选择器:\n\n```\n$ hermes model\n→ Configure auxiliary models\n\n[ ] vision currently: auto / main model\n[ ] web_extract currently: auto / main model\n[ ] title_generation currently: openrouter / google/gemini-3-flash-preview\n[ ] compression currently: auto / main model\n[ ] approval currently: auto / main model\n[ ] triage_specifier currently: auto / main model\n[ ] kanban_decomposer currently: auto / main model\n[ ] profile_describer currently: auto / main model\n```\n\n选择任务,选择 provider(OAuth 流程打开浏览器;API 密钥 provider 提示输入),选择模型。更改持久化到 `config.yaml` 中的 `auxiliary.<task>.*`。与主模型选择器相同的机制 —— 无需学习额外语法。\n\n### 视频教程\n\n<div style={{position: 'relative', width: '100%', aspectRatio: '16 / 9', marginBottom: '1.5rem'}}>\n <iframe\n src=\"https://www.youtube.com/embed/NoF-YajElIM\"\n title=\"Hermes Agent — Auxiliary Models Tutorial\"\n style={{position: 'absolute', top: 0, left: 0, width: '100%', height: '100%', border: 0}}\n allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\"\n allowFullScreen\n />\n</div>\n\n### 通用配置模式\n\nHermes 中的每个模型槽位 —— 辅助任务、压缩、回退 —— 使用相同的三个旋钮:\n\n| 键 | 作用 | 默认值 |\n|-----|-------------|---------|\n| `provider` | 用于认证和路由的 provider | `\"auto\"` |\n| `model` | 请求的模型 | provider 的默认值 |\n| `base_url` | 自定义 OpenAI 兼容端点(覆盖 provider) | 未设置 |\n\n当设置 `base_url` 时,Hermes 忽略 provider 并直接调用该端点(使用 `api_key` 或 `OPENAI_API_KEY` 进行认证)。当仅设置 `provider` 时,Hermes 使用该 provider 的内置认证和基础 URL。\n\n辅助任务的可用 providers:`auto`、`main`,以及[provider 注册表](/reference/environment-variables)中的任何 provider —— `openrouter`、`nous`、`openai-codex`、`copilot`、`copilot-acp`、`anthropic`、`gemini`、`google-gemini-cli`、`qwen-oauth`、`zai`、`kimi-coding`、`kimi-coding-cn`、`minimax`、`minimax-cn`、`minimax-oauth`、`deepseek`、`nvidia`、`xai`、`xai-oauth`、`ollama-cloud`、`alibaba`、`bedrock`、`huggingface`、`arcee`、`xiaomi`、`kilocode`、`opencode-zen`、`opencode-go`、`azure-foundry` —— 或您 `custom_providers` 列表中任何命名的自定义 provider(例如 `provider: \"beans\"`)。\n\n:::tip MiniMax OAuth\n`minimax-oauth` 通过浏览器 OAuth 登录(无需 API 密钥)。运行 `hermes model` 并选择 **MiniMax (OAuth)** 进行认证。辅助任务自动使用 `MiniMax-M2.7-highspeed`。参阅 [MiniMax OAuth 指南](../guides/minimax-oauth.md)。\n:::\n\n:::tip xAI Grok OAuth\n`xai-oauth` 通过浏览器 OAuth 为 SuperGrok 和 X Premium+ 订阅者登录(无需 API 密钥)。运行 `hermes model` 并选择 **xAI Grok OAuth (SuperGrok / Premium+)** 进行认证。相同的 OAuth token 可重用于每个直接到 xAI 的接口(聊天、辅助任务、TTS、图像生成、视频生成、转录)。参阅 [xAI Grok OAuth 指南](../guides/xai-grok-oauth.md),如果 Hermes 在远程主机上,请参阅 [SSH/远程主机上的 OAuth](../guides/oauth-over-ssh.md)。\n:::\n\n:::warning `\"main\"` 仅用于辅助任务\n`\"main\"` provider 选项表示\"使用我的主 agent 使用的任何 provider\" —— 它仅在 `auxiliary:`、`compression:` 和 `fallback_model:` 配置中有效。它**不是**顶级 `model.provider` 设置的有效值。如果您使用自定义 OpenAI 兼容端点,请在 `model:` 部分设置 `provider: custom`。所有主模型 provider 选项请参阅 [AI Providers](/integrations/providers)。\n:::\n\n### 完整辅助配置参考\n\n```yaml\nauxiliary:\n # 图像分析(vision_analyze 工具 + 浏览器截图)\n vision:\n provider: \"auto\" # \"auto\"、\"openrouter\"、\"nous\"、\"codex\"、\"main\" 等\n model: \"\" # 例如 \"openai/gpt-4o\"、\"google/gemini-2.5-flash\"\n base_url: \"\" # 自定义 OpenAI 兼容端点(覆盖 provider)\n api_key: \"\" # base_url 的 API 密钥(回退到 OPENAI_API_KEY)\n timeout: 120 # 秒 —— LLM API 调用超时;视觉负载需要宽裕的超时\n download_timeout: 30 # 秒 —— 图像 HTTP 下载;慢速连接请增加\n\n # 网页摘要 + 浏览器页面文本提取\n web_extract:\n provider: \"auto\"\n model: \"\" # 例如 \"google/gemini-2.5-flash\"\n base_url: \"\"\n api_key: \"\"\n timeout: 360 # 秒(6 分钟)—— 每次尝试的 LLM 摘要\n\n # 危险命令审批分类器\n approval:\n provider: \"auto\"\n model: \"\"\n base_url: \"\"\n api_key: \"\"\n timeout: 30 # 秒\n\n # 上下文压缩超时(与 compression.* 配置分开)\n compression:\n timeout: 120 # 秒 —— 压缩摘要长对话,需要更多时间\n\n # 技能中心 —— 技能匹配和搜索\n skills_hub:\n provider: \"auto\"\n model: \"\"\n base_url: \"\"\n api_key: \"\"\n timeout: 30\n\n # MCP 工具调度\n mcp:\n provider: \"auto\"\n model: \"\"\n base_url: \"\"\n api_key: \"\"\n timeout: 30\n\n # Kanban 分类规格说明器 —— `hermes kanban specify <id>`(或\n # 仪表板上 Triage 列卡片的 ✨ Specify 按钮)使用此\n # 槽位将单行描述扩展为具体规格并将\n # 任务提升到 `todo`。便宜快速的模型在这里效果很好;规格扩展\n # 很短,不需要推理深度。\n triage_specifier:\n provider: \"auto\"\n model: \"\"\n base_url: \"\"\n api_key: \"\"\n timeout: 120\n```\n\n:::tip\n每个辅助任务都有可配置的 `timeout`(秒)。默认值:vision 120s、web_extract 360s、approval 30s、compression 120s。如果您为辅助任务使用慢速本地模型,请增加这些值。Vision 还有单独的 `download_timeout`(默认 30s)用于 HTTP 图像下载 —— 对于慢速连接或自托管图像服务器,请增加此值。\n:::\n\n:::info\n上下文压缩有自己的 `compression:` 块用于阈值,以及 `auxiliary.compression:` 块用于模型/provider 设置 —— 参阅上方的[上下文压缩](#context-compression)。回退模型使用 `fallback_model:` 块 —— 参阅[回退模型](/integrations/providers#fallback-model)。三者都遵循相同的 provider/model/base_url 模式。\n:::\n\n### OpenRouter 路由和辅助任务的 Pareto Code\n\n当辅助任务解析到 OpenRouter(显式或通过 `provider: \"main\"` 而您的主 agent 在 OpenRouter 上)时,主 agent 的 `provider_routing` 和 `openrouter.min_coding_score` 设置**不会传播** —— 按设计,每个辅助任务是独立的。要为特定辅助任务设置 OpenRouter provider 偏好或使用 [Pareto Code 路由器](/integrations/providers#openrouter-pareto-code-router),请通过 `extra_body` 按任务设置:\n\n```yaml\nauxiliary:\n compression:\n provider: openrouter\n model: openrouter/pareto-code # 为此任务使用 Pareto Code 路由器\n extra_body:\n provider: # OpenRouter provider 路由偏好\n order: [anthropic, google] # 按此顺序尝试这些 providers\n sort: throughput # 或 \"price\" | \"latency\"\n # only: [anthropic] # 限制到特定 provider\n # ignore: [deepinfra] # 排除特定 providers\n plugins: # OpenRouter Pareto Code 路由器旋钮\n - id: pareto-router\n min_coding_score: 0.5 # 0.0–1.0;越高 = 更强的编码能力\n```\n\n形状与 OpenRouter 在聊天补全请求体中接受的内容一致。Hermes 原样转发整个 `extra_body`,因此 [openrouter.ai/docs](https://openrouter.ai/docs) 中记录的任何其他 OpenRouter 请求体字段都以相同方式工作。\n\n### 更改视觉模型\n\n使用 GPT-4o 而非 Gemini Flash 进行图像分析:\n\n```yaml\nauxiliary:\n vision:\n model: \"openai/gpt-4o\"\n```\n\n或通过环境变量(在 `~/.hermes/.env` 中):\n\n```bash\nAUXILIARY_VISION_MODEL=openai/gpt-4o\n```\n\n### Provider 选项\n\n这些选项适用于**辅助任务配置**(`auxiliary:`、`compression:`、`fallback_model:`),而非您的主 `model.provider` 设置。\n\n| Provider | 描述 | 要求 |\n|----------|-------------|-------------|\n| `\"auto\"` | 最佳可用(默认)。Vision 尝试 OpenRouter → Nous → Codex。 | — |\n| `\"openrouter\"` | 强制 OpenRouter —— 路由到任何模型(Gemini、GPT-4o、Claude 等) | `OPENROUTER_API_KEY` |\n| `\"nous\"` | 强制 Nous Portal | `hermes auth` |\n| `\"codex\"` | 强制 Codex OAuth(ChatGPT 账户)。支持视觉(gpt-5.3-codex)。 | `hermes model` → Codex |\n| `\"minimax-oauth\"` | 强制 MiniMax OAuth(浏览器登录,无需 API 密钥)。辅助任务使用 MiniMax-M2.7-highspeed。 | `hermes model` → MiniMax (OAuth) |\n| `\"xai-oauth\"` | 强制 xAI Grok OAuth(SuperGrok 或 X Premium+ 订阅者的浏览器登录,无需 API 密钥)。相同的 OAuth token 涵盖聊天、TTS、图像、视频和转录。 | `hermes model` → xAI Grok OAuth (SuperGrok / Premium+) |\n| `\"main\"` | 使用您的活跃自定义/主端点。可以来自 `OPENAI_BASE_URL` + `OPENAI_API_KEY` 或通过 `hermes model` / `config.yaml` 保存的自定义端点。适用于 OpenAI、本地模型或任何 OpenAI 兼容 API。**仅限辅助任务 —— 对 `model.provider` 无效。** | 自定义端点凭据 + 基础 URL |\n\n当您希望附带任务绕过默认路由器时,主 provider 目录中的直接 API 密钥 providers 也在这里工作。配置 `GMI_API_KEY` 后,`gmi` 有效:\n\n```yaml\nauxiliary:\n compression:\n provider: \"gmi\"\n model: \"anthropic/claude-opus-4.6\"\n```\n\n对于 GMI 辅助路由,使用 GMI 的 `/v1/models` 端点返回的确切模型 ID。\n\n### 常见设置\n\n**使用直接自定义端点**(比 `provider: \"main\"` 对本地/自托管 API 更清晰):\n```yaml\nauxiliary:\n vision:\n base_url: \"http://localhost:1234/v1\"\n api_key: \"local-key\"\n model: \"qwen2.5-vl\"\n```\n\n`base_url` 优先于 `provider`,因此这是将辅助任务路由到特定端点的最明确方式。对于直接端点覆盖,Hermes 使用配置的 `api_key` 或回退到 `OPENAI_API_KEY`;它不会为该自定义端点重用 `OPENROUTER_API_KEY`。\n\n**使用 OpenAI API 密钥进行视觉:**\n```yaml\n# 在 ~/.hermes/.env 中:\n# OPENAI_BASE_URL=https://api.openai.com/v1\n# OPENAI_API_KEY=sk-...\n\nauxiliary:\n vision:\n provider: \"main\"\n model: \"gpt-4o\" # 或 \"gpt-4o-mini\" 更便宜\n```\n\n**使用 OpenRouter 进行视觉**(路由到任何模型):\n```yaml\nauxiliary:\n vision:\n provider: \"openrouter\"\n model: \"openai/gpt-4o\" # 或 \"google/gemini-2.5-flash\" 等\n```\n\n**使用 Codex OAuth**(ChatGPT Pro/Plus 账户 —— 无需 API 密钥):\n```yaml\nauxiliary:\n vision:\n provider: \"codex\" # 使用您的 ChatGPT OAuth token\n # 模型默认为 gpt-5.3-codex(支持视觉)\n```\n\n**使用 MiniMax OAuth**(浏览器登录,无需 API 密钥):\n```yaml\nmodel:\n default: MiniMax-M2.7\n provider: minimax-oauth\n base_url: https://api.minimax.io/anthropic\n```\n运行 `hermes model` 并选择 **MiniMax (OAuth)** 自动登录并设置此项。对于中国区域,基础 URL 将是 `https://api.minimaxi.com/anthropic`。完整演练请参阅 [MiniMax OAuth 指南](../guides/minimax-oauth.md)。\n\n**使用本地/自托管模型:**\n```yaml\nauxiliary:\n vision:\n provider: \"main\" # 使用您的活跃自定义端点\n model: \"my-local-model\"\n```\n\n`provider: \"main\"` 使用 Hermes 用于普通聊天的任何 provider —— 无论是命名的自定义 provider(例如 `beans`)、内置 provider(如 `openrouter`)还是旧版 `OPENAI_BASE_URL` 端点。\n\n:::tip\n如果您使用 Codex OAuth 作为主模型 provider,视觉会自动工作 —— 无需额外配置。Codex 包含在视觉的自动检测链中。\n:::\n\n:::warning\n**视觉需要多模态模型。** 如果您设置 `provider: \"main\"`,请确保您的端点支持多模态/视觉 —— 否则图像分析将失败。\n:::\n\n### 环境变量(旧版)\n\n辅助模型也可以通过环境变量配置。但是,`config.yaml` 是首选方法 —— 它更易于管理,并支持所有选项,包括 `base_url` 和 `api_key`。\n\n| 设置 | 环境变量 |\n|---------|---------------------|\n| Vision provider | `AUXILIARY_VISION_PROVIDER` |\n| Vision 模型 | `AUXILIARY_VISION_MODEL` |\n| Vision 端点 | `AUXILIARY_VISION_BASE_URL` |\n| Vision API 密钥 | `AUXILIARY_VISION_API_KEY` |\n| Web 提取 provider | `AUXILIARY_WEB_EXTRACT_PROVIDER` |\n| Web 提取模型 | `AUXILIARY_WEB_EXTRACT_MODEL` |\n| Web 提取端点 | `AUXILIARY_WEB_EXTRACT_BASE_URL` |\n| Web 提取 API 密钥 | `AUXILIARY_WEB_EXTRACT_API_KEY` |\n\n压缩和回退模型设置仅限 config.yaml。\n\n:::tip\n运行 `hermes config` 查看您当前的辅助模型设置。覆盖仅在与默认值不同时显示。\n:::\n\n## 推理努力程度\n\n控制模型在响应前进行多少\"思考\":\n\n```yaml\nagent:\n reasoning_effort: \"\" # 空 = 中等(默认)。选项:none、minimal、low、medium、high、xhigh(最大)\n```\n\n未设置时(默认),推理努力程度默认为\"medium\" —— 适合大多数任务的平衡级别。设置值会覆盖它 —— 更高的推理努力程度在复杂任务上提供更好的结果,但代价是更多 token 和延迟。\n\n您也可以在运行时使用 `/reasoning` 命令更改推理努力程度:\n\n```\n/reasoning # 显示当前努力程度和显示状态\n/reasoning high # 将推理努力程度设为 high\n/reasoning none # 禁用推理\n/reasoning show # 在每次响应上方显示模型思考\n/reasoning hide # 隐藏模型思考\n```\n\n## 工具使用强制\n\n某些模型偶尔会将预期操作描述为文本而不是进行工具调用(\"我会运行测试...\"而不是实际调用终端)。工具使用强制会注入系统提示词指导,引导模型实际调用工具。\n\n```yaml\nagent:\n tool_use_enforcement: \"auto\" # \"auto\" | true | false | [\"model-substring\", ...]\n```\n\n| 值 | 行为 |\n|-------|----------|\n| `\"auto\"`(默认) | 对匹配以下模型启用:`gpt`、`codex`、`gemini`、`gemma`、`grok`。对所有其他模型禁用(Claude、DeepSeek、Qwen 等)。 |\n| `true` | 始终启用,无论模型如何。如果您注意到当前模型描述操作而不是执行操作,请使用此选项。 |\n| `false` | 始终禁用,无论模型如何。 |\n| `[\"gpt\", \"codex\", \"qwen\", \"llama\"]` | 仅当模型名称包含列出的子字符串之一时启用(不区分大小写)。 |\n\n### 注入的内容\n\n启用后,系统提示词中可能会添加三层指导:\n\n1. **通用工具使用强制**(所有匹配模型)—— 指示模型立即进行工具调用而不是描述意图,持续工作直到任务完成,永远不要以未来操作的承诺结束轮次。\n\n2. **OpenAI 执行纪律**(仅限 GPT 和 Codex 模型)—— 针对 GPT 特定失败模式的额外指导:在部分结果上放弃工作、跳过先决条件查找、幻觉而不是使用工具、在未验证的情况下宣布\"完成\"。\n\n3. **Google 操作指导**(仅限 Gemini 和 Gemma 模型)—— 简洁性、绝对路径、并行工具调用和编辑前验证模式。\n\n这些对用户透明,仅影响系统提示词。已经可靠使用工具的模型(如 Claude)不需要此指导,这就是为什么 `\"auto\"` 排除它们。\n\n### 何时开启\n\n如果您使用的模型不在默认自动列表中,并注意到它经常描述它*会*做什么而不是实际去做,请设置 `tool_use_enforcement: true` 或将模型子字符串添加到列表中:\n\n```yaml\nagent:\n tool_use_enforcement: [\"gpt\", \"codex\", \"gemini\", \"grok\", \"my-custom-model\"]\n```\n\n## TTS 配置\n\n```yaml\ntts:\n provider: \"edge\" # \"edge\" | \"elevenlabs\" | \"openai\" | \"minimax\" | \"mistral\" | \"gemini\" | \"xai\" | \"neutts\"\n speed: 1.0 # 全局速度倍数(所有 provider 的回退)\n edge:\n voice: \"en-US-AriaNeural\" # 322 种声音,74 种语言\n speed: 1.0 # 速度倍数(转换为速率百分比,例如 1.5 → +50%)\n elevenlabs:\n voice_id: \"pNInz6obpgDQGcFmaJgB\"\n model_id: \"eleven_multilingual_v2\"\n openai:\n model: \"gpt-4o-mini-tts\"\n voice: \"alloy\" # alloy、echo、fable、onyx、nova、shimmer\n speed: 1.0 # 速度倍数(API 限制为 0.25–4.0)\n base_url: \"https://api.openai.com/v1\" # 覆盖 OpenAI 兼容 TTS 端点\n minimax:\n speed: 1.0 # 语音速度倍数\n # base_url: \"\" # 可选:覆盖 OpenAI 兼容 TTS 端点\n mistral:\n model: \"voxtral-mini-tts-2603\"\n voice_id: \"c69964a6-ab8b-4f8a-9465-ec0925096ec8\" # Paul - Neutral(默认)\n gemini:\n model: \"gemini-2.5-flash-preview-tts\" # 或 gemini-2.5-pro-preview-tts\n voice: \"Kore\" # 30 种预置声音:Zephyr、Puck、Kore、Enceladus 等\n xai:\n voice_id: \"eve\" # xAI TTS 声音\n language: \"en\" # ISO 639-1\n sample_rate: 24000\n bit_rate: 128000 # MP3 比特率\n # base_url: \"https://api.x.ai/v1\"\n neutts:\n ref_audio: ''\n ref_text: ''\n model: neuphonic/neutts-air-q4-gguf\n device: cpu\n```\n\n这控制 `text_to_speech` 工具和语音模式中的口语回复(CLI 中的 `/voice tts` 或消息 gateway)。\n\n**速度回退层次:** provider 特定速度(例如 `tts.edge.speed`)→ 全局 `tts.speed` → `1.0` 默认值。设置全局 `tts.speed` 以在所有 provider 上应用统一速度,或按 provider 覆盖以进行精细控制。\n\n## 显示设置\n\n```yaml\ndisplay:\n tool_progress: all # off | new | all | verbose\n tool_progress_command: false # 在消息 gateway 中启用 /verbose 斜杠命令\n platforms: {} # 每平台显示覆盖(见下文)\n tool_progress_overrides: {} # 已弃用 —— 改用 display.platforms\n interim_assistant_messages: true # Gateway:将自然的轮次中 assistant 更新作为单独消息发送\n skin: default # 内置或自定义 CLI 皮肤(参阅 user-guide/features/skins)\n personality: \"kawaii\" # 旧版外观字段,仍在某些摘要中显示\n compact: false # 紧凑输出模式(减少空白)\n resume_display: full # full(恢复时显示之前的消息)| minimal(仅单行)\n bell_on_complete: false # 当 agent 完成时播放终端铃声(适合长任务)\n show_reasoning: false # 在每次响应上方显示模型推理/思考(用 /reasoning show|hide 切换)\n streaming: false # 将 token 实时流式传输到终端\n show_cost: false # 在 CLI 状态栏中显示估计 $ 成本\n timestamps: false # 为 true 时,在 CLI/TUI 记录中为用户和 assistant 标签添加 [HH:MM] 时间戳前缀\n tool_preview_length: 0 # 工具调用预览的最大字符数(0 = 无限制,显示完整路径/命令)\n runtime_footer: # Gateway:在最终回复中附加运行时上下文页脚\n enabled: false\n fields: [\"model\", \"context_pct\", \"cwd\"]\n file_mutation_verifier: true # 当本轮 write_file/patch 调用失败时附加建议性页脚\n language: en # 静态消息的 UI 语言(审批提示、部分 gateway 回复)。en | zh | zh-hant | ja | de | es | fr | tr | uk | af | ko | it | ga | pt | ru | hu\n```\n\n### 文件变更验证器\n\n当 `display.file_mutation_verifier` 为 `true`(默认)时,每当本轮中 `write_file` 或 `patch` 调用失败且从未被对同一路径的成功写入取代时,Hermes 会在 assistant 的最终响应中附加一行建议。这捕获了\"批量并行补丁,一半静默失败,模型总结成功\"这类过度声明,而无需您在每次编辑后手动运行 `git status`。\n\n示例页脚:\n\n```\n⚠️ File-mutation verifier: 3 file(s) were NOT modified this turn despite any wording above that may suggest otherwise. Run `git status` or `read_file` to confirm.\n • concepts/automatic-organization.md — [patch] Could not find match for old_string\n • concepts/lora.md — [patch] Could not find match for old_string\n • concepts/rag-pipeline.md — [patch] Could not find match for old_string\n```\n\n设置 `file_mutation_verifier: false`(或 `HERMES_FILE_MUTATION_VERIFIER=0`)以禁止页脚。验证器仅在轮次结束时有真实失败未解决时触发 —— 在同一轮次内重试失败补丁并成功的模型不会为该文件触发它。\n\n### 静态消息的 UI 语言\n\n`display.language` 设置翻译一小组静态面向用户的消息 —— CLI 审批提示、少数 gateway 斜杠命令回复(例如重启排空通知、\"审批已过期\"、\"目标已清除\")。它**不**翻译 agent 响应、日志行、工具输出、错误回溯或斜杠命令描述 —— 这些保持英文。如果您希望 agent 本身用另一种语言回复,只需在您的提示词或系统消息中告诉它。\n\n支持的值:`en`(默认)、`zh`(简体中文)、`ja`(日语)、`de`(德语)、`es`(西班牙语)、`fr`(法语)、`tr`(土耳其语)、`uk`(乌克兰语)。未知值回退到英文。\n\n您也可以使用 `HERMES_LANGUAGE` 环境变量按会话设置,它会覆盖配置值。\n\n```yaml\ndisplay:\n language: zh # CLI 审批提示以中文显示\n```\n\n| 模式 | 您看到的内容 |\n|------|-------------|\n| `off` | 静默 —— 仅最终响应 |\n| `new` | 仅在工具更改时显示工具指示器 |\n| `all` | 每次工具调用附带简短预览(默认) |\n| `verbose` | 完整参数、结果和调试日志 |\n\n在 CLI 中,使用 `/verbose` 循环切换这些模式。要在消息平台(Telegram、Discord、Slack 等)中使用 `/verbose`,请在上方的 `display` 部分设置 `tool_progress_command: true`。该命令将循环切换模式并保存到配置。\n\n### 运行时元数据页脚(仅限 gateway)\n\n当 `display.runtime_footer.enabled: true` 时,Hermes 在每个 gateway 轮次的**最终**消息中附加一个小型运行时上下文页脚 —— 与 CLI 在其状态栏中显示的相同信息(模型、上下文 %、cwd、会话时长、token、成本)。默认关闭;如果您的团队希望每个回复都包含来源信息,请按 gateway 选择加入。\n\n```yaml\ndisplay:\n runtime_footer:\n enabled: true\n fields: [\"model\", \"context_pct\", \"cwd\"] # 任意:model、context_pct、cwd、duration、tokens、cost\n```\n\n`/footer` 斜杠命令在任何会话中运行时切换此功能。\n\n附加到 Telegram/Discord/Slack 回复的示例页脚:\n\n```\n— claude-opus-4.7 · 12 tool calls · 2m 14s · $0.042\n```\n\n只有轮次的**最终**消息获得页脚;中间更新保持干净。\n\n### 每平台进度覆盖\n\n不同平台有不同的详细程度需求。例如,Signal 无法编辑消息,因此每次进度更新都会成为单独的消息 —— 很嘈杂。使用 `display.platforms` 设置每平台模式:\n\n```yaml\ndisplay:\n tool_progress: all # 全局默认\n platforms:\n signal:\n tool_progress: 'off' # 在 Signal 上静默进度\n telegram:\n tool_progress: verbose # 在 Telegram 上详细进度\n slack:\n tool_progress: 'off' # 在共享 Slack 工作区中保持安静\n```\n\n没有覆盖的平台回退到全局 `tool_progress` 值。有效平台键:`telegram`、`discord`、`slack`、`signal`、`whatsapp`、`matrix`、`mattermost`、`email`、`sms`、`homeassistant`、`dingtalk`、`feishu`、`wecom`、`weixin`、`bluebubbles`、`qqbot`。旧版 `display.tool_progress_overrides` 键仍可加载以向后兼容,但已弃用,并在首次加载时迁移到 `display.platforms`。\n\n`interim_assistant_messages` 仅限 gateway。启用后,Hermes 将已完成的轮次中 assistant 更新作为单独的聊天消息发送。这与 `tool_progress` 无关,不需要 gateway 流式传输。\n\n## 隐私\n\n```yaml\nprivacy:\n redact_pii: false # 从 LLM 上下文中删除 PII(仅限 gateway)\n```\n\n当 `redact_pii` 为 `true` 时,gateway 在将系统提示词发送到受支持平台上的 LLM 之前,会从中删除个人身份信息:\n\n| 字段 | 处理方式 |\n|-------|-----------|\n| 电话号码(WhatsApp/Signal 上的用户 ID) | 哈希为 `user_<12-char-sha256>` |\n| 用户 ID | 哈希为 `user_<12-char-sha256>` |\n| 聊天 ID | 数字部分哈希,保留平台前缀(`telegram:<hash>`) |\n| 主频道 ID | 数字部分哈希 |\n| 用户名/昵称 | **不受影响**(用户选择的,公开可见) |\n\n**平台支持:** 删除适用于 WhatsApp、Signal 和 Telegram。Discord 和 Slack 被排除,因为它们的提及系统(`<@user_id>`)需要 LLM 上下文中的真实 ID。\n\n哈希是确定性的 —— 同一用户始终映射到同一哈希,因此模型仍然可以在群聊中区分用户。路由和传递在内部使用原始值。\n\n## 语音转文字(STT)\n\n```yaml\nstt:\n provider: \"local\" # \"local\" | \"groq\" | \"openai\" | \"mistral\"\n local:\n model: \"base\" # tiny、base、small、medium、large-v3\n openai:\n model: \"whisper-1\" # whisper-1 | gpt-4o-mini-transcribe | gpt-4o-transcribe\n # model: \"whisper-1\" # 旧版回退键仍受支持\n```\n\nProvider 行为:\n\n- `local` 使用在您机器上运行的 `faster-whisper`。使用 `pip install faster-whisper` 单独安装。\n- `groq` 使用 Groq 的 Whisper 兼容端点,读取 `GROQ_API_KEY`。\n- `openai` 使用 OpenAI 语音 API,读取 `VOICE_TOOLS_OPENAI_KEY`。\n\n如果请求的 provider 不可用,Hermes 按此顺序自动回退:`local` → `groq` → `openai`。\n\nGroq 和 OpenAI 模型覆盖由环境变量驱动:\n\n```bash\nSTT_GROQ_MODEL=whisper-large-v3-turbo\nSTT_OPENAI_MODEL=whisper-1\nGROQ_BASE_URL=https://api.groq.com/openai/v1\nSTT_OPENAI_BASE_URL=https://api.openai.com/v1\n```\n\n## 语音模式(CLI)\n\n```yaml\nvoice:\n record_key: \"ctrl+b\" # CLI 内的按键通话键\n max_recording_seconds: 120 # 长录音的硬停止\n auto_tts: false # /voice on 时自动启用口语回复\n beep_enabled: true # 在 CLI 语音模式中播放录音开始/停止提示音\n silence_threshold: 200 # 语音检测的 RMS 阈值\n silence_duration: 3.0 # 自动停止前的静默秒数\n```\n\n在 CLI 中使用 `/voice on` 启用麦克风模式,使用 `record_key` 开始/停止录音,使用 `/voice tts` 切换口语回复。端到端设置和平台特定行为请参阅[语音模式](/user-guide/features/voice-mode)。\n\n## 流式传输\n\n将 token 实时流式传输到终端或消息平台,而不是等待完整响应。\n\n### CLI 流式传输\n\n```yaml\ndisplay:\n streaming: true # 实时将 token 流式传输到终端\n show_reasoning: true # 同时流式传输推理/思考 token(可选)\n```\n\n启用后,响应在流式传输框内逐 token 出现。工具调用仍然静默捕获。如果 provider 不支持流式传输,它会自动回退到正常显示。\n\n### Gateway 流式传输(Telegram、Discord、Slack)\n\n```yaml\nstreaming:\n enabled: true # 启用渐进式消息编辑\n transport: edit # \"edit\"(渐进式消息编辑)或 \"off\"\n edit_interval: 0.3 # 消息编辑之间的秒数\n buffer_threshold: 40 # 强制编辑刷新前的字符数\n cursor: \" ▉\" # 流式传输期间显示的光标\n fresh_final_after_seconds: 60 # 当预览超过此时间时发送新的最终消息(Telegram);0 = 始终就地编辑\n```\n\n启用后,bot 在第一个 token 时发送消息,然后随着更多 token 到来渐进式编辑它。不支持消息编辑的平台(Signal、Email、Home Assistant)在第一次尝试时自动检测 —— 该会话的流式传输被优雅地禁用,不会产生大量消息。\n\n对于不带渐进式 token 编辑的独立自然轮次中 assistant 更新,请设置 `display.interim_assistant_messages: true`。\n\n**溢出处理:** 如果流式传输的文本超过平台的消息长度限制(约 4096 字符),当前消息被最终化,新消息自动开始。\n\n**新的最终消息(Telegram):** Telegram 的 `editMessageText` 保留原始消息时间戳,因此长时间运行的流式回复即使在完成后也会保留第一个 token 的时间戳。当 `fresh_final_after_seconds > 0`(默认 `60`)时,完成的回复作为全新消息传递(尽力删除旧预览),以便 Telegram 的可见时间戳反映完成时间。短预览仍然就地最终化。设置为 `0` 以始终就地编辑。\n\n:::note\n流式传输默认禁用。在 `~/.hermes/config.yaml` 中启用以尝试流式传输 UX。\n:::\n\n## 群聊会话隔离\n\n控制共享聊天是每个房间保持一个对话还是每个参与者一个对话:\n\n```yaml\ngroup_sessions_per_user: true # true = 群组/频道中每用户隔离,false = 每个聊天一个共享会话\n```\n\n- `true` 是默认和推荐设置。在 Discord 频道、Telegram 群组、Slack 频道和类似共享上下文中,当平台提供用户 ID 时,每个发送者获得自己的会话。\n- `false` 恢复到旧的共享房间行为。如果您明确希望 Hermes 将频道视为一个协作对话,这可能有用,但这也意味着用户共享上下文、token 成本和中断状态。\n- 私信不受影响。Hermes 仍然像往常一样通过聊天/DM ID 键入 DM。\n- 线程与其父频道保持隔离;使用 `true` 时,每个参与者在线程内也获得自己的会话。\n\n有关行为详情和示例,请参阅[会话](/user-guide/sessions)和 [Discord 指南](/user-guide/messaging/discord)。\n\n## 未授权 DM 行为\n\n控制当未知用户发送私信时 Hermes 的行为:\n\n```yaml\nunauthorized_dm_behavior: pair\n\nwhatsapp:\n unauthorized_dm_behavior: ignore\n```\n\n- `pair` 是默认值。Hermes 拒绝访问,但在 DM 中回复一次性配对码。\n- `ignore` 静默丢弃未授权的 DM。\n- 平台部分覆盖全局默认值,因此您可以在广泛范围内保持配对启用,同时使一个平台更安静。\n\n## 快速命令\n\n定义自定义命令,这些命令要么在不调用 LLM 的情况下运行 shell 命令,要么将一个斜杠命令别名为另一个。Exec 快速命令是零 token 的,对于从消息平台(Telegram、Discord 等)进行快速服务器检查或实用脚本很有用。\n\n```yaml\nquick_commands:\n status:\n type: exec\n command: systemctl status hermes-agent\n disk:\n type: exec\n command: df -h /\n update:\n type: exec\n command: cd ~/.hermes/hermes-agent && git pull && pip install -e .\n gpu:\n type: exec\n command: nvidia-smi --query-gpu=name,utilization.gpu,memory.used,memory.total --format=csv,noheader\n restart:\n type: alias\n target: /gateway restart\n```\n\n用法:在 CLI 或任何消息平台中输入 `/status`、`/disk`、`/update`、`/gpu` 或 `/restart`。`exec` 命令在宿主本地运行并直接返回输出 —— 无 LLM 调用,不消耗 token。`alias` 命令重写为配置的斜杠命令目标。\n\n- **30 秒超时** —— 长时间运行的命令被终止并显示错误消息\n- **优先级** —— 快速命令在技能命令之前检查,因此您可以覆盖技能名称\n- **自动补全** —— 快速命令在调度时解析,不显示在内置斜杠命令自动补全表中\n- **类型** —— 支持的类型为 `exec` 和 `alias`;其他类型显示错误\n- **到处可用** —— CLI、Telegram、Discord、Slack、WhatsApp、Signal、Email、Home Assistant\n\n仅字符串的 prompt 快捷方式不是有效的快速命令。对于可重用的 prompt 工作流,请创建技能或别名到现有斜杠命令。\n\n## 人类延迟\n\n在消息平台中模拟类人响应节奏:\n\n```yaml\nhuman_delay:\n mode: \"off\" # off | natural | custom\n min_ms: 800 # 最小延迟(自定义模式)\n max_ms: 2500 # 最大延迟(自定义模式)\n```\n\n## 代码执行\n\n配置 `execute_code` 工具:\n\n```yaml\ncode_execution:\n mode: project # project(默认)| strict\n timeout: 300 # 最大执行时间(秒)\n max_tool_calls: 50 # 代码执行中的最大工具调用次数\n```\n\n**`mode`** 控制脚本的工作目录和 Python 解释器:\n\n- **`project`**(默认)—— 脚本在会话的工作目录中以活跃 virtualenv/conda 环境的 python 运行。项目依赖(`pandas`、`torch`、项目包)和相对路径(`.env`、`./data.csv`)自然解析,与 `terminal()` 看到的一致。\n- **`strict`** —— 脚本在临时暂存目录中以 `sys.executable`(Hermes 自己的 python)运行。最大可重现性,但项目依赖和相对路径不会解析。\n\n环境清理(删除 `*_API_KEY`、`*_TOKEN`、`*_SECRET`、`*_PASSWORD`、`*_CREDENTIAL`、`*_PASSWD`、`*_AUTH`)和工具白名单在两种模式下完全相同 —— 切换模式不会改变安全态势。\n\n## Web 搜索后端\n\n`web_search` 和 `web_extract` 工具支持五种后端 provider。在 `config.yaml` 中或通过 `hermes tools` 配置后端:\n\n```yaml\nweb:\n backend: firecrawl # firecrawl | searxng | parallel | tavily | exa\n\n # 或使用每功能键混合 provider(例如免费搜索 + 付费提取):\n search_backend: \"searxng\"\n extract_backend: \"firecrawl\"\n```\n\n| 后端 | 环境变量 | 搜索 | 提取 |\n|---------|---------|--------|---------|\n| **Firecrawl**(默认) | `FIRECRAWL_API_KEY` | ✔ | ✔ |\n| **SearXNG** | `SEARXNG_URL` | ✔ | — |\n| **Parallel** | `PARALLEL_API_KEY` | ✔ | ✔ |\n| **Tavily** | `TAVILY_API_KEY` | ✔ | ✔ |\n| **Exa** | `EXA_API_KEY` | ✔ | ✔ |\n\n**后端选择:** 如果未设置 `web.backend`,后端从可用的 API 密钥自动检测。如果仅设置了 `SEARXNG_URL`,使用 SearXNG。如果仅设置了 `EXA_API_KEY`,使用 Exa。如果仅设置了 `TAVILY_API_KEY`,使用 Tavily。如果仅设置了 `PARALLEL_API_KEY`,使用 Parallel。否则 Firecrawl 是默认值。\n\n**SearXNG** 是一个免费、自托管、尊重隐私的元搜索引擎,查询 70+ 个搜索引擎。无需 API 密钥 —— 只需将 `SEARXNG_URL` 设置为您的实例(例如 `http://localhost:8080`)。SearXNG 仅限搜索;`web_extract` 需要单独的提取 provider(设置 `web.extract_backend`)。Docker 设置说明请参阅 [Web 搜索设置指南](/user-guide/features/web-search)。\n\n**自托管 Firecrawl:** 设置 `FIRECRAWL_API_URL` 指向您自己的实例。设置自定义 URL 后,API 密钥变为可选(在服务器上设置 `USE_DB_AUTHENTICATION=***` 以禁用认证)。\n\n**Parallel 搜索模式:** 设置 `PARALLEL_SEARCH_MODE` 控制搜索行为 —— `fast`、`one-shot` 或 `agentic`(默认:`agentic`)。\n\n**Exa:** 在 `~/.hermes/.env` 中设置 `EXA_API_KEY`。支持 `category` 过滤(`company`、`research paper`、`news`、`people`、`personal site`、`pdf`)和域名/日期过滤器。\n\n## 浏览器\n\n配置浏览器自动化行为:\n\n```yaml\nbrowser:\n inactivity_timeout: 120 # 自动关闭空闲会话前的秒数\n command_timeout: 30 # 浏览器命令超时(截图、导航等)(秒)\n record_sessions: false # 自动将浏览器会话录制为 WebM 视频到 ~/.hermes/browser_recordings/\n # 可选 CDP 覆盖 —— 设置后,Hermes 直接附加到您自己的\n # Chromium 系浏览器(通过 /browser connect),而不是启动无头浏览器。\n cdp_url: \"\"\n # 对话框监督器 —— 控制当 CDP 后端附加时(Browserbase、本地 Chromium 系\n # 浏览器通过 /browser connect)如何处理原生 JS 对话框(alert/confirm/prompt)。\n # 在 Camofox 和默认本地 agent 浏览器模式下忽略。\n dialog_policy: must_respond # must_respond | auto_dismiss | auto_accept\n dialog_timeout_s: 300 # must_respond 下的安全自动关闭(秒)\n camofox:\n managed_persistence: false # 为 true 时,Camofox 会话跨重启持久化 cookie/登录\n user_id: \"\" # 可选的外部管理 Camofox userId\n session_key: \"\" # Hermes 创建标签页时发送的可选会话密钥\n adopt_existing_tab: false # 在创建新标签页之前重用此身份的现有标签页\n```\n\n**对话框策略:**\n\n- `must_respond`(默认)—— 捕获对话框,在 `browser_snapshot.pending_dialogs` 中显示,等待 agent 调用 `browser_dialog(action=...)`。在 `dialog_timeout_s` 秒内无响应后,对话框被自动关闭以防止页面的 JS 线程永久停滞。\n- `auto_dismiss` —— 捕获,立即关闭。Agent 仍然在事后的 `browser_snapshot.recent_dialogs` 中看到对话框记录,`closed_by=\"auto_policy\"`。\n- `auto_accept` —— 捕获,立即接受。适用于有激进 `beforeunload` 提示的页面。\n\n完整对话框工作流请参阅[浏览器功能页面](./features/browser.md#browser_dialog)。\n\n浏览器工具集支持多个 provider。有关 Browserbase、Browser Use 和本地 Chromium 系 CDP 设置的详细信息,请参阅[浏览器功能页面](/user-guide/features/browser)。\n\n## 时区\n\n使用 IANA 时区字符串覆盖服务器本地时区。影响日志中的时间戳、cron 调度和系统提示词时间注入。\n\n```yaml\ntimezone: \"America/New_York\" # IANA 时区(默认:\"\" = 服务器本地时间)\n```\n\n支持的值:任何 IANA 时区标识符(例如 `America/New_York`、`Europe/London`、`Asia/Kolkata`、`UTC`)。留空或省略以使用服务器本地时间。\n\n## Discord\n\n为消息 gateway 配置 Discord 特定行为:\n\n```yaml\ndiscord:\n require_mention: true # 在服务器频道中需要 @提及才能响应\n free_response_channels: \"\" # 逗号分隔的频道 ID,bot 在这些频道无需 @提及即可响应\n auto_thread: true # 在频道中 @提及时自动创建线程\n```\n\n- `require_mention` —— 为 `true`(默认)时,bot 仅在服务器频道中被 `@BotName` 提及时响应。DM 始终无需提及即可工作。\n- `free_response_channels` —— 逗号分隔的频道 ID 列表,bot 在这些频道对每条消息响应,无需提及。\n- `auto_thread` —— 为 `true`(默认)时,频道中的提及会自动为对话创建线程,保持频道整洁(类似 Slack 线程)。\n\n## 安全\n\n预执行安全扫描和机密脱敏:\n\n```yaml\nsecurity:\n redact_secrets: false # 在工具输出和日志中脱敏 API 密钥模式(默认关闭)\n tirith_enabled: true # 为终端命令启用 Tirith 安全扫描\n tirith_path: \"tirith\" # tirith 二进制文件路径(默认:$PATH 中的 \"tirith\")\n tirith_timeout: 5 # 等待 tirith 扫描的秒数\n tirith_fail_open: true # 如果 tirith 不可用,允许命令执行\n website_blocklist: # 参见下方网站黑名单部分\n enabled: false\n domains: []\n shared_files: []\n```\n\n- `redact_secrets` —— 为 `true` 时,自动检测并脱敏工具输出中看起来像 API 密钥、token 和密码的模式,然后再进入对话上下文和日志。**默认关闭** —— 如果您经常在工具输出中处理真实凭据并希望有安全网,请启用。显式设置为 `true` 以开启。\n- `tirith_enabled` —— 为 `true` 时,终端命令在执行前由 [Tirith](https://github.com/sheeki03/tirith) 扫描以检测潜在危险操作。\n- `tirith_path` —— tirith 二进制文件的路径。如果 tirith 安装在非标准位置,请设置此项。\n- `tirith_timeout` —— 等待 tirith 扫描的最大秒数。如果扫描超时,命令继续执行。\n- `tirith_fail_open` —— 为 `true`(默认)时,如果 tirith 不可用或失败,允许命令执行。设置为 `false` 以在 tirith 无法验证时阻止命令。\n\n## 网站黑名单\n\n阻止 agent 的 web 和浏览器工具访问特定域名:\n\n```yaml\nsecurity:\n website_blocklist:\n enabled: false # 启用 URL 阻止(默认:false)\n domains: # 被阻止的域名模式列表\n - \"*.internal.company.com\"\n - \"admin.example.com\"\n - \"*.local\"\n shared_files: # 从外部文件加载额外规则\n - \"/etc/hermes/blocked-sites.txt\"\n```\n\n启用后,任何匹配被阻止域名模式的 URL 在 web 或浏览器工具执行之前都会被拒绝。这适用于 `web_search`、`web_extract`、`browser_navigate` 以及任何访问 URL 的工具。\n\n域名规则支持:\n- 精确域名:`admin.example.com`\n- 通配符子域名:`*.internal.company.com`(阻止所有子域名)\n- TLD 通配符:`*.local`\n\n共享文件每行包含一条域名规则(空行和 `#` 注释被忽略)。缺失或不可读的文件记录警告,但不禁用其他 web 工具。\n\n策略缓存 30 秒,因此配置更改无需重启即可快速生效。\n\n## 智能审批\n\n控制 Hermes 如何处理潜在危险命令:\n\n```yaml\napprovals:\n mode: manual # manual | smart | off\n```\n\n| 模式 | 行为 |\n|------|----------|\n| `manual`(默认) | 在执行任何被标记的命令之前提示用户。在 CLI 中显示交互式审批对话框。在消息中排队待处理的审批请求。 |\n| `smart` | 使用辅助 LLM 评估被标记的命令是否真正危险。低风险命令以会话级持久性自动批准。真正有风险的命令升级给用户。 |\n| `off` | 跳过所有审批检查。等同于 `HERMES_YOLO_MODE=true`。**谨慎使用。** |\n\n智能模式对于减少审批疲劳特别有用 —— 它让 agent 在安全操作上更自主地工作,同时仍然捕获真正破坏性的命令。\n\n:::warning\n设置 `approvals.mode: off` 会禁用终端命令的所有安全检查。仅在受信任的沙箱环境中使用。\n:::\n\n## 检查点\n\n破坏性文件操作之前的自动文件系统快照。详情请参阅[检查点与回滚](/user-guide/checkpoints-and-rollback)。\n\n```yaml\ncheckpoints:\n enabled: false # 启用自动检查点(也可:hermes chat --checkpoints)。默认:false(选择加入)。\n max_snapshots: 20 # 每个目录保留的最大检查点数(默认:20)\n```\n\n\n## 委托\n\n为委托工具配置子 agent 行为:\n\n```yaml\ndelegation:\n # model: \"google/gemini-3-flash-preview\" # 覆盖模型(空 = 继承父级)\n # provider: \"openrouter\" # 覆盖 provider(空 = 继承父级)\n # base_url: \"http://localhost:1234/v1\" # 直接 OpenAI 兼容端点(优先于 provider)\n # api_key: \"local-key\" # base_url 的 API 密钥(回退到 OPENAI_API_KEY)\n # api_mode: \"\" # base_url 的线路协议:\"chat_completions\"、\"codex_responses\" 或 \"anthropic_messages\"。空 = 从 URL 自动检测(例如 /anthropic 后缀 → anthropic_messages)。对启发式无法检测的非标准端点显式设置。\n max_concurrent_children: 3 # 每批并行子 agent 数(下限 1,无上限)。也可通过 DELEGATION_MAX_CONCURRENT_CHILDREN 环境变量设置。\n max_spawn_depth: 1 # 委托树深度上限(1-3,截断)。1 = 扁平(默认):父级生成无法委托的叶子。2 = 编排器子级可以生成叶子孙级。3 = 三级。\n orchestrator_enabled: true # 全局终止开关。为 false 时,role=\"orchestrator\" 被忽略,每个子级无论 max_spawn_depth 如何都被强制为叶子。\n```\n\n**子 agent provider:model 覆盖:** 默认情况下,子 agent 继承父 agent 的 provider 和模型。设置 `delegation.provider` 和 `delegation.model` 将子 agent 路由到不同的 provider:model 对 —— 例如,在您的主 agent 运行昂贵推理模型时,为范围较窄的子任务使用便宜/快速的模型。\n\n**直接端点覆盖:** 如果您想要明显的自定义端点路径,请设置 `delegation.base_url`、`delegation.api_key` 和 `delegation.model`。这将子 agent 直接发送到该 OpenAI 兼容端点,并优先于 `delegation.provider`。如果省略 `delegation.api_key`,Hermes 仅回退到 `OPENAI_API_KEY`。\n\n**线路协议(`api_mode`):** Hermes 从 `delegation.base_url` 自动检测线路协议(例如以 `/anthropic` 结尾的路径 → `anthropic_messages`;Codex/原生 Anthropic/Kimi-coding 主机名保留其现有检测)。对于启发式无法分类的端点 —— 例如 Azure AI Foundry、MiniMax、Zhipu GLM 或前置 Anthropic 形状后端的 LiteLLM 代理 —— 请将 `delegation.api_mode` 显式设置为 `chat_completions`、`codex_responses` 或 `anthropic_messages` 之一。留空(默认)以保持自动检测。\n\n委托 provider 使用与 CLI/gateway 启动相同的凭据解析。所有配置的 provider 均受支持:`openrouter`、`nous`、`copilot`、`zai`、`kimi-coding`、`minimax`、`minimax-cn`。设置 provider 时,系统自动解析正确的基础 URL、API 密钥和 API 模式 —— 无需手动凭据连接。\n\n**优先级:** 配置中的 `delegation.base_url` → 配置中的 `delegation.provider` → 父 provider(继承)。配置中的 `delegation.model` → 父模型(继承)。仅设置 `model` 而不设置 `provider` 仅更改模型名称,同时保留父级凭据(适用于在同一 provider(如 OpenRouter)内切换模型)。\n\n**宽度和深度:** `max_concurrent_children` 限制每批并行运行的子 agent 数量(默认 `3`,下限 1,无上限)。也可通过 `DELEGATION_MAX_CONCURRENT_CHILDREN` 环境变量设置。当模型提交的 `tasks` 数组超过上限时,`delegate_task` 返回工具错误解释限制,而不是静默截断。`max_spawn_depth` 控制委托树深度(截断到 1-3)。在默认 `1` 时,委托是扁平的:子级无法生成孙级,传递 `role=\"orchestrator\"` 静默降级为 `leaf`。提升到 `2` 使编排器子级可以生成叶子孙级;`3` 用于三级树。Agent 通过 `role=\"orchestrator\"` 按调用选择编排;`orchestrator_enabled: false` 强制每个子级回到叶子,无论如何。成本呈乘法增长 —— 在 `max_spawn_depth: 3` 和 `max_concurrent_children: 3` 时,树可以达到 3×3×3 = 27 个并发叶子 agent。使用模式请参阅[子 Agent 委托 → 深度限制和嵌套编排](features/delegation.md#depth-limit-and-nested-orchestration)。\n\n## 澄清\n\n配置澄清提示行为:\n\n```yaml\nclarify:\n timeout: 120 # 等待用户澄清响应的秒数\n```\n\n## 上下文文件(SOUL.md、AGENTS.md)\n\nHermes 使用两种不同的上下文范围:\n\n| 文件 | 用途 | 范围 |\n|------|---------|-------|\n| `SOUL.md` | **主要 agent 身份** —— 定义 agent 是谁(系统提示词第 #1 槽位) | `~/.hermes/SOUL.md` 或 `$HERMES_HOME/SOUL.md` |\n| `.hermes.md` / `HERMES.md` | 项目特定指令(最高优先级) | 向上走到 git 根目录 |\n| `AGENTS.md` | 项目特定指令、编码规范 | 递归目录遍历 |\n| `CLAUDE.md` | Claude Code 上下文文件(也会检测) | 仅工作目录 |\n| `.cursorrules` | Cursor IDE 规则(也会检测) | 仅工作目录 |\n| `.cursor/rules/*.mdc` | Cursor 规则文件(也会检测) | 仅工作目录 |\n\n- **SOUL.md** 是 agent 的主要身份。它占据系统提示词的第 #1 槽位,完全替换内置的默认身份。编辑它以完全自定义 agent 是谁。\n- 如果 SOUL.md 缺失、为空或无法加载,Hermes 回退到内置默认身份。\n- **项目上下文文件使用优先级系统** —— 仅加载一种类型(第一个匹配优先):`.hermes.md` → `AGENTS.md` → `CLAUDE.md` → `.cursorrules`。SOUL.md 始终独立加载。\n- **AGENTS.md** 是分层的:如果子目录也有 AGENTS.md,所有都会合并。\n- 如果 `SOUL.md` 不存在,Hermes 会自动生成默认的 `SOUL.md`。\n- 所有加载的上下文文件上限为 20,000 字符,并进行智能截断。\n\n另请参阅:\n- [个性与 SOUL.md](/user-guide/features/personality)\n- [上下文文件](/user-guide/features/context-files)\n\n## 工作目录\n\n| 上下文 | 默认值 |\n|---------|---------|\n| **CLI(`hermes`)** | 运行命令的当前目录 |\n| **消息 gateway** | 主目录 `~`(用 `MESSAGING_CWD` 覆盖) |\n| **Docker / Singularity / Modal / SSH** | 容器或远程机器内用户的主目录 |\n\n覆盖工作目录:\n```bash\n# 在 ~/.hermes/.env 或 ~/.hermes/config.yaml 中:\nMESSAGING_CWD=/home/myuser/projects # Gateway 会话\nTERMINAL_CWD=/workspace # 所有终端会话\n```"}, "febc4cfec0a79b175a430304765473c97e10622f": {"t": 1785768010.9488783, "n": 55370, "h": "ddf56386c4f4957d", "c": "---\nsidebar_position: 2\ntitle: \"配置\"\ndescription: \"配置 Hermes Agent — config.yaml、providers、模型、API 密钥等\"\n---\n\n# 配置\n\n所有设置均存储在 `~/.hermes/` 目录中,便于访问。\n\n## 目录结构\n\n```text\n~/.hermes/\n├── config.yaml # 设置(模型、终端、TTS、压缩等)\n├── .env # API 密钥和机密\n├── auth.json # OAuth provider 凭据(Nous Portal 等)\n├── SOUL.md # 主要 agent 身份(系统提示词第 #1 槽位)\n├── memories/ # 持久记忆(MEMORY.md、USER.md)\n├── skills/ # Agent 创建的技能(通过 skill_manage 工具管理)\n├── cron/ # 定时任务\n├── sessions/ # Gateway 会话\n└── logs/ # 日志(errors.log、gateway.log — 机密自动脱敏)\n```\n\n## 管理配置\n\n```bash\nhermes config # 查看当前配置\nhermes config edit # 在编辑器中打开 config.yaml\nhermes config set KEY VAL # 设置特定值\nhermes config check # 检查缺失选项(更新后使用)\nhermes config migrate # 交互式添加缺失选项\n\n# 示例:\nhermes config set model anthropic/claude-opus-4\nhermes config set terminal.backend docker\nhermes config set OPENROUTER_API_KEY sk-or-... # 保存到 .env\n```\n\n:::tip\n`hermes config set` 命令会自动将值路由到正确的文件 —— API 密钥保存到 `.env`,其他所有内容保存到 `config.yaml`。\n:::\n\n## 配置优先级\n\n设置按以下顺序解析(优先级从高到低):\n\n1. **CLI 参数** —— 例如 `hermes chat --model anthropic/claude-sonnet-4`(单次调用覆盖)\n2. **`~/.hermes/config.yaml`** —— 所有非机密设置的主配置文件\n3. **`~/.hermes/.env`** —— 环境变量的回退;机密(API 密钥、token、密码)**必须**放这里\n4. **内置默认值** —— 未设置任何内容时的硬编码安全默认值\n\n:::info 经验法则\n机密(API 密钥、bot token、密码)放入 `.env`。其他所有内容(模型、终端后端、压缩设置、内存限制、工具集)放入 `config.yaml`。当两者都设置时,`config.yaml` 对非机密设置优先。\n:::\n\n## 环境变量替换\n\n可以在 `config.yaml` 中使用 `${VAR_NAME}` 语法引用环境变量:\n\n```yaml\nauxiliary:\n vision:\n api_key: ${GOOGLE_API_KEY}\n base_url: ${CUSTOM_VISION_URL}\n\ndelegation:\n api_key: ${DELEGATION_KEY}\n```\n\n单个值中可以有多个引用:`url: \"${HOST}:${PORT}\"`。如果引用的变量未设置,占位符将保持原样(`${UNDEFINED_VAR}` 保持不变)。仅支持 `${VAR}` 语法 —— 裸 `$VAR` 不会被展开。\n\n关于 AI provider 设置(OpenRouter、Anthropic、Copilot、自定义端点、自托管 LLM、回退模型等),请参阅 [AI Providers](/integrations/providers)。\n\n### Provider 超时\n\n可以为 provider 设置 `providers.<id>.request_timeout_seconds` 作为全局请求超时,以及 `providers.<id>.models.<model>.timeout_seconds` 作为特定模型的覆盖值。适用于每种传输方式(OpenAI-wire、原生 Anthropic、Anthropic 兼容)上的主轮次客户端、回退链、凭据轮换后的重建,以及(对于 OpenAI-wire)每请求超时 kwarg —— 因此配置值优先于旧版 `HERMES_API_TIMEOUT` 环境变量。\n\n还可以设置 `providers.<id>.stale_timeout_seconds` 用于非流式陈旧调用检测器,以及 `providers.<id>.models.<model>.stale_timeout_seconds` 作为特定模型的覆盖值。此值优先于旧版 `HERMES_API_CALL_STALE_TIMEOUT` 环境变量。\n\n不设置这些值将保持旧版默认值(`HERMES_API_TIMEOUT=1800`s、`HERMES_API_CALL_STALE_TIMEOUT=300`s、原生 Anthropic 900s)。目前不适用于 AWS Bedrock(`bedrock_converse` 和 AnthropicBedrock SDK 路径均使用 boto3 及其自身的超时配置)。请参阅 [`cli-config.yaml.example`](https://github.com/NousResearch/hermes-agent/blob/main/cli-config.yaml.example) 中的注释示例。\n\n## 终端后端配置\n\nHermes 支持六种终端后端。每种后端决定 agent 的 shell 命令实际在哪里执行 —— 本地机器、Docker 容器、通过 SSH 的远程服务器、Modal 云沙箱(直接或通过 Nous 托管的 gateway)、Daytona 工作区,或 Singularity/Apptainer 容器。\n\n```yaml\nterminal:\n backend: local # local | docker | ssh | modal | daytona | singularity\n cwd: \".\" # Gateway/cron 工作目录(CLI 始终使用启动目录)\n timeout: 180 # 每条命令的超时时间(秒)\n env_passthrough: [] # 转发到沙箱执行的环境变量名(terminal + execute_code)\n singularity_image: \"docker://nikolaik/python-nodejs:python3.11-nodejs20\" # Singularity 后端的容器镜像\n modal_image: \"nikolaik/python-nodejs:python3.11-nodejs20\" # Modal 后端的容器镜像\n daytona_image: \"nikolaik/python-nodejs:python3.11-nodejs20\" # Daytona 后端的容器镜像\n```\n\n对于 Modal 和 Daytona 等云沙箱,`container_persistent: true` 表示 Hermes 将尝试在沙箱重建后保留文件系统状态。这并不保证相同的活跃沙箱、PID 空间或后台进程之后仍在运行。\n\n### 后端概览\n\n| 后端 | 命令运行位置 | 隔离性 | 最适合 |\n|---------|-------------------|-----------|----------|\n| **local** | 直接在您的机器上 | 无 | 开发、个人使用 |\n| **docker** | 单个持久 Docker 容器(跨会话、`/new`、子 agent 共享) | 完全(命名空间、cap-drop) | 安全沙箱、CI/CD |\n| **ssh** | 通过 SSH 的远程服务器 | 网络边界 | 远程开发、强大硬件 |\n| **modal** | Modal 云沙箱 | 完全(云 VM) | 临时云计算、评估 |\n| **daytona** | Daytona 工作区 | 完全(云容器) | 托管云开发环境 |\n| **singularity** | Singularity/Apptainer 容器 | 命名空间(--containall) | HPC 集群、共享机器 |\n\n### Local 后端\n\n默认后端。命令直接在您的机器上运行,无隔离。无需特殊设置。\n\n```yaml\nterminal:\n backend: local\n```\n\n:::warning\nAgent 拥有与您的用户账户相同的文件系统访问权限。使用 `hermes tools` 禁用不需要的工具,或切换到 Docker 进行沙箱隔离。\n:::\n\n### Docker 后端\n\n在具有安全加固的 Docker 容器内运行命令(所有权限已删除、无权限提升、PID 限制)。\n\n**单个持久容器,而非每条命令一个容器。** Hermes 在首次使用时启动一个长期运行的容器,并通过 `docker exec` 将每个终端、文件和 `execute_code` 调用路由到同一容器中 —— 跨会话、`/new`、`/reset` 和 `delegate_task` 子 agent,贯穿 Hermes 进程的整个生命周期。工作目录更改、已安装的包以及 `/workspace` 中的文件会从一次工具调用延续到下一次,就像本地 shell 一样。容器在关闭时停止并删除。详情请参阅下方的**容器生命周期**。\n\n```yaml\nterminal:\n backend: docker\n docker_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n docker_mount_cwd_to_workspace: false # 将启动目录挂载到 /workspace\n docker_run_as_host_user: false # 参见下方\"以宿主用户身份运行容器\"\n docker_forward_env: # 转发到容器的环境变量\n - \"GITHUB_TOKEN\"\n docker_volumes: # 宿主目录挂载\n - \"/home/user/projects:/workspace/projects\"\n - \"/home/user/data:/data:ro\" # :ro 表示只读\n docker_extra_args: # 附加到 `docker run` 的额外标志\n - \"--gpus=all\"\n - \"--network=host\"\n\n # 资源限制\n container_cpu: 1 # CPU 核心数(0 = 不限制)\n container_memory: 5120 # MB(0 = 不限制)\n container_disk: 51200 # MB(需要 XFS+pquota 上的 overlay2)\n container_persistent: true # 跨会话持久化 /workspace 和 /root\n```\n\n**`terminal.docker_extra_args`**(也可通过 `TERMINAL_DOCKER_EXTRA_ARGS='[\"--gpus=all\"]'` 覆盖)允许传递 Hermes 未作为一级键公开的任意 `docker run` 标志 —— `--gpus`、`--network`、`--add-host`、替代 `--security-opt` 覆盖等。每个条目必须是字符串;该列表最后附加到组装好的 `docker run` 调用中,因此可以在需要时覆盖 Hermes 的默认值。请谨慎使用 —— 与沙箱加固(权限删除、`--user`、workspace 绑定挂载)冲突的标志将悄然削弱隔离性。\n\n**要求:** 已安装并运行 Docker Desktop 或 Docker Engine。Hermes 会探测 `$PATH` 以及常见的 macOS 安装位置(`/usr/local/bin/docker`、`/opt/homebrew/bin/docker`、Docker Desktop 应用包)。开箱即用支持 Podman:设置 `HERMES_DOCKER_BINARY=podman`(或完整路径)以在两者都安装时强制使用它。\n\n**容器生命周期:** Hermes 为每个终端和文件工具调用重用单个长期运行的容器(`docker run -d ... sleep 2h`),跨会话、`/new`、`/reset` 和 `delegate_task` 子 agent,贯穿 Hermes 进程的整个生命周期。命令通过带登录 shell 的 `docker exec` 运行,因此工作目录更改、已安装的包以及 `/workspace` 中的文件都会从一次工具调用延续到下一次。容器在 Hermes 关闭时(或空闲清理回收时)停止并删除。\n\n通过 `delegate_task(tasks=[...])` 生成的并行子 agent 共享这一个容器 —— 并发的 `cd`、环境变量修改以及对同一路径的写入会发生冲突。如果子 agent 需要隔离的沙箱,必须通过 `register_task_env_overrides()` 注册每任务镜像覆盖,RL 和基准测试环境(TerminalBench2、HermesSweEnv 等)会自动为其每任务 Docker 镜像执行此操作。\n\n**安全加固:**\n- `--cap-drop ALL`,仅添加回 `DAC_OVERRIDE`、`CHOWN`、`FOWNER`\n- `--security-opt no-new-privileges`\n- `--pids-limit 256`\n- `/tmp`(512MB)、`/var/tmp`(256MB)、`/run`(64MB)的大小限制 tmpfs\n\n**凭据转发:** `docker_forward_env` 中列出的环境变量首先从您的 shell 环境解析,然后回退到 `~/.hermes/.env`。技能也可以声明 `required_environment_variables`,这些变量会自动合并。\n\n### SSH 后端\n\n通过 SSH 在远程服务器上运行命令。使用 ControlMaster 进行连接复用(5 分钟空闲保活)。默认启用持久 shell —— 状态(cwd、环境变量)在命令之间保持。\n\n```yaml\nterminal:\n backend: ssh\n persistent_shell: true # 保持长期运行的 bash 会话(默认:true)\n```\n\n**必需的环境变量:**\n\n```bash\nTERMINAL_SSH_HOST=my-server.example.com\nTERMINAL_SSH_USER=ubuntu\n```\n\n**可选:**\n\n| 变量 | 默认值 | 描述 |\n|----------|---------|-------------|\n| `TERMINAL_SSH_PORT` | `22` | SSH 端口 |\n| `TERMINAL_SSH_KEY` | (系统默认) | SSH 私钥路径 |\n| `TERMINAL_SSH_PERSISTENT` | `true` | 启用持久 shell |\n\n**工作原理:** 使用 `BatchMode=yes` 和 `StrictHostKeyChecking=accept-new` 在初始化时连接。持久 shell 在远程主机上保持单个 `bash -l` 进程存活,通过临时文件进行通信。需要 `stdin_data` 或 `sudo` 的命令会自动回退到单次模式。\n\n### Modal 后端\n\n在 [Modal](https://modal.com) 云沙箱中运行命令。每个任务获得一个具有可配置 CPU、内存和磁盘的隔离 VM。文件系统可以跨会话快照/恢复。\n\n```yaml\nterminal:\n backend: modal\n container_cpu: 1 # CPU 核心数\n container_memory: 5120 # MB(5GB)\n container_disk: 51200 # MB(50GB)\n container_persistent: true # 快照/恢复文件系统\n```\n\n**必需:** `MODAL_TOKEN_ID` + `MODAL_TOKEN_SECRET` 环境变量,或 `~/.modal.toml` 配置文件。\n\n**持久化:** 启用后,沙箱文件系统在清理时快照,并在下次会话时恢复。快照在 `~/.hermes/modal_snapshots.json` 中跟踪。这保留文件系统状态,而非活跃进程、PID 空间或后台任务。\n\n**凭据文件:** 自动从 `~/.hermes/` 挂载(OAuth token 等),并在每条命令前同步。\n\n### Daytona 后端\n\n在 [Daytona](https://daytona.io) 托管工作区中运行命令。支持停止/恢复以实现持久化。\n\n```yaml\nterminal:\n backend: daytona\n container_cpu: 1 # CPU 核心数\n container_memory: 5120 # MB → 转换为 GiB\n container_disk: 10240 # MB → 转换为 GiB(最大 10 GiB)\n container_persistent: true # 停止/恢复而非删除\n```\n\n**必需:** `DAYTONA_API_KEY` 环境变量。\n\n**持久化:** 启用后,沙箱在清理时停止(而非删除),并在下次会话时恢复。沙箱名称遵循 `hermes-{task_id}` 模式。\n\n**磁盘限制:** Daytona 强制执行 10 GiB 最大值。超过此值的请求将被截断并发出警告。\n\n### Singularity/Apptainer 后端\n\n在 [Singularity/Apptainer](https://apptainer.org) 容器中运行命令。专为 Docker 不可用的 HPC 集群和共享机器设计。\n\n```yaml\nterminal:\n backend: singularity\n singularity_image: \"docker://nikolaik/python-nodejs:python3.11-nodejs20\"\n container_cpu: 1 # CPU 核心数\n container_memory: 5120 # MB\n container_persistent: true # 可写覆盖层跨会话持久化\n```\n\n**要求:** `$PATH` 中有 `apptainer` 或 `singularity` 二进制文件。\n\n**镜像处理:** Docker URL(`docker://...`)自动转换为 SIF 文件并缓存。现有 `.sif` 文件直接使用。\n\n**临时目录:** 按顺序解析:`TERMINAL_SCRATCH_DIR` → `TERMINAL_SANDBOX_DIR/singularity` → `/scratch/$USER/hermes-agent`(HPC 惯例)→ `~/.hermes/sandboxes/singularity`。\n\n**隔离:** 使用 `--containall --no-home` 实现完全命名空间隔离,不挂载宿主 home 目录。\n\n### 常见终端后端问题\n\n如果终端命令立即失败或终端工具报告为已禁用:\n\n- **Local** —— 无特殊要求。入门时最安全的默认选项。\n- **Docker** —— 运行 `docker version` 验证 Docker 是否正常工作。如果失败,修复 Docker 或执行 `hermes config set terminal.backend local`。\n- **SSH** —— `TERMINAL_SSH_HOST` 和 `TERMINAL_SSH_USER` 都必须设置。如果缺少任一项,Hermes 会记录清晰的错误。\n- **Modal** —— 需要 `MODAL_TOKEN_ID` 环境变量或 `~/.modal.toml`。运行 `hermes doctor` 检查。\n- **Daytona** —— 需要 `DAYTONA_API_KEY`。Daytona SDK 处理服务器 URL 配置。\n- **Singularity** —— 需要 `$PATH` 中有 `apptainer` 或 `singularity`。HPC 集群上常见。\n\n如有疑问,将 `terminal.backend` 设回 `local` 并首先验证命令在那里运行。\n\n### 拆卸时远程到宿主文件同步\n\n对于 **SSH**、**Modal** 和 **Daytona** 后端(agent 的工作树位于与运行 Hermes 的宿主不同的机器上),Hermes 跟踪 agent 在远程沙箱中触及的文件,并在会话拆卸/沙箱清理时,将修改的文件**同步回宿主**,存放在 `~/.hermes/cache/remote-syncs/<session-id>/` 下。\n\n- 触发时机:会话关闭、`/new`、`/reset`、gateway 消息超时、子 agent 使用远程后端时 `delegate_task` 子 agent 完成。\n- 覆盖 agent 修改的整个树,而不仅仅是它明确打开的文件。添加、编辑和删除都会被捕获。\n- 远程沙箱可能在您查找时已被拆除;本地 `~/.hermes/cache/remote-syncs/…` 副本是 agent 更改内容的权威记录。\n- 大型二进制输出(模型检查点、原始数据集)按大小限制 —— 同步跳过超过 `file_sync_max_mb`(默认 `100`)的文件。如果您期望更大的工件返回,请调高该值。\n\n```yaml\nterminal:\n file_sync_max_mb: 100 # 默认 —— 同步最大 100 MB 的文件\n file_sync_enabled: true # 默认 —— 设为 false 可完全跳过同步\n```\n\n这是从会话结束后被销毁的临时云沙箱中恢复结果的方式,无需告诉 agent 显式地 `scp` 或 `modal volume put` 每个工件。\n\n### Docker 卷挂载\n\n使用 Docker 后端时,`docker_volumes` 允许您与容器共享宿主目录。每个条目使用标准 Docker `-v` 语法:`host_path:container_path[:options]`。\n\n```yaml\nterminal:\n backend: docker\n docker_volumes:\n - \"/home/user/projects:/workspace/projects\" # 读写(默认)\n - \"/home/user/datasets:/data:ro\" # 只读\n - \"/home/user/.hermes/cache/documents:/output\" # Gateway 可见的导出\n```\n\n适用于:\n- **向 agent 提供文件**(数据集、配置、参考代码)\n- **从 agent 接收文件**(生成的代码、报告、导出)\n- **共享工作区**,您和 agent 都访问相同的文件\n\n如果您使用消息 gateway 并希望 agent 通过 `MEDIA:/...` 发送生成的文件,建议使用专用的宿主可见导出挂载,例如 `/home/user/.hermes/cache/documents:/output`。\n\n- 在 Docker 中将文件写入 `/output/...`\n- 在 `MEDIA:` 中发出**宿主路径**,例如:`MEDIA:/home/user/.hermes/cache/documents/report.txt`\n- **不要**发出 `/workspace/...` 或 `/output/...`,除非该确切路径在宿主上对 gateway 进程也存在\n\n:::warning\nYAML 重复键会静默覆盖之前的键。如果您已有 `docker_volumes:` 块,请将新挂载合并到同一列表中,而不是在文件后面再添加一个 `docker_volumes:` 键。\n:::\n\n也可以通过环境变量设置:`TERMINAL_DOCKER_VOLUMES='[\"/host:/container\"]'`(JSON 数组)。\n\n### Docker 凭据转发\n\n默认情况下,Docker 终端会话不继承任意宿主凭据。如果您需要在容器内使用特定 token,请将其添加到 `terminal.docker_forward_env`。\n\n```yaml\nterminal:\n backend: docker\n docker_forward_env:\n - \"GITHUB_TOKEN\"\n - \"NPM_TOKEN\"\n```\n\nHermes 首先从您当前的 shell 解析每个列出的变量,然后回退到通过 `hermes config set` 保存的 `~/.hermes/.env`。\n\n:::warning\n`docker_forward_env` 中列出的任何内容都会对容器内运行的命令可见。只转发您愿意暴露给终端会话的凭据。\n:::\n\n### 以宿主用户身份运行容器\n\n默认情况下,Docker 容器以 `root`(UID 0)身份运行。在 `/workspace` 或其他绑定挂载中创建的文件在宿主上归 root 所有,因此会话结束后您必须 `sudo chown` 才能从宿主编辑器编辑它们。`terminal.docker_run_as_host_user` 标志解决了这个问题:\n\n```yaml\nterminal:\n backend: docker\n docker_run_as_host_user: true # 默认:false\n```\n\n启用后,Hermes 将 `--user $(id -u):$(id -g)` 附加到 `docker run` 命令,使写入绑定挂载目录(`/workspace`、`/root`、`docker_volumes` 中的任何内容)的文件归您的宿主用户所有,而非 root。权衡:容器将无法再 `apt install` 或写入 `/root/.npm` 等 root 拥有的路径 —— 如果您同时需要这两者,请使用 `HOME` 归非 root 用户所有的基础镜像(或在镜像构建时添加所需工具)。\n\n保持 `false`(默认)以获得向后兼容的行为。当您的工作流主要是\"编辑挂载的宿主文件\"且厌倦了 `sudo chown -R` 时,请开启此选项。\n\n### 可选:将启动目录挂载到 `/workspace`\n\nDocker 沙箱默认保持隔离。Hermes **不会**将您当前的宿主工作目录传入容器,除非您明确选择加入。\n\n在 `config.yaml` 中启用:\n\n```yaml\nterminal:\n backend: docker\n docker_mount_cwd_to_workspace: true\n```\n\n启用后:\n- 如果您从 `~/projects/my-app` 启动 Hermes,该宿主目录将绑定挂载到 `/workspace`\n- Docker 后端从 `/workspace` 开始\n- 文件工具和终端命令都能看到相同的挂载项目\n\n禁用时,`/workspace` 保持沙箱所有,除非您通过 `docker_volumes` 显式挂载内容。\n\n安全权衡:\n- `false` 保留沙箱边界\n- `true` 使沙箱直接访问您启动 Hermes 的目录\n\n仅在您有意希望容器处理实时宿主文件时才选择加入。\n\n### 持久 Shell\n\n默认情况下,每条终端命令在其自己的子进程中运行 —— 工作目录、环境变量和 shell 变量在命令之间重置。启用**持久 shell** 后,单个长期运行的 bash 进程在 `execute()` 调用之间保持存活,使状态在命令之间保持。\n\n这对 **SSH 后端**最有用,它还消除了每条命令的连接开销。持久 shell **对 SSH 默认启用**,对本地后端禁用。\n\n```yaml\nterminal:\n persistent_shell: true # 默认 —— 为 SSH 启用持久 shell\n```\n\n禁用:\n\n```bash\nhermes config set terminal.persistent_shell false\n```\n\n**跨命令保持的内容:**\n- 工作目录(`cd /tmp` 对下一条命令生效)\n- 导出的环境变量(`export FOO=bar`)\n- Shell 变量(`MY_VAR=hello`)\n\n**优先级:**\n\n| 级别 | 变量 | 默认值 |\n|-------|----------|---------|\n| 配置 | `terminal.persistent_shell` | `true` |\n| SSH 覆盖 | `TERMINAL_SSH_PERSISTENT` | 遵循配置 |\n| Local 覆盖 | `TERMINAL_LOCAL_PERSISTENT` | `false` |\n\n每个后端的环境变量具有最高优先级。如果您也想在本地后端使用持久 shell:\n\n```bash\nexport TERMINAL_LOCAL_PERSISTENT=true\n```\n\n:::note\n需要 `stdin_data` 或 sudo 的命令会自动回退到单次模式,因为持久 shell 的 stdin 已被 IPC 协议占用。\n:::\n\n有关每个后端的详细信息,请参阅[代码执行](features/code-execution.md)和 [README 的终端部分](features/tools.md)。\n\n## 技能设置\n\n技能可以通过其 SKILL.md frontmatter 声明自己的配置设置。这些是非机密值(路径、偏好、域设置),存储在 `config.yaml` 的 `skills.config` 命名空间下。\n\n```yaml\nskills:\n config:\n myplugin:\n path: ~/myplugin-data # 示例 —— 每个技能定义自己的键\n```\n\n**技能设置的工作原理:**\n\n- `hermes config migrate` 扫描所有已启用的技能,找到未配置的设置,并提供提示\n- `hermes config show` 在\"技能设置\"下显示所有技能设置及其所属技能\n- 技能加载时,其解析的配置值会自动注入到技能上下文中\n\n**手动设置值:**\n\n```bash\nhermes config set skills.config.myplugin.path ~/myplugin-data\n```\n\n有关在您自己的技能中声明配置设置的详细信息,请参阅[创建技能 — 配置设置](/developer-guide/creating-skills#config-settings-configyaml)。\n\n### Agent 创建技能写入的守卫\n\n当 agent 使用 `skill_manage` 创建、编辑、修补或删除技能时,Hermes 可以选择扫描新/更新的内容以查找危险关键字模式(凭据收集、明显的 prompt 注入、数据外泄指令)。扫描器**默认关闭** —— 合法触及 `~/.ssh/` 或提及 `$OPENAI_API_KEY` 的真实 agent 工作流触发启发式规则过于频繁。如果您希望扫描器在 agent 的技能写入落地前提示您,请重新开启:\n\n```yaml\nskills:\n guard_agent_created: true # 默认:false\n```\n\n开启后,任何被标记的 `skill_manage` 写入都会以审批提示的形式出现,并附带扫描器的理由。接受的写入落地;拒绝的写入向 agent 返回解释性错误。\n\n## 内存配置\n\n```yaml\nmemory:\n memory_enabled: true\n user_profile_enabled: true\n memory_char_limit: 2200 # ~800 tokens\n user_char_limit: 1375 # ~500 tokens\n```\n\n## 文件读取安全\n\n控制单次 `read_file` 调用可以返回多少内容。超过限制的读取将被拒绝,并向 agent 返回错误,提示使用 `offset` 和 `limit` 读取较小范围。这可以防止单次读取压缩的 JS 包或大型数据文件时淹没上下文窗口。\n\n```yaml\nfile_read_max_chars: 100000 # 默认 —— ~25-35K tokens\n```\n\n如果您使用具有大上下文窗口的模型并经常读取大文件,请调高此值。对于小上下文模型,请降低以保持读取高效:\n\n```yaml\n# 大上下文模型(200K+)\nfile_read_max_chars: 200000\n\n# 小型本地模型(16K 上下文)\nfile_read_max_chars: 30000\n```\n\nAgent 还会自动去重文件读取 —— 如果同一文件区域被读取两次且文件未更改,则返回轻量级存根而不是重新发送内容。这在上下文压缩后重置,以便 agent 在内容被摘要后可以重新读取文件。\n\n## 工具输出截断限制\n\n三个相关的上限控制工具在 Hermes 截断之前可以返回多少原始输出:\n\n```yaml\ntool_output:\n max_bytes: 50000 # 终端输出上限(字符)\n max_lines: 2000 # read_file 分页上限\n max_line_length: 2000 # read_file 行号视图中的每行上限\n```\n\n- **`max_bytes`** —— 当 `terminal` 命令产生超过此字符数的合并 stdout/stderr 时,Hermes 保留前 40% 和后 60%,并在中间插入 `[OUTPUT TRUNCATED]` 通知。默认 `50000`(典型分词器约 12-15K tokens)。\n- **`max_lines`** —— 单次 `read_file` 调用的 `limit` 参数上限。超过此值的请求将被截断,以防单次读取淹没上下文窗口。默认 `2000`。\n- **`max_line_length`** —— `read_file` 发出行号视图时应用的每行上限。超过此长度的行将被截断为此字符数,后跟 `... [truncated]`。默认 `2000`。\n\n对于具有大上下文窗口且每次调用可以承受更多原始输出的模型,请调高限制。对于小上下文模型,请降低以保持工具结果紧凑:\n\n```yaml\n# 大上下文模型(200K+)\ntool_output:\n max_bytes: 150000\n max_lines: 5000\n\n# 小型本地模型(16K 上下文)\ntool_output:\n max_bytes: 20000\n max_lines: 500\n```\n\n## 全局工具集禁用\n\n要在 CLI 和每个 gateway 平台上统一禁用特定工具集,请在 `agent.disabled_toolsets` 下列出其名称:\n\n```yaml\nagent:\n disabled_toolsets:\n - memory # 隐藏内存工具 + MEMORY_GUIDANCE 注入\n - web # 任何地方都不使用 web_search / web_extract\n```\n\n这在每个平台的工具配置(由 `hermes tools` 写入的 `platform_toolsets`)**之后**应用,因此此处列出的工具集始终被删除 —— 即使平台的已保存配置仍然列出它。当您希望有一个\"到处关闭 X\"的单一开关而不是编辑 `hermes tools` UI 中的 15+ 个平台行时,请使用此选项。\n\n留空列表或省略键不会产生任何效果。\n\n## Git Worktree 隔离\n\n启用隔离的 git worktree,以便在同一仓库上并行运行多个 agent:\n\n```yaml\nworktree: true # 始终创建 worktree(与 hermes -w 相同)\n# worktree: false # 默认 —— 仅在传递 -w 标志时\n```\n\n启用后,每个 CLI 会话在 `.worktrees/` 下创建一个带有自己分支的新 worktree。Agent 可以编辑文件、提交、推送和创建 PR,而不会相互干扰。干净的 worktree 在退出时删除;脏的 worktree 保留以供手动恢复。\n\n您还可以通过仓库根目录中的 `.worktreeinclude` 列出要复制到 worktree 的 gitignore 文件:\n\n```\n# .worktreeinclude\n.env\n.venv/\nnode_modules/\n```\n\n## 上下文压缩\n\nHermes 自动压缩长对话以保持在模型的上下文窗口内。压缩摘要器是一个单独的 LLM 调用 —— 您可以将其指向任何 provider 或端点。\n\n所有压缩设置都在 `config.yaml` 中(无环境变量)。\n\n### 完整参考\n\n```yaml\ncompression:\n enabled: true # 开启/关闭压缩\n threshold: 0.50 # 在上下文限制的此百分比时压缩\n target_ratio: 0.20 # 保留为最近尾部的阈值分数\n protect_last_n: 20 # 保持未压缩的最少最近消息数\n hygiene_hard_message_limit: 400 # Gateway 安全阀 —— 见下文\n\n# 摘要模型/provider 在 auxiliary: 下配置:\nauxiliary:\n compression:\n model: \"\" # 空 = 使用主聊天模型。覆盖为例如 \"google/gemini-3-flash-preview\" 以获得更便宜/更快的压缩。\n provider: \"auto\" # Provider:\"auto\"、\"openrouter\"、\"nous\"、\"codex\"、\"main\" 等\n base_url: null # 自定义 OpenAI 兼容端点(覆盖 provider)\n```\n\n:::info 旧版配置迁移\n带有 `compression.summary_model`、`compression.summary_provider` 和 `compression.summary_base_url` 的旧版配置在首次加载时自动迁移到 `auxiliary.compression.*`(配置版本 17)。无需手动操作。\n:::\n\n`hygiene_hard_message_limit` 是仅限 gateway 的**预压缩安全阀**。拥有数千条消息的失控会话可能在正常的上下文百分比阈值触发之前就达到模型上下文限制;当消息数超过此上限时,Hermes 强制压缩,无论 token 使用情况如何。默认 `400` —— 对于非常长的会话正常的平台,请调高;要强制更积极的压缩,请降低。在运行中的 gateway 上编辑此值将在下一条消息时生效(见下文)。\n\n:::tip Gateway 热重载压缩和上下文长度\n从最近的版本开始,在运行中的 gateway 上编辑 `config.yaml` 中的 `model.context_length` 或任何 `compression.*` 键将在下一条消息时生效 —— 无需 gateway 重启、`/reset` 或会话轮换。缓存的 agent 签名包含这些键,因此 gateway 在检测到更改时会透明地重建 agent。API 密钥和工具/技能配置仍需要通常的重载路径。\n:::\n\n### 常见设置\n\n**默认(自动检测)—— 无需配置:**\n```yaml\ncompression:\n enabled: true\n threshold: 0.50\n```\n使用您的主 provider 和主模型。如果您希望在比主聊天模型更便宜的模型上进行压缩,请覆盖每任务(例如 `auxiliary.compression.provider: openrouter` + `model: google/gemini-2.5-flash`)。\n\n**强制特定 provider**(基于 OAuth 或 API 密钥):\n```yaml\nauxiliary:\n compression:\n provider: nous\n model: gemini-3-flash\n```\n适用于任何 provider:`nous`、`openrouter`、`codex`、`anthropic`、`main` 等。\n\n**自定义端点**(自托管、Ollama、zai、DeepSeek 等):\n```yaml\nauxiliary:\n compression:\n model: glm-4.7\n base_url: https://api.z.ai/api/coding/paas/v4\n```\n指向自定义 OpenAI 兼容端点。使用 `OPENAI_API_KEY` 进行认证。\n\n### 三个旋钮的交互方式\n\n| `auxiliary.compression.provider` | `auxiliary.compression.base_url` | 结果 |\n|---------------------|---------------------|--------|\n| `auto`(默认) | 未设置 | 自动检测最佳可用 provider |\n| `nous` / `openrouter` / 等 | 未设置 | 强制使用该 provider,使用其认证 |\n| 任意 | 已设置 | 直接使用自定义端点(忽略 provider) |\n\n:::warning 摘要模型上下文长度要求\n摘要模型**必须**具有至少与您的主 agent 模型一样大的上下文窗口。压缩器将对话的完整中间部分发送给摘要模型 —— 如果该模型的上下文窗口小于主模型的,摘要调用将因上下文长度错误而失败。发生这种情况时,中间轮次将**在没有摘要的情况下被丢弃**,静默丢失对话上下文。如果您覆盖模型,请验证其上下文长度满足或超过您的主模型。\n:::\n\n## 上下文引擎\n\n上下文引擎控制在接近模型 token 限制时如何管理对话。内置的 `compressor` 引擎使用有损摘要(参见[上下文压缩](/developer-guide/context-compression-and-caching))。插件引擎可以用替代策略替换它。\n\n```yaml\ncontext:\n engine: \"compressor\" # 默认 —— 内置有损摘要\n```\n\n使用插件引擎(例如,用于无损上下文管理的 LCM):\n\n```yaml\ncontext:\n engine: \"lcm\" # 必须与插件名称匹配\n```\n\n插件引擎**永远不会自动激活** —— 您必须将 `context.engine` 显式设置为插件名称。可用引擎可以通过 `hermes plugins` → Provider Plugins → Context Engine 浏览和选择。\n\n有关内存插件的类似单选系统,请参阅[内存 Providers](/user-guide/features/memory-providers)。\n\n## 迭代预算压力\n\n当 agent 在处理具有许多工具调用的复杂任务时,它可能会在没有意识到预算不足的情况下耗尽其迭代预算(默认:90 轮)。预算压力会在模型接近限制时自动发出警告:\n\n| 阈值 | 级别 | 模型看到的内容 |\n|-----------|-------|---------------------|\n| **70%** | 注意 | `[BUDGET: 63/90. 27 iterations left. Start consolidating.]` |\n| **90%** | 警告 | `[BUDGET WARNING: 81/90. Only 9 left. Respond NOW.]` |\n\n警告注入到最后一个工具结果的 JSON 中(作为 `_budget_warning` 字段),而不是作为单独的消息 —— 这保留了 prompt 缓存,不会破坏对话结构。\n\n```yaml\nagent:\n max_turns: 90 # 每次对话轮次的最大迭代次数(默认:90)\n api_max_retries: 3 # 回退启动前每个 provider 的重试次数(默认:3)\n```\n\n预算压力默认启用。Agent 自然地将警告视为工具结果的一部分,鼓励它在耗尽迭代之前整合工作并提供响应。\n\n当迭代预算完全耗尽时,CLI 向用户显示通知:`⚠ Iteration budget reached (90/90) — response may be incomplete`。如果预算在活跃工作期间耗尽,agent 会在停止前生成已完成内容的摘要。\n\n`agent.api_max_retries` 控制 Hermes 在回退 provider 切换启动**之前**对瞬时错误(速率限制、连接断开、5xx)重试 provider API 调用的次数。默认为 `3` —— 总共四次尝试。如果您配置了[回退 providers](/user-guide/features/fallback-providers) 并希望更快地故障转移,请将其降至 `0`,这样主 provider 上的第一个瞬时错误会立即切换到回退,而不是对不稳定的端点进行重试。\n\n### API 超时\n\nHermes 对流式传输有单独的超时层,以及用于非流式调用的陈旧检测器。陈旧检测器仅在您将其保留为隐式默认值时才会自动调整本地 provider。\n\n| 超时 | 默认值 | 本地 providers | 配置/环境变量 |\n|---------|---------|----------------|--------------|\n| Socket 读取超时 | 120s | 自动提升至 1800s | `HERMES_STREAM_READ_TIMEOUT` |\n| 陈旧流检测 | 180s | 自动禁用 | `HERMES_STREAM_STALE_TIMEOUT` |\n| 陈旧非流检测 | 300s | 保持隐式时自动禁用 | `providers.<id>.stale_timeout_seconds` 或 `HERMES_API_CALL_STALE_TIMEOUT` |\n| API 调用(非流式) | 1800s | 不变 | `providers.<id>.request_timeout_seconds` / `timeout_seconds` 或 `HERMES_API_TIMEOUT` |\n\n**Socket 读取超时**控制 httpx 等待 provider 下一个数据块的时间。本地 LLM 在大上下文上预填充可能需要几分钟才能产生第一个 token,因此当 Hermes 检测到本地端点时,会将此值提升至 30 分钟。如果您显式设置 `HERMES_STREAM_READ_TIMEOUT`,无论端点检测如何,始终使用该值。\n\n**陈旧流检测**终止接收 SSE 保活 ping 但没有实际内容的连接。对于本地 providers,这完全禁用,因为它们在预填充期间不发送保活 ping。\n\n**陈旧非流检测**终止长时间没有响应的非流式调用。默认情况下,Hermes 在本地端点上禁用此功能,以避免长时间预填充期间的误报。如果您显式设置 `providers.<id>.stale_timeout_seconds`、`providers.<id>.models.<model>.stale_timeout_seconds` 或 `HERMES_API_CALL_STALE_TIMEOUT`,即使在本地端点上也会遵守该显式值。\n\n## 上下文压力警告\n\n与迭代预算压力分开,上下文压力跟踪对话距**压缩阈值**有多近 —— 即上下文压缩触发以摘要旧消息的点。这有助于您和 agent 了解对话何时变长。\n\n| 进度 | 级别 | 发生的事情 |\n|----------|-------|-------------|\n| **≥ 60%** 到阈值 | 信息 | CLI 显示青色进度条;gateway 发送信息通知 |\n| **≥ 85%** 到阈值 | 警告 | CLI 显示粗体黄色进度条;gateway 警告压缩即将发生 |\n\n在 CLI 中,上下文压力在工具输出流中显示为进度条:\n\n```\n ◐ context ████████████░░░░░░░░ 62% to compaction 48k threshold (50%) · approaching compaction\n```\n\n在消息平台上,发送纯文本通知:\n\n```\n◐ Context: ████████████░░░░░░░░ 62% to compaction (threshold: 50% of window).\n```\n\n如果自动压缩被禁用,警告会告诉您上下文可能被截断。\n\n上下文压力是自动的 —— 无需配置。它纯粹作为面向用户的通知触发,不修改消息流或向模型上下文注入任何内容。\n\n## 凭据池策略\n\n当您为同一 provider 拥有多个 API 密钥或 OAuth token 时,配置轮换策略:\n\n```yaml\ncredential_pool_strategies:\n openrouter: round_robin # 均匀循环使用密钥\n anthropic: least_used # 始终选择使用最少的密钥\n```\n\n选项:`fill_first`(默认)、`round_robin`、`least_used`、`random`。完整文档请参阅[凭据池](/user-guide/features/credential-pools)。\n\n## Prompt 缓存\n\n当活跃 provider 支持时,Hermes 自动开启跨会话 prompt 缓存 —— 无需用户配置。\n\n对于**原生 Anthropic**、**OpenRouter** 和 **Nous Portal** 上的 Claude,Hermes 在系统提示词和技能块上附加带有 1 小时 TTL(`ttl: \"1h\"`)的 `cache_control` 断点。在新鲜的一小时内首次发送时按完整输入费率计费;同一小时内任何会话的后续发送以折扣缓存读取费率从缓存中提取。这意味着系统提示词、加载的技能内容以及任何长上下文包含的早期部分在第一个小时内跨 `hermes` 会话和分叉子 agent 被重用。\n\nQwen Cloud(阿里巴巴 DashScope)上游将缓存 TTL 限制为 5 分钟,因此 Hermes 在那里使用 5 分钟断点 TTL。其他通过第三方的 Claude 路径(AWS Bedrock、Azure Foundry)回退到 provider 自己的缓存默认值。xAI Grok 使用单独的会话固定对话 ID 机制 —— 参阅 [xAI prompt 缓存](/integrations/providers#xai-grok--responses-api--prompt-caching)。\n\n不存在禁用此功能的旋钮 —— 缓存始终开启,即使在单轮对话中也能节省费用,因为仅系统提示词就占输入 token 数的相当大比例。\n\n## 辅助模型\n\nHermes 使用\"辅助\"模型处理图像分析、网页摘要、浏览器截图分析、会话标题生成和上下文压缩等附带任务。默认情况下(`auxiliary.*.provider: \"auto\"`),Hermes 将每个辅助任务路由到您的**主聊天模型** —— 与您在 `hermes model` 中选择的相同 provider/模型。您无需配置任何内容即可开始,但请注意,在昂贵的推理模型(Opus、MiniMax M2.7 等)上,辅助任务会增加显著成本。如果您希望无论主模型如何都使用便宜且快速的附带任务,请显式设置 `auxiliary.<task>.provider` 和 `auxiliary.<task>.model`(例如,在 OpenRouter 上使用 Gemini Flash 进行视觉和网页提取)。\n\n:::note 为什么 \"auto\" 使用您的主模型\n早期版本将聚合器用户(OpenRouter、Nous Portal)分流到便宜的 provider 端默认值。这令人惊讶 —— 付费购买聚合器订阅的用户会看到不同的模型处理其辅助流量。`auto` 现在对所有人使用主模型,`config.yaml` 中的每任务覆盖仍然优先(见下方[完整辅助配置参考](#full-auxiliary-config-reference))。\n:::\n\n### 交互式配置辅助模型\n\n无需手动编辑 YAML,运行 `hermes model` 并从菜单中选择**\"配置辅助模型\"**。您将获得交互式的每任务选择器:\n\n```\n$ hermes model\n→ Configure auxiliary models\n\n[ ] vision currently: auto / main model\n[ ] web_extract currently: auto / main model\n[ ] title_generation currently: openrouter / google/gemini-3-flash-preview\n[ ] compression currently: auto / main model\n[ ] approval currently: auto / main model\n[ ] triage_specifier currently: auto / main model\n[ ] kanban_decomposer currently: auto / main model\n[ ] profile_describer currently: auto / main model\n```\n\n选择任务,选择 provider(OAuth 流程打开浏览器;API 密钥 provider 提示输入),选择模型。更改持久化到 `config.yaml` 中的 `auxiliary.<task>.*`。与主模型选择器相同的机制 —— 无需学习额外语法。\n\n### 视频教程\n\n<div style={{position: 'relative', width: '100%', aspectRatio: '16 / 9', marginBottom: '1.5rem'}}>\n <iframe\n src=\"https://www.youtube.com/embed/NoF-YajElIM\"\n title=\"Hermes Agent — Auxiliary Models Tutorial\"\n style={{position: 'absolute', top: 0, left: 0, width: '100%', height: '100%', border: 0}}\n allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\"\n allowFullScreen\n />\n</div>\n\n### 通用配置模式\n\nHermes 中的每个模型槽位 —— 辅助任务、压缩、回退 —— 使用相同的三个旋钮:\n\n| 键 | 作用 | 默认值 |\n|-----|-------------|---------|\n| `provider` | 用于认证和路由的 provider | `\"auto\"` |\n| `model` | 请求的模型 | provider 的默认值 |\n| `base_url` | 自定义 OpenAI 兼容端点(覆盖 provider) | 未设置 |\n\n当设置 `base_url` 时,Hermes 忽略 provider 并直接调用该端点(使用 `api_key` 或 `OPENAI_API_KEY` 进行认证)。当仅设置 `provider` 时,Hermes 使用该 provider 的内置认证和基础 URL。\n\n辅助任务的可用 providers:`auto`、`main`,以及[provider 注册表](/reference/environment-variables)中的任何 provider —— `openrouter`、`nous`、`openai-codex`、`copilot`、`copilot-acp`、`anthropic`、`gemini`、`google-gemini-cli`、`qwen-oauth`、`zai`、`kimi-coding`、`kimi-coding-cn`、`minimax`、`minimax-cn`、`minimax-oauth`、`deepseek`、`nvidia`、`xai`、`xai-oauth`、`ollama-cloud`、`alibaba`、`bedrock`、`huggingface`、`arcee`、`xiaomi`、`kilocode`、`opencode-zen`、`opencode-go`、`azure-foundry` —— 或您 `custom_providers` 列表中任何命名的自定义 provider(例如 `provider: \"beans\"`)。\n\n:::tip MiniMax OAuth\n`minimax-oauth` 通过浏览器 OAuth 登录(无需 API 密钥)。运行 `hermes model` 并选择 **MiniMax (OAuth)** 进行认证。辅助任务自动使用 `MiniMax-M2.7-highspeed`。参阅 [MiniMax OAuth 指南](../guides/minimax-oauth.md)。\n:::\n\n:::tip xAI Grok OAuth\n`xai-oauth` 通过浏览器 OAuth 为 SuperGrok 和 X Premium+ 订阅者登录(无需 API 密钥)。运行 `hermes model` 并选择 **xAI Grok OAuth (SuperGrok / Premium+)** 进行认证。相同的 OAuth token 可重用于每个直接到 xAI 的接口(聊天、辅助任务、TTS、图像生成、视频生成、转录)。参阅 [xAI Grok OAuth 指南](../guides/xai-grok-oauth.md),如果 Hermes 在远程主机上,请参阅 [SSH/远程主机上的 OAuth](../guides/oauth-over-ssh.md)。\n:::\n\n:::warning `\"main\"` 仅用于辅助任务\n`\"main\"` provider 选项表示\"使用我的主 agent 使用的任何 provider\" —— 它仅在 `auxiliary:`、`compression:` 和 `fallback_model:` 配置中有效。它**不是**顶级 `model.provider` 设置的有效值。如果您使用自定义 OpenAI 兼容端点,请在 `model:` 部分设置 `provider: custom`。所有主模型 provider 选项请参阅 [AI Providers](/integrations/providers)。\n:::\n\n### 完整辅助配置参考\n\n```yaml\nauxiliary:\n # 图像分析(vision_analyze 工具 + 浏览器截图)\n vision:\n provider: \"auto\" # \"auto\"、\"openrouter\"、\"nous\"、\"codex\"、\"main\" 等\n model: \"\" # 例如 \"openai/gpt-4o\"、\"google/gemini-2.5-flash\"\n base_url: \"\" # 自定义 OpenAI 兼容端点(覆盖 provider)\n api_key: \"\" # base_url 的 API 密钥(回退到 OPENAI_API_KEY)\n timeout: 120 # 秒 —— LLM API 调用超时;视觉负载需要宽裕的超时\n download_timeout: 30 # 秒 —— 图像 HTTP 下载;慢速连接请增加\n\n # 网页摘要 + 浏览器页面文本提取\n web_extract:\n provider: \"auto\"\n model: \"\" # 例如 \"google/gemini-2.5-flash\"\n base_url: \"\"\n api_key: \"\"\n timeout: 360 # 秒(6 分钟)—— 每次尝试的 LLM 摘要\n\n # 危险命令审批分类器\n approval:\n provider: \"auto\"\n model: \"\"\n base_url: \"\"\n api_key: \"\"\n timeout: 30 # 秒\n\n # 上下文压缩超时(与 compression.* 配置分开)\n compression:\n timeout: 120 # 秒 —— 压缩摘要长对话,需要更多时间\n\n # 技能中心 —— 技能匹配和搜索\n skills_hub:\n provider: \"auto\"\n model: \"\"\n base_url: \"\"\n api_key: \"\"\n timeout: 30\n\n # MCP 工具调度\n mcp:\n provider: \"auto\"\n model: \"\"\n base_url: \"\"\n api_key: \"\"\n timeout: 30\n\n # Kanban 分类规格说明器 —— `hermes kanban specify <id>`(或\n # 仪表板上 Triage 列卡片的 ✨ Specify 按钮)使用此\n # 槽位将单行描述扩展为具体规格并将\n # 任务提升到 `todo`。便宜快速的模型在这里效果很好;规格扩展\n # 很短,不需要推理深度。\n triage_specifier:\n provider: \"auto\"\n model: \"\"\n base_url: \"\"\n api_key: \"\"\n timeout: 120\n```\n\n:::tip\n每个辅助任务都有可配置的 `timeout`(秒)。默认值:vision 120s、web_extract 360s、approval 30s、compression 120s。如果您为辅助任务使用慢速本地模型,请增加这些值。Vision 还有单独的 `download_timeout`(默认 30s)用于 HTTP 图像下载 —— 对于慢速连接或自托管图像服务器,请增加此值。\n:::\n\n:::info\n上下文压缩有自己的 `compression:` 块用于阈值,以及 `auxiliary.compression:` 块用于模型/provider 设置 —— 参阅上方的[上下文压缩](#context-compression)。回退模型使用 `fallback_model:` 块 —— 参阅[回退模型](/integrations/providers#fallback-model)。三者都遵循相同的 provider/model/base_url 模式。\n:::\n\n### OpenRouter 路由和辅助任务的 Pareto Code\n\n当辅助任务解析到 OpenRouter(显式或通过 `provider: \"main\"` 而您的主 agent 在 OpenRouter 上)时,主 agent 的 `provider_routing` 和 `openrouter.min_coding_score` 设置**不会传播** —— 按设计,每个辅助任务是独立的。要为特定辅助任务设置 OpenRouter provider 偏好或使用 [Pareto Code 路由器](/integrations/providers#openrouter-pareto-code-router),请通过 `extra_body` 按任务设置:\n\n```yaml\nauxiliary:\n compression:\n provider: openrouter\n model: openrouter/pareto-code # 为此任务使用 Pareto Code 路由器\n extra_body:\n provider: # OpenRouter provider 路由偏好\n order: [anthropic, google] # 按此顺序尝试这些 providers\n sort: throughput # 或 \"price\" | \"latency\"\n # only: [anthropic] # 限制到特定 provider\n # ignore: [deepinfra] # 排除特定 providers\n plugins: # OpenRouter Pareto Code 路由器旋钮\n - id: pareto-router\n min_coding_score: 0.5 # 0.0–1.0;越高 = 更强的编码能力\n```\n\n形状与 OpenRouter 在聊天补全请求体中接受的内容一致。Hermes 原样转发整个 `extra_body`,因此 [openrouter.ai/docs](https://openrouter.ai/docs) 中记录的任何其他 OpenRouter 请求体字段都以相同方式工作。\n\n### 更改视觉模型\n\n使用 GPT-4o 而非 Gemini Flash 进行图像分析:\n\n```yaml\nauxiliary:\n vision:\n model: \"openai/gpt-4o\"\n```\n\n或通过环境变量(在 `~/.hermes/.env` 中):\n\n```bash\nAUXILIARY_VISION_MODEL=openai/gpt-4o\n```\n\n### Provider 选项\n\n这些选项适用于**辅助任务配置**(`auxiliary:`、`compression:`、`fallback_model:`),而非您的主 `model.provider` 设置。\n\n| Provider | 描述 | 要求 |\n|----------|-------------|-------------|\n| `\"auto\"` | 最佳可用(默认)。Vision 尝试 OpenRouter → Nous → Codex。 | — |\n| `\"openrouter\"` | 强制 OpenRouter —— 路由到任何模型(Gemini、GPT-4o、Claude 等) | `OPENROUTER_API_KEY` |\n| `\"nous\"` | 强制 Nous Portal | `hermes auth` |\n| `\"codex\"` | 强制 Codex OAuth(ChatGPT 账户)。支持视觉(gpt-5.3-codex)。 | `hermes model` → Codex |\n| `\"minimax-oauth\"` | 强制 MiniMax OAuth(浏览器登录,无需 API 密钥)。辅助任务使用 MiniMax-M2.7-highspeed。 | `hermes model` → MiniMax (OAuth) |\n| `\"xai-oauth\"` | 强制 xAI Grok OAuth(SuperGrok 或 X Premium+ 订阅者的浏览器登录,无需 API 密钥)。相同的 OAuth token 涵盖聊天、TTS、图像、视频和转录。 | `hermes model` → xAI Grok OAuth (SuperGrok / Premium+) |\n| `\"main\"` | 使用您的活跃自定义/主端点。可以来自 `OPENAI_BASE_URL` + `OPENAI_API_KEY` 或通过 `hermes model` / `config.yaml` 保存的自定义端点。适用于 OpenAI、本地模型或任何 OpenAI 兼容 API。**仅限辅助任务 —— 对 `model.provider` 无效。** | 自定义端点凭据 + 基础 URL |\n\n当您希望附带任务绕过默认路由器时,主 provider 目录中的直接 API 密钥 providers 也在这里工作。配置 `GMI_API_KEY` 后,`gmi` 有效:\n\n```yaml\nauxiliary:\n compression:\n provider: \"gmi\"\n model: \"anthropic/claude-opus-4.6\"\n```\n\n对于 GMI 辅助路由,使用 GMI 的 `/v1/models` 端点返回的确切模型 ID。\n\n### 常见设置\n\n**使用直接自定义端点**(比 `provider: \"main\"` 对本地/自托管 API 更清晰):\n```yaml\nauxiliary:\n vision:\n base_url: \"http://localhost:1234/v1\"\n api_key: \"local-key\"\n model: \"qwen2.5-vl\"\n```\n\n`base_url` 优先于 `provider`,因此这是将辅助任务路由到特定端点的最明确方式。对于直接端点覆盖,Hermes 使用配置的 `api_key` 或回退到 `OPENAI_API_KEY`;它不会为该自定义端点重用 `OPENROUTER_API_KEY`。\n\n**使用 OpenAI API 密钥进行视觉:**\n```yaml\n# 在 ~/.hermes/.env 中:\n# OPENAI_BASE_URL=https://api.openai.com/v1\n# OPENAI_API_KEY=sk-...\n\nauxiliary:\n vision:\n provider: \"main\"\n model: \"gpt-4o\" # 或 \"gpt-4o-mini\" 更便宜\n```\n\n**使用 OpenRouter 进行视觉**(路由到任何模型):\n```yaml\nauxiliary:\n vision:\n provider: \"openrouter\"\n model: \"openai/gpt-4o\" # 或 \"google/gemini-2.5-flash\" 等\n```\n\n**使用 Codex OAuth**(ChatGPT Pro/Plus 账户 —— 无需 API 密钥):\n```yaml\nauxiliary:\n vision:\n provider: \"codex\" # 使用您的 ChatGPT OAuth token\n # 模型默认为 gpt-5.3-codex(支持视觉)\n```\n\n**使用 MiniMax OAuth**(浏览器登录,无需 API 密钥):\n```yaml\nmodel:\n default: MiniMax-M2.7\n provider: minimax-oauth\n base_url: https://api.minimax.io/anthropic\n```\n运行 `hermes model` 并选择 **MiniMax (OAuth)** 自动登录并设置此项。对于中国区域,基础 URL 将是 `https://api.minimaxi.com/anthropic`。完整演练请参阅 [MiniMax OAuth 指南](../guides/minimax-oauth.md)。\n\n**使用本地/自托管模型:**\n```yaml\nauxiliary:\n vision:\n provider: \"main\" # 使用您的活跃自定义端点\n model: \"my-local-model\"\n```\n\n`provider: \"main\"` 使用 Hermes 用于普通聊天的任何 provider —— 无论是命名的自定义 provider(例如 `beans`)、内置 provider(如 `openrouter`)还是旧版 `OPENAI_BASE_URL` 端点。\n\n:::tip\n如果您使用 Codex OAuth 作为主模型 provider,视觉会自动工作 —— 无需额外配置。Codex 包含在视觉的自动检测链中。\n:::\n\n:::warning\n**视觉需要多模态模型。** 如果您设置 `provider: \"main\"`,请确保您的端点支持多模态/视觉 —— 否则图像分析将失败。\n:::\n\n### 环境变量(旧版)\n\n辅助模型也可以通过环境变量配置。但是,`config.yaml` 是首选方法 —— 它更易于管理,并支持所有选项,包括 `base_url` 和 `api_key`。\n\n| 设置 | 环境变量 |\n|---------|---------------------|\n| Vision provider | `AUXILIARY_VISION_PROVIDER` |\n| Vision 模型 | `AUXILIARY_VISION_MODEL` |\n| Vision 端点 | `AUXILIARY_VISION_BASE_URL` |\n| Vision API 密钥 | `AUXILIARY_VISION_API_KEY` |\n| Web 提取 provider | `AUXILIARY_WEB_EXTRACT_PROVIDER` |\n| Web 提取模型 | `AUXILIARY_WEB_EXTRACT_MODEL` |\n| Web 提取端点 | `AUXILIARY_WEB_EXTRACT_BASE_URL` |\n| Web 提取 API 密钥 | `AUXILIARY_WEB_EXTRACT_API_KEY` |\n\n压缩和回退模型设置仅限 config.yaml。\n\n:::tip\n运行 `hermes config` 查看您当前的辅助模型设置。覆盖仅在与默认值不同时显示。\n:::\n\n## 推理努力程度\n\n控制模型在响应前进行多少\"思考\":\n\n```yaml\nagent:\n reasoning_effort: \"\" # 空 = 中等(默认)。选项:none、minimal、low、medium、high、xhigh(最大)\n```\n\n未设置时(默认),推理努力程度默认为\"medium\" —— 适合大多数任务的平衡级别。设置值会覆盖它 —— 更高的推理努力程度在复杂任务上提供更好的结果,但代价是更多 token 和延迟。\n\n您也可以在运行时使用 `/reasoning` 命令更改推理努力程度:\n\n```\n/reasoning # 显示当前努力程度和显示状态\n/reasoning high # 将推理努力程度设为 high\n/reasoning none # 禁用推理\n/reasoning show # 在每次响应上方显示模型思考\n/reasoning hide # 隐藏模型思考\n```\n\n## 工具使用强制\n\n某些模型偶尔会将预期操作描述为文本而不是进行工具调用(\"我会运行测试...\"而不是实际调用终端)。工具使用强制会注入系统提示词指导,引导模型实际调用工具。\n\n```yaml\nagent:\n tool_use_enforcement: \"auto\" # \"auto\" | true | false | [\"model-substring\", ...]\n```\n\n| 值 | 行为 |\n|-------|----------|\n| `\"auto\"`(默认) | 对匹配以下模型启用:`gpt`、`codex`、`gemini`、`gemma`、`grok`。对所有其他模型禁用(Claude、DeepSeek、Qwen 等)。 |\n| `true` | 始终启用,无论模型如何。如果您注意到当前模型描述操作而不是执行操作,请使用此选项。 |\n| `false` | 始终禁用,无论模型如何。 |\n| `[\"gpt\", \"codex\", \"qwen\", \"llama\"]` | 仅当模型名称包含列出的子字符串之一时启用(不区分大小写)。 |\n\n### 注入的内容\n\n启用后,系统提示词中可能会添加三层指导:\n\n1. **通用工具使用强制**(所有匹配模型)—— 指示模型立即进行工具调用而不是描述意图,持续工作直到任务完成,永远不要以未来操作的承诺结束轮次。\n\n2. **OpenAI 执行纪律**(仅限 GPT 和 Codex 模型)—— 针对 GPT 特定失败模式的额外指导:在部分结果上放弃工作、跳过先决条件查找、幻觉而不是使用工具、在未验证的情况下宣布\"完成\"。\n\n3. **Google 操作指导**(仅限 Gemini 和 Gemma 模型)—— 简洁性、绝对路径、并行工具调用和编辑前验证模式。\n\n这些对用户透明,仅影响系统提示词。已经可靠使用工具的模型(如 Claude)不需要此指导,这就是为什么 `\"auto\"` 排除它们。\n\n### 何时开启\n\n如果您使用的模型不在默认自动列表中,并注意到它经常描述它*会*做什么而不是实际去做,请设置 `tool_use_enforcement: true` 或将模型子字符串添加到列表中:\n\n```yaml\nagent:\n tool_use_enforcement: [\"gpt\", \"codex\", \"gemini\", \"grok\", \"my-custom-model\"]\n```\n\n## TTS 配置\n\n```yaml\ntts:\n provider: \"edge\" # \"edge\" | \"elevenlabs\" | \"openai\" | \"minimax\" | \"mistral\" | \"gemini\" | \"xai\" | \"neutts\"\n speed: 1.0 # 全局速度倍数(所有 provider 的回退)\n edge:\n voice: \"en-US-AriaNeural\" # 322 种声音,74 种语言\n speed: 1.0 # 速度倍数(转换为速率百分比,例如 1.5 → +50%)\n elevenlabs:\n voice_id: \"pNInz6obpgDQGcFmaJgB\"\n model_id: \"eleven_multilingual_v2\"\n openai:\n model: \"gpt-4o-mini-tts\"\n voice: \"alloy\" # alloy、echo、fable、onyx、nova、shimmer\n speed: 1.0 # 速度倍数(API 限制为 0.25–4.0)\n base_url: \"https://api.openai.com/v1\" # 覆盖 OpenAI 兼容 TTS 端点\n minimax:\n speed: 1.0 # 语音速度倍数\n # base_url: \"\" # 可选:覆盖 OpenAI 兼容 TTS 端点\n mistral:\n model: \"voxtral-mini-tts-2603\"\n voice_id: \"c69964a6-ab8b-4f8a-9465-ec0925096ec8\" # Paul - Neutral(默认)\n gemini:\n model: \"gemini-2.5-flash-preview-tts\" # 或 gemini-2.5-pro-preview-tts\n voice: \"Kore\" # 30 种预置声音:Zephyr、Puck、Kore、Enceladus 等\n xai:\n voice_id: \"eve\" # xAI TTS 声音\n language: \"en\" # ISO 639-1\n sample_rate: 24000\n bit_rate: 128000 # MP3 比特率\n # base_url: \"https://api.x.ai/v1\"\n neutts:\n ref_audio: ''\n ref_text: ''\n model: neuphonic/neutts-air-q4-gguf\n device: cpu\n```\n\n这控制 `text_to_speech` 工具和语音模式中的口语回复(CLI 中的 `/voice tts` 或消息 gateway)。\n\n**速度回退层次:** provider 特定速度(例如 `tts.edge.speed`)→ 全局 `tts.speed` → `1.0` 默认值。设置全局 `tts.speed` 以在所有 provider 上应用统一速度,或按 provider 覆盖以进行精细控制。\n\n## 显示设置\n\n```yaml\ndisplay:\n tool_progress: all # off | new | all | verbose\n tool_progress_command: false # 在消息 gateway 中启用 /verbose 斜杠命令\n platforms: {} # 每平台显示覆盖(见下文)\n tool_progress_overrides: {} # 已弃用 —— 改用 display.platforms\n interim_assistant_messages: true # Gateway:将自然的轮次中 assistant 更新作为单独消息发送\n skin: default # 内置或自定义 CLI 皮肤(参阅 user-guide/features/skins)\n personality: \"kawaii\" # 旧版外观字段,仍在某些摘要中显示\n compact: false # 紧凑输出模式(减少空白)\n resume_display: full # full(恢复时显示之前的消息)| minimal(仅单行)\n bell_on_complete: false # 当 agent 完成时播放终端铃声(适合长任务)\n show_reasoning: false # 在每次响应上方显示模型推理/思考(用 /reasoning show|hide 切换)\n streaming: false # 将 token 实时流式传输到终端\n show_cost: false # 在 CLI 状态栏中显示估计 $ 成本\n timestamps: false # 为 true 时,在 CLI/TUI 记录中为用户和 assistant 标签添加 [HH:MM] 时间戳前缀\n tool_preview_length: 0 # 工具调用预览的最大字符数(0 = 无限制,显示完整路径/命令)\n runtime_footer: # Gateway:在最终回复中附加运行时上下文页脚\n enabled: false\n fields: [\"model\", \"context_pct\", \"cwd\"]\n file_mutation_verifier: true # 当本轮 write_file/patch 调用失败时附加建议性页脚\n language: en # 静态消息的 UI 语言(审批提示、部分 gateway 回复)。en | zh | zh-hant | ja | de | es | fr | tr | uk | af | ko | it | ga | pt | ru | hu\n```\n\n### 文件变更验证器\n\n当 `display.file_mutation_verifier` 为 `true`(默认)时,每当本轮中 `write_file` 或 `patch` 调用失败且从未被对同一路径的成功写入取代时,Hermes 会在 assistant 的最终响应中附加一行建议。这捕获了\"批量并行补丁,一半静默失败,模型总结成功\"这类过度声明,而无需您在每次编辑后手动运行 `git status`。\n\n示例页脚:\n\n```\n⚠️ File-mutation verifier: 3 file(s) were NOT modified this turn despite any wording above that may suggest otherwise. Run `git status` or `read_file` to confirm.\n • concepts/automatic-organization.md — [patch] Could not find match for old_string\n • concepts/lora.md — [patch] Could not find match for old_string\n • concepts/rag-pipeline.md — [patch] Could not find match for old_string\n```\n\n设置 `file_mutation_verifier: false`(或 `HERMES_FILE_MUTATION_VERIFIER=0`)以禁止页脚。验证器仅在轮次结束时有真实失败未解决时触发 —— 在同一轮次内重试失败补丁并成功的模型不会为该文件触发它。\n\n### 静态消息的 UI 语言\n\n`display.language` 设置翻译一小组静态面向用户的消息 —— CLI 审批提示、少数 gateway 斜杠命令回复(例如重启排空通知、\"审批已过期\"、\"目标已清除\")。它**不**翻译 agent 响应、日志行、工具输出、错误回溯或斜杠命令描述 —— 这些保持英文。如果您希望 agent 本身用另一种语言回复,只需在您的提示词或系统消息中告诉它。\n\n支持的值:`en`(默认)、`zh`(简体中文)、`ja`(日语)、`de`(德语)、`es`(西班牙语)、`fr`(法语)、`tr`(土耳其语)、`uk`(乌克兰语)。未知值回退到英文。\n\n您也可以使用 `HERMES_LANGUAGE` 环境变量按会话设置,它会覆盖配置值。\n\n```yaml\ndisplay:\n language: zh # CLI 审批提示以中文显示\n```\n\n| 模式 | 您看到的内容 |\n|------|-------------|\n| `off` | 静默 —— 仅最终响应 |\n| `new` | 仅在工具更改时显示工具指示器 |\n| `all` | 每次工具调用附带简短预览(默认) |\n| `verbose` | 完整参数、结果和调试日志 |\n\n在 CLI 中,使用 `/verbose` 循环切换这些模式。要在消息平台(Telegram、Discord、Slack 等)中使用 `/verbose`,请在上方的 `display` 部分设置 `tool_progress_command: true`。该命令将循环切换模式并保存到配置。\n\n### 运行时元数据页脚(仅限 gateway)\n\n当 `display.runtime_footer.enabled: true` 时,Hermes 在每个 gateway 轮次的**最终**消息中附加一个小型运行时上下文页脚 —— 与 CLI 在其状态栏中显示的相同信息(模型、上下文 %、cwd、会话时长、token、成本)。默认关闭;如果您的团队希望每个回复都包含来源信息,请按 gateway 选择加入。\n\n```yaml\ndisplay:\n runtime_footer:\n enabled: true\n fields: [\"model\", \"context_pct\", \"cwd\"] # 任意:model、context_pct、cwd、duration、tokens、cost\n```\n\n`/footer` 斜杠命令在任何会话中运行时切换此功能。\n\n附加到 Telegram/Discord/Slack 回复的示例页脚:\n\n```\n— claude-opus-4.7 · 12 tool calls · 2m 14s · $0.042\n```\n\n只有轮次的**最终**消息获得页脚;中间更新保持干净。\n\n### 每平台进度覆盖\n\n不同平台有不同的详细程度需求。例如,Signal 无法编辑消息,因此每次进度更新都会成为单独的消息 —— 很嘈杂。使用 `display.platforms` 设置每平台模式:\n\n```yaml\ndisplay:\n tool_progress: all # 全局默认\n platforms:\n signal:\n tool_progress: 'off' # 在 Signal 上静默进度\n telegram:\n tool_progress: verbose # 在 Telegram 上详细进度\n slack:\n tool_progress: 'off' # 在共享 Slack 工作区中保持安静\n```\n\n没有覆盖的平台回退到全局 `tool_progress` 值。有效平台键:`telegram`、`discord`、`slack`、`signal`、`whatsapp`、`matrix`、`mattermost`、`email`、`sms`、`homeassistant`、`dingtalk`、`feishu`、`wecom`、`weixin`、`bluebubbles`、`qqbot`。旧版 `display.tool_progress_overrides` 键仍可加载以向后兼容,但已弃用,并在首次加载时迁移到 `display.platforms`。\n\n`interim_assistant_messages` 仅限 gateway。启用后,Hermes 将已完成的轮次中 assistant 更新作为单独的聊天消息发送。这与 `tool_progress` 无关,不需要 gateway 流式传输。\n\n## 隐私\n\n```yaml\nprivacy:\n redact_pii: false # 从 LLM 上下文中删除 PII(仅限 gateway)\n```\n\n当 `redact_pii` 为 `true` 时,gateway 在将系统提示词发送到受支持平台上的 LLM 之前,会从中删除个人身份信息:\n\n| 字段 | 处理方式 |\n|-------|-----------|\n| 电话号码(WhatsApp/Signal 上的用户 ID) | 哈希为 `user_<12-char-sha256>` |\n| 用户 ID | 哈希为 `user_<12-char-sha256>` |\n| 聊天 ID | 数字部分哈希,保留平台前缀(`telegram:<hash>`) |\n| 主频道 ID | 数字部分哈希 |\n| 用户名/昵称 | **不受影响**(用户选择的,公开可见) |\n\n**平台支持:** 删除适用于 WhatsApp、Signal 和 Telegram。Discord 和 Slack 被排除,因为它们的提及系统(`<@user_id>`)需要 LLM 上下文中的真实 ID。\n\n哈希是确定性的 —— 同一用户始终映射到同一哈希,因此模型仍然可以在群聊中区分用户。路由和传递在内部使用原始值。\n\n## 语音转文字(STT)\n\n```yaml\nstt:\n provider: \"local\" # \"local\" | \"groq\" | \"openai\" | \"mistral\"\n local:\n model: \"base\" # tiny、base、small、medium、large-v3\n openai:\n model: \"whisper-1\" # whisper-1 | gpt-4o-mini-transcribe | gpt-4o-transcribe\n # model: \"whisper-1\" # 旧版回退键仍受支持\n```\n\nProvider 行为:\n\n- `local` 使用在您机器上运行的 `faster-whisper`。使用 `pip install faster-whisper` 单独安装。\n- `groq` 使用 Groq 的 Whisper 兼容端点,读取 `GROQ_API_KEY`。\n- `openai` 使用 OpenAI 语音 API,读取 `VOICE_TOOLS_OPENAI_KEY`。\n\n如果请求的 provider 不可用,Hermes 按此顺序自动回退:`local` → `groq` → `openai`。\n\nGroq 和 OpenAI 模型覆盖由环境变量驱动:\n\n```bash\nSTT_GROQ_MODEL=whisper-large-v3-turbo\nSTT_OPENAI_MODEL=whisper-1\nGROQ_BASE_URL=https://api.groq.com/openai/v1\nSTT_OPENAI_BASE_URL=https://api.openai.com/v1\n```\n\n## 语音模式(CLI)\n\n```yaml\nvoice:\n record_key: \"ctrl+b\" # CLI 内的按键通话键\n max_recording_seconds: 120 # 长录音的硬停止\n auto_tts: false # /voice on 时自动启用口语回复\n beep_enabled: true # 在 CLI 语音模式中播放录音开始/停止提示音\n silence_threshold: 200 # 语音检测的 RMS 阈值\n silence_duration: 3.0 # 自动停止前的静默秒数\n```\n\n在 CLI 中使用 `/voice on` 启用麦克风模式,使用 `record_key` 开始/停止录音,使用 `/voice tts` 切换口语回复。端到端设置和平台特定行为请参阅[语音模式](/user-guide/features/voice-mode)。\n\n## 流式传输\n\n将 token 实时流式传输到终端或消息平台,而不是等待完整响应。\n\n### CLI 流式传输\n\n```yaml\ndisplay:\n streaming: true # 实时将 token 流式传输到终端\n show_reasoning: true # 同时流式传输推理/思考 token(可选)\n```\n\n启用后,响应在流式传输框内逐 token 出现。工具调用仍然静默捕获。如果 provider 不支持流式传输,它会自动回退到正常显示。\n\n### Gateway 流式传输(Telegram、Discord、Slack)\n\n```yaml\nstreaming:\n enabled: true # 启用渐进式消息编辑\n transport: edit # \"edit\"(渐进式消息编辑)或 \"off\"\n edit_interval: 0.3 # 消息编辑之间的秒数\n buffer_threshold: 40 # 强制编辑刷新前的字符数\n cursor: \" ▉\" # 流式传输期间显示的光标\n fresh_final_after_seconds: 60 # 当预览超过此时间时发送新的最终消息(Telegram);0 = 始终就地编辑\n```\n\n启用后,bot 在第一个 token 时发送消息,然后随着更多 token 到来渐进式编辑它。不支持消息编辑的平台(Signal、Email、Home Assistant)在第一次尝试时自动检测 —— 该会话的流式传输被优雅地禁用,不会产生大量消息。\n\n对于不带渐进式 token 编辑的独立自然轮次中 assistant 更新,请设置 `display.interim_assistant_messages: true`。\n\n**溢出处理:** 如果流式传输的文本超过平台的消息长度限制(约 4096 字符),当前消息被最终化,新消息自动开始。\n\n**新的最终消息(Telegram):** Telegram 的 `editMessageText` 保留原始消息时间戳,因此长时间运行的流式回复即使在完成后也会保留第一个 token 的时间戳。当 `fresh_final_after_seconds > 0`(默认 `60`)时,完成的回复作为全新消息传递(尽力删除旧预览),以便 Telegram 的可见时间戳反映完成时间。短预览仍然就地最终化。设置为 `0` 以始终就地编辑。\n\n:::note\n流式传输默认禁用。在 `~/.hermes/config.yaml` 中启用以尝试流式传输 UX。\n:::\n\n## 群聊会话隔离\n\n控制共享聊天是每个房间保持一个对话还是每个参与者一个对话:\n\n```yaml\ngroup_sessions_per_user: true # true = 群组/频道中每用户隔离,false = 每个聊天一个共享会话\n```\n\n- `true` 是默认和推荐设置。在 Discord 频道、Telegram 群组、Slack 频道和类似共享上下文中,当平台提供用户 ID 时,每个发送者获得自己的会话。\n- `false` 恢复到旧的共享房间行为。如果您明确希望 Hermes 将频道视为一个协作对话,这可能有用,但这也意味着用户共享上下文、token 成本和中断状态。\n- 私信不受影响。Hermes 仍然像往常一样通过聊天/DM ID 键入 DM。\n- 线程与其父频道保持隔离;使用 `true` 时,每个参与者在线程内也获得自己的会话。\n\n有关行为详情和示例,请参阅[会话](/user-guide/sessions)和 [Discord 指南](/user-guide/messaging/discord)。\n\n## 未授权 DM 行为\n\n控制当未知用户发送私信时 Hermes 的行为:\n\n```yaml\nunauthorized_dm_behavior: pair\n\nwhatsapp:\n unauthorized_dm_behavior: ignore\n```\n\n- `pair` 是默认值。Hermes 拒绝访问,但在 DM 中回复一次性配对码。\n- `ignore` 静默丢弃未授权的 DM。\n- 平台部分覆盖全局默认值,因此您可以在广泛范围内保持配对启用,同时使一个平台更安静。\n\n## 快速命令\n\n定义自定义命令,这些命令要么在不调用 LLM 的情况下运行 shell 命令,要么将一个斜杠命令别名为另一个。Exec 快速命令是零 token 的,对于从消息平台(Telegram、Discord 等)进行快速服务器检查或实用脚本很有用。\n\n```yaml\nquick_commands:\n status:\n type: exec\n command: systemctl status hermes-agent\n disk:\n type: exec\n command: df -h /\n update:\n type: exec\n command: cd ~/.hermes/hermes-agent && git pull && pip install -e .\n gpu:\n type: exec\n command: nvidia-smi --query-gpu=name,utilization.gpu,memory.used,memory.total --format=csv,noheader\n restart:\n type: alias\n target: /gateway restart\n```\n\n用法:在 CLI 或任何消息平台中输入 `/status`、`/disk`、`/update`、`/gpu` 或 `/restart`。`exec` 命令在宿主本地运行并直接返回输出 —— 无 LLM 调用,不消耗 token。`alias` 命令重写为配置的斜杠命令目标。\n\n- **30 秒超时** —— 长时间运行的命令被终止并显示错误消息\n- **优先级** —— 快速命令在技能命令之前检查,因此您可以覆盖技能名称\n- **自动补全** —— 快速命令在调度时解析,不显示在内置斜杠命令自动补全表中\n- **类型** —— 支持的类型为 `exec` 和 `alias`;其他类型显示错误\n- **到处可用** —— CLI、Telegram、Discord、Slack、WhatsApp、Signal、Email、Home Assistant\n\n仅字符串的 prompt 快捷方式不是有效的快速命令。对于可重用的 prompt 工作流,请创建技能或别名到现有斜杠命令。\n\n## 人类延迟\n\n在消息平台中模拟类人响应节奏:\n\n```yaml\nhuman_delay:\n mode: \"off\" # off | natural | custom\n min_ms: 800 # 最小延迟(自定义模式)\n max_ms: 2500 # 最大延迟(自定义模式)\n```\n\n## 代码执行\n\n配置 `execute_code` 工具:\n\n```yaml\ncode_execution:\n mode: project # project(默认)| strict\n timeout: 300 # 最大执行时间(秒)\n max_tool_calls: 50 # 代码执行中的最大工具调用次数\n```\n\n**`mode`** 控制脚本的工作目录和 Python 解释器:\n\n- **`project`**(默认)—— 脚本在会话的工作目录中以活跃 virtualenv/conda 环境的 python 运行。项目依赖(`pandas`、`torch`、项目包)和相对路径(`.env`、`./data.csv`)自然解析,与 `terminal()` 看到的一致。\n- **`strict`** —— 脚本在临时暂存目录中以 `sys.executable`(Hermes 自己的 python)运行。最大可重现性,但项目依赖和相对路径不会解析。\n\n环境清理(删除 `*_API_KEY`、`*_TOKEN`、`*_SECRET`、`*_PASSWORD`、`*_CREDENTIAL`、`*_PASSWD`、`*_AUTH`)和工具白名单在两种模式下完全相同 —— 切换模式不会改变安全态势。\n\n## Web 搜索后端\n\n`web_search`、`web_extract` 和 `web_crawl` 工具支持五种后端 provider。在 `config.yaml` 中或通过 `hermes tools` 配置后端:\n\n```yaml\nweb:\n backend: firecrawl # firecrawl | searxng | parallel | tavily | exa\n\n # 或使用每功能键混合 provider(例如免费搜索 + 付费提取):\n search_backend: \"searxng\"\n extract_backend: \"firecrawl\"\n```\n\n| 后端 | 环境变量 | 搜索 | 提取 | 爬取 |\n|---------|---------|--------|---------|-------|\n| **Firecrawl**(默认) | `FIRECRAWL_API_KEY` | ✔ | ✔ | ✔ |\n| **SearXNG** | `SEARXNG_URL` | ✔ | — | — |\n| **Parallel** | `PARALLEL_API_KEY` | ✔ | ✔ | — |\n| **Tavily** | `TAVILY_API_KEY` | ✔ | ✔ | ✔ |\n| **Exa** | `EXA_API_KEY` | ✔ | ✔ | — |\n\n**后端选择:** 如果未设置 `web.backend`,后端从可用的 API 密钥自动检测。如果仅设置了 `SEARXNG_URL`,使用 SearXNG。如果仅设置了 `EXA_API_KEY`,使用 Exa。如果仅设置了 `TAVILY_API_KEY`,使用 Tavily。如果仅设置了 `PARALLEL_API_KEY`,使用 Parallel。否则 Firecrawl 是默认值。\n\n**SearXNG** 是一个免费、自托管、尊重隐私的元搜索引擎,查询 70+ 个搜索引擎。无需 API 密钥 —— 只需将 `SEARXNG_URL` 设置为您的实例(例如 `http://localhost:8080`)。SearXNG 仅限搜索;`web_extract` 和 `web_crawl` 需要单独的提取 provider(设置 `web.extract_backend`)。Docker 设置说明请参阅 [Web 搜索设置指南](/user-guide/features/web-search)。\n\n**自托管 Firecrawl:** 设置 `FIRECRAWL_API_URL` 指向您自己的实例。设置自定义 URL 后,API 密钥变为可选(在服务器上设置 `USE_DB_AUTHENTICATION=***` 以禁用认证)。\n\n**Parallel 搜索模式:** 设置 `PARALLEL_SEARCH_MODE` 控制搜索行为 —— `fast`、`one-shot` 或 `agentic`(默认:`agentic`)。\n\n**Exa:** 在 `~/.hermes/.env` 中设置 `EXA_API_KEY`。支持 `category` 过滤(`company`、`research paper`、`news`、`people`、`personal site`、`pdf`)和域名/日期过滤器。\n\n## 浏览器\n\n配置浏览器自动化行为:\n\n```yaml\nbrowser:\n inactivity_timeout: 120 # 自动关闭空闲会话前的秒数\n command_timeout: 30 # 浏览器命令超时(截图、导航等)(秒)\n record_sessions: false # 自动将浏览器会话录制为 WebM 视频到 ~/.hermes/browser_recordings/\n # 可选 CDP 覆盖 —— 设置后,Hermes 直接附加到您自己的\n # Chromium 系浏览器(通过 /browser connect),而不是启动无头浏览器。\n cdp_url: \"\"\n # 对话框监督器 —— 控制当 CDP 后端附加时(Browserbase、本地 Chromium 系\n # 浏览器通过 /browser connect)如何处理原生 JS 对话框(alert/confirm/prompt)。\n # 在 Camofox 和默认本地 agent 浏览器模式下忽略。\n dialog_policy: must_respond # must_respond | auto_dismiss | auto_accept\n dialog_timeout_s: 300 # must_respond 下的安全自动关闭(秒)\n camofox:\n managed_persistence: false # 为 true 时,Camofox 会话跨重启持久化 cookie/登录\n user_id: \"\" # 可选的外部管理 Camofox userId\n session_key: \"\" # Hermes 创建标签页时发送的可选会话密钥\n adopt_existing_tab: false # 在创建新标签页之前重用此身份的现有标签页\n```\n\n**对话框策略:**\n\n- `must_respond`(默认)—— 捕获对话框,在 `browser_snapshot.pending_dialogs` 中显示,等待 agent 调用 `browser_dialog(action=...)`。在 `dialog_timeout_s` 秒内无响应后,对话框被自动关闭以防止页面的 JS 线程永久停滞。\n- `auto_dismiss` —— 捕获,立即关闭。Agent 仍然在事后的 `browser_snapshot.recent_dialogs` 中看到对话框记录,`closed_by=\"auto_policy\"`。\n- `auto_accept` —— 捕获,立即接受。适用于有激进 `beforeunload` 提示的页面。\n\n完整对话框工作流请参阅[浏览器功能页面](./features/browser.md#browser_dialog)。\n\n浏览器工具集支持多个 provider。有关 Browserbase、Browser Use 和本地 Chromium 系 CDP 设置的详细信息,请参阅[浏览器功能页面](/user-guide/features/browser)。\n\n## 时区\n\n使用 IANA 时区字符串覆盖服务器本地时区。影响日志中的时间戳、cron 调度和系统提示词时间注入。\n\n```yaml\ntimezone: \"America/New_York\" # IANA 时区(默认:\"\" = 服务器本地时间)\n```\n\n支持的值:任何 IANA 时区标识符(例如 `America/New_York`、`Europe/London`、`Asia/Kolkata`、`UTC`)。留空或省略以使用服务器本地时间。\n\n## Discord\n\n为消息 gateway 配置 Discord 特定行为:\n\n```yaml\ndiscord:\n require_mention: true # 在服务器频道中需要 @提及才能响应\n free_response_channels: \"\" # 逗号分隔的频道 ID,bot 在这些频道无需 @提及即可响应\n auto_thread: true # 在频道中 @提及时自动创建线程\n```\n\n- `require_mention` —— 为 `true`(默认)时,bot 仅在服务器频道中被 `@BotName` 提及时响应。DM 始终无需提及即可工作。\n- `free_response_channels` —— 逗号分隔的频道 ID 列表,bot 在这些频道对每条消息响应,无需提及。\n- `auto_thread` —— 为 `true`(默认)时,频道中的提及会自动为对话创建线程,保持频道整洁(类似 Slack 线程)。\n\n## 安全\n\n预执行安全扫描和机密脱敏:\n\n```yaml\nsecurity:\n redact_secrets: false # 在工具输出和日志中脱敏 API 密钥模式(默认关闭)\n tirith_enabled: true # 为终端命令启用 Tirith 安全扫描\n tirith_path: \"tirith\" # tirith 二进制文件路径(默认:$PATH 中的 \"tirith\")\n tirith_timeout: 5 # 等待 tirith 扫描的秒数\n tirith_fail_open: true # 如果 tirith 不可用,允许命令执行\n website_blocklist: # 参见下方网站黑名单部分\n enabled: false\n domains: []\n shared_files: []\n```\n\n- `redact_secrets` —— 为 `true` 时,自动检测并脱敏工具输出中看起来像 API 密钥、token 和密码的模式,然后再进入对话上下文和日志。**默认关闭** —— 如果您经常在工具输出中处理真实凭据并希望有安全网,请启用。显式设置为 `true` 以开启。\n- `tirith_enabled` —— 为 `true` 时,终端命令在执行前由 [Tirith](https://github.com/sheeki03/tirith) 扫描以检测潜在危险操作。\n- `tirith_path` —— tirith 二进制文件的路径。如果 tirith 安装在非标准位置,请设置此项。\n- `tirith_timeout` —— 等待 tirith 扫描的最大秒数。如果扫描超时,命令继续执行。\n- `tirith_fail_open` —— 为 `true`(默认)时,如果 tirith 不可用或失败,允许命令执行。设置为 `false` 以在 tirith 无法验证时阻止命令。\n\n## 网站黑名单\n\n阻止 agent 的 web 和浏览器工具访问特定域名:\n\n```yaml\nsecurity:\n website_blocklist:\n enabled: false # 启用 URL 阻止(默认:false)\n domains: # 被阻止的域名模式列表\n - \"*.internal.company.com\"\n - \"admin.example.com\"\n - \"*.local\"\n shared_files: # 从外部文件加载额外规则\n - \"/etc/hermes/blocked-sites.txt\"\n```\n\n启用后,任何匹配被阻止域名模式的 URL 在 web 或浏览器工具执行之前都会被拒绝。这适用于 `web_search`、`web_extract`、`browser_navigate` 以及任何访问 URL 的工具。\n\n域名规则支持:\n- 精确域名:`admin.example.com`\n- 通配符子域名:`*.internal.company.com`(阻止所有子域名)\n- TLD 通配符:`*.local`\n\n共享文件每行包含一条域名规则(空行和 `#` 注释被忽略)。缺失或不可读的文件记录警告,但不禁用其他 web 工具。\n\n策略缓存 30 秒,因此配置更改无需重启即可快速生效。\n\n## 智能审批\n\n控制 Hermes 如何处理潜在危险命令:\n\n```yaml\napprovals:\n mode: manual # manual | smart | off\n```\n\n| 模式 | 行为 |\n|------|----------|\n| `manual`(默认) | 在执行任何被标记的命令之前提示用户。在 CLI 中显示交互式审批对话框。在消息中排队待处理的审批请求。 |\n| `smart` | 使用辅助 LLM 评估被标记的命令是否真正危险。低风险命令以会话级持久性自动批准。真正有风险的命令升级给用户。 |\n| `off` | 跳过所有审批检查。等同于 `HERMES_YOLO_MODE=true`。**谨慎使用。** |\n\n智能模式对于减少审批疲劳特别有用 —— 它让 agent 在安全操作上更自主地工作,同时仍然捕获真正破坏性的命令。\n\n:::warning\n设置 `approvals.mode: off` 会禁用终端命令的所有安全检查。仅在受信任的沙箱环境中使用。\n:::\n\n## 检查点\n\n破坏性文件操作之前的自动文件系统快照。详情请参阅[检查点与回滚](/user-guide/checkpoints-and-rollback)。\n\n```yaml\ncheckpoints:\n enabled: false # 启用自动检查点(也可:hermes chat --checkpoints)。默认:false(选择加入)。\n max_snapshots: 20 # 每个目录保留的最大检查点数(默认:20)\n```\n\n\n## 委托\n\n为委托工具配置子 agent 行为:\n\n```yaml\ndelegation:\n # model: \"google/gemini-3-flash-preview\" # 覆盖模型(空 = 继承父级)\n # provider: \"openrouter\" # 覆盖 provider(空 = 继承父级)\n # base_url: \"http://localhost:1234/v1\" # 直接 OpenAI 兼容端点(优先于 provider)\n # api_key: \"local-key\" # base_url 的 API 密钥(回退到 OPENAI_API_KEY)\n # api_mode: \"\" # base_url 的线路协议:\"chat_completions\"、\"codex_responses\" 或 \"anthropic_messages\"。空 = 从 URL 自动检测(例如 /anthropic 后缀 → anthropic_messages)。对启发式无法检测的非标准端点显式设置。\n max_concurrent_children: 3 # 每批并行子 agent 数(下限 1,无上限)。也可通过 DELEGATION_MAX_CONCURRENT_CHILDREN 环境变量设置。\n max_spawn_depth: 1 # 委托树深度上限(1-3,截断)。1 = 扁平(默认):父级生成无法委托的叶子。2 = 编排器子级可以生成叶子孙级。3 = 三级。\n orchestrator_enabled: true # 全局终止开关。为 false 时,role=\"orchestrator\" 被忽略,每个子级无论 max_spawn_depth 如何都被强制为叶子。\n```\n\n**子 agent provider:model 覆盖:** 默认情况下,子 agent 继承父 agent 的 provider 和模型。设置 `delegation.provider` 和 `delegation.model` 将子 agent 路由到不同的 provider:model 对 —— 例如,在您的主 agent 运行昂贵推理模型时,为范围较窄的子任务使用便宜/快速的模型。\n\n**直接端点覆盖:** 如果您想要明显的自定义端点路径,请设置 `delegation.base_url`、`delegation.api_key` 和 `delegation.model`。这将子 agent 直接发送到该 OpenAI 兼容端点,并优先于 `delegation.provider`。如果省略 `delegation.api_key`,Hermes 仅回退到 `OPENAI_API_KEY`。\n\n**线路协议(`api_mode`):** Hermes 从 `delegation.base_url` 自动检测线路协议(例如以 `/anthropic` 结尾的路径 → `anthropic_messages`;Codex/原生 Anthropic/Kimi-coding 主机名保留其现有检测)。对于启发式无法分类的端点 —— 例如 Azure AI Foundry、MiniMax、Zhipu GLM 或前置 Anthropic 形状后端的 LiteLLM 代理 —— 请将 `delegation.api_mode` 显式设置为 `chat_completions`、`codex_responses` 或 `anthropic_messages` 之一。留空(默认)以保持自动检测。\n\n委托 provider 使用与 CLI/gateway 启动相同的凭据解析。所有配置的 provider 均受支持:`openrouter`、`nous`、`copilot`、`zai`、`kimi-coding`、`minimax`、`minimax-cn`。设置 provider 时,系统自动解析正确的基础 URL、API 密钥和 API 模式 —— 无需手动凭据连接。\n\n**优先级:** 配置中的 `delegation.base_url` → 配置中的 `delegation.provider` → 父 provider(继承)。配置中的 `delegation.model` → 父模型(继承)。仅设置 `model` 而不设置 `provider` 仅更改模型名称,同时保留父级凭据(适用于在同一 provider(如 OpenRouter)内切换模型)。\n\n**宽度和深度:** `max_concurrent_children` 限制每批并行运行的子 agent 数量(默认 `3`,下限 1,无上限)。也可通过 `DELEGATION_MAX_CONCURRENT_CHILDREN` 环境变量设置。当模型提交的 `tasks` 数组超过上限时,`delegate_task` 返回工具错误解释限制,而不是静默截断。`max_spawn_depth` 控制委托树深度(截断到 1-3)。在默认 `1` 时,委托是扁平的:子级无法生成孙级,传递 `role=\"orchestrator\"` 静默降级为 `leaf`。提升到 `2` 使编排器子级可以生成叶子孙级;`3` 用于三级树。Agent 通过 `role=\"orchestrator\"` 按调用选择编排;`orchestrator_enabled: false` 强制每个子级回到叶子,无论如何。成本呈乘法增长 —— 在 `max_spawn_depth: 3` 和 `max_concurrent_children: 3` 时,树可以达到 3×3×3 = 27 个并发叶子 agent。使用模式请参阅[子 Agent 委托 → 深度限制和嵌套编排](features/delegation.md#depth-limit-and-nested-orchestration)。\n\n## 澄清\n\n配置澄清提示行为:\n\n```yaml\nclarify:\n timeout: 120 # 等待用户澄清响应的秒数\n```\n\n## 上下文文件(SOUL.md、AGENTS.md)\n\nHermes 使用两种不同的上下文范围:\n\n| 文件 | 用途 | 范围 |\n|------|---------|-------|\n| `SOUL.md` | **主要 agent 身份** —— 定义 agent 是谁(系统提示词第 #1 槽位) | `~/.hermes/SOUL.md` 或 `$HERMES_HOME/SOUL.md` |\n| `.hermes.md` / `HERMES.md` | 项目特定指令(最高优先级) | 向上走到 git 根目录 |\n| `AGENTS.md` | 项目特定指令、编码规范 | 递归目录遍历 |\n| `CLAUDE.md` | Claude Code 上下文文件(也会检测) | 仅工作目录 |\n| `.cursorrules` | Cursor IDE 规则(也会检测) | 仅工作目录 |\n| `.cursor/rules/*.mdc` | Cursor 规则文件(也会检测) | 仅工作目录 |\n\n- **SOUL.md** 是 agent 的主要身份。它占据系统提示词的第 #1 槽位,完全替换内置的默认身份。编辑它以完全自定义 agent 是谁。\n- 如果 SOUL.md 缺失、为空或无法加载,Hermes 回退到内置默认身份。\n- **项目上下文文件使用优先级系统** —— 仅加载一种类型(第一个匹配优先):`.hermes.md` → `AGENTS.md` → `CLAUDE.md` → `.cursorrules`。SOUL.md 始终独立加载。\n- **AGENTS.md** 是分层的:如果子目录也有 AGENTS.md,所有都会合并。\n- 如果 `SOUL.md` 不存在,Hermes 会自动生成默认的 `SOUL.md`。\n- 所有加载的上下文文件上限为 20,000 字符,并进行智能截断。\n\n另请参阅:\n- [个性与 SOUL.md](/user-guide/features/personality)\n- [上下文文件](/user-guide/features/context-files)\n\n## 工作目录\n\n| 上下文 | 默认值 |\n|---------|---------|\n| **CLI(`hermes`)** | 运行命令的当前目录 |\n| **消息 gateway** | 主目录 `~`(用 `MESSAGING_CWD` 覆盖) |\n| **Docker / Singularity / Modal / SSH** | 容器或远程机器内用户的主目录 |\n\n覆盖工作目录:\n```bash\n# 在 ~/.hermes/.env 或 ~/.hermes/config.yaml 中:\nMESSAGING_CWD=/home/myuser/projects # Gateway 会话\nTERMINAL_CWD=/workspace # 所有终端会话\n```"}, "76135b329dea75cf9c079223fba3b67b88bf4d0b": {"t": 1785768013.456657, "n": 56898, "h": "2ff7b88e5543b65e", "c": "---\nsidebar_position: 2\ntitle: \"配置\"\ndescription: \"配置 Hermes Agent — config.yaml、providers、模型、API 密钥等\"\n---\n\n# 配置\n\n所有设置均存储在 `~/.hermes/` 目录中,便于访问。\n\n## 目录结构\n\n```text\n~/.hermes/\n├── config.yaml # 设置(模型、终端、TTS、压缩等)\n├── .env # API 密钥和机密\n├── auth.json # OAuth provider 凭据(Nous Portal 等)\n├── SOUL.md # 主要 agent 身份(系统提示词第 #1 槽位)\n├── memories/ # 持久记忆(MEMORY.md、USER.md)\n├── skills/ # Agent 创建的技能(通过 skill_manage 工具管理)\n├── cron/ # 定时任务\n├── sessions/ # Gateway 会话\n└── logs/ # 日志(errors.log、gateway.log — 机密自动脱敏)\n```\n\n## 管理配置\n\n```bash\nhermes config # 查看当前配置\nhermes config edit # 在编辑器中打开 config.yaml\nhermes config set KEY VAL # 设置特定值\nhermes config check # 检查缺失选项(更新后使用)\nhermes config migrate # 交互式添加缺失选项\n\n# 示例:\nhermes config set model anthropic/claude-opus-4\nhermes config set terminal.backend docker\nhermes config set OPENROUTER_API_KEY sk-or-... # 保存到 .env\n```\n\n:::tip\n`hermes config set` 命令会自动将值路由到正确的文件 —— API 密钥保存到 `.env`,其他所有内容保存到 `config.yaml`。\n:::\n\n## 配置优先级\n\n设置按以下顺序解析(优先级从高到低):\n\n1. **CLI 参数** —— 例如 `hermes chat --model anthropic/claude-sonnet-4`(单次调用覆盖)\n2. **`~/.hermes/config.yaml`** —— 所有非机密设置的主配置文件\n3. **`~/.hermes/.env`** —— 环境变量的回退;机密(API 密钥、token、密码)**必须**放这里\n4. **内置默认值** —— 未设置任何内容时的硬编码安全默认值\n\n:::info 经验法则\n机密(API 密钥、bot token、密码)放入 `.env`。其他所有内容(模型、终端后端、压缩设置、内存限制、工具集)放入 `config.yaml`。当两者都设置时,`config.yaml` 对非机密设置优先。\n:::\n\n## 环境变量替换\n\n可以在 `config.yaml` 中使用 `${VAR_NAME}` 语法引用环境变量:\n\n```yaml\nauxiliary:\n vision:\n api_key: ${GOOGLE_API_KEY}\n base_url: ${CUSTOM_VISION_URL}\n\ndelegation:\n api_key: ${DELEGATION_KEY}\n```\n\n单个值中可以有多个引用:`url: \"${HOST}:${PORT}\"`。如果引用的变量未设置,占位符将保持原样(`${UNDEFINED_VAR}` 保持不变)。仅支持 `${VAR}` 语法 —— 裸 `$VAR` 不会被展开。\n\n关于 AI provider 设置(OpenRouter、Anthropic、Copilot、自定义端点、自托管 LLM、回退模型等),请参阅 [AI Providers](/integrations/providers)。\n\n### Provider 超时\n\n可以为 provider 设置 `providers.<id>.request_timeout_seconds` 作为全局请求超时,以及 `providers.<id>.models.<model>.timeout_seconds` 作为特定模型的覆盖值。适用于每种传输方式(OpenAI-wire、原生 Anthropic、Anthropic 兼容)上的主轮次客户端、回退链、凭据轮换后的重建,以及(对于 OpenAI-wire)每请求超时 kwarg —— 因此配置值优先于旧版 `HERMES_API_TIMEOUT` 环境变量。\n\n还可以设置 `providers.<id>.stale_timeout_seconds` 用于非流式陈旧调用检测器,以及 `providers.<id>.models.<model>.stale_timeout_seconds` 作为特定模型的覆盖值。此值优先于旧版 `HERMES_API_CALL_STALE_TIMEOUT` 环境变量。\n\n不设置这些值将保持旧版默认值(`HERMES_API_TIMEOUT=1800`s、`HERMES_API_CALL_STALE_TIMEOUT=300`s、原生 Anthropic 900s)。目前不适用于 AWS Bedrock(`bedrock_converse` 和 AnthropicBedrock SDK 路径均使用 boto3 及其自身的超时配置)。请参阅 [`cli-config.yaml.example`](https://github.com/NousResearch/hermes-agent/blob/main/cli-config.yaml.example) 中的注释示例。\n\n## 终端后端配置\n\nHermes 支持七种终端后端。每种后端决定 agent 的 shell 命令实际在哪里执行 —— 本地机器、Docker 容器、通过 SSH 的远程服务器、Modal 云沙箱(直接或通过 Nous 托管的 gateway)、Daytona 工作区、Vercel Sandbox,或 Singularity/Apptainer 容器。\n\n```yaml\nterminal:\n backend: local # local | docker | ssh | modal | daytona | vercel_sandbox | singularity\n cwd: \".\" # Gateway/cron 工作目录(CLI 始终使用启动目录)\n timeout: 180 # 每条命令的超时时间(秒)\n env_passthrough: [] # 转发到沙箱执行的环境变量名(terminal + execute_code)\n singularity_image: \"docker://nikolaik/python-nodejs:python3.11-nodejs20\" # Singularity 后端的容器镜像\n modal_image: \"nikolaik/python-nodejs:python3.11-nodejs20\" # Modal 后端的容器镜像\n daytona_image: \"nikolaik/python-nodejs:python3.11-nodejs20\" # Daytona 后端的容器镜像\n```\n\n对于 Modal、Daytona 和 Vercel Sandbox 等云沙箱,`container_persistent: true` 表示 Hermes 将尝试在沙箱重建后保留文件系统状态。这并不保证相同的活跃沙箱、PID 空间或后台进程之后仍在运行。\n\n### 后端概览\n\n| 后端 | 命令运行位置 | 隔离性 | 最适合 |\n|---------|-------------------|-----------|----------|\n| **local** | 直接在您的机器上 | 无 | 开发、个人使用 |\n| **docker** | 单个持久 Docker 容器(跨会话、`/new`、子 agent 共享) | 完全(命名空间、cap-drop) | 安全沙箱、CI/CD |\n| **ssh** | 通过 SSH 的远程服务器 | 网络边界 | 远程开发、强大硬件 |\n| **modal** | Modal 云沙箱 | 完全(云 VM) | 临时云计算、评估 |\n| **daytona** | Daytona 工作区 | 完全(云容器) | 托管云开发环境 |\n| **vercel_sandbox** | Vercel Sandbox | 完全(云 microVM) | 带快照文件系统持久化的云执行 |\n| **singularity** | Singularity/Apptainer 容器 | 命名空间(--containall) | HPC 集群、共享机器 |\n\n### Local 后端\n\n默认后端。命令直接在您的机器上运行,无隔离。无需特殊设置。\n\n```yaml\nterminal:\n backend: local\n```\n\n:::warning\nAgent 拥有与您的用户账户相同的文件系统访问权限。使用 `hermes tools` 禁用不需要的工具,或切换到 Docker 进行沙箱隔离。\n:::\n\n### Docker 后端\n\n在具有安全加固的 Docker 容器内运行命令(所有权限已删除、无权限提升、PID 限制)。\n\n**单个持久容器,而非每条命令一个容器。** Hermes 在首次使用时启动一个长期运行的容器,并通过 `docker exec` 将每个终端、文件和 `execute_code` 调用路由到同一容器中 —— 跨会话、`/new`、`/reset` 和 `delegate_task` 子 agent,贯穿 Hermes 进程的整个生命周期。工作目录更改、已安装的包以及 `/workspace` 中的文件会从一次工具调用延续到下一次,就像本地 shell 一样。容器在关闭时停止并删除。详情请参阅下方的**容器生命周期**。\n\n```yaml\nterminal:\n backend: docker\n docker_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n docker_mount_cwd_to_workspace: false # 将启动目录挂载到 /workspace\n docker_run_as_host_user: false # 参见下方\"以宿主用户身份运行容器\"\n docker_forward_env: # 转发到容器的环境变量\n - \"GITHUB_TOKEN\"\n docker_volumes: # 宿主目录挂载\n - \"/home/user/projects:/workspace/projects\"\n - \"/home/user/data:/data:ro\" # :ro 表示只读\n docker_extra_args: # 附加到 `docker run` 的额外标志\n - \"--gpus=all\"\n - \"--network=host\"\n\n # 资源限制\n container_cpu: 1 # CPU 核心数(0 = 不限制)\n container_memory: 5120 # MB(0 = 不限制)\n container_disk: 51200 # MB(需要 XFS+pquota 上的 overlay2)\n container_persistent: true # 跨会话持久化 /workspace 和 /root\n```\n\n**`terminal.docker_extra_args`**(也可通过 `TERMINAL_DOCKER_EXTRA_ARGS='[\"--gpus=all\"]'` 覆盖)允许传递 Hermes 未作为一级键公开的任意 `docker run` 标志 —— `--gpus`、`--network`、`--add-host`、替代 `--security-opt` 覆盖等。每个条目必须是字符串;该列表最后附加到组装好的 `docker run` 调用中,因此可以在需要时覆盖 Hermes 的默认值。请谨慎使用 —— 与沙箱加固(权限删除、`--user`、workspace 绑定挂载)冲突的标志将悄然削弱隔离性。\n\n**要求:** 已安装并运行 Docker Desktop 或 Docker Engine。Hermes 会探测 `$PATH` 以及常见的 macOS 安装位置(`/usr/local/bin/docker`、`/opt/homebrew/bin/docker`、Docker Desktop 应用包)。开箱即用支持 Podman:设置 `HERMES_DOCKER_BINARY=podman`(或完整路径)以在两者都安装时强制使用它。\n\n**容器生命周期:** Hermes 为每个终端和文件工具调用重用单个长期运行的容器(`docker run -d ... sleep 2h`),跨会话、`/new`、`/reset` 和 `delegate_task` 子 agent,贯穿 Hermes 进程的整个生命周期。命令通过带登录 shell 的 `docker exec` 运行,因此工作目录更改、已安装的包以及 `/workspace` 中的文件都会从一次工具调用延续到下一次。容器在 Hermes 关闭时(或空闲清理回收时)停止并删除。\n\n通过 `delegate_task(tasks=[...])` 生成的并行子 agent 共享这一个容器 —— 并发的 `cd`、环境变量修改以及对同一路径的写入会发生冲突。如果子 agent 需要隔离的沙箱,必须通过 `register_task_env_overrides()` 注册每任务镜像覆盖,RL 和基准测试环境(TerminalBench2、HermesSweEnv 等)会自动为其每任务 Docker 镜像执行此操作。\n\n**安全加固:**\n- `--cap-drop ALL`,仅添加回 `DAC_OVERRIDE`、`CHOWN`、`FOWNER`\n- `--security-opt no-new-privileges`\n- `--pids-limit 256`\n- `/tmp`(512MB)、`/var/tmp`(256MB)、`/run`(64MB)的大小限制 tmpfs\n\n**凭据转发:** `docker_forward_env` 中列出的环境变量首先从您的 shell 环境解析,然后回退到 `~/.hermes/.env`。技能也可以声明 `required_environment_variables`,这些变量会自动合并。\n\n### SSH 后端\n\n通过 SSH 在远程服务器上运行命令。使用 ControlMaster 进行连接复用(5 分钟空闲保活)。默认启用持久 shell —— 状态(cwd、环境变量)在命令之间保持。\n\n```yaml\nterminal:\n backend: ssh\n persistent_shell: true # 保持长期运行的 bash 会话(默认:true)\n```\n\n**必需的环境变量:**\n\n```bash\nTERMINAL_SSH_HOST=my-server.example.com\nTERMINAL_SSH_USER=ubuntu\n```\n\n**可选:**\n\n| 变量 | 默认值 | 描述 |\n|----------|---------|-------------|\n| `TERMINAL_SSH_PORT` | `22` | SSH 端口 |\n| `TERMINAL_SSH_KEY` | (系统默认) | SSH 私钥路径 |\n| `TERMINAL_SSH_PERSISTENT` | `true` | 启用持久 shell |\n\n**工作原理:** 使用 `BatchMode=yes` 和 `StrictHostKeyChecking=accept-new` 在初始化时连接。持久 shell 在远程主机上保持单个 `bash -l` 进程存活,通过临时文件进行通信。需要 `stdin_data` 或 `sudo` 的命令会自动回退到单次模式。\n\n### Modal 后端\n\n在 [Modal](https://modal.com) 云沙箱中运行命令。每个任务获得一个具有可配置 CPU、内存和磁盘的隔离 VM。文件系统可以跨会话快照/恢复。\n\n```yaml\nterminal:\n backend: modal\n container_cpu: 1 # CPU 核心数\n container_memory: 5120 # MB(5GB)\n container_disk: 51200 # MB(50GB)\n container_persistent: true # 快照/恢复文件系统\n```\n\n**必需:** `MODAL_TOKEN_ID` + `MODAL_TOKEN_SECRET` 环境变量,或 `~/.modal.toml` 配置文件。\n\n**持久化:** 启用后,沙箱文件系统在清理时快照,并在下次会话时恢复。快照在 `~/.hermes/modal_snapshots.json` 中跟踪。这保留文件系统状态,而非活跃进程、PID 空间或后台任务。\n\n**凭据文件:** 自动从 `~/.hermes/` 挂载(OAuth token 等),并在每条命令前同步。\n\n### Daytona 后端\n\n在 [Daytona](https://daytona.io) 托管工作区中运行命令。支持停止/恢复以实现持久化。\n\n```yaml\nterminal:\n backend: daytona\n container_cpu: 1 # CPU 核心数\n container_memory: 5120 # MB → 转换为 GiB\n container_disk: 10240 # MB → 转换为 GiB(最大 10 GiB)\n container_persistent: true # 停止/恢复而非删除\n```\n\n**必需:** `DAYTONA_API_KEY` 环境变量。\n\n**持久化:** 启用后,沙箱在清理时停止(而非删除),并在下次会话时恢复。沙箱名称遵循 `hermes-{task_id}` 模式。\n\n**磁盘限制:** Daytona 强制执行 10 GiB 最大值。超过此值的请求将被截断并发出警告。\n\n### Vercel Sandbox 后端\n\n在 [Vercel Sandbox](https://vercel.com/docs/vercel-sandbox) 云 microVM 中运行命令。Hermes 使用普通的终端和文件工具接口;没有 Vercel 特定的面向模型的工具。\n\n```yaml\nterminal:\n backend: vercel_sandbox\n vercel_runtime: node24 # node24 | node22 | python3.13\n cwd: /vercel/sandbox # 默认工作区根目录\n container_persistent: true # 快照/恢复文件系统\n container_disk: 51200 # 仅共享默认值;不支持自定义磁盘\n```\n\n**必需安装:** 安装可选 SDK 扩展:\n\n```bash\npip install 'hermes-agent[vercel]'\n```\n\n**必需认证:** 使用 `VERCEL_TOKEN`、`VERCEL_PROJECT_ID` 和 `VERCEL_TEAM_ID` 三者全部配置访问令牌认证。这是在 Render、Railway、Docker 及类似宿主上部署和正常长期运行 Hermes 进程的受支持设置。\n\n对于一次性本地开发,Hermes 也接受短期 Vercel OIDC token:\n\n```bash\nVERCEL_OIDC_TOKEN=\"$(vc project token <project-name>)\" hermes chat\n```\n\n在已链接的 Vercel 项目目录中,可以省略项目名称:\n\n```bash\nVERCEL_OIDC_TOKEN=\"$(vc project token)\" hermes chat\n```\n\nOIDC token 是短期的,不应作为文档化的部署路径使用。\n\n**运行时:** `terminal.vercel_runtime` 支持 `node24`、`node22` 和 `python3.13`。未设置时,Hermes 默认使用 `node24`。\n\n**持久化:** 当 `container_persistent: true` 时,Hermes 在清理期间对沙箱文件系统进行快照,并从该快照为同一任务恢复后续沙箱。快照内容可以包括复制到沙箱中的 Hermes 同步凭据、技能和缓存文件。这仅保留文件系统状态;不保留活跃沙箱身份、PID 空间、shell 状态或正在运行的后台进程。\n\n**后台命令:** `terminal(background=true)` 使用 Hermes 的通用非本地后台进程流程。您可以在沙箱存活期间通过普通进程工具生成、轮询、等待、查看日志和终止进程。Hermes 不提供清理或重启后的原生 Vercel 分离进程恢复。\n\n**磁盘大小:** Vercel Sandbox 目前不支持 Hermes 的 `container_disk` 资源旋钮。将 `container_disk` 保持未设置或使用共享默认值 `51200`;非默认值会导致诊断和后端创建失败,而不是被静默忽略。\n\n### Singularity/Apptainer 后端\n\n在 [Singularity/Apptainer](https://apptainer.org) 容器中运行命令。专为 Docker 不可用的 HPC 集群和共享机器设计。\n\n```yaml\nterminal:\n backend: singularity\n singularity_image: \"docker://nikolaik/python-nodejs:python3.11-nodejs20\"\n container_cpu: 1 # CPU 核心数\n container_memory: 5120 # MB\n container_persistent: true # 可写覆盖层跨会话持久化\n```\n\n**要求:** `$PATH` 中有 `apptainer` 或 `singularity` 二进制文件。\n\n**镜像处理:** Docker URL(`docker://...`)自动转换为 SIF 文件并缓存。现有 `.sif` 文件直接使用。\n\n**临时目录:** 按顺序解析:`TERMINAL_SCRATCH_DIR` → `TERMINAL_SANDBOX_DIR/singularity` → `/scratch/$USER/hermes-agent`(HPC 惯例)→ `~/.hermes/sandboxes/singularity`。\n\n**隔离:** 使用 `--containall --no-home` 实现完全命名空间隔离,不挂载宿主 home 目录。\n\n### 常见终端后端问题\n\n如果终端命令立即失败或终端工具报告为已禁用:\n\n- **Local** —— 无特殊要求。入门时最安全的默认选项。\n- **Docker** —— 运行 `docker version` 验证 Docker 是否正常工作。如果失败,修复 Docker 或执行 `hermes config set terminal.backend local`。\n- **SSH** —— `TERMINAL_SSH_HOST` 和 `TERMINAL_SSH_USER` 都必须设置。如果缺少任一项,Hermes 会记录清晰的错误。\n- **Modal** —— 需要 `MODAL_TOKEN_ID` 环境变量或 `~/.modal.toml`。运行 `hermes doctor` 检查。\n- **Daytona** —— 需要 `DAYTONA_API_KEY`。Daytona SDK 处理服务器 URL 配置。\n- **Singularity** —— 需要 `$PATH` 中有 `apptainer` 或 `singularity`。HPC 集群上常见。\n\n如有疑问,将 `terminal.backend` 设回 `local` 并首先验证命令在那里运行。\n\n### 拆卸时远程到宿主文件同步\n\n对于 **SSH**、**Modal** 和 **Daytona** 后端(agent 的工作树位于与运行 Hermes 的宿主不同的机器上),Hermes 跟踪 agent 在远程沙箱中触及的文件,并在会话拆卸/沙箱清理时,将修改的文件**同步回宿主**,存放在 `~/.hermes/cache/remote-syncs/<session-id>/` 下。\n\n- 触发时机:会话关闭、`/new`、`/reset`、gateway 消息超时、子 agent 使用远程后端时 `delegate_task` 子 agent 完成。\n- 覆盖 agent 修改的整个树,而不仅仅是它明确打开的文件。添加、编辑和删除都会被捕获。\n- 远程沙箱可能在您查找时已被拆除;本地 `~/.hermes/cache/remote-syncs/…` 副本是 agent 更改内容的权威记录。\n- 大型二进制输出(模型检查点、原始数据集)按大小限制 —— 同步跳过超过 `file_sync_max_mb`(默认 `100`)的文件。如果您期望更大的工件返回,请调高该值。\n\n```yaml\nterminal:\n file_sync_max_mb: 100 # 默认 —— 同步最大 100 MB 的文件\n file_sync_enabled: true # 默认 —— 设为 false 可完全跳过同步\n```\n\n这是从会话结束后被销毁的临时云沙箱中恢复结果的方式,无需告诉 agent 显式地 `scp` 或 `modal volume put` 每个工件。\n\n### Docker 卷挂载\n\n使用 Docker 后端时,`docker_volumes` 允许您与容器共享宿主目录。每个条目使用标准 Docker `-v` 语法:`host_path:container_path[:options]`。\n\n```yaml\nterminal:\n backend: docker\n docker_volumes:\n - \"/home/user/projects:/workspace/projects\" # 读写(默认)\n - \"/home/user/datasets:/data:ro\" # 只读\n - \"/home/user/.hermes/cache/documents:/output\" # Gateway 可见的导出\n```\n\n适用于:\n- **向 agent 提供文件**(数据集、配置、参考代码)\n- **从 agent 接收文件**(生成的代码、报告、导出)\n- **共享工作区**,您和 agent 都访问相同的文件\n\n如果您使用消息 gateway 并希望 agent 通过 `MEDIA:/...` 发送生成的文件,建议使用专用的宿主可见导出挂载,例如 `/home/user/.hermes/cache/documents:/output`。\n\n- 在 Docker 中将文件写入 `/output/...`\n- 在 `MEDIA:` 中发出**宿主路径**,例如:`MEDIA:/home/user/.hermes/cache/documents/report.txt`\n- **不要**发出 `/workspace/...` 或 `/output/...`,除非该确切路径在宿主上对 gateway 进程也存在\n\n:::warning\nYAML 重复键会静默覆盖之前的键。如果您已有 `docker_volumes:` 块,请将新挂载合并到同一列表中,而不是在文件后面再添加一个 `docker_volumes:` 键。\n:::\n\n也可以通过环境变量设置:`TERMINAL_DOCKER_VOLUMES='[\"/host:/container\"]'`(JSON 数组)。\n\n### Docker 凭据转发\n\n默认情况下,Docker 终端会话不继承任意宿主凭据。如果您需要在容器内使用特定 token,请将其添加到 `terminal.docker_forward_env`。\n\n```yaml\nterminal:\n backend: docker\n docker_forward_env:\n - \"GITHUB_TOKEN\"\n - \"NPM_TOKEN\"\n```\n\nHermes 首先从您当前的 shell 解析每个列出的变量,然后回退到通过 `hermes config set` 保存的 `~/.hermes/.env`。\n\n:::warning\n`docker_forward_env` 中列出的任何内容都会对容器内运行的命令可见。只转发您愿意暴露给终端会话的凭据。\n:::\n\n### 以宿主用户身份运行容器\n\n默认情况下,Docker 容器以 `root`(UID 0)身份运行。在 `/workspace` 或其他绑定挂载中创建的文件在宿主上归 root 所有,因此会话结束后您必须 `sudo chown` 才能从宿主编辑器编辑它们。`terminal.docker_run_as_host_user` 标志解决了这个问题:\n\n```yaml\nterminal:\n backend: docker\n docker_run_as_host_user: true # 默认:false\n```\n\n启用后,Hermes 将 `--user $(id -u):$(id -g)` 附加到 `docker run` 命令,使写入绑定挂载目录(`/workspace`、`/root`、`docker_volumes` 中的任何内容)的文件归您的宿主用户所有,而非 root。权衡:容器将无法再 `apt install` 或写入 `/root/.npm` 等 root 拥有的路径 —— 如果您同时需要这两者,请使用 `HOME` 归非 root 用户所有的基础镜像(或在镜像构建时添加所需工具)。\n\n保持 `false`(默认)以获得向后兼容的行为。当您的工作流主要是\"编辑挂载的宿主文件\"且厌倦了 `sudo chown -R` 时,请开启此选项。\n\n### 可选:将启动目录挂载到 `/workspace`\n\nDocker 沙箱默认保持隔离。Hermes **不会**将您当前的宿主工作目录传入容器,除非您明确选择加入。\n\n在 `config.yaml` 中启用:\n\n```yaml\nterminal:\n backend: docker\n docker_mount_cwd_to_workspace: true\n```\n\n启用后:\n- 如果您从 `~/projects/my-app` 启动 Hermes,该宿主目录将绑定挂载到 `/workspace`\n- Docker 后端从 `/workspace` 开始\n- 文件工具和终端命令都能看到相同的挂载项目\n\n禁用时,`/workspace` 保持沙箱所有,除非您通过 `docker_volumes` 显式挂载内容。\n\n安全权衡:\n- `false` 保留沙箱边界\n- `true` 使沙箱直接访问您启动 Hermes 的目录\n\n仅在您有意希望容器处理实时宿主文件时才选择加入。\n\n### 持久 Shell\n\n默认情况下,每条终端命令在其自己的子进程中运行 —— 工作目录、环境变量和 shell 变量在命令之间重置。启用**持久 shell** 后,单个长期运行的 bash 进程在 `execute()` 调用之间保持存活,使状态在命令之间保持。\n\n这对 **SSH 后端**最有用,它还消除了每条命令的连接开销。持久 shell **对 SSH 默认启用**,对本地后端禁用。\n\n```yaml\nterminal:\n persistent_shell: true # 默认 —— 为 SSH 启用持久 shell\n```\n\n禁用:\n\n```bash\nhermes config set terminal.persistent_shell false\n```\n\n**跨命令保持的内容:**\n- 工作目录(`cd /tmp` 对下一条命令生效)\n- 导出的环境变量(`export FOO=bar`)\n- Shell 变量(`MY_VAR=hello`)\n\n**优先级:**\n\n| 级别 | 变量 | 默认值 |\n|-------|----------|---------|\n| 配置 | `terminal.persistent_shell` | `true` |\n| SSH 覆盖 | `TERMINAL_SSH_PERSISTENT` | 遵循配置 |\n| Local 覆盖 | `TERMINAL_LOCAL_PERSISTENT` | `false` |\n\n每个后端的环境变量具有最高优先级。如果您也想在本地后端使用持久 shell:\n\n```bash\nexport TERMINAL_LOCAL_PERSISTENT=true\n```\n\n:::note\n需要 `stdin_data` 或 sudo 的命令会自动回退到单次模式,因为持久 shell 的 stdin 已被 IPC 协议占用。\n:::\n\n有关每个后端的详细信息,请参阅[代码执行](features/code-execution.md)和 [README 的终端部分](features/tools.md)。\n\n## 技能设置\n\n技能可以通过其 SKILL.md frontmatter 声明自己的配置设置。这些是非机密值(路径、偏好、域设置),存储在 `config.yaml` 的 `skills.config` 命名空间下。\n\n```yaml\nskills:\n config:\n myplugin:\n path: ~/myplugin-data # 示例 —— 每个技能定义自己的键\n```\n\n**技能设置的工作原理:**\n\n- `hermes config migrate` 扫描所有已启用的技能,找到未配置的设置,并提供提示\n- `hermes config show` 在\"技能设置\"下显示所有技能设置及其所属技能\n- 技能加载时,其解析的配置值会自动注入到技能上下文中\n\n**手动设置值:**\n\n```bash\nhermes config set skills.config.myplugin.path ~/myplugin-data\n```\n\n有关在您自己的技能中声明配置设置的详细信息,请参阅[创建技能 — 配置设置](/developer-guide/creating-skills#config-settings-configyaml)。\n\n### Agent 创建技能写入的守卫\n\n当 agent 使用 `skill_manage` 创建、编辑、修补或删除技能时,Hermes 可以选择扫描新/更新的内容以查找危险关键字模式(凭据收集、明显的 prompt 注入、数据外泄指令)。扫描器**默认关闭** —— 合法触及 `~/.ssh/` 或提及 `$OPENAI_API_KEY` 的真实 agent 工作流触发启发式规则过于频繁。如果您希望扫描器在 agent 的技能写入落地前提示您,请重新开启:\n\n```yaml\nskills:\n guard_agent_created: true # 默认:false\n```\n\n开启后,任何被标记的 `skill_manage` 写入都会以审批提示的形式出现,并附带扫描器的理由。接受的写入落地;拒绝的写入向 agent 返回解释性错误。\n\n## 内存配置\n\n```yaml\nmemory:\n memory_enabled: true\n user_profile_enabled: true\n memory_char_limit: 2200 # ~800 tokens\n user_char_limit: 1375 # ~500 tokens\n```\n\n## 文件读取安全\n\n控制单次 `read_file` 调用可以返回多少内容。超过限制的读取将被拒绝,并向 agent 返回错误,提示使用 `offset` 和 `limit` 读取较小范围。这可以防止单次读取压缩的 JS 包或大型数据文件时淹没上下文窗口。\n\n```yaml\nfile_read_max_chars: 100000 # 默认 —— ~25-35K tokens\n```\n\n如果您使用具有大上下文窗口的模型并经常读取大文件,请调高此值。对于小上下文模型,请降低以保持读取高效:\n\n```yaml\n# 大上下文模型(200K+)\nfile_read_max_chars: 200000\n\n# 小型本地模型(16K 上下文)\nfile_read_max_chars: 30000\n```\n\nAgent 还会自动去重文件读取 —— 如果同一文件区域被读取两次且文件未更改,则返回轻量级存根而不是重新发送内容。这在上下文压缩后重置,以便 agent 在内容被摘要后可以重新读取文件。\n\n## 工具输出截断限制\n\n三个相关的上限控制工具在 Hermes 截断之前可以返回多少原始输出:\n\n```yaml\ntool_output:\n max_bytes: 50000 # 终端输出上限(字符)\n max_lines: 2000 # read_file 分页上限\n max_line_length: 2000 # read_file 行号视图中的每行上限\n```\n\n- **`max_bytes`** —— 当 `terminal` 命令产生超过此字符数的合并 stdout/stderr 时,Hermes 保留前 40% 和后 60%,并在中间插入 `[OUTPUT TRUNCATED]` 通知。默认 `50000`(典型分词器约 12-15K tokens)。\n- **`max_lines`** —— 单次 `read_file` 调用的 `limit` 参数上限。超过此值的请求将被截断,以防单次读取淹没上下文窗口。默认 `2000`。\n- **`max_line_length`** —— `read_file` 发出行号视图时应用的每行上限。超过此长度的行将被截断为此字符数,后跟 `... [truncated]`。默认 `2000`。\n\n对于具有大上下文窗口且每次调用可以承受更多原始输出的模型,请调高限制。对于小上下文模型,请降低以保持工具结果紧凑:\n\n```yaml\n# 大上下文模型(200K+)\ntool_output:\n max_bytes: 150000\n max_lines: 5000\n\n# 小型本地模型(16K 上下文)\ntool_output:\n max_bytes: 20000\n max_lines: 500\n```\n\n## 全局工具集禁用\n\n要在 CLI 和每个 gateway 平台上统一禁用特定工具集,请在 `agent.disabled_toolsets` 下列出其名称:\n\n```yaml\nagent:\n disabled_toolsets:\n - memory # 隐藏内存工具 + MEMORY_GUIDANCE 注入\n - web # 任何地方都不使用 web_search / web_extract\n```\n\n这在每个平台的工具配置(由 `hermes tools` 写入的 `platform_toolsets`)**之后**应用,因此此处列出的工具集始终被删除 —— 即使平台的已保存配置仍然列出它。当您希望有一个\"到处关闭 X\"的单一开关而不是编辑 `hermes tools` UI 中的 15+ 个平台行时,请使用此选项。\n\n留空列表或省略键不会产生任何效果。\n\n## Git Worktree 隔离\n\n启用隔离的 git worktree,以便在同一仓库上并行运行多个 agent:\n\n```yaml\nworktree: true # 始终创建 worktree(与 hermes -w 相同)\n# worktree: false # 默认 —— 仅在传递 -w 标志时\n```\n\n启用后,每个 CLI 会话在 `.worktrees/` 下创建一个带有自己分支的新 worktree。Agent 可以编辑文件、提交、推送和创建 PR,而不会相互干扰。干净的 worktree 在退出时删除;脏的 worktree 保留以供手动恢复。\n\n您还可以通过仓库根目录中的 `.worktreeinclude` 列出要复制到 worktree 的 gitignore 文件:\n\n```\n# .worktreeinclude\n.env\n.venv/\nnode_modules/\n```\n\n## 上下文压缩\n\nHermes 自动压缩长对话以保持在模型的上下文窗口内。压缩摘要器是一个单独的 LLM 调用 —— 您可以将其指向任何 provider 或端点。\n\n所有压缩设置都在 `config.yaml` 中(无环境变量)。\n\n### 完整参考\n\n```yaml\ncompression:\n enabled: true # 开启/关闭压缩\n threshold: 0.50 # 在上下文限制的此百分比时压缩\n target_ratio: 0.20 # 保留为最近尾部的阈值分数\n protect_last_n: 20 # 保持未压缩的最少最近消息数\n hygiene_hard_message_limit: 400 # Gateway 安全阀 —— 见下文\n\n# 摘要模型/provider 在 auxiliary: 下配置:\nauxiliary:\n compression:\n model: \"\" # 空 = 使用主聊天模型。覆盖为例如 \"google/gemini-3-flash-preview\" 以获得更便宜/更快的压缩。\n provider: \"auto\" # Provider:\"auto\"、\"openrouter\"、\"nous\"、\"codex\"、\"main\" 等\n base_url: null # 自定义 OpenAI 兼容端点(覆盖 provider)\n```\n\n:::info 旧版配置迁移\n带有 `compression.summary_model`、`compression.summary_provider` 和 `compression.summary_base_url` 的旧版配置在首次加载时自动迁移到 `auxiliary.compression.*`(配置版本 17)。无需手动操作。\n:::\n\n`hygiene_hard_message_limit` 是仅限 gateway 的**预压缩安全阀**。拥有数千条消息的失控会话可能在正常的上下文百分比阈值触发之前就达到模型上下文限制;当消息数超过此上限时,Hermes 强制压缩,无论 token 使用情况如何。默认 `400` —— 对于非常长的会话正常的平台,请调高;要强制更积极的压缩,请降低。在运行中的 gateway 上编辑此值将在下一条消息时生效(见下文)。\n\n:::tip Gateway 热重载压缩和上下文长度\n从最近的版本开始,在运行中的 gateway 上编辑 `config.yaml` 中的 `model.context_length` 或任何 `compression.*` 键将在下一条消息时生效 —— 无需 gateway 重启、`/reset` 或会话轮换。缓存的 agent 签名包含这些键,因此 gateway 在检测到更改时会透明地重建 agent。API 密钥和工具/技能配置仍需要通常的重载路径。\n:::\n\n### 常见设置\n\n**默认(自动检测)—— 无需配置:**\n```yaml\ncompression:\n enabled: true\n threshold: 0.50\n```\n使用您的主 provider 和主模型。如果您希望在比主聊天模型更便宜的模型上进行压缩,请覆盖每任务(例如 `auxiliary.compression.provider: openrouter` + `model: google/gemini-2.5-flash`)。\n\n**强制特定 provider**(基于 OAuth 或 API 密钥):\n```yaml\nauxiliary:\n compression:\n provider: nous\n model: gemini-3-flash\n```\n适用于任何 provider:`nous`、`openrouter`、`codex`、`anthropic`、`main` 等。\n\n**自定义端点**(自托管、Ollama、zai、DeepSeek 等):\n```yaml\nauxiliary:\n compression:\n model: glm-4.7\n base_url: https://api.z.ai/api/coding/paas/v4\n```\n指向自定义 OpenAI 兼容端点。使用 `OPENAI_API_KEY` 进行认证。\n\n### 三个旋钮的交互方式\n\n| `auxiliary.compression.provider` | `auxiliary.compression.base_url` | 结果 |\n|---------------------|---------------------|--------|\n| `auto`(默认) | 未设置 | 自动检测最佳可用 provider |\n| `nous` / `openrouter` / 等 | 未设置 | 强制使用该 provider,使用其认证 |\n| 任意 | 已设置 | 直接使用自定义端点(忽略 provider) |\n\n:::warning 摘要模型上下文长度要求\n摘要模型**必须**具有至少与您的主 agent 模型一样大的上下文窗口。压缩器将对话的完整中间部分发送给摘要模型 —— 如果该模型的上下文窗口小于主模型的,摘要调用将因上下文长度错误而失败。发生这种情况时,中间轮次将**在没有摘要的情况下被丢弃**,静默丢失对话上下文。如果您覆盖模型,请验证其上下文长度满足或超过您的主模型。\n:::\n\n## 上下文引擎\n\n上下文引擎控制在接近模型 token 限制时如何管理对话。内置的 `compressor` 引擎使用有损摘要(参见[上下文压缩](/developer-guide/context-compression-and-caching))。插件引擎可以用替代策略替换它。\n\n```yaml\ncontext:\n engine: \"compressor\" # 默认 —— 内置有损摘要\n```\n\n使用插件引擎(例如,用于无损上下文管理的 LCM):\n\n```yaml\ncontext:\n engine: \"lcm\" # 必须与插件名称匹配\n```\n\n插件引擎**永远不会自动激活** —— 您必须将 `context.engine` 显式设置为插件名称。可用引擎可以通过 `hermes plugins` → Provider Plugins → Context Engine 浏览和选择。\n\n有关内存插件的类似单选系统,请参阅[内存 Providers](/user-guide/features/memory-providers)。\n\n## 迭代预算压力\n\n当 agent 在处理具有许多工具调用的复杂任务时,它可能会在没有意识到预算不足的情况下耗尽其迭代预算(默认:90 轮)。预算压力会在模型接近限制时自动发出警告:\n\n| 阈值 | 级别 | 模型看到的内容 |\n|-----------|-------|---------------------|\n| **70%** | 注意 | `[BUDGET: 63/90. 27 iterations left. Start consolidating.]` |\n| **90%** | 警告 | `[BUDGET WARNING: 81/90. Only 9 left. Respond NOW.]` |\n\n警告注入到最后一个工具结果的 JSON 中(作为 `_budget_warning` 字段),而不是作为单独的消息 —— 这保留了 prompt 缓存,不会破坏对话结构。\n\n```yaml\nagent:\n max_turns: 90 # 每次对话轮次的最大迭代次数(默认:90)\n api_max_retries: 3 # 回退启动前每个 provider 的重试次数(默认:3)\n```\n\n预算压力默认启用。Agent 自然地将警告视为工具结果的一部分,鼓励它在耗尽迭代之前整合工作并提供响应。\n\n当迭代预算完全耗尽时,CLI 向用户显示通知:`⚠ Iteration budget reached (90/90) — response may be incomplete`。如果预算在活跃工作期间耗尽,agent 会在停止前生成已完成内容的摘要。\n\n`agent.api_max_retries` 控制 Hermes 在回退 provider 切换启动**之前**对瞬时错误(速率限制、连接断开、5xx)重试 provider API 调用的次数。默认为 `3` —— 总共四次尝试。如果您配置了[回退 providers](/user-guide/features/fallback-providers) 并希望更快地故障转移,请将其降至 `0`,这样主 provider 上的第一个瞬时错误会立即切换到回退,而不是对不稳定的端点进行重试。\n\n### API 超时\n\nHermes 对流式传输有单独的超时层,以及用于非流式调用的陈旧检测器。陈旧检测器仅在您将其保留为隐式默认值时才会自动调整本地 provider。\n\n| 超时 | 默认值 | 本地 providers | 配置/环境变量 |\n|---------|---------|----------------|--------------|\n| Socket 读取超时 | 120s | 自动提升至 1800s | `HERMES_STREAM_READ_TIMEOUT` |\n| 陈旧流检测 | 180s | 自动禁用 | `HERMES_STREAM_STALE_TIMEOUT` |\n| 陈旧非流检测 | 300s | 保持隐式时自动禁用 | `providers.<id>.stale_timeout_seconds` 或 `HERMES_API_CALL_STALE_TIMEOUT` |\n| API 调用(非流式) | 1800s | 不变 | `providers.<id>.request_timeout_seconds` / `timeout_seconds` 或 `HERMES_API_TIMEOUT` |\n\n**Socket 读取超时**控制 httpx 等待 provider 下一个数据块的时间。本地 LLM 在大上下文上预填充可能需要几分钟才能产生第一个 token,因此当 Hermes 检测到本地端点时,会将此值提升至 30 分钟。如果您显式设置 `HERMES_STREAM_READ_TIMEOUT`,无论端点检测如何,始终使用该值。\n\n**陈旧流检测**终止接收 SSE 保活 ping 但没有实际内容的连接。对于本地 providers,这完全禁用,因为它们在预填充期间不发送保活 ping。\n\n**陈旧非流检测**终止长时间没有响应的非流式调用。默认情况下,Hermes 在本地端点上禁用此功能,以避免长时间预填充期间的误报。如果您显式设置 `providers.<id>.stale_timeout_seconds`、`providers.<id>.models.<model>.stale_timeout_seconds` 或 `HERMES_API_CALL_STALE_TIMEOUT`,即使在本地端点上也会遵守该显式值。\n\n## 上下文压力警告\n\n与迭代预算压力分开,上下文压力跟踪对话距**压缩阈值**有多近 —— 即上下文压缩触发以摘要旧消息的点。这有助于您和 agent 了解对话何时变长。\n\n| 进度 | 级别 | 发生的事情 |\n|----------|-------|-------------|\n| **≥ 60%** 到阈值 | 信息 | CLI 显示青色进度条;gateway 发送信息通知 |\n| **≥ 85%** 到阈值 | 警告 | CLI 显示粗体黄色进度条;gateway 警告压缩即将发生 |\n\n在 CLI 中,上下文压力在工具输出流中显示为进度条:\n\n```\n ◐ context ████████████░░░░░░░░ 62% to compaction 48k threshold (50%) · approaching compaction\n```\n\n在消息平台上,发送纯文本通知:\n\n```\n◐ Context: ████████████░░░░░░░░ 62% to compaction (threshold: 50% of window).\n```\n\n如果自动压缩被禁用,警告会告诉您上下文可能被截断。\n\n上下文压力是自动的 —— 无需配置。它纯粹作为面向用户的通知触发,不修改消息流或向模型上下文注入任何内容。\n\n## 凭据池策略\n\n当您为同一 provider 拥有多个 API 密钥或 OAuth token 时,配置轮换策略:\n\n```yaml\ncredential_pool_strategies:\n openrouter: round_robin # 均匀循环使用密钥\n anthropic: least_used # 始终选择使用最少的密钥\n```\n\n选项:`fill_first`(默认)、`round_robin`、`least_used`、`random`。完整文档请参阅[凭据池](/user-guide/features/credential-pools)。\n\n## Prompt 缓存\n\n当活跃 provider 支持时,Hermes 自动开启跨会话 prompt 缓存 —— 无需用户配置。\n\n对于**原生 Anthropic**、**OpenRouter** 和 **Nous Portal** 上的 Claude,Hermes 在系统提示词和技能块上附加带有 1 小时 TTL(`ttl: \"1h\"`)的 `cache_control` 断点。在新鲜的一小时内首次发送时按完整输入费率计费;同一小时内任何会话的后续发送以折扣缓存读取费率从缓存中提取。这意味着系统提示词、加载的技能内容以及任何长上下文包含的早期部分在第一个小时内跨 `hermes` 会话和分叉子 agent 被重用。\n\nQwen Cloud(阿里巴巴 DashScope)上游将缓存 TTL 限制为 5 分钟,因此 Hermes 在那里使用 5 分钟断点 TTL。其他通过第三方的 Claude 路径(AWS Bedrock、Azure Foundry)回退到 provider 自己的缓存默认值。xAI Grok 使用单独的会话固定对话 ID 机制 —— 参阅 [xAI prompt 缓存](/integrations/providers#xai-grok--responses-api--prompt-caching)。\n\n不存在禁用此功能的旋钮 —— 缓存始终开启,即使在单轮对话中也能节省费用,因为仅系统提示词就占输入 token 数的相当大比例。\n\n## 辅助模型\n\nHermes 使用\"辅助\"模型处理图像分析、网页摘要、浏览器截图分析、会话标题生成和上下文压缩等附带任务。默认情况下(`auxiliary.*.provider: \"auto\"`),Hermes 将每个辅助任务路由到您的**主聊天模型** —— 与您在 `hermes model` 中选择的相同 provider/模型。您无需配置任何内容即可开始,但请注意,在昂贵的推理模型(Opus、MiniMax M2.7 等)上,辅助任务会增加显著成本。如果您希望无论主模型如何都使用便宜且快速的附带任务,请显式设置 `auxiliary.<task>.provider` 和 `auxiliary.<task>.model`(例如,在 OpenRouter 上使用 Gemini Flash 进行视觉和网页提取)。\n\n:::note 为什么 \"auto\" 使用您的主模型\n早期版本将聚合器用户(OpenRouter、Nous Portal)分流到便宜的 provider 端默认值。这令人惊讶 —— 付费购买聚合器订阅的用户会看到不同的模型处理其辅助流量。`auto` 现在对所有人使用主模型,`config.yaml` 中的每任务覆盖仍然优先(见下方[完整辅助配置参考](#full-auxiliary-config-reference))。\n:::\n\n### 交互式配置辅助模型\n\n无需手动编辑 YAML,运行 `hermes model` 并从菜单中选择**\"配置辅助模型\"**。您将获得交互式的每任务选择器:\n\n```\n$ hermes model\n→ Configure auxiliary models\n\n[ ] vision currently: auto / main model\n[ ] web_extract currently: auto / main model\n[ ] title_generation currently: openrouter / google/gemini-3-flash-preview\n[ ] compression currently: auto / main model\n[ ] approval currently: auto / main model\n[ ] triage_specifier currently: auto / main model\n[ ] kanban_decomposer currently: auto / main model\n[ ] profile_describer currently: auto / main model\n```\n\n选择任务,选择 provider(OAuth 流程打开浏览器;API 密钥 provider 提示输入),选择模型。更改持久化到 `config.yaml` 中的 `auxiliary.<task>.*`。与主模型选择器相同的机制 —— 无需学习额外语法。\n\n### 视频教程\n\n<div style={{position: 'relative', width: '100%', aspectRatio: '16 / 9', marginBottom: '1.5rem'}}>\n <iframe\n src=\"https://www.youtube.com/embed/NoF-YajElIM\"\n title=\"Hermes Agent — Auxiliary Models Tutorial\"\n style={{position: 'absolute', top: 0, left: 0, width: '100%', height: '100%', border: 0}}\n allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\"\n allowFullScreen\n />\n</div>\n\n### 通用配置模式\n\nHermes 中的每个模型槽位 —— 辅助任务、压缩、回退 —— 使用相同的三个旋钮:\n\n| 键 | 作用 | 默认值 |\n|-----|-------------|---------|\n| `provider` | 用于认证和路由的 provider | `\"auto\"` |\n| `model` | 请求的模型 | provider 的默认值 |\n| `base_url` | 自定义 OpenAI 兼容端点(覆盖 provider) | 未设置 |\n\n当设置 `base_url` 时,Hermes 忽略 provider 并直接调用该端点(使用 `api_key` 或 `OPENAI_API_KEY` 进行认证)。当仅设置 `provider` 时,Hermes 使用该 provider 的内置认证和基础 URL。\n\n辅助任务的可用 providers:`auto`、`main`,以及[provider 注册表](/reference/environment-variables)中的任何 provider —— `openrouter`、`nous`、`openai-codex`、`copilot`、`copilot-acp`、`anthropic`、`gemini`、`google-gemini-cli`、`qwen-oauth`、`zai`、`kimi-coding`、`kimi-coding-cn`、`minimax`、`minimax-cn`、`minimax-oauth`、`deepseek`、`nvidia`、`xai`、`xai-oauth`、`ollama-cloud`、`alibaba`、`bedrock`、`huggingface`、`arcee`、`xiaomi`、`kilocode`、`opencode-zen`、`opencode-go`、`ai-gateway`、`azure-foundry` —— 或您 `custom_providers` 列表中任何命名的自定义 provider(例如 `provider: \"beans\"`)。\n\n:::tip MiniMax OAuth\n`minimax-oauth` 通过浏览器 OAuth 登录(无需 API 密钥)。运行 `hermes model` 并选择 **MiniMax (OAuth)** 进行认证。辅助任务自动使用 `MiniMax-M2.7-highspeed`。参阅 [MiniMax OAuth 指南](../guides/minimax-oauth.md)。\n:::\n\n:::tip xAI Grok OAuth\n`xai-oauth` 通过浏览器 OAuth 为 SuperGrok 和 X Premium+ 订阅者登录(无需 API 密钥)。运行 `hermes model` 并选择 **xAI Grok OAuth (SuperGrok / Premium+)** 进行认证。相同的 OAuth token 可重用于每个直接到 xAI 的接口(聊天、辅助任务、TTS、图像生成、视频生成、转录)。参阅 [xAI Grok OAuth 指南](../guides/xai-grok-oauth.md),如果 Hermes 在远程主机上,请参阅 [SSH/远程主机上的 OAuth](../guides/oauth-over-ssh.md)。\n:::\n\n:::warning `\"main\"` 仅用于辅助任务\n`\"main\"` provider 选项表示\"使用我的主 agent 使用的任何 provider\" —— 它仅在 `auxiliary:`、`compression:` 和 `fallback_model:` 配置中有效。它**不是**顶级 `model.provider` 设置的有效值。如果您使用自定义 OpenAI 兼容端点,请在 `model:` 部分设置 `provider: custom`。所有主模型 provider 选项请参阅 [AI Providers](/integrations/providers)。\n:::\n\n### 完整辅助配置参考\n\n```yaml\nauxiliary:\n # 图像分析(vision_analyze 工具 + 浏览器截图)\n vision:\n provider: \"auto\" # \"auto\"、\"openrouter\"、\"nous\"、\"codex\"、\"main\" 等\n model: \"\" # 例如 \"openai/gpt-4o\"、\"google/gemini-2.5-flash\"\n base_url: \"\" # 自定义 OpenAI 兼容端点(覆盖 provider)\n api_key: \"\" # base_url 的 API 密钥(回退到 OPENAI_API_KEY)\n timeout: 120 # 秒 —— LLM API 调用超时;视觉负载需要宽裕的超时\n download_timeout: 30 # 秒 —— 图像 HTTP 下载;慢速连接请增加\n\n # 网页摘要 + 浏览器页面文本提取\n web_extract:\n provider: \"auto\"\n model: \"\" # 例如 \"google/gemini-2.5-flash\"\n base_url: \"\"\n api_key: \"\"\n timeout: 360 # 秒(6 分钟)—— 每次尝试的 LLM 摘要\n\n # 危险命令审批分类器\n approval:\n provider: \"auto\"\n model: \"\"\n base_url: \"\"\n api_key: \"\"\n timeout: 30 # 秒\n\n # 上下文压缩超时(与 compression.* 配置分开)\n compression:\n timeout: 120 # 秒 —— 压缩摘要长对话,需要更多时间\n\n # 技能中心 —— 技能匹配和搜索\n skills_hub:\n provider: \"auto\"\n model: \"\"\n base_url: \"\"\n api_key: \"\"\n timeout: 30\n\n # MCP 工具调度\n mcp:\n provider: \"auto\"\n model: \"\"\n base_url: \"\"\n api_key: \"\"\n timeout: 30\n\n # Kanban 分类规格说明器 —— `hermes kanban specify <id>`(或\n # 仪表板上 Triage 列卡片的 ✨ Specify 按钮)使用此\n # 槽位将单行描述扩展为具体规格并将\n # 任务提升到 `todo`。便宜快速的模型在这里效果很好;规格扩展\n # 很短,不需要推理深度。\n triage_specifier:\n provider: \"auto\"\n model: \"\"\n base_url: \"\"\n api_key: \"\"\n timeout: 120\n```\n\n:::tip\n每个辅助任务都有可配置的 `timeout`(秒)。默认值:vision 120s、web_extract 360s、approval 30s、compression 120s。如果您为辅助任务使用慢速本地模型,请增加这些值。Vision 还有单独的 `download_timeout`(默认 30s)用于 HTTP 图像下载 —— 对于慢速连接或自托管图像服务器,请增加此值。\n:::\n\n:::info\n上下文压缩有自己的 `compression:` 块用于阈值,以及 `auxiliary.compression:` 块用于模型/provider 设置 —— 参阅上方的[上下文压缩](#context-compression)。回退模型使用 `fallback_model:` 块 —— 参阅[回退模型](/integrations/providers#fallback-model)。三者都遵循相同的 provider/model/base_url 模式。\n:::\n\n### OpenRouter 路由和辅助任务的 Pareto Code\n\n当辅助任务解析到 OpenRouter(显式或通过 `provider: \"main\"` 而您的主 agent 在 OpenRouter 上)时,主 agent 的 `provider_routing` 和 `openrouter.min_coding_score` 设置**不会传播** —— 按设计,每个辅助任务是独立的。要为特定辅助任务设置 OpenRouter provider 偏好或使用 [Pareto Code 路由器](/integrations/providers#openrouter-pareto-code-router),请通过 `extra_body` 按任务设置:\n\n```yaml\nauxiliary:\n compression:\n provider: openrouter\n model: openrouter/pareto-code # 为此任务使用 Pareto Code 路由器\n extra_body:\n provider: # OpenRouter provider 路由偏好\n order: [anthropic, google] # 按此顺序尝试这些 providers\n sort: throughput # 或 \"price\" | \"latency\"\n # only: [anthropic] # 限制到特定 provider\n # ignore: [deepinfra] # 排除特定 providers\n plugins: # OpenRouter Pareto Code 路由器旋钮\n - id: pareto-router\n min_coding_score: 0.5 # 0.0–1.0;越高 = 更强的编码能力\n```\n\n形状与 OpenRouter 在聊天补全请求体中接受的内容一致。Hermes 原样转发整个 `extra_body`,因此 [openrouter.ai/docs](https://openrouter.ai/docs) 中记录的任何其他 OpenRouter 请求体字段都以相同方式工作。\n\n### 更改视觉模型\n\n使用 GPT-4o 而非 Gemini Flash 进行图像分析:\n\n```yaml\nauxiliary:\n vision:\n model: \"openai/gpt-4o\"\n```\n\n或通过环境变量(在 `~/.hermes/.env` 中):\n\n```bash\nAUXILIARY_VISION_MODEL=openai/gpt-4o\n```\n\n### Provider 选项\n\n这些选项适用于**辅助任务配置**(`auxiliary:`、`compression:`、`fallback_model:`),而非您的主 `model.provider` 设置。\n\n| Provider | 描述 | 要求 |\n|----------|-------------|-------------|\n| `\"auto\"` | 最佳可用(默认)。Vision 尝试 OpenRouter → Nous → Codex。 | — |\n| `\"openrouter\"` | 强制 OpenRouter —— 路由到任何模型(Gemini、GPT-4o、Claude 等) | `OPENROUTER_API_KEY` |\n| `\"nous\"` | 强制 Nous Portal | `hermes auth` |\n| `\"codex\"` | 强制 Codex OAuth(ChatGPT 账户)。支持视觉(gpt-5.3-codex)。 | `hermes model` → Codex |\n| `\"minimax-oauth\"` | 强制 MiniMax OAuth(浏览器登录,无需 API 密钥)。辅助任务使用 MiniMax-M2.7-highspeed。 | `hermes model` → MiniMax (OAuth) |\n| `\"xai-oauth\"` | 强制 xAI Grok OAuth(SuperGrok 或 X Premium+ 订阅者的浏览器登录,无需 API 密钥)。相同的 OAuth token 涵盖聊天、TTS、图像、视频和转录。 | `hermes model` → xAI Grok OAuth (SuperGrok / Premium+) |\n| `\"main\"` | 使用您的活跃自定义/主端点。可以来自 `OPENAI_BASE_URL` + `OPENAI_API_KEY` 或通过 `hermes model` / `config.yaml` 保存的自定义端点。适用于 OpenAI、本地模型或任何 OpenAI 兼容 API。**仅限辅助任务 —— 对 `model.provider` 无效。** | 自定义端点凭据 + 基础 URL |\n\n当您希望附带任务绕过默认路由器时,主 provider 目录中的直接 API 密钥 providers 也在这里工作。配置 `GMI_API_KEY` 后,`gmi` 有效:\n\n```yaml\nauxiliary:\n compression:\n provider: \"gmi\"\n model: \"anthropic/claude-opus-4.6\"\n```\n\n对于 GMI 辅助路由,使用 GMI 的 `/v1/models` 端点返回的确切模型 ID。\n\n### 常见设置\n\n**使用直接自定义端点**(比 `provider: \"main\"` 对本地/自托管 API 更清晰):\n```yaml\nauxiliary:\n vision:\n base_url: \"http://localhost:1234/v1\"\n api_key: \"local-key\"\n model: \"qwen2.5-vl\"\n```\n\n`base_url` 优先于 `provider`,因此这是将辅助任务路由到特定端点的最明确方式。对于直接端点覆盖,Hermes 使用配置的 `api_key` 或回退到 `OPENAI_API_KEY`;它不会为该自定义端点重用 `OPENROUTER_API_KEY`。\n\n**使用 OpenAI API 密钥进行视觉:**\n```yaml\n# 在 ~/.hermes/.env 中:\n# OPENAI_BASE_URL=https://api.openai.com/v1\n# OPENAI_API_KEY=sk-...\n\nauxiliary:\n vision:\n provider: \"main\"\n model: \"gpt-4o\" # 或 \"gpt-4o-mini\" 更便宜\n```\n\n**使用 OpenRouter 进行视觉**(路由到任何模型):\n```yaml\nauxiliary:\n vision:\n provider: \"openrouter\"\n model: \"openai/gpt-4o\" # 或 \"google/gemini-2.5-flash\" 等\n```\n\n**使用 Codex OAuth**(ChatGPT Pro/Plus 账户 —— 无需 API 密钥):\n```yaml\nauxiliary:\n vision:\n provider: \"codex\" # 使用您的 ChatGPT OAuth token\n # 模型默认为 gpt-5.3-codex(支持视觉)\n```\n\n**使用 MiniMax OAuth**(浏览器登录,无需 API 密钥):\n```yaml\nmodel:\n default: MiniMax-M2.7\n provider: minimax-oauth\n base_url: https://api.minimax.io/anthropic\n```\n运行 `hermes model` 并选择 **MiniMax (OAuth)** 自动登录并设置此项。对于中国区域,基础 URL 将是 `https://api.minimaxi.com/anthropic`。完整演练请参阅 [MiniMax OAuth 指南](../guides/minimax-oauth.md)。\n\n**使用本地/自托管模型:**\n```yaml\nauxiliary:\n vision:\n provider: \"main\" # 使用您的活跃自定义端点\n model: \"my-local-model\"\n```\n\n`provider: \"main\"` 使用 Hermes 用于普通聊天的任何 provider —— 无论是命名的自定义 provider(例如 `beans`)、内置 provider(如 `openrouter`)还是旧版 `OPENAI_BASE_URL` 端点。\n\n:::tip\n如果您使用 Codex OAuth 作为主模型 provider,视觉会自动工作 —— 无需额外配置。Codex 包含在视觉的自动检测链中。\n:::\n\n:::warning\n**视觉需要多模态模型。** 如果您设置 `provider: \"main\"`,请确保您的端点支持多模态/视觉 —— 否则图像分析将失败。\n:::\n\n### 环境变量(旧版)\n\n辅助模型也可以通过环境变量配置。但是,`config.yaml` 是首选方法 —— 它更易于管理,并支持所有选项,包括 `base_url` 和 `api_key`。\n\n| 设置 | 环境变量 |\n|---------|---------------------|\n| Vision provider | `AUXILIARY_VISION_PROVIDER` |\n| Vision 模型 | `AUXILIARY_VISION_MODEL` |\n| Vision 端点 | `AUXILIARY_VISION_BASE_URL` |\n| Vision API 密钥 | `AUXILIARY_VISION_API_KEY` |\n| Web 提取 provider | `AUXILIARY_WEB_EXTRACT_PROVIDER` |\n| Web 提取模型 | `AUXILIARY_WEB_EXTRACT_MODEL` |\n| Web 提取端点 | `AUXILIARY_WEB_EXTRACT_BASE_URL` |\n| Web 提取 API 密钥 | `AUXILIARY_WEB_EXTRACT_API_KEY` |\n\n压缩和回退模型设置仅限 config.yaml。\n\n:::tip\n运行 `hermes config` 查看您当前的辅助模型设置。覆盖仅在与默认值不同时显示。\n:::\n\n## 推理努力程度\n\n控制模型在响应前进行多少\"思考\":\n\n```yaml\nagent:\n reasoning_effort: \"\" # 空 = 中等(默认)。选项:none、minimal、low、medium、high、xhigh(最大)\n```\n\n未设置时(默认),推理努力程度默认为\"medium\" —— 适合大多数任务的平衡级别。设置值会覆盖它 —— 更高的推理努力程度在复杂任务上提供更好的结果,但代价是更多 token 和延迟。\n\n您也可以在运行时使用 `/reasoning` 命令更改推理努力程度:\n\n```\n/reasoning # 显示当前努力程度和显示状态\n/reasoning high # 将推理努力程度设为 high\n/reasoning none # 禁用推理\n/reasoning show # 在每次响应上方显示模型思考\n/reasoning hide # 隐藏模型思考\n```\n\n## 工具使用强制\n\n某些模型偶尔会将预期操作描述为文本而不是进行工具调用(\"我会运行测试...\"而不是实际调用终端)。工具使用强制会注入系统提示词指导,引导模型实际调用工具。\n\n```yaml\nagent:\n tool_use_enforcement: \"auto\" # \"auto\" | true | false | [\"model-substring\", ...]\n```\n\n| 值 | 行为 |\n|-------|----------|\n| `\"auto\"`(默认) | 对匹配以下模型启用:`gpt`、`codex`、`gemini`、`gemma`、`grok`。对所有其他模型禁用(Claude、DeepSeek、Qwen 等)。 |\n| `true` | 始终启用,无论模型如何。如果您注意到当前模型描述操作而不是执行操作,请使用此选项。 |\n| `false` | 始终禁用,无论模型如何。 |\n| `[\"gpt\", \"codex\", \"qwen\", \"llama\"]` | 仅当模型名称包含列出的子字符串之一时启用(不区分大小写)。 |\n\n### 注入的内容\n\n启用后,系统提示词中可能会添加三层指导:\n\n1. **通用工具使用强制**(所有匹配模型)—— 指示模型立即进行工具调用而不是描述意图,持续工作直到任务完成,永远不要以未来操作的承诺结束轮次。\n\n2. **OpenAI 执行纪律**(仅限 GPT 和 Codex 模型)—— 针对 GPT 特定失败模式的额外指导:在部分结果上放弃工作、跳过先决条件查找、幻觉而不是使用工具、在未验证的情况下宣布\"完成\"。\n\n3. **Google 操作指导**(仅限 Gemini 和 Gemma 模型)—— 简洁性、绝对路径、并行工具调用和编辑前验证模式。\n\n这些对用户透明,仅影响系统提示词。已经可靠使用工具的模型(如 Claude)不需要此指导,这就是为什么 `\"auto\"` 排除它们。\n\n### 何时开启\n\n如果您使用的模型不在默认自动列表中,并注意到它经常描述它*会*做什么而不是实际去做,请设置 `tool_use_enforcement: true` 或将模型子字符串添加到列表中:\n\n```yaml\nagent:\n tool_use_enforcement: [\"gpt\", \"codex\", \"gemini\", \"grok\", \"my-custom-model\"]\n```\n\n## TTS 配置\n\n```yaml\ntts:\n provider: \"edge\" # \"edge\" | \"elevenlabs\" | \"openai\" | \"minimax\" | \"mistral\" | \"gemini\" | \"xai\" | \"neutts\"\n speed: 1.0 # 全局速度倍数(所有 provider 的回退)\n edge:\n voice: \"en-US-AriaNeural\" # 322 种声音,74 种语言\n speed: 1.0 # 速度倍数(转换为速率百分比,例如 1.5 → +50%)\n elevenlabs:\n voice_id: \"pNInz6obpgDQGcFmaJgB\"\n model_id: \"eleven_multilingual_v2\"\n openai:\n model: \"gpt-4o-mini-tts\"\n voice: \"alloy\" # alloy、echo、fable、onyx、nova、shimmer\n speed: 1.0 # 速度倍数(API 限制为 0.25–4.0)\n base_url: \"https://api.openai.com/v1\" # 覆盖 OpenAI 兼容 TTS 端点\n minimax:\n speed: 1.0 # 语音速度倍数\n # base_url: \"\" # 可选:覆盖 OpenAI 兼容 TTS 端点\n mistral:\n model: \"voxtral-mini-tts-2603\"\n voice_id: \"c69964a6-ab8b-4f8a-9465-ec0925096ec8\" # Paul - Neutral(默认)\n gemini:\n model: \"gemini-2.5-flash-preview-tts\" # 或 gemini-2.5-pro-preview-tts\n voice: \"Kore\" # 30 种预置声音:Zephyr、Puck、Kore、Enceladus 等\n xai:\n voice_id: \"eve\" # xAI TTS 声音\n language: \"en\" # ISO 639-1\n sample_rate: 24000\n bit_rate: 128000 # MP3 比特率\n # base_url: \"https://api.x.ai/v1\"\n neutts:\n ref_audio: ''\n ref_text: ''\n model: neuphonic/neutts-air-q4-gguf\n device: cpu\n```\n\n这控制 `text_to_speech` 工具和语音模式中的口语回复(CLI 中的 `/voice tts` 或消息 gateway)。\n\n**速度回退层次:** provider 特定速度(例如 `tts.edge.speed`)→ 全局 `tts.speed` → `1.0` 默认值。设置全局 `tts.speed` 以在所有 provider 上应用统一速度,或按 provider 覆盖以进行精细控制。\n\n## 显示设置\n\n```yaml\ndisplay:\n tool_progress: all # off | new | all | verbose\n tool_progress_command: false # 在消息 gateway 中启用 /verbose 斜杠命令\n platforms: {} # 每平台显示覆盖(见下文)\n tool_progress_overrides: {} # 已弃用 —— 改用 display.platforms\n interim_assistant_messages: true # Gateway:将自然的轮次中 assistant 更新作为单独消息发送\n skin: default # 内置或自定义 CLI 皮肤(参阅 user-guide/features/skins)\n personality: \"kawaii\" # 旧版外观字段,仍在某些摘要中显示\n compact: false # 紧凑输出模式(减少空白)\n resume_display: full # full(恢复时显示之前的消息)| minimal(仅单行)\n bell_on_complete: false # 当 agent 完成时播放终端铃声(适合长任务)\n show_reasoning: false # 在每次响应上方显示模型推理/思考(用 /reasoning show|hide 切换)\n streaming: false # 将 token 实时流式传输到终端\n show_cost: false # 在 CLI 状态栏中显示估计 $ 成本\n timestamps: false # 为 true 时,在 CLI/TUI 记录中为用户和 assistant 标签添加 [HH:MM] 时间戳前缀\n tool_preview_length: 0 # 工具调用预览的最大字符数(0 = 无限制,显示完整路径/命令)\n runtime_footer: # Gateway:在最终回复中附加运行时上下文页脚\n enabled: false\n fields: [\"model\", \"context_pct\", \"cwd\"]\n file_mutation_verifier: true # 当本轮 write_file/patch 调用失败时附加建议性页脚\n language: en # 静态消息的 UI 语言(审批提示、部分 gateway 回复)。en | zh | zh-hant | ja | de | es | fr | tr | uk | af | ko | it | ga | pt | ru | hu\n```\n\n### 文件变更验证器\n\n当 `display.file_mutation_verifier` 为 `true`(默认)时,每当本轮中 `write_file` 或 `patch` 调用失败且从未被对同一路径的成功写入取代时,Hermes 会在 assistant 的最终响应中附加一行建议。这捕获了\"批量并行补丁,一半静默失败,模型总结成功\"这类过度声明,而无需您在每次编辑后手动运行 `git status`。\n\n示例页脚:\n\n```\n⚠️ File-mutation verifier: 3 file(s) were NOT modified this turn despite any wording above that may suggest otherwise. Run `git status` or `read_file` to confirm.\n • concepts/automatic-organization.md — [patch] Could not find match for old_string\n • concepts/lora.md — [patch] Could not find match for old_string\n • concepts/rag-pipeline.md — [patch] Could not find match for old_string\n```\n\n设置 `file_mutation_verifier: false`(或 `HERMES_FILE_MUTATION_VERIFIER=0`)以禁止页脚。验证器仅在轮次结束时有真实失败未解决时触发 —— 在同一轮次内重试失败补丁并成功的模型不会为该文件触发它。\n\n### 静态消息的 UI 语言\n\n`display.language` 设置翻译一小组静态面向用户的消息 —— CLI 审批提示、少数 gateway 斜杠命令回复(例如重启排空通知、\"审批已过期\"、\"目标已清除\")。它**不**翻译 agent 响应、日志行、工具输出、错误回溯或斜杠命令描述 —— 这些保持英文。如果您希望 agent 本身用另一种语言回复,只需在您的提示词或系统消息中告诉它。\n\n支持的值:`en`(默认)、`zh`(简体中文)、`ja`(日语)、`de`(德语)、`es`(西班牙语)、`fr`(法语)、`tr`(土耳其语)、`uk`(乌克兰语)。未知值回退到英文。\n\n您也可以使用 `HERMES_LANGUAGE` 环境变量按会话设置,它会覆盖配置值。\n\n```yaml\ndisplay:\n language: zh # CLI 审批提示以中文显示\n```\n\n| 模式 | 您看到的内容 |\n|------|-------------|\n| `off` | 静默 —— 仅最终响应 |\n| `new` | 仅在工具更改时显示工具指示器 |\n| `all` | 每次工具调用附带简短预览(默认) |\n| `verbose` | 完整参数、结果和调试日志 |\n\n在 CLI 中,使用 `/verbose` 循环切换这些模式。要在消息平台(Telegram、Discord、Slack 等)中使用 `/verbose`,请在上方的 `display` 部分设置 `tool_progress_command: true`。该命令将循环切换模式并保存到配置。\n\n### 运行时元数据页脚(仅限 gateway)\n\n当 `display.runtime_footer.enabled: true` 时,Hermes 在每个 gateway 轮次的**最终**消息中附加一个小型运行时上下文页脚 —— 与 CLI 在其状态栏中显示的相同信息(模型、上下文 %、cwd、会话时长、token、成本)。默认关闭;如果您的团队希望每个回复都包含来源信息,请按 gateway 选择加入。\n\n```yaml\ndisplay:\n runtime_footer:\n enabled: true\n fields: [\"model\", \"context_pct\", \"cwd\"] # 任意:model、context_pct、cwd、duration、tokens、cost\n```\n\n`/footer` 斜杠命令在任何会话中运行时切换此功能。\n\n附加到 Telegram/Discord/Slack 回复的示例页脚:\n\n```\n— claude-opus-4.7 · 12 tool calls · 2m 14s · $0.042\n```\n\n只有轮次的**最终**消息获得页脚;中间更新保持干净。\n\n### 每平台进度覆盖\n\n不同平台有不同的详细程度需求。例如,Signal 无法编辑消息,因此每次进度更新都会成为单独的消息 —— 很嘈杂。使用 `display.platforms` 设置每平台模式:\n\n```yaml\ndisplay:\n tool_progress: all # 全局默认\n platforms:\n signal:\n tool_progress: 'off' # 在 Signal 上静默进度\n telegram:\n tool_progress: verbose # 在 Telegram 上详细进度\n slack:\n tool_progress: 'off' # 在共享 Slack 工作区中保持安静\n```\n\n没有覆盖的平台回退到全局 `tool_progress` 值。有效平台键:`telegram`、`discord`、`slack`、`signal`、`whatsapp`、`matrix`、`mattermost`、`email`、`sms`、`homeassistant`、`dingtalk`、`feishu`、`wecom`、`weixin`、`bluebubbles`、`qqbot`。旧版 `display.tool_progress_overrides` 键仍可加载以向后兼容,但已弃用,并在首次加载时迁移到 `display.platforms`。\n\n`interim_assistant_messages` 仅限 gateway。启用后,Hermes 将已完成的轮次中 assistant 更新作为单独的聊天消息发送。这与 `tool_progress` 无关,不需要 gateway 流式传输。\n\n## 隐私\n\n```yaml\nprivacy:\n redact_pii: false # 从 LLM 上下文中删除 PII(仅限 gateway)\n```\n\n当 `redact_pii` 为 `true` 时,gateway 在将系统提示词发送到受支持平台上的 LLM 之前,会从中删除个人身份信息:\n\n| 字段 | 处理方式 |\n|-------|-----------|\n| 电话号码(WhatsApp/Signal 上的用户 ID) | 哈希为 `user_<12-char-sha256>` |\n| 用户 ID | 哈希为 `user_<12-char-sha256>` |\n| 聊天 ID | 数字部分哈希,保留平台前缀(`telegram:<hash>`) |\n| 主频道 ID | 数字部分哈希 |\n| 用户名/昵称 | **不受影响**(用户选择的,公开可见) |\n\n**平台支持:** 删除适用于 WhatsApp、Signal 和 Telegram。Discord 和 Slack 被排除,因为它们的提及系统(`<@user_id>`)需要 LLM 上下文中的真实 ID。\n\n哈希是确定性的 —— 同一用户始终映射到同一哈希,因此模型仍然可以在群聊中区分用户。路由和传递在内部使用原始值。\n\n## 语音转文字(STT)\n\n```yaml\nstt:\n provider: \"local\" # \"local\" | \"groq\" | \"openai\" | \"mistral\"\n local:\n model: \"base\" # tiny、base、small、medium、large-v3\n openai:\n model: \"whisper-1\" # whisper-1 | gpt-4o-mini-transcribe | gpt-4o-transcribe\n # model: \"whisper-1\" # 旧版回退键仍受支持\n```\n\nProvider 行为:\n\n- `local` 使用在您机器上运行的 `faster-whisper`。使用 `pip install faster-whisper` 单独安装。\n- `groq` 使用 Groq 的 Whisper 兼容端点,读取 `GROQ_API_KEY`。\n- `openai` 使用 OpenAI 语音 API,读取 `VOICE_TOOLS_OPENAI_KEY`。\n\n如果请求的 provider 不可用,Hermes 按此顺序自动回退:`local` → `groq` → `openai`。\n\nGroq 和 OpenAI 模型覆盖由环境变量驱动:\n\n```bash\nSTT_GROQ_MODEL=whisper-large-v3-turbo\nSTT_OPENAI_MODEL=whisper-1\nGROQ_BASE_URL=https://api.groq.com/openai/v1\nSTT_OPENAI_BASE_URL=https://api.openai.com/v1\n```\n\n## 语音模式(CLI)\n\n```yaml\nvoice:\n record_key: \"ctrl+b\" # CLI 内的按键通话键\n max_recording_seconds: 120 # 长录音的硬停止\n auto_tts: false # /voice on 时自动启用口语回复\n beep_enabled: true # 在 CLI 语音模式中播放录音开始/停止提示音\n silence_threshold: 200 # 语音检测的 RMS 阈值\n silence_duration: 3.0 # 自动停止前的静默秒数\n```\n\n在 CLI 中使用 `/voice on` 启用麦克风模式,使用 `record_key` 开始/停止录音,使用 `/voice tts` 切换口语回复。端到端设置和平台特定行为请参阅[语音模式](/user-guide/features/voice-mode)。\n\n## 流式传输\n\n将 token 实时流式传输到终端或消息平台,而不是等待完整响应。\n\n### CLI 流式传输\n\n```yaml\ndisplay:\n streaming: true # 实时将 token 流式传输到终端\n show_reasoning: true # 同时流式传输推理/思考 token(可选)\n```\n\n启用后,响应在流式传输框内逐 token 出现。工具调用仍然静默捕获。如果 provider 不支持流式传输,它会自动回退到正常显示。\n\n### Gateway 流式传输(Telegram、Discord、Slack)\n\n```yaml\nstreaming:\n enabled: true # 启用渐进式消息编辑\n transport: edit # \"edit\"(渐进式消息编辑)或 \"off\"\n edit_interval: 0.3 # 消息编辑之间的秒数\n buffer_threshold: 40 # 强制编辑刷新前的字符数\n cursor: \" ▉\" # 流式传输期间显示的光标\n fresh_final_after_seconds: 60 # 当预览超过此时间时发送新的最终消息(Telegram);0 = 始终就地编辑\n```\n\n启用后,bot 在第一个 token 时发送消息,然后随着更多 token 到来渐进式编辑它。不支持消息编辑的平台(Signal、Email、Home Assistant)在第一次尝试时自动检测 —— 该会话的流式传输被优雅地禁用,不会产生大量消息。\n\n对于不带渐进式 token 编辑的独立自然轮次中 assistant 更新,请设置 `display.interim_assistant_messages: true`。\n\n**溢出处理:** 如果流式传输的文本超过平台的消息长度限制(约 4096 字符),当前消息被最终化,新消息自动开始。\n\n**新的最终消息(Telegram):** Telegram 的 `editMessageText` 保留原始消息时间戳,因此长时间运行的流式回复即使在完成后也会保留第一个 token 的时间戳。当 `fresh_final_after_seconds > 0`(默认 `60`)时,完成的回复作为全新消息传递(尽力删除旧预览),以便 Telegram 的可见时间戳反映完成时间。短预览仍然就地最终化。设置为 `0` 以始终就地编辑。\n\n:::note\n流式传输默认禁用。在 `~/.hermes/config.yaml` 中启用以尝试流式传输 UX。\n:::\n\n## 群聊会话隔离\n\n控制共享聊天是每个房间保持一个对话还是每个参与者一个对话:\n\n```yaml\ngroup_sessions_per_user: true # true = 群组/频道中每用户隔离,false = 每个聊天一个共享会话\n```\n\n- `true` 是默认和推荐设置。在 Discord 频道、Telegram 群组、Slack 频道和类似共享上下文中,当平台提供用户 ID 时,每个发送者获得自己的会话。\n- `false` 恢复到旧的共享房间行为。如果您明确希望 Hermes 将频道视为一个协作对话,这可能有用,但这也意味着用户共享上下文、token 成本和中断状态。\n- 私信不受影响。Hermes 仍然像往常一样通过聊天/DM ID 键入 DM。\n- 线程与其父频道保持隔离;使用 `true` 时,每个参与者在线程内也获得自己的会话。\n\n有关行为详情和示例,请参阅[会话](/user-guide/sessions)和 [Discord 指南](/user-guide/messaging/discord)。\n\n## 未授权 DM 行为\n\n控制当未知用户发送私信时 Hermes 的行为:\n\n```yaml\nunauthorized_dm_behavior: pair\n\nwhatsapp:\n unauthorized_dm_behavior: ignore\n```\n\n- `pair` 是默认值。Hermes 拒绝访问,但在 DM 中回复一次性配对码。\n- `ignore` 静默丢弃未授权的 DM。\n- 平台部分覆盖全局默认值,因此您可以在广泛范围内保持配对启用,同时使一个平台更安静。\n\n## 快速命令\n\n定义自定义命令,这些命令要么在不调用 LLM 的情况下运行 shell 命令,要么将一个斜杠命令别名为另一个。Exec 快速命令是零 token 的,对于从消息平台(Telegram、Discord 等)进行快速服务器检查或实用脚本很有用。\n\n```yaml\nquick_commands:\n status:\n type: exec\n command: systemctl status hermes-agent\n disk:\n type: exec\n command: df -h /\n update:\n type: exec\n command: cd ~/.hermes/hermes-agent && git pull && pip install -e .\n gpu:\n type: exec\n command: nvidia-smi --query-gpu=name,utilization.gpu,memory.used,memory.total --format=csv,noheader\n restart:\n type: alias\n target: /gateway restart\n```\n\n用法:在 CLI 或任何消息平台中输入 `/status`、`/disk`、`/update`、`/gpu` 或 `/restart`。`exec` 命令在宿主本地运行并直接返回输出 —— 无 LLM 调用,不消耗 token。`alias` 命令重写为配置的斜杠命令目标。\n\n- **30 秒超时** —— 长时间运行的命令被终止并显示错误消息\n- **优先级** —— 快速命令在技能命令之前检查,因此您可以覆盖技能名称\n- **自动补全** —— 快速命令在调度时解析,不显示在内置斜杠命令自动补全表中\n- **类型** —— 支持的类型为 `exec` 和 `alias`;其他类型显示错误\n- **到处可用** —— CLI、Telegram、Discord、Slack、WhatsApp、Signal、Email、Home Assistant\n\n仅字符串的 prompt 快捷方式不是有效的快速命令。对于可重用的 prompt 工作流,请创建技能或别名到现有斜杠命令。\n\n## 人类延迟\n\n在消息平台中模拟类人响应节奏:\n\n```yaml\nhuman_delay:\n mode: \"off\" # off | natural | custom\n min_ms: 800 # 最小延迟(自定义模式)\n max_ms: 2500 # 最大延迟(自定义模式)\n```\n\n## 代码执行\n\n配置 `execute_code` 工具:\n\n```yaml\ncode_execution:\n mode: project # project(默认)| strict\n timeout: 300 # 最大执行时间(秒)\n max_tool_calls: 50 # 代码执行中的最大工具调用次数\n```\n\n**`mode`** 控制脚本的工作目录和 Python 解释器:\n\n- **`project`**(默认)—— 脚本在会话的工作目录中以活跃 virtualenv/conda 环境的 python 运行。项目依赖(`pandas`、`torch`、项目包)和相对路径(`.env`、`./data.csv`)自然解析,与 `terminal()` 看到的一致。\n- **`strict`** —— 脚本在临时暂存目录中以 `sys.executable`(Hermes 自己的 python)运行。最大可重现性,但项目依赖和相对路径不会解析。\n\n环境清理(删除 `*_API_KEY`、`*_TOKEN`、`*_SECRET`、`*_PASSWORD`、`*_CREDENTIAL`、`*_PASSWD`、`*_AUTH`)和工具白名单在两种模式下完全相同 —— 切换模式不会改变安全态势。\n\n## Web 搜索后端\n\n`web_search`、`web_extract` 和 `web_crawl` 工具支持五种后端 provider。在 `config.yaml` 中或通过 `hermes tools` 配置后端:\n\n```yaml\nweb:\n backend: firecrawl # firecrawl | searxng | parallel | tavily | exa\n\n # 或使用每功能键混合 provider(例如免费搜索 + 付费提取):\n search_backend: \"searxng\"\n extract_backend: \"firecrawl\"\n```\n\n| 后端 | 环境变量 | 搜索 | 提取 | 爬取 |\n|---------|---------|--------|---------|-------|\n| **Firecrawl**(默认) | `FIRECRAWL_API_KEY` | ✔ | ✔ | ✔ |\n| **SearXNG** | `SEARXNG_URL` | ✔ | — | — |\n| **Parallel** | `PARALLEL_API_KEY` | ✔ | ✔ | — |\n| **Tavily** | `TAVILY_API_KEY` | ✔ | ✔ | ✔ |\n| **Exa** | `EXA_API_KEY` | ✔ | ✔ | — |\n\n**后端选择:** 如果未设置 `web.backend`,后端从可用的 API 密钥自动检测。如果仅设置了 `SEARXNG_URL`,使用 SearXNG。如果仅设置了 `EXA_API_KEY`,使用 Exa。如果仅设置了 `TAVILY_API_KEY`,使用 Tavily。如果仅设置了 `PARALLEL_API_KEY`,使用 Parallel。否则 Firecrawl 是默认值。\n\n**SearXNG** 是一个免费、自托管、尊重隐私的元搜索引擎,查询 70+ 个搜索引擎。无需 API 密钥 —— 只需将 `SEARXNG_URL` 设置为您的实例(例如 `http://localhost:8080`)。SearXNG 仅限搜索;`web_extract` 和 `web_crawl` 需要单独的提取 provider(设置 `web.extract_backend`)。Docker 设置说明请参阅 [Web 搜索设置指南](/user-guide/features/web-search)。\n\n**自托管 Firecrawl:** 设置 `FIRECRAWL_API_URL` 指向您自己的实例。设置自定义 URL 后,API 密钥变为可选(在服务器上设置 `USE_DB_AUTHENTICATION=***` 以禁用认证)。\n\n**Parallel 搜索模式:** 设置 `PARALLEL_SEARCH_MODE` 控制搜索行为 —— `fast`、`one-shot` 或 `agentic`(默认:`agentic`)。\n\n**Exa:** 在 `~/.hermes/.env` 中设置 `EXA_API_KEY`。支持 `category` 过滤(`company`、`research paper`、`news`、`people`、`personal site`、`pdf`)和域名/日期过滤器。\n\n## 浏览器\n\n配置浏览器自动化行为:\n\n```yaml\nbrowser:\n inactivity_timeout: 120 # 自动关闭空闲会话前的秒数\n command_timeout: 30 # 浏览器命令超时(截图、导航等)(秒)\n record_sessions: false # 自动将浏览器会话录制为 WebM 视频到 ~/.hermes/browser_recordings/\n # 可选 CDP 覆盖 —— 设置后,Hermes 直接附加到您自己的\n # Chromium 系浏览器(通过 /browser connect),而不是启动无头浏览器。\n cdp_url: \"\"\n # 对话框监督器 —— 控制当 CDP 后端附加时(Browserbase、本地 Chromium 系\n # 浏览器通过 /browser connect)如何处理原生 JS 对话框(alert/confirm/prompt)。\n # 在 Camofox 和默认本地 agent 浏览器模式下忽略。\n dialog_policy: must_respond # must_respond | auto_dismiss | auto_accept\n dialog_timeout_s: 300 # must_respond 下的安全自动关闭(秒)\n camofox:\n managed_persistence: false # 为 true 时,Camofox 会话跨重启持久化 cookie/登录\n user_id: \"\" # 可选的外部管理 Camofox userId\n session_key: \"\" # Hermes 创建标签页时发送的可选会话密钥\n adopt_existing_tab: false # 在创建新标签页之前重用此身份的现有标签页\n```\n\n**对话框策略:**\n\n- `must_respond`(默认)—— 捕获对话框,在 `browser_snapshot.pending_dialogs` 中显示,等待 agent 调用 `browser_dialog(action=...)`。在 `dialog_timeout_s` 秒内无响应后,对话框被自动关闭以防止页面的 JS 线程永久停滞。\n- `auto_dismiss` —— 捕获,立即关闭。Agent 仍然在事后的 `browser_snapshot.recent_dialogs` 中看到对话框记录,`closed_by=\"auto_policy\"`。\n- `auto_accept` —— 捕获,立即接受。适用于有激进 `beforeunload` 提示的页面。\n\n完整对话框工作流请参阅[浏览器功能页面](./features/browser.md#browser_dialog)。\n\n浏览器工具集支持多个 provider。有关 Browserbase、Browser Use 和本地 Chromium 系 CDP 设置的详细信息,请参阅[浏览器功能页面](/user-guide/features/browser)。\n\n## 时区\n\n使用 IANA 时区字符串覆盖服务器本地时区。影响日志中的时间戳、cron 调度和系统提示词时间注入。\n\n```yaml\ntimezone: \"America/New_York\" # IANA 时区(默认:\"\" = 服务器本地时间)\n```\n\n支持的值:任何 IANA 时区标识符(例如 `America/New_York`、`Europe/London`、`Asia/Kolkata`、`UTC`)。留空或省略以使用服务器本地时间。\n\n## Discord\n\n为消息 gateway 配置 Discord 特定行为:\n\n```yaml\ndiscord:\n require_mention: true # 在服务器频道中需要 @提及才能响应\n free_response_channels: \"\" # 逗号分隔的频道 ID,bot 在这些频道无需 @提及即可响应\n auto_thread: true # 在频道中 @提及时自动创建线程\n```\n\n- `require_mention` —— 为 `true`(默认)时,bot 仅在服务器频道中被 `@BotName` 提及时响应。DM 始终无需提及即可工作。\n- `free_response_channels` —— 逗号分隔的频道 ID 列表,bot 在这些频道对每条消息响应,无需提及。\n- `auto_thread` —— 为 `true`(默认)时,频道中的提及会自动为对话创建线程,保持频道整洁(类似 Slack 线程)。\n\n## 安全\n\n预执行安全扫描和机密脱敏:\n\n```yaml\nsecurity:\n redact_secrets: false # 在工具输出和日志中脱敏 API 密钥模式(默认关闭)\n tirith_enabled: true # 为终端命令启用 Tirith 安全扫描\n tirith_path: \"tirith\" # tirith 二进制文件路径(默认:$PATH 中的 \"tirith\")\n tirith_timeout: 5 # 等待 tirith 扫描的秒数\n tirith_fail_open: true # 如果 tirith 不可用,允许命令执行\n website_blocklist: # 参见下方网站黑名单部分\n enabled: false\n domains: []\n shared_files: []\n```\n\n- `redact_secrets` —— 为 `true` 时,自动检测并脱敏工具输出中看起来像 API 密钥、token 和密码的模式,然后再进入对话上下文和日志。**默认关闭** —— 如果您经常在工具输出中处理真实凭据并希望有安全网,请启用。显式设置为 `true` 以开启。\n- `tirith_enabled` —— 为 `true` 时,终端命令在执行前由 [Tirith](https://github.com/sheeki03/tirith) 扫描以检测潜在危险操作。\n- `tirith_path` —— tirith 二进制文件的路径。如果 tirith 安装在非标准位置,请设置此项。\n- `tirith_timeout` —— 等待 tirith 扫描的最大秒数。如果扫描超时,命令继续执行。\n- `tirith_fail_open` —— 为 `true`(默认)时,如果 tirith 不可用或失败,允许命令执行。设置为 `false` 以在 tirith 无法验证时阻止命令。\n\n## 网站黑名单\n\n阻止 agent 的 web 和浏览器工具访问特定域名:\n\n```yaml\nsecurity:\n website_blocklist:\n enabled: false # 启用 URL 阻止(默认:false)\n domains: # 被阻止的域名模式列表\n - \"*.internal.company.com\"\n - \"admin.example.com\"\n - \"*.local\"\n shared_files: # 从外部文件加载额外规则\n - \"/etc/hermes/blocked-sites.txt\"\n```\n\n启用后,任何匹配被阻止域名模式的 URL 在 web 或浏览器工具执行之前都会被拒绝。这适用于 `web_search`、`web_extract`、`browser_navigate` 以及任何访问 URL 的工具。\n\n域名规则支持:\n- 精确域名:`admin.example.com`\n- 通配符子域名:`*.internal.company.com`(阻止所有子域名)\n- TLD 通配符:`*.local`\n\n共享文件每行包含一条域名规则(空行和 `#` 注释被忽略)。缺失或不可读的文件记录警告,但不禁用其他 web 工具。\n\n策略缓存 30 秒,因此配置更改无需重启即可快速生效。\n\n## 智能审批\n\n控制 Hermes 如何处理潜在危险命令:\n\n```yaml\napprovals:\n mode: manual # manual | smart | off\n```\n\n| 模式 | 行为 |\n|------|----------|\n| `manual`(默认) | 在执行任何被标记的命令之前提示用户。在 CLI 中显示交互式审批对话框。在消息中排队待处理的审批请求。 |\n| `smart` | 使用辅助 LLM 评估被标记的命令是否真正危险。低风险命令以会话级持久性自动批准。真正有风险的命令升级给用户。 |\n| `off` | 跳过所有审批检查。等同于 `HERMES_YOLO_MODE=true`。**谨慎使用。** |\n\n智能模式对于减少审批疲劳特别有用 —— 它让 agent 在安全操作上更自主地工作,同时仍然捕获真正破坏性的命令。\n\n:::warning\n设置 `approvals.mode: off` 会禁用终端命令的所有安全检查。仅在受信任的沙箱环境中使用。\n:::\n\n## 检查点\n\n破坏性文件操作之前的自动文件系统快照。详情请参阅[检查点与回滚](/user-guide/checkpoints-and-rollback)。\n\n```yaml\ncheckpoints:\n enabled: false # 启用自动检查点(也可:hermes chat --checkpoints)。默认:false(选择加入)。\n max_snapshots: 20 # 每个目录保留的最大检查点数(默认:20)\n```\n\n\n## 委托\n\n为委托工具配置子 agent 行为:\n\n```yaml\ndelegation:\n # model: \"google/gemini-3-flash-preview\" # 覆盖模型(空 = 继承父级)\n # provider: \"openrouter\" # 覆盖 provider(空 = 继承父级)\n # base_url: \"http://localhost:1234/v1\" # 直接 OpenAI 兼容端点(优先于 provider)\n # api_key: \"local-key\" # base_url 的 API 密钥(回退到 OPENAI_API_KEY)\n # api_mode: \"\" # base_url 的线路协议:\"chat_completions\"、\"codex_responses\" 或 \"anthropic_messages\"。空 = 从 URL 自动检测(例如 /anthropic 后缀 → anthropic_messages)。对启发式无法检测的非标准端点显式设置。\n max_concurrent_children: 3 # 每批并行子 agent 数(下限 1,无上限)。也可通过 DELEGATION_MAX_CONCURRENT_CHILDREN 环境变量设置。\n max_spawn_depth: 1 # 委托树深度上限(1-3,截断)。1 = 扁平(默认):父级生成无法委托的叶子。2 = 编排器子级可以生成叶子孙级。3 = 三级。\n orchestrator_enabled: true # 全局终止开关。为 false 时,role=\"orchestrator\" 被忽略,每个子级无论 max_spawn_depth 如何都被强制为叶子。\n```\n\n**子 agent provider:model 覆盖:** 默认情况下,子 agent 继承父 agent 的 provider 和模型。设置 `delegation.provider` 和 `delegation.model` 将子 agent 路由到不同的 provider:model 对 —— 例如,在您的主 agent 运行昂贵推理模型时,为范围较窄的子任务使用便宜/快速的模型。\n\n**直接端点覆盖:** 如果您想要明显的自定义端点路径,请设置 `delegation.base_url`、`delegation.api_key` 和 `delegation.model`。这将子 agent 直接发送到该 OpenAI 兼容端点,并优先于 `delegation.provider`。如果省略 `delegation.api_key`,Hermes 仅回退到 `OPENAI_API_KEY`。\n\n**线路协议(`api_mode`):** Hermes 从 `delegation.base_url` 自动检测线路协议(例如以 `/anthropic` 结尾的路径 → `anthropic_messages`;Codex/原生 Anthropic/Kimi-coding 主机名保留其现有检测)。对于启发式无法分类的端点 —— 例如 Azure AI Foundry、MiniMax、Zhipu GLM 或前置 Anthropic 形状后端的 LiteLLM 代理 —— 请将 `delegation.api_mode` 显式设置为 `chat_completions`、`codex_responses` 或 `anthropic_messages` 之一。留空(默认)以保持自动检测。\n\n委托 provider 使用与 CLI/gateway 启动相同的凭据解析。所有配置的 provider 均受支持:`openrouter`、`nous`、`copilot`、`zai`、`kimi-coding`、`minimax`、`minimax-cn`。设置 provider 时,系统自动解析正确的基础 URL、API 密钥和 API 模式 —— 无需手动凭据连接。\n\n**优先级:** 配置中的 `delegation.base_url` → 配置中的 `delegation.provider` → 父 provider(继承)。配置中的 `delegation.model` → 父模型(继承)。仅设置 `model` 而不设置 `provider` 仅更改模型名称,同时保留父级凭据(适用于在同一 provider(如 OpenRouter)内切换模型)。\n\n**宽度和深度:** `max_concurrent_children` 限制每批并行运行的子 agent 数量(默认 `3`,下限 1,无上限)。也可通过 `DELEGATION_MAX_CONCURRENT_CHILDREN` 环境变量设置。当模型提交的 `tasks` 数组超过上限时,`delegate_task` 返回工具错误解释限制,而不是静默截断。`max_spawn_depth` 控制委托树深度(截断到 1-3)。在默认 `1` 时,委托是扁平的:子级无法生成孙级,传递 `role=\"orchestrator\"` 静默降级为 `leaf`。提升到 `2` 使编排器子级可以生成叶子孙级;`3` 用于三级树。Agent 通过 `role=\"orchestrator\"` 按调用选择编排;`orchestrator_enabled: false` 强制每个子级回到叶子,无论如何。成本呈乘法增长 —— 在 `max_spawn_depth: 3` 和 `max_concurrent_children: 3` 时,树可以达到 3×3×3 = 27 个并发叶子 agent。使用模式请参阅[子 Agent 委托 → 深度限制和嵌套编排](features/delegation.md#depth-limit-and-nested-orchestration)。\n\n## 澄清\n\n配置澄清提示行为:\n\n```yaml\nclarify:\n timeout: 120 # 等待用户澄清响应的秒数\n```\n\n## 上下文文件(SOUL.md、AGENTS.md)\n\nHermes 使用两种不同的上下文范围:\n\n| 文件 | 用途 | 范围 |\n|------|---------|-------|\n| `SOUL.md` | **主要 agent 身份** —— 定义 agent 是谁(系统提示词第 #1 槽位) | `~/.hermes/SOUL.md` 或 `$HERMES_HOME/SOUL.md` |\n| `.hermes.md` / `HERMES.md` | 项目特定指令(最高优先级) | 向上走到 git 根目录 |\n| `AGENTS.md` | 项目特定指令、编码规范 | 递归目录遍历 |\n| `CLAUDE.md` | Claude Code 上下文文件(也会检测) | 仅工作目录 |\n| `.cursorrules` | Cursor IDE 规则(也会检测) | 仅工作目录 |\n| `.cursor/rules/*.mdc` | Cursor 规则文件(也会检测) | 仅工作目录 |\n\n- **SOUL.md** 是 agent 的主要身份。它占据系统提示词的第 #1 槽位,完全替换内置的默认身份。编辑它以完全自定义 agent 是谁。\n- 如果 SOUL.md 缺失、为空或无法加载,Hermes 回退到内置默认身份。\n- **项目上下文文件使用优先级系统** —— 仅加载一种类型(第一个匹配优先):`.hermes.md` → `AGENTS.md` → `CLAUDE.md` → `.cursorrules`。SOUL.md 始终独立加载。\n- **AGENTS.md** 是分层的:如果子目录也有 AGENTS.md,所有都会合并。\n- 如果 `SOUL.md` 不存在,Hermes 会自动生成默认的 `SOUL.md`。\n- 所有加载的上下文文件上限为 20,000 字符,并进行智能截断。\n\n另请参阅:\n- [个性与 SOUL.md](/user-guide/features/personality)\n- [上下文文件](/user-guide/features/context-files)\n\n## 工作目录\n\n| 上下文 | 默认值 |\n|---------|---------|\n| **CLI(`hermes`)** | 运行命令的当前目录 |\n| **消息 gateway** | 主目录 `~`(用 `MESSAGING_CWD` 覆盖) |\n| **Docker / Singularity / Modal / SSH** | 容器或远程机器内用户的主目录 |\n\n覆盖工作目录:\n```bash\n# 在 ~/.hermes/.env 或 ~/.hermes/config.yaml 中:\nMESSAGING_CWD=/home/myuser/projects # Gateway 会话\nTERMINAL_CWD=/workspace # 所有终端会话\n```"}, "7f3c62b26323dc91718ae04682b585cccfbd6b07": {"t": 1786068602.3386908, "n": 56672, "h": "86d8f1ce0d525265", "c": "---\nsidebar_position: 2\ntitle: \"配置\"\ndescription: \"配置 Hermes Agent — config.yaml、providers、模型、API 密钥等\"\n---\n\n# 配置\n\n所有设置均存储在 `~/.hermes/` 目录中,便于访问。\n\n## 目录结构\n\n```text\n~/.hermes/\n├── config.yaml # 设置(模型、终端、TTS、压缩等)\n├── .env # API 密钥和机密\n├── auth.json # OAuth provider 凭据(Nous Portal 等)\n├── SOUL.md # 主要 agent 身份(系统提示词第 #1 槽位)\n├── memories/ # 持久记忆(MEMORY.md、USER.md)\n├── skills/ # Agent 创建的技能(通过 skill_manage 工具管理)\n├── cron/ # 定时任务\n├── sessions/ # Gateway 会话\n└── logs/ # 日志(errors.log、gateway.log — 机密自动脱敏)\n```\n\n## 管理配置\n\n```bash\nhermes config # 查看当前配置\nhermes config edit # 在编辑器中打开 config.yaml\nhermes config set KEY VAL # 设置特定值\nhermes config check # 检查缺失选项(更新后使用)\nhermes config migrate # 交互式添加缺失选项\n\n# 示例:\nhermes config set model anthropic/claude-opus-4\nhermes config set terminal.backend docker\nhermes config set OPENROUTER_API_KEY sk-or-... # 保存到 .env\n```\n\n:::tip\n`hermes config set` 命令会自动将值路由到正确的文件 —— API 密钥保存到 `.env`,其他所有内容保存到 `config.yaml`。\n:::\n\n## 配置优先级\n\n设置按以下顺序解析(优先级从高到低):\n\n1. **CLI 参数** —— 例如 `hermes chat --model anthropic/claude-sonnet-4`(单次调用覆盖)\n2. **`~/.hermes/config.yaml`** —— 所有非机密设置的主配置文件\n3. **`~/.hermes/.env`** —— 环境变量的回退;机密(API 密钥、token、密码)**必须**放这里\n4. **内置默认值** —— 未设置任何内容时的硬编码安全默认值\n\n:::info 经验法则\n机密(API 密钥、bot token、密码)放入 `.env`。其他所有内容(模型、终端后端、压缩设置、内存限制、工具集)放入 `config.yaml`。当两者都设置时,`config.yaml` 对非机密设置优先。\n:::\n\n## 环境变量替换\n\n可以在 `config.yaml` 中使用 `${VAR_NAME}` 语法引用环境变量:\n\n```yaml\nauxiliary:\n vision:\n api_key: ${GOOGLE_API_KEY}\n base_url: ${CUSTOM_VISION_URL}\n\ndelegation:\n api_key: ${DELEGATION_KEY}\n```\n\n单个值中可以有多个引用:`url: \"${HOST}:${PORT}\"`。如果引用的变量未设置,占位符将保持原样(`${UNDEFINED_VAR}` 保持不变)。仅支持 `${VAR}` 语法 —— 裸 `$VAR` 不会被展开。\n\n关于 AI provider 设置(OpenRouter、Anthropic、Copilot、自定义端点、自托管 LLM、回退模型等),请参阅 [AI Providers](/integrations/providers)。\n\n### Provider 超时\n\n可以为 provider 设置 `providers.<id>.request_timeout_seconds` 作为全局请求超时,以及 `providers.<id>.models.<model>.timeout_seconds` 作为特定模型的覆盖值。适用于每种传输方式(OpenAI-wire、原生 Anthropic、Anthropic 兼容)上的主轮次客户端、回退链、凭据轮换后的重建,以及(对于 OpenAI-wire)每请求超时 kwarg —— 因此配置值优先于旧版 `HERMES_API_TIMEOUT` 环境变量。\n\n还可以设置 `providers.<id>.stale_timeout_seconds` 用于非流式陈旧调用检测器,以及 `providers.<id>.models.<model>.stale_timeout_seconds` 作为特定模型的覆盖值。此值优先于旧版 `HERMES_API_CALL_STALE_TIMEOUT` 环境变量。\n\n不设置这些值将保持旧版默认值(`HERMES_API_TIMEOUT=1800`s、`HERMES_API_CALL_STALE_TIMEOUT=90`s、原生 Anthropic 900s)。隐式的非流式 stale 检测会在本地端点上自动禁用,并且会在超大上下文下自动放宽。目前不适用于 AWS Bedrock(`bedrock_converse` 和 AnthropicBedrock SDK 路径均使用 boto3 及其自身的超时配置)。请参阅 [`cli-config.yaml.example`](https://github.com/NousResearch/hermes-agent/blob/main/cli-config.yaml.example) 中的注释示例。\n\n## 终端后端配置\n\nHermes 支持六种终端后端。每种后端决定 agent 的 shell 命令实际在哪里执行 —— 本地机器、Docker 容器、通过 SSH 的远程服务器、Modal 云沙箱(直接或通过 Nous 托管的 gateway)、Daytona 工作区,或 Singularity/Apptainer 容器。\n\n```yaml\nterminal:\n backend: local # local | docker | ssh | modal | daytona | singularity\n cwd: \".\" # Gateway/cron 工作目录(CLI 始终使用启动目录)\n timeout: 180 # 每条命令的超时时间(秒)\n env_passthrough: [] # 转发到沙箱执行的环境变量名(terminal + execute_code)\n singularity_image: \"docker://nikolaik/python-nodejs:python3.11-nodejs20\" # Singularity 后端的容器镜像\n modal_image: \"nikolaik/python-nodejs:python3.11-nodejs20\" # Modal 后端的容器镜像\n daytona_image: \"nikolaik/python-nodejs:python3.11-nodejs20\" # Daytona 后端的容器镜像\n```\n\n对于 Modal 和 Daytona 等云沙箱,`container_persistent: true` 表示 Hermes 将尝试在沙箱重建后保留文件系统状态。这并不保证相同的活跃沙箱、PID 空间或后台进程之后仍在运行。\n\n### 后端概览\n\n| 后端 | 命令运行位置 | 隔离性 | 最适合 |\n|---------|-------------------|-----------|----------|\n| **local** | 直接在您的机器上 | 无 | 开发、个人使用 |\n| **docker** | 单个持久 Docker 容器(跨会话、`/new`、子 agent 共享) | 完全(命名空间、cap-drop) | 安全沙箱、CI/CD |\n| **ssh** | 通过 SSH 的远程服务器 | 网络边界 | 远程开发、强大硬件 |\n| **modal** | Modal 云沙箱 | 完全(云 VM) | 临时云计算、评估 |\n| **daytona** | Daytona 工作区 | 完全(云容器) | 托管云开发环境 |\n| **singularity** | Singularity/Apptainer 容器 | 命名空间(--containall) | HPC 集群、共享机器 |\n\n### Local 后端\n\n默认后端。命令直接在您的机器上运行,无隔离。无需特殊设置。\n\n```yaml\nterminal:\n backend: local\n```\n\n:::warning\nAgent 拥有与您的用户账户相同的文件系统访问权限。使用 `hermes tools` 禁用不需要的工具,或切换到 Docker 进行沙箱隔离。\n:::\n\n### Docker 后端\n\n在具有安全加固的 Docker 容器内运行命令(所有权限已删除、无权限提升、PID 限制)。\n\n**单个持久容器,而非每条命令一个容器。** Hermes 在首次使用时启动一个长期运行的容器,并通过 `docker exec` 将每个终端、文件和 `execute_code` 调用路由到同一容器中 —— 跨会话、`/new`、`/reset` 和 `delegate_task` 子 agent,贯穿 Hermes 进程的整个生命周期。工作目录更改、已安装的包以及 `/workspace` 中的文件会从一次工具调用延续到下一次,就像本地 shell 一样。容器在关闭时停止并删除。详情请参阅下方的**容器生命周期**。\n\n```yaml\nterminal:\n backend: docker\n docker_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n docker_mount_cwd_to_workspace: false # 将启动目录挂载到 /workspace\n docker_run_as_host_user: false # 参见下方\"以宿主用户身份运行容器\"\n docker_forward_env: # 转发到容器的环境变量\n - \"GITHUB_TOKEN\"\n docker_volumes: # 宿主目录挂载\n - \"/home/user/projects:/workspace/projects\"\n - \"/home/user/data:/data:ro\" # :ro 表示只读\n docker_extra_args: # 附加到 `docker run` 的额外标志\n - \"--gpus=all\"\n - \"--network=host\"\n\n # 资源限制\n container_cpu: 1 # CPU 核心数(0 = 不限制)\n container_memory: 5120 # MB(0 = 不限制)\n container_disk: 51200 # MB(需要 XFS+pquota 上的 overlay2)\n container_persistent: true # 跨会话持久化 /workspace 和 /root\n```\n\n**`terminal.docker_extra_args`**(也可通过 `TERMINAL_DOCKER_EXTRA_ARGS='[\"--gpus=all\"]'` 覆盖)允许传递 Hermes 未作为一级键公开的任意 `docker run` 标志 —— `--gpus`、`--network`、`--add-host`、替代 `--security-opt` 覆盖等。每个条目必须是字符串;该列表最后附加到组装好的 `docker run` 调用中,因此可以在需要时覆盖 Hermes 的默认值。请谨慎使用 —— 与沙箱加固(权限删除、`--user`、workspace 绑定挂载)冲突的标志将悄然削弱隔离性。\n\n**要求:** 已安装并运行 Docker Desktop 或 Docker Engine。Hermes 会探测 `$PATH` 以及常见的 macOS 安装位置(`/usr/local/bin/docker`、`/opt/homebrew/bin/docker`、Docker Desktop 应用包)。开箱即用支持 Podman:设置 `HERMES_DOCKER_BINARY=podman`(或完整路径)以在两者都安装时强制使用它。\n\n**容器生命周期:** Hermes 为每个终端和文件工具调用重用单个长期运行的容器(`docker run -d ... sleep 2h`),跨会话、`/new`、`/reset` 和 `delegate_task` 子 agent,贯穿 Hermes 进程的整个生命周期。命令通过带登录 shell 的 `docker exec` 运行,因此工作目录更改、已安装的包以及 `/workspace` 中的文件都会从一次工具调用延续到下一次。容器在 Hermes 关闭时(或空闲清理回收时)停止并删除。\n\n通过 `delegate_task(tasks=[...])` 生成的并行子 agent 共享这一个容器 —— 并发的 `cd`、环境变量修改以及对同一路径的写入会发生冲突。如果子 agent 需要隔离的沙箱,必须通过 `register_task_env_overrides()` 注册每任务镜像覆盖,RL 和基准测试环境(TerminalBench2、HermesSweEnv 等)会自动为其每任务 Docker 镜像执行此操作。\n\n**安全加固:**\n- `--cap-drop ALL`,仅添加回 `DAC_OVERRIDE`、`CHOWN`、`FOWNER`\n- `--security-opt no-new-privileges`\n- `--pids-limit 256`\n- `/tmp`(512MB)、`/var/tmp`(256MB)、`/run`(64MB)的大小限制 tmpfs\n\n**凭据转发:** `docker_forward_env` 中列出的环境变量首先从您的 shell 环境解析,然后回退到 `~/.hermes/.env`。技能也可以声明 `required_environment_variables`,这些变量会自动合并。\n\n### SSH 后端\n\n通过 SSH 在远程服务器上运行命令。使用 ControlMaster 进行连接复用(5 分钟空闲保活)。默认启用持久 shell —— 状态(cwd、环境变量)在命令之间保持。\n\n```yaml\nterminal:\n backend: ssh\n persistent_shell: true # 保持长期运行的 bash 会话(默认:true)\n```\n\n**必需的环境变量:**\n\n```bash\nTERMINAL_SSH_HOST=my-server.example.com\nTERMINAL_SSH_USER=ubuntu\n```\n\n**可选:**\n\n| 变量 | 默认值 | 描述 |\n|----------|---------|-------------|\n| `TERMINAL_SSH_PORT` | `22` | SSH 端口 |\n| `TERMINAL_SSH_KEY` | (系统默认) | SSH 私钥路径 |\n| `TERMINAL_SSH_PERSISTENT` | `true` | 启用持久 shell |\n\n**工作原理:** 使用 `BatchMode=yes` 和 `StrictHostKeyChecking=accept-new` 在初始化时连接。持久 shell 在远程主机上保持单个 `bash -l` 进程存活,通过临时文件进行通信。需要 `stdin_data` 或 `sudo` 的命令会自动回退到单次模式。\n\n### Modal 后端\n\n在 [Modal](https://modal.com) 云沙箱中运行命令。每个任务获得一个具有可配置 CPU、内存和磁盘的隔离 VM。文件系统可以跨会话快照/恢复。\n\n```yaml\nterminal:\n backend: modal\n container_cpu: 1 # CPU 核心数\n container_memory: 5120 # MB(5GB)\n container_disk: 51200 # MB(50GB)\n container_persistent: true # 快照/恢复文件系统\n```\n\n**必需:** `MODAL_TOKEN_ID` + `MODAL_TOKEN_SECRET` 环境变量,或 `~/.modal.toml` 配置文件。\n\n**持久化:** 启用后,沙箱文件系统在清理时快照,并在下次会话时恢复。快照在 `~/.hermes/modal_snapshots.json` 中跟踪。这保留文件系统状态,而非活跃进程、PID 空间或后台任务。\n\n**凭据文件:** 自动从 `~/.hermes/` 挂载(OAuth token 等),并在每条命令前同步。\n\n### Daytona 后端\n\n在 [Daytona](https://daytona.io) 托管工作区中运行命令。支持停止/恢复以实现持久化。\n\n```yaml\nterminal:\n backend: daytona\n container_cpu: 1 # CPU 核心数\n container_memory: 5120 # MB → 转换为 GiB\n container_disk: 10240 # MB → 转换为 GiB(最大 10 GiB)\n container_persistent: true # 停止/恢复而非删除\n```\n\n**必需:** `DAYTONA_API_KEY` 环境变量。\n\n**持久化:** 启用后,沙箱在清理时停止(而非删除),并在下次会话时恢复。沙箱名称遵循 `hermes-{task_id}` 模式。\n\n**磁盘限制:** Daytona 强制执行 10 GiB 最大值。超过此值的请求将被截断并发出警告。\n\n### Singularity/Apptainer 后端\n\n在 [Singularity/Apptainer](https://apptainer.org) 容器中运行命令。专为 Docker 不可用的 HPC 集群和共享机器设计。\n\n```yaml\nterminal:\n backend: singularity\n singularity_image: \"docker://nikolaik/python-nodejs:python3.11-nodejs20\"\n container_cpu: 1 # CPU 核心数\n container_memory: 5120 # MB\n container_persistent: true # 可写覆盖层跨会话持久化\n```\n\n**要求:** `$PATH` 中有 `apptainer` 或 `singularity` 二进制文件。\n\n**镜像处理:** Docker URL(`docker://...`)自动转换为 SIF 文件并缓存。现有 `.sif` 文件直接使用。\n\n**临时目录:** 按顺序解析:`TERMINAL_SCRATCH_DIR` → `TERMINAL_SANDBOX_DIR/singularity` → `/scratch/$USER/hermes-agent`(HPC 惯例)→ `~/.hermes/sandboxes/singularity`。\n\n**隔离:** 使用 `--containall --no-home` 实现完全命名空间隔离,不挂载宿主 home 目录。\n\n### 常见终端后端问题\n\n如果终端命令立即失败或终端工具报告为已禁用:\n\n- **Local** —— 无特殊要求。入门时最安全的默认选项。\n- **Docker** —— 运行 `docker version` 验证 Docker 是否正常工作。如果失败,修复 Docker 或执行 `hermes config set terminal.backend local`。\n- **SSH** —— `TERMINAL_SSH_HOST` 和 `TERMINAL_SSH_USER` 都必须设置。如果缺少任一项,Hermes 会记录清晰的错误。\n- **Modal** —— 需要 `MODAL_TOKEN_ID` 环境变量或 `~/.modal.toml`。运行 `hermes doctor` 检查。\n- **Daytona** —— 需要 `DAYTONA_API_KEY`。Daytona SDK 处理服务器 URL 配置。\n- **Singularity** —— 需要 `$PATH` 中有 `apptainer` 或 `singularity`。HPC 集群上常见。\n\n如有疑问,将 `terminal.backend` 设回 `local` 并首先验证命令在那里运行。\n\n### 拆卸时远程到宿主文件同步\n\n对于 **SSH**、**Modal** 和 **Daytona** 后端(agent 的工作树位于与运行 Hermes 的宿主不同的机器上),Hermes 跟踪 agent 在远程沙箱中触及的文件,并在会话拆卸/沙箱清理时,将修改的文件**同步回宿主**,存放在 `~/.hermes/cache/remote-syncs/<session-id>/` 下。\n\n- 触发时机:会话关闭、`/new`、`/reset`、gateway 消息超时、子 agent 使用远程后端时 `delegate_task` 子 agent 完成。\n- 覆盖 agent 修改的整个树,而不仅仅是它明确打开的文件。添加、编辑和删除都会被捕获。\n- 远程沙箱可能在您查找时已被拆除;本地 `~/.hermes/cache/remote-syncs/…` 副本是 agent 更改内容的权威记录。\n- 大型二进制输出(模型检查点、原始数据集)按大小限制 —— 同步跳过超过 `file_sync_max_mb`(默认 `100`)的文件。如果您期望更大的工件返回,请调高该值。\n\n```yaml\nterminal:\n file_sync_max_mb: 100 # 默认 —— 同步最大 100 MB 的文件\n file_sync_enabled: true # 默认 —— 设为 false 可完全跳过同步\n```\n\n这是从会话结束后被销毁的临时云沙箱中恢复结果的方式,无需告诉 agent 显式地 `scp` 或 `modal volume put` 每个工件。\n\n### Docker 卷挂载\n\n使用 Docker 后端时,`docker_volumes` 允许您与容器共享宿主目录。每个条目使用标准 Docker `-v` 语法:`host_path:container_path[:options]`。\n\n```yaml\nterminal:\n backend: docker\n docker_volumes:\n - \"/home/user/projects:/workspace/projects\" # 读写(默认)\n - \"/home/user/datasets:/data:ro\" # 只读\n - \"/home/user/.hermes/cache/documents:/output\" # Gateway 可见的导出\n```\n\n适用于:\n- **向 agent 提供文件**(数据集、配置、参考代码)\n- **从 agent 接收文件**(生成的代码、报告、导出)\n- **共享工作区**,您和 agent 都访问相同的文件\n\n如果您使用消息 gateway 并希望 agent 通过 `MEDIA:/...` 发送生成的文件,建议使用专用的宿主可见导出挂载,例如 `/home/user/.hermes/cache/documents:/output`。\n\n- 在 Docker 中将文件写入 `/output/...`\n- 在 `MEDIA:` 中发出**宿主路径**,例如:`MEDIA:/home/user/.hermes/cache/documents/report.txt`\n- **不要**发出 `/workspace/...` 或 `/output/...`,除非该确切路径在宿主上对 gateway 进程也存在\n\n:::warning\nYAML 重复键会静默覆盖之前的键。如果您已有 `docker_volumes:` 块,请将新挂载合并到同一列表中,而不是在文件后面再添加一个 `docker_volumes:` 键。\n:::\n\n也可以通过环境变量设置:`TERMINAL_DOCKER_VOLUMES='[\"/host:/container\"]'`(JSON 数组)。\n\n### Docker 凭据转发\n\n默认情况下,Docker 终端会话不继承任意宿主凭据。如果您需要在容器内使用特定 token,请将其添加到 `terminal.docker_forward_env`。\n\n```yaml\nterminal:\n backend: docker\n docker_forward_env:\n - \"GITHUB_TOKEN\"\n - \"NPM_TOKEN\"\n```\n\nHermes 首先从您当前的 shell 解析每个列出的变量,然后回退到通过 `hermes config set` 保存的 `~/.hermes/.env`。\n\n:::warning\n`docker_forward_env` 中列出的任何内容都会对容器内运行的命令可见。只转发您愿意暴露给终端会话的凭据。\n:::\n\n### 以宿主用户身份运行容器\n\n默认情况下,Docker 容器以 `root`(UID 0)身份运行。在 `/workspace` 或其他绑定挂载中创建的文件在宿主上归 root 所有,因此会话结束后您必须 `sudo chown` 才能从宿主编辑器编辑它们。`terminal.docker_run_as_host_user` 标志解决了这个问题:\n\n```yaml\nterminal:\n backend: docker\n docker_run_as_host_user: true # 默认:false\n```\n\n启用后,Hermes 将 `--user $(id -u):$(id -g)` 附加到 `docker run` 命令,使写入绑定挂载目录(`/workspace`、`/root`、`docker_volumes` 中的任何内容)的文件归您的宿主用户所有,而非 root。权衡:容器将无法再 `apt install` 或写入 `/root/.npm` 等 root 拥有的路径 —— 如果您同时需要这两者,请使用 `HOME` 归非 root 用户所有的基础镜像(或在镜像构建时添加所需工具)。\n\n保持 `false`(默认)以获得向后兼容的行为。当您的工作流主要是\"编辑挂载的宿主文件\"且厌倦了 `sudo chown -R` 时,请开启此选项。\n\n### 可选:将启动目录挂载到 `/workspace`\n\nDocker 沙箱默认保持隔离。Hermes **不会**将您当前的宿主工作目录传入容器,除非您明确选择加入。\n\n在 `config.yaml` 中启用:\n\n```yaml\nterminal:\n backend: docker\n docker_mount_cwd_to_workspace: true\n```\n\n启用后:\n- 如果您从 `~/projects/my-app` 启动 Hermes,该宿主目录将绑定挂载到 `/workspace`\n- Docker 后端从 `/workspace` 开始\n- 文件工具和终端命令都能看到相同的挂载项目\n\n禁用时,`/workspace` 保持沙箱所有,除非您通过 `docker_volumes` 显式挂载内容。\n\n安全权衡:\n- `false` 保留沙箱边界\n- `true` 使沙箱直接访问您启动 Hermes 的目录\n\n仅在您有意希望容器处理实时宿主文件时才选择加入。\n\n### 持久 Shell\n\n默认情况下,每条终端命令在其自己的子进程中运行 —— 工作目录、环境变量和 shell 变量在命令之间重置。启用**持久 shell** 后,单个长期运行的 bash 进程在 `execute()` 调用之间保持存活,使状态在命令之间保持。\n\n这对 **SSH 后端**最有用,它还消除了每条命令的连接开销。持久 shell **对 SSH 默认启用**,对本地后端禁用。\n\n```yaml\nterminal:\n persistent_shell: true # 默认 —— 为 SSH 启用持久 shell\n```\n\n禁用:\n\n```bash\nhermes config set terminal.persistent_shell false\n```\n\n**跨命令保持的内容:**\n- 工作目录(`cd /tmp` 对下一条命令生效)\n- 导出的环境变量(`export FOO=bar`)\n- Shell 变量(`MY_VAR=hello`)\n\n**优先级:**\n\n| 级别 | 变量 | 默认值 |\n|-------|----------|---------|\n| 配置 | `terminal.persistent_shell` | `true` |\n| SSH 覆盖 | `TERMINAL_SSH_PERSISTENT` | 遵循配置 |\n| Local 覆盖 | `TERMINAL_LOCAL_PERSISTENT` | `false` |\n\n每个后端的环境变量具有最高优先级。如果您也想在本地后端使用持久 shell:\n\n```bash\nexport TERMINAL_LOCAL_PERSISTENT=true\n```\n\n:::note\n需要 `stdin_data` 或 sudo 的命令会自动回退到单次模式,因为持久 shell 的 stdin 已被 IPC 协议占用。\n:::\n\n有关每个后端的详细信息,请参阅[代码执行](features/code-execution.md)和 [README 的终端部分](features/tools.md)。\n\n## 技能设置\n\n技能可以通过其 SKILL.md frontmatter 声明自己的配置设置。这些是非机密值(路径、偏好、域设置),存储在 `config.yaml` 的 `skills.config` 命名空间下。\n\n```yaml\nskills:\n config:\n myplugin:\n path: ~/myplugin-data # 示例 —— 每个技能定义自己的键\n```\n\n**技能设置的工作原理:**\n\n- `hermes config migrate` 扫描所有已启用的技能,找到未配置的设置,并提供提示\n- `hermes config show` 在\"技能设置\"下显示所有技能设置及其所属技能\n- 技能加载时,其解析的配置值会自动注入到技能上下文中\n\n**手动设置值:**\n\n```bash\nhermes config set skills.config.myplugin.path ~/myplugin-data\n```\n\n有关在您自己的技能中声明配置设置的详细信息,请参阅[创建技能 — 配置设置](/developer-guide/creating-skills#config-settings-configyaml)。\n\n### Agent 创建技能写入的守卫\n\n当 agent 使用 `skill_manage` 创建、编辑、修补或删除技能时,Hermes 可以选择扫描新/更新的内容以查找危险关键字模式(凭据收集、明显的 prompt 注入、数据外泄指令)。扫描器**默认关闭** —— 合法触及 `~/.ssh/` 或提及 `$OPENAI_API_KEY` 的真实 agent 工作流触发启发式规则过于频繁。如果您希望扫描器在 agent 的技能写入落地前提示您,请重新开启:\n\n```yaml\nskills:\n guard_agent_created: true # 默认:false\n```\n\n开启后,任何被标记的 `skill_manage` 写入都会以审批提示的形式出现,并附带扫描器的理由。接受的写入落地;拒绝的写入向 agent 返回解释性错误。\n\n## 内存配置\n\n```yaml\nmemory:\n memory_enabled: true\n user_profile_enabled: true\n memory_char_limit: 2200 # ~800 tokens\n user_char_limit: 1375 # ~500 tokens\n```\n\n## 文件读取安全\n\n控制单次 `read_file` 调用可以返回多少内容。超过限制的读取将被拒绝,并向 agent 返回错误,提示使用 `offset` 和 `limit` 读取较小范围。这可以防止单次读取压缩的 JS 包或大型数据文件时淹没上下文窗口。\n\n```yaml\nfile_read_max_chars: 100000 # 默认 —— ~25-35K tokens\n```\n\n如果您使用具有大上下文窗口的模型并经常读取大文件,请调高此值。对于小上下文模型,请降低以保持读取高效:\n\n```yaml\n# 大上下文模型(200K+)\nfile_read_max_chars: 200000\n\n# 小型本地模型(16K 上下文)\nfile_read_max_chars: 30000\n```\n\nAgent 还会自动去重文件读取 —— 如果同一文件区域被读取两次且文件未更改,则返回轻量级存根而不是重新发送内容。这在上下文压缩后重置,以便 agent 在内容被摘要后可以重新读取文件。\n\n## 工具输出截断限制\n\n三个相关的上限控制工具在 Hermes 截断之前可以返回多少原始输出:\n\n```yaml\ntool_output:\n max_bytes: 50000 # 终端输出上限(字符)\n max_lines: 2000 # read_file 分页上限\n max_line_length: 2000 # read_file 行号视图中的每行上限\n```\n\n- **`max_bytes`** —— 当 `terminal` 命令产生超过此字符数的合并 stdout/stderr 时,Hermes 保留前 40% 和后 60%,并在中间插入 `[OUTPUT TRUNCATED]` 通知。默认 `50000`(典型分词器约 12-15K tokens)。\n- **`max_lines`** —— 单次 `read_file` 调用的 `limit` 参数上限。超过此值的请求将被截断,以防单次读取淹没上下文窗口。默认 `2000`。\n- **`max_line_length`** —— `read_file` 发出行号视图时应用的每行上限。超过此长度的行将被截断为此字符数,后跟 `... [truncated]`。默认 `2000`。\n\n对于具有大上下文窗口且每次调用可以承受更多原始输出的模型,请调高限制。对于小上下文模型,请降低以保持工具结果紧凑:\n\n```yaml\n# 大上下文模型(200K+)\ntool_output:\n max_bytes: 150000\n max_lines: 5000\n\n# 小型本地模型(16K 上下文)\ntool_output:\n max_bytes: 20000\n max_lines: 500\n```\n\n## 全局工具集禁用\n\n要在 CLI 和每个 gateway 平台上统一禁用特定工具集,请在 `agent.disabled_toolsets` 下列出其名称:\n\n```yaml\nagent:\n disabled_toolsets:\n - memory # 隐藏内存工具 + MEMORY_GUIDANCE 注入\n - web # 任何地方都不使用 web_search / web_extract\n```\n\n这在每个平台的工具配置(由 `hermes tools` 写入的 `platform_toolsets`)**之后**应用,因此此处列出的工具集始终被删除 —— 即使平台的已保存配置仍然列出它。当您希望有一个\"到处关闭 X\"的单一开关而不是编辑 `hermes tools` UI 中的 15+ 个平台行时,请使用此选项。\n\n留空列表或省略键不会产生任何效果。\n\n## Git Worktree 隔离\n\n启用隔离的 git worktree,以便在同一仓库上并行运行多个 agent:\n\n```yaml\nworktree: true # 始终创建 worktree(与 hermes -w 相同)\n# worktree: false # 默认 —— 仅在传递 -w 标志时\n```\n\n启用后,每个 CLI 会话在 `.worktrees/` 下创建一个带有自己分支的新 worktree。Agent 可以编辑文件、提交、推送和创建 PR,而不会相互干扰。干净的 worktree 在退出时删除;脏的 worktree 保留以供手动恢复。\n\n您还可以通过仓库根目录中的 `.worktreeinclude` 列出要复制到 worktree 的 gitignore 文件:\n\n```\n# .worktreeinclude\n.env\n.venv/\nnode_modules/\n```\n\n## 上下文压缩\n\nHermes 自动压缩长对话以保持在模型的上下文窗口内。压缩摘要器是一个单独的 LLM 调用 —— 您可以将其指向任何 provider 或端点。\n\n所有压缩设置都在 `config.yaml` 中(无环境变量)。\n\n### 完整参考\n\n```yaml\ncompression:\n enabled: true # 开启/关闭压缩\n threshold: 0.50 # 在上下文限制的此百分比时压缩\n target_ratio: 0.20 # 保留为最近尾部的阈值分数\n protect_last_n: 20 # 保持未压缩的最少最近消息数\n hygiene_hard_message_limit: 5000 # Gateway 安全阀 —— 见下文\n\n# 摘要模型/provider 在 auxiliary: 下配置:\nauxiliary:\n compression:\n model: \"\" # 空 = 使用主聊天模型。覆盖为例如 \"google/gemini-3-flash-preview\" 以获得更便宜/更快的压缩。\n provider: \"auto\" # Provider:\"auto\"、\"openrouter\"、\"nous\"、\"codex\"、\"main\" 等\n base_url: null # 自定义 OpenAI 兼容端点(覆盖 provider)\n```\n\n:::info 旧版配置迁移\n带有 `compression.summary_model`、`compression.summary_provider` 和 `compression.summary_base_url` 的旧版配置在首次加载时自动迁移到 `auxiliary.compression.*`(配置版本 17)。无需手动操作。\n:::\n\n`hygiene_hard_message_limit` 是仅限 gateway 的**预压缩安全阀**。它的存在是为了打破一个死循环:当超大会话的 API 调用持续断开时,gateway 永远收不到 token 使用数据,基于 token 的阈值因此无法触发,于是 transcript 持续增长、断开愈发严重。这个基于消息数的下限仅凭消息数量触发(无论 API 是否失败,消息数始终已知),强制压缩以恢复会话。默认 `5000` —— 远高于任何正常会话,包括做数千次短轮次的大上下文(1M+)模型,它们早就在 token 阈值处压缩了。对于异常平台可调得更高;要强制更积极的压缩则调低。在运行中的 gateway 上编辑此值将在下一条消息时生效(见下文)。\n\n:::tip Gateway 热重载压缩和上下文长度\n从最近的版本开始,在运行中的 gateway 上编辑 `config.yaml` 中的 `model.context_length` 或任何 `compression.*` 键将在下一条消息时生效 —— 无需 gateway 重启、`/reset` 或会话轮换。缓存的 agent 签名包含这些键,因此 gateway 在检测到更改时会透明地重建 agent。API 密钥和工具/技能配置仍需要通常的重载路径。\n:::\n\n### 常见设置\n\n**默认(自动检测)—— 无需配置:**\n```yaml\ncompression:\n enabled: true\n threshold: 0.50\n```\n使用您的主 provider 和主模型。如果您希望在比主聊天模型更便宜的模型上进行压缩,请覆盖每任务(例如 `auxiliary.compression.provider: openrouter` + `model: google/gemini-2.5-flash`)。\n\n**强制特定 provider**(基于 OAuth 或 API 密钥):\n```yaml\nauxiliary:\n compression:\n provider: nous\n model: gemini-3-flash\n```\n适用于任何 provider:`nous`、`openrouter`、`codex`、`anthropic`、`main` 等。\n\n**自定义端点**(自托管、Ollama、zai、DeepSeek 等):\n```yaml\nauxiliary:\n compression:\n model: glm-4.7\n base_url: https://api.z.ai/api/coding/paas/v4\n```\n指向自定义 OpenAI 兼容端点。使用 `OPENAI_API_KEY` 进行认证。\n\n### 三个旋钮的交互方式\n\n| `auxiliary.compression.provider` | `auxiliary.compression.base_url` | 结果 |\n|---------------------|---------------------|--------|\n| `auto`(默认) | 未设置 | 自动检测最佳可用 provider |\n| `nous` / `openrouter` / 等 | 未设置 | 强制使用该 provider,使用其认证 |\n| 任意 | 已设置 | 直接使用自定义端点(忽略 provider) |\n\n:::warning 摘要模型上下文长度要求\n摘要模型**必须**具有至少与您的主 agent 模型一样大的上下文窗口。压缩器将对话的完整中间部分发送给摘要模型 —— 如果该模型的上下文窗口小于主模型的,摘要调用将因上下文长度错误而失败。发生这种情况时,中间轮次将**在没有摘要的情况下被丢弃**,静默丢失对话上下文。如果您覆盖模型,请验证其上下文长度满足或超过您的主模型。\n:::\n\n## 上下文引擎\n\n上下文引擎控制在接近模型 token 限制时如何管理对话。内置的 `compressor` 引擎使用有损摘要(参见[上下文压缩](/developer-guide/context-compression-and-caching))。插件引擎可以用替代策略替换它。\n\n```yaml\ncontext:\n engine: \"compressor\" # 默认 —— 内置有损摘要\n```\n\n使用插件引擎(例如,用于无损上下文管理的 LCM):\n\n```yaml\ncontext:\n engine: \"lcm\" # 必须与插件名称匹配\n```\n\n插件引擎**永远不会自动激活** —— 您必须将 `context.engine` 显式设置为插件名称。可用引擎可以通过 `hermes plugins` → Provider Plugins → Context Engine 浏览和选择。\n\n有关内存插件的类似单选系统,请参阅[内存 Providers](/user-guide/features/memory-providers)。\n\n## 迭代预算\n\n当 agent 在处理具有许多工具调用的复杂任务时,它可能会耗尽其迭代预算(默认:500 轮)。Hermes **不会**在任务中途注入压力警告 —— 早期版本会在预算达到 70%/90% 时警告模型,这会导致模型过早放弃复杂任务,该机制已于 2026 年 4 月移除。\n\n取而代之的是,当预算真正耗尽(500/500)时,Hermes 注入一条消息要求模型收尾,并允许一次**宽限调用**以便其给出最终响应。如果该宽限调用仍未产生文本,则会要求 agent 总结已完成的工作。\n\n```yaml\nagent:\n max_turns: 500 # 每次对话轮次的最大迭代次数(默认:500)\n api_max_retries: 3 # 回退启动前每个 provider 的重试次数(默认:3)\n```\n\n当迭代预算完全耗尽时,CLI 向用户显示通知:`⚠ Iteration budget reached (500/500) — response may be incomplete`。\n\n`agent.api_max_retries` 控制 Hermes 在回退 provider 切换启动**之前**对瞬时错误(速率限制、连接断开、5xx)重试 provider API 调用的次数。默认为 `3` —— 总共四次尝试。如果您配置了[回退 providers](/user-guide/features/fallback-providers) 并希望更快地故障转移,请将其降至 `0`,这样主 provider 上的第一个瞬时错误会立即切换到回退,而不是对不稳定的端点进行重试。\n\n### API 超时\n\nHermes 对流式传输有单独的超时层,以及用于非流式调用的陈旧检测器。陈旧检测器仅在您将其保留为隐式默认值时才会自动调整本地 provider。\n\n| 超时 | 默认值 | 本地 providers | 配置/环境变量 |\n|---------|---------|----------------|--------------|\n| Socket 读取超时 | 120s | 自动提升至 1800s | `HERMES_STREAM_READ_TIMEOUT` |\n| 陈旧流检测 | 180s | 自动禁用 | `HERMES_STREAM_STALE_TIMEOUT` |\n| 陈旧非流检测 | 300s | 保持隐式时自动禁用 | `providers.<id>.stale_timeout_seconds` 或 `HERMES_API_CALL_STALE_TIMEOUT` |\n| API 调用(非流式) | 1800s | 不变 | `providers.<id>.request_timeout_seconds` / `timeout_seconds` 或 `HERMES_API_TIMEOUT` |\n\n**Socket 读取超时**控制 httpx 等待 provider 下一个数据块的时间。本地 LLM 在大上下文上预填充可能需要几分钟才能产生第一个 token,因此当 Hermes 检测到本地端点时,会将此值提升至 30 分钟。如果您显式设置 `HERMES_STREAM_READ_TIMEOUT`,无论端点检测如何,始终使用该值。\n\n**陈旧流检测**终止接收 SSE 保活 ping 但没有实际内容的连接。对于本地 providers,这完全禁用,因为它们在预填充期间不发送保活 ping。\n\n**陈旧非流检测**终止长时间没有响应的非流式调用。默认情况下,Hermes 在本地端点上禁用此功能,以避免长时间预填充期间的误报。如果您显式设置 `providers.<id>.stale_timeout_seconds`、`providers.<id>.models.<model>.stale_timeout_seconds` 或 `HERMES_API_CALL_STALE_TIMEOUT`,即使在本地端点上也会遵守该显式值。\n\n## 上下文压力警告\n\n与迭代预算压力分开,上下文压力跟踪对话距**压缩阈值**有多近 —— 即上下文压缩触发以摘要旧消息的点。这有助于您和 agent 了解对话何时变长。\n\n| 进度 | 级别 | 发生的事情 |\n|----------|-------|-------------|\n| **≥ 60%** 到阈值 | 信息 | CLI 显示青色进度条;gateway 发送信息通知 |\n| **≥ 85%** 到阈值 | 警告 | CLI 显示粗体黄色进度条;gateway 警告压缩即将发生 |\n\n在 CLI 中,上下文压力在工具输出流中显示为进度条:\n\n```\n ◐ context ████████████░░░░░░░░ 62% to compaction 48k threshold (50%) · approaching compaction\n```\n\n在消息平台上,发送纯文本通知:\n\n```\n◐ Context: ████████████░░░░░░░░ 62% to compaction (threshold: 50% of window).\n```\n\n如果自动压缩被禁用,警告会告诉您上下文可能被截断。\n\n上下文压力是自动的 —— 无需配置。它纯粹作为面向用户的通知触发,不修改消息流或向模型上下文注入任何内容。\n\n## 凭据池策略\n\n当您为同一 provider 拥有多个 API 密钥或 OAuth token 时,配置轮换策略:\n\n```yaml\ncredential_pool_strategies:\n openrouter: round_robin # 均匀循环使用密钥\n anthropic: least_used # 始终选择使用最少的密钥\n```\n\n选项:`fill_first`(默认)、`round_robin`、`least_used`、`random`。完整文档请参阅[凭据池](/user-guide/features/credential-pools)。\n\n## Prompt 缓存\n\n当活跃 provider 支持时,Hermes 自动开启跨会话 prompt 缓存 —— 无需用户配置。\n\n对于**原生 Anthropic**、**OpenRouter** 和 **Nous Portal** 上的 Claude,Hermes 在系统提示词和技能块上附加带有 1 小时 TTL(`ttl: \"1h\"`)的 `cache_control` 断点。在新鲜的一小时内首次发送时按完整输入费率计费;同一小时内任何会话的后续发送以折扣缓存读取费率从缓存中提取。这意味着系统提示词、加载的技能内容以及任何长上下文包含的早期部分在第一个小时内跨 `hermes` 会话和分叉子 agent 被重用。\n\nQwen Cloud(阿里巴巴 DashScope)上游将缓存 TTL 限制为 5 分钟,因此 Hermes 在那里使用 5 分钟断点 TTL。其他通过第三方的 Claude 路径(AWS Bedrock、Azure Foundry)回退到 provider 自己的缓存默认值。xAI Grok 使用单独的会话固定对话 ID 机制 —— 参阅 [xAI prompt 缓存](/integrations/providers#xai-grok--responses-api--prompt-caching)。\n\n不存在禁用此功能的旋钮 —— 缓存始终开启,即使在单轮对话中也能节省费用,因为仅系统提示词就占输入 token 数的相当大比例。\n\n## 辅助模型\n\nHermes 使用\"辅助\"模型处理图像分析、网页摘要、浏览器截图分析、会话标题生成和上下文压缩等附带任务。默认情况下(`auxiliary.*.provider: \"auto\"`),Hermes 将每个辅助任务路由到您的**主聊天模型** —— 与您在 `hermes model` 中选择的相同 provider/模型。您无需配置任何内容即可开始,但请注意,在昂贵的推理模型(Opus、MiniMax M2.7 等)上,辅助任务会增加显著成本。如果您希望无论主模型如何都使用便宜且快速的附带任务,请显式设置 `auxiliary.<task>.provider` 和 `auxiliary.<task>.model`(例如,在 OpenRouter 上使用 Gemini Flash 进行视觉和网页提取)。\n\n:::note 为什么 \"auto\" 使用您的主模型\n早期版本将聚合器用户(OpenRouter、Nous Portal)分流到便宜的 provider 端默认值。这令人惊讶 —— 付费购买聚合器订阅的用户会看到不同的模型处理其辅助流量。`auto` 现在对所有人使用主模型,`config.yaml` 中的每任务覆盖仍然优先(见下方[完整辅助配置参考](#full-auxiliary-config-reference))。\n:::\n\n### 交互式配置辅助模型\n\n无需手动编辑 YAML,运行 `hermes model` 并从菜单中选择**\"配置辅助模型\"**。您将获得交互式的每任务选择器:\n\n```\n$ hermes model\n→ Configure auxiliary models\n\n[ ] vision currently: auto / main model\n[ ] web_extract currently: auto / main model\n[ ] title_generation currently: openrouter / google/gemini-3-flash-preview\n[ ] compression currently: auto / main model\n[ ] approval currently: auto / main model\n[ ] triage_specifier currently: auto / main model\n[ ] kanban_decomposer currently: auto / main model\n[ ] profile_describer currently: auto / main model\n```\n\n选择任务,选择 provider(OAuth 流程打开浏览器;API 密钥 provider 提示输入),选择模型。更改持久化到 `config.yaml` 中的 `auxiliary.<task>.*`。与主模型选择器相同的机制 —— 无需学习额外语法。\n\n### 视频教程\n\n<div style={{position: 'relative', width: '100%', aspectRatio: '16 / 9', marginBottom: '1.5rem'}}>\n <iframe\n src=\"https://www.youtube.com/embed/NoF-YajElIM\"\n title=\"Hermes Agent — Auxiliary Models Tutorial\"\n style={{position: 'absolute', top: 0, left: 0, width: '100%', height: '100%', border: 0}}\n allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\"\n allowFullScreen\n />\n</div>\n\n### 通用配置模式\n\nHermes 中的每个模型槽位 —— 辅助任务、压缩、回退 —— 使用相同的三个旋钮:\n\n| 键 | 作用 | 默认值 |\n|-----|-------------|---------|\n| `provider` | 用于认证和路由的 provider | `\"auto\"` |\n| `model` | 请求的模型 | provider 的默认值 |\n| `base_url` | 自定义 OpenAI 兼容端点(覆盖 provider) | 未设置 |\n\n当设置 `base_url` 时,Hermes 忽略 provider 并直接调用该端点(使用 `api_key` 或 `OPENAI_API_KEY` 进行认证)。当仅设置 `provider` 时,Hermes 使用该 provider 的内置认证和基础 URL。\n\n辅助任务的可用 providers:`auto`、`main`,以及[provider 注册表](/reference/environment-variables)中的任何 provider —— `openrouter`、`nous`、`openai-codex`、`copilot`、`copilot-acp`、`anthropic`、`gemini`、`qwen-oauth`、`zai`、`kimi-coding`、`kimi-coding-cn`、`minimax`、`minimax-cn`、`minimax-oauth`、`deepseek`、`nvidia`、`xai`、`xai-oauth`、`ollama-cloud`、`alibaba`、`bedrock`、`huggingface`、`arcee`、`xiaomi`、`kilocode`、`opencode-zen`、`opencode-go`、`azure-foundry` —— 或您 `custom_providers` 列表中任何命名的自定义 provider(例如 `provider: \"beans\"`)。\n\n:::tip MiniMax OAuth\n`minimax-oauth` 通过浏览器 OAuth 登录(无需 API 密钥)。运行 `hermes model` 并选择 **MiniMax (OAuth)** 进行认证。辅助任务自动使用 `MiniMax-M2.7-highspeed`。参阅 [MiniMax OAuth 指南](../guides/minimax-oauth.md)。\n:::\n\n:::tip xAI Grok OAuth\n`xai-oauth` 通过浏览器 OAuth 为 SuperGrok 和 X Premium+ 订阅者登录(无需 API 密钥)。运行 `hermes model` 并选择 **xAI Grok OAuth (SuperGrok / Premium+)** 进行认证。相同的 OAuth token 可重用于每个直接到 xAI 的接口(聊天、辅助任务、TTS、图像生成、视频生成、转录)。参阅 [xAI Grok OAuth 指南](../guides/xai-grok-oauth.md),如果 Hermes 在远程主机上,请参阅 [SSH/远程主机上的 OAuth](../guides/oauth-over-ssh.md)。\n:::\n\n:::warning `\"main\"` 仅用于辅助任务\n`\"main\"` provider 选项表示\"使用我的主 agent 使用的任何 provider\" —— 它仅在 `auxiliary:`、`compression:` 和 `fallback_model:` 配置中有效。它**不是**顶级 `model.provider` 设置的有效值。如果您使用自定义 OpenAI 兼容端点,请在 `model:` 部分设置 `provider: custom`。所有主模型 provider 选项请参阅 [AI Providers](/integrations/providers)。\n:::\n\n### 完整辅助配置参考\n\n```yaml\nauxiliary:\n # 图像分析(vision_analyze 工具 + 浏览器截图)\n vision:\n provider: \"auto\" # \"auto\"、\"openrouter\"、\"nous\"、\"codex\"、\"main\" 等\n model: \"\" # 例如 \"openai/gpt-4o\"、\"google/gemini-2.5-flash\"\n base_url: \"\" # 自定义 OpenAI 兼容端点(覆盖 provider)\n api_key: \"\" # base_url 的 API 密钥(回退到 OPENAI_API_KEY)\n timeout: 120 # 秒 —— LLM API 调用超时;视觉负载需要宽裕的超时\n download_timeout: 30 # 秒 —— 图像 HTTP 下载;慢速连接请增加\n\n # 网页摘要 + 浏览器页面文本提取\n web_extract:\n provider: \"auto\"\n model: \"\" # 例如 \"google/gemini-2.5-flash\"\n base_url: \"\"\n api_key: \"\"\n timeout: 360 # 秒(6 分钟)—— 每次尝试的 LLM 摘要\n\n # 危险命令审批分类器\n approval:\n provider: \"auto\"\n model: \"\"\n base_url: \"\"\n api_key: \"\"\n timeout: 30 # 秒\n\n # 上下文压缩超时(与 compression.* 配置分开)\n compression:\n timeout: 120 # 秒 —— 压缩摘要长对话,需要更多时间\n # fallback_chain: # 可选 —— 发生速率限制/连接故障时尝试的 provider\n # - provider: nous\n # model: deepseek/deepseek-chat\n # - provider: openrouter\n # model: google/gemini-2.5-flash\n # base_url: \"\"\n # api_key: \"\"\n\n # 技能中心 —— 技能匹配和搜索\n skills_hub:\n provider: \"auto\"\n model: \"\"\n base_url: \"\"\n api_key: \"\"\n timeout: 30\n\n # MCP 工具调度\n mcp:\n provider: \"auto\"\n model: \"\"\n base_url: \"\"\n api_key: \"\"\n timeout: 30\n\n # Kanban 分类规格说明器 —— `hermes kanban specify <id>`(或\n # 仪表板上 Triage 列卡片的 ✨ Specify 按钮)使用此\n # 槽位将单行描述扩展为具体规格并将\n # 任务提升到 `todo`。便宜快速的模型在这里效果很好;规格扩展\n # 很短,不需要推理深度。\n triage_specifier:\n provider: \"auto\"\n model: \"\"\n base_url: \"\"\n api_key: \"\"\n timeout: 120\n```\n\n:::tip\n每个辅助任务都有可配置的 `timeout`(秒)。默认值:vision 120s、web_extract 360s、approval 30s、compression 120s。如果您为辅助任务使用慢速本地模型,请增加这些值。Vision 还有单独的 `download_timeout`(默认 30s)用于 HTTP 图像下载 —— 对于慢速连接或自托管图像服务器,请增加此值。\n:::\n\n:::info\n上下文压缩有自己的 `compression:` 块用于阈值,以及 `auxiliary.compression:` 块用于模型/provider 设置 —— 参阅上方的[上下文压缩](#context-compression)。主备用链使用顶层的 `fallback_providers:` 列表 —— 参阅[备用提供商](/integrations/providers#fallback-providers)。三者都遵循相同的 provider/model/base_url 模式。\n:::\n\n### 辅助任务的每任务回退链\n\n每个辅助任务都可以选择性地定义一个 `fallback_chain` —— 一个 provider/model 条目列表,当主要辅助 provider 因速率限制、网络连接问题或付费限制而失败时,Hermes 会尝试使用该列表:\n\n```yaml\nauxiliary:\n compression:\n provider: openrouter\n model: openai/gpt-4o-mini\n fallback_chain:\n - provider: nous\n model: deepseek/deepseek-chat\n - provider: openrouter\n model: google/gemini-2.5-flash\n```\n\n当主要辅助 provider(`openrouter` / `openai/gpt-4o-mini`)返回速率限制、连接超时或需要付费错误时,Hermes 将依次遍历 `fallback_chain`。它会跳过 provider 与已失败 provider 相同的条目,并尝试每个剩余条目,直到有一个成功或该链耗尽。如果所有回退都失败,Hermes 会回退到主 agent 模型作为最终的安全网。\n\n每个条目支持与任何辅助任务配置相同的三个旋钮:\n\n| 键 | 描述 |\n|-----|-------------|\n| `provider` | Provider 名称(`nous`、`openrouter`、`anthropic`、`gemini`、`main` 等) |\n| `model` | 该 provider 的模型名称 |\n| `base_url` | (可选)自定义 OpenAI 兼容端点 |\n\n`fallback_chain` 适用于任何辅助任务 —— `compression`、`vision`、`web_extract`、`approval`、`skills_hub`、`mcp` 等。\n\n### OpenRouter 路由和辅助任务的 Pareto Code\n\n当辅助任务解析到 OpenRouter(显式或通过 `provider: \"main\"` 而您的主 agent 在 OpenRouter 上)时,主 agent 的 `provider_routing` 和 `openrouter.min_coding_score` 设置**不会传播** —— 按设计,每个辅助任务是独立的。要为特定辅助任务设置 OpenRouter provider 偏好或使用 [Pareto Code 路由器](/integrations/providers#openrouter-pareto-code-router),请通过 `extra_body` 按任务设置:\n\n```yaml\nauxiliary:\n compression:\n provider: openrouter\n model: openrouter/pareto-code # 为此任务使用 Pareto Code 路由器\n extra_body:\n provider: # OpenRouter provider 路由偏好\n order: [anthropic, google] # 按此顺序尝试这些 providers\n sort: throughput # 或 \"price\" | \"latency\"\n # only: [anthropic] # 限制到特定 provider\n # ignore: [deepinfra] # 排除特定 providers\n plugins: # OpenRouter Pareto Code 路由器旋钮\n - id: pareto-router\n min_coding_score: 0.5 # 0.0–1.0;越高 = 更强的编码能力\n```\n\n形状与 OpenRouter 在聊天补全请求体中接受的内容一致。Hermes 原样转发整个 `extra_body`,因此 [openrouter.ai/docs](https://openrouter.ai/docs) 中记录的任何其他 OpenRouter 请求体字段都以相同方式工作。\n\n### 更改视觉模型\n\n使用 GPT-4o 而非 Gemini Flash 进行图像分析:\n\n```yaml\nauxiliary:\n vision:\n model: \"openai/gpt-4o\"\n```\n\n或通过环境变量(在 `~/.hermes/.env` 中):\n\n```bash\nAUXILIARY_VISION_MODEL=openai/gpt-4o\n```\n\n### Provider 选项\n\n这些选项适用于**辅助任务配置**(`auxiliary:`、`compression:`、`fallback_model:`),而非您的主 `model.provider` 设置。\n\n| Provider | 描述 | 要求 |\n|----------|-------------|-------------|\n| `\"auto\"` | 最佳可用(默认)。Vision 尝试 OpenRouter → Nous → Codex。 | — |\n| `\"openrouter\"` | 强制 OpenRouter —— 路由到任何模型(Gemini、GPT-4o、Claude 等) | `OPENROUTER_API_KEY` |\n| `\"nous\"` | 强制 Nous Portal | `hermes auth` |\n| `\"codex\"` | 强制 Codex OAuth(ChatGPT 账户)。支持视觉(gpt-5.3-codex)。 | `hermes model` → Codex |\n| `\"minimax-oauth\"` | 强制 MiniMax OAuth(浏览器登录,无需 API 密钥)。辅助任务使用 MiniMax-M2.7-highspeed。 | `hermes model` → MiniMax (OAuth) |\n| `\"xai-oauth\"` | 强制 xAI Grok OAuth(SuperGrok 或 X Premium+ 订阅者的浏览器登录,无需 API 密钥)。相同的 OAuth token 涵盖聊天、TTS、图像、视频和转录。 | `hermes model` → xAI Grok OAuth (SuperGrok / Premium+) |\n| `\"main\"` | 使用您的活跃自定义/主端点。可以来自 `OPENAI_BASE_URL` + `OPENAI_API_KEY` 或通过 `hermes model` / `config.yaml` 保存的自定义端点。适用于 OpenAI、本地模型或任何 OpenAI 兼容 API。**仅限辅助任务 —— 对 `model.provider` 无效。** | 自定义端点凭据 + 基础 URL |\n\n当您希望附带任务绕过默认路由器时,主 provider 目录中的直接 API 密钥 providers 也在这里工作。配置 `GMI_API_KEY` 后,`gmi` 有效:\n\n```yaml\nauxiliary:\n compression:\n provider: \"gmi\"\n model: \"anthropic/claude-opus-4.6\"\n```\n\n对于 GMI 辅助路由,使用 GMI 的 `/v1/models` 端点返回的确切模型 ID。\n\n### 常见设置\n\n**使用直接自定义端点**(比 `provider: \"main\"` 对本地/自托管 API 更清晰):\n```yaml\nauxiliary:\n vision:\n base_url: \"http://localhost:1234/v1\"\n api_key: \"local-key\"\n model: \"qwen2.5-vl\"\n```\n\n`base_url` 优先于 `provider`,因此这是将辅助任务路由到特定端点的最明确方式。对于直接端点覆盖,Hermes 使用配置的 `api_key` 或回退到 `OPENAI_API_KEY`;它不会为该自定义端点重用 `OPENROUTER_API_KEY`。\n\n**使用 OpenAI API 密钥进行视觉:**\n```yaml\n# 在 ~/.hermes/.env 中:\n# OPENAI_BASE_URL=https://api.openai.com/v1\n# OPENAI_API_KEY=sk-...\n\nauxiliary:\n vision:\n provider: \"main\"\n model: \"gpt-4o\" # 或 \"gpt-4o-mini\" 更便宜\n```\n\n**使用 OpenRouter 进行视觉**(路由到任何模型):\n```yaml\nauxiliary:\n vision:\n provider: \"openrouter\"\n model: \"openai/gpt-4o\" # 或 \"google/gemini-2.5-flash\" 等\n```\n\n**使用 Codex OAuth**(ChatGPT Pro/Plus 账户 —— 无需 API 密钥):\n```yaml\nauxiliary:\n vision:\n provider: \"codex\" # 使用您的 ChatGPT OAuth token\n # 模型默认为 gpt-5.3-codex(支持视觉)\n```\n\n**使用 MiniMax OAuth**(浏览器登录,无需 API 密钥):\n```yaml\nmodel:\n default: MiniMax-M2.7\n provider: minimax-oauth\n base_url: https://api.minimax.io/anthropic\n```\n运行 `hermes model` 并选择 **MiniMax (OAuth)** 自动登录并设置此项。对于中国区域,基础 URL 将是 `https://api.minimaxi.com/anthropic`。完整演练请参阅 [MiniMax OAuth 指南](../guides/minimax-oauth.md)。\n\n**使用本地/自托管模型:**\n```yaml\nauxiliary:\n vision:\n provider: \"main\" # 使用您的活跃自定义端点\n model: \"my-local-model\"\n```\n\n`provider: \"main\"` 使用 Hermes 用于普通聊天的任何 provider —— 无论是命名的自定义 provider(例如 `beans`)、内置 provider(如 `openrouter`)还是旧版 `OPENAI_BASE_URL` 端点。\n\n:::tip\n如果您使用 Codex OAuth 作为主模型 provider,视觉会自动工作 —— 无需额外配置。Codex 包含在视觉的自动检测链中。\n:::\n\n:::warning\n**视觉需要多模态模型。** 如果您设置 `provider: \"main\"`,请确保您的端点支持多模态/视觉 —— 否则图像分析将失败。\n:::\n\n### 环境变量(旧版)\n\n辅助模型也可以通过环境变量配置。但是,`config.yaml` 是首选方法 —— 它更易于管理,并支持所有选项,包括 `base_url` 和 `api_key`。\n\n| 设置 | 环境变量 |\n|---------|---------------------|\n| Vision provider | `AUXILIARY_VISION_PROVIDER` |\n| Vision 模型 | `AUXILIARY_VISION_MODEL` |\n| Vision 端点 | `AUXILIARY_VISION_BASE_URL` |\n| Vision API 密钥 | `AUXILIARY_VISION_API_KEY` |\n| Web 提取 provider | `AUXILIARY_WEB_EXTRACT_PROVIDER` |\n| Web 提取模型 | `AUXILIARY_WEB_EXTRACT_MODEL` |\n| Web 提取端点 | `AUXILIARY_WEB_EXTRACT_BASE_URL` |\n| Web 提取 API 密钥 | `AUXILIARY_WEB_EXTRACT_API_KEY` |\n\n压缩和回退模型设置仅限 config.yaml。\n\n:::tip\n运行 `hermes config` 查看您当前的辅助模型设置。覆盖仅在与默认值不同时显示。\n:::\n\n## 推理努力程度\n\n控制模型在响应前进行多少\"思考\":\n\n```yaml\nagent:\n reasoning_effort: \"\" # 空 = 中等。选项:none、minimal、low、medium、high、xhigh、max、ultra\n```\n\n未设置时(默认),推理努力程度默认为\"medium\" —— 适合大多数任务的平衡级别。设置值会覆盖它 —— 更高的推理努力程度在复杂任务上提供更好的结果,但代价是更多 token 和延迟。\n\n您也可以在运行时使用 `/reasoning` 命令更改推理努力程度:\n\n```\n/reasoning # 显示当前努力程度和显示状态\n/reasoning high # 将推理努力程度设为 high\n/reasoning none # 禁用推理\n/reasoning show # 在每次响应上方显示模型思考\n/reasoning hide # 隐藏模型思考\n```\n\n## 工具使用强制\n\n某些模型偶尔会将预期操作描述为文本而不是进行工具调用(\"我会运行测试...\"而不是实际调用终端)。工具使用强制会注入系统提示词指导,引导模型实际调用工具。\n\n```yaml\nagent:\n tool_use_enforcement: \"auto\" # \"auto\" | true | false | [\"model-substring\", ...]\n```\n\n| 值 | 行为 |\n|-------|----------|\n| `\"auto\"`(默认) | 对匹配以下模型启用:`gpt`、`codex`、`gemini`、`gemma`、`grok`。对所有其他模型禁用(Claude、DeepSeek、Qwen 等)。 |\n| `true` | 始终启用,无论模型如何。如果您注意到当前模型描述操作而不是执行操作,请使用此选项。 |\n| `false` | 始终禁用,无论模型如何。 |\n| `[\"gpt\", \"codex\", \"qwen\", \"llama\"]` | 仅当模型名称包含列出的子字符串之一时启用(不区分大小写)。 |\n\n### 注入的内容\n\n启用后,系统提示词中可能会添加三层指导:\n\n1. **通用工具使用强制**(所有匹配模型)—— 指示模型立即进行工具调用而不是描述意图,持续工作直到任务完成,永远不要以未来操作的承诺结束轮次。\n\n2. **OpenAI 执行纪律**(仅限 GPT 和 Codex 模型)—— 针对 GPT 特定失败模式的额外指导:在部分结果上放弃工作、跳过先决条件查找、幻觉而不是使用工具、在未验证的情况下宣布\"完成\"。\n\n3. **Google 操作指导**(仅限 Gemini 和 Gemma 模型)—— 简洁性、绝对路径、并行工具调用和编辑前验证模式。\n\n这些对用户透明,仅影响系统提示词。已经可靠使用工具的模型(如 Claude)不需要此指导,这就是为什么 `\"auto\"` 排除它们。\n\n### 何时开启\n\n如果您使用的模型不在默认自动列表中,并注意到它经常描述它*会*做什么而不是实际去做,请设置 `tool_use_enforcement: true` 或将模型子字符串添加到列表中:\n\n```yaml\nagent:\n tool_use_enforcement: [\"gpt\", \"codex\", \"gemini\", \"grok\", \"my-custom-model\"]\n```\n\n## TTS 配置\n\n```yaml\ntts:\n provider: \"edge\" # \"edge\" | \"elevenlabs\" | \"openai\" | \"minimax\" | \"mistral\" | \"gemini\" | \"xai\" | \"neutts\"\n speed: 1.0 # 全局速度倍数(所有 provider 的回退)\n edge:\n voice: \"en-US-AriaNeural\" # 322 种声音,74 种语言\n speed: 1.0 # 速度倍数(转换为速率百分比,例如 1.5 → +50%)\n elevenlabs:\n voice_id: \"pNInz6obpgDQGcFmaJgB\"\n model_id: \"eleven_multilingual_v2\"\n openai:\n model: \"gpt-4o-mini-tts\"\n voice: \"alloy\" # alloy、echo、fable、onyx、nova、shimmer\n speed: 1.0 # 速度倍数(API 限制为 0.25–4.0)\n base_url: \"https://api.openai.com/v1\" # 覆盖 OpenAI 兼容 TTS 端点\n minimax:\n speed: 1.0 # 语音速度倍数\n # base_url: \"\" # 可选:覆盖 OpenAI 兼容 TTS 端点\n mistral:\n model: \"voxtral-mini-tts-2603\"\n voice_id: \"c69964a6-ab8b-4f8a-9465-ec0925096ec8\" # Paul - Neutral(默认)\n gemini:\n model: \"gemini-2.5-flash-preview-tts\" # 或 gemini-2.5-pro-preview-tts\n voice: \"Kore\" # 30 种预置声音:Zephyr、Puck、Kore、Enceladus 等\n xai:\n voice_id: \"eve\" # xAI TTS 声音\n language: \"en\" # ISO 639-1\n sample_rate: 24000\n bit_rate: 128000 # MP3 比特率\n # base_url: \"https://api.x.ai/v1\"\n neutts:\n ref_audio: ''\n ref_text: ''\n model: neuphonic/neutts-air-q4-gguf\n device: cpu\n```\n\n这控制 `text_to_speech` 工具和语音模式中的口语回复(CLI 中的 `/voice tts` 或消息 gateway)。\n\n**速度回退层次:** provider 特定速度(例如 `tts.edge.speed`)→ 全局 `tts.speed` → `1.0` 默认值。设置全局 `tts.speed` 以在所有 provider 上应用统一速度,或按 provider 覆盖以进行精细控制。\n\n## 显示设置\n\n```yaml\ndisplay:\n tool_progress: all # off | new | all | verbose\n tool_progress_command: false # 在消息 gateway 中启用 /verbose 斜杠命令\n platforms: {} # 每平台显示覆盖(见下文)\n tool_progress_overrides: {} # 已弃用 —— 改用 display.platforms\n interim_assistant_messages: true # Gateway:将自然的轮次中 assistant 更新作为单独消息发送\n skin: default # 内置或自定义 CLI 皮肤(参阅 user-guide/features/skins)\n personality: \"kawaii\" # 旧版外观字段,仍在某些摘要中显示\n compact: false # 紧凑输出模式(减少空白)\n resume_display: full # full(恢复时显示之前的消息)| minimal(仅单行)\n bell_on_complete: false # 当 agent 完成时播放终端铃声(适合长任务)\n show_reasoning: false # 在每次响应上方显示模型推理/思考(用 /reasoning show|hide 切换)\n streaming: false # 将 token 实时流式传输到终端\n show_cost: false # 在 CLI 状态栏中显示估计 $ 成本\n timestamps: false # 为 true 时,在 CLI/TUI 记录中为用户和 assistant 标签添加 [HH:MM] 时间戳前缀\n tool_preview_length: 0 # 工具调用预览的最大字符数(0 = 无限制,显示完整路径/命令)\n runtime_footer: # Gateway:在最终回复中附加运行时上下文页脚\n enabled: false\n fields: [\"model\", \"context_pct\", \"cwd\"]\n file_mutation_verifier: true # 当本轮 write_file/patch 调用失败时附加建议性页脚\n language: en # 静态消息的 UI 语言(审批提示、部分 gateway 回复)。en | zh | zh-hant | ja | de | es | fr | tr | uk | af | ko | it | ga | pt | ru | hu\n```\n\n### 文件变更验证器\n\n当 `display.file_mutation_verifier` 为 `true`(默认)时,每当本轮中 `write_file` 或 `patch` 调用失败且从未被对同一路径的成功写入取代时,Hermes 会在 assistant 的最终响应中附加一行建议。这捕获了\"批量并行补丁,一半静默失败,模型总结成功\"这类过度声明,而无需您在每次编辑后手动运行 `git status`。\n\n示例页脚:\n\n```\n⚠️ File-mutation verifier: 3 file(s) were NOT modified this turn despite any wording above that may suggest otherwise. Run `git status` or `read_file` to confirm.\n • concepts/automatic-organization.md — [patch] Could not find match for old_string\n • concepts/lora.md — [patch] Could not find match for old_string\n • concepts/rag-pipeline.md — [patch] Could not find match for old_string\n```\n\n设置 `file_mutation_verifier: false`(或 `HERMES_FILE_MUTATION_VERIFIER=0`)以禁止页脚。验证器仅在轮次结束时有真实失败未解决时触发 —— 在同一轮次内重试失败补丁并成功的模型不会为该文件触发它。\n\n### 静态消息的 UI 语言\n\n`display.language` 设置翻译一小组静态面向用户的消息 —— CLI 审批提示、少数 gateway 斜杠命令回复(例如重启排空通知、\"审批已过期\"、\"目标已清除\")。它**不**翻译 agent 响应、日志行、工具输出、错误回溯或斜杠命令描述 —— 这些保持英文。如果您希望 agent 本身用另一种语言回复,只需在您的提示词或系统消息中告诉它。\n\n支持的值:`en`(默认)、`zh`(简体中文)、`zh-hant`(繁体中文)、`ja`(日语)、`de`(德语)、`es`(西班牙语)、`fr`(法语)、`tr`(土耳其语)、`uk`(乌克兰语)、`af`(南非荷兰语)、`ko`(韩语)、`it`(意大利语)、`ga`(爱尔兰语)、`pt`(葡萄牙语)、`ru`(俄语)、`hu`(匈牙利语)。未知值回退到英文。\n\n您也可以使用 `HERMES_LANGUAGE` 环境变量按会话设置,它会覆盖配置值。\n\n```yaml\ndisplay:\n language: zh # CLI 审批提示以中文显示\n```\n\n| 模式 | 您看到的内容 |\n|------|-------------|\n| `off` | 静默 —— 仅最终响应 |\n| `new` | 仅在工具更改时显示工具指示器 |\n| `all` | 每次工具调用附带简短预览(默认) |\n| `verbose` | 完整参数、结果和调试日志 |\n\n在 CLI 中,使用 `/verbose` 循环切换这些模式。要在消息平台(Telegram、Discord、Slack 等)中使用 `/verbose`,请在上方的 `display` 部分设置 `tool_progress_command: true`。该命令将循环切换模式并保存到配置。\n\n### 运行时元数据页脚(仅限 gateway)\n\n当 `display.runtime_footer.enabled: true` 时,Hermes 在每个 gateway 轮次的**最终**消息中附加一个小型运行时上下文页脚。目前页脚可显示模型、上下文窗口百分比和当前工作目录。默认关闭;如果您的团队希望每个回复都包含这些来源信息,请按 gateway 选择加入。\n\n```yaml\ndisplay:\n runtime_footer:\n enabled: true\n fields: [\"model\", \"context_pct\", \"cwd\"] # 支持字段:model、context_pct、cwd\n```\n\n`/footer` 斜杠命令在任何会话中运行时切换此功能。\n\n附加到 Telegram/Discord/Slack 回复的示例页脚:\n\n```\n— claude-opus-4.7 · 12 tool calls · 2m 14s · $0.042\n```\n\n只有轮次的**最终**消息获得页脚;中间更新保持干净。\n\n### 每平台进度覆盖\n\n不同平台有不同的详细程度需求。例如,Signal 无法编辑消息,因此每次进度更新都会成为单独的消息 —— 很嘈杂。使用 `display.platforms` 设置每平台模式:\n\n```yaml\ndisplay:\n tool_progress: all # 全局默认\n platforms:\n signal:\n tool_progress: 'off' # 在 Signal 上静默进度\n telegram:\n tool_progress: verbose # 在 Telegram 上详细进度\n slack:\n tool_progress: 'off' # 在共享 Slack 工作区中保持安静\n```\n\n没有覆盖的平台回退到全局 `tool_progress` 值。有效平台键:`telegram`、`discord`、`slack`、`signal`、`whatsapp`、`matrix`、`mattermost`、`email`、`sms`、`homeassistant`、`dingtalk`、`feishu`、`wecom`、`weixin`、`bluebubbles`、`qqbot`。旧版 `display.tool_progress_overrides` 键仍可加载以向后兼容,但已弃用,并在首次加载时迁移到 `display.platforms`。\n\n`interim_assistant_messages` 仅限 gateway。启用后,Hermes 将已完成的轮次中 assistant 更新作为单独的聊天消息发送。这与 `tool_progress` 无关,不需要 gateway 流式传输。\n\n## 隐私\n\n```yaml\nprivacy:\n redact_pii: false # 从 LLM 上下文中删除 PII(仅限 gateway)\n```\n\n当 `redact_pii` 为 `true` 时,gateway 在将系统提示词发送到受支持平台上的 LLM 之前,会从中删除个人身份信息:\n\n| 字段 | 处理方式 |\n|-------|-----------|\n| 电话号码(WhatsApp/Signal 上的用户 ID) | 哈希为 `user_<12-char-sha256>` |\n| 用户 ID | 哈希为 `user_<12-char-sha256>` |\n| 聊天 ID | 数字部分哈希,保留平台前缀(`telegram:<hash>`) |\n| 主频道 ID | 数字部分哈希 |\n| 用户名/昵称 | **不受影响**(用户选择的,公开可见) |\n\n**平台支持:** 删除适用于 WhatsApp、Signal 和 Telegram。Discord 和 Slack 被排除,因为它们的提及系统(`<@user_id>`)需要 LLM 上下文中的真实 ID。\n\n哈希是确定性的 —— 同一用户始终映射到同一哈希,因此模型仍然可以在群聊中区分用户。路由和传递在内部使用原始值。\n\n## 语音转文字(STT)\n\n```yaml\nstt:\n provider: \"local\" # \"local\" | \"groq\" | \"openai\" | \"mistral\"\n local:\n model: \"base\" # tiny、base、small、medium、large-v3\n openai:\n model: \"whisper-1\" # whisper-1 | gpt-4o-mini-transcribe | gpt-4o-transcribe\n # model: \"whisper-1\" # 旧版回退键仍受支持\n```\n\nProvider 行为:\n\n- `local` 使用在您机器上运行的 `faster-whisper`。使用 `pip install faster-whisper` 单独安装。\n- `groq` 使用 Groq 的 Whisper 兼容端点,读取 `GROQ_API_KEY`。\n- `openai` 使用 OpenAI 语音 API,读取 `VOICE_TOOLS_OPENAI_KEY`。\n\n如果请求的 provider 不可用,Hermes 按此顺序自动回退:`local` → `groq` → `openai`。\n\nGroq 和 OpenAI 模型覆盖由环境变量驱动:\n\n```bash\nSTT_GROQ_MODEL=whisper-large-v3-turbo\nSTT_OPENAI_MODEL=whisper-1\nGROQ_BASE_URL=https://api.groq.com/openai/v1\nSTT_OPENAI_BASE_URL=https://api.openai.com/v1\n```\n\n## 语音模式(CLI)\n\n```yaml\nvoice:\n record_key: \"ctrl+b\" # CLI 内的按键通话键\n max_recording_seconds: 120 # 长录音的硬停止\n auto_tts: false # /voice on 时自动启用口语回复\n beep_enabled: true # 在 CLI 语音模式中播放录音开始/停止提示音\n silence_threshold: 200 # 语音检测的 RMS 阈值\n silence_duration: 3.0 # 自动停止前的静默秒数\n```\n\n在 CLI 中使用 `/voice on` 启用麦克风模式,使用 `record_key` 开始/停止录音,使用 `/voice tts` 切换口语回复。端到端设置和平台特定行为请参阅[语音模式](/user-guide/features/voice-mode)。\n\n## 流式传输\n\n将 token 实时流式传输到终端或消息平台,而不是等待完整响应。\n\n### CLI 流式传输\n\n```yaml\ndisplay:\n streaming: true # 实时将 token 流式传输到终端\n show_reasoning: true # 同时流式传输推理/思考 token(可选)\n```\n\n启用后,响应在流式传输框内逐 token 出现。工具调用仍然静默捕获。如果 provider 不支持流式传输,它会自动回退到正常显示。\n\n### Gateway 流式传输(Telegram、Discord、Slack)\n\n```yaml\nstreaming:\n enabled: true # 启用渐进式消息编辑\n transport: edit # \"edit\"(渐进式消息编辑)或 \"off\"\n edit_interval: 0.3 # 消息编辑之间的秒数\n buffer_threshold: 40 # 强制编辑刷新前的字符数\n cursor: \" ▉\" # 流式传输期间显示的光标\n fresh_final_after_seconds: 0 # 预览超过此时间时选择发送新的最终消息(Telegram)\n```\n\n启用后,bot 在第一个 token 时发送消息,然后随着更多 token 到来渐进式编辑它。不支持消息编辑的平台(Signal、Email、Home Assistant)在第一次尝试时自动检测 —— 该会话的流式传输被优雅地禁用,不会产生大量消息。\n\n对于不带渐进式 token 编辑的独立自然轮次中 assistant 更新,请设置 `display.interim_assistant_messages: true`。\n\n**溢出处理:** 如果流式传输的文本超过平台的消息长度限制(约 4096 字符),当前消息被最终化,新消息自动开始。\n\n**新的最终消息(Telegram):** Telegram 的 `editMessageText` 保留原始消息时间戳,因此长时间运行的流式回复即使在完成后也会保留第一个 token 的时间戳。设置 `fresh_final_after_seconds > 0` 可选择将旧预览作为全新的最终消息传递,并尽力删除旧预览。默认值为 `0`,始终就地最终化流式回复,避免某些客户端短暂显示重复消息再删除其中一条。\n\n:::note\n主开关 `streaming.enabled` 默认为 `false`——在你启用之前不会有任何流式传输。启用后,是否流式传输按**平台**决定:Telegram 默认带有 `display.platforms.telegram.streaming: true`(流式传输),Discord 为 `display.platforms.discord.streaming: false`(不流式传输)。因此启用流式传输后,Telegram 开箱即用地流式传输,Discord 在你修改其开关之前仍使用整条消息回复。你可以在仪表盘的 **Channels** 开关中或直接在 `~/.hermes/config.yaml` 中调整这些按平台的开关。\n:::\n\n## 群聊会话隔离\n\n控制共享聊天是每个房间保持一个对话还是每个参与者一个对话:\n\n```yaml\ngroup_sessions_per_user: true # true = 群组/频道中每用户隔离,false = 每个聊天一个共享会话\n```\n\n- `true` 是默认和推荐设置。在 Discord 频道、Telegram 群组、Slack 频道和类似共享上下文中,当平台提供用户 ID 时,每个发送者获得自己的会话。\n- `false` 恢复到旧的共享房间行为。如果您明确希望 Hermes 将频道视为一个协作对话,这可能有用,但这也意味着用户共享上下文、token 成本和中断状态。\n- 私信不受影响。Hermes 仍然像往常一样通过聊天/DM ID 键入 DM。\n- 线程与其父频道保持隔离;使用 `true` 时,每个参与者在线程内也获得自己的会话。\n\n有关行为详情和示例,请参阅[会话](/user-guide/sessions)和 [Discord 指南](/user-guide/messaging/discord)。\n\n## 未授权 DM 行为\n\n控制当未知用户发送私信时 Hermes 的行为:\n\n```yaml\nunauthorized_dm_behavior: pair\n\nwhatsapp:\n unauthorized_dm_behavior: ignore\n```\n\n- `pair` 是默认值。Hermes 拒绝访问,但在 DM 中回复一次性配对码。\n- `ignore` 静默丢弃未授权的 DM。\n- 平台部分覆盖全局默认值,因此您可以在广泛范围内保持配对启用,同时使一个平台更安静。\n\n## 快速命令\n\n定义自定义命令,这些命令要么在不调用 LLM 的情况下运行 shell 命令,要么将一个斜杠命令别名为另一个。Exec 快速命令是零 token 的,对于从消息平台(Telegram、Discord 等)进行快速服务器检查或实用脚本很有用。\n\n```yaml\nquick_commands:\n status:\n type: exec\n command: systemctl status hermes-agent\n disk:\n type: exec\n command: df -h /\n update:\n type: exec\n command: cd ~/.hermes/hermes-agent && git pull && uv pip install -e .\n gpu:\n type: exec\n command: nvidia-smi --query-gpu=name,utilization.gpu,memory.used,memory.total --format=csv,noheader\n restart:\n type: alias\n target: /gateway restart\n```\n\n用法:在 CLI 或任何消息平台中输入 `/status`、`/disk`、`/update`、`/gpu` 或 `/restart`。`exec` 命令在宿主本地运行并直接返回输出 —— 无 LLM 调用,不消耗 token。`alias` 命令重写为配置的斜杠命令目标。\n\n- **30 秒超时** —— 长时间运行的命令被终止并显示错误消息\n- **优先级** —— 快速命令在技能命令之前检查,因此您可以覆盖技能名称\n- **自动补全** —— 快速命令在调度时解析,不显示在内置斜杠命令自动补全表中\n- **类型** —— 支持的类型为 `exec` 和 `alias`;其他类型显示错误\n- **到处可用** —— CLI、Telegram、Discord、Slack、WhatsApp、Signal、Email、Home Assistant\n\n仅字符串的 prompt 快捷方式不是有效的快速命令。对于可重用的 prompt 工作流,请创建技能或别名到现有斜杠命令。\n\n## 人类延迟\n\n在消息平台中模拟类人响应节奏:\n\n```yaml\nhuman_delay:\n mode: \"off\" # off | natural | custom\n min_ms: 800 # 最小延迟(自定义模式)\n max_ms: 2500 # 最大延迟(自定义模式)\n```\n\n## 代码执行\n\n配置 `execute_code` 工具:\n\n```yaml\ncode_execution:\n mode: project # project(默认)| strict\n timeout: 300 # 最大执行时间(秒)\n max_tool_calls: 50 # 代码执行中的最大工具调用次数\n```\n\n**`mode`** 控制脚本的工作目录和 Python 解释器:\n\n- **`project`**(默认)—— 脚本在会话的工作目录中以活跃 virtualenv/conda 环境的 python 运行。项目依赖(`pandas`、`torch`、项目包)和相对路径(`.env`、`./data.csv`)自然解析,与 `terminal()` 看到的一致。\n- **`strict`** —— 脚本在临时暂存目录中以 `sys.executable`(Hermes 自己的 python)运行。最大可重现性,但项目依赖和相对路径不会解析。\n\n环境清理(删除 `*_API_KEY`、`*_TOKEN`、`*_SECRET`、`*_PASSWORD`、`*_CREDENTIAL`、`*_PASSWD`、`*_AUTH`)和工具白名单在两种模式下完全相同 —— 切换模式不会改变安全态势。\n\n## Web 搜索后端\n\n`web_search` 和 `web_extract` 工具支持五种后端 provider。在 `config.yaml` 中或通过 `hermes tools` 配置后端:\n\n```yaml\nweb:\n backend: firecrawl # firecrawl | searxng | parallel | tavily | exa\n\n # 或使用每功能键混合 provider(例如免费搜索 + 付费提取):\n search_backend: \"searxng\"\n extract_backend: \"firecrawl\"\n```\n\n| 后端 | 环境变量 | 搜索 | 提取 |\n|---------|---------|--------|---------|\n| **Firecrawl**(默认) | `FIRECRAWL_API_KEY` | ✔ | ✔ |\n| **SearXNG** | `SEARXNG_URL` | ✔ | — |\n| **Parallel** | `PARALLEL_API_KEY` | ✔ | ✔ |\n| **Tavily** | `TAVILY_API_KEY` | ✔ | ✔ |\n| **Exa** | `EXA_API_KEY` | ✔ | ✔ |\n\n**后端选择:** 如果未设置 `web.backend`,后端从可用的 API 密钥自动检测。如果仅设置了 `SEARXNG_URL`,使用 SearXNG。如果仅设置了 `EXA_API_KEY`,使用 Exa。如果仅设置了 `TAVILY_API_KEY`,使用 Tavily。如果仅设置了 `PARALLEL_API_KEY`,使用 Parallel。否则 Firecrawl 是默认值。\n\n**SearXNG** 是一个免费、自托管、尊重隐私的元搜索引擎,查询 70+ 个搜索引擎。无需 API 密钥 —— 只需将 `SEARXNG_URL` 设置为您的实例(例如 `http://localhost:8080`)。SearXNG 仅限搜索;`web_extract` 需要单独的提取 provider(设置 `web.extract_backend`)。Docker 设置说明请参阅 [Web 搜索设置指南](/user-guide/features/web-search)。\n\n**自托管 Firecrawl:** 设置 `FIRECRAWL_API_URL` 指向您自己的实例。设置自定义 URL 后,API 密钥变为可选(在服务器上设置 `USE_DB_AUTHENTICATION=***` 以禁用认证)。\n\n**Parallel 搜索模式:** 设置 `PARALLEL_SEARCH_MODE` 控制搜索行为 —— `fast`、`one-shot` 或 `agentic`(默认:`agentic`)。\n\n**Exa:** 在 `~/.hermes/.env` 中设置 `EXA_API_KEY`。支持 `category` 过滤(`company`、`research paper`、`news`、`people`、`personal site`、`pdf`)和域名/日期过滤器。\n\n## 浏览器\n\n配置浏览器自动化行为:\n\n```yaml\nbrowser:\n inactivity_timeout: 120 # 自动关闭空闲会话前的秒数\n command_timeout: 30 # 浏览器命令超时(截图、导航等)(秒)\n record_sessions: false # 自动将浏览器会话录制为 WebM 视频到 ~/.hermes/browser_recordings/\n # 可选 CDP 覆盖 —— 设置后,Hermes 直接附加到您自己的\n # Chromium 系浏览器(通过 /browser connect),而不是启动无头浏览器。\n cdp_url: \"\"\n # 对话框监督器 —— 控制当 CDP 后端附加时(Browserbase、本地 Chromium 系\n # 浏览器通过 /browser connect)如何处理原生 JS 对话框(alert/confirm/prompt)。\n # 在 Camofox 和默认本地 agent 浏览器模式下忽略。\n dialog_policy: must_respond # must_respond | auto_dismiss | auto_accept\n dialog_timeout_s: 300 # must_respond 下的安全自动关闭(秒)\n camofox:\n managed_persistence: false # 为 true 时,Camofox 会话跨重启持久化 cookie/登录\n user_id: \"\" # 可选的外部管理 Camofox userId\n session_key: \"\" # Hermes 创建标签页时发送的可选会话密钥\n adopt_existing_tab: false # 在创建新标签页之前重用此身份的现有标签页\n```\n\n**对话框策略:**\n\n- `must_respond`(默认)—— 捕获对话框,在 `browser_snapshot.pending_dialogs` 中显示,等待 agent 调用 `browser_dialog(action=...)`。在 `dialog_timeout_s` 秒内无响应后,对话框被自动关闭以防止页面的 JS 线程永久停滞。\n- `auto_dismiss` —— 捕获,立即关闭。Agent 仍然在事后的 `browser_snapshot.recent_dialogs` 中看到对话框记录,`closed_by=\"auto_policy\"`。\n- `auto_accept` —— 捕获,立即接受。适用于有激进 `beforeunload` 提示的页面。\n\n完整对话框工作流请参阅[浏览器功能页面](./features/browser.md#browser_dialog)。\n\n浏览器工具集支持多个 provider。有关 Browserbase、Browser Use 和本地 Chromium 系 CDP 设置的详细信息,请参阅[浏览器功能页面](/user-guide/features/browser)。\n\n## 时区\n\n使用 IANA 时区字符串覆盖服务器本地时区。影响日志中的时间戳、cron 调度和系统提示词时间注入。\n\n```yaml\ntimezone: \"America/New_York\" # IANA 时区(默认:\"\" = 服务器本地时间)\n```\n\n支持的值:任何 IANA 时区标识符(例如 `America/New_York`、`Europe/London`、`Asia/Kolkata`、`UTC`)。留空或省略以使用服务器本地时间。\n\n## Discord\n\n为消息 gateway 配置 Discord 特定行为:\n\n```yaml\ndiscord:\n require_mention: true # 在服务器频道中需要 @提及才能响应\n free_response_channels: \"\" # 逗号分隔的频道 ID,bot 在这些频道无需 @提及即可响应\n auto_thread: true # 在频道中 @提及时自动创建线程\n```\n\n- `require_mention` —— 为 `true`(默认)时,bot 仅在服务器频道中被 `@BotName` 提及时响应。DM 始终无需提及即可工作。\n- `free_response_channels` —— 逗号分隔的频道 ID 列表,bot 在这些频道对每条消息响应,无需提及。\n- `auto_thread` —— 为 `true`(默认)时,频道中的提及会自动为对话创建线程,保持频道整洁(类似 Slack 线程)。\n\n## 安全\n\n预执行安全扫描和机密脱敏:\n\n```yaml\nsecurity:\n redact_secrets: false # 在工具输出和日志中脱敏 API 密钥模式(默认关闭)\n tirith_enabled: true # 为终端命令启用 Tirith 安全扫描\n tirith_path: \"tirith\" # tirith 二进制文件路径(默认:$PATH 中的 \"tirith\")\n tirith_timeout: 5 # 等待 tirith 扫描的秒数\n tirith_fail_open: true # 如果 tirith 不可用,允许命令执行\n website_blocklist: # 参见下方网站黑名单部分\n enabled: false\n domains: []\n shared_files: []\n```\n\n- `redact_secrets` —— 为 `true` 时,自动检测并脱敏工具输出中看起来像 API 密钥、token 和密码的模式,然后再进入对话上下文和日志。**默认关闭** —— 如果您经常在工具输出中处理真实凭据并希望有安全网,请启用。显式设置为 `true` 以开启。\n- `tirith_enabled` —— 为 `true` 时,终端命令在执行前由 [Tirith](https://github.com/sheeki03/tirith) 扫描以检测潜在危险操作。\n- `tirith_path` —— tirith 二进制文件的路径。如果 tirith 安装在非标准位置,请设置此项。\n- `tirith_timeout` —— 等待 tirith 扫描的最大秒数。如果扫描超时,命令继续执行。\n- `tirith_fail_open` —— 为 `true`(默认)时,如果 tirith 不可用或失败,允许命令执行。设置为 `false` 以在 tirith 无法验证时阻止命令。\n\n## 网站黑名单\n\n阻止 agent 的 web 和浏览器工具访问特定域名:\n\n```yaml\nsecurity:\n website_blocklist:\n enabled: false # 启用 URL 阻止(默认:false)\n domains: # 被阻止的域名模式列表\n - \"*.internal.company.com\"\n - \"admin.example.com\"\n - \"*.local\"\n shared_files: # 从外部文件加载额外规则\n - \"/etc/hermes/blocked-sites.txt\"\n```\n\n启用后,任何匹配被阻止域名模式的 URL 在 web 或浏览器工具执行之前都会被拒绝。这适用于 `web_search`、`web_extract`、`browser_navigate` 以及任何访问 URL 的工具。\n\n域名规则支持:\n- 精确域名:`admin.example.com`\n- 通配符子域名:`*.internal.company.com`(阻止所有子域名)\n- TLD 通配符:`*.local`\n\n共享文件每行包含一条域名规则(空行和 `#` 注释被忽略)。缺失或不可读的文件记录警告,但不禁用其他 web 工具。\n\n策略缓存 30 秒,因此配置更改无需重启即可快速生效。\n\n## 智能审批\n\n控制 Hermes 如何处理潜在危险命令:\n\n```yaml\napprovals:\n mode: smart # smart | manual | off\n```\n\n| 模式 | 行为 |\n|------|----------|\n| `smart`(默认) | 使用辅助 LLM 评估被标记的命令是否真正危险。低风险命令仅对当前命令自动批准,真正危险的命令自动拒绝,不确定的情况升级给用户。 |\n| `manual` | 在执行任何被标记的命令之前提示用户。在 CLI 中显示交互式审批对话框。在消息中排队待处理的审批请求。 |\n| `off` | 跳过所有审批检查。等同于 `HERMES_YOLO_MODE=true`。**谨慎使用。** |\n\n智能模式对于减少审批疲劳特别有用 —— 它让 agent 在安全操作上更自主地工作,同时仍然捕获真正破坏性的命令。\n\n:::warning\n设置 `approvals.mode: off` 会禁用终端命令的所有安全检查。仅在受信任的沙箱环境中使用。\n:::\n\n## 检查点\n\n破坏性文件操作之前的自动文件系统快照。详情请参阅[检查点与回滚](/user-guide/checkpoints-and-rollback)。\n\n```yaml\ncheckpoints:\n enabled: false # 启用自动检查点(也可:hermes chat --checkpoints)。默认:false(选择加入)。\n max_snapshots: 20 # 每个目录保留的最大检查点数(默认:20)\n```\n\n\n## 委托\n\n为委托工具配置子 agent 行为:\n\n```yaml\ndelegation:\n # model: \"google/gemini-3-flash-preview\" # 覆盖模型(空 = 继承父级)\n # provider: \"openrouter\" # 覆盖 provider(空 = 继承父级)\n # base_url: \"http://localhost:1234/v1\" # 直接 OpenAI 兼容端点(优先于 provider)\n # api_key: \"local-key\" # base_url 的 API 密钥(回退到 OPENAI_API_KEY)\n # api_mode: \"\" # base_url 的线路协议:\"chat_completions\"、\"codex_responses\" 或 \"anthropic_messages\"。空 = 从 URL 自动检测(例如 /anthropic 后缀 → anthropic_messages)。对启发式无法检测的非标准端点显式设置。\n max_concurrent_children: 3 # 每批并行子 agent 数(下限 1,无上限)。也可通过 DELEGATION_MAX_CONCURRENT_CHILDREN 环境变量设置。\n max_spawn_depth: 1 # 委托树深度上限(1-3,截断)。1 = 扁平(默认):父级生成无法委托的叶子。2 = 编排器子级可以生成叶子孙级。3 = 三级。\n orchestrator_enabled: true # 全局终止开关。为 false 时,role=\"orchestrator\" 被忽略,每个子级无论 max_spawn_depth 如何都被强制为叶子。\n```\n\n**子 agent provider:model 覆盖:** 默认情况下,子 agent 继承父 agent 的 provider 和模型。设置 `delegation.provider` 和 `delegation.model` 将子 agent 路由到不同的 provider:model 对 —— 例如,在您的主 agent 运行昂贵推理模型时,为范围较窄的子任务使用便宜/快速的模型。\n\n**直接端点覆盖:** 如果您想要明显的自定义端点路径,请设置 `delegation.base_url`、`delegation.api_key` 和 `delegation.model`。这将子 agent 直接发送到该 OpenAI 兼容端点,并优先于 `delegation.provider`。如果省略 `delegation.api_key`,Hermes 仅回退到 `OPENAI_API_KEY`。\n\n**线路协议(`api_mode`):** Hermes 从 `delegation.base_url` 自动检测线路协议(例如以 `/anthropic` 结尾的路径 → `anthropic_messages`;Codex/原生 Anthropic/Kimi-coding 主机名保留其现有检测)。对于启发式无法分类的端点 —— 例如 Azure AI Foundry、MiniMax、Zhipu GLM 或前置 Anthropic 形状后端的 LiteLLM 代理 —— 请将 `delegation.api_mode` 显式设置为 `chat_completions`、`codex_responses` 或 `anthropic_messages` 之一。留空(默认)以保持自动检测。\n\n委托 provider 使用与 CLI/gateway 启动相同的凭据解析。所有配置的 provider 均受支持:`openrouter`、`nous`、`copilot`、`zai`、`kimi-coding`、`minimax`、`minimax-cn`。设置 provider 时,系统自动解析正确的基础 URL、API 密钥和 API 模式 —— 无需手动凭据连接。\n\n**优先级:** 配置中的 `delegation.base_url` → 配置中的 `delegation.provider` → 父 provider(继承)。配置中的 `delegation.model` → 父模型(继承)。仅设置 `model` 而不设置 `provider` 仅更改模型名称,同时保留父级凭据(适用于在同一 provider(如 OpenRouter)内切换模型)。\n\n**宽度和深度:** `max_concurrent_children` 限制每批并行运行的子 agent 数量(默认 `3`,下限 1,无上限)。也可通过 `DELEGATION_MAX_CONCURRENT_CHILDREN` 环境变量设置。当模型提交的 `tasks` 数组超过上限时,`delegate_task` 返回工具错误解释限制,而不是静默截断。`max_spawn_depth` 控制委托树深度(截断到 1-3)。在默认 `1` 时,委托是扁平的:子级无法生成孙级,传递 `role=\"orchestrator\"` 静默降级为 `leaf`。提升到 `2` 使编排器子级可以生成叶子孙级;`3` 用于三级树。Agent 通过 `role=\"orchestrator\"` 按调用选择编排;`orchestrator_enabled: false` 强制每个子级回到叶子,无论如何。成本呈乘法增长 —— 在 `max_spawn_depth: 3` 和 `max_concurrent_children: 3` 时,树可以达到 3×3×3 = 27 个并发叶子 agent。使用模式请参阅[子 Agent 委托 → 深度限制和嵌套编排](features/delegation.md#depth-limit-and-nested-orchestration)。\n\n## 澄清\n\n配置澄清提示行为:\n\n```yaml\nclarify:\n timeout: 120 # 等待用户澄清响应的秒数\n```\n\n## 上下文文件(SOUL.md、AGENTS.md)\n\nHermes 使用两种不同的上下文范围:\n\n| 文件 | 用途 | 范围 |\n|------|---------|-------|\n| `SOUL.md` | **主要 agent 身份** —— 定义 agent 是谁(系统提示词第 #1 槽位) | `~/.hermes/SOUL.md` 或 `$HERMES_HOME/SOUL.md` |\n| `.hermes.md` / `HERMES.md` | 项目特定指令(最高优先级) | 向上走到 git 根目录 |\n| `AGENTS.md` | 项目特定指令、编码规范 | 递归目录遍历 |\n| `CLAUDE.md` | Claude Code 上下文文件(也会检测) | 仅工作目录 |\n| `.cursorrules` | Cursor IDE 规则(也会检测) | 仅工作目录 |\n| `.cursor/rules/*.mdc` | Cursor 规则文件(也会检测) | 仅工作目录 |\n\n- **SOUL.md** 是 agent 的主要身份。它占据系统提示词的第 #1 槽位,完全替换内置的默认身份。编辑它以完全自定义 agent 是谁。\n- 如果 SOUL.md 缺失、为空或无法加载,Hermes 回退到内置默认身份。\n- **项目上下文文件使用优先级系统** —— 仅加载一种类型(第一个匹配优先):`.hermes.md` → `AGENTS.md` → `CLAUDE.md` → `.cursorrules`。SOUL.md 始终独立加载。\n- **AGENTS.md** 是分层的:如果子目录也有 AGENTS.md,所有都会合并。\n- 如果 `SOUL.md` 不存在,Hermes 会自动生成默认的 `SOUL.md`。\n- 所有加载的上下文文件上限为 20,000 字符,并进行智能截断。\n\n另请参阅:\n- [个性与 SOUL.md](/user-guide/features/personality)\n- [上下文文件](/user-guide/features/context-files)\n\n## 工作目录\n\n| 上下文 | 默认值 |\n|---------|---------|\n| **CLI(`hermes`)** | 运行命令的当前目录 |\n| **消息 gateway** | 主目录 `~`(用 `MESSAGING_CWD` 覆盖) |\n| **Docker / Singularity / Modal / SSH** | 容器或远程机器内用户的主目录 |\n\n覆盖工作目录:\n```bash\n# 在 ~/.hermes/.env 或 ~/.hermes/config.yaml 中:\nMESSAGING_CWD=/home/myuser/projects # Gateway 会话\nTERMINAL_CWD=/workspace # 所有终端会话\n```\n"}}}} |