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