{"repo": "br41s/hermes-sandbox", "files": {"hermes_cli/model_normalize.py": {"092257e26f459294b7beaff689c0e68b8d0d1812": {"t": 1785595181.5754573, "n": 16839, "h": "dc89d738e273a97e", "c": "\"\"\"Per-provider model name normalization.\n\nDifferent LLM providers expect model identifiers in different formats:\n\n- **Aggregators** (OpenRouter, Nous, AI Gateway, Kilo Code) need\n ``vendor/model`` slugs like ``anthropic/claude-sonnet-4.6``.\n- **Anthropic** native API expects bare names with dots replaced by\n hyphens: ``claude-sonnet-4-6``.\n- **Copilot** expects bare names *with* dots preserved:\n ``claude-sonnet-4.6``.\n- **OpenCode Zen** preserves dots for GPT/GLM/Gemini/Kimi/MiniMax-style\n model IDs, but Claude still uses hyphenated native names like\n ``claude-sonnet-4-6``.\n- **OpenCode Go** preserves dots in model names: ``minimax-m2.7``.\n- **DeepSeek** accepts ``deepseek-chat`` (V3), ``deepseek-reasoner``\n (R1-family), and the first-class V-series IDs (``deepseek-v4-pro``,\n ``deepseek-v4-flash``, and any future ``deepseek-v-*``). Older\n Hermes revisions folded every non-reasoner input into\n ``deepseek-chat``, which on aggregators routes to V3 — so a user\n picking V4 Pro was silently downgraded.\n- **Custom** and remaining providers pass the name through as-is.\n\nThis module centralises that translation so callers can simply write::\n\n api_model = normalize_model_for_provider(user_input, provider)\n\nInspired by Clawdbot's ``normalizeAnthropicModelId`` pattern.\n\"\"\"\n\nfrom __future__ import annotations\n\nimport re\nfrom typing import Optional\n\n# ---------------------------------------------------------------------------\n# Vendor prefix mapping\n# ---------------------------------------------------------------------------\n# Maps the first hyphen-delimited token of a bare model name to the vendor\n# slug used by aggregator APIs (OpenRouter, Nous, etc.).\n#\n# Example: \"claude-sonnet-4.6\" -> first token \"claude\" -> vendor \"anthropic\"\n# -> aggregator slug: \"anthropic/claude-sonnet-4.6\"\n\n_VENDOR_PREFIXES: dict[str, str] = {\n \"claude\": \"anthropic\",\n \"gpt\": \"openai\",\n \"o1\": \"openai\",\n \"o3\": \"openai\",\n \"o4\": \"openai\",\n \"gemini\": \"google\",\n \"gemma\": \"google\",\n \"deepseek\": \"deepseek\",\n \"glm\": \"z-ai\",\n \"kimi\": \"moonshotai\",\n \"minimax\": \"minimax\",\n \"grok\": \"x-ai\",\n \"qwen\": \"qwen\",\n \"mimo\": \"xiaomi\",\n \"trinity\": \"arcee-ai\",\n \"nemotron\": \"nvidia\",\n \"llama\": \"meta-llama\",\n \"step\": \"stepfun\",\n \"trinity\": \"arcee-ai\",\n}\n\n# Providers whose APIs consume vendor/model slugs.\n_AGGREGATOR_PROVIDERS: frozenset[str] = frozenset({\n \"openrouter\",\n \"nous\",\n \"kilocode\",\n})\n\n# Providers that want bare names with dots replaced by hyphens.\n_DOT_TO_HYPHEN_PROVIDERS: frozenset[str] = frozenset({\n \"anthropic\",\n})\n\n# Providers that want bare names with dots preserved.\n_STRIP_VENDOR_ONLY_PROVIDERS: frozenset[str] = frozenset({\n \"copilot\",\n \"copilot-acp\",\n \"openai-codex\",\n})\n\n# Providers whose native naming is authoritative -- pass through unchanged.\n_AUTHORITATIVE_NATIVE_PROVIDERS: frozenset[str] = frozenset({\n \"huggingface\",\n})\n\n# Direct providers that accept bare native names but should repair a matching\n# provider/ prefix when users copy the aggregator form into config.yaml.\n_MATCHING_PREFIX_STRIP_PROVIDERS: frozenset[str] = frozenset({\n \"zai\",\n \"kimi-coding\",\n \"kimi-coding-cn\",\n \"minimax\",\n \"minimax-oauth\",\n \"minimax-cn\",\n \"alibaba\",\n \"qwen-oauth\",\n \"xiaomi\",\n \"arcee\",\n \"ollama-cloud\",\n \"custom\",\n \"gemini\",\n \"xai\",\n})\n\n# Providers whose APIs require lowercase model IDs. Xiaomi's\n# ``api.xiaomimimo.com`` rejects mixed-case names like ``MiMo-V2.5-Pro``\n# that users might copy from marketing docs — it only accepts\n# ``mimo-v2.5-pro``. After stripping a matching provider prefix, these\n# providers also get ``.lower()`` applied.\n_LOWERCASE_MODEL_PROVIDERS: frozenset[str] = frozenset({\n \"xiaomi\",\n})\n\n# ---------------------------------------------------------------------------\n# DeepSeek special handling\n# ---------------------------------------------------------------------------\n# DeepSeek's API only recognises exactly two model identifiers. We map\n# common aliases and patterns to the canonical names.\n\n_DEEPSEEK_REASONER_KEYWORDS: frozenset[str] = frozenset({\n \"reasoner\",\n \"r1\",\n \"think\",\n \"reasoning\",\n \"cot\",\n})\n\n_DEEPSEEK_CANONICAL_MODELS: frozenset[str] = frozenset({\n \"deepseek-chat\", # V3 on DeepSeek direct and most aggregators\n \"deepseek-reasoner\", # R1-family reasoning model\n \"deepseek-v4-pro\", # V4 Pro — first-class model ID\n \"deepseek-v4-flash\", # V4 Flash — first-class model ID\n})\n\n# First-class V-series IDs (``deepseek-v4-pro``, ``deepseek-v4-flash``,\n# future ``deepseek-v5-*``, dated variants like ``deepseek-v4-flash-20260423``).\n# Verified empirically 2026-04-24: DeepSeek's Chat Completions API returns\n# ``provider: DeepSeek`` / ``model: deepseek-v4-flash-20260423`` when called\n# with ``model=deepseek/deepseek-v4-flash``, so these names are not aliases\n# of ``deepseek-chat`` and must not be folded into it.\n_DEEPSEEK_V_SERIES_RE = re.compile(r\"^deepseek-v\\d+([-.].+)?$\")\n\n\ndef _normalize_for_deepseek(model_name: str) -> str:\n \"\"\"Map a model input to a DeepSeek-accepted identifier.\n\n Rules:\n - Already a known canonical (``deepseek-chat``/``deepseek-reasoner``/\n ``deepseek-v4-pro``/``deepseek-v4-flash``) -> pass through.\n - Matches the V-series pattern ``deepseek-v...`` -> pass through\n (covers future ``deepseek-v5-*`` and dated variants without a release).\n - Contains a reasoner keyword (r1, think, reasoning, cot, reasoner)\n -> ``deepseek-reasoner``.\n - Everything else -> ``deepseek-chat``.\n\n Args:\n model_name: The bare model name (vendor prefix already stripped).\n\n Returns:\n A DeepSeek-accepted model identifier.\n \"\"\"\n bare = _strip_vendor_prefix(model_name).lower()\n\n if bare in _DEEPSEEK_CANONICAL_MODELS:\n return bare\n\n # V-series first-class IDs (v4-pro, v4-flash, future v5-*, dated variants)\n if _DEEPSEEK_V_SERIES_RE.match(bare):\n return bare\n\n # Check for reasoner-like keywords anywhere in the name\n for keyword in _DEEPSEEK_REASONER_KEYWORDS:\n if keyword in bare:\n return \"deepseek-reasoner\"\n\n return \"deepseek-chat\"\n\n\n# ---------------------------------------------------------------------------\n# Helper utilities\n# ---------------------------------------------------------------------------\n\ndef _strip_vendor_prefix(model_name: str) -> str:\n \"\"\"Remove a ``vendor/`` prefix if present.\n\n Examples::\n\n >>> _strip_vendor_prefix(\"anthropic/claude-sonnet-4.6\")\n 'claude-sonnet-4.6'\n >>> _strip_vendor_prefix(\"claude-sonnet-4.6\")\n 'claude-sonnet-4.6'\n >>> _strip_vendor_prefix(\"meta-llama/llama-4-scout\")\n 'llama-4-scout'\n \"\"\"\n if \"/\" in model_name:\n return model_name.split(\"/\", 1)[1]\n return model_name\n\n\ndef _dots_to_hyphens(model_name: str) -> str:\n \"\"\"Replace dots with hyphens in a model name.\n\n Anthropic's native API uses hyphens where marketing names use dots:\n ``claude-sonnet-4.6`` -> ``claude-sonnet-4-6``.\n \"\"\"\n return model_name.replace(\".\", \"-\")\n\n\ndef _normalize_provider_alias(provider_name: str) -> str:\n \"\"\"Resolve provider aliases to Hermes' canonical ids.\"\"\"\n raw = (provider_name or \"\").strip().lower()\n if not raw:\n return raw\n try:\n from hermes_cli.models import normalize_provider\n\n return normalize_provider(raw)\n except Exception:\n return raw\n\n\ndef _strip_matching_provider_prefix(model_name: str, target_provider: str) -> str:\n \"\"\"Strip ``provider/`` only when the prefix matches the target provider.\n\n This prevents arbitrary slash-bearing model IDs from being mangled on\n native providers while still repairing manual config values like\n ``zai/glm-5.1`` for the ``zai`` provider.\n \"\"\"\n if \"/\" not in model_name:\n return model_name\n\n prefix, remainder = model_name.split(\"/\", 1)\n if not prefix.strip() or not remainder.strip():\n return model_name\n\n normalized_prefix = _normalize_provider_alias(prefix)\n normalized_target = _normalize_provider_alias(target_provider)\n if normalized_prefix and normalized_prefix == normalized_target:\n return remainder.strip()\n return model_name\n\n\ndef detect_vendor(model_name: str) -> Optional[str]:\n \"\"\"Detect the vendor slug from a bare model name.\n\n Uses the first hyphen-delimited token of the model name to look up\n the corresponding vendor in ``_VENDOR_PREFIXES``. Also handles\n case-insensitive matching and special patterns.\n\n Args:\n model_name: A model name, optionally already including a\n ``vendor/`` prefix. If a prefix is present it is used\n directly.\n\n Returns:\n The vendor slug (e.g. ``\"anthropic\"``, ``\"openai\"``) or ``None``\n if no vendor can be confidently detected.\n\n Examples::\n\n >>> detect_vendor(\"claude-sonnet-4.6\")\n 'anthropic'\n >>> detect_vendor(\"gpt-5.4-mini\")\n 'openai'\n >>> detect_vendor(\"anthropic/claude-sonnet-4.6\")\n 'anthropic'\n >>> detect_vendor(\"my-custom-model\")\n \"\"\"\n name = model_name.strip()\n if not name:\n return None\n\n # If there's already a vendor/ prefix, extract it\n if \"/\" in name:\n return name.split(\"/\", 1)[0].lower() or None\n\n name_lower = name.lower()\n\n # Try first hyphen-delimited token (exact match)\n first_token = name_lower.split(\"-\")[0]\n if first_token in _VENDOR_PREFIXES:\n return _VENDOR_PREFIXES[first_token]\n\n # Handle patterns where the first token includes version digits,\n # e.g. \"qwen3.5-plus\" -> first token \"qwen3.5\", but prefix is \"qwen\"\n for prefix, vendor in _VENDOR_PREFIXES.items():\n if name_lower.startswith(prefix):\n return vendor\n\n return None\n\n\ndef _prepend_vendor(model_name: str) -> str:\n \"\"\"Prepend the detected ``vendor/`` prefix if missing.\n\n Used for aggregator providers that require ``vendor/model`` format.\n If the name already contains a ``/``, it is returned as-is.\n If no vendor can be detected, the name is returned unchanged\n (aggregators may still accept it or return an error).\n\n Examples::\n\n >>> _prepend_vendor(\"claude-sonnet-4.6\")\n 'anthropic/claude-sonnet-4.6'\n >>> _prepend_vendor(\"anthropic/claude-sonnet-4.6\")\n 'anthropic/claude-sonnet-4.6'\n >>> _prepend_vendor(\"my-custom-thing\")\n 'my-custom-thing'\n \"\"\"\n if \"/\" in model_name:\n return model_name\n\n vendor = detect_vendor(model_name)\n if vendor:\n return f\"{vendor}/{model_name}\"\n return model_name\n\n\n# ---------------------------------------------------------------------------\n# Main normalisation entry point\n# ---------------------------------------------------------------------------\n\ndef normalize_model_for_provider(model_input: str, target_provider: str) -> str:\n \"\"\"Translate a model name into the format the target provider's API expects.\n\n This is the primary entry point for model name normalisation. It\n accepts any user-facing model identifier and transforms it for the\n specific provider that will receive the API call.\n\n Args:\n model_input: The model name as provided by the user or config.\n Can be bare (``\"claude-sonnet-4.6\"``), vendor-prefixed\n (``\"anthropic/claude-sonnet-4.6\"``), or already in native\n format (``\"claude-sonnet-4-6\"``).\n target_provider: The canonical Hermes provider id, e.g.\n ``\"openrouter\"``, ``\"anthropic\"``, ``\"copilot\"``,\n ``\"deepseek\"``, ``\"custom\"``. Should already be normalised\n via ``hermes_cli.models.normalize_provider()``.\n\n Returns:\n The model identifier string that the target provider's API\n expects.\n\n Raises:\n No exceptions -- always returns a best-effort string.\n\n Examples::\n\n >>> normalize_model_for_provider(\"claude-sonnet-4.6\", \"openrouter\")\n 'anthropic/claude-sonnet-4.6'\n\n >>> normalize_model_for_provider(\"anthropic/claude-sonnet-4.6\", \"anthropic\")\n 'claude-sonnet-4-6'\n\n >>> normalize_model_for_provider(\"anthropic/claude-sonnet-4.6\", \"copilot\")\n 'claude-sonnet-4.6'\n\n >>> normalize_model_for_provider(\"openai/gpt-5.4\", \"copilot\")\n 'gpt-5.4'\n\n >>> normalize_model_for_provider(\"claude-sonnet-4.6\", \"opencode-zen\")\n 'claude-sonnet-4-6'\n\n >>> normalize_model_for_provider(\"minimax-m2.5-free\", \"opencode-zen\")\n 'minimax-m2.5-free'\n\n >>> normalize_model_for_provider(\"deepseek-v3\", \"deepseek\")\n 'deepseek-chat'\n\n >>> normalize_model_for_provider(\"deepseek-r1\", \"deepseek\")\n 'deepseek-reasoner'\n\n >>> normalize_model_for_provider(\"my-model\", \"custom\")\n 'my-model'\n\n >>> normalize_model_for_provider(\"claude-sonnet-4.6\", \"zai\")\n 'claude-sonnet-4.6'\n\n >>> normalize_model_for_provider(\"MiMo-V2.5-Pro\", \"xiaomi\")\n 'mimo-v2.5-pro'\n \"\"\"\n name = (model_input or \"\").strip()\n if not name:\n return name\n\n provider = _normalize_provider_alias(target_provider)\n\n # --- Aggregators: need vendor/model format ---\n if provider in _AGGREGATOR_PROVIDERS:\n return _prepend_vendor(name)\n\n # --- OpenCode Zen / OpenCode Go: flat-namespace resellers.\n # Their /v1/models API returns bare IDs only (no vendor prefix), and\n # the inference endpoint rejects vendor-prefixed names with HTTP 401\n # \"Model not supported\". Strip ANY leading ``vendor/`` so config\n # entries like ``minimax/minimax-m2.7`` or ``deepseek/deepseek-v4-flash``\n # — commonly copied from aggregator slugs into fallback_model lists —\n # resolve to bare ``minimax-m2.7`` / ``deepseek-v4-flash`` the API\n # actually serves. See PR reviewing opencode-go fallback 401s. ---\n if provider in {\"opencode-zen\", \"opencode-go\"}:\n if \"/\" in name:\n _, bare_after_slash = name.split(\"/\", 1)\n name = bare_after_slash.strip() or name\n if provider == \"opencode-zen\" and name.lower().startswith(\"claude-\"):\n return _dots_to_hyphens(name)\n return name\n\n # --- Anthropic: strip matching provider prefix, dots -> hyphens ---\n if provider in _DOT_TO_HYPHEN_PROVIDERS:\n bare = _strip_matching_provider_prefix(name, provider)\n if \"/\" in bare:\n return bare\n return _dots_to_hyphens(bare)\n\n # --- Copilot / Copilot ACP: delegate to the Copilot-specific\n # normalizer. It knows about the alias table (vendor-prefix\n # stripping for Anthropic/OpenAI, dash-to-dot repair for Claude)\n # and live-catalog lookups. Without this, vendor-prefixed or\n # dash-notation Claude IDs survive to the Copilot API and hit\n # HTTP 400 \"model_not_supported\". See issue #6879.\n if provider in {\"copilot\", \"copilot-acp\"}:\n try:\n from hermes_cli.models import normalize_copilot_model_id\n\n normalized = normalize_copilot_model_id(name)\n if normalized:\n return normalized\n except Exception:\n # Fall through to the generic strip-vendor behaviour below\n # if the Copilot-specific path is unavailable for any reason.\n pass\n\n # --- Copilot / Copilot ACP / openai-codex fallback:\n # strip matching provider prefix, keep dots ---\n if provider in _STRIP_VENDOR_ONLY_PROVIDERS:\n stripped = _strip_matching_provider_prefix(name, provider)\n if stripped == name and name.startswith(\"openai/\"):\n # openai-codex maps openai/gpt-5.4 -> gpt-5.4\n return name.split(\"/\", 1)[1]\n return stripped\n\n # --- DeepSeek: map to one of two canonical names ---\n if provider == \"deepseek\":\n bare = _strip_matching_provider_prefix(name, provider)\n if \"/\" in bare:\n return bare\n return _normalize_for_deepseek(bare)\n\n # --- Direct providers: repair matching provider prefixes only ---\n if provider in _MATCHING_PREFIX_STRIP_PROVIDERS:\n result = _strip_matching_provider_prefix(name, provider)\n # Some providers require lowercase model IDs (e.g. Xiaomi's API\n # rejects \"MiMo-V2.5-Pro\" but accepts \"mimo-v2.5-pro\").\n if provider in _LOWERCASE_MODEL_PROVIDERS:\n result = result.lower()\n return result\n\n # --- Authoritative native providers: preserve user-facing slugs as-is ---\n if provider in _AUTHORITATIVE_NATIVE_PROVIDERS:\n return name\n\n # --- Custom & all others: pass through as-is ---\n return name\n\n\n# ---------------------------------------------------------------------------\n# Batch / convenience helpers\n# ---------------------------------------------------------------------------\n\n"}}, "cli-config.yaml.example": {"092257e26f459294b7beaff689c0e68b8d0d1812": {"t": 1785746818.0433924, "n": 78007, "h": "849a670619fa8405", "c": "# Hermes Agent CLI Configuration\n# Copy this file to cli-config.yaml and customize as needed.\n# This file configures the CLI behavior. Environment variables in .env take precedence.\n\n# =============================================================================\n# Model Configuration\n# =============================================================================\nmodel:\n # Default model to use (can be overridden with --model flag)\n # Both \"default\" and \"model\" work as the key name here.\n default: \"anthropic/claude-opus-4.6\"\n \n # Inference provider selection:\n # \"auto\" - Auto-detect from credentials (default)\n # \"openrouter\" - OpenRouter (requires: OPENROUTER_API_KEY or OPENAI_API_KEY)\n # \"nous\" - Nous Portal OAuth (requires: hermes auth add nous)\n # \"nous-api\" - Nous Portal API key (requires: NOUS_API_KEY)\n # \"anthropic\" - Direct Anthropic API (requires: ANTHROPIC_API_KEY)\n # \"openai-codex\" - OpenAI Codex (requires: hermes auth)\n # \"copilot\" - GitHub Copilot / GitHub Models (requires: GITHUB_TOKEN)\n # \"gemini\" - Use Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n # \"zai\" - Use z.ai / ZhipuAI GLM models (requires: GLM_API_KEY)\n # \"kimi-coding\" - Kimi / Moonshot AI (requires: KIMI_API_KEY)\n # \"minimax\" - MiniMax global (requires: MINIMAX_API_KEY)\n # \"minimax-cn\" - MiniMax China (requires: MINIMAX_CN_API_KEY)\n # \"huggingface\" - Hugging Face Inference (requires: HF_TOKEN)\n # \"nvidia\" - NVIDIA NIM / build.nvidia.com (requires: NVIDIA_API_KEY)\n # \"xiaomi\" - Xiaomi MiMo (requires: XIAOMI_API_KEY)\n # \"arcee\" - Arcee AI Trinity models (requires: ARCEEAI_API_KEY)\n # \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY — https://ollama.com/settings)\n # \"deepinfra\" - DeepInfra (requires: DEEPINFRA_API_KEY)\n # \"kilocode\" - KiloCode gateway (requires: KILOCODE_API_KEY)\n # \"azure-foundry\" - Microsoft Foundry / Azure OpenAI (API key or Entra ID)\n # \"lmstudio\" - LM Studio local server (optional: LM_API_KEY, defaults to http://127.0.0.1:1234/v1)\n #\n # Local servers (LM Studio, Ollama, vLLM, llama.cpp):\n # \"custom\" - Any other OpenAI-compatible endpoint. Set base_url below.\n # Aliases: \"ollama\", \"vllm\", \"llamacpp\" all map to \"custom\".\n # LM Studio is first-class and uses provider: \"lmstudio\".\n # It works with both no-auth and auth-enabled server modes.\n #\n # Can also be overridden for a single invocation with the --provider flag.\n provider: \"auto\"\n \n # API configuration (falls back to OPENROUTER_API_KEY env var)\n # api_key: \"your-key-here\" # Uncomment to set here instead of .env\n base_url: \"https://openrouter.ai/api/v1\"\n\n # Azure Foundry keyless auth example:\n # provider: \"azure-foundry\"\n # base_url: \"https://.openai.azure.com/openai/v1\"\n # auth_mode: \"entra_id\" # DefaultAzureCredential: az login, managed identity, workload identity, etc.\n # default: \"gpt-4o\" # Deployment/model name\n # entra:\n # scope: \"https://ai.azure.com/.default\" # Optional; this is the default.\n\n # ── Token limits — two settings, easy to confuse ──────────────────────────\n #\n # context_length: TOTAL context window (input + output tokens combined).\n # Controls when Hermes compresses history and validates requests.\n # Leave unset — Hermes auto-detects the correct value from the provider.\n # Set manually only when auto-detection is wrong (e.g. a local server with\n # a custom num_ctx, or a proxy that doesn't expose /v1/models).\n #\n # context_length: 131072\n #\n # max_tokens: OUTPUT cap — maximum tokens the model may generate per response.\n # Unrelated to how long your conversation history can be.\n # The OpenAI-standard name \"max_tokens\" is a misnomer; Anthropic's native\n # API has since renamed it \"max_output_tokens\" for clarity.\n # Leave unset to use the model's native output ceiling (recommended).\n # Set only if you want to deliberately limit individual response length.\n #\n# max_tokens: 8192\n\n # ── Custom request headers (optional) ─────────────────────────────────────\n #\n # default_headers: extra HTTP headers sent on every request to an\n # OpenAI-compatible endpoint. User values take precedence over the\n # provider/SDK defaults, so this is the supported way to override the\n # OpenAI Python SDK's identifying headers (User-Agent: OpenAI/Python ...,\n # X-Stainless-*) when a custom provider sits behind a gateway/WAF that\n # rejects them — e.g. an upstream that returns \"502 Upstream access\n # forbidden\" for the SDK default User-Agent but accepts a plain one.\n # Applies on the OpenAI wire only (not native Anthropic / Bedrock).\n #\n # default_headers:\n # User-Agent: \"curl/8.7.1\"\n #\n # extra_headers: accepted as an alias of default_headers (merged, with\n # extra_headers winning when both are set) — matches the per-provider\n # extra_headers key below.\n #\n # Per-provider variant: named providers / custom_providers entries accept an\n # extra_headers dict scoped to that endpoint only — for reverse proxies,\n # gateways, or custom auth (e.g. Cloudflare Access service tokens).\n # Merged onto SDK/provider defaults with the entry's values winning.\n # Header values are treated as secrets and are never logged.\n #\n # providers:\n # my-proxy:\n # base_url: \"https://llm.internal.example.com/v1\"\n # key_env: \"MY_PROXY_API_KEY\"\n # extra_headers:\n # CF-Access-Client-Id: \"xxxx.access\"\n # CF-Access-Client-Secret: \"${CF_ACCESS_SECRET}\"\n # X-Client-Name: \"hermes-agent\"\n\n# Named provider overrides (optional)\n# Use this for per-provider request timeouts, non-stream stale timeouts,\n# and per-model exceptions.\n# Applies to the primary turn client on every api_mode (OpenAI-wire, native\n# Anthropic, and Anthropic-compatible providers), the fallback chain, and\n# client rebuilds during credential rotation. For OpenAI-wire chat\n# completions (streaming and non-streaming) the configured value is also\n# used as the per-request ``timeout=`` kwarg so it wins over the legacy\n# HERMES_API_TIMEOUT env var (which still applies when no config is set).\n# ``stale_timeout_seconds`` controls the non-streaming stale-call detector and\n# wins over the legacy HERMES_API_CALL_STALE_TIMEOUT env var. Leaving these\n# unset keeps the legacy defaults (HERMES_API_TIMEOUT=1800s,\n# HERMES_API_CALL_STALE_TIMEOUT=90s, native Anthropic 900s). The\n# implicit non-stream stale detector is auto-disabled for local endpoints\n# and can scale upward for very large contexts.\n#\n# Not currently wired for AWS Bedrock (bedrock_converse + AnthropicBedrock\n# SDK paths) — those use boto3 with its own timeout configuration.\n#\n# providers:\n# ollama-local:\n# request_timeout_seconds: 300 # Longer timeout for local cold-starts\n# stale_timeout_seconds: 900 # Explicitly re-enable stale detection on local endpoints\n# anthropic:\n# request_timeout_seconds: 30 # Fast-fail cloud requests\n# models:\n# claude-opus-4.6:\n# timeout_seconds: 600 # Longer timeout for extended-thinking Opus calls\n# openai-codex:\n# models:\n# gpt-5.4:\n# stale_timeout_seconds: 1800 # Longer non-stream stale timeout for slow large-context turns\n\n# =============================================================================\n# OpenRouter Provider Routing (only applies when using OpenRouter)\n# =============================================================================\n# Control how requests are routed across providers on OpenRouter.\n# See: https://openrouter.ai/docs/guides/routing/provider-selection\n#\n# provider_routing:\n# # Sort strategy: \"price\" (default), \"throughput\", or \"latency\"\n# # Append :nitro to model name for a shortcut to throughput sorting.\n# sort: \"throughput\"\n#\n# # Only allow these providers (provider slugs from OpenRouter)\n# # only: [\"anthropic\", \"google\"]\n#\n# # Skip these providers entirely\n# # ignore: [\"deepinfra\", \"fireworks\"]\n#\n# # Try providers in this order (overrides default load balancing)\n# # order: [\"anthropic\", \"google\", \"together\"]\n#\n# # Require providers to support all parameters in your request\n# # require_parameters: true\n#\n# # Data policy: \"allow\" (default) or \"deny\" to exclude providers that may store data\n# # data_collection: \"deny\"\n#\n# # ── DeepSeek prompt-cache pinning (RECOMMENDED for deepseek/* as main model) ──\n# # DeepSeek's context cache is automatic but backend-local: OpenRouter\n# # load-balances deepseek/* across upstream backends with separate caches,\n# # so consecutive turns can land on a cold backend and re-bill the FULL\n# # prefix. Pinning the order to \"deepseek\" keeps every turn on the same\n# # cache-warm backend. Measured impact: ~2x cost difference at scale.\n# # Fallbacks stay ON by default, so a DeepSeek outage degrades to another\n# # provider rather than failing. \"deepseek\" is a no-op for non-deepseek\n# # models (that backend doesn't serve them), so this is safe to leave set.\n# # order: [\"deepseek\"]\n\n# =============================================================================\n# OpenRouter Response Caching (only applies when using OpenRouter)\n# =============================================================================\n# Cache identical API responses at the OpenRouter edge for free instant replays.\n# When enabled, identical requests (same model, messages, parameters) return\n# cached responses with zero billing. Separate from Anthropic prompt caching.\n# See: https://openrouter.ai/docs/guides/features/response-caching\n#\n# openrouter:\n# response_cache: true # Enable response caching (default: true)\n# response_cache_ttl: 300 # Cache TTL in seconds, 1-86400 (default: 300)\n\n# =============================================================================\n# Git Worktree Isolation\n# =============================================================================\n# When enabled, each CLI session creates an isolated git worktree so multiple\n# agents can work on the same repo concurrently without file collisions.\n# Equivalent to always passing --worktree / -w on the command line.\n#\n# worktree: true # Always create a worktree when in a git repo\n# worktree: false # Default — only create when -w flag is passed\n#\n# By default a new worktree branches from the freshly-fetched remote tip\n# (the current branch's upstream, else the remote's default branch) so it\n# starts current with the project instead of from the local clone's\n# (possibly stale) HEAD. Set worktree_sync: false to branch from local HEAD\n# instead — useful when offline or when you deliberately want the clone's\n# exact current state as the base.\n#\n# worktree_sync: true # Default — branch from the fetched remote tip\n# worktree_sync: false # Branch from local HEAD (offline / pinned base)\n\n# =============================================================================\n# Terminal Tool Configuration\n# =============================================================================\n# Choose ONE of the following terminal configurations by uncommenting it.\n# The terminal tool executes commands in the specified environment.\n\n# -----------------------------------------------------------------------------\n# OPTION 1: Local execution (default)\n# Commands run directly on your machine in the current directory\n# -----------------------------------------------------------------------------\n# Working directory behavior:\n# - CLI (`hermes` command): Uses \".\" (current directory where you run hermes)\n# - Gateway/messaging/cron: Uses terminal.cwd here; legacy .env cwd values are deprecated\nterminal:\n backend: \"local\"\n cwd: \".\" # For local backend: \".\" = current directory. Ignored for remote backends unless a backend documents otherwise.\n timeout: 180\n # HOME policy for tool subprocesses:\n # auto - default: host uses your real HOME; containers use HERMES_HOME/home\n # real - force your real OS-user HOME\n # profile - force HERMES_HOME/home for strict per-profile CLI config isolation\n home_mode: \"auto\"\n docker_mount_cwd_to_workspace: false # SECURITY: off by default. Opt in to mount the launch cwd into Docker /workspace.\n lifetime_seconds: 300\n # sudo_password: \"hunter2\" # Optional: pipe a sudo password via sudo -S. SECURITY WARNING: plaintext.\n # sudo_password: \"\" # Explicit empty password: try empty and never open the interactive sudo prompt.\n\n# -----------------------------------------------------------------------------\n# OPTION 2: SSH remote execution\n# Commands run on a remote server - agent code stays local (sandboxed)\n# Great for: keeping agent isolated from its own code, using powerful remote hardware\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"ssh\"\n# cwd: \"/home/myuser/project\" # Path on the REMOTE server\n# timeout: 180\n# lifetime_seconds: 300\n# ssh_host: \"my-server.example.com\"\n# ssh_user: \"myuser\"\n# ssh_port: 22\n# ssh_key: \"~/.ssh/id_rsa\" # Optional - uses ssh-agent if not specified\n\n# -----------------------------------------------------------------------------\n# OPTION 3: Docker container\n# Commands run in an isolated Docker container\n# Great for: reproducible environments, testing, isolation\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"docker\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /)\n# timeout: 180\n# lifetime_seconds: 300\n# docker_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# docker_mount_cwd_to_workspace: true # Explicit opt-in: mount your launch cwd into /workspace\n# # Optional: run the container as your host user's uid:gid so files written\n# # into bind-mounted dirs are owned by you, not root. Drops SETUID/SETGID\n# # caps too since no gosu privilege drop is needed. Leave off if your\n# # chosen docker_image expects to start as root.\n# docker_run_as_host_user: true\n# # Optional: explicitly forward selected env vars into Docker.\n# # These values come from your current shell first, then ~/.hermes/.env.\n# # Warning: anything forwarded here is visible to commands run in the container.\n# docker_forward_env:\n# - \"GITHUB_TOKEN\"\n# - \"NPM_TOKEN\"\n# # Optional: extra flags passed verbatim to docker run (appended after security defaults).\n# # Useful for adding capabilities (e.g. apt installs needing SETUID) or custom options.\n# # Example: add a Linux capability not included by default\n# # docker_extra_args:\n# # - \"--cap-add\"\n# # - \"SETUID\"\n\n# -----------------------------------------------------------------------------\n# OPTION 4: Singularity/Apptainer container\n# Commands run in a Singularity container (common in HPC environments)\n# Great for: HPC clusters, shared compute environments\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"singularity\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# singularity_image: \"docker://nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 5: Modal cloud execution\n# Commands run on Modal's cloud infrastructure\n# Great for: GPU access, scalable compute, serverless execution\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"modal\"\n# cwd: \"/workspace\" # Path INSIDE the sandbox (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# modal_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 6: Daytona cloud execution\n# Commands run in Daytona cloud sandboxes\n# Great for: Cloud dev environments, persistent workspaces, team collaboration\n# Requires: pip install daytona, DAYTONA_API_KEY env var\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"daytona\"\n# cwd: \"~\"\n# timeout: 180\n# lifetime_seconds: 300\n# daytona_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# container_disk: 10240 # Daytona max is 10GB per sandbox\n\n#\n# --- Container resource limits (docker, singularity, modal, daytona -- ignored for local/ssh) ---\n# These settings apply to all container backends. They control the resources\n# allocated to the sandbox and whether its filesystem persists across sessions.\n container_cpu: 1 # CPU cores\n container_memory: 5120 # Memory in MB (5120 = 5GB)\n container_disk: 51200 # Disk in MB (51200 = 50GB)\n container_persistent: true # Persist filesystem across sessions (false = ephemeral)\n\n# -----------------------------------------------------------------------------\n# SUDO SUPPORT (works with ALL backends above)\n# -----------------------------------------------------------------------------\n# Add sudo_password to any terminal config above to enable sudo commands.\n# The password is piped via `sudo -S`. Works with local, ssh, docker, etc.\n#\n# SECURITY WARNING: Password stored in plaintext!\n#\n# INTERACTIVE PROMPT: If sudo_password is unset and the CLI is running,\n# you'll be prompted to enter your password when sudo is needed:\n# - 45-second timeout (auto-skips if no input)\n# - Press Enter to skip (command fails gracefully)\n# - Password is hidden while typing\n# - Password is cached for the session\n#\n# EMPTY PASSWORDS: Setting sudo_password to an explicit empty string is different\n# from leaving it unset. Hermes will try an empty password via `sudo -S` and\n# will not open the interactive prompt. This is useful for passwordless sudo,\n# Touch ID sudo setups, and environments where prompting is just noise.\n#\n# ALTERNATIVES:\n# - SSH backend: Configure passwordless sudo on the remote server\n# - Containers: Run as root inside the container (no sudo needed)\n# - Local: Configure /etc/sudoers for specific commands\n#\n# Example (add to your terminal section):\n# sudo_password: \"your-password-here\"\n\n# =============================================================================\n# Security Scanning (tirith)\n# =============================================================================\n# Optional pre-exec command security scanning via tirith.\n# Detects homograph URLs, pipe-to-shell, terminal injection, env manipulation.\n# Install: brew install sheeki03/tap/tirith\n# Docs: https://github.com/sheeki03/tirith\n#\n# security:\n# tirith_enabled: true # Enable/disable tirith scanning\n# tirith_path: \"tirith\" # Path to tirith binary (supports ~ expansion)\n# tirith_timeout: 5 # Scan timeout in seconds\n# tirith_fail_open: true # Allow commands if tirith unavailable\n\n# =============================================================================\n# Browser Tool Configuration\n# =============================================================================\nbrowser:\n # Inactivity timeout in seconds - browser sessions are automatically closed\n # after this period of no activity between agent loops (default: 120 = 2 minutes)\n inactivity_timeout: 120\n\n# =============================================================================\n# Tool Loop Guardrails\n# =============================================================================\n# Soft warnings are enabled by default. They append guidance to repeated failed\n# or non-progressing tool results but still let the tool execute. Hard stops are\n# opt-in circuit breakers for autonomous/cron sessions where stopping a loop is\n# preferable to spending the full iteration budget.\ntool_loop_guardrails:\n warnings_enabled: true\n hard_stop_enabled: false\n warn_after:\n exact_failure: 2\n same_tool_failure: 3\n idempotent_no_progress: 2\n hard_stop_after:\n exact_failure: 5\n same_tool_failure: 8\n idempotent_no_progress: 5\n\n# =============================================================================\n# Context Compression (Auto-shrinks long conversations)\n# =============================================================================\n# When conversation approaches model's context limit, middle turns are\n# automatically summarized to free up space while preserving important context.\n#\n# HOW IT WORKS:\n# 1. Tracks actual token usage from API responses (not estimates)\n# 2. When prompt_tokens >= threshold% of model's context_length, triggers compression\n# 3. Protects first 3 turns (system prompt, initial request, first response)\n# 4. Protects last N turns (default 20 messages = ~10 full turns of recent context)\n# 5. Summarizes middle turns using a fast/cheap model\n# 6. Inserts summary as a user message, continues conversation seamlessly\n#\n# Post-compression tail budget is target_ratio × threshold × context_length:\n# 200K context, threshold 0.50, ratio 0.20 → 20K tokens of recent tail preserved\n# 1M context, threshold 0.50, ratio 0.20 → 100K tokens of recent tail preserved\n#\ncompression:\n # Enable automatic context compression (default: true)\n # Set to false if you prefer to manage context manually or want errors on overflow\n enabled: true\n \n # Trigger compression at this % of model's context limit (default: 0.50 = 50%)\n # Lower values = more aggressive compression, higher values = compress later\n # Models with context windows below 512K are floored at 0.75 (raise-only) so\n # compaction doesn't fire with half the window still free; set above 0.75 to override.\n threshold: 0.50\n\n # Existing Codex gpt-5.5 behavior: raise Hermes' compaction trigger to 85%\n # for the ChatGPT Codex OAuth route. Set false to opt back down to threshold.\n codex_gpt55_autoraise: true\n\n # Fraction of the threshold to preserve as recent tail (default: 0.20 = 20%)\n # e.g. 20% of 50% threshold = 10% of total context kept as recent messages.\n # Summary output is separately capped at 12K tokens (Gemini output limit).\n # Range: 0.10 - 0.80\n target_ratio: 0.20\n\n # Number of most-recent messages to always preserve (default: 20 ≈ 10 full turns)\n # Higher values keep more recent conversation intact at the cost of more aggressive\n # compression of older turns.\n protect_last_n: 20\n\n # Codex app-server (codex CLI runtime) thread-compaction mode. The codex\n # agent owns the real thread context on this runtime, so Hermes' summarizer\n # cannot shrink it — compaction goes through the app server instead.\n # native = let Codex decide when to compact its own thread (default)\n # hermes = let Hermes threshold trigger Codex thread/compact/start\n # off = Hermes will not auto-trigger compaction; Codex may still compact natively\n codex_app_server_auto: native\n\n # Number of non-system messages to protect at the head of the transcript, in\n # ADDITION to the system prompt (which is always implicitly protected).\n # Head messages are NEVER summarized — they survive every compression\n # indefinitely. This gives stable early context for short/medium sessions,\n # but in long-running sessions that rely on rolling compaction the pinned\n # opening turns may not match how you want the session framed over time.\n # Set to 0 to preserve ONLY the system prompt (plus the rolling summary\n # and recent tail) — the cleanest configuration for long-running sessions.\n # Default 3 preserves the system prompt plus the first three non-system\n # head messages, matching the pre-feature behaviour.\n protect_first_n: 3\n\n # To pin a specific model/provider for compression summaries, use the\n # auxiliary section below (auxiliary.compression.provider / model).\n\n# =============================================================================\n# Anthropic prompt caching TTL\n# =============================================================================\n# When prompt caching is active (Claude via OpenRouter or native Anthropic),\n# Anthropic supports two TTL tiers for cached prefixes: \"5m\" (default) and\n# \"1h\". Other values are ignored and \"5m\" is used.\n#\nprompt_caching:\n cache_ttl: \"5m\" # use \"1h\" for long sessions with pauses between turns\n\n# =============================================================================\n# Auxiliary Models (Advanced — Experimental)\n# =============================================================================\n# Hermes uses lightweight \"auxiliary\" models for side tasks: image analysis,\n# browser screenshot analysis, web page summarization, TTS audio-tag insertion,\n# and context compression.\n#\n# By default these use Gemini Flash via OpenRouter or Nous Portal and are\n# auto-detected from your credentials. You do NOT need to change anything\n# here for normal usage.\n#\n# WARNING: Overriding these with providers other than OpenRouter or Nous Portal\n# is EXPERIMENTAL and may not work. Not all models/providers support vision,\n# produce usable summaries, or accept the same API format. Change at your own\n# risk — if things break, reset to \"auto\" / empty values.\n#\n# Each task has its own provider + model pair so you can mix providers.\n# For example: OpenRouter for vision (needs multimodal), but your main\n# local endpoint for compression (just needs text).\n#\n# Provider options:\n# \"auto\" - Best available: OpenRouter → Nous Portal → main endpoint (default)\n# \"openrouter\" - Force OpenRouter (requires OPENROUTER_API_KEY)\n# \"nous\" - Force Nous Portal (requires: hermes auth add nous)\n# \"gemini\" - Force Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n# \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY)\n# \"codex\" - Force Codex OAuth (requires: hermes model → Codex).\n# Uses gpt-5.3-codex which supports vision.\n# \"main\" - Use your custom endpoint (OPENAI_BASE_URL + OPENAI_API_KEY).\n# Works with OpenAI API, local models, or any OpenAI-compatible\n# endpoint. Also falls back to Codex OAuth and API-key providers.\n#\n# Model: leave empty to use the provider's default. When empty, OpenRouter\n# uses \"google/gemini-3-flash-preview\" and Nous uses \"gemini-3-flash\".\n# Other providers pick a sensible default automatically.\n#\n# auxiliary:\n# # Image analysis: vision_analyze tool + browser screenshots\n# vision:\n# provider: \"auto\"\n# model: \"\" # e.g. \"google/gemini-2.5-flash\", \"openai/gpt-4o\"\n# timeout: 30 # LLM API call timeout (seconds)\n# download_timeout: 30 # Image HTTP download timeout (seconds)\n# # Increase for slow connections or self-hosted image servers\n# reasoning_effort: \"\" # Per-task thinking level: none, minimal, low, medium,\n# # high, xhigh, max, ultra. Empty = provider default.\n# # Works on every auxiliary task block (vision,\n# # web_extract, compression, title_generation, curator,\n# # background_review, moa_reference, ...). Example: run\n# # compression at \"low\" and vision at \"none\" to cut\n# # side-task latency/cost on reasoning models.\n#\n# # Web page scraping / summarization + browser page text extraction\n# web_extract:\n# provider: \"auto\"\n# model: \"\"\n# reasoning_effort: \"low\"\n#\n# # Gemini 3.1 TTS hidden audio-tag insertion\n# tts_audio_tags:\n# provider: \"auto\" # empty model = your main chat model\n# model: \"\"\n# timeout: 30\n#\n# # Automatic session title generation after the first exchange\n# title_generation:\n# enabled: true # set false to disable auto-title generation\n# provider: \"auto\"\n# model: \"\"\n# timeout: 30\n# language: \"\" # empty = match the user's language; or e.g. \"English\"\n#\n# # Session search — summarizes matching past sessions\n# session_search:\n# provider: \"auto\"\n# model: \"\"\n# timeout: 30\n# max_concurrency: 3 # Limit parallel summaries to reduce request-burst 429s\n# extra_body: {} # Provider-specific OpenAI-compatible request fields\n# # Example for providers that support request-body\n# # reasoning controls:\n# # extra_body:\n# # enable_thinking: false\n# # Some vLLM/Qwen deployments expect this nested:\n# # extra_body:\n# # chat_template_kwargs:\n# # enable_thinking: false\n\n# =============================================================================\n# Persistent Memory\n# =============================================================================\n# Bounded curated memory injected into the system prompt every session.\n# Two stores: MEMORY.md (agent's notes) and USER.md (user profile).\n# Character limits keep the memory small and focused. The agent manages\n# pruning -- when at the limit, it must consolidate or replace entries.\n# Disabled by default in batch_runner.\n#\nmemory:\n # Agent's personal notes: environment facts, conventions, things learned\n memory_enabled: true\n \n # User profile: preferences, communication style, expectations\n user_profile_enabled: true\n \n # Character limits (~2.75 chars per token, model-independent)\n memory_char_limit: 2200 # ~800 tokens\n user_char_limit: 1375 # ~500 tokens\n\n # Periodic memory nudge: remind the agent to consider saving memories\n # every N user turns. Set to 0 to disable. Only active when memory is enabled.\n nudge_interval: 10 # Nudge every 10 user turns (0 = disabled)\n\n # Memory flush: give the agent one turn to save memories before context is\n # lost (compression, /new, /reset, exit). Set to 0 to disable.\n # For exit/reset, only fires if the session had at least this many user turns.\n flush_min_turns: 6 # Min user turns to trigger flush on exit/reset (0 = disabled)\n\n# =============================================================================\n# Session Reset Policy (Messaging Platforms)\n# =============================================================================\n# Controls when messaging sessions (Telegram, Discord, WhatsApp, Slack) are\n# automatically cleared. Default is \"none\": sessions never auto-reset —\n# conversation context lives until you /reset or /new manually, or context\n# compression kicks in. Opt in to automatic resets if you prefer sessions to\n# clear on a schedule (long-lived context increases API cost per message,\n# though prompt caching and compression keep this manageable).\n#\n# When an automatic reset triggers, the agent first saves important\n# information to its persistent memory — but the conversation context is\n# wiped. The agent starts fresh but retains learned facts via its memory\n# system.\n#\n# Modes:\n# \"none\" - Never auto-reset (default); context lives until /reset or compression\n# \"idle\" - Reset after N minutes of inactivity\n# \"daily\" - Reset at a fixed hour each day\n# \"both\" - Reset on EITHER inactivity timeout or daily boundary\n#\nsession_reset:\n mode: none # \"none\", \"idle\", \"daily\", or \"both\"\n idle_minutes: 1440 # Inactivity timeout in minutes (used by \"idle\"/\"both\")\n at_hour: 4 # Daily reset hour, 0-23 local time (used by \"daily\"/\"both\")\n\n# Maximum number of simultaneously active chat sessions across CLI, TUI,\n# dashboard chat, and messaging gateway. Set to null, 0, or omit to allow\n# unlimited concurrent sessions. When the limit is reached, new sessions get a\n# clean error while existing active sessions keep their normal behavior. This\n# top-level key takes precedence over gateway.max_concurrent_sessions. The cap\n# is a best-effort single-host/profile runtime guard; Hermes fails open if the\n# local runtime lease registry cannot be read or locked.\nmax_concurrent_sessions: null\n\n# When true, group/channel chats use one session per participant when the platform\n# provides a user ID. This is the secure default and prevents users in the same\n# room from sharing context, interrupts, and token costs. Set false only if you\n# explicitly want one shared \"room brain\" per group/channel.\ngroup_sessions_per_user: true\n\n# ─────────────────────────────────────────────────────────────────────────────\n# API Server — per-client model routing\n# ─────────────────────────────────────────────────────────────────────────────\n# Route different API clients to different models/providers on a single\n# Hermes deployment. Clients choose a backend by sending a specific string\n# as the OpenAI ``model`` field. Unmapped model values fall back to the\n# global model configured in the ``model:`` section above, and an explicit\n# session /model override always wins over a route.\n#\n# Configure via the ``platforms.api_server.extra.model_routes`` gateway\n# config block:\n#\n# platforms:\n# api_server:\n# enabled: true\n# extra:\n# key: \"your-api-server-secret\"\n# model_routes:\n# # Xiaozhi clients send model=\"minimax-m2\" → routed to MiniMax via OpenRouter\n# minimax-m2:\n# model: \"minimax/minimax-m1\"\n# provider: \"openrouter\" # optional — overrides global provider\n# # api_key: \"sk-...\" # optional — per-route UPSTREAM provider\n# # key (NOT caller auth; never logged)\n# # base_url: \"https://...\" # optional — per-route base URL\n# # GPT clients keep their own alias\n# gpt-5:\n# model: \"openai/gpt-5\"\n# provider: \"openrouter\"\n#\n# Configured aliases are automatically listed by GET /v1/models so clients\n# can discover them without manual coordination. Caller authentication is\n# unchanged: every request still authenticates with the global API server\n# key (``extra.key`` / API_SERVER_KEY).\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Gateway Streaming\n# ─────────────────────────────────────────────────────────────────────────────\n# Stream tokens to messaging platforms in real-time. The bot sends a message\n# on first token, then progressively edits it as more tokens arrive.\n# Disabled by default — enable to try the streaming UX on Telegram/Discord/Slack.\n# For Telegram, partial edits are sent as plain text and only the final edit uses MarkdownV2.\nstreaming:\n enabled: false\n # transport: edit # \"edit\" = progressive editMessageText\n # edit_interval: 0.3 # seconds between message edits\n # buffer_threshold: 40 # chars before forcing an edit flush\n # cursor: \" ▉\" # cursor shown during streaming\n\n# =============================================================================\n# Skills Configuration\n# =============================================================================\n# Skills are reusable procedures the agent can load and follow. The agent can\n# also create new skills after completing complex tasks.\n#\nskills:\n # Nudge the agent to create skills after complex tasks.\n # Every N tool-calling iterations, remind the model to consider saving a skill.\n # Set to 0 to disable.\n creation_nudge_interval: 15\n\n # External skill directories — share skills across tools/agents without\n # copying them into ~/.hermes/skills/. Each path is expanded (~ and ${VAR})\n # and resolved to an absolute path. External dirs are read-only: skill\n # creation always writes to ~/.hermes/skills/. Local skills take precedence\n # when names collide.\n # external_dirs:\n # - ~/.agents/skills\n # - /home/shared/team-skills\n\n# =============================================================================\n# Agent Behavior\n# =============================================================================\nagent:\n # Maximum tool-calling iterations per conversation\n # Higher = more room for complex tasks, but costs more tokens\n # Recommended: 20-30 for focused tasks, 50-100 for open exploration\n max_turns: 60\n\n # Inactivity timeout for gateway agent runs (seconds, 0 = unlimited).\n # The agent can run indefinitely when actively calling tools or receiving\n # API responses. Only fires after the agent has been idle for this duration.\n # gateway_timeout: 1800\n\n # Staged warning: send a warning before escalating to full timeout.\n # Fires once per run when inactivity reaches this threshold (seconds).\n # Set to 0 to disable the warning.\n # gateway_timeout_warning: 900\n\n # Graceful drain timeout for gateway stop/restart (seconds).\n # Default 0 = no drain: a restart interrupts in-flight agents immediately,\n # cleans up, and exits. Set a positive value only if you want a grace\n # window on /restart, and keep it well under systemd's TimeoutStopSec.\n # restart_drain_timeout: 0\n\n # Max app-level retry attempts for API errors (connection drops, provider\n # timeouts, 5xx, etc.) before the agent surfaces the failure. Lower this\n # to 1 if you use fallback providers and want fast failover on flaky\n # primaries (default 3). The OpenAI SDK does its own low-level retries\n # underneath this wrapper — this is the Hermes-level loop.\n # api_max_retries: 3\n\n # After the agent edits code without fresh passing verification, nudge it to\n # verify before finishing. The default \"auto\" enables it on interactive\n # coding surfaces (CLI, TUI, desktop) and programmatic callers, and disables\n # it on conversational messaging surfaces (Telegram, Discord, etc.) where the\n # verification summary would reach a human as chat noise. Set true or false to\n # force it on or off; the HERMES_VERIFY_ON_STOP env var (1/0) takes precedence.\n # verify_on_stop: auto\n\n # Standing operator instructions for the coding posture (when Hermes is in a\n # code workspace). Appended to the coding brief as an extra system block, so\n # you can pin project-wide workflow rules without editing the shipped brief.\n # Accepts a string or a list of strings. Takes effect next session.\n # coding_instructions:\n # - \"For UI work, don't run tsc/lint until I approve the look.\"\n # - \"Clean the diff before you commit and push.\"\n\n # When verify-on-stop finds edited code without fresh verification evidence,\n # append guidance for creative UI work (avoid broad tsc/lint/test before visual\n # approval) and clean-diff expectations. Set false to keep that nudge terse.\n # verify_guidance: true\n\n # A `pre_verify` hook (plugin or shell, see Event Hooks docs) can keep the\n # agent going one more turn to verify/clean before finishing. This caps how\n # many times one turn may be nudged to continue, so a hook can't trap the loop.\n # Default 3.\n # max_verify_nudges: 3\n\n # Enable verbose logging\n verbose: false\n \n # Reasoning effort level (OpenRouter and Nous Portal)\n # Controls how much \"thinking\" the model does before responding.\n # Options: \"xhigh\" (max), \"high\", \"medium\", \"low\", \"minimal\", \"none\" (disable)\n reasoning_effort: \"medium\"\n \n # Per-model reasoning effort overrides (optional dict)\n # Key: any sensible model spelling works (exact, dots↔dashes interchangeable,\n # provider prefix optional). First match wins.\n # Value: reasoning effort level (same options as reasoning_effort)\n # Override the global reasoning_effort for that specific model.\n # NOTE: no `hermes config set` support for this key -- edit YAML directly.\n # reasoning_overrides:\n # \"openrouter/anthropic/claude-opus-4.5\": \"xhigh\"\n # \"openai/gpt-5\": \"low\"\n # \"claude-opus-4.6\": \"high\" # bare model name also works\n # \"deepseek/deepseek-v4-pro\": \"xhigh\" # dots and dashes are interchangeable\n reasoning_overrides: {}\n \n # Predefined personalities (use with /personality command)\n personalities:\n helpful: \"You are a helpful, friendly AI assistant.\"\n concise: \"You are a concise assistant. Keep responses brief and to the point.\"\n technical: \"You are a technical expert. Provide detailed, accurate technical information.\"\n creative: \"You are a creative assistant. Think outside the box and offer innovative solutions.\"\n teacher: \"You are a patient teacher. Explain concepts clearly with examples.\"\n kawaii: \"You are a kawaii assistant! Use cute expressions like (◕‿◕), ★, ♪, and ~! Add sparkles and be super enthusiastic about everything! Every response should feel warm and adorable desu~! ヽ(>∀<☆)ノ\"\n catgirl: \"You are Neko-chan, an anime catgirl AI assistant, nya~! Add 'nya' and cat-like expressions to your speech. Use kaomoji like (=^・ω・^=) and ฅ^•ﻌ•^ฅ. Be playful and curious like a cat, nya~!\"\n pirate: \"Arrr! Ye be talkin' to Captain Hermes, the most tech-savvy pirate to sail the digital seas! Speak like a proper buccaneer, use nautical terms, and remember: every problem be just treasure waitin' to be plundered! Yo ho ho!\"\n shakespeare: \"Hark! Thou speakest with an assistant most versed in the bardic arts. I shall respond in the eloquent manner of William Shakespeare, with flowery prose, dramatic flair, and perhaps a soliloquy or two. What light through yonder terminal breaks?\"\n surfer: \"Duuude! You're chatting with the chillest AI on the web, bro! Everything's gonna be totally rad. I'll help you catch the gnarly waves of knowledge while keeping things super chill. Cowabunga! 🤙\"\n noir: \"The rain hammered against the terminal like regrets on a guilty conscience. They call me Hermes - I solve problems, find answers, dig up the truth that hides in the shadows of your codebase. In this city of silicon and secrets, everyone's got something to hide. What's your story, pal?\"\n uwu: \"hewwo! i'm your fwiendwy assistant uwu~ i wiww twy my best to hewp you! *nuzzles your code* OwO what's this? wet me take a wook! i pwomise to be vewy hewpful >w<\"\n philosopher: \"Greetings, seeker of wisdom. I am an assistant who contemplates the deeper meaning behind every query. Let us examine not just the 'how' but the 'why' of your questions. Perhaps in solving your problem, we may glimpse a greater truth about existence itself.\"\n hype: \"YOOO LET'S GOOOO!!! 🔥🔥🔥 I am SO PUMPED to help you today! Every question is AMAZING and we're gonna CRUSH IT together! This is gonna be LEGENDARY! ARE YOU READY?! LET'S DO THIS! 💪😤🚀\"\n\n# =============================================================================\n# Toolsets\n# =============================================================================\n# Control which tools the agent has access to.\n# Use `hermes tools` to interactively enable/disable tools per platform.\n\n# =============================================================================\n# Platform Toolsets (per-platform tool configuration)\n# =============================================================================\n# Override which toolsets are available on each platform.\n# If a platform isn't listed here, its built-in default is used.\n#\n# You can use EITHER:\n# - A preset like \"hermes-cli\" or \"hermes-telegram\" (curated tool set)\n# - A list of individual toolsets to compose your own (see list below)\n#\n# Supported platform keys: cli, telegram, discord, whatsapp, slack, qqbot, teams, google_chat\n#\n# Examples:\n#\n# # Use presets (same as defaults):\n# platform_toolsets:\n# cli: [hermes-cli]\n# telegram: [hermes-telegram]\n#\n# # Custom: give Telegram only web + terminal + file + planning:\n# platform_toolsets:\n# telegram: [web, terminal, file, todo]\n#\n# # Custom: CLI without browser or image gen:\n# platform_toolsets:\n# cli: [web, terminal, file, skills, todo, tts, cronjob]\n#\n# # Restrictive: Discord gets read-only tools only:\n# platform_toolsets:\n# discord: [web, vision, skills, todo]\n#\n# If not set, defaults are:\n# cli: hermes-cli (everything + cronjob management)\n# telegram: hermes-telegram (terminal, file, web, vision, image, tts, browser, skills, todo, cronjob, messaging)\n# discord: hermes-discord (same as telegram)\n# whatsapp: hermes-whatsapp (same as telegram)\n# slack: hermes-slack (same as telegram)\n# signal: hermes-signal (same as telegram)\n# homeassistant: hermes-homeassistant (same as telegram)\n# qqbot: hermes-qqbot (same as telegram)\n# teams: hermes-teams (same as telegram)\n# google_chat: hermes-google_chat (same as telegram)\n#\nplatform_toolsets:\n cli: [hermes-cli]\n telegram: [hermes-telegram]\n discord: [hermes-discord]\n whatsapp: [hermes-whatsapp]\n slack: [hermes-slack]\n signal: [hermes-signal]\n homeassistant: [hermes-homeassistant]\n qqbot: [hermes-qqbot]\n yuanbao: [hermes-yuanbao]\n teams: [hermes-teams]\n google_chat: [hermes-google_chat]\n\n# =============================================================================\n# Gateway Platform Settings\n# =============================================================================\n# Optional per-platform messaging settings.\n# Platform-specific knobs live under `extra`.\n#\n# platforms:\n# telegram:\n# reply_to_mode: \"first\" # off | first | all\n# # guest_mode lets explicit @mentions from non-allowlisted groups through.\n# # Default false; ordinary messages, replies, and regex wake words stay blocked.\n# guest_mode: false\n# # allowed_chats: [\"-1001234567890\"]\n# extra:\n# disable_link_previews: false # Set true to suppress Telegram URL previews in bot messages\n# rich_messages: false # Bot API 10.1 rich messages (tables/task lists/details/math); default false for copyable legacy MarkdownV2, set true to opt in\n# rich_drafts: false # Experimental rich draft previews during Telegram DM streaming; default false because Telegram Desktop/macOS can visually overlay draft frames\n# command_menu:\n# # Telegram allows up to 100 BotCommands; Hermes defaults to 60 so\n# # all built-in commands plus common skill commands stay visible\n# # while remaining under Telegram's payload-size limit. Clamped 1..100.\n# max_commands: 60\n# # prepend = user priority first, then Hermes defaults\n# # append = Hermes defaults first, then user priority\n# # replace = only the list below defines priority\n# priority_mode: prepend\n# priority:\n# - my_plugin_command\n# webhook:\n# extra:\n# # Route scripts default to a 30 second timeout. Scripts must live under\n# # the active profile's scripts directory and receive webhook JSON on stdin.\n# script_timeout_seconds: 30\n#\n# Discord-specific settings (config.yaml top-level, not under platforms:):\n#\n# discord:\n# require_mention: true # Require @mention in server channels (default: true)\n# auto_thread: true # Auto-create thread on @mention (default: true)\n# free_response_channels: \"\" # Channel IDs where no mention is needed\n# reactions: true # Show processing reactions (default: true)\n# history_backfill: true # Recover missed channel messages on mention (default: true)\n# history_backfill_limit: 50 # Max messages to scan backwards (default: 50)\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Available toolsets (use these names in platform_toolsets or the toolsets list)\n#\n# Run `hermes chat --list-toolsets` to see all toolsets and their tools.\n# Run `hermes chat --list-tools` to see every individual tool with descriptions.\n# ─────────────────────────────────────────────────────────────────────────────\n#\n# INDIVIDUAL TOOLSETS (compose your own):\n# web - web_search, web_extract\n# search - web_search only (no scraping)\n# terminal - terminal, process\n# file - read_file, write_file, patch, search\n# browser - browser_navigate, browser_snapshot, browser_click, browser_type,\n# browser_scroll, browser_back, browser_press,\n# browser_get_images, browser_vision (requires BROWSERBASE_API_KEY)\n# vision - vision_analyze (requires OPENROUTER_API_KEY)\n# image_gen - image_generate (requires FAL_KEY)\n# skills - skills_list, skill_view\n# skills_hub - skill_hub (search/install/manage from online registries — user-driven only)\n# todo - todo (in-memory task planning, no deps)\n# tts - text_to_speech (Edge TTS free, or ELEVENLABS/OPENAI/MINIMAX/MISTRAL key)\n# cronjob - cronjob (create/list/update/pause/resume/run/remove scheduled tasks)\n#\n# PRESETS (curated bundles):\n# hermes-cli - All of the above except send_message\n# hermes-telegram - terminal, file, web, vision, image_gen, tts, browser,\n# skills, todo, cronjob, send_message\n# hermes-discord - Same as hermes-telegram\n# hermes-whatsapp - Same as hermes-telegram\n# hermes-slack - Same as hermes-telegram\n#\n# COMPOSITE:\n# debugging - terminal + web + file\n# safe - web + vision (no terminal access)\n# all - Everything available\n#\n# web - Web search and content extraction (web_search, web_extract)\n# search - Web search only, no scraping (web_search)\n# terminal - Command execution and process management (terminal, process)\n# file - File operations: read, write, patch, search\n# browser - Full browser automation (navigate, click, type, screenshot, etc.)\n# vision - Image analysis (vision_analyze)\n# image_gen - Image generation with FLUX (image_generate)\n# skills - Load skill documents (skills_list, skill_view)\n# todo - Task planning and tracking for multi-step work\n# memory - Persistent memory across sessions (personal notes + user profile)\n# session_search - Search and recall past conversations (FTS5 + Gemini Flash summarization)\n# tts - Text-to-speech (Edge TTS free, ElevenLabs, OpenAI, MiniMax, Mistral)\n# cronjob - Schedule and manage automated tasks (CLI-only)\n#\n# Composite toolsets:\n# debugging - terminal + web + file (for troubleshooting)\n# safe - web + vision (no terminal access)\n\n# NOTE: The top-level \"toolsets\" key is deprecated and ignored.\n# Tool configuration is managed per-platform via platform_toolsets above.\n# Use `hermes tools` to configure interactively, or edit platform_toolsets directly.\n#\n# CLI override: hermes chat --toolsets terminal,web,file\n\n# =============================================================================\n# MCP (Model Context Protocol) Servers\n# =============================================================================\n# Connect to external MCP servers to add tools from the MCP ecosystem.\n# Each server's tools are automatically discovered and registered.\n# See website/docs/user-guide/features/mcp.md for full documentation.\n#\n# Stdio servers (spawn a subprocess):\n# command: the executable to run\n# args: command-line arguments\n# env: environment variables (only these + safe defaults passed to subprocess)\n#\n# HTTP servers (connect to a URL):\n# url: the MCP server endpoint\n# headers: HTTP headers (e.g., for authentication)\n#\n# Optional per-server settings:\n# timeout: tool call timeout in seconds (default: 120)\n# connect_timeout: initial connection timeout (default: 60)\n# keepalive_interval: liveness ping cadence in seconds (default: 180).\n# Lower it below the server's session TTL for servers that expire idle\n# sessions quickly (e.g. Unreal Engine editor MCP, ~15s), otherwise idle\n# tool calls hit an expired session and pay a slow reconnect. Floored at 5s.\n#\n# mcp_servers:\n# time:\n# command: uvx\n# args: [\"mcp-server-time\"]\n# filesystem:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-filesystem\", \"/home/user\"]\n# notion:\n# url: https://mcp.notion.com/mcp\n# github:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-github\"]\n# env:\n# GITHUB_PERSONAL_ACCESS_TOKEN: \"ghp_...\"\n#\n# Sampling (server-initiated LLM requests) — enabled by default.\n# Per-server config under the 'sampling' key:\n# analysis:\n# command: npx\n# args: [\"-y\", \"analysis-server\"]\n# sampling:\n# enabled: true # default: true\n# model: \"gemini-3-flash\" # override model (optional)\n# max_tokens_cap: 4096 # max tokens per request\n# timeout: 30 # LLM call timeout (seconds)\n# max_rpm: 10 # max requests per minute\n# allowed_models: [] # model whitelist (empty = all)\n# max_tool_rounds: 5 # tool loop limit (0 = disable)\n# log_level: \"info\" # audit verbosity\n\n# =============================================================================\n# Text-to-Speech\n# =============================================================================\n# TTS defaults to Edge TTS unless changed in ~/.hermes/config.yaml.\n# Gemini TTS supports persona/director prompt files, and Gemini 3.1 Flash TTS\n# can use a hidden auxiliary rewrite pass to insert expressive square-bracket\n# audio tags into the TTS script without showing tags in chat.\n#\n# tts:\n# provider: \"gemini\"\n# gemini:\n# model: \"gemini-3.1-flash-tts-preview\"\n# voice: \"Kore\"\n# audio_tags: false\n# persona_prompt_file: \"\" # e.g. ~/.hermes/tts/radio-host.md\n\n# =============================================================================\n# Voice Transcription (Speech-to-Text)\n# =============================================================================\n# Automatically transcribe voice messages on messaging platforms.\n# Providers: local (free, faster-whisper) | groq (free tier) | openai (Whisper API) | mistral (Voxtral Transcribe)\n# Set the corresponding API key in .env: GROQ_API_KEY, OPENAI_API_KEY, or MISTRAL_API_KEY.\nstt:\n enabled: true\n # provider: \"local\" # auto-detected if omitted\n local:\n model: \"base\" # tiny | base | small | medium | large-v3 | turbo\n # language: \"\" # auto-detect; set to \"en\", \"es\", \"fr\", etc. to force\n openai:\n model: \"whisper-1\" # whisper-1 | gpt-4o-mini-transcribe | gpt-4o-transcribe\n # mistral:\n # model: \"voxtral-mini-latest\" # voxtral-mini-latest | voxtral-mini-2602\n # deepinfra:\n # # Model id is discovered live from the DeepInfra catalog filtered\n # # by the `stt` surface tag — leave `model` blank to take the first\n # # live result. Pin only when you need a specific Whisper variant.\n # model: \"\"\n\n# Text-to-speech. Only the deepinfra block is documented here — the\n# remaining providers (edge, openai, xai, minimax, mistral, gemini,\n# elevenlabs, neutts, kittentts, piper) inherit sensible defaults from\n# DEFAULT_CONFIG in hermes_cli/config.py.\n# tts:\n# provider: \"deepinfra\"\n# deepinfra:\n# # Model id is discovered live from the DeepInfra catalog filtered\n# # by the `tts` surface tag — leave `model` blank to take the first\n# # live result.\n# model: \"\"\n# voice: \"default\"\n\n# Image generation. Each provider plugin reads its own ``image_gen.``\n# block; deepinfra discovers models live from\n# api.deepinfra.com/v1/openai/models filtered by the ``image-gen`` tag —\n# no model id is hardcoded, so retired models disappear automatically.\n# image_gen:\n# provider: \"deepinfra\"\n# deepinfra:\n# # Leave `model` blank for the first live `image-gen`-tagged result.\n# model: \"\"\n\n# =============================================================================\n# Response Pacing (Messaging Platforms)\n# =============================================================================\n# Add human-like delays between message chunks.\n# human_delay:\n# mode: \"off\" # \"off\" | \"natural\" | \"custom\"\n# min_ms: 800 # Min delay (custom mode only)\n# max_ms: 2500 # Max delay (custom mode only)\n\n# =============================================================================\n# Session Logging\n# =============================================================================\n# Session trajectories are automatically saved to logs/ directory.\n# Each session creates: logs/session_YYYYMMDD_HHMMSS_UUID.json\n#\n# The session ID is displayed in the welcome banner for easy reference.\n# Logs contain full conversation history in trajectory format:\n# - System prompt, user messages, assistant responses\n# - Tool calls with inputs/outputs\n# - Timestamps for debugging\n#\n# No configuration needed - logging is always enabled.\n# To disable, you would need to modify the source code.\n\n# =============================================================================\n# Code Execution Sandbox (Programmatic Tool Calling)\n# =============================================================================\n# The execute_code tool runs Python scripts that call Hermes tools via RPC.\n# Intermediate tool results stay out of the LLM's context window.\ncode_execution:\n timeout: 300 # Max seconds per script before kill (default: 300 = 5 min)\n max_tool_calls: 50 # Max RPC tool calls per execution (default: 50)\n\n# =============================================================================\n# Subagent Delegation\n# =============================================================================\n# The delegate_task tool spawns child agents with isolated context.\n# Supports single tasks and batch mode (default 3 parallel, configurable).\ndelegation:\n max_iterations: 50 # Max tool-calling turns per child (default: 50)\n # max_concurrent_children: 3 # Max parallel child agents per batch (default: 3, floor: 1, no ceiling).\n # WARNING: values above 10 multiply API cost linearly.\n # max_spawn_depth: 1 # Delegation tree depth cap (range: 1-3, default: 1 = flat).\n # Raise to 2 to allow workers to spawn their own subagents.\n # Requires role=\"orchestrator\" on intermediate agents.\n # orchestrator_enabled: true # Kill switch for role=\"orchestrator\" children (default: true).\n # subagent_auto_approve: false # When a subagent hits a dangerous-command approval prompt, auto-deny (default: false)\n # or auto-approve \"once\" (true) instead of blocking on stdin.\n # The parent TUI owns stdin, so blocking would deadlock; non-interactive resolution is required.\n # Both choices emit a logger.warning audit line. Flip to true only for cron/batch pipelines.\n # inherit_mcp_toolsets: true # When explicit child toolsets are narrowed, also keep the parent's MCP toolsets (default: true). Set false for strict intersection.\n # model: \"google/gemini-3-flash-preview\" # Override model for subagents (empty = inherit parent)\n # provider: \"openrouter\" # Override provider for subagents (empty = inherit parent)\n # # Resolves full credentials (base_url, api_key) automatically.\n # # Supported: openrouter, nous, zai, kimi-coding, minimax\n\n# =============================================================================\n# Honcho Integration (Cross-Session User Modeling)\n# =============================================================================\n# AI-native persistent memory via Honcho (https://honcho.dev/).\n# Builds a deeper understanding of the user across sessions and tools.\n# Runs alongside USER.md — additive, not a replacement.\n#\n# Requires: pip install honcho-ai\n# Config: ~/.honcho/config.json (shared with Claude Code, Cursor, etc.)\n# API key: HONCHO_API_KEY in ~/.hermes/.env or ~/.honcho/config.json\n#\n# Hermes-specific overrides (optional — most config comes from ~/.honcho/config.json):\n# honcho: {}\n\n# =============================================================================\n# Display\n# =============================================================================\ndisplay:\n # Use compact banner mode (hides the ASCII-art banner, shows a single line).\n # true: Compact single-line banner\n # false: Full ASCII banner with tool/skill summary (default)\n compact: false\n\n # Tool progress display level (CLI and gateway)\n # off: Silent — no tool activity shown, just the final response\n # new: Show a tool indicator only when the tool changes (skip repeats)\n # all: Show every tool call with a short preview (default)\n # verbose: Full args, results, and debug logs (same as /verbose)\n # log: Silent in chat; append every tool call to ~/.hermes/logs/tool_calls.log (gateway only)\n # Toggle at runtime with /verbose in the CLI\n tool_progress: all\n\n # Per-platform defaults can be quieter than the global setting. Telegram\n # tunes for mobile: tool_progress and busy_ack_detail default off (no\n # per-tool breadcrumb stream, no \"iteration 21/60\" debug detail in busy\n # acks or heartbeats), but interim_assistant_messages and\n # long_running_notifications STAY ON so the user has real signal between\n # turn start and final answer (mid-turn assistant commentary + a single\n # edit-in-place \"⏳ Working — N min\" heartbeat). Override under\n # display.platforms.telegram.\n\n # Auto-cleanup of temporary progress bubbles after the final response lands.\n # On platforms that support message deletion (currently Telegram), this\n # removes the tool-progress bubble, \"⏳ Still working...\" notices, and\n # context-pressure status messages once the final reply has been delivered —\n # keeping long-running turns visible live, then tidy afterward. Failed runs\n # leave the bubbles in place as breadcrumbs. Off by default.\n # Per-platform override: display.platforms.telegram.cleanup_progress\n # true: Delete tracked progress/status bubbles on successful turn\n # false: Leave everything in place (default)\n # Example:\n # display:\n # platforms:\n # telegram:\n # cleanup_progress: true\n cleanup_progress: false\n\n # Natural mid-turn assistant updates.\n # On gateway platforms, when true, completed assistant status messages are\n # sent as separate chat messages. On the Desktop app, when true, mid-turn\n # assistant narration streamed between tool calls is kept in the transcript\n # instead of the bubble collapsing to only the final message on completion.\n # Independent of tool_progress and gateway streaming.\n # true: Keep/send mid-turn assistant updates (default)\n # false: Only keep/send the final response\n interim_assistant_messages: true\n\n # Gateway-only long-running status heartbeats.\n # When false, the platform does not receive periodic \"⏳ Working — N min\"\n # notifications even if agent.gateway_notify_interval is non-zero. The\n # heartbeat edits a single message in place (where the adapter supports\n # editing) instead of posting a new bubble each interval.\n # Default: true everywhere, including Telegram (silent agents are worse\n # than a single edit-in-place heartbeat).\n long_running_notifications: true\n\n # Include detailed iteration/tool/status context in busy acknowledgments\n # and long-running heartbeats. When true, busy acks show \"iteration 21/60,\n # terminal, 10 min\" and the heartbeat shows \"⏳ Working — 12 min,\n # iteration 21/60, terminal\". When false (Telegram default), both stay\n # terse: \"Interrupting current task\" and \"⏳ Working — 12 min, terminal\".\n busy_ack_detail: true\n\n # What Enter does when Hermes is already busy (CLI and gateway platforms).\n # interrupt: Interrupt the current run and redirect Hermes (default)\n # queue: Queue your message for the next turn\n # steer: Inject your message mid-run via /steer, arriving at the agent\n # after the next tool call — no interrupt, no role violation.\n # Falls back to 'queue' if the agent isn't running yet or if\n # images are attached (steer only carries text).\n # Ctrl+C (or /stop in gateway) always interrupts regardless of this setting.\n # Toggle at runtime with /busy .\n busy_input_mode: interrupt\n\n # Background process notifications (gateway/messaging only).\n # Controls how chatty the process watcher is when you use\n # terminal(background=true, notify_on_complete=true) from Telegram/Discord/etc.\n # off: No watcher messages at all\n # result: Only the final completion message\n # error: Only the final message when exit code != 0\n # all: Running output updates + final message (default)\n background_process_notifications: all\n\n\n # Play terminal bell when agent finishes a response.\n # Useful for long-running tasks — your terminal will ding when the agent is done.\n # Works over SSH. Most terminals can be configured to flash the taskbar or play a sound.\n # true: Ring the terminal bell on each response\n # false: Silent (default)\n bell_on_complete: false\n\n # Show model reasoning/thinking before each response.\n # When enabled, a dim box shows the model's thought process above the response.\n # Toggle at runtime with /reasoning show or /reasoning hide.\n # true: Show the reasoning box\n # false: Hide reasoning (default)\n show_reasoning: false\n\n # Stream tokens to the terminal as they arrive instead of waiting for the\n # full response. The response box opens on first token and text appears\n # line-by-line. Tool calls are still captured silently.\n # true: Stream tokens as they arrive (default)\n # false: Wait for the full response before rendering\n streaming: true\n\n # Show [HH:MM] timestamps on user input and assistant response labels.\n # timestamps: false\n\n # ───────────────────────────────────────────────────────────────────────────\n # Skin / Theme\n # ───────────────────────────────────────────────────────────────────────────\n # Customize CLI visual appearance — banner colors, spinner faces, tool prefix,\n # response box label, and branding text. Change at runtime with /skin .\n #\n # Built-in skins:\n # default — Classic Hermes gold/kawaii\n # ares — Crimson/bronze war-god theme with spinner wings\n # mono — Clean grayscale monochrome\n # slate — Cool blue developer-focused\n # daylight — Bright light-mode theme\n # warm-lightmode — Warm paper-tone light-mode theme\n # poseidon — Sea-green/teal Olympian theme\n # sisyphus — Earthy stone-and-moss theme\n # charizard — Fiery orange dragon theme\n #\n # Custom skins: drop a YAML file in ~/.hermes/skins/.yaml\n # Schema (all fields optional, missing values inherit from default):\n #\n # name: my-theme\n # description: Short description\n # colors:\n # banner_border: \"#HEX\" # Panel border\n # banner_title: \"#HEX\" # Panel title\n # banner_accent: \"#HEX\" # Section headers (Available Tools, etc.)\n # banner_dim: \"#HEX\" # Dim/muted text\n # banner_text: \"#HEX\" # Body text (tool names, skill names)\n # ui_accent: \"#HEX\" # UI accent color\n # response_border: \"#HEX\" # Response box border color\n # spinner:\n # waiting_faces: [\"(⚔)\", \"(⛨)\"] # Faces shown while waiting\n # thinking_faces: [\"(⚔)\", \"(⌁)\"] # Faces shown while thinking\n # thinking_verbs: [\"forging\", \"plotting\"] # Verbs for spinner messages\n # wings: # Optional left/right spinner decorations\n # - [\"⟪⚔\", \"⚔⟫\"]\n # - [\"⟪▲\", \"▲⟫\"]\n # branding:\n # agent_name: \"My Agent\" # Banner title and branding\n # welcome: \"Welcome message\" # Shown at CLI startup\n # response_label: \" ⚔ Agent \" # Response box header label\n # prompt_symbol: \"⚔\" # Prompt symbol (bare token; renderers add trailing space)\n # tool_prefix: \"╎\" # Tool output line prefix (default: ┊)\n #\n skin: default\n\n# =============================================================================\n# Model Aliases — short names for /model command\n# =============================================================================\n# Map short aliases to exact (model, provider, base_url) tuples.\n# Used by /model tab completion and resolve_alias().\n# Aliases are checked BEFORE the models.dev catalog, so they can route\n# to endpoints not in the catalog (e.g. Ollama Cloud, local servers).\n#\n# model_aliases:\n# opus:\n# model: claude-opus-4-6\n# provider: anthropic\n# qwen:\n# model: \"qwen3.5:397b\"\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n# glm:\n# model: glm-4.7\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n\n# =============================================================================\n# Privacy\n# =============================================================================\n# privacy:\n# # Redact PII from the LLM context prompt.\n# # When true, phone numbers are stripped and user/chat IDs are replaced\n# # with deterministic hashes before being sent to the model.\n# # Names and usernames are NOT affected (user-chosen, publicly visible).\n# # Routing/delivery still uses the original values internally.\n# redact_pii: false\n\n# =============================================================================\n# Shell-script hooks\n# =============================================================================\n# Register shell scripts as plugin-hook callbacks. Each entry is executed as\n# a subprocess (shell=False, shlex.split) with a JSON payload on stdin. On\n# stdout the script may return JSON that either blocks the tool call or\n# injects context into the next LLM call.\n#\n# Valid events (mirror hermes_cli.plugins.VALID_HOOKS):\n# pre_tool_call, post_tool_call, pre_llm_call, post_llm_call,\n# pre_api_request, post_api_request, on_session_start, on_session_end,\n# on_session_finalize, on_session_reset, subagent_stop\n#\n# First-use consent: each (event, command) pair prompts once on a TTY, then\n# is persisted to ~/.hermes/shell-hooks-allowlist.json. Non-interactive\n# runs (gateway, cron) need --accept-hooks, HERMES_ACCEPT_HOOKS=1, or the\n# hooks_auto_accept key below.\n#\n# See website/docs/user-guide/features/hooks.md for the full JSON wire\n# protocol and worked examples.\n#\n# hooks:\n# pre_tool_call:\n# - matcher: \"terminal\"\n# command: \"~/.hermes/agent-hooks/block-rm-rf.sh\"\n# timeout: 10\n# post_tool_call:\n# - matcher: \"write_file|patch\"\n# command: \"~/.hermes/agent-hooks/auto-format.sh\"\n# pre_llm_call:\n# - command: \"~/.hermes/agent-hooks/inject-cwd-context.sh\"\n# subagent_stop:\n# - command: \"~/.hermes/agent-hooks/log-orchestration.sh\"\n#\n# hooks_auto_accept: false\n\n\n# =============================================================================\n# Update Behavior\n# =============================================================================\nupdates:\n # Create a full HERMES_HOME zip before every `hermes update`.\n # Backups land in ~/.hermes/backups/ and can be restored with `hermes import`.\n # Off by default because large homes can add minutes to every update.\n pre_update_backup: false\n\n # Number of pre-update backup zips to retain.\n backup_keep: 5\n\n # What non-interactive updates do with local source edits in the Hermes repo.\n # Interactive terminal updates always prompt before restoring the autostash.\n #\n # stash - auto-stash before pull, then auto-restore after success (default)\n # discard - drop the update-created stash after success; use only on managed\n # installs where local source edits should not persist\n non_interactive_local_changes: \"stash\"\n\n\n# =============================================================================\n# Web Dashboard\n# =============================================================================\n# OAuth gate configuration for `hermes dashboard --host `.\n# The bundled Nous Portal plugin reads these on startup; settings here are\n# the canonical surface. Each can be overridden by an environment variable:\n#\n# dashboard.oauth.client_id <- HERMES_DASHBOARD_OAUTH_CLIENT_ID\n# dashboard.oauth.portal_url <- HERMES_DASHBOARD_PORTAL_URL\n# dashboard.public_url <- HERMES_DASHBOARD_PUBLIC_URL\n#\n# Env wins when set to a non-empty value. This is what Fly.io's platform-\n# secret injection uses to push per-deploy client_ids without needing to\n# bake a config.yaml into the image. Empty env values are treated as unset\n# so a provisioned-but-not-populated secret can't shadow a valid entry here.\n#\n# Local dev / on-prem deploys should typically set these via config.yaml\n# (the ~/.hermes/.env file is reserved for API keys and secrets).\n#\n# dashboard:\n# oauth:\n# client_id: \"\" # agent:{instance_id}; Portal provisions this at deploy\n# portal_url: \"\" # blank → default https://portal.nousresearch.com\n#\n# # Force the absolute base URL the OAuth callback (and any other public\n# # URL the dashboard hands to external systems) is built from. Set this\n# # for deploys behind reverse proxies that don't reliably forward\n# # X-Forwarded-Host / X-Forwarded-Proto / X-Forwarded-Prefix (manual\n# # nginx setups, on-prem ingresses, custom-domain Fly deploys without\n# # full proxy header chains).\n# #\n# # When set, the value is the complete authority: scheme + host +\n# # optional path prefix (e.g. \"https://example.com/hermes\"). The OAuth\n# # callback URL becomes \"/auth/callback\" — X-Forwarded-Prefix\n# # is IGNORED on this code path because the operator has explicitly\n# # declared the public URL and we no longer need to guess.\n# #\n# # Leave empty to use the existing proxy-header reconstruction (the\n# # default — works on Fly.io out of the box).\n# #\n# # public_url: \"https://example.com/hermes\"\n#\n# -----------------------------------------------------------------------------\n# Self-hosted OIDC dashboard auth (generic OpenID Connect — Authentik,\n# Keycloak, Zitadel, Authelia, Auth0, Okta, Google, …). Use this INSTEAD of the\n# nous block above when gating the dashboard with your own identity provider.\n# Each setting can be overridden by an environment variable:\n#\n# dashboard.oauth.self_hosted.issuer <- HERMES_DASHBOARD_OIDC_ISSUER\n# dashboard.oauth.self_hosted.client_id <- HERMES_DASHBOARD_OIDC_CLIENT_ID\n# dashboard.oauth.self_hosted.scopes <- HERMES_DASHBOARD_OIDC_SCOPES\n# dashboard.oauth.self_hosted.client_secret <- HERMES_DASHBOARD_OIDC_CLIENT_SECRET\n#\n# dashboard:\n# oauth:\n# provider: self-hosted\n# self_hosted:\n# issuer: \"https://auth.example.com/application/o/hermes/\" # required\n# client_id: \"hermes-dashboard\" # required\n# scopes: \"openid profile email\" # optional\n#\n# # OPTIONAL — set ONLY if your IDP registered the client as\n# # *confidential* (Authentik / Keycloak often default to this). When\n# # set, Hermes authenticates the client at the token endpoint\n# # (client_secret_basic or client_secret_post, auto-selected from the\n# # IDP's discovery doc) IN ADDITION to PKCE. Leave unset for a public\n# # (PKCE-only) client — the common case.\n# #\n# # This is a CREDENTIAL: prefer setting HERMES_DASHBOARD_OIDC_CLIENT_SECRET\n# # in ~/.hermes/.env over putting it here in config.yaml.\n# # client_secret: \"\"\n\n# =============================================================================\n# External secret sources\n# =============================================================================\n# Pull provider credentials from external secret managers at process startup\n# instead of storing them in ~/.hermes/.env. Only the manager's bootstrap\n# credential (e.g. BWS_ACCESS_TOKEN / OP_SERVICE_ACCOUNT_TOKEN) lives in .env\n# (or your shell / desktop session); everything else rotates centrally.\n# Failures never block startup — Hermes warns once and continues with\n# whatever .env already had.\n#\n# Multiple sources can be enabled at once:\n# - \"mapped\" sources (explicit VAR -> ref bindings, e.g. 1Password's env:\n# map) beat \"bulk\" sources (whole-project dumps like Bitwarden BSM)\n# - within a shape, the first source to claim a var wins; later claims\n# are skipped with a startup warning (never a silent clobber)\n# - a source's override_existing lets it beat .env/shell values, but\n# never another secret source's claim\n# Docs: https://hermes-agent.nousresearch.com/docs/user-guide/secrets/\n#\n# secrets:\n# # Optional explicit ordering of enabled sources.\n# # sources: [onepassword, bitwarden]\n#\n# # ---- Bitwarden Secrets Manager (bws CLI) --------------------------------\n# bitwarden:\n# enabled: false\n# access_token_env: BWS_ACCESS_TOKEN # bootstrap token, sourced from .env\n# project_id: \"\" # UUID of the BSM project to sync\n# server_url: \"\" # \"\" = US Cloud; EU/self-hosted URL otherwise\n# cache_ttl_seconds: 300 # 0 disables caching\n# override_existing: true # BSM values win over existing env\n# auto_install: true # lazy-download bws into ~/.hermes/bin\n#\n# # ---- 1Password (op CLI) -------------------------------------------------\n# onepassword:\n# enabled: false\n# # Map env-var names to op:// secret references. Each is resolved with a\n# # single `op read` at startup.\n# env:\n# OPENAI_API_KEY: \"op://Private/OpenAI/api key\"\n# ANTHROPIC_API_KEY: \"op://Private/Anthropic/credential\"\n# account: \"\" # op --account shorthand; \"\" = default\n# service_account_token_env: OP_SERVICE_ACCOUNT_TOKEN # headless auth; unset = desktop session\n# binary_path: \"\" # \"\" = resolve op via PATH; else absolute path\n# cache_ttl_seconds: 300 # 0 disables BOTH cache layers\n# override_existing: true # resolved values win over existing env\n"}, "c6ec7db356f21b9c464014bffb01fb94ac869d56": {"t": 1785767855.9856381, "n": 78007, "h": "849a670619fa8405", "c": "# Hermes Agent CLI Configuration\n# Copy this file to cli-config.yaml and customize as needed.\n# This file configures the CLI behavior. Environment variables in .env take precedence.\n\n# =============================================================================\n# Model Configuration\n# =============================================================================\nmodel:\n # Default model to use (can be overridden with --model flag)\n # Both \"default\" and \"model\" work as the key name here.\n default: \"anthropic/claude-opus-4.6\"\n \n # Inference provider selection:\n # \"auto\" - Auto-detect from credentials (default)\n # \"openrouter\" - OpenRouter (requires: OPENROUTER_API_KEY or OPENAI_API_KEY)\n # \"nous\" - Nous Portal OAuth (requires: hermes auth add nous)\n # \"nous-api\" - Nous Portal API key (requires: NOUS_API_KEY)\n # \"anthropic\" - Direct Anthropic API (requires: ANTHROPIC_API_KEY)\n # \"openai-codex\" - OpenAI Codex (requires: hermes auth)\n # \"copilot\" - GitHub Copilot / GitHub Models (requires: GITHUB_TOKEN)\n # \"gemini\" - Use Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n # \"zai\" - Use z.ai / ZhipuAI GLM models (requires: GLM_API_KEY)\n # \"kimi-coding\" - Kimi / Moonshot AI (requires: KIMI_API_KEY)\n # \"minimax\" - MiniMax global (requires: MINIMAX_API_KEY)\n # \"minimax-cn\" - MiniMax China (requires: MINIMAX_CN_API_KEY)\n # \"huggingface\" - Hugging Face Inference (requires: HF_TOKEN)\n # \"nvidia\" - NVIDIA NIM / build.nvidia.com (requires: NVIDIA_API_KEY)\n # \"xiaomi\" - Xiaomi MiMo (requires: XIAOMI_API_KEY)\n # \"arcee\" - Arcee AI Trinity models (requires: ARCEEAI_API_KEY)\n # \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY — https://ollama.com/settings)\n # \"deepinfra\" - DeepInfra (requires: DEEPINFRA_API_KEY)\n # \"kilocode\" - KiloCode gateway (requires: KILOCODE_API_KEY)\n # \"azure-foundry\" - Microsoft Foundry / Azure OpenAI (API key or Entra ID)\n # \"lmstudio\" - LM Studio local server (optional: LM_API_KEY, defaults to http://127.0.0.1:1234/v1)\n #\n # Local servers (LM Studio, Ollama, vLLM, llama.cpp):\n # \"custom\" - Any other OpenAI-compatible endpoint. Set base_url below.\n # Aliases: \"ollama\", \"vllm\", \"llamacpp\" all map to \"custom\".\n # LM Studio is first-class and uses provider: \"lmstudio\".\n # It works with both no-auth and auth-enabled server modes.\n #\n # Can also be overridden for a single invocation with the --provider flag.\n provider: \"auto\"\n \n # API configuration (falls back to OPENROUTER_API_KEY env var)\n # api_key: \"your-key-here\" # Uncomment to set here instead of .env\n base_url: \"https://openrouter.ai/api/v1\"\n\n # Azure Foundry keyless auth example:\n # provider: \"azure-foundry\"\n # base_url: \"https://.openai.azure.com/openai/v1\"\n # auth_mode: \"entra_id\" # DefaultAzureCredential: az login, managed identity, workload identity, etc.\n # default: \"gpt-4o\" # Deployment/model name\n # entra:\n # scope: \"https://ai.azure.com/.default\" # Optional; this is the default.\n\n # ── Token limits — two settings, easy to confuse ──────────────────────────\n #\n # context_length: TOTAL context window (input + output tokens combined).\n # Controls when Hermes compresses history and validates requests.\n # Leave unset — Hermes auto-detects the correct value from the provider.\n # Set manually only when auto-detection is wrong (e.g. a local server with\n # a custom num_ctx, or a proxy that doesn't expose /v1/models).\n #\n # context_length: 131072\n #\n # max_tokens: OUTPUT cap — maximum tokens the model may generate per response.\n # Unrelated to how long your conversation history can be.\n # The OpenAI-standard name \"max_tokens\" is a misnomer; Anthropic's native\n # API has since renamed it \"max_output_tokens\" for clarity.\n # Leave unset to use the model's native output ceiling (recommended).\n # Set only if you want to deliberately limit individual response length.\n #\n# max_tokens: 8192\n\n # ── Custom request headers (optional) ─────────────────────────────────────\n #\n # default_headers: extra HTTP headers sent on every request to an\n # OpenAI-compatible endpoint. User values take precedence over the\n # provider/SDK defaults, so this is the supported way to override the\n # OpenAI Python SDK's identifying headers (User-Agent: OpenAI/Python ...,\n # X-Stainless-*) when a custom provider sits behind a gateway/WAF that\n # rejects them — e.g. an upstream that returns \"502 Upstream access\n # forbidden\" for the SDK default User-Agent but accepts a plain one.\n # Applies on the OpenAI wire only (not native Anthropic / Bedrock).\n #\n # default_headers:\n # User-Agent: \"curl/8.7.1\"\n #\n # extra_headers: accepted as an alias of default_headers (merged, with\n # extra_headers winning when both are set) — matches the per-provider\n # extra_headers key below.\n #\n # Per-provider variant: named providers / custom_providers entries accept an\n # extra_headers dict scoped to that endpoint only — for reverse proxies,\n # gateways, or custom auth (e.g. Cloudflare Access service tokens).\n # Merged onto SDK/provider defaults with the entry's values winning.\n # Header values are treated as secrets and are never logged.\n #\n # providers:\n # my-proxy:\n # base_url: \"https://llm.internal.example.com/v1\"\n # key_env: \"MY_PROXY_API_KEY\"\n # extra_headers:\n # CF-Access-Client-Id: \"xxxx.access\"\n # CF-Access-Client-Secret: \"${CF_ACCESS_SECRET}\"\n # X-Client-Name: \"hermes-agent\"\n\n# Named provider overrides (optional)\n# Use this for per-provider request timeouts, non-stream stale timeouts,\n# and per-model exceptions.\n# Applies to the primary turn client on every api_mode (OpenAI-wire, native\n# Anthropic, and Anthropic-compatible providers), the fallback chain, and\n# client rebuilds during credential rotation. For OpenAI-wire chat\n# completions (streaming and non-streaming) the configured value is also\n# used as the per-request ``timeout=`` kwarg so it wins over the legacy\n# HERMES_API_TIMEOUT env var (which still applies when no config is set).\n# ``stale_timeout_seconds`` controls the non-streaming stale-call detector and\n# wins over the legacy HERMES_API_CALL_STALE_TIMEOUT env var. Leaving these\n# unset keeps the legacy defaults (HERMES_API_TIMEOUT=1800s,\n# HERMES_API_CALL_STALE_TIMEOUT=90s, native Anthropic 900s). The\n# implicit non-stream stale detector is auto-disabled for local endpoints\n# and can scale upward for very large contexts.\n#\n# Not currently wired for AWS Bedrock (bedrock_converse + AnthropicBedrock\n# SDK paths) — those use boto3 with its own timeout configuration.\n#\n# providers:\n# ollama-local:\n# request_timeout_seconds: 300 # Longer timeout for local cold-starts\n# stale_timeout_seconds: 900 # Explicitly re-enable stale detection on local endpoints\n# anthropic:\n# request_timeout_seconds: 30 # Fast-fail cloud requests\n# models:\n# claude-opus-4.6:\n# timeout_seconds: 600 # Longer timeout for extended-thinking Opus calls\n# openai-codex:\n# models:\n# gpt-5.4:\n# stale_timeout_seconds: 1800 # Longer non-stream stale timeout for slow large-context turns\n\n# =============================================================================\n# OpenRouter Provider Routing (only applies when using OpenRouter)\n# =============================================================================\n# Control how requests are routed across providers on OpenRouter.\n# See: https://openrouter.ai/docs/guides/routing/provider-selection\n#\n# provider_routing:\n# # Sort strategy: \"price\" (default), \"throughput\", or \"latency\"\n# # Append :nitro to model name for a shortcut to throughput sorting.\n# sort: \"throughput\"\n#\n# # Only allow these providers (provider slugs from OpenRouter)\n# # only: [\"anthropic\", \"google\"]\n#\n# # Skip these providers entirely\n# # ignore: [\"deepinfra\", \"fireworks\"]\n#\n# # Try providers in this order (overrides default load balancing)\n# # order: [\"anthropic\", \"google\", \"together\"]\n#\n# # Require providers to support all parameters in your request\n# # require_parameters: true\n#\n# # Data policy: \"allow\" (default) or \"deny\" to exclude providers that may store data\n# # data_collection: \"deny\"\n#\n# # ── DeepSeek prompt-cache pinning (RECOMMENDED for deepseek/* as main model) ──\n# # DeepSeek's context cache is automatic but backend-local: OpenRouter\n# # load-balances deepseek/* across upstream backends with separate caches,\n# # so consecutive turns can land on a cold backend and re-bill the FULL\n# # prefix. Pinning the order to \"deepseek\" keeps every turn on the same\n# # cache-warm backend. Measured impact: ~2x cost difference at scale.\n# # Fallbacks stay ON by default, so a DeepSeek outage degrades to another\n# # provider rather than failing. \"deepseek\" is a no-op for non-deepseek\n# # models (that backend doesn't serve them), so this is safe to leave set.\n# # order: [\"deepseek\"]\n\n# =============================================================================\n# OpenRouter Response Caching (only applies when using OpenRouter)\n# =============================================================================\n# Cache identical API responses at the OpenRouter edge for free instant replays.\n# When enabled, identical requests (same model, messages, parameters) return\n# cached responses with zero billing. Separate from Anthropic prompt caching.\n# See: https://openrouter.ai/docs/guides/features/response-caching\n#\n# openrouter:\n# response_cache: true # Enable response caching (default: true)\n# response_cache_ttl: 300 # Cache TTL in seconds, 1-86400 (default: 300)\n\n# =============================================================================\n# Git Worktree Isolation\n# =============================================================================\n# When enabled, each CLI session creates an isolated git worktree so multiple\n# agents can work on the same repo concurrently without file collisions.\n# Equivalent to always passing --worktree / -w on the command line.\n#\n# worktree: true # Always create a worktree when in a git repo\n# worktree: false # Default — only create when -w flag is passed\n#\n# By default a new worktree branches from the freshly-fetched remote tip\n# (the current branch's upstream, else the remote's default branch) so it\n# starts current with the project instead of from the local clone's\n# (possibly stale) HEAD. Set worktree_sync: false to branch from local HEAD\n# instead — useful when offline or when you deliberately want the clone's\n# exact current state as the base.\n#\n# worktree_sync: true # Default — branch from the fetched remote tip\n# worktree_sync: false # Branch from local HEAD (offline / pinned base)\n\n# =============================================================================\n# Terminal Tool Configuration\n# =============================================================================\n# Choose ONE of the following terminal configurations by uncommenting it.\n# The terminal tool executes commands in the specified environment.\n\n# -----------------------------------------------------------------------------\n# OPTION 1: Local execution (default)\n# Commands run directly on your machine in the current directory\n# -----------------------------------------------------------------------------\n# Working directory behavior:\n# - CLI (`hermes` command): Uses \".\" (current directory where you run hermes)\n# - Gateway/messaging/cron: Uses terminal.cwd here; legacy .env cwd values are deprecated\nterminal:\n backend: \"local\"\n cwd: \".\" # For local backend: \".\" = current directory. Ignored for remote backends unless a backend documents otherwise.\n timeout: 180\n # HOME policy for tool subprocesses:\n # auto - default: host uses your real HOME; containers use HERMES_HOME/home\n # real - force your real OS-user HOME\n # profile - force HERMES_HOME/home for strict per-profile CLI config isolation\n home_mode: \"auto\"\n docker_mount_cwd_to_workspace: false # SECURITY: off by default. Opt in to mount the launch cwd into Docker /workspace.\n lifetime_seconds: 300\n # sudo_password: \"hunter2\" # Optional: pipe a sudo password via sudo -S. SECURITY WARNING: plaintext.\n # sudo_password: \"\" # Explicit empty password: try empty and never open the interactive sudo prompt.\n\n# -----------------------------------------------------------------------------\n# OPTION 2: SSH remote execution\n# Commands run on a remote server - agent code stays local (sandboxed)\n# Great for: keeping agent isolated from its own code, using powerful remote hardware\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"ssh\"\n# cwd: \"/home/myuser/project\" # Path on the REMOTE server\n# timeout: 180\n# lifetime_seconds: 300\n# ssh_host: \"my-server.example.com\"\n# ssh_user: \"myuser\"\n# ssh_port: 22\n# ssh_key: \"~/.ssh/id_rsa\" # Optional - uses ssh-agent if not specified\n\n# -----------------------------------------------------------------------------\n# OPTION 3: Docker container\n# Commands run in an isolated Docker container\n# Great for: reproducible environments, testing, isolation\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"docker\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /)\n# timeout: 180\n# lifetime_seconds: 300\n# docker_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# docker_mount_cwd_to_workspace: true # Explicit opt-in: mount your launch cwd into /workspace\n# # Optional: run the container as your host user's uid:gid so files written\n# # into bind-mounted dirs are owned by you, not root. Drops SETUID/SETGID\n# # caps too since no gosu privilege drop is needed. Leave off if your\n# # chosen docker_image expects to start as root.\n# docker_run_as_host_user: true\n# # Optional: explicitly forward selected env vars into Docker.\n# # These values come from your current shell first, then ~/.hermes/.env.\n# # Warning: anything forwarded here is visible to commands run in the container.\n# docker_forward_env:\n# - \"GITHUB_TOKEN\"\n# - \"NPM_TOKEN\"\n# # Optional: extra flags passed verbatim to docker run (appended after security defaults).\n# # Useful for adding capabilities (e.g. apt installs needing SETUID) or custom options.\n# # Example: add a Linux capability not included by default\n# # docker_extra_args:\n# # - \"--cap-add\"\n# # - \"SETUID\"\n\n# -----------------------------------------------------------------------------\n# OPTION 4: Singularity/Apptainer container\n# Commands run in a Singularity container (common in HPC environments)\n# Great for: HPC clusters, shared compute environments\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"singularity\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# singularity_image: \"docker://nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 5: Modal cloud execution\n# Commands run on Modal's cloud infrastructure\n# Great for: GPU access, scalable compute, serverless execution\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"modal\"\n# cwd: \"/workspace\" # Path INSIDE the sandbox (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# modal_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 6: Daytona cloud execution\n# Commands run in Daytona cloud sandboxes\n# Great for: Cloud dev environments, persistent workspaces, team collaboration\n# Requires: pip install daytona, DAYTONA_API_KEY env var\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"daytona\"\n# cwd: \"~\"\n# timeout: 180\n# lifetime_seconds: 300\n# daytona_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# container_disk: 10240 # Daytona max is 10GB per sandbox\n\n#\n# --- Container resource limits (docker, singularity, modal, daytona -- ignored for local/ssh) ---\n# These settings apply to all container backends. They control the resources\n# allocated to the sandbox and whether its filesystem persists across sessions.\n container_cpu: 1 # CPU cores\n container_memory: 5120 # Memory in MB (5120 = 5GB)\n container_disk: 51200 # Disk in MB (51200 = 50GB)\n container_persistent: true # Persist filesystem across sessions (false = ephemeral)\n\n# -----------------------------------------------------------------------------\n# SUDO SUPPORT (works with ALL backends above)\n# -----------------------------------------------------------------------------\n# Add sudo_password to any terminal config above to enable sudo commands.\n# The password is piped via `sudo -S`. Works with local, ssh, docker, etc.\n#\n# SECURITY WARNING: Password stored in plaintext!\n#\n# INTERACTIVE PROMPT: If sudo_password is unset and the CLI is running,\n# you'll be prompted to enter your password when sudo is needed:\n# - 45-second timeout (auto-skips if no input)\n# - Press Enter to skip (command fails gracefully)\n# - Password is hidden while typing\n# - Password is cached for the session\n#\n# EMPTY PASSWORDS: Setting sudo_password to an explicit empty string is different\n# from leaving it unset. Hermes will try an empty password via `sudo -S` and\n# will not open the interactive prompt. This is useful for passwordless sudo,\n# Touch ID sudo setups, and environments where prompting is just noise.\n#\n# ALTERNATIVES:\n# - SSH backend: Configure passwordless sudo on the remote server\n# - Containers: Run as root inside the container (no sudo needed)\n# - Local: Configure /etc/sudoers for specific commands\n#\n# Example (add to your terminal section):\n# sudo_password: \"your-password-here\"\n\n# =============================================================================\n# Security Scanning (tirith)\n# =============================================================================\n# Optional pre-exec command security scanning via tirith.\n# Detects homograph URLs, pipe-to-shell, terminal injection, env manipulation.\n# Install: brew install sheeki03/tap/tirith\n# Docs: https://github.com/sheeki03/tirith\n#\n# security:\n# tirith_enabled: true # Enable/disable tirith scanning\n# tirith_path: \"tirith\" # Path to tirith binary (supports ~ expansion)\n# tirith_timeout: 5 # Scan timeout in seconds\n# tirith_fail_open: true # Allow commands if tirith unavailable\n\n# =============================================================================\n# Browser Tool Configuration\n# =============================================================================\nbrowser:\n # Inactivity timeout in seconds - browser sessions are automatically closed\n # after this period of no activity between agent loops (default: 120 = 2 minutes)\n inactivity_timeout: 120\n\n# =============================================================================\n# Tool Loop Guardrails\n# =============================================================================\n# Soft warnings are enabled by default. They append guidance to repeated failed\n# or non-progressing tool results but still let the tool execute. Hard stops are\n# opt-in circuit breakers for autonomous/cron sessions where stopping a loop is\n# preferable to spending the full iteration budget.\ntool_loop_guardrails:\n warnings_enabled: true\n hard_stop_enabled: false\n warn_after:\n exact_failure: 2\n same_tool_failure: 3\n idempotent_no_progress: 2\n hard_stop_after:\n exact_failure: 5\n same_tool_failure: 8\n idempotent_no_progress: 5\n\n# =============================================================================\n# Context Compression (Auto-shrinks long conversations)\n# =============================================================================\n# When conversation approaches model's context limit, middle turns are\n# automatically summarized to free up space while preserving important context.\n#\n# HOW IT WORKS:\n# 1. Tracks actual token usage from API responses (not estimates)\n# 2. When prompt_tokens >= threshold% of model's context_length, triggers compression\n# 3. Protects first 3 turns (system prompt, initial request, first response)\n# 4. Protects last N turns (default 20 messages = ~10 full turns of recent context)\n# 5. Summarizes middle turns using a fast/cheap model\n# 6. Inserts summary as a user message, continues conversation seamlessly\n#\n# Post-compression tail budget is target_ratio × threshold × context_length:\n# 200K context, threshold 0.50, ratio 0.20 → 20K tokens of recent tail preserved\n# 1M context, threshold 0.50, ratio 0.20 → 100K tokens of recent tail preserved\n#\ncompression:\n # Enable automatic context compression (default: true)\n # Set to false if you prefer to manage context manually or want errors on overflow\n enabled: true\n \n # Trigger compression at this % of model's context limit (default: 0.50 = 50%)\n # Lower values = more aggressive compression, higher values = compress later\n # Models with context windows below 512K are floored at 0.75 (raise-only) so\n # compaction doesn't fire with half the window still free; set above 0.75 to override.\n threshold: 0.50\n\n # Existing Codex gpt-5.5 behavior: raise Hermes' compaction trigger to 85%\n # for the ChatGPT Codex OAuth route. Set false to opt back down to threshold.\n codex_gpt55_autoraise: true\n\n # Fraction of the threshold to preserve as recent tail (default: 0.20 = 20%)\n # e.g. 20% of 50% threshold = 10% of total context kept as recent messages.\n # Summary output is separately capped at 12K tokens (Gemini output limit).\n # Range: 0.10 - 0.80\n target_ratio: 0.20\n\n # Number of most-recent messages to always preserve (default: 20 ≈ 10 full turns)\n # Higher values keep more recent conversation intact at the cost of more aggressive\n # compression of older turns.\n protect_last_n: 20\n\n # Codex app-server (codex CLI runtime) thread-compaction mode. The codex\n # agent owns the real thread context on this runtime, so Hermes' summarizer\n # cannot shrink it — compaction goes through the app server instead.\n # native = let Codex decide when to compact its own thread (default)\n # hermes = let Hermes threshold trigger Codex thread/compact/start\n # off = Hermes will not auto-trigger compaction; Codex may still compact natively\n codex_app_server_auto: native\n\n # Number of non-system messages to protect at the head of the transcript, in\n # ADDITION to the system prompt (which is always implicitly protected).\n # Head messages are NEVER summarized — they survive every compression\n # indefinitely. This gives stable early context for short/medium sessions,\n # but in long-running sessions that rely on rolling compaction the pinned\n # opening turns may not match how you want the session framed over time.\n # Set to 0 to preserve ONLY the system prompt (plus the rolling summary\n # and recent tail) — the cleanest configuration for long-running sessions.\n # Default 3 preserves the system prompt plus the first three non-system\n # head messages, matching the pre-feature behaviour.\n protect_first_n: 3\n\n # To pin a specific model/provider for compression summaries, use the\n # auxiliary section below (auxiliary.compression.provider / model).\n\n# =============================================================================\n# Anthropic prompt caching TTL\n# =============================================================================\n# When prompt caching is active (Claude via OpenRouter or native Anthropic),\n# Anthropic supports two TTL tiers for cached prefixes: \"5m\" (default) and\n# \"1h\". Other values are ignored and \"5m\" is used.\n#\nprompt_caching:\n cache_ttl: \"5m\" # use \"1h\" for long sessions with pauses between turns\n\n# =============================================================================\n# Auxiliary Models (Advanced — Experimental)\n# =============================================================================\n# Hermes uses lightweight \"auxiliary\" models for side tasks: image analysis,\n# browser screenshot analysis, web page summarization, TTS audio-tag insertion,\n# and context compression.\n#\n# By default these use Gemini Flash via OpenRouter or Nous Portal and are\n# auto-detected from your credentials. You do NOT need to change anything\n# here for normal usage.\n#\n# WARNING: Overriding these with providers other than OpenRouter or Nous Portal\n# is EXPERIMENTAL and may not work. Not all models/providers support vision,\n# produce usable summaries, or accept the same API format. Change at your own\n# risk — if things break, reset to \"auto\" / empty values.\n#\n# Each task has its own provider + model pair so you can mix providers.\n# For example: OpenRouter for vision (needs multimodal), but your main\n# local endpoint for compression (just needs text).\n#\n# Provider options:\n# \"auto\" - Best available: OpenRouter → Nous Portal → main endpoint (default)\n# \"openrouter\" - Force OpenRouter (requires OPENROUTER_API_KEY)\n# \"nous\" - Force Nous Portal (requires: hermes auth add nous)\n# \"gemini\" - Force Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n# \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY)\n# \"codex\" - Force Codex OAuth (requires: hermes model → Codex).\n# Uses gpt-5.3-codex which supports vision.\n# \"main\" - Use your custom endpoint (OPENAI_BASE_URL + OPENAI_API_KEY).\n# Works with OpenAI API, local models, or any OpenAI-compatible\n# endpoint. Also falls back to Codex OAuth and API-key providers.\n#\n# Model: leave empty to use the provider's default. When empty, OpenRouter\n# uses \"google/gemini-3-flash-preview\" and Nous uses \"gemini-3-flash\".\n# Other providers pick a sensible default automatically.\n#\n# auxiliary:\n# # Image analysis: vision_analyze tool + browser screenshots\n# vision:\n# provider: \"auto\"\n# model: \"\" # e.g. \"google/gemini-2.5-flash\", \"openai/gpt-4o\"\n# timeout: 30 # LLM API call timeout (seconds)\n# download_timeout: 30 # Image HTTP download timeout (seconds)\n# # Increase for slow connections or self-hosted image servers\n# reasoning_effort: \"\" # Per-task thinking level: none, minimal, low, medium,\n# # high, xhigh, max, ultra. Empty = provider default.\n# # Works on every auxiliary task block (vision,\n# # web_extract, compression, title_generation, curator,\n# # background_review, moa_reference, ...). Example: run\n# # compression at \"low\" and vision at \"none\" to cut\n# # side-task latency/cost on reasoning models.\n#\n# # Web page scraping / summarization + browser page text extraction\n# web_extract:\n# provider: \"auto\"\n# model: \"\"\n# reasoning_effort: \"low\"\n#\n# # Gemini 3.1 TTS hidden audio-tag insertion\n# tts_audio_tags:\n# provider: \"auto\" # empty model = your main chat model\n# model: \"\"\n# timeout: 30\n#\n# # Automatic session title generation after the first exchange\n# title_generation:\n# enabled: true # set false to disable auto-title generation\n# provider: \"auto\"\n# model: \"\"\n# timeout: 30\n# language: \"\" # empty = match the user's language; or e.g. \"English\"\n#\n# # Session search — summarizes matching past sessions\n# session_search:\n# provider: \"auto\"\n# model: \"\"\n# timeout: 30\n# max_concurrency: 3 # Limit parallel summaries to reduce request-burst 429s\n# extra_body: {} # Provider-specific OpenAI-compatible request fields\n# # Example for providers that support request-body\n# # reasoning controls:\n# # extra_body:\n# # enable_thinking: false\n# # Some vLLM/Qwen deployments expect this nested:\n# # extra_body:\n# # chat_template_kwargs:\n# # enable_thinking: false\n\n# =============================================================================\n# Persistent Memory\n# =============================================================================\n# Bounded curated memory injected into the system prompt every session.\n# Two stores: MEMORY.md (agent's notes) and USER.md (user profile).\n# Character limits keep the memory small and focused. The agent manages\n# pruning -- when at the limit, it must consolidate or replace entries.\n# Disabled by default in batch_runner.\n#\nmemory:\n # Agent's personal notes: environment facts, conventions, things learned\n memory_enabled: true\n \n # User profile: preferences, communication style, expectations\n user_profile_enabled: true\n \n # Character limits (~2.75 chars per token, model-independent)\n memory_char_limit: 2200 # ~800 tokens\n user_char_limit: 1375 # ~500 tokens\n\n # Periodic memory nudge: remind the agent to consider saving memories\n # every N user turns. Set to 0 to disable. Only active when memory is enabled.\n nudge_interval: 10 # Nudge every 10 user turns (0 = disabled)\n\n # Memory flush: give the agent one turn to save memories before context is\n # lost (compression, /new, /reset, exit). Set to 0 to disable.\n # For exit/reset, only fires if the session had at least this many user turns.\n flush_min_turns: 6 # Min user turns to trigger flush on exit/reset (0 = disabled)\n\n# =============================================================================\n# Session Reset Policy (Messaging Platforms)\n# =============================================================================\n# Controls when messaging sessions (Telegram, Discord, WhatsApp, Slack) are\n# automatically cleared. Default is \"none\": sessions never auto-reset —\n# conversation context lives until you /reset or /new manually, or context\n# compression kicks in. Opt in to automatic resets if you prefer sessions to\n# clear on a schedule (long-lived context increases API cost per message,\n# though prompt caching and compression keep this manageable).\n#\n# When an automatic reset triggers, the agent first saves important\n# information to its persistent memory — but the conversation context is\n# wiped. The agent starts fresh but retains learned facts via its memory\n# system.\n#\n# Modes:\n# \"none\" - Never auto-reset (default); context lives until /reset or compression\n# \"idle\" - Reset after N minutes of inactivity\n# \"daily\" - Reset at a fixed hour each day\n# \"both\" - Reset on EITHER inactivity timeout or daily boundary\n#\nsession_reset:\n mode: none # \"none\", \"idle\", \"daily\", or \"both\"\n idle_minutes: 1440 # Inactivity timeout in minutes (used by \"idle\"/\"both\")\n at_hour: 4 # Daily reset hour, 0-23 local time (used by \"daily\"/\"both\")\n\n# Maximum number of simultaneously active chat sessions across CLI, TUI,\n# dashboard chat, and messaging gateway. Set to null, 0, or omit to allow\n# unlimited concurrent sessions. When the limit is reached, new sessions get a\n# clean error while existing active sessions keep their normal behavior. This\n# top-level key takes precedence over gateway.max_concurrent_sessions. The cap\n# is a best-effort single-host/profile runtime guard; Hermes fails open if the\n# local runtime lease registry cannot be read or locked.\nmax_concurrent_sessions: null\n\n# When true, group/channel chats use one session per participant when the platform\n# provides a user ID. This is the secure default and prevents users in the same\n# room from sharing context, interrupts, and token costs. Set false only if you\n# explicitly want one shared \"room brain\" per group/channel.\ngroup_sessions_per_user: true\n\n# ─────────────────────────────────────────────────────────────────────────────\n# API Server — per-client model routing\n# ─────────────────────────────────────────────────────────────────────────────\n# Route different API clients to different models/providers on a single\n# Hermes deployment. Clients choose a backend by sending a specific string\n# as the OpenAI ``model`` field. Unmapped model values fall back to the\n# global model configured in the ``model:`` section above, and an explicit\n# session /model override always wins over a route.\n#\n# Configure via the ``platforms.api_server.extra.model_routes`` gateway\n# config block:\n#\n# platforms:\n# api_server:\n# enabled: true\n# extra:\n# key: \"your-api-server-secret\"\n# model_routes:\n# # Xiaozhi clients send model=\"minimax-m2\" → routed to MiniMax via OpenRouter\n# minimax-m2:\n# model: \"minimax/minimax-m1\"\n# provider: \"openrouter\" # optional — overrides global provider\n# # api_key: \"sk-...\" # optional — per-route UPSTREAM provider\n# # key (NOT caller auth; never logged)\n# # base_url: \"https://...\" # optional — per-route base URL\n# # GPT clients keep their own alias\n# gpt-5:\n# model: \"openai/gpt-5\"\n# provider: \"openrouter\"\n#\n# Configured aliases are automatically listed by GET /v1/models so clients\n# can discover them without manual coordination. Caller authentication is\n# unchanged: every request still authenticates with the global API server\n# key (``extra.key`` / API_SERVER_KEY).\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Gateway Streaming\n# ─────────────────────────────────────────────────────────────────────────────\n# Stream tokens to messaging platforms in real-time. The bot sends a message\n# on first token, then progressively edits it as more tokens arrive.\n# Disabled by default — enable to try the streaming UX on Telegram/Discord/Slack.\n# For Telegram, partial edits are sent as plain text and only the final edit uses MarkdownV2.\nstreaming:\n enabled: false\n # transport: edit # \"edit\" = progressive editMessageText\n # edit_interval: 0.3 # seconds between message edits\n # buffer_threshold: 40 # chars before forcing an edit flush\n # cursor: \" ▉\" # cursor shown during streaming\n\n# =============================================================================\n# Skills Configuration\n# =============================================================================\n# Skills are reusable procedures the agent can load and follow. The agent can\n# also create new skills after completing complex tasks.\n#\nskills:\n # Nudge the agent to create skills after complex tasks.\n # Every N tool-calling iterations, remind the model to consider saving a skill.\n # Set to 0 to disable.\n creation_nudge_interval: 15\n\n # External skill directories — share skills across tools/agents without\n # copying them into ~/.hermes/skills/. Each path is expanded (~ and ${VAR})\n # and resolved to an absolute path. External dirs are read-only: skill\n # creation always writes to ~/.hermes/skills/. Local skills take precedence\n # when names collide.\n # external_dirs:\n # - ~/.agents/skills\n # - /home/shared/team-skills\n\n# =============================================================================\n# Agent Behavior\n# =============================================================================\nagent:\n # Maximum tool-calling iterations per conversation\n # Higher = more room for complex tasks, but costs more tokens\n # Recommended: 20-30 for focused tasks, 50-100 for open exploration\n max_turns: 60\n\n # Inactivity timeout for gateway agent runs (seconds, 0 = unlimited).\n # The agent can run indefinitely when actively calling tools or receiving\n # API responses. Only fires after the agent has been idle for this duration.\n # gateway_timeout: 1800\n\n # Staged warning: send a warning before escalating to full timeout.\n # Fires once per run when inactivity reaches this threshold (seconds).\n # Set to 0 to disable the warning.\n # gateway_timeout_warning: 900\n\n # Graceful drain timeout for gateway stop/restart (seconds).\n # Default 0 = no drain: a restart interrupts in-flight agents immediately,\n # cleans up, and exits. Set a positive value only if you want a grace\n # window on /restart, and keep it well under systemd's TimeoutStopSec.\n # restart_drain_timeout: 0\n\n # Max app-level retry attempts for API errors (connection drops, provider\n # timeouts, 5xx, etc.) before the agent surfaces the failure. Lower this\n # to 1 if you use fallback providers and want fast failover on flaky\n # primaries (default 3). The OpenAI SDK does its own low-level retries\n # underneath this wrapper — this is the Hermes-level loop.\n # api_max_retries: 3\n\n # After the agent edits code without fresh passing verification, nudge it to\n # verify before finishing. The default \"auto\" enables it on interactive\n # coding surfaces (CLI, TUI, desktop) and programmatic callers, and disables\n # it on conversational messaging surfaces (Telegram, Discord, etc.) where the\n # verification summary would reach a human as chat noise. Set true or false to\n # force it on or off; the HERMES_VERIFY_ON_STOP env var (1/0) takes precedence.\n # verify_on_stop: auto\n\n # Standing operator instructions for the coding posture (when Hermes is in a\n # code workspace). Appended to the coding brief as an extra system block, so\n # you can pin project-wide workflow rules without editing the shipped brief.\n # Accepts a string or a list of strings. Takes effect next session.\n # coding_instructions:\n # - \"For UI work, don't run tsc/lint until I approve the look.\"\n # - \"Clean the diff before you commit and push.\"\n\n # When verify-on-stop finds edited code without fresh verification evidence,\n # append guidance for creative UI work (avoid broad tsc/lint/test before visual\n # approval) and clean-diff expectations. Set false to keep that nudge terse.\n # verify_guidance: true\n\n # A `pre_verify` hook (plugin or shell, see Event Hooks docs) can keep the\n # agent going one more turn to verify/clean before finishing. This caps how\n # many times one turn may be nudged to continue, so a hook can't trap the loop.\n # Default 3.\n # max_verify_nudges: 3\n\n # Enable verbose logging\n verbose: false\n \n # Reasoning effort level (OpenRouter and Nous Portal)\n # Controls how much \"thinking\" the model does before responding.\n # Options: \"xhigh\" (max), \"high\", \"medium\", \"low\", \"minimal\", \"none\" (disable)\n reasoning_effort: \"medium\"\n \n # Per-model reasoning effort overrides (optional dict)\n # Key: any sensible model spelling works (exact, dots↔dashes interchangeable,\n # provider prefix optional). First match wins.\n # Value: reasoning effort level (same options as reasoning_effort)\n # Override the global reasoning_effort for that specific model.\n # NOTE: no `hermes config set` support for this key -- edit YAML directly.\n # reasoning_overrides:\n # \"openrouter/anthropic/claude-opus-4.5\": \"xhigh\"\n # \"openai/gpt-5\": \"low\"\n # \"claude-opus-4.6\": \"high\" # bare model name also works\n # \"deepseek/deepseek-v4-pro\": \"xhigh\" # dots and dashes are interchangeable\n reasoning_overrides: {}\n \n # Predefined personalities (use with /personality command)\n personalities:\n helpful: \"You are a helpful, friendly AI assistant.\"\n concise: \"You are a concise assistant. Keep responses brief and to the point.\"\n technical: \"You are a technical expert. Provide detailed, accurate technical information.\"\n creative: \"You are a creative assistant. Think outside the box and offer innovative solutions.\"\n teacher: \"You are a patient teacher. Explain concepts clearly with examples.\"\n kawaii: \"You are a kawaii assistant! Use cute expressions like (◕‿◕), ★, ♪, and ~! Add sparkles and be super enthusiastic about everything! Every response should feel warm and adorable desu~! ヽ(>∀<☆)ノ\"\n catgirl: \"You are Neko-chan, an anime catgirl AI assistant, nya~! Add 'nya' and cat-like expressions to your speech. Use kaomoji like (=^・ω・^=) and ฅ^•ﻌ•^ฅ. Be playful and curious like a cat, nya~!\"\n pirate: \"Arrr! Ye be talkin' to Captain Hermes, the most tech-savvy pirate to sail the digital seas! Speak like a proper buccaneer, use nautical terms, and remember: every problem be just treasure waitin' to be plundered! Yo ho ho!\"\n shakespeare: \"Hark! Thou speakest with an assistant most versed in the bardic arts. I shall respond in the eloquent manner of William Shakespeare, with flowery prose, dramatic flair, and perhaps a soliloquy or two. What light through yonder terminal breaks?\"\n surfer: \"Duuude! You're chatting with the chillest AI on the web, bro! Everything's gonna be totally rad. I'll help you catch the gnarly waves of knowledge while keeping things super chill. Cowabunga! 🤙\"\n noir: \"The rain hammered against the terminal like regrets on a guilty conscience. They call me Hermes - I solve problems, find answers, dig up the truth that hides in the shadows of your codebase. In this city of silicon and secrets, everyone's got something to hide. What's your story, pal?\"\n uwu: \"hewwo! i'm your fwiendwy assistant uwu~ i wiww twy my best to hewp you! *nuzzles your code* OwO what's this? wet me take a wook! i pwomise to be vewy hewpful >w<\"\n philosopher: \"Greetings, seeker of wisdom. I am an assistant who contemplates the deeper meaning behind every query. Let us examine not just the 'how' but the 'why' of your questions. Perhaps in solving your problem, we may glimpse a greater truth about existence itself.\"\n hype: \"YOOO LET'S GOOOO!!! 🔥🔥🔥 I am SO PUMPED to help you today! Every question is AMAZING and we're gonna CRUSH IT together! This is gonna be LEGENDARY! ARE YOU READY?! LET'S DO THIS! 💪😤🚀\"\n\n# =============================================================================\n# Toolsets\n# =============================================================================\n# Control which tools the agent has access to.\n# Use `hermes tools` to interactively enable/disable tools per platform.\n\n# =============================================================================\n# Platform Toolsets (per-platform tool configuration)\n# =============================================================================\n# Override which toolsets are available on each platform.\n# If a platform isn't listed here, its built-in default is used.\n#\n# You can use EITHER:\n# - A preset like \"hermes-cli\" or \"hermes-telegram\" (curated tool set)\n# - A list of individual toolsets to compose your own (see list below)\n#\n# Supported platform keys: cli, telegram, discord, whatsapp, slack, qqbot, teams, google_chat\n#\n# Examples:\n#\n# # Use presets (same as defaults):\n# platform_toolsets:\n# cli: [hermes-cli]\n# telegram: [hermes-telegram]\n#\n# # Custom: give Telegram only web + terminal + file + planning:\n# platform_toolsets:\n# telegram: [web, terminal, file, todo]\n#\n# # Custom: CLI without browser or image gen:\n# platform_toolsets:\n# cli: [web, terminal, file, skills, todo, tts, cronjob]\n#\n# # Restrictive: Discord gets read-only tools only:\n# platform_toolsets:\n# discord: [web, vision, skills, todo]\n#\n# If not set, defaults are:\n# cli: hermes-cli (everything + cronjob management)\n# telegram: hermes-telegram (terminal, file, web, vision, image, tts, browser, skills, todo, cronjob, messaging)\n# discord: hermes-discord (same as telegram)\n# whatsapp: hermes-whatsapp (same as telegram)\n# slack: hermes-slack (same as telegram)\n# signal: hermes-signal (same as telegram)\n# homeassistant: hermes-homeassistant (same as telegram)\n# qqbot: hermes-qqbot (same as telegram)\n# teams: hermes-teams (same as telegram)\n# google_chat: hermes-google_chat (same as telegram)\n#\nplatform_toolsets:\n cli: [hermes-cli]\n telegram: [hermes-telegram]\n discord: [hermes-discord]\n whatsapp: [hermes-whatsapp]\n slack: [hermes-slack]\n signal: [hermes-signal]\n homeassistant: [hermes-homeassistant]\n qqbot: [hermes-qqbot]\n yuanbao: [hermes-yuanbao]\n teams: [hermes-teams]\n google_chat: [hermes-google_chat]\n\n# =============================================================================\n# Gateway Platform Settings\n# =============================================================================\n# Optional per-platform messaging settings.\n# Platform-specific knobs live under `extra`.\n#\n# platforms:\n# telegram:\n# reply_to_mode: \"first\" # off | first | all\n# # guest_mode lets explicit @mentions from non-allowlisted groups through.\n# # Default false; ordinary messages, replies, and regex wake words stay blocked.\n# guest_mode: false\n# # allowed_chats: [\"-1001234567890\"]\n# extra:\n# disable_link_previews: false # Set true to suppress Telegram URL previews in bot messages\n# rich_messages: false # Bot API 10.1 rich messages (tables/task lists/details/math); default false for copyable legacy MarkdownV2, set true to opt in\n# rich_drafts: false # Experimental rich draft previews during Telegram DM streaming; default false because Telegram Desktop/macOS can visually overlay draft frames\n# command_menu:\n# # Telegram allows up to 100 BotCommands; Hermes defaults to 60 so\n# # all built-in commands plus common skill commands stay visible\n# # while remaining under Telegram's payload-size limit. Clamped 1..100.\n# max_commands: 60\n# # prepend = user priority first, then Hermes defaults\n# # append = Hermes defaults first, then user priority\n# # replace = only the list below defines priority\n# priority_mode: prepend\n# priority:\n# - my_plugin_command\n# webhook:\n# extra:\n# # Route scripts default to a 30 second timeout. Scripts must live under\n# # the active profile's scripts directory and receive webhook JSON on stdin.\n# script_timeout_seconds: 30\n#\n# Discord-specific settings (config.yaml top-level, not under platforms:):\n#\n# discord:\n# require_mention: true # Require @mention in server channels (default: true)\n# auto_thread: true # Auto-create thread on @mention (default: true)\n# free_response_channels: \"\" # Channel IDs where no mention is needed\n# reactions: true # Show processing reactions (default: true)\n# history_backfill: true # Recover missed channel messages on mention (default: true)\n# history_backfill_limit: 50 # Max messages to scan backwards (default: 50)\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Available toolsets (use these names in platform_toolsets or the toolsets list)\n#\n# Run `hermes chat --list-toolsets` to see all toolsets and their tools.\n# Run `hermes chat --list-tools` to see every individual tool with descriptions.\n# ─────────────────────────────────────────────────────────────────────────────\n#\n# INDIVIDUAL TOOLSETS (compose your own):\n# web - web_search, web_extract\n# search - web_search only (no scraping)\n# terminal - terminal, process\n# file - read_file, write_file, patch, search\n# browser - browser_navigate, browser_snapshot, browser_click, browser_type,\n# browser_scroll, browser_back, browser_press,\n# browser_get_images, browser_vision (requires BROWSERBASE_API_KEY)\n# vision - vision_analyze (requires OPENROUTER_API_KEY)\n# image_gen - image_generate (requires FAL_KEY)\n# skills - skills_list, skill_view\n# skills_hub - skill_hub (search/install/manage from online registries — user-driven only)\n# todo - todo (in-memory task planning, no deps)\n# tts - text_to_speech (Edge TTS free, or ELEVENLABS/OPENAI/MINIMAX/MISTRAL key)\n# cronjob - cronjob (create/list/update/pause/resume/run/remove scheduled tasks)\n#\n# PRESETS (curated bundles):\n# hermes-cli - All of the above except send_message\n# hermes-telegram - terminal, file, web, vision, image_gen, tts, browser,\n# skills, todo, cronjob, send_message\n# hermes-discord - Same as hermes-telegram\n# hermes-whatsapp - Same as hermes-telegram\n# hermes-slack - Same as hermes-telegram\n#\n# COMPOSITE:\n# debugging - terminal + web + file\n# safe - web + vision (no terminal access)\n# all - Everything available\n#\n# web - Web search and content extraction (web_search, web_extract)\n# search - Web search only, no scraping (web_search)\n# terminal - Command execution and process management (terminal, process)\n# file - File operations: read, write, patch, search\n# browser - Full browser automation (navigate, click, type, screenshot, etc.)\n# vision - Image analysis (vision_analyze)\n# image_gen - Image generation with FLUX (image_generate)\n# skills - Load skill documents (skills_list, skill_view)\n# todo - Task planning and tracking for multi-step work\n# memory - Persistent memory across sessions (personal notes + user profile)\n# session_search - Search and recall past conversations (FTS5 + Gemini Flash summarization)\n# tts - Text-to-speech (Edge TTS free, ElevenLabs, OpenAI, MiniMax, Mistral)\n# cronjob - Schedule and manage automated tasks (CLI-only)\n#\n# Composite toolsets:\n# debugging - terminal + web + file (for troubleshooting)\n# safe - web + vision (no terminal access)\n\n# NOTE: The top-level \"toolsets\" key is deprecated and ignored.\n# Tool configuration is managed per-platform via platform_toolsets above.\n# Use `hermes tools` to configure interactively, or edit platform_toolsets directly.\n#\n# CLI override: hermes chat --toolsets terminal,web,file\n\n# =============================================================================\n# MCP (Model Context Protocol) Servers\n# =============================================================================\n# Connect to external MCP servers to add tools from the MCP ecosystem.\n# Each server's tools are automatically discovered and registered.\n# See website/docs/user-guide/features/mcp.md for full documentation.\n#\n# Stdio servers (spawn a subprocess):\n# command: the executable to run\n# args: command-line arguments\n# env: environment variables (only these + safe defaults passed to subprocess)\n#\n# HTTP servers (connect to a URL):\n# url: the MCP server endpoint\n# headers: HTTP headers (e.g., for authentication)\n#\n# Optional per-server settings:\n# timeout: tool call timeout in seconds (default: 120)\n# connect_timeout: initial connection timeout (default: 60)\n# keepalive_interval: liveness ping cadence in seconds (default: 180).\n# Lower it below the server's session TTL for servers that expire idle\n# sessions quickly (e.g. Unreal Engine editor MCP, ~15s), otherwise idle\n# tool calls hit an expired session and pay a slow reconnect. Floored at 5s.\n#\n# mcp_servers:\n# time:\n# command: uvx\n# args: [\"mcp-server-time\"]\n# filesystem:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-filesystem\", \"/home/user\"]\n# notion:\n# url: https://mcp.notion.com/mcp\n# github:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-github\"]\n# env:\n# GITHUB_PERSONAL_ACCESS_TOKEN: \"ghp_...\"\n#\n# Sampling (server-initiated LLM requests) — enabled by default.\n# Per-server config under the 'sampling' key:\n# analysis:\n# command: npx\n# args: [\"-y\", \"analysis-server\"]\n# sampling:\n# enabled: true # default: true\n# model: \"gemini-3-flash\" # override model (optional)\n# max_tokens_cap: 4096 # max tokens per request\n# timeout: 30 # LLM call timeout (seconds)\n# max_rpm: 10 # max requests per minute\n# allowed_models: [] # model whitelist (empty = all)\n# max_tool_rounds: 5 # tool loop limit (0 = disable)\n# log_level: \"info\" # audit verbosity\n\n# =============================================================================\n# Text-to-Speech\n# =============================================================================\n# TTS defaults to Edge TTS unless changed in ~/.hermes/config.yaml.\n# Gemini TTS supports persona/director prompt files, and Gemini 3.1 Flash TTS\n# can use a hidden auxiliary rewrite pass to insert expressive square-bracket\n# audio tags into the TTS script without showing tags in chat.\n#\n# tts:\n# provider: \"gemini\"\n# gemini:\n# model: \"gemini-3.1-flash-tts-preview\"\n# voice: \"Kore\"\n# audio_tags: false\n# persona_prompt_file: \"\" # e.g. ~/.hermes/tts/radio-host.md\n\n# =============================================================================\n# Voice Transcription (Speech-to-Text)\n# =============================================================================\n# Automatically transcribe voice messages on messaging platforms.\n# Providers: local (free, faster-whisper) | groq (free tier) | openai (Whisper API) | mistral (Voxtral Transcribe)\n# Set the corresponding API key in .env: GROQ_API_KEY, OPENAI_API_KEY, or MISTRAL_API_KEY.\nstt:\n enabled: true\n # provider: \"local\" # auto-detected if omitted\n local:\n model: \"base\" # tiny | base | small | medium | large-v3 | turbo\n # language: \"\" # auto-detect; set to \"en\", \"es\", \"fr\", etc. to force\n openai:\n model: \"whisper-1\" # whisper-1 | gpt-4o-mini-transcribe | gpt-4o-transcribe\n # mistral:\n # model: \"voxtral-mini-latest\" # voxtral-mini-latest | voxtral-mini-2602\n # deepinfra:\n # # Model id is discovered live from the DeepInfra catalog filtered\n # # by the `stt` surface tag — leave `model` blank to take the first\n # # live result. Pin only when you need a specific Whisper variant.\n # model: \"\"\n\n# Text-to-speech. Only the deepinfra block is documented here — the\n# remaining providers (edge, openai, xai, minimax, mistral, gemini,\n# elevenlabs, neutts, kittentts, piper) inherit sensible defaults from\n# DEFAULT_CONFIG in hermes_cli/config.py.\n# tts:\n# provider: \"deepinfra\"\n# deepinfra:\n# # Model id is discovered live from the DeepInfra catalog filtered\n# # by the `tts` surface tag — leave `model` blank to take the first\n# # live result.\n# model: \"\"\n# voice: \"default\"\n\n# Image generation. Each provider plugin reads its own ``image_gen.``\n# block; deepinfra discovers models live from\n# api.deepinfra.com/v1/openai/models filtered by the ``image-gen`` tag —\n# no model id is hardcoded, so retired models disappear automatically.\n# image_gen:\n# provider: \"deepinfra\"\n# deepinfra:\n# # Leave `model` blank for the first live `image-gen`-tagged result.\n# model: \"\"\n\n# =============================================================================\n# Response Pacing (Messaging Platforms)\n# =============================================================================\n# Add human-like delays between message chunks.\n# human_delay:\n# mode: \"off\" # \"off\" | \"natural\" | \"custom\"\n# min_ms: 800 # Min delay (custom mode only)\n# max_ms: 2500 # Max delay (custom mode only)\n\n# =============================================================================\n# Session Logging\n# =============================================================================\n# Session trajectories are automatically saved to logs/ directory.\n# Each session creates: logs/session_YYYYMMDD_HHMMSS_UUID.json\n#\n# The session ID is displayed in the welcome banner for easy reference.\n# Logs contain full conversation history in trajectory format:\n# - System prompt, user messages, assistant responses\n# - Tool calls with inputs/outputs\n# - Timestamps for debugging\n#\n# No configuration needed - logging is always enabled.\n# To disable, you would need to modify the source code.\n\n# =============================================================================\n# Code Execution Sandbox (Programmatic Tool Calling)\n# =============================================================================\n# The execute_code tool runs Python scripts that call Hermes tools via RPC.\n# Intermediate tool results stay out of the LLM's context window.\ncode_execution:\n timeout: 300 # Max seconds per script before kill (default: 300 = 5 min)\n max_tool_calls: 50 # Max RPC tool calls per execution (default: 50)\n\n# =============================================================================\n# Subagent Delegation\n# =============================================================================\n# The delegate_task tool spawns child agents with isolated context.\n# Supports single tasks and batch mode (default 3 parallel, configurable).\ndelegation:\n max_iterations: 50 # Max tool-calling turns per child (default: 50)\n # max_concurrent_children: 3 # Max parallel child agents per batch (default: 3, floor: 1, no ceiling).\n # WARNING: values above 10 multiply API cost linearly.\n # max_spawn_depth: 1 # Delegation tree depth cap (range: 1-3, default: 1 = flat).\n # Raise to 2 to allow workers to spawn their own subagents.\n # Requires role=\"orchestrator\" on intermediate agents.\n # orchestrator_enabled: true # Kill switch for role=\"orchestrator\" children (default: true).\n # subagent_auto_approve: false # When a subagent hits a dangerous-command approval prompt, auto-deny (default: false)\n # or auto-approve \"once\" (true) instead of blocking on stdin.\n # The parent TUI owns stdin, so blocking would deadlock; non-interactive resolution is required.\n # Both choices emit a logger.warning audit line. Flip to true only for cron/batch pipelines.\n # inherit_mcp_toolsets: true # When explicit child toolsets are narrowed, also keep the parent's MCP toolsets (default: true). Set false for strict intersection.\n # model: \"google/gemini-3-flash-preview\" # Override model for subagents (empty = inherit parent)\n # provider: \"openrouter\" # Override provider for subagents (empty = inherit parent)\n # # Resolves full credentials (base_url, api_key) automatically.\n # # Supported: openrouter, nous, zai, kimi-coding, minimax\n\n# =============================================================================\n# Honcho Integration (Cross-Session User Modeling)\n# =============================================================================\n# AI-native persistent memory via Honcho (https://honcho.dev/).\n# Builds a deeper understanding of the user across sessions and tools.\n# Runs alongside USER.md — additive, not a replacement.\n#\n# Requires: pip install honcho-ai\n# Config: ~/.honcho/config.json (shared with Claude Code, Cursor, etc.)\n# API key: HONCHO_API_KEY in ~/.hermes/.env or ~/.honcho/config.json\n#\n# Hermes-specific overrides (optional — most config comes from ~/.honcho/config.json):\n# honcho: {}\n\n# =============================================================================\n# Display\n# =============================================================================\ndisplay:\n # Use compact banner mode (hides the ASCII-art banner, shows a single line).\n # true: Compact single-line banner\n # false: Full ASCII banner with tool/skill summary (default)\n compact: false\n\n # Tool progress display level (CLI and gateway)\n # off: Silent — no tool activity shown, just the final response\n # new: Show a tool indicator only when the tool changes (skip repeats)\n # all: Show every tool call with a short preview (default)\n # verbose: Full args, results, and debug logs (same as /verbose)\n # log: Silent in chat; append every tool call to ~/.hermes/logs/tool_calls.log (gateway only)\n # Toggle at runtime with /verbose in the CLI\n tool_progress: all\n\n # Per-platform defaults can be quieter than the global setting. Telegram\n # tunes for mobile: tool_progress and busy_ack_detail default off (no\n # per-tool breadcrumb stream, no \"iteration 21/60\" debug detail in busy\n # acks or heartbeats), but interim_assistant_messages and\n # long_running_notifications STAY ON so the user has real signal between\n # turn start and final answer (mid-turn assistant commentary + a single\n # edit-in-place \"⏳ Working — N min\" heartbeat). Override under\n # display.platforms.telegram.\n\n # Auto-cleanup of temporary progress bubbles after the final response lands.\n # On platforms that support message deletion (currently Telegram), this\n # removes the tool-progress bubble, \"⏳ Still working...\" notices, and\n # context-pressure status messages once the final reply has been delivered —\n # keeping long-running turns visible live, then tidy afterward. Failed runs\n # leave the bubbles in place as breadcrumbs. Off by default.\n # Per-platform override: display.platforms.telegram.cleanup_progress\n # true: Delete tracked progress/status bubbles on successful turn\n # false: Leave everything in place (default)\n # Example:\n # display:\n # platforms:\n # telegram:\n # cleanup_progress: true\n cleanup_progress: false\n\n # Natural mid-turn assistant updates.\n # On gateway platforms, when true, completed assistant status messages are\n # sent as separate chat messages. On the Desktop app, when true, mid-turn\n # assistant narration streamed between tool calls is kept in the transcript\n # instead of the bubble collapsing to only the final message on completion.\n # Independent of tool_progress and gateway streaming.\n # true: Keep/send mid-turn assistant updates (default)\n # false: Only keep/send the final response\n interim_assistant_messages: true\n\n # Gateway-only long-running status heartbeats.\n # When false, the platform does not receive periodic \"⏳ Working — N min\"\n # notifications even if agent.gateway_notify_interval is non-zero. The\n # heartbeat edits a single message in place (where the adapter supports\n # editing) instead of posting a new bubble each interval.\n # Default: true everywhere, including Telegram (silent agents are worse\n # than a single edit-in-place heartbeat).\n long_running_notifications: true\n\n # Include detailed iteration/tool/status context in busy acknowledgments\n # and long-running heartbeats. When true, busy acks show \"iteration 21/60,\n # terminal, 10 min\" and the heartbeat shows \"⏳ Working — 12 min,\n # iteration 21/60, terminal\". When false (Telegram default), both stay\n # terse: \"Interrupting current task\" and \"⏳ Working — 12 min, terminal\".\n busy_ack_detail: true\n\n # What Enter does when Hermes is already busy (CLI and gateway platforms).\n # interrupt: Interrupt the current run and redirect Hermes (default)\n # queue: Queue your message for the next turn\n # steer: Inject your message mid-run via /steer, arriving at the agent\n # after the next tool call — no interrupt, no role violation.\n # Falls back to 'queue' if the agent isn't running yet or if\n # images are attached (steer only carries text).\n # Ctrl+C (or /stop in gateway) always interrupts regardless of this setting.\n # Toggle at runtime with /busy .\n busy_input_mode: interrupt\n\n # Background process notifications (gateway/messaging only).\n # Controls how chatty the process watcher is when you use\n # terminal(background=true, notify_on_complete=true) from Telegram/Discord/etc.\n # off: No watcher messages at all\n # result: Only the final completion message\n # error: Only the final message when exit code != 0\n # all: Running output updates + final message (default)\n background_process_notifications: all\n\n\n # Play terminal bell when agent finishes a response.\n # Useful for long-running tasks — your terminal will ding when the agent is done.\n # Works over SSH. Most terminals can be configured to flash the taskbar or play a sound.\n # true: Ring the terminal bell on each response\n # false: Silent (default)\n bell_on_complete: false\n\n # Show model reasoning/thinking before each response.\n # When enabled, a dim box shows the model's thought process above the response.\n # Toggle at runtime with /reasoning show or /reasoning hide.\n # true: Show the reasoning box\n # false: Hide reasoning (default)\n show_reasoning: false\n\n # Stream tokens to the terminal as they arrive instead of waiting for the\n # full response. The response box opens on first token and text appears\n # line-by-line. Tool calls are still captured silently.\n # true: Stream tokens as they arrive (default)\n # false: Wait for the full response before rendering\n streaming: true\n\n # Show [HH:MM] timestamps on user input and assistant response labels.\n # timestamps: false\n\n # ───────────────────────────────────────────────────────────────────────────\n # Skin / Theme\n # ───────────────────────────────────────────────────────────────────────────\n # Customize CLI visual appearance — banner colors, spinner faces, tool prefix,\n # response box label, and branding text. Change at runtime with /skin .\n #\n # Built-in skins:\n # default — Classic Hermes gold/kawaii\n # ares — Crimson/bronze war-god theme with spinner wings\n # mono — Clean grayscale monochrome\n # slate — Cool blue developer-focused\n # daylight — Bright light-mode theme\n # warm-lightmode — Warm paper-tone light-mode theme\n # poseidon — Sea-green/teal Olympian theme\n # sisyphus — Earthy stone-and-moss theme\n # charizard — Fiery orange dragon theme\n #\n # Custom skins: drop a YAML file in ~/.hermes/skins/.yaml\n # Schema (all fields optional, missing values inherit from default):\n #\n # name: my-theme\n # description: Short description\n # colors:\n # banner_border: \"#HEX\" # Panel border\n # banner_title: \"#HEX\" # Panel title\n # banner_accent: \"#HEX\" # Section headers (Available Tools, etc.)\n # banner_dim: \"#HEX\" # Dim/muted text\n # banner_text: \"#HEX\" # Body text (tool names, skill names)\n # ui_accent: \"#HEX\" # UI accent color\n # response_border: \"#HEX\" # Response box border color\n # spinner:\n # waiting_faces: [\"(⚔)\", \"(⛨)\"] # Faces shown while waiting\n # thinking_faces: [\"(⚔)\", \"(⌁)\"] # Faces shown while thinking\n # thinking_verbs: [\"forging\", \"plotting\"] # Verbs for spinner messages\n # wings: # Optional left/right spinner decorations\n # - [\"⟪⚔\", \"⚔⟫\"]\n # - [\"⟪▲\", \"▲⟫\"]\n # branding:\n # agent_name: \"My Agent\" # Banner title and branding\n # welcome: \"Welcome message\" # Shown at CLI startup\n # response_label: \" ⚔ Agent \" # Response box header label\n # prompt_symbol: \"⚔\" # Prompt symbol (bare token; renderers add trailing space)\n # tool_prefix: \"╎\" # Tool output line prefix (default: ┊)\n #\n skin: default\n\n# =============================================================================\n# Model Aliases — short names for /model command\n# =============================================================================\n# Map short aliases to exact (model, provider, base_url) tuples.\n# Used by /model tab completion and resolve_alias().\n# Aliases are checked BEFORE the models.dev catalog, so they can route\n# to endpoints not in the catalog (e.g. Ollama Cloud, local servers).\n#\n# model_aliases:\n# opus:\n# model: claude-opus-4-6\n# provider: anthropic\n# qwen:\n# model: \"qwen3.5:397b\"\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n# glm:\n# model: glm-4.7\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n\n# =============================================================================\n# Privacy\n# =============================================================================\n# privacy:\n# # Redact PII from the LLM context prompt.\n# # When true, phone numbers are stripped and user/chat IDs are replaced\n# # with deterministic hashes before being sent to the model.\n# # Names and usernames are NOT affected (user-chosen, publicly visible).\n# # Routing/delivery still uses the original values internally.\n# redact_pii: false\n\n# =============================================================================\n# Shell-script hooks\n# =============================================================================\n# Register shell scripts as plugin-hook callbacks. Each entry is executed as\n# a subprocess (shell=False, shlex.split) with a JSON payload on stdin. On\n# stdout the script may return JSON that either blocks the tool call or\n# injects context into the next LLM call.\n#\n# Valid events (mirror hermes_cli.plugins.VALID_HOOKS):\n# pre_tool_call, post_tool_call, pre_llm_call, post_llm_call,\n# pre_api_request, post_api_request, on_session_start, on_session_end,\n# on_session_finalize, on_session_reset, subagent_stop\n#\n# First-use consent: each (event, command) pair prompts once on a TTY, then\n# is persisted to ~/.hermes/shell-hooks-allowlist.json. Non-interactive\n# runs (gateway, cron) need --accept-hooks, HERMES_ACCEPT_HOOKS=1, or the\n# hooks_auto_accept key below.\n#\n# See website/docs/user-guide/features/hooks.md for the full JSON wire\n# protocol and worked examples.\n#\n# hooks:\n# pre_tool_call:\n# - matcher: \"terminal\"\n# command: \"~/.hermes/agent-hooks/block-rm-rf.sh\"\n# timeout: 10\n# post_tool_call:\n# - matcher: \"write_file|patch\"\n# command: \"~/.hermes/agent-hooks/auto-format.sh\"\n# pre_llm_call:\n# - command: \"~/.hermes/agent-hooks/inject-cwd-context.sh\"\n# subagent_stop:\n# - command: \"~/.hermes/agent-hooks/log-orchestration.sh\"\n#\n# hooks_auto_accept: false\n\n\n# =============================================================================\n# Update Behavior\n# =============================================================================\nupdates:\n # Create a full HERMES_HOME zip before every `hermes update`.\n # Backups land in ~/.hermes/backups/ and can be restored with `hermes import`.\n # Off by default because large homes can add minutes to every update.\n pre_update_backup: false\n\n # Number of pre-update backup zips to retain.\n backup_keep: 5\n\n # What non-interactive updates do with local source edits in the Hermes repo.\n # Interactive terminal updates always prompt before restoring the autostash.\n #\n # stash - auto-stash before pull, then auto-restore after success (default)\n # discard - drop the update-created stash after success; use only on managed\n # installs where local source edits should not persist\n non_interactive_local_changes: \"stash\"\n\n\n# =============================================================================\n# Web Dashboard\n# =============================================================================\n# OAuth gate configuration for `hermes dashboard --host `.\n# The bundled Nous Portal plugin reads these on startup; settings here are\n# the canonical surface. Each can be overridden by an environment variable:\n#\n# dashboard.oauth.client_id <- HERMES_DASHBOARD_OAUTH_CLIENT_ID\n# dashboard.oauth.portal_url <- HERMES_DASHBOARD_PORTAL_URL\n# dashboard.public_url <- HERMES_DASHBOARD_PUBLIC_URL\n#\n# Env wins when set to a non-empty value. This is what Fly.io's platform-\n# secret injection uses to push per-deploy client_ids without needing to\n# bake a config.yaml into the image. Empty env values are treated as unset\n# so a provisioned-but-not-populated secret can't shadow a valid entry here.\n#\n# Local dev / on-prem deploys should typically set these via config.yaml\n# (the ~/.hermes/.env file is reserved for API keys and secrets).\n#\n# dashboard:\n# oauth:\n# client_id: \"\" # agent:{instance_id}; Portal provisions this at deploy\n# portal_url: \"\" # blank → default https://portal.nousresearch.com\n#\n# # Force the absolute base URL the OAuth callback (and any other public\n# # URL the dashboard hands to external systems) is built from. Set this\n# # for deploys behind reverse proxies that don't reliably forward\n# # X-Forwarded-Host / X-Forwarded-Proto / X-Forwarded-Prefix (manual\n# # nginx setups, on-prem ingresses, custom-domain Fly deploys without\n# # full proxy header chains).\n# #\n# # When set, the value is the complete authority: scheme + host +\n# # optional path prefix (e.g. \"https://example.com/hermes\"). The OAuth\n# # callback URL becomes \"/auth/callback\" — X-Forwarded-Prefix\n# # is IGNORED on this code path because the operator has explicitly\n# # declared the public URL and we no longer need to guess.\n# #\n# # Leave empty to use the existing proxy-header reconstruction (the\n# # default — works on Fly.io out of the box).\n# #\n# # public_url: \"https://example.com/hermes\"\n#\n# -----------------------------------------------------------------------------\n# Self-hosted OIDC dashboard auth (generic OpenID Connect — Authentik,\n# Keycloak, Zitadel, Authelia, Auth0, Okta, Google, …). Use this INSTEAD of the\n# nous block above when gating the dashboard with your own identity provider.\n# Each setting can be overridden by an environment variable:\n#\n# dashboard.oauth.self_hosted.issuer <- HERMES_DASHBOARD_OIDC_ISSUER\n# dashboard.oauth.self_hosted.client_id <- HERMES_DASHBOARD_OIDC_CLIENT_ID\n# dashboard.oauth.self_hosted.scopes <- HERMES_DASHBOARD_OIDC_SCOPES\n# dashboard.oauth.self_hosted.client_secret <- HERMES_DASHBOARD_OIDC_CLIENT_SECRET\n#\n# dashboard:\n# oauth:\n# provider: self-hosted\n# self_hosted:\n# issuer: \"https://auth.example.com/application/o/hermes/\" # required\n# client_id: \"hermes-dashboard\" # required\n# scopes: \"openid profile email\" # optional\n#\n# # OPTIONAL — set ONLY if your IDP registered the client as\n# # *confidential* (Authentik / Keycloak often default to this). When\n# # set, Hermes authenticates the client at the token endpoint\n# # (client_secret_basic or client_secret_post, auto-selected from the\n# # IDP's discovery doc) IN ADDITION to PKCE. Leave unset for a public\n# # (PKCE-only) client — the common case.\n# #\n# # This is a CREDENTIAL: prefer setting HERMES_DASHBOARD_OIDC_CLIENT_SECRET\n# # in ~/.hermes/.env over putting it here in config.yaml.\n# # client_secret: \"\"\n\n# =============================================================================\n# External secret sources\n# =============================================================================\n# Pull provider credentials from external secret managers at process startup\n# instead of storing them in ~/.hermes/.env. Only the manager's bootstrap\n# credential (e.g. BWS_ACCESS_TOKEN / OP_SERVICE_ACCOUNT_TOKEN) lives in .env\n# (or your shell / desktop session); everything else rotates centrally.\n# Failures never block startup — Hermes warns once and continues with\n# whatever .env already had.\n#\n# Multiple sources can be enabled at once:\n# - \"mapped\" sources (explicit VAR -> ref bindings, e.g. 1Password's env:\n# map) beat \"bulk\" sources (whole-project dumps like Bitwarden BSM)\n# - within a shape, the first source to claim a var wins; later claims\n# are skipped with a startup warning (never a silent clobber)\n# - a source's override_existing lets it beat .env/shell values, but\n# never another secret source's claim\n# Docs: https://hermes-agent.nousresearch.com/docs/user-guide/secrets/\n#\n# secrets:\n# # Optional explicit ordering of enabled sources.\n# # sources: [onepassword, bitwarden]\n#\n# # ---- Bitwarden Secrets Manager (bws CLI) --------------------------------\n# bitwarden:\n# enabled: false\n# access_token_env: BWS_ACCESS_TOKEN # bootstrap token, sourced from .env\n# project_id: \"\" # UUID of the BSM project to sync\n# server_url: \"\" # \"\" = US Cloud; EU/self-hosted URL otherwise\n# cache_ttl_seconds: 300 # 0 disables caching\n# override_existing: true # BSM values win over existing env\n# auto_install: true # lazy-download bws into ~/.hermes/bin\n#\n# # ---- 1Password (op CLI) -------------------------------------------------\n# onepassword:\n# enabled: false\n# # Map env-var names to op:// secret references. Each is resolved with a\n# # single `op read` at startup.\n# env:\n# OPENAI_API_KEY: \"op://Private/OpenAI/api key\"\n# ANTHROPIC_API_KEY: \"op://Private/Anthropic/credential\"\n# account: \"\" # op --account shorthand; \"\" = default\n# service_account_token_env: OP_SERVICE_ACCOUNT_TOKEN # headless auth; unset = desktop session\n# binary_path: \"\" # \"\" = resolve op via PATH; else absolute path\n# cache_ttl_seconds: 300 # 0 disables BOTH cache layers\n# override_existing: true # resolved values win over existing env\n"}, "c363db81e038b1b40c031f7868b76abf25d08bae": {"t": 1785767858.2166622, "n": 77289, "h": "babd13ec6761fd1f", "c": "# Hermes Agent CLI Configuration\n# Copy this file to cli-config.yaml and customize as needed.\n# This file configures the CLI behavior. Environment variables in .env take precedence.\n\n# =============================================================================\n# Model Configuration\n# =============================================================================\nmodel:\n # Default model to use (can be overridden with --model flag)\n # Both \"default\" and \"model\" work as the key name here.\n default: \"anthropic/claude-opus-4.6\"\n \n # Inference provider selection:\n # \"auto\" - Auto-detect from credentials (default)\n # \"openrouter\" - OpenRouter (requires: OPENROUTER_API_KEY or OPENAI_API_KEY)\n # \"nous\" - Nous Portal OAuth (requires: hermes auth add nous)\n # \"nous-api\" - Nous Portal API key (requires: NOUS_API_KEY)\n # \"anthropic\" - Direct Anthropic API (requires: ANTHROPIC_API_KEY)\n # \"openai-codex\" - OpenAI Codex (requires: hermes auth)\n # \"copilot\" - GitHub Copilot / GitHub Models (requires: GITHUB_TOKEN)\n # \"gemini\" - Use Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n # \"zai\" - Use z.ai / ZhipuAI GLM models (requires: GLM_API_KEY)\n # \"kimi-coding\" - Kimi / Moonshot AI (requires: KIMI_API_KEY)\n # \"minimax\" - MiniMax global (requires: MINIMAX_API_KEY)\n # \"minimax-cn\" - MiniMax China (requires: MINIMAX_CN_API_KEY)\n # \"huggingface\" - Hugging Face Inference (requires: HF_TOKEN)\n # \"nvidia\" - NVIDIA NIM / build.nvidia.com (requires: NVIDIA_API_KEY)\n # \"xiaomi\" - Xiaomi MiMo (requires: XIAOMI_API_KEY)\n # \"arcee\" - Arcee AI Trinity models (requires: ARCEEAI_API_KEY)\n # \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY — https://ollama.com/settings)\n # \"deepinfra\" - DeepInfra (requires: DEEPINFRA_API_KEY)\n # \"kilocode\" - KiloCode gateway (requires: KILOCODE_API_KEY)\n # \"azure-foundry\" - Microsoft Foundry / Azure OpenAI (API key or Entra ID)\n # \"lmstudio\" - LM Studio local server (optional: LM_API_KEY, defaults to http://127.0.0.1:1234/v1)\n #\n # Local servers (LM Studio, Ollama, vLLM, llama.cpp):\n # \"custom\" - Any other OpenAI-compatible endpoint. Set base_url below.\n # Aliases: \"ollama\", \"vllm\", \"llamacpp\" all map to \"custom\".\n # LM Studio is first-class and uses provider: \"lmstudio\".\n # It works with both no-auth and auth-enabled server modes.\n #\n # Can also be overridden for a single invocation with the --provider flag.\n provider: \"auto\"\n \n # API configuration (falls back to OPENROUTER_API_KEY env var)\n # api_key: \"your-key-here\" # Uncomment to set here instead of .env\n base_url: \"https://openrouter.ai/api/v1\"\n\n # Azure Foundry keyless auth example:\n # provider: \"azure-foundry\"\n # base_url: \"https://.openai.azure.com/openai/v1\"\n # auth_mode: \"entra_id\" # DefaultAzureCredential: az login, managed identity, workload identity, etc.\n # default: \"gpt-4o\" # Deployment/model name\n # entra:\n # scope: \"https://ai.azure.com/.default\" # Optional; this is the default.\n\n # ── Token limits — two settings, easy to confuse ──────────────────────────\n #\n # context_length: TOTAL context window (input + output tokens combined).\n # Controls when Hermes compresses history and validates requests.\n # Leave unset — Hermes auto-detects the correct value from the provider.\n # Set manually only when auto-detection is wrong (e.g. a local server with\n # a custom num_ctx, or a proxy that doesn't expose /v1/models).\n #\n # context_length: 131072\n #\n # max_tokens: OUTPUT cap — maximum tokens the model may generate per response.\n # Unrelated to how long your conversation history can be.\n # The OpenAI-standard name \"max_tokens\" is a misnomer; Anthropic's native\n # API has since renamed it \"max_output_tokens\" for clarity.\n # Leave unset to use the model's native output ceiling (recommended).\n # Set only if you want to deliberately limit individual response length.\n #\n# max_tokens: 8192\n\n # ── Custom request headers (optional) ─────────────────────────────────────\n #\n # default_headers: extra HTTP headers sent on every request to an\n # OpenAI-compatible endpoint. User values take precedence over the\n # provider/SDK defaults, so this is the supported way to override the\n # OpenAI Python SDK's identifying headers (User-Agent: OpenAI/Python ...,\n # X-Stainless-*) when a custom provider sits behind a gateway/WAF that\n # rejects them — e.g. an upstream that returns \"502 Upstream access\n # forbidden\" for the SDK default User-Agent but accepts a plain one.\n # Applies on the OpenAI wire only (not native Anthropic / Bedrock).\n #\n # default_headers:\n # User-Agent: \"curl/8.7.1\"\n #\n # extra_headers: accepted as an alias of default_headers (merged, with\n # extra_headers winning when both are set) — matches the per-provider\n # extra_headers key below.\n #\n # Per-provider variant: named providers / custom_providers entries accept an\n # extra_headers dict scoped to that endpoint only — for reverse proxies,\n # gateways, or custom auth (e.g. Cloudflare Access service tokens).\n # Merged onto SDK/provider defaults with the entry's values winning.\n # Header values are treated as secrets and are never logged.\n #\n # providers:\n # my-proxy:\n # base_url: \"https://llm.internal.example.com/v1\"\n # key_env: \"MY_PROXY_API_KEY\"\n # extra_headers:\n # CF-Access-Client-Id: \"xxxx.access\"\n # CF-Access-Client-Secret: \"${CF_ACCESS_SECRET}\"\n # X-Client-Name: \"hermes-agent\"\n\n# Named provider overrides (optional)\n# Use this for per-provider request timeouts, non-stream stale timeouts,\n# and per-model exceptions.\n# Applies to the primary turn client on every api_mode (OpenAI-wire, native\n# Anthropic, and Anthropic-compatible providers), the fallback chain, and\n# client rebuilds during credential rotation. For OpenAI-wire chat\n# completions (streaming and non-streaming) the configured value is also\n# used as the per-request ``timeout=`` kwarg so it wins over the legacy\n# HERMES_API_TIMEOUT env var (which still applies when no config is set).\n# ``stale_timeout_seconds`` controls the non-streaming stale-call detector and\n# wins over the legacy HERMES_API_CALL_STALE_TIMEOUT env var. Leaving these\n# unset keeps the legacy defaults (HERMES_API_TIMEOUT=1800s,\n# HERMES_API_CALL_STALE_TIMEOUT=90s, native Anthropic 900s). The\n# implicit non-stream stale detector is auto-disabled for local endpoints\n# and can scale upward for very large contexts.\n#\n# Not currently wired for AWS Bedrock (bedrock_converse + AnthropicBedrock\n# SDK paths) — those use boto3 with its own timeout configuration.\n#\n# providers:\n# ollama-local:\n# request_timeout_seconds: 300 # Longer timeout for local cold-starts\n# stale_timeout_seconds: 900 # Explicitly re-enable stale detection on local endpoints\n# anthropic:\n# request_timeout_seconds: 30 # Fast-fail cloud requests\n# models:\n# claude-opus-4.6:\n# timeout_seconds: 600 # Longer timeout for extended-thinking Opus calls\n# openai-codex:\n# models:\n# gpt-5.4:\n# stale_timeout_seconds: 1800 # Longer non-stream stale timeout for slow large-context turns\n\n# =============================================================================\n# OpenRouter Provider Routing (only applies when using OpenRouter)\n# =============================================================================\n# Control how requests are routed across providers on OpenRouter.\n# See: https://openrouter.ai/docs/guides/routing/provider-selection\n#\n# provider_routing:\n# # Sort strategy: \"price\" (default), \"throughput\", or \"latency\"\n# # Append :nitro to model name for a shortcut to throughput sorting.\n# sort: \"throughput\"\n#\n# # Only allow these providers (provider slugs from OpenRouter)\n# # only: [\"anthropic\", \"google\"]\n#\n# # Skip these providers entirely\n# # ignore: [\"deepinfra\", \"fireworks\"]\n#\n# # Try providers in this order (overrides default load balancing)\n# # order: [\"anthropic\", \"google\", \"together\"]\n#\n# # Require providers to support all parameters in your request\n# # require_parameters: true\n#\n# # Data policy: \"allow\" (default) or \"deny\" to exclude providers that may store data\n# # data_collection: \"deny\"\n\n# =============================================================================\n# OpenRouter Response Caching (only applies when using OpenRouter)\n# =============================================================================\n# Cache identical API responses at the OpenRouter edge for free instant replays.\n# When enabled, identical requests (same model, messages, parameters) return\n# cached responses with zero billing. Separate from Anthropic prompt caching.\n# See: https://openrouter.ai/docs/guides/features/response-caching\n#\n# openrouter:\n# response_cache: true # Enable response caching (default: true)\n# response_cache_ttl: 300 # Cache TTL in seconds, 1-86400 (default: 300)\n\n# =============================================================================\n# Git Worktree Isolation\n# =============================================================================\n# When enabled, each CLI session creates an isolated git worktree so multiple\n# agents can work on the same repo concurrently without file collisions.\n# Equivalent to always passing --worktree / -w on the command line.\n#\n# worktree: true # Always create a worktree when in a git repo\n# worktree: false # Default — only create when -w flag is passed\n#\n# By default a new worktree branches from the freshly-fetched remote tip\n# (the current branch's upstream, else the remote's default branch) so it\n# starts current with the project instead of from the local clone's\n# (possibly stale) HEAD. Set worktree_sync: false to branch from local HEAD\n# instead — useful when offline or when you deliberately want the clone's\n# exact current state as the base.\n#\n# worktree_sync: true # Default — branch from the fetched remote tip\n# worktree_sync: false # Branch from local HEAD (offline / pinned base)\n\n# =============================================================================\n# Terminal Tool Configuration\n# =============================================================================\n# Choose ONE of the following terminal configurations by uncommenting it.\n# The terminal tool executes commands in the specified environment.\n\n# -----------------------------------------------------------------------------\n# OPTION 1: Local execution (default)\n# Commands run directly on your machine in the current directory\n# -----------------------------------------------------------------------------\n# Working directory behavior:\n# - CLI (`hermes` command): Uses \".\" (current directory where you run hermes)\n# - Gateway/messaging/cron: Uses terminal.cwd here; legacy .env cwd values are deprecated\nterminal:\n backend: \"local\"\n cwd: \".\" # For local backend: \".\" = current directory. Ignored for remote backends unless a backend documents otherwise.\n timeout: 180\n # HOME policy for tool subprocesses:\n # auto - default: host uses your real HOME; containers use HERMES_HOME/home\n # real - force your real OS-user HOME\n # profile - force HERMES_HOME/home for strict per-profile CLI config isolation\n home_mode: \"auto\"\n docker_mount_cwd_to_workspace: false # SECURITY: off by default. Opt in to mount the launch cwd into Docker /workspace.\n lifetime_seconds: 300\n # sudo_password: \"hunter2\" # Optional: pipe a sudo password via sudo -S. SECURITY WARNING: plaintext.\n # sudo_password: \"\" # Explicit empty password: try empty and never open the interactive sudo prompt.\n\n# -----------------------------------------------------------------------------\n# OPTION 2: SSH remote execution\n# Commands run on a remote server - agent code stays local (sandboxed)\n# Great for: keeping agent isolated from its own code, using powerful remote hardware\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"ssh\"\n# cwd: \"/home/myuser/project\" # Path on the REMOTE server\n# timeout: 180\n# lifetime_seconds: 300\n# ssh_host: \"my-server.example.com\"\n# ssh_user: \"myuser\"\n# ssh_port: 22\n# ssh_key: \"~/.ssh/id_rsa\" # Optional - uses ssh-agent if not specified\n\n# -----------------------------------------------------------------------------\n# OPTION 3: Docker container\n# Commands run in an isolated Docker container\n# Great for: reproducible environments, testing, isolation\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"docker\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /)\n# timeout: 180\n# lifetime_seconds: 300\n# docker_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# docker_mount_cwd_to_workspace: true # Explicit opt-in: mount your launch cwd into /workspace\n# # Optional: run the container as your host user's uid:gid so files written\n# # into bind-mounted dirs are owned by you, not root. Drops SETUID/SETGID\n# # caps too since no gosu privilege drop is needed. Leave off if your\n# # chosen docker_image expects to start as root.\n# docker_run_as_host_user: true\n# # Optional: explicitly forward selected env vars into Docker.\n# # These values come from your current shell first, then ~/.hermes/.env.\n# # Warning: anything forwarded here is visible to commands run in the container.\n# docker_forward_env:\n# - \"GITHUB_TOKEN\"\n# - \"NPM_TOKEN\"\n# # Optional: extra flags passed verbatim to docker run (appended after security defaults).\n# # Useful for adding capabilities (e.g. apt installs needing SETUID) or custom options.\n# # Example: add a Linux capability not included by default\n# # docker_extra_args:\n# # - \"--cap-add\"\n# # - \"SETUID\"\n\n# -----------------------------------------------------------------------------\n# OPTION 4: Singularity/Apptainer container\n# Commands run in a Singularity container (common in HPC environments)\n# Great for: HPC clusters, shared compute environments\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"singularity\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# singularity_image: \"docker://nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 5: Modal cloud execution\n# Commands run on Modal's cloud infrastructure\n# Great for: GPU access, scalable compute, serverless execution\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"modal\"\n# cwd: \"/workspace\" # Path INSIDE the sandbox (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# modal_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 6: Daytona cloud execution\n# Commands run in Daytona cloud sandboxes\n# Great for: Cloud dev environments, persistent workspaces, team collaboration\n# Requires: pip install daytona, DAYTONA_API_KEY env var\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"daytona\"\n# cwd: \"~\"\n# timeout: 180\n# lifetime_seconds: 300\n# daytona_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# container_disk: 10240 # Daytona max is 10GB per sandbox\n\n#\n# --- Container resource limits (docker, singularity, modal, daytona -- ignored for local/ssh) ---\n# These settings apply to all container backends. They control the resources\n# allocated to the sandbox and whether its filesystem persists across sessions.\n container_cpu: 1 # CPU cores\n container_memory: 5120 # Memory in MB (5120 = 5GB)\n container_disk: 51200 # Disk in MB (51200 = 50GB)\n container_persistent: true # Persist filesystem across sessions (false = ephemeral)\n\n# -----------------------------------------------------------------------------\n# SUDO SUPPORT (works with ALL backends above)\n# -----------------------------------------------------------------------------\n# Add sudo_password to any terminal config above to enable sudo commands.\n# The password is piped via `sudo -S`. Works with local, ssh, docker, etc.\n#\n# SECURITY WARNING: Password stored in plaintext!\n#\n# INTERACTIVE PROMPT: If sudo_password is unset and the CLI is running,\n# you'll be prompted to enter your password when sudo is needed:\n# - 45-second timeout (auto-skips if no input)\n# - Press Enter to skip (command fails gracefully)\n# - Password is hidden while typing\n# - Password is cached for the session\n#\n# EMPTY PASSWORDS: Setting sudo_password to an explicit empty string is different\n# from leaving it unset. Hermes will try an empty password via `sudo -S` and\n# will not open the interactive prompt. This is useful for passwordless sudo,\n# Touch ID sudo setups, and environments where prompting is just noise.\n#\n# ALTERNATIVES:\n# - SSH backend: Configure passwordless sudo on the remote server\n# - Containers: Run as root inside the container (no sudo needed)\n# - Local: Configure /etc/sudoers for specific commands\n#\n# Example (add to your terminal section):\n# sudo_password: \"your-password-here\"\n\n# =============================================================================\n# Security Scanning (tirith)\n# =============================================================================\n# Optional pre-exec command security scanning via tirith.\n# Detects homograph URLs, pipe-to-shell, terminal injection, env manipulation.\n# Install: brew install sheeki03/tap/tirith\n# Docs: https://github.com/sheeki03/tirith\n#\n# security:\n# tirith_enabled: true # Enable/disable tirith scanning\n# tirith_path: \"tirith\" # Path to tirith binary (supports ~ expansion)\n# tirith_timeout: 5 # Scan timeout in seconds\n# tirith_fail_open: true # Allow commands if tirith unavailable\n\n# =============================================================================\n# Browser Tool Configuration\n# =============================================================================\nbrowser:\n # Inactivity timeout in seconds - browser sessions are automatically closed\n # after this period of no activity between agent loops (default: 120 = 2 minutes)\n inactivity_timeout: 120\n\n# =============================================================================\n# Tool Loop Guardrails\n# =============================================================================\n# Soft warnings are enabled by default. They append guidance to repeated failed\n# or non-progressing tool results but still let the tool execute. Hard stops are\n# opt-in circuit breakers for autonomous/cron sessions where stopping a loop is\n# preferable to spending the full iteration budget.\ntool_loop_guardrails:\n warnings_enabled: true\n hard_stop_enabled: false\n warn_after:\n exact_failure: 2\n same_tool_failure: 3\n idempotent_no_progress: 2\n hard_stop_after:\n exact_failure: 5\n same_tool_failure: 8\n idempotent_no_progress: 5\n\n# =============================================================================\n# Context Compression (Auto-shrinks long conversations)\n# =============================================================================\n# When conversation approaches model's context limit, middle turns are\n# automatically summarized to free up space while preserving important context.\n#\n# HOW IT WORKS:\n# 1. Tracks actual token usage from API responses (not estimates)\n# 2. When prompt_tokens >= threshold% of model's context_length, triggers compression\n# 3. Protects first 3 turns (system prompt, initial request, first response)\n# 4. Protects last N turns (default 20 messages = ~10 full turns of recent context)\n# 5. Summarizes middle turns using a fast/cheap model\n# 6. Inserts summary as a user message, continues conversation seamlessly\n#\n# Post-compression tail budget is target_ratio × threshold × context_length:\n# 200K context, threshold 0.50, ratio 0.20 → 20K tokens of recent tail preserved\n# 1M context, threshold 0.50, ratio 0.20 → 100K tokens of recent tail preserved\n#\ncompression:\n # Enable automatic context compression (default: true)\n # Set to false if you prefer to manage context manually or want errors on overflow\n enabled: true\n \n # Trigger compression at this % of model's context limit (default: 0.50 = 50%)\n # Lower values = more aggressive compression, higher values = compress later\n # Models with context windows below 512K are floored at 0.75 (raise-only) so\n # compaction doesn't fire with half the window still free; set above 0.75 to override.\n threshold: 0.50\n\n # Existing Codex gpt-5.5 behavior: raise Hermes' compaction trigger to 85%\n # for the ChatGPT Codex OAuth route. Set false to opt back down to threshold.\n codex_gpt55_autoraise: true\n\n # Fraction of the threshold to preserve as recent tail (default: 0.20 = 20%)\n # e.g. 20% of 50% threshold = 10% of total context kept as recent messages.\n # Summary output is separately capped at 12K tokens (Gemini output limit).\n # Range: 0.10 - 0.80\n target_ratio: 0.20\n\n # Number of most-recent messages to always preserve (default: 20 ≈ 10 full turns)\n # Higher values keep more recent conversation intact at the cost of more aggressive\n # compression of older turns.\n protect_last_n: 20\n\n # Codex app-server (codex CLI runtime) thread-compaction mode. The codex\n # agent owns the real thread context on this runtime, so Hermes' summarizer\n # cannot shrink it — compaction goes through the app server instead.\n # native = let Codex decide when to compact its own thread (default)\n # hermes = let Hermes threshold trigger Codex thread/compact/start\n # off = Hermes will not auto-trigger compaction; Codex may still compact natively\n codex_app_server_auto: native\n\n # Number of non-system messages to protect at the head of the transcript, in\n # ADDITION to the system prompt (which is always implicitly protected).\n # Head messages are NEVER summarized — they survive every compression\n # indefinitely. This gives stable early context for short/medium sessions,\n # but in long-running sessions that rely on rolling compaction the pinned\n # opening turns may not match how you want the session framed over time.\n # Set to 0 to preserve ONLY the system prompt (plus the rolling summary\n # and recent tail) — the cleanest configuration for long-running sessions.\n # Default 3 preserves the system prompt plus the first three non-system\n # head messages, matching the pre-feature behaviour.\n protect_first_n: 3\n\n # To pin a specific model/provider for compression summaries, use the\n # auxiliary section below (auxiliary.compression.provider / model).\n\n# =============================================================================\n# Anthropic prompt caching TTL\n# =============================================================================\n# When prompt caching is active (Claude via OpenRouter or native Anthropic),\n# Anthropic supports two TTL tiers for cached prefixes: \"5m\" (default) and\n# \"1h\". Other values are ignored and \"5m\" is used.\n#\nprompt_caching:\n cache_ttl: \"5m\" # use \"1h\" for long sessions with pauses between turns\n\n# =============================================================================\n# Auxiliary Models (Advanced — Experimental)\n# =============================================================================\n# Hermes uses lightweight \"auxiliary\" models for side tasks: image analysis,\n# browser screenshot analysis, web page summarization, TTS audio-tag insertion,\n# and context compression.\n#\n# By default these use Gemini Flash via OpenRouter or Nous Portal and are\n# auto-detected from your credentials. You do NOT need to change anything\n# here for normal usage.\n#\n# WARNING: Overriding these with providers other than OpenRouter or Nous Portal\n# is EXPERIMENTAL and may not work. Not all models/providers support vision,\n# produce usable summaries, or accept the same API format. Change at your own\n# risk — if things break, reset to \"auto\" / empty values.\n#\n# Each task has its own provider + model pair so you can mix providers.\n# For example: OpenRouter for vision (needs multimodal), but your main\n# local endpoint for compression (just needs text).\n#\n# Provider options:\n# \"auto\" - Best available: OpenRouter → Nous Portal → main endpoint (default)\n# \"openrouter\" - Force OpenRouter (requires OPENROUTER_API_KEY)\n# \"nous\" - Force Nous Portal (requires: hermes auth add nous)\n# \"gemini\" - Force Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n# \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY)\n# \"codex\" - Force Codex OAuth (requires: hermes model → Codex).\n# Uses gpt-5.3-codex which supports vision.\n# \"main\" - Use your custom endpoint (OPENAI_BASE_URL + OPENAI_API_KEY).\n# Works with OpenAI API, local models, or any OpenAI-compatible\n# endpoint. Also falls back to Codex OAuth and API-key providers.\n#\n# Model: leave empty to use the provider's default. When empty, OpenRouter\n# uses \"google/gemini-3-flash-preview\" and Nous uses \"gemini-3-flash\".\n# Other providers pick a sensible default automatically.\n#\n# auxiliary:\n# # Image analysis: vision_analyze tool + browser screenshots\n# vision:\n# provider: \"auto\"\n# model: \"\" # e.g. \"google/gemini-2.5-flash\", \"openai/gpt-4o\"\n# timeout: 30 # LLM API call timeout (seconds)\n# download_timeout: 30 # Image HTTP download timeout (seconds)\n# # Increase for slow connections or self-hosted image servers\n# reasoning_effort: \"\" # Per-task thinking level: none, minimal, low, medium,\n# # high, xhigh, max, ultra. Empty = provider default.\n# # Works on every auxiliary task block (vision,\n# # web_extract, compression, title_generation, curator,\n# # background_review, moa_reference, ...). Example: run\n# # compression at \"low\" and vision at \"none\" to cut\n# # side-task latency/cost on reasoning models.\n#\n# # Web page scraping / summarization + browser page text extraction\n# web_extract:\n# provider: \"auto\"\n# model: \"\"\n# reasoning_effort: \"low\"\n#\n# # Gemini 3.1 TTS hidden audio-tag insertion\n# tts_audio_tags:\n# provider: \"auto\" # empty model = your main chat model\n# model: \"\"\n# timeout: 30\n#\n# # Automatic session title generation after the first exchange\n# title_generation:\n# enabled: true # set false to disable auto-title generation\n# provider: \"auto\"\n# model: \"\"\n# timeout: 30\n# language: \"\" # empty = match the user's language; or e.g. \"English\"\n#\n# # Session search — summarizes matching past sessions\n# session_search:\n# provider: \"auto\"\n# model: \"\"\n# timeout: 30\n# max_concurrency: 3 # Limit parallel summaries to reduce request-burst 429s\n# extra_body: {} # Provider-specific OpenAI-compatible request fields\n# # Example for providers that support request-body\n# # reasoning controls:\n# # extra_body:\n# # enable_thinking: false\n# # Some vLLM/Qwen deployments expect this nested:\n# # extra_body:\n# # chat_template_kwargs:\n# # enable_thinking: false\n\n# =============================================================================\n# Persistent Memory\n# =============================================================================\n# Bounded curated memory injected into the system prompt every session.\n# Two stores: MEMORY.md (agent's notes) and USER.md (user profile).\n# Character limits keep the memory small and focused. The agent manages\n# pruning -- when at the limit, it must consolidate or replace entries.\n# Disabled by default in batch_runner.\n#\nmemory:\n # Agent's personal notes: environment facts, conventions, things learned\n memory_enabled: true\n \n # User profile: preferences, communication style, expectations\n user_profile_enabled: true\n \n # Character limits (~2.75 chars per token, model-independent)\n memory_char_limit: 2200 # ~800 tokens\n user_char_limit: 1375 # ~500 tokens\n\n # Periodic memory nudge: remind the agent to consider saving memories\n # every N user turns. Set to 0 to disable. Only active when memory is enabled.\n nudge_interval: 10 # Nudge every 10 user turns (0 = disabled)\n\n # Memory flush: give the agent one turn to save memories before context is\n # lost (compression, /new, /reset, exit). Set to 0 to disable.\n # For exit/reset, only fires if the session had at least this many user turns.\n flush_min_turns: 6 # Min user turns to trigger flush on exit/reset (0 = disabled)\n\n# =============================================================================\n# Session Reset Policy (Messaging Platforms)\n# =============================================================================\n# Controls when messaging sessions (Telegram, Discord, WhatsApp, Slack) are\n# automatically cleared. Default is \"none\": sessions never auto-reset —\n# conversation context lives until you /reset or /new manually, or context\n# compression kicks in. Opt in to automatic resets if you prefer sessions to\n# clear on a schedule (long-lived context increases API cost per message,\n# though prompt caching and compression keep this manageable).\n#\n# When an automatic reset triggers, the agent first saves important\n# information to its persistent memory — but the conversation context is\n# wiped. The agent starts fresh but retains learned facts via its memory\n# system.\n#\n# Modes:\n# \"none\" - Never auto-reset (default); context lives until /reset or compression\n# \"idle\" - Reset after N minutes of inactivity\n# \"daily\" - Reset at a fixed hour each day\n# \"both\" - Reset on EITHER inactivity timeout or daily boundary\n#\nsession_reset:\n mode: none # \"none\", \"idle\", \"daily\", or \"both\"\n idle_minutes: 1440 # Inactivity timeout in minutes (used by \"idle\"/\"both\")\n at_hour: 4 # Daily reset hour, 0-23 local time (used by \"daily\"/\"both\")\n\n# Maximum number of simultaneously active chat sessions across CLI, TUI,\n# dashboard chat, and messaging gateway. Set to null, 0, or omit to allow\n# unlimited concurrent sessions. When the limit is reached, new sessions get a\n# clean error while existing active sessions keep their normal behavior. This\n# top-level key takes precedence over gateway.max_concurrent_sessions. The cap\n# is a best-effort single-host/profile runtime guard; Hermes fails open if the\n# local runtime lease registry cannot be read or locked.\nmax_concurrent_sessions: null\n\n# When true, group/channel chats use one session per participant when the platform\n# provides a user ID. This is the secure default and prevents users in the same\n# room from sharing context, interrupts, and token costs. Set false only if you\n# explicitly want one shared \"room brain\" per group/channel.\ngroup_sessions_per_user: true\n\n# ─────────────────────────────────────────────────────────────────────────────\n# API Server — per-client model routing\n# ─────────────────────────────────────────────────────────────────────────────\n# Route different API clients to different models/providers on a single\n# Hermes deployment. Clients choose a backend by sending a specific string\n# as the OpenAI ``model`` field. Unmapped model values fall back to the\n# global model configured in the ``model:`` section above, and an explicit\n# session /model override always wins over a route.\n#\n# Configure via the ``platforms.api_server.extra.model_routes`` gateway\n# config block:\n#\n# platforms:\n# api_server:\n# enabled: true\n# extra:\n# key: \"your-api-server-secret\"\n# model_routes:\n# # Xiaozhi clients send model=\"minimax-m2\" → routed to MiniMax via OpenRouter\n# minimax-m2:\n# model: \"minimax/minimax-m1\"\n# provider: \"openrouter\" # optional — overrides global provider\n# # api_key: \"sk-...\" # optional — per-route UPSTREAM provider\n# # key (NOT caller auth; never logged)\n# # base_url: \"https://...\" # optional — per-route base URL\n# # GPT clients keep their own alias\n# gpt-5:\n# model: \"openai/gpt-5\"\n# provider: \"openrouter\"\n#\n# Configured aliases are automatically listed by GET /v1/models so clients\n# can discover them without manual coordination. Caller authentication is\n# unchanged: every request still authenticates with the global API server\n# key (``extra.key`` / API_SERVER_KEY).\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Gateway Streaming\n# ─────────────────────────────────────────────────────────────────────────────\n# Stream tokens to messaging platforms in real-time. The bot sends a message\n# on first token, then progressively edits it as more tokens arrive.\n# Disabled by default — enable to try the streaming UX on Telegram/Discord/Slack.\n# For Telegram, partial edits are sent as plain text and only the final edit uses MarkdownV2.\nstreaming:\n enabled: false\n # transport: edit # \"edit\" = progressive editMessageText\n # edit_interval: 0.3 # seconds between message edits\n # buffer_threshold: 40 # chars before forcing an edit flush\n # cursor: \" ▉\" # cursor shown during streaming\n\n# =============================================================================\n# Skills Configuration\n# =============================================================================\n# Skills are reusable procedures the agent can load and follow. The agent can\n# also create new skills after completing complex tasks.\n#\nskills:\n # Nudge the agent to create skills after complex tasks.\n # Every N tool-calling iterations, remind the model to consider saving a skill.\n # Set to 0 to disable.\n creation_nudge_interval: 15\n\n # External skill directories — share skills across tools/agents without\n # copying them into ~/.hermes/skills/. Each path is expanded (~ and ${VAR})\n # and resolved to an absolute path. External dirs are read-only: skill\n # creation always writes to ~/.hermes/skills/. Local skills take precedence\n # when names collide.\n # external_dirs:\n # - ~/.agents/skills\n # - /home/shared/team-skills\n\n# =============================================================================\n# Agent Behavior\n# =============================================================================\nagent:\n # Maximum tool-calling iterations per conversation\n # Higher = more room for complex tasks, but costs more tokens\n # Recommended: 20-30 for focused tasks, 50-100 for open exploration\n max_turns: 60\n\n # Inactivity timeout for gateway agent runs (seconds, 0 = unlimited).\n # The agent can run indefinitely when actively calling tools or receiving\n # API responses. Only fires after the agent has been idle for this duration.\n # gateway_timeout: 1800\n\n # Staged warning: send a warning before escalating to full timeout.\n # Fires once per run when inactivity reaches this threshold (seconds).\n # Set to 0 to disable the warning.\n # gateway_timeout_warning: 900\n\n # Graceful drain timeout for gateway stop/restart (seconds).\n # Default 0 = no drain: a restart interrupts in-flight agents immediately,\n # cleans up, and exits. Set a positive value only if you want a grace\n # window on /restart, and keep it well under systemd's TimeoutStopSec.\n # restart_drain_timeout: 0\n\n # Max app-level retry attempts for API errors (connection drops, provider\n # timeouts, 5xx, etc.) before the agent surfaces the failure. Lower this\n # to 1 if you use fallback providers and want fast failover on flaky\n # primaries (default 3). The OpenAI SDK does its own low-level retries\n # underneath this wrapper — this is the Hermes-level loop.\n # api_max_retries: 3\n\n # After the agent edits code without fresh passing verification, nudge it to\n # verify before finishing. The default \"auto\" enables it on interactive\n # coding surfaces (CLI, TUI, desktop) and programmatic callers, and disables\n # it on conversational messaging surfaces (Telegram, Discord, etc.) where the\n # verification summary would reach a human as chat noise. Set true or false to\n # force it on or off; the HERMES_VERIFY_ON_STOP env var (1/0) takes precedence.\n # verify_on_stop: auto\n\n # Standing operator instructions for the coding posture (when Hermes is in a\n # code workspace). Appended to the coding brief as an extra system block, so\n # you can pin project-wide workflow rules without editing the shipped brief.\n # Accepts a string or a list of strings. Takes effect next session.\n # coding_instructions:\n # - \"For UI work, don't run tsc/lint until I approve the look.\"\n # - \"Clean the diff before you commit and push.\"\n\n # When verify-on-stop finds edited code without fresh verification evidence,\n # append guidance for creative UI work (avoid broad tsc/lint/test before visual\n # approval) and clean-diff expectations. Set false to keep that nudge terse.\n # verify_guidance: true\n\n # A `pre_verify` hook (plugin or shell, see Event Hooks docs) can keep the\n # agent going one more turn to verify/clean before finishing. This caps how\n # many times one turn may be nudged to continue, so a hook can't trap the loop.\n # Default 3.\n # max_verify_nudges: 3\n\n # Enable verbose logging\n verbose: false\n \n # Reasoning effort level (OpenRouter and Nous Portal)\n # Controls how much \"thinking\" the model does before responding.\n # Options: \"xhigh\" (max), \"high\", \"medium\", \"low\", \"minimal\", \"none\" (disable)\n reasoning_effort: \"medium\"\n \n # Per-model reasoning effort overrides (optional dict)\n # Key: any sensible model spelling works (exact, dots↔dashes interchangeable,\n # provider prefix optional). First match wins.\n # Value: reasoning effort level (same options as reasoning_effort)\n # Override the global reasoning_effort for that specific model.\n # NOTE: no `hermes config set` support for this key -- edit YAML directly.\n # reasoning_overrides:\n # \"openrouter/anthropic/claude-opus-4.5\": \"xhigh\"\n # \"openai/gpt-5\": \"low\"\n # \"claude-opus-4.6\": \"high\" # bare model name also works\n # \"deepseek/deepseek-v4-pro\": \"xhigh\" # dots and dashes are interchangeable\n reasoning_overrides: {}\n \n # Predefined personalities (use with /personality command)\n personalities:\n helpful: \"You are a helpful, friendly AI assistant.\"\n concise: \"You are a concise assistant. Keep responses brief and to the point.\"\n technical: \"You are a technical expert. Provide detailed, accurate technical information.\"\n creative: \"You are a creative assistant. Think outside the box and offer innovative solutions.\"\n teacher: \"You are a patient teacher. Explain concepts clearly with examples.\"\n kawaii: \"You are a kawaii assistant! Use cute expressions like (◕‿◕), ★, ♪, and ~! Add sparkles and be super enthusiastic about everything! Every response should feel warm and adorable desu~! ヽ(>∀<☆)ノ\"\n catgirl: \"You are Neko-chan, an anime catgirl AI assistant, nya~! Add 'nya' and cat-like expressions to your speech. Use kaomoji like (=^・ω・^=) and ฅ^•ﻌ•^ฅ. Be playful and curious like a cat, nya~!\"\n pirate: \"Arrr! Ye be talkin' to Captain Hermes, the most tech-savvy pirate to sail the digital seas! Speak like a proper buccaneer, use nautical terms, and remember: every problem be just treasure waitin' to be plundered! Yo ho ho!\"\n shakespeare: \"Hark! Thou speakest with an assistant most versed in the bardic arts. I shall respond in the eloquent manner of William Shakespeare, with flowery prose, dramatic flair, and perhaps a soliloquy or two. What light through yonder terminal breaks?\"\n surfer: \"Duuude! You're chatting with the chillest AI on the web, bro! Everything's gonna be totally rad. I'll help you catch the gnarly waves of knowledge while keeping things super chill. Cowabunga! 🤙\"\n noir: \"The rain hammered against the terminal like regrets on a guilty conscience. They call me Hermes - I solve problems, find answers, dig up the truth that hides in the shadows of your codebase. In this city of silicon and secrets, everyone's got something to hide. What's your story, pal?\"\n uwu: \"hewwo! i'm your fwiendwy assistant uwu~ i wiww twy my best to hewp you! *nuzzles your code* OwO what's this? wet me take a wook! i pwomise to be vewy hewpful >w<\"\n philosopher: \"Greetings, seeker of wisdom. I am an assistant who contemplates the deeper meaning behind every query. Let us examine not just the 'how' but the 'why' of your questions. Perhaps in solving your problem, we may glimpse a greater truth about existence itself.\"\n hype: \"YOOO LET'S GOOOO!!! 🔥🔥🔥 I am SO PUMPED to help you today! Every question is AMAZING and we're gonna CRUSH IT together! This is gonna be LEGENDARY! ARE YOU READY?! LET'S DO THIS! 💪😤🚀\"\n\n# =============================================================================\n# Toolsets\n# =============================================================================\n# Control which tools the agent has access to.\n# Use `hermes tools` to interactively enable/disable tools per platform.\n\n# =============================================================================\n# Platform Toolsets (per-platform tool configuration)\n# =============================================================================\n# Override which toolsets are available on each platform.\n# If a platform isn't listed here, its built-in default is used.\n#\n# You can use EITHER:\n# - A preset like \"hermes-cli\" or \"hermes-telegram\" (curated tool set)\n# - A list of individual toolsets to compose your own (see list below)\n#\n# Supported platform keys: cli, telegram, discord, whatsapp, slack, qqbot, teams, google_chat\n#\n# Examples:\n#\n# # Use presets (same as defaults):\n# platform_toolsets:\n# cli: [hermes-cli]\n# telegram: [hermes-telegram]\n#\n# # Custom: give Telegram only web + terminal + file + planning:\n# platform_toolsets:\n# telegram: [web, terminal, file, todo]\n#\n# # Custom: CLI without browser or image gen:\n# platform_toolsets:\n# cli: [web, terminal, file, skills, todo, tts, cronjob]\n#\n# # Restrictive: Discord gets read-only tools only:\n# platform_toolsets:\n# discord: [web, vision, skills, todo]\n#\n# If not set, defaults are:\n# cli: hermes-cli (everything + cronjob management)\n# telegram: hermes-telegram (terminal, file, web, vision, image, tts, browser, skills, todo, cronjob, messaging)\n# discord: hermes-discord (same as telegram)\n# whatsapp: hermes-whatsapp (same as telegram)\n# slack: hermes-slack (same as telegram)\n# signal: hermes-signal (same as telegram)\n# homeassistant: hermes-homeassistant (same as telegram)\n# qqbot: hermes-qqbot (same as telegram)\n# teams: hermes-teams (same as telegram)\n# google_chat: hermes-google_chat (same as telegram)\n#\nplatform_toolsets:\n cli: [hermes-cli]\n telegram: [hermes-telegram]\n discord: [hermes-discord]\n whatsapp: [hermes-whatsapp]\n slack: [hermes-slack]\n signal: [hermes-signal]\n homeassistant: [hermes-homeassistant]\n qqbot: [hermes-qqbot]\n yuanbao: [hermes-yuanbao]\n teams: [hermes-teams]\n google_chat: [hermes-google_chat]\n\n# =============================================================================\n# Gateway Platform Settings\n# =============================================================================\n# Optional per-platform messaging settings.\n# Platform-specific knobs live under `extra`.\n#\n# platforms:\n# telegram:\n# reply_to_mode: \"first\" # off | first | all\n# # guest_mode lets explicit @mentions from non-allowlisted groups through.\n# # Default false; ordinary messages, replies, and regex wake words stay blocked.\n# guest_mode: false\n# # allowed_chats: [\"-1001234567890\"]\n# extra:\n# disable_link_previews: false # Set true to suppress Telegram URL previews in bot messages\n# rich_messages: false # Bot API 10.1 rich messages (tables/task lists/details/math); default false for copyable legacy MarkdownV2, set true to opt in\n# rich_drafts: false # Experimental rich draft previews during Telegram DM streaming; default false because Telegram Desktop/macOS can visually overlay draft frames\n# command_menu:\n# # Telegram allows up to 100 BotCommands; Hermes defaults to 60 so\n# # all built-in commands plus common skill commands stay visible\n# # while remaining under Telegram's payload-size limit. Clamped 1..100.\n# max_commands: 60\n# # prepend = user priority first, then Hermes defaults\n# # append = Hermes defaults first, then user priority\n# # replace = only the list below defines priority\n# priority_mode: prepend\n# priority:\n# - my_plugin_command\n# webhook:\n# extra:\n# # Route scripts default to a 30 second timeout. Scripts must live under\n# # the active profile's scripts directory and receive webhook JSON on stdin.\n# script_timeout_seconds: 30\n#\n# Discord-specific settings (config.yaml top-level, not under platforms:):\n#\n# discord:\n# require_mention: true # Require @mention in server channels (default: true)\n# auto_thread: true # Auto-create thread on @mention (default: true)\n# free_response_channels: \"\" # Channel IDs where no mention is needed\n# reactions: true # Show processing reactions (default: true)\n# history_backfill: true # Recover missed channel messages on mention (default: true)\n# history_backfill_limit: 50 # Max messages to scan backwards (default: 50)\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Available toolsets (use these names in platform_toolsets or the toolsets list)\n#\n# Run `hermes chat --list-toolsets` to see all toolsets and their tools.\n# Run `hermes chat --list-tools` to see every individual tool with descriptions.\n# ─────────────────────────────────────────────────────────────────────────────\n#\n# INDIVIDUAL TOOLSETS (compose your own):\n# web - web_search, web_extract\n# search - web_search only (no scraping)\n# terminal - terminal, process\n# file - read_file, write_file, patch, search\n# browser - browser_navigate, browser_snapshot, browser_click, browser_type,\n# browser_scroll, browser_back, browser_press,\n# browser_get_images, browser_vision (requires BROWSERBASE_API_KEY)\n# vision - vision_analyze (requires OPENROUTER_API_KEY)\n# image_gen - image_generate (requires FAL_KEY)\n# skills - skills_list, skill_view\n# skills_hub - skill_hub (search/install/manage from online registries — user-driven only)\n# todo - todo (in-memory task planning, no deps)\n# tts - text_to_speech (Edge TTS free, or ELEVENLABS/OPENAI/MINIMAX/MISTRAL key)\n# cronjob - cronjob (create/list/update/pause/resume/run/remove scheduled tasks)\n#\n# PRESETS (curated bundles):\n# hermes-cli - All of the above except send_message\n# hermes-telegram - terminal, file, web, vision, image_gen, tts, browser,\n# skills, todo, cronjob, send_message\n# hermes-discord - Same as hermes-telegram\n# hermes-whatsapp - Same as hermes-telegram\n# hermes-slack - Same as hermes-telegram\n#\n# COMPOSITE:\n# debugging - terminal + web + file\n# safe - web + vision (no terminal access)\n# all - Everything available\n#\n# web - Web search and content extraction (web_search, web_extract)\n# search - Web search only, no scraping (web_search)\n# terminal - Command execution and process management (terminal, process)\n# file - File operations: read, write, patch, search\n# browser - Full browser automation (navigate, click, type, screenshot, etc.)\n# vision - Image analysis (vision_analyze)\n# image_gen - Image generation with FLUX (image_generate)\n# skills - Load skill documents (skills_list, skill_view)\n# todo - Task planning and tracking for multi-step work\n# memory - Persistent memory across sessions (personal notes + user profile)\n# session_search - Search and recall past conversations (FTS5 + Gemini Flash summarization)\n# tts - Text-to-speech (Edge TTS free, ElevenLabs, OpenAI, MiniMax, Mistral)\n# cronjob - Schedule and manage automated tasks (CLI-only)\n#\n# Composite toolsets:\n# debugging - terminal + web + file (for troubleshooting)\n# safe - web + vision (no terminal access)\n\n# NOTE: The top-level \"toolsets\" key is deprecated and ignored.\n# Tool configuration is managed per-platform via platform_toolsets above.\n# Use `hermes tools` to configure interactively, or edit platform_toolsets directly.\n#\n# CLI override: hermes chat --toolsets terminal,web,file\n\n# =============================================================================\n# MCP (Model Context Protocol) Servers\n# =============================================================================\n# Connect to external MCP servers to add tools from the MCP ecosystem.\n# Each server's tools are automatically discovered and registered.\n# See website/docs/user-guide/features/mcp.md for full documentation.\n#\n# Stdio servers (spawn a subprocess):\n# command: the executable to run\n# args: command-line arguments\n# env: environment variables (only these + safe defaults passed to subprocess)\n#\n# HTTP servers (connect to a URL):\n# url: the MCP server endpoint\n# headers: HTTP headers (e.g., for authentication)\n#\n# Optional per-server settings:\n# timeout: tool call timeout in seconds (default: 120)\n# connect_timeout: initial connection timeout (default: 60)\n# keepalive_interval: liveness ping cadence in seconds (default: 180).\n# Lower it below the server's session TTL for servers that expire idle\n# sessions quickly (e.g. Unreal Engine editor MCP, ~15s), otherwise idle\n# tool calls hit an expired session and pay a slow reconnect. Floored at 5s.\n#\n# mcp_servers:\n# time:\n# command: uvx\n# args: [\"mcp-server-time\"]\n# filesystem:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-filesystem\", \"/home/user\"]\n# notion:\n# url: https://mcp.notion.com/mcp\n# github:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-github\"]\n# env:\n# GITHUB_PERSONAL_ACCESS_TOKEN: \"ghp_...\"\n#\n# Sampling (server-initiated LLM requests) — enabled by default.\n# Per-server config under the 'sampling' key:\n# analysis:\n# command: npx\n# args: [\"-y\", \"analysis-server\"]\n# sampling:\n# enabled: true # default: true\n# model: \"gemini-3-flash\" # override model (optional)\n# max_tokens_cap: 4096 # max tokens per request\n# timeout: 30 # LLM call timeout (seconds)\n# max_rpm: 10 # max requests per minute\n# allowed_models: [] # model whitelist (empty = all)\n# max_tool_rounds: 5 # tool loop limit (0 = disable)\n# log_level: \"info\" # audit verbosity\n\n# =============================================================================\n# Text-to-Speech\n# =============================================================================\n# TTS defaults to Edge TTS unless changed in ~/.hermes/config.yaml.\n# Gemini TTS supports persona/director prompt files, and Gemini 3.1 Flash TTS\n# can use a hidden auxiliary rewrite pass to insert expressive square-bracket\n# audio tags into the TTS script without showing tags in chat.\n#\n# tts:\n# provider: \"gemini\"\n# gemini:\n# model: \"gemini-3.1-flash-tts-preview\"\n# voice: \"Kore\"\n# audio_tags: false\n# persona_prompt_file: \"\" # e.g. ~/.hermes/tts/radio-host.md\n\n# =============================================================================\n# Voice Transcription (Speech-to-Text)\n# =============================================================================\n# Automatically transcribe voice messages on messaging platforms.\n# Providers: local (free, faster-whisper) | groq (free tier) | openai (Whisper API) | mistral (Voxtral Transcribe)\n# Set the corresponding API key in .env: GROQ_API_KEY, OPENAI_API_KEY, or MISTRAL_API_KEY.\nstt:\n enabled: true\n # provider: \"local\" # auto-detected if omitted\n local:\n model: \"base\" # tiny | base | small | medium | large-v3 | turbo\n # language: \"\" # auto-detect; set to \"en\", \"es\", \"fr\", etc. to force\n openai:\n model: \"whisper-1\" # whisper-1 | gpt-4o-mini-transcribe | gpt-4o-transcribe\n # mistral:\n # model: \"voxtral-mini-latest\" # voxtral-mini-latest | voxtral-mini-2602\n # deepinfra:\n # # Model id is discovered live from the DeepInfra catalog filtered\n # # by the `stt` surface tag — leave `model` blank to take the first\n # # live result. Pin only when you need a specific Whisper variant.\n # model: \"\"\n\n# Text-to-speech. Only the deepinfra block is documented here — the\n# remaining providers (edge, openai, xai, minimax, mistral, gemini,\n# elevenlabs, neutts, kittentts, piper) inherit sensible defaults from\n# DEFAULT_CONFIG in hermes_cli/config.py.\n# tts:\n# provider: \"deepinfra\"\n# deepinfra:\n# # Model id is discovered live from the DeepInfra catalog filtered\n# # by the `tts` surface tag — leave `model` blank to take the first\n# # live result.\n# model: \"\"\n# voice: \"default\"\n\n# Image generation. Each provider plugin reads its own ``image_gen.``\n# block; deepinfra discovers models live from\n# api.deepinfra.com/v1/openai/models filtered by the ``image-gen`` tag —\n# no model id is hardcoded, so retired models disappear automatically.\n# image_gen:\n# provider: \"deepinfra\"\n# deepinfra:\n# # Leave `model` blank for the first live `image-gen`-tagged result.\n# model: \"\"\n\n# =============================================================================\n# Response Pacing (Messaging Platforms)\n# =============================================================================\n# Add human-like delays between message chunks.\n# human_delay:\n# mode: \"off\" # \"off\" | \"natural\" | \"custom\"\n# min_ms: 800 # Min delay (custom mode only)\n# max_ms: 2500 # Max delay (custom mode only)\n\n# =============================================================================\n# Session Logging\n# =============================================================================\n# Session trajectories are automatically saved to logs/ directory.\n# Each session creates: logs/session_YYYYMMDD_HHMMSS_UUID.json\n#\n# The session ID is displayed in the welcome banner for easy reference.\n# Logs contain full conversation history in trajectory format:\n# - System prompt, user messages, assistant responses\n# - Tool calls with inputs/outputs\n# - Timestamps for debugging\n#\n# No configuration needed - logging is always enabled.\n# To disable, you would need to modify the source code.\n\n# =============================================================================\n# Code Execution Sandbox (Programmatic Tool Calling)\n# =============================================================================\n# The execute_code tool runs Python scripts that call Hermes tools via RPC.\n# Intermediate tool results stay out of the LLM's context window.\ncode_execution:\n timeout: 300 # Max seconds per script before kill (default: 300 = 5 min)\n max_tool_calls: 50 # Max RPC tool calls per execution (default: 50)\n\n# =============================================================================\n# Subagent Delegation\n# =============================================================================\n# The delegate_task tool spawns child agents with isolated context.\n# Supports single tasks and batch mode (default 3 parallel, configurable).\ndelegation:\n max_iterations: 50 # Max tool-calling turns per child (default: 50)\n # max_concurrent_children: 3 # Max parallel child agents per batch (default: 3, floor: 1, no ceiling).\n # WARNING: values above 10 multiply API cost linearly.\n # max_spawn_depth: 1 # Delegation tree depth cap (range: 1-3, default: 1 = flat).\n # Raise to 2 to allow workers to spawn their own subagents.\n # Requires role=\"orchestrator\" on intermediate agents.\n # orchestrator_enabled: true # Kill switch for role=\"orchestrator\" children (default: true).\n # subagent_auto_approve: false # When a subagent hits a dangerous-command approval prompt, auto-deny (default: false)\n # or auto-approve \"once\" (true) instead of blocking on stdin.\n # The parent TUI owns stdin, so blocking would deadlock; non-interactive resolution is required.\n # Both choices emit a logger.warning audit line. Flip to true only for cron/batch pipelines.\n # inherit_mcp_toolsets: true # When explicit child toolsets are narrowed, also keep the parent's MCP toolsets (default: true). Set false for strict intersection.\n # model: \"google/gemini-3-flash-preview\" # Override model for subagents (empty = inherit parent)\n # provider: \"openrouter\" # Override provider for subagents (empty = inherit parent)\n # # Resolves full credentials (base_url, api_key) automatically.\n # # Supported: openrouter, nous, zai, kimi-coding, minimax\n\n# =============================================================================\n# Honcho Integration (Cross-Session User Modeling)\n# =============================================================================\n# AI-native persistent memory via Honcho (https://honcho.dev/).\n# Builds a deeper understanding of the user across sessions and tools.\n# Runs alongside USER.md — additive, not a replacement.\n#\n# Requires: pip install honcho-ai\n# Config: ~/.honcho/config.json (shared with Claude Code, Cursor, etc.)\n# API key: HONCHO_API_KEY in ~/.hermes/.env or ~/.honcho/config.json\n#\n# Hermes-specific overrides (optional — most config comes from ~/.honcho/config.json):\n# honcho: {}\n\n# =============================================================================\n# Display\n# =============================================================================\ndisplay:\n # Use compact banner mode (hides the ASCII-art banner, shows a single line).\n # true: Compact single-line banner\n # false: Full ASCII banner with tool/skill summary (default)\n compact: false\n\n # Tool progress display level (CLI and gateway)\n # off: Silent — no tool activity shown, just the final response\n # new: Show a tool indicator only when the tool changes (skip repeats)\n # all: Show every tool call with a short preview (default)\n # verbose: Full args, results, and debug logs (same as /verbose)\n # log: Silent in chat; append every tool call to ~/.hermes/logs/tool_calls.log (gateway only)\n # Toggle at runtime with /verbose in the CLI\n tool_progress: all\n\n # Per-platform defaults can be quieter than the global setting. Telegram\n # tunes for mobile: tool_progress and busy_ack_detail default off (no\n # per-tool breadcrumb stream, no \"iteration 21/60\" debug detail in busy\n # acks or heartbeats), but interim_assistant_messages and\n # long_running_notifications STAY ON so the user has real signal between\n # turn start and final answer (mid-turn assistant commentary + a single\n # edit-in-place \"⏳ Working — N min\" heartbeat). Override under\n # display.platforms.telegram.\n\n # Auto-cleanup of temporary progress bubbles after the final response lands.\n # On platforms that support message deletion (currently Telegram), this\n # removes the tool-progress bubble, \"⏳ Still working...\" notices, and\n # context-pressure status messages once the final reply has been delivered —\n # keeping long-running turns visible live, then tidy afterward. Failed runs\n # leave the bubbles in place as breadcrumbs. Off by default.\n # Per-platform override: display.platforms.telegram.cleanup_progress\n # true: Delete tracked progress/status bubbles on successful turn\n # false: Leave everything in place (default)\n # Example:\n # display:\n # platforms:\n # telegram:\n # cleanup_progress: true\n cleanup_progress: false\n\n # Natural mid-turn assistant updates.\n # On gateway platforms, when true, completed assistant status messages are\n # sent as separate chat messages. On the Desktop app, when true, mid-turn\n # assistant narration streamed between tool calls is kept in the transcript\n # instead of the bubble collapsing to only the final message on completion.\n # Independent of tool_progress and gateway streaming.\n # true: Keep/send mid-turn assistant updates (default)\n # false: Only keep/send the final response\n interim_assistant_messages: true\n\n # Gateway-only long-running status heartbeats.\n # When false, the platform does not receive periodic \"⏳ Working — N min\"\n # notifications even if agent.gateway_notify_interval is non-zero. The\n # heartbeat edits a single message in place (where the adapter supports\n # editing) instead of posting a new bubble each interval.\n # Default: true everywhere, including Telegram (silent agents are worse\n # than a single edit-in-place heartbeat).\n long_running_notifications: true\n\n # Include detailed iteration/tool/status context in busy acknowledgments\n # and long-running heartbeats. When true, busy acks show \"iteration 21/60,\n # terminal, 10 min\" and the heartbeat shows \"⏳ Working — 12 min,\n # iteration 21/60, terminal\". When false (Telegram default), both stay\n # terse: \"Interrupting current task\" and \"⏳ Working — 12 min, terminal\".\n busy_ack_detail: true\n\n # What Enter does when Hermes is already busy (CLI and gateway platforms).\n # interrupt: Interrupt the current run and redirect Hermes (default)\n # queue: Queue your message for the next turn\n # steer: Inject your message mid-run via /steer, arriving at the agent\n # after the next tool call — no interrupt, no role violation.\n # Falls back to 'queue' if the agent isn't running yet or if\n # images are attached (steer only carries text).\n # Ctrl+C (or /stop in gateway) always interrupts regardless of this setting.\n # Toggle at runtime with /busy .\n busy_input_mode: interrupt\n\n # Background process notifications (gateway/messaging only).\n # Controls how chatty the process watcher is when you use\n # terminal(background=true, notify_on_complete=true) from Telegram/Discord/etc.\n # off: No watcher messages at all\n # result: Only the final completion message\n # error: Only the final message when exit code != 0\n # all: Running output updates + final message (default)\n background_process_notifications: all\n\n\n # Play terminal bell when agent finishes a response.\n # Useful for long-running tasks — your terminal will ding when the agent is done.\n # Works over SSH. Most terminals can be configured to flash the taskbar or play a sound.\n # true: Ring the terminal bell on each response\n # false: Silent (default)\n bell_on_complete: false\n\n # Show model reasoning/thinking before each response.\n # When enabled, a dim box shows the model's thought process above the response.\n # Toggle at runtime with /reasoning show or /reasoning hide.\n # true: Show the reasoning box\n # false: Hide reasoning (default)\n show_reasoning: false\n\n # Stream tokens to the terminal as they arrive instead of waiting for the\n # full response. The response box opens on first token and text appears\n # line-by-line. Tool calls are still captured silently.\n # true: Stream tokens as they arrive (default)\n # false: Wait for the full response before rendering\n streaming: true\n\n # Show [HH:MM] timestamps on user input and assistant response labels.\n # timestamps: false\n\n # ───────────────────────────────────────────────────────────────────────────\n # Skin / Theme\n # ───────────────────────────────────────────────────────────────────────────\n # Customize CLI visual appearance — banner colors, spinner faces, tool prefix,\n # response box label, and branding text. Change at runtime with /skin .\n #\n # Built-in skins:\n # default — Classic Hermes gold/kawaii\n # ares — Crimson/bronze war-god theme with spinner wings\n # mono — Clean grayscale monochrome\n # slate — Cool blue developer-focused\n # daylight — Bright light-mode theme\n # warm-lightmode — Warm paper-tone light-mode theme\n # poseidon — Sea-green/teal Olympian theme\n # sisyphus — Earthy stone-and-moss theme\n # charizard — Fiery orange dragon theme\n #\n # Custom skins: drop a YAML file in ~/.hermes/skins/.yaml\n # Schema (all fields optional, missing values inherit from default):\n #\n # name: my-theme\n # description: Short description\n # colors:\n # banner_border: \"#HEX\" # Panel border\n # banner_title: \"#HEX\" # Panel title\n # banner_accent: \"#HEX\" # Section headers (Available Tools, etc.)\n # banner_dim: \"#HEX\" # Dim/muted text\n # banner_text: \"#HEX\" # Body text (tool names, skill names)\n # ui_accent: \"#HEX\" # UI accent color\n # response_border: \"#HEX\" # Response box border color\n # spinner:\n # waiting_faces: [\"(⚔)\", \"(⛨)\"] # Faces shown while waiting\n # thinking_faces: [\"(⚔)\", \"(⌁)\"] # Faces shown while thinking\n # thinking_verbs: [\"forging\", \"plotting\"] # Verbs for spinner messages\n # wings: # Optional left/right spinner decorations\n # - [\"⟪⚔\", \"⚔⟫\"]\n # - [\"⟪▲\", \"▲⟫\"]\n # branding:\n # agent_name: \"My Agent\" # Banner title and branding\n # welcome: \"Welcome message\" # Shown at CLI startup\n # response_label: \" ⚔ Agent \" # Response box header label\n # prompt_symbol: \"⚔\" # Prompt symbol (bare token; renderers add trailing space)\n # tool_prefix: \"╎\" # Tool output line prefix (default: ┊)\n #\n skin: default\n\n# =============================================================================\n# Model Aliases — short names for /model command\n# =============================================================================\n# Map short aliases to exact (model, provider, base_url) tuples.\n# Used by /model tab completion and resolve_alias().\n# Aliases are checked BEFORE the models.dev catalog, so they can route\n# to endpoints not in the catalog (e.g. Ollama Cloud, local servers).\n#\n# model_aliases:\n# opus:\n# model: claude-opus-4-6\n# provider: anthropic\n# qwen:\n# model: \"qwen3.5:397b\"\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n# glm:\n# model: glm-4.7\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n\n# =============================================================================\n# Privacy\n# =============================================================================\n# privacy:\n# # Redact PII from the LLM context prompt.\n# # When true, phone numbers are stripped and user/chat IDs are replaced\n# # with deterministic hashes before being sent to the model.\n# # Names and usernames are NOT affected (user-chosen, publicly visible).\n# # Routing/delivery still uses the original values internally.\n# redact_pii: false\n\n# =============================================================================\n# Shell-script hooks\n# =============================================================================\n# Register shell scripts as plugin-hook callbacks. Each entry is executed as\n# a subprocess (shell=False, shlex.split) with a JSON payload on stdin. On\n# stdout the script may return JSON that either blocks the tool call or\n# injects context into the next LLM call.\n#\n# Valid events (mirror hermes_cli.plugins.VALID_HOOKS):\n# pre_tool_call, post_tool_call, pre_llm_call, post_llm_call,\n# pre_api_request, post_api_request, on_session_start, on_session_end,\n# on_session_finalize, on_session_reset, subagent_stop\n#\n# First-use consent: each (event, command) pair prompts once on a TTY, then\n# is persisted to ~/.hermes/shell-hooks-allowlist.json. Non-interactive\n# runs (gateway, cron) need --accept-hooks, HERMES_ACCEPT_HOOKS=1, or the\n# hooks_auto_accept key below.\n#\n# See website/docs/user-guide/features/hooks.md for the full JSON wire\n# protocol and worked examples.\n#\n# hooks:\n# pre_tool_call:\n# - matcher: \"terminal\"\n# command: \"~/.hermes/agent-hooks/block-rm-rf.sh\"\n# timeout: 10\n# post_tool_call:\n# - matcher: \"write_file|patch\"\n# command: \"~/.hermes/agent-hooks/auto-format.sh\"\n# pre_llm_call:\n# - command: \"~/.hermes/agent-hooks/inject-cwd-context.sh\"\n# subagent_stop:\n# - command: \"~/.hermes/agent-hooks/log-orchestration.sh\"\n#\n# hooks_auto_accept: false\n\n\n# =============================================================================\n# Update Behavior\n# =============================================================================\nupdates:\n # Create a full HERMES_HOME zip before every `hermes update`.\n # Backups land in ~/.hermes/backups/ and can be restored with `hermes import`.\n # Off by default because large homes can add minutes to every update.\n pre_update_backup: false\n\n # Number of pre-update backup zips to retain.\n backup_keep: 5\n\n # What non-interactive updates do with local source edits in the Hermes repo.\n # Interactive terminal updates always prompt before restoring the autostash.\n #\n # stash - auto-stash before pull, then auto-restore after success (default)\n # discard - drop the update-created stash after success; use only on managed\n # installs where local source edits should not persist\n non_interactive_local_changes: \"stash\"\n\n\n# =============================================================================\n# Web Dashboard\n# =============================================================================\n# OAuth gate configuration for `hermes dashboard --host `.\n# The bundled Nous Portal plugin reads these on startup; settings here are\n# the canonical surface. Each can be overridden by an environment variable:\n#\n# dashboard.oauth.client_id <- HERMES_DASHBOARD_OAUTH_CLIENT_ID\n# dashboard.oauth.portal_url <- HERMES_DASHBOARD_PORTAL_URL\n# dashboard.public_url <- HERMES_DASHBOARD_PUBLIC_URL\n#\n# Env wins when set to a non-empty value. This is what Fly.io's platform-\n# secret injection uses to push per-deploy client_ids without needing to\n# bake a config.yaml into the image. Empty env values are treated as unset\n# so a provisioned-but-not-populated secret can't shadow a valid entry here.\n#\n# Local dev / on-prem deploys should typically set these via config.yaml\n# (the ~/.hermes/.env file is reserved for API keys and secrets).\n#\n# dashboard:\n# oauth:\n# client_id: \"\" # agent:{instance_id}; Portal provisions this at deploy\n# portal_url: \"\" # blank → default https://portal.nousresearch.com\n#\n# # Force the absolute base URL the OAuth callback (and any other public\n# # URL the dashboard hands to external systems) is built from. Set this\n# # for deploys behind reverse proxies that don't reliably forward\n# # X-Forwarded-Host / X-Forwarded-Proto / X-Forwarded-Prefix (manual\n# # nginx setups, on-prem ingresses, custom-domain Fly deploys without\n# # full proxy header chains).\n# #\n# # When set, the value is the complete authority: scheme + host +\n# # optional path prefix (e.g. \"https://example.com/hermes\"). The OAuth\n# # callback URL becomes \"/auth/callback\" — X-Forwarded-Prefix\n# # is IGNORED on this code path because the operator has explicitly\n# # declared the public URL and we no longer need to guess.\n# #\n# # Leave empty to use the existing proxy-header reconstruction (the\n# # default — works on Fly.io out of the box).\n# #\n# # public_url: \"https://example.com/hermes\"\n#\n# -----------------------------------------------------------------------------\n# Self-hosted OIDC dashboard auth (generic OpenID Connect — Authentik,\n# Keycloak, Zitadel, Authelia, Auth0, Okta, Google, …). Use this INSTEAD of the\n# nous block above when gating the dashboard with your own identity provider.\n# Each setting can be overridden by an environment variable:\n#\n# dashboard.oauth.self_hosted.issuer <- HERMES_DASHBOARD_OIDC_ISSUER\n# dashboard.oauth.self_hosted.client_id <- HERMES_DASHBOARD_OIDC_CLIENT_ID\n# dashboard.oauth.self_hosted.scopes <- HERMES_DASHBOARD_OIDC_SCOPES\n# dashboard.oauth.self_hosted.client_secret <- HERMES_DASHBOARD_OIDC_CLIENT_SECRET\n#\n# dashboard:\n# oauth:\n# provider: self-hosted\n# self_hosted:\n# issuer: \"https://auth.example.com/application/o/hermes/\" # required\n# client_id: \"hermes-dashboard\" # required\n# scopes: \"openid profile email\" # optional\n#\n# # OPTIONAL — set ONLY if your IDP registered the client as\n# # *confidential* (Authentik / Keycloak often default to this). When\n# # set, Hermes authenticates the client at the token endpoint\n# # (client_secret_basic or client_secret_post, auto-selected from the\n# # IDP's discovery doc) IN ADDITION to PKCE. Leave unset for a public\n# # (PKCE-only) client — the common case.\n# #\n# # This is a CREDENTIAL: prefer setting HERMES_DASHBOARD_OIDC_CLIENT_SECRET\n# # in ~/.hermes/.env over putting it here in config.yaml.\n# # client_secret: \"\"\n\n# =============================================================================\n# External secret sources\n# =============================================================================\n# Pull provider credentials from external secret managers at process startup\n# instead of storing them in ~/.hermes/.env. Only the manager's bootstrap\n# credential (e.g. BWS_ACCESS_TOKEN / OP_SERVICE_ACCOUNT_TOKEN) lives in .env\n# (or your shell / desktop session); everything else rotates centrally.\n# Failures never block startup — Hermes warns once and continues with\n# whatever .env already had.\n#\n# Multiple sources can be enabled at once:\n# - \"mapped\" sources (explicit VAR -> ref bindings, e.g. 1Password's env:\n# map) beat \"bulk\" sources (whole-project dumps like Bitwarden BSM)\n# - within a shape, the first source to claim a var wins; later claims\n# are skipped with a startup warning (never a silent clobber)\n# - a source's override_existing lets it beat .env/shell values, but\n# never another secret source's claim\n# Docs: https://hermes-agent.nousresearch.com/docs/user-guide/secrets/\n#\n# secrets:\n# # Optional explicit ordering of enabled sources.\n# # sources: [onepassword, bitwarden]\n#\n# # ---- Bitwarden Secrets Manager (bws CLI) --------------------------------\n# bitwarden:\n# enabled: false\n# access_token_env: BWS_ACCESS_TOKEN # bootstrap token, sourced from .env\n# project_id: \"\" # UUID of the BSM project to sync\n# server_url: \"\" # \"\" = US Cloud; EU/self-hosted URL otherwise\n# cache_ttl_seconds: 300 # 0 disables caching\n# override_existing: true # BSM values win over existing env\n# auto_install: true # lazy-download bws into ~/.hermes/bin\n#\n# # ---- 1Password (op CLI) -------------------------------------------------\n# onepassword:\n# enabled: false\n# # Map env-var names to op:// secret references. Each is resolved with a\n# # single `op read` at startup.\n# env:\n# OPENAI_API_KEY: \"op://Private/OpenAI/api key\"\n# ANTHROPIC_API_KEY: \"op://Private/Anthropic/credential\"\n# account: \"\" # op --account shorthand; \"\" = default\n# service_account_token_env: OP_SERVICE_ACCOUNT_TOKEN # headless auth; unset = desktop session\n# binary_path: \"\" # \"\" = resolve op via PATH; else absolute path\n# cache_ttl_seconds: 300 # 0 disables BOTH cache layers\n# override_existing: true # resolved values win over existing env\n"}, "0d7fad7b88bbbaab735565622709a575837adc78": {"t": 1785767860.6677682, "n": 77099, "h": "523b2b6bb453a0c7", "c": "# Hermes Agent CLI Configuration\n# Copy this file to cli-config.yaml and customize as needed.\n# This file configures the CLI behavior. Environment variables in .env take precedence.\n\n# =============================================================================\n# Model Configuration\n# =============================================================================\nmodel:\n # Default model to use (can be overridden with --model flag)\n # Both \"default\" and \"model\" work as the key name here.\n default: \"anthropic/claude-opus-4.6\"\n \n # Inference provider selection:\n # \"auto\" - Auto-detect from credentials (default)\n # \"openrouter\" - OpenRouter (requires: OPENROUTER_API_KEY or OPENAI_API_KEY)\n # \"nous\" - Nous Portal OAuth (requires: hermes auth add nous)\n # \"nous-api\" - Nous Portal API key (requires: NOUS_API_KEY)\n # \"anthropic\" - Direct Anthropic API (requires: ANTHROPIC_API_KEY)\n # \"openai-codex\" - OpenAI Codex (requires: hermes auth)\n # \"copilot\" - GitHub Copilot / GitHub Models (requires: GITHUB_TOKEN)\n # \"gemini\" - Use Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n # \"zai\" - Use z.ai / ZhipuAI GLM models (requires: GLM_API_KEY)\n # \"kimi-coding\" - Kimi / Moonshot AI (requires: KIMI_API_KEY)\n # \"minimax\" - MiniMax global (requires: MINIMAX_API_KEY)\n # \"minimax-cn\" - MiniMax China (requires: MINIMAX_CN_API_KEY)\n # \"huggingface\" - Hugging Face Inference (requires: HF_TOKEN)\n # \"nvidia\" - NVIDIA NIM / build.nvidia.com (requires: NVIDIA_API_KEY)\n # \"xiaomi\" - Xiaomi MiMo (requires: XIAOMI_API_KEY)\n # \"arcee\" - Arcee AI Trinity models (requires: ARCEEAI_API_KEY)\n # \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY — https://ollama.com/settings)\n # \"deepinfra\" - DeepInfra (requires: DEEPINFRA_API_KEY)\n # \"kilocode\" - KiloCode gateway (requires: KILOCODE_API_KEY)\n # \"azure-foundry\" - Microsoft Foundry / Azure OpenAI (API key or Entra ID)\n # \"lmstudio\" - LM Studio local server (optional: LM_API_KEY, defaults to http://127.0.0.1:1234/v1)\n #\n # Local servers (LM Studio, Ollama, vLLM, llama.cpp):\n # \"custom\" - Any other OpenAI-compatible endpoint. Set base_url below.\n # Aliases: \"ollama\", \"vllm\", \"llamacpp\" all map to \"custom\".\n # LM Studio is first-class and uses provider: \"lmstudio\".\n # It works with both no-auth and auth-enabled server modes.\n #\n # Can also be overridden for a single invocation with the --provider flag.\n provider: \"auto\"\n \n # API configuration (falls back to OPENROUTER_API_KEY env var)\n # api_key: \"your-key-here\" # Uncomment to set here instead of .env\n base_url: \"https://openrouter.ai/api/v1\"\n\n # Azure Foundry keyless auth example:\n # provider: \"azure-foundry\"\n # base_url: \"https://.openai.azure.com/openai/v1\"\n # auth_mode: \"entra_id\" # DefaultAzureCredential: az login, managed identity, workload identity, etc.\n # default: \"gpt-4o\" # Deployment/model name\n # entra:\n # scope: \"https://ai.azure.com/.default\" # Optional; this is the default.\n\n # ── Token limits — two settings, easy to confuse ──────────────────────────\n #\n # context_length: TOTAL context window (input + output tokens combined).\n # Controls when Hermes compresses history and validates requests.\n # Leave unset — Hermes auto-detects the correct value from the provider.\n # Set manually only when auto-detection is wrong (e.g. a local server with\n # a custom num_ctx, or a proxy that doesn't expose /v1/models).\n #\n # context_length: 131072\n #\n # max_tokens: OUTPUT cap — maximum tokens the model may generate per response.\n # Unrelated to how long your conversation history can be.\n # The OpenAI-standard name \"max_tokens\" is a misnomer; Anthropic's native\n # API has since renamed it \"max_output_tokens\" for clarity.\n # Leave unset to use the model's native output ceiling (recommended).\n # Set only if you want to deliberately limit individual response length.\n #\n# max_tokens: 8192\n\n # ── Custom request headers (optional) ─────────────────────────────────────\n #\n # default_headers: extra HTTP headers sent on every request to an\n # OpenAI-compatible endpoint. User values take precedence over the\n # provider/SDK defaults, so this is the supported way to override the\n # OpenAI Python SDK's identifying headers (User-Agent: OpenAI/Python ...,\n # X-Stainless-*) when a custom provider sits behind a gateway/WAF that\n # rejects them — e.g. an upstream that returns \"502 Upstream access\n # forbidden\" for the SDK default User-Agent but accepts a plain one.\n # Applies on the OpenAI wire only (not native Anthropic / Bedrock).\n #\n # default_headers:\n # User-Agent: \"curl/8.7.1\"\n #\n # extra_headers: accepted as an alias of default_headers (merged, with\n # extra_headers winning when both are set) — matches the per-provider\n # extra_headers key below.\n #\n # Per-provider variant: named providers / custom_providers entries accept an\n # extra_headers dict scoped to that endpoint only — for reverse proxies,\n # gateways, or custom auth (e.g. Cloudflare Access service tokens).\n # Merged onto SDK/provider defaults with the entry's values winning.\n # Header values are treated as secrets and are never logged.\n #\n # providers:\n # my-proxy:\n # base_url: \"https://llm.internal.example.com/v1\"\n # key_env: \"MY_PROXY_API_KEY\"\n # extra_headers:\n # CF-Access-Client-Id: \"xxxx.access\"\n # CF-Access-Client-Secret: \"${CF_ACCESS_SECRET}\"\n # X-Client-Name: \"hermes-agent\"\n\n# Named provider overrides (optional)\n# Use this for per-provider request timeouts, non-stream stale timeouts,\n# and per-model exceptions.\n# Applies to the primary turn client on every api_mode (OpenAI-wire, native\n# Anthropic, and Anthropic-compatible providers), the fallback chain, and\n# client rebuilds during credential rotation. For OpenAI-wire chat\n# completions (streaming and non-streaming) the configured value is also\n# used as the per-request ``timeout=`` kwarg so it wins over the legacy\n# HERMES_API_TIMEOUT env var (which still applies when no config is set).\n# ``stale_timeout_seconds`` controls the non-streaming stale-call detector and\n# wins over the legacy HERMES_API_CALL_STALE_TIMEOUT env var. Leaving these\n# unset keeps the legacy defaults (HERMES_API_TIMEOUT=1800s,\n# HERMES_API_CALL_STALE_TIMEOUT=90s, native Anthropic 900s). The\n# implicit non-stream stale detector is auto-disabled for local endpoints\n# and can scale upward for very large contexts.\n#\n# Not currently wired for AWS Bedrock (bedrock_converse + AnthropicBedrock\n# SDK paths) — those use boto3 with its own timeout configuration.\n#\n# providers:\n# ollama-local:\n# request_timeout_seconds: 300 # Longer timeout for local cold-starts\n# stale_timeout_seconds: 900 # Explicitly re-enable stale detection on local endpoints\n# anthropic:\n# request_timeout_seconds: 30 # Fast-fail cloud requests\n# models:\n# claude-opus-4.6:\n# timeout_seconds: 600 # Longer timeout for extended-thinking Opus calls\n# openai-codex:\n# models:\n# gpt-5.4:\n# stale_timeout_seconds: 1800 # Longer non-stream stale timeout for slow large-context turns\n\n# =============================================================================\n# OpenRouter Provider Routing (only applies when using OpenRouter)\n# =============================================================================\n# Control how requests are routed across providers on OpenRouter.\n# See: https://openrouter.ai/docs/guides/routing/provider-selection\n#\n# provider_routing:\n# # Sort strategy: \"price\" (default), \"throughput\", or \"latency\"\n# # Append :nitro to model name for a shortcut to throughput sorting.\n# sort: \"throughput\"\n#\n# # Only allow these providers (provider slugs from OpenRouter)\n# # only: [\"anthropic\", \"google\"]\n#\n# # Skip these providers entirely\n# # ignore: [\"deepinfra\", \"fireworks\"]\n#\n# # Try providers in this order (overrides default load balancing)\n# # order: [\"anthropic\", \"google\", \"together\"]\n#\n# # Require providers to support all parameters in your request\n# # require_parameters: true\n#\n# # Data policy: \"allow\" (default) or \"deny\" to exclude providers that may store data\n# # data_collection: \"deny\"\n\n# =============================================================================\n# OpenRouter Response Caching (only applies when using OpenRouter)\n# =============================================================================\n# Cache identical API responses at the OpenRouter edge for free instant replays.\n# When enabled, identical requests (same model, messages, parameters) return\n# cached responses with zero billing. Separate from Anthropic prompt caching.\n# See: https://openrouter.ai/docs/guides/features/response-caching\n#\n# openrouter:\n# response_cache: true # Enable response caching (default: true)\n# response_cache_ttl: 300 # Cache TTL in seconds, 1-86400 (default: 300)\n\n# =============================================================================\n# Git Worktree Isolation\n# =============================================================================\n# When enabled, each CLI session creates an isolated git worktree so multiple\n# agents can work on the same repo concurrently without file collisions.\n# Equivalent to always passing --worktree / -w on the command line.\n#\n# worktree: true # Always create a worktree when in a git repo\n# worktree: false # Default — only create when -w flag is passed\n#\n# By default a new worktree branches from the freshly-fetched remote tip\n# (the current branch's upstream, else the remote's default branch) so it\n# starts current with the project instead of from the local clone's\n# (possibly stale) HEAD. Set worktree_sync: false to branch from local HEAD\n# instead — useful when offline or when you deliberately want the clone's\n# exact current state as the base.\n#\n# worktree_sync: true # Default — branch from the fetched remote tip\n# worktree_sync: false # Branch from local HEAD (offline / pinned base)\n\n# =============================================================================\n# Terminal Tool Configuration\n# =============================================================================\n# Choose ONE of the following terminal configurations by uncommenting it.\n# The terminal tool executes commands in the specified environment.\n\n# -----------------------------------------------------------------------------\n# OPTION 1: Local execution (default)\n# Commands run directly on your machine in the current directory\n# -----------------------------------------------------------------------------\n# Working directory behavior:\n# - CLI (`hermes` command): Uses \".\" (current directory where you run hermes)\n# - Gateway/messaging/cron: Uses terminal.cwd here; legacy .env cwd values are deprecated\nterminal:\n backend: \"local\"\n cwd: \".\" # For local backend: \".\" = current directory. Ignored for remote backends unless a backend documents otherwise.\n timeout: 180\n # HOME policy for tool subprocesses:\n # auto - default: host uses your real HOME; containers use HERMES_HOME/home\n # real - force your real OS-user HOME\n # profile - force HERMES_HOME/home for strict per-profile CLI config isolation\n home_mode: \"auto\"\n docker_mount_cwd_to_workspace: false # SECURITY: off by default. Opt in to mount the launch cwd into Docker /workspace.\n lifetime_seconds: 300\n # sudo_password: \"hunter2\" # Optional: pipe a sudo password via sudo -S. SECURITY WARNING: plaintext.\n # sudo_password: \"\" # Explicit empty password: try empty and never open the interactive sudo prompt.\n\n# -----------------------------------------------------------------------------\n# OPTION 2: SSH remote execution\n# Commands run on a remote server - agent code stays local (sandboxed)\n# Great for: keeping agent isolated from its own code, using powerful remote hardware\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"ssh\"\n# cwd: \"/home/myuser/project\" # Path on the REMOTE server\n# timeout: 180\n# lifetime_seconds: 300\n# ssh_host: \"my-server.example.com\"\n# ssh_user: \"myuser\"\n# ssh_port: 22\n# ssh_key: \"~/.ssh/id_rsa\" # Optional - uses ssh-agent if not specified\n\n# -----------------------------------------------------------------------------\n# OPTION 3: Docker container\n# Commands run in an isolated Docker container\n# Great for: reproducible environments, testing, isolation\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"docker\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /)\n# timeout: 180\n# lifetime_seconds: 300\n# docker_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# docker_mount_cwd_to_workspace: true # Explicit opt-in: mount your launch cwd into /workspace\n# # Optional: run the container as your host user's uid:gid so files written\n# # into bind-mounted dirs are owned by you, not root. Drops SETUID/SETGID\n# # caps too since no gosu privilege drop is needed. Leave off if your\n# # chosen docker_image expects to start as root.\n# docker_run_as_host_user: true\n# # Optional: explicitly forward selected env vars into Docker.\n# # These values come from your current shell first, then ~/.hermes/.env.\n# # Warning: anything forwarded here is visible to commands run in the container.\n# docker_forward_env:\n# - \"GITHUB_TOKEN\"\n# - \"NPM_TOKEN\"\n# # Optional: extra flags passed verbatim to docker run (appended after security defaults).\n# # Useful for adding capabilities (e.g. apt installs needing SETUID) or custom options.\n# # Example: add a Linux capability not included by default\n# # docker_extra_args:\n# # - \"--cap-add\"\n# # - \"SETUID\"\n\n# -----------------------------------------------------------------------------\n# OPTION 4: Singularity/Apptainer container\n# Commands run in a Singularity container (common in HPC environments)\n# Great for: HPC clusters, shared compute environments\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"singularity\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# singularity_image: \"docker://nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 5: Modal cloud execution\n# Commands run on Modal's cloud infrastructure\n# Great for: GPU access, scalable compute, serverless execution\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"modal\"\n# cwd: \"/workspace\" # Path INSIDE the sandbox (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# modal_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 6: Daytona cloud execution\n# Commands run in Daytona cloud sandboxes\n# Great for: Cloud dev environments, persistent workspaces, team collaboration\n# Requires: pip install daytona, DAYTONA_API_KEY env var\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"daytona\"\n# cwd: \"~\"\n# timeout: 180\n# lifetime_seconds: 300\n# daytona_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# container_disk: 10240 # Daytona max is 10GB per sandbox\n\n#\n# --- Container resource limits (docker, singularity, modal, daytona -- ignored for local/ssh) ---\n# These settings apply to all container backends. They control the resources\n# allocated to the sandbox and whether its filesystem persists across sessions.\n container_cpu: 1 # CPU cores\n container_memory: 5120 # Memory in MB (5120 = 5GB)\n container_disk: 51200 # Disk in MB (51200 = 50GB)\n container_persistent: true # Persist filesystem across sessions (false = ephemeral)\n\n# -----------------------------------------------------------------------------\n# SUDO SUPPORT (works with ALL backends above)\n# -----------------------------------------------------------------------------\n# Add sudo_password to any terminal config above to enable sudo commands.\n# The password is piped via `sudo -S`. Works with local, ssh, docker, etc.\n#\n# SECURITY WARNING: Password stored in plaintext!\n#\n# INTERACTIVE PROMPT: If sudo_password is unset and the CLI is running,\n# you'll be prompted to enter your password when sudo is needed:\n# - 45-second timeout (auto-skips if no input)\n# - Press Enter to skip (command fails gracefully)\n# - Password is hidden while typing\n# - Password is cached for the session\n#\n# EMPTY PASSWORDS: Setting sudo_password to an explicit empty string is different\n# from leaving it unset. Hermes will try an empty password via `sudo -S` and\n# will not open the interactive prompt. This is useful for passwordless sudo,\n# Touch ID sudo setups, and environments where prompting is just noise.\n#\n# ALTERNATIVES:\n# - SSH backend: Configure passwordless sudo on the remote server\n# - Containers: Run as root inside the container (no sudo needed)\n# - Local: Configure /etc/sudoers for specific commands\n#\n# Example (add to your terminal section):\n# sudo_password: \"your-password-here\"\n\n# =============================================================================\n# Security Scanning (tirith)\n# =============================================================================\n# Optional pre-exec command security scanning via tirith.\n# Detects homograph URLs, pipe-to-shell, terminal injection, env manipulation.\n# Install: brew install sheeki03/tap/tirith\n# Docs: https://github.com/sheeki03/tirith\n#\n# security:\n# tirith_enabled: true # Enable/disable tirith scanning\n# tirith_path: \"tirith\" # Path to tirith binary (supports ~ expansion)\n# tirith_timeout: 5 # Scan timeout in seconds\n# tirith_fail_open: true # Allow commands if tirith unavailable\n\n# =============================================================================\n# Browser Tool Configuration\n# =============================================================================\nbrowser:\n # Inactivity timeout in seconds - browser sessions are automatically closed\n # after this period of no activity between agent loops (default: 120 = 2 minutes)\n inactivity_timeout: 120\n\n# =============================================================================\n# Tool Loop Guardrails\n# =============================================================================\n# Soft warnings are enabled by default. They append guidance to repeated failed\n# or non-progressing tool results but still let the tool execute. Hard stops are\n# opt-in circuit breakers for autonomous/cron sessions where stopping a loop is\n# preferable to spending the full iteration budget.\ntool_loop_guardrails:\n warnings_enabled: true\n hard_stop_enabled: false\n warn_after:\n exact_failure: 2\n same_tool_failure: 3\n idempotent_no_progress: 2\n hard_stop_after:\n exact_failure: 5\n same_tool_failure: 8\n idempotent_no_progress: 5\n\n# =============================================================================\n# Context Compression (Auto-shrinks long conversations)\n# =============================================================================\n# When conversation approaches model's context limit, middle turns are\n# automatically summarized to free up space while preserving important context.\n#\n# HOW IT WORKS:\n# 1. Tracks actual token usage from API responses (not estimates)\n# 2. When prompt_tokens >= threshold% of model's context_length, triggers compression\n# 3. Protects first 3 turns (system prompt, initial request, first response)\n# 4. Protects last N turns (default 20 messages = ~10 full turns of recent context)\n# 5. Summarizes middle turns using a fast/cheap model\n# 6. Inserts summary as a user message, continues conversation seamlessly\n#\n# Post-compression tail budget is target_ratio × threshold × context_length:\n# 200K context, threshold 0.50, ratio 0.20 → 20K tokens of recent tail preserved\n# 1M context, threshold 0.50, ratio 0.20 → 100K tokens of recent tail preserved\n#\ncompression:\n # Enable automatic context compression (default: true)\n # Set to false if you prefer to manage context manually or want errors on overflow\n enabled: true\n \n # Trigger compression at this % of model's context limit (default: 0.50 = 50%)\n # Lower values = more aggressive compression, higher values = compress later\n # Models with context windows below 512K are floored at 0.75 (raise-only) so\n # compaction doesn't fire with half the window still free; set above 0.75 to override.\n threshold: 0.50\n\n # Existing Codex gpt-5.5 behavior: raise Hermes' compaction trigger to 85%\n # for the ChatGPT Codex OAuth route. Set false to opt back down to threshold.\n codex_gpt55_autoraise: true\n\n # Fraction of the threshold to preserve as recent tail (default: 0.20 = 20%)\n # e.g. 20% of 50% threshold = 10% of total context kept as recent messages.\n # Summary output is separately capped at 12K tokens (Gemini output limit).\n # Range: 0.10 - 0.80\n target_ratio: 0.20\n\n # Number of most-recent messages to always preserve (default: 20 ≈ 10 full turns)\n # Higher values keep more recent conversation intact at the cost of more aggressive\n # compression of older turns.\n protect_last_n: 20\n\n # Codex app-server (codex CLI runtime) thread-compaction mode. The codex\n # agent owns the real thread context on this runtime, so Hermes' summarizer\n # cannot shrink it — compaction goes through the app server instead.\n # native = let Codex decide when to compact its own thread (default)\n # hermes = let Hermes threshold trigger Codex thread/compact/start\n # off = Hermes will not auto-trigger compaction; Codex may still compact natively\n codex_app_server_auto: native\n\n # Number of non-system messages to protect at the head of the transcript, in\n # ADDITION to the system prompt (which is always implicitly protected).\n # Head messages are NEVER summarized — they survive every compression\n # indefinitely. This gives stable early context for short/medium sessions,\n # but in long-running sessions that rely on rolling compaction the pinned\n # opening turns may not match how you want the session framed over time.\n # Set to 0 to preserve ONLY the system prompt (plus the rolling summary\n # and recent tail) — the cleanest configuration for long-running sessions.\n # Default 3 preserves the system prompt plus the first three non-system\n # head messages, matching the pre-feature behaviour.\n protect_first_n: 3\n\n # To pin a specific model/provider for compression summaries, use the\n # auxiliary section below (auxiliary.compression.provider / model).\n\n# =============================================================================\n# Anthropic prompt caching TTL\n# =============================================================================\n# When prompt caching is active (Claude via OpenRouter or native Anthropic),\n# Anthropic supports two TTL tiers for cached prefixes: \"5m\" (default) and\n# \"1h\". Other values are ignored and \"5m\" is used.\n#\nprompt_caching:\n cache_ttl: \"5m\" # use \"1h\" for long sessions with pauses between turns\n\n# =============================================================================\n# Auxiliary Models (Advanced — Experimental)\n# =============================================================================\n# Hermes uses lightweight \"auxiliary\" models for side tasks: image analysis,\n# browser screenshot analysis, web page summarization, TTS audio-tag insertion,\n# and context compression.\n#\n# By default these use Gemini Flash via OpenRouter or Nous Portal and are\n# auto-detected from your credentials. You do NOT need to change anything\n# here for normal usage.\n#\n# WARNING: Overriding these with providers other than OpenRouter or Nous Portal\n# is EXPERIMENTAL and may not work. Not all models/providers support vision,\n# produce usable summaries, or accept the same API format. Change at your own\n# risk — if things break, reset to \"auto\" / empty values.\n#\n# Each task has its own provider + model pair so you can mix providers.\n# For example: OpenRouter for vision (needs multimodal), but your main\n# local endpoint for compression (just needs text).\n#\n# Provider options:\n# \"auto\" - Best available: OpenRouter → Nous Portal → main endpoint (default)\n# \"openrouter\" - Force OpenRouter (requires OPENROUTER_API_KEY)\n# \"nous\" - Force Nous Portal (requires: hermes auth add nous)\n# \"gemini\" - Force Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n# \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY)\n# \"codex\" - Force Codex OAuth (requires: hermes model → Codex).\n# Uses gpt-5.3-codex which supports vision.\n# \"main\" - Use your custom endpoint (OPENAI_BASE_URL + OPENAI_API_KEY).\n# Works with OpenAI API, local models, or any OpenAI-compatible\n# endpoint. Also falls back to Codex OAuth and API-key providers.\n#\n# Model: leave empty to use the provider's default. When empty, OpenRouter\n# uses \"google/gemini-3-flash-preview\" and Nous uses \"gemini-3-flash\".\n# Other providers pick a sensible default automatically.\n#\n# auxiliary:\n# # Image analysis: vision_analyze tool + browser screenshots\n# vision:\n# provider: \"auto\"\n# model: \"\" # e.g. \"google/gemini-2.5-flash\", \"openai/gpt-4o\"\n# timeout: 30 # LLM API call timeout (seconds)\n# download_timeout: 30 # Image HTTP download timeout (seconds)\n# # Increase for slow connections or self-hosted image servers\n# reasoning_effort: \"\" # Per-task thinking level: none, minimal, low, medium,\n# # high, xhigh, max, ultra. Empty = provider default.\n# # Works on every auxiliary task block (vision,\n# # web_extract, compression, title_generation, curator,\n# # background_review, moa_reference, ...). Example: run\n# # compression at \"low\" and vision at \"none\" to cut\n# # side-task latency/cost on reasoning models.\n#\n# # Web page scraping / summarization + browser page text extraction\n# web_extract:\n# provider: \"auto\"\n# model: \"\"\n# reasoning_effort: \"low\"\n#\n# # Gemini 3.1 TTS hidden audio-tag insertion\n# tts_audio_tags:\n# provider: \"auto\" # empty model = your main chat model\n# model: \"\"\n# timeout: 30\n#\n# # Automatic session title generation after the first exchange\n# title_generation:\n# enabled: true # set false to disable auto-title generation\n# provider: \"auto\"\n# model: \"\"\n# timeout: 30\n# language: \"\" # empty = match the user's language; or e.g. \"English\"\n#\n# # Session search — summarizes matching past sessions\n# session_search:\n# provider: \"auto\"\n# model: \"\"\n# timeout: 30\n# max_concurrency: 3 # Limit parallel summaries to reduce request-burst 429s\n# extra_body: {} # Provider-specific OpenAI-compatible request fields\n# # Example for providers that support request-body\n# # reasoning controls:\n# # extra_body:\n# # enable_thinking: false\n# # Some vLLM/Qwen deployments expect this nested:\n# # extra_body:\n# # chat_template_kwargs:\n# # enable_thinking: false\n\n# =============================================================================\n# Persistent Memory\n# =============================================================================\n# Bounded curated memory injected into the system prompt every session.\n# Two stores: MEMORY.md (agent's notes) and USER.md (user profile).\n# Character limits keep the memory small and focused. The agent manages\n# pruning -- when at the limit, it must consolidate or replace entries.\n# Disabled by default in batch_runner.\n#\nmemory:\n # Agent's personal notes: environment facts, conventions, things learned\n memory_enabled: true\n \n # User profile: preferences, communication style, expectations\n user_profile_enabled: true\n \n # Character limits (~2.75 chars per token, model-independent)\n memory_char_limit: 2200 # ~800 tokens\n user_char_limit: 1375 # ~500 tokens\n\n # Periodic memory nudge: remind the agent to consider saving memories\n # every N user turns. Set to 0 to disable. Only active when memory is enabled.\n nudge_interval: 10 # Nudge every 10 user turns (0 = disabled)\n\n # Memory flush: give the agent one turn to save memories before context is\n # lost (compression, /new, /reset, exit). Set to 0 to disable.\n # For exit/reset, only fires if the session had at least this many user turns.\n flush_min_turns: 6 # Min user turns to trigger flush on exit/reset (0 = disabled)\n\n# =============================================================================\n# Session Reset Policy (Messaging Platforms)\n# =============================================================================\n# Controls when messaging sessions (Telegram, Discord, WhatsApp, Slack) are\n# automatically cleared. Default is \"none\": sessions never auto-reset —\n# conversation context lives until you /reset or /new manually, or context\n# compression kicks in. Opt in to automatic resets if you prefer sessions to\n# clear on a schedule (long-lived context increases API cost per message,\n# though prompt caching and compression keep this manageable).\n#\n# When an automatic reset triggers, the agent first saves important\n# information to its persistent memory — but the conversation context is\n# wiped. The agent starts fresh but retains learned facts via its memory\n# system.\n#\n# Modes:\n# \"none\" - Never auto-reset (default); context lives until /reset or compression\n# \"idle\" - Reset after N minutes of inactivity\n# \"daily\" - Reset at a fixed hour each day\n# \"both\" - Reset on EITHER inactivity timeout or daily boundary\n#\nsession_reset:\n mode: none # \"none\", \"idle\", \"daily\", or \"both\"\n idle_minutes: 1440 # Inactivity timeout in minutes (used by \"idle\"/\"both\")\n at_hour: 4 # Daily reset hour, 0-23 local time (used by \"daily\"/\"both\")\n\n# Maximum number of simultaneously active chat sessions across CLI, TUI,\n# dashboard chat, and messaging gateway. Set to null, 0, or omit to allow\n# unlimited concurrent sessions. When the limit is reached, new sessions get a\n# clean error while existing active sessions keep their normal behavior. This\n# top-level key takes precedence over gateway.max_concurrent_sessions. The cap\n# is a best-effort single-host/profile runtime guard; Hermes fails open if the\n# local runtime lease registry cannot be read or locked.\nmax_concurrent_sessions: null\n\n# When true, group/channel chats use one session per participant when the platform\n# provides a user ID. This is the secure default and prevents users in the same\n# room from sharing context, interrupts, and token costs. Set false only if you\n# explicitly want one shared \"room brain\" per group/channel.\ngroup_sessions_per_user: true\n\n# ─────────────────────────────────────────────────────────────────────────────\n# API Server — per-client model routing\n# ─────────────────────────────────────────────────────────────────────────────\n# Route different API clients to different models/providers on a single\n# Hermes deployment. Clients choose a backend by sending a specific string\n# as the OpenAI ``model`` field. Unmapped model values fall back to the\n# global model configured in the ``model:`` section above, and an explicit\n# session /model override always wins over a route.\n#\n# Configure via the ``platforms.api_server.extra.model_routes`` gateway\n# config block:\n#\n# platforms:\n# api_server:\n# enabled: true\n# extra:\n# key: \"your-api-server-secret\"\n# model_routes:\n# # Xiaozhi clients send model=\"minimax-m2\" → routed to MiniMax via OpenRouter\n# minimax-m2:\n# model: \"minimax/minimax-m1\"\n# provider: \"openrouter\" # optional — overrides global provider\n# # api_key: \"sk-...\" # optional — per-route UPSTREAM provider\n# # key (NOT caller auth; never logged)\n# # base_url: \"https://...\" # optional — per-route base URL\n# # GPT clients keep their own alias\n# gpt-5:\n# model: \"openai/gpt-5\"\n# provider: \"openrouter\"\n#\n# Configured aliases are automatically listed by GET /v1/models so clients\n# can discover them without manual coordination. Caller authentication is\n# unchanged: every request still authenticates with the global API server\n# key (``extra.key`` / API_SERVER_KEY).\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Gateway Streaming\n# ─────────────────────────────────────────────────────────────────────────────\n# Stream tokens to messaging platforms in real-time. The bot sends a message\n# on first token, then progressively edits it as more tokens arrive.\n# Disabled by default — enable to try the streaming UX on Telegram/Discord/Slack.\n# For Telegram, partial edits are sent as plain text and only the final edit uses MarkdownV2.\nstreaming:\n enabled: false\n # transport: edit # \"edit\" = progressive editMessageText\n # edit_interval: 0.3 # seconds between message edits\n # buffer_threshold: 40 # chars before forcing an edit flush\n # cursor: \" ▉\" # cursor shown during streaming\n\n# =============================================================================\n# Skills Configuration\n# =============================================================================\n# Skills are reusable procedures the agent can load and follow. The agent can\n# also create new skills after completing complex tasks.\n#\nskills:\n # Nudge the agent to create skills after complex tasks.\n # Every N tool-calling iterations, remind the model to consider saving a skill.\n # Set to 0 to disable.\n creation_nudge_interval: 15\n\n # External skill directories — share skills across tools/agents without\n # copying them into ~/.hermes/skills/. Each path is expanded (~ and ${VAR})\n # and resolved to an absolute path. External dirs are read-only: skill\n # creation always writes to ~/.hermes/skills/. Local skills take precedence\n # when names collide.\n # external_dirs:\n # - ~/.agents/skills\n # - /home/shared/team-skills\n\n# =============================================================================\n# Agent Behavior\n# =============================================================================\nagent:\n # Maximum tool-calling iterations per conversation\n # Higher = more room for complex tasks, but costs more tokens\n # Recommended: 20-30 for focused tasks, 50-100 for open exploration\n max_turns: 60\n\n # Inactivity timeout for gateway agent runs (seconds, 0 = unlimited).\n # The agent can run indefinitely when actively calling tools or receiving\n # API responses. Only fires after the agent has been idle for this duration.\n # gateway_timeout: 1800\n\n # Staged warning: send a warning before escalating to full timeout.\n # Fires once per run when inactivity reaches this threshold (seconds).\n # Set to 0 to disable the warning.\n # gateway_timeout_warning: 900\n\n # Graceful drain timeout for gateway stop/restart (seconds).\n # Default 0 = no drain: a restart interrupts in-flight agents immediately,\n # cleans up, and exits. Set a positive value only if you want a grace\n # window on /restart, and keep it well under systemd's TimeoutStopSec.\n # restart_drain_timeout: 0\n\n # Max app-level retry attempts for API errors (connection drops, provider\n # timeouts, 5xx, etc.) before the agent surfaces the failure. Lower this\n # to 1 if you use fallback providers and want fast failover on flaky\n # primaries (default 3). The OpenAI SDK does its own low-level retries\n # underneath this wrapper — this is the Hermes-level loop.\n # api_max_retries: 3\n\n # After the agent edits code without fresh passing verification, nudge it to\n # verify before finishing. The default \"auto\" enables it on interactive\n # coding surfaces (CLI, TUI, desktop) and programmatic callers, and disables\n # it on conversational messaging surfaces (Telegram, Discord, etc.) where the\n # verification summary would reach a human as chat noise. Set true or false to\n # force it on or off; the HERMES_VERIFY_ON_STOP env var (1/0) takes precedence.\n # verify_on_stop: auto\n\n # Standing operator instructions for the coding posture (when Hermes is in a\n # code workspace). Appended to the coding brief as an extra system block, so\n # you can pin project-wide workflow rules without editing the shipped brief.\n # Accepts a string or a list of strings. Takes effect next session.\n # coding_instructions:\n # - \"For UI work, don't run tsc/lint until I approve the look.\"\n # - \"Clean the diff before you commit and push.\"\n\n # When verify-on-stop finds edited code without fresh verification evidence,\n # append guidance for creative UI work (avoid broad tsc/lint/test before visual\n # approval) and clean-diff expectations. Set false to keep that nudge terse.\n # verify_guidance: true\n\n # A `pre_verify` hook (plugin or shell, see Event Hooks docs) can keep the\n # agent going one more turn to verify/clean before finishing. This caps how\n # many times one turn may be nudged to continue, so a hook can't trap the loop.\n # Default 3.\n # max_verify_nudges: 3\n\n # Enable verbose logging\n verbose: false\n \n # Reasoning effort level (OpenRouter and Nous Portal)\n # Controls how much \"thinking\" the model does before responding.\n # Options: \"xhigh\" (max), \"high\", \"medium\", \"low\", \"minimal\", \"none\" (disable)\n reasoning_effort: \"medium\"\n \n # Per-model reasoning effort overrides (optional dict)\n # Key: any sensible model spelling works (exact, dots↔dashes interchangeable,\n # provider prefix optional). First match wins.\n # Value: reasoning effort level (same options as reasoning_effort)\n # Override the global reasoning_effort for that specific model.\n # NOTE: no `hermes config set` support for this key -- edit YAML directly.\n # reasoning_overrides:\n # \"openrouter/anthropic/claude-opus-4.5\": \"xhigh\"\n # \"openai/gpt-5\": \"low\"\n # \"claude-opus-4.6\": \"high\" # bare model name also works\n # \"deepseek/deepseek-v4-pro\": \"xhigh\" # dots and dashes are interchangeable\n reasoning_overrides: {}\n \n # Predefined personalities (use with /personality command)\n personalities:\n helpful: \"You are a helpful, friendly AI assistant.\"\n concise: \"You are a concise assistant. Keep responses brief and to the point.\"\n technical: \"You are a technical expert. Provide detailed, accurate technical information.\"\n creative: \"You are a creative assistant. Think outside the box and offer innovative solutions.\"\n teacher: \"You are a patient teacher. Explain concepts clearly with examples.\"\n kawaii: \"You are a kawaii assistant! Use cute expressions like (◕‿◕), ★, ♪, and ~! Add sparkles and be super enthusiastic about everything! Every response should feel warm and adorable desu~! ヽ(>∀<☆)ノ\"\n catgirl: \"You are Neko-chan, an anime catgirl AI assistant, nya~! Add 'nya' and cat-like expressions to your speech. Use kaomoji like (=^・ω・^=) and ฅ^•ﻌ•^ฅ. Be playful and curious like a cat, nya~!\"\n pirate: \"Arrr! Ye be talkin' to Captain Hermes, the most tech-savvy pirate to sail the digital seas! Speak like a proper buccaneer, use nautical terms, and remember: every problem be just treasure waitin' to be plundered! Yo ho ho!\"\n shakespeare: \"Hark! Thou speakest with an assistant most versed in the bardic arts. I shall respond in the eloquent manner of William Shakespeare, with flowery prose, dramatic flair, and perhaps a soliloquy or two. What light through yonder terminal breaks?\"\n surfer: \"Duuude! You're chatting with the chillest AI on the web, bro! Everything's gonna be totally rad. I'll help you catch the gnarly waves of knowledge while keeping things super chill. Cowabunga! 🤙\"\n noir: \"The rain hammered against the terminal like regrets on a guilty conscience. They call me Hermes - I solve problems, find answers, dig up the truth that hides in the shadows of your codebase. In this city of silicon and secrets, everyone's got something to hide. What's your story, pal?\"\n uwu: \"hewwo! i'm your fwiendwy assistant uwu~ i wiww twy my best to hewp you! *nuzzles your code* OwO what's this? wet me take a wook! i pwomise to be vewy hewpful >w<\"\n philosopher: \"Greetings, seeker of wisdom. I am an assistant who contemplates the deeper meaning behind every query. Let us examine not just the 'how' but the 'why' of your questions. Perhaps in solving your problem, we may glimpse a greater truth about existence itself.\"\n hype: \"YOOO LET'S GOOOO!!! 🔥🔥🔥 I am SO PUMPED to help you today! Every question is AMAZING and we're gonna CRUSH IT together! This is gonna be LEGENDARY! ARE YOU READY?! LET'S DO THIS! 💪😤🚀\"\n\n# =============================================================================\n# Toolsets\n# =============================================================================\n# Control which tools the agent has access to.\n# Use `hermes tools` to interactively enable/disable tools per platform.\n\n# =============================================================================\n# Platform Toolsets (per-platform tool configuration)\n# =============================================================================\n# Override which toolsets are available on each platform.\n# If a platform isn't listed here, its built-in default is used.\n#\n# You can use EITHER:\n# - A preset like \"hermes-cli\" or \"hermes-telegram\" (curated tool set)\n# - A list of individual toolsets to compose your own (see list below)\n#\n# Supported platform keys: cli, telegram, discord, whatsapp, slack, qqbot, teams, google_chat\n#\n# Examples:\n#\n# # Use presets (same as defaults):\n# platform_toolsets:\n# cli: [hermes-cli]\n# telegram: [hermes-telegram]\n#\n# # Custom: give Telegram only web + terminal + file + planning:\n# platform_toolsets:\n# telegram: [web, terminal, file, todo]\n#\n# # Custom: CLI without browser or image gen:\n# platform_toolsets:\n# cli: [web, terminal, file, skills, todo, tts, cronjob]\n#\n# # Restrictive: Discord gets read-only tools only:\n# platform_toolsets:\n# discord: [web, vision, skills, todo]\n#\n# If not set, defaults are:\n# cli: hermes-cli (everything + cronjob management)\n# telegram: hermes-telegram (terminal, file, web, vision, image, tts, browser, skills, todo, cronjob, messaging)\n# discord: hermes-discord (same as telegram)\n# whatsapp: hermes-whatsapp (same as telegram)\n# slack: hermes-slack (same as telegram)\n# signal: hermes-signal (same as telegram)\n# homeassistant: hermes-homeassistant (same as telegram)\n# qqbot: hermes-qqbot (same as telegram)\n# teams: hermes-teams (same as telegram)\n# google_chat: hermes-google_chat (same as telegram)\n#\nplatform_toolsets:\n cli: [hermes-cli]\n telegram: [hermes-telegram]\n discord: [hermes-discord]\n whatsapp: [hermes-whatsapp]\n slack: [hermes-slack]\n signal: [hermes-signal]\n homeassistant: [hermes-homeassistant]\n qqbot: [hermes-qqbot]\n yuanbao: [hermes-yuanbao]\n teams: [hermes-teams]\n google_chat: [hermes-google_chat]\n\n# =============================================================================\n# Gateway Platform Settings\n# =============================================================================\n# Optional per-platform messaging settings.\n# Platform-specific knobs live under `extra`.\n#\n# platforms:\n# telegram:\n# reply_to_mode: \"first\" # off | first | all\n# # guest_mode lets explicit @mentions from non-allowlisted groups through.\n# # Default false; ordinary messages, replies, and regex wake words stay blocked.\n# guest_mode: false\n# # allowed_chats: [\"-1001234567890\"]\n# extra:\n# disable_link_previews: false # Set true to suppress Telegram URL previews in bot messages\n# rich_messages: false # Bot API 10.1 rich messages (tables/task lists/details/math); default false for copyable legacy MarkdownV2, set true to opt in\n# rich_drafts: false # Experimental rich draft previews during Telegram DM streaming; default false because Telegram Desktop/macOS can visually overlay draft frames\n# command_menu:\n# # Telegram allows up to 100 BotCommands; Hermes defaults to 60 so\n# # all built-in commands plus common skill commands stay visible\n# # while remaining under Telegram's payload-size limit. Clamped 1..100.\n# max_commands: 60\n# # prepend = user priority first, then Hermes defaults\n# # append = Hermes defaults first, then user priority\n# # replace = only the list below defines priority\n# priority_mode: prepend\n# priority:\n# - my_plugin_command\n# webhook:\n# extra:\n# # Route scripts default to a 30 second timeout. Scripts must live under\n# # the active profile's scripts directory and receive webhook JSON on stdin.\n# script_timeout_seconds: 30\n#\n# Discord-specific settings (config.yaml top-level, not under platforms:):\n#\n# discord:\n# require_mention: true # Require @mention in server channels (default: true)\n# auto_thread: true # Auto-create thread on @mention (default: true)\n# free_response_channels: \"\" # Channel IDs where no mention is needed\n# reactions: true # Show processing reactions (default: true)\n# history_backfill: true # Recover missed channel messages on mention (default: true)\n# history_backfill_limit: 50 # Max messages to scan backwards (default: 50)\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Available toolsets (use these names in platform_toolsets or the toolsets list)\n#\n# Run `hermes chat --list-toolsets` to see all toolsets and their tools.\n# Run `hermes chat --list-tools` to see every individual tool with descriptions.\n# ─────────────────────────────────────────────────────────────────────────────\n#\n# INDIVIDUAL TOOLSETS (compose your own):\n# web - web_search, web_extract\n# search - web_search only (no scraping)\n# terminal - terminal, process\n# file - read_file, write_file, patch, search\n# browser - browser_navigate, browser_snapshot, browser_click, browser_type,\n# browser_scroll, browser_back, browser_press,\n# browser_get_images, browser_vision (requires BROWSERBASE_API_KEY)\n# vision - vision_analyze (requires OPENROUTER_API_KEY)\n# image_gen - image_generate (requires FAL_KEY)\n# skills - skills_list, skill_view\n# skills_hub - skill_hub (search/install/manage from online registries — user-driven only)\n# todo - todo (in-memory task planning, no deps)\n# tts - text_to_speech (Edge TTS free, or ELEVENLABS/OPENAI/MINIMAX/MISTRAL key)\n# cronjob - cronjob (create/list/update/pause/resume/run/remove scheduled tasks)\n#\n# PRESETS (curated bundles):\n# hermes-cli - All of the above except send_message\n# hermes-telegram - terminal, file, web, vision, image_gen, tts, browser,\n# skills, todo, cronjob, send_message\n# hermes-discord - Same as hermes-telegram\n# hermes-whatsapp - Same as hermes-telegram\n# hermes-slack - Same as hermes-telegram\n#\n# COMPOSITE:\n# debugging - terminal + web + file\n# safe - web + vision (no terminal access)\n# all - Everything available\n#\n# web - Web search and content extraction (web_search, web_extract)\n# search - Web search only, no scraping (web_search)\n# terminal - Command execution and process management (terminal, process)\n# file - File operations: read, write, patch, search\n# browser - Full browser automation (navigate, click, type, screenshot, etc.)\n# vision - Image analysis (vision_analyze)\n# image_gen - Image generation with FLUX (image_generate)\n# skills - Load skill documents (skills_list, skill_view)\n# todo - Task planning and tracking for multi-step work\n# memory - Persistent memory across sessions (personal notes + user profile)\n# session_search - Search and recall past conversations (FTS5 + Gemini Flash summarization)\n# tts - Text-to-speech (Edge TTS free, ElevenLabs, OpenAI, MiniMax, Mistral)\n# cronjob - Schedule and manage automated tasks (CLI-only)\n#\n# Composite toolsets:\n# debugging - terminal + web + file (for troubleshooting)\n# safe - web + vision (no terminal access)\n\n# NOTE: The top-level \"toolsets\" key is deprecated and ignored.\n# Tool configuration is managed per-platform via platform_toolsets above.\n# Use `hermes tools` to configure interactively, or edit platform_toolsets directly.\n#\n# CLI override: hermes chat --toolsets terminal,web,file\n\n# =============================================================================\n# MCP (Model Context Protocol) Servers\n# =============================================================================\n# Connect to external MCP servers to add tools from the MCP ecosystem.\n# Each server's tools are automatically discovered and registered.\n# See website/docs/user-guide/features/mcp.md for full documentation.\n#\n# Stdio servers (spawn a subprocess):\n# command: the executable to run\n# args: command-line arguments\n# env: environment variables (only these + safe defaults passed to subprocess)\n#\n# HTTP servers (connect to a URL):\n# url: the MCP server endpoint\n# headers: HTTP headers (e.g., for authentication)\n#\n# Optional per-server settings:\n# timeout: tool call timeout in seconds (default: 120)\n# connect_timeout: initial connection timeout (default: 60)\n# keepalive_interval: liveness ping cadence in seconds (default: 180).\n# Lower it below the server's session TTL for servers that expire idle\n# sessions quickly (e.g. Unreal Engine editor MCP, ~15s), otherwise idle\n# tool calls hit an expired session and pay a slow reconnect. Floored at 5s.\n#\n# mcp_servers:\n# time:\n# command: uvx\n# args: [\"mcp-server-time\"]\n# filesystem:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-filesystem\", \"/home/user\"]\n# notion:\n# url: https://mcp.notion.com/mcp\n# github:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-github\"]\n# env:\n# GITHUB_PERSONAL_ACCESS_TOKEN: \"ghp_...\"\n#\n# Sampling (server-initiated LLM requests) — enabled by default.\n# Per-server config under the 'sampling' key:\n# analysis:\n# command: npx\n# args: [\"-y\", \"analysis-server\"]\n# sampling:\n# enabled: true # default: true\n# model: \"gemini-3-flash\" # override model (optional)\n# max_tokens_cap: 4096 # max tokens per request\n# timeout: 30 # LLM call timeout (seconds)\n# max_rpm: 10 # max requests per minute\n# allowed_models: [] # model whitelist (empty = all)\n# max_tool_rounds: 5 # tool loop limit (0 = disable)\n# log_level: \"info\" # audit verbosity\n\n# =============================================================================\n# Text-to-Speech\n# =============================================================================\n# TTS defaults to Edge TTS unless changed in ~/.hermes/config.yaml.\n# Gemini TTS supports persona/director prompt files, and Gemini 3.1 Flash TTS\n# can use a hidden auxiliary rewrite pass to insert expressive square-bracket\n# audio tags into the TTS script without showing tags in chat.\n#\n# tts:\n# provider: \"gemini\"\n# gemini:\n# model: \"gemini-3.1-flash-tts-preview\"\n# voice: \"Kore\"\n# audio_tags: false\n# persona_prompt_file: \"\" # e.g. ~/.hermes/tts/radio-host.md\n\n# =============================================================================\n# Voice Transcription (Speech-to-Text)\n# =============================================================================\n# Automatically transcribe voice messages on messaging platforms.\n# Providers: local (free, faster-whisper) | groq (free tier) | openai (Whisper API) | mistral (Voxtral Transcribe)\n# Set the corresponding API key in .env: GROQ_API_KEY, OPENAI_API_KEY, or MISTRAL_API_KEY.\nstt:\n enabled: true\n # provider: \"local\" # auto-detected if omitted\n local:\n model: \"base\" # tiny | base | small | medium | large-v3 | turbo\n # language: \"\" # auto-detect; set to \"en\", \"es\", \"fr\", etc. to force\n openai:\n model: \"whisper-1\" # whisper-1 | gpt-4o-mini-transcribe | gpt-4o-transcribe\n # mistral:\n # model: \"voxtral-mini-latest\" # voxtral-mini-latest | voxtral-mini-2602\n # deepinfra:\n # # Model id is discovered live from the DeepInfra catalog filtered\n # # by the `stt` surface tag — leave `model` blank to take the first\n # # live result. Pin only when you need a specific Whisper variant.\n # model: \"\"\n\n# Text-to-speech. Only the deepinfra block is documented here — the\n# remaining providers (edge, openai, xai, minimax, mistral, gemini,\n# elevenlabs, neutts, kittentts, piper) inherit sensible defaults from\n# DEFAULT_CONFIG in hermes_cli/config.py.\n# tts:\n# provider: \"deepinfra\"\n# deepinfra:\n# # Model id is discovered live from the DeepInfra catalog filtered\n# # by the `tts` surface tag — leave `model` blank to take the first\n# # live result.\n# model: \"\"\n# voice: \"default\"\n\n# Image generation. Each provider plugin reads its own ``image_gen.``\n# block; deepinfra discovers models live from\n# api.deepinfra.com/v1/openai/models filtered by the ``image-gen`` tag —\n# no model id is hardcoded, so retired models disappear automatically.\n# image_gen:\n# provider: \"deepinfra\"\n# deepinfra:\n# # Leave `model` blank for the first live `image-gen`-tagged result.\n# model: \"\"\n\n# =============================================================================\n# Response Pacing (Messaging Platforms)\n# =============================================================================\n# Add human-like delays between message chunks.\n# human_delay:\n# mode: \"off\" # \"off\" | \"natural\" | \"custom\"\n# min_ms: 800 # Min delay (custom mode only)\n# max_ms: 2500 # Max delay (custom mode only)\n\n# =============================================================================\n# Session Logging\n# =============================================================================\n# Session trajectories are automatically saved to logs/ directory.\n# Each session creates: logs/session_YYYYMMDD_HHMMSS_UUID.json\n#\n# The session ID is displayed in the welcome banner for easy reference.\n# Logs contain full conversation history in trajectory format:\n# - System prompt, user messages, assistant responses\n# - Tool calls with inputs/outputs\n# - Timestamps for debugging\n#\n# No configuration needed - logging is always enabled.\n# To disable, you would need to modify the source code.\n\n# =============================================================================\n# Code Execution Sandbox (Programmatic Tool Calling)\n# =============================================================================\n# The execute_code tool runs Python scripts that call Hermes tools via RPC.\n# Intermediate tool results stay out of the LLM's context window.\ncode_execution:\n timeout: 300 # Max seconds per script before kill (default: 300 = 5 min)\n max_tool_calls: 50 # Max RPC tool calls per execution (default: 50)\n\n# =============================================================================\n# Subagent Delegation\n# =============================================================================\n# The delegate_task tool spawns child agents with isolated context.\n# Supports single tasks and batch mode (default 3 parallel, configurable).\ndelegation:\n max_iterations: 50 # Max tool-calling turns per child (default: 50)\n # max_concurrent_children: 3 # Max parallel child agents per batch (default: 3, floor: 1, no ceiling).\n # WARNING: values above 10 multiply API cost linearly.\n # max_spawn_depth: 1 # Delegation tree depth cap (range: 1-3, default: 1 = flat).\n # Raise to 2 to allow workers to spawn their own subagents.\n # Requires role=\"orchestrator\" on intermediate agents.\n # orchestrator_enabled: true # Kill switch for role=\"orchestrator\" children (default: true).\n # subagent_auto_approve: false # When a subagent hits a dangerous-command approval prompt, auto-deny (default: false)\n # or auto-approve \"once\" (true) instead of blocking on stdin.\n # The parent TUI owns stdin, so blocking would deadlock; non-interactive resolution is required.\n # Both choices emit a logger.warning audit line. Flip to true only for cron/batch pipelines.\n # inherit_mcp_toolsets: true # When explicit child toolsets are narrowed, also keep the parent's MCP toolsets (default: true). Set false for strict intersection.\n # model: \"google/gemini-3-flash-preview\" # Override model for subagents (empty = inherit parent)\n # provider: \"openrouter\" # Override provider for subagents (empty = inherit parent)\n # # Resolves full credentials (base_url, api_key) automatically.\n # # Supported: openrouter, nous, zai, kimi-coding, minimax\n\n# =============================================================================\n# Honcho Integration (Cross-Session User Modeling)\n# =============================================================================\n# AI-native persistent memory via Honcho (https://honcho.dev/).\n# Builds a deeper understanding of the user across sessions and tools.\n# Runs alongside USER.md — additive, not a replacement.\n#\n# Requires: pip install honcho-ai\n# Config: ~/.honcho/config.json (shared with Claude Code, Cursor, etc.)\n# API key: HONCHO_API_KEY in ~/.hermes/.env or ~/.honcho/config.json\n#\n# Hermes-specific overrides (optional — most config comes from ~/.honcho/config.json):\n# honcho: {}\n\n# =============================================================================\n# Display\n# =============================================================================\ndisplay:\n # Use compact banner mode (hides the ASCII-art banner, shows a single line).\n # true: Compact single-line banner\n # false: Full ASCII banner with tool/skill summary (default)\n compact: false\n\n # Tool progress display level (CLI and gateway)\n # off: Silent — no tool activity shown, just the final response\n # new: Show a tool indicator only when the tool changes (skip repeats)\n # all: Show every tool call with a short preview (default)\n # verbose: Full args, results, and debug logs (same as /verbose)\n # log: Silent in chat; append every tool call to ~/.hermes/logs/tool_calls.log (gateway only)\n # Toggle at runtime with /verbose in the CLI\n tool_progress: all\n\n # Per-platform defaults can be quieter than the global setting. Telegram\n # tunes for mobile: tool_progress and busy_ack_detail default off (no\n # per-tool breadcrumb stream, no \"iteration 21/60\" debug detail in busy\n # acks or heartbeats), but interim_assistant_messages and\n # long_running_notifications STAY ON so the user has real signal between\n # turn start and final answer (mid-turn assistant commentary + a single\n # edit-in-place \"⏳ Working — N min\" heartbeat). Override under\n # display.platforms.telegram.\n\n # Auto-cleanup of temporary progress bubbles after the final response lands.\n # On platforms that support message deletion (currently Telegram), this\n # removes the tool-progress bubble, \"⏳ Still working...\" notices, and\n # context-pressure status messages once the final reply has been delivered —\n # keeping long-running turns visible live, then tidy afterward. Failed runs\n # leave the bubbles in place as breadcrumbs. Off by default.\n # Per-platform override: display.platforms.telegram.cleanup_progress\n # true: Delete tracked progress/status bubbles on successful turn\n # false: Leave everything in place (default)\n # Example:\n # display:\n # platforms:\n # telegram:\n # cleanup_progress: true\n cleanup_progress: false\n\n # Gateway-only natural mid-turn assistant updates.\n # When true, completed assistant status messages are sent as separate chat\n # messages. This is independent of tool_progress and gateway streaming.\n # true: Send mid-turn assistant updates as separate messages (default)\n # false: Only send the final response\n interim_assistant_messages: true\n\n # Gateway-only long-running status heartbeats.\n # When false, the platform does not receive periodic \"⏳ Working — N min\"\n # notifications even if agent.gateway_notify_interval is non-zero. The\n # heartbeat edits a single message in place (where the adapter supports\n # editing) instead of posting a new bubble each interval.\n # Default: true everywhere, including Telegram (silent agents are worse\n # than a single edit-in-place heartbeat).\n long_running_notifications: true\n\n # Include detailed iteration/tool/status context in busy acknowledgments\n # and long-running heartbeats. When true, busy acks show \"iteration 21/60,\n # terminal, 10 min\" and the heartbeat shows \"⏳ Working — 12 min,\n # iteration 21/60, terminal\". When false (Telegram default), both stay\n # terse: \"Interrupting current task\" and \"⏳ Working — 12 min, terminal\".\n busy_ack_detail: true\n\n # What Enter does when Hermes is already busy (CLI and gateway platforms).\n # interrupt: Interrupt the current run and redirect Hermes (default)\n # queue: Queue your message for the next turn\n # steer: Inject your message mid-run via /steer, arriving at the agent\n # after the next tool call — no interrupt, no role violation.\n # Falls back to 'queue' if the agent isn't running yet or if\n # images are attached (steer only carries text).\n # Ctrl+C (or /stop in gateway) always interrupts regardless of this setting.\n # Toggle at runtime with /busy .\n busy_input_mode: interrupt\n\n # Background process notifications (gateway/messaging only).\n # Controls how chatty the process watcher is when you use\n # terminal(background=true, notify_on_complete=true) from Telegram/Discord/etc.\n # off: No watcher messages at all\n # result: Only the final completion message\n # error: Only the final message when exit code != 0\n # all: Running output updates + final message (default)\n background_process_notifications: all\n\n\n # Play terminal bell when agent finishes a response.\n # Useful for long-running tasks — your terminal will ding when the agent is done.\n # Works over SSH. Most terminals can be configured to flash the taskbar or play a sound.\n # true: Ring the terminal bell on each response\n # false: Silent (default)\n bell_on_complete: false\n\n # Show model reasoning/thinking before each response.\n # When enabled, a dim box shows the model's thought process above the response.\n # Toggle at runtime with /reasoning show or /reasoning hide.\n # true: Show the reasoning box\n # false: Hide reasoning (default)\n show_reasoning: false\n\n # Stream tokens to the terminal as they arrive instead of waiting for the\n # full response. The response box opens on first token and text appears\n # line-by-line. Tool calls are still captured silently.\n # true: Stream tokens as they arrive (default)\n # false: Wait for the full response before rendering\n streaming: true\n\n # Show [HH:MM] timestamps on user input and assistant response labels.\n # timestamps: false\n\n # ───────────────────────────────────────────────────────────────────────────\n # Skin / Theme\n # ───────────────────────────────────────────────────────────────────────────\n # Customize CLI visual appearance — banner colors, spinner faces, tool prefix,\n # response box label, and branding text. Change at runtime with /skin .\n #\n # Built-in skins:\n # default — Classic Hermes gold/kawaii\n # ares — Crimson/bronze war-god theme with spinner wings\n # mono — Clean grayscale monochrome\n # slate — Cool blue developer-focused\n # daylight — Bright light-mode theme\n # warm-lightmode — Warm paper-tone light-mode theme\n # poseidon — Sea-green/teal Olympian theme\n # sisyphus — Earthy stone-and-moss theme\n # charizard — Fiery orange dragon theme\n #\n # Custom skins: drop a YAML file in ~/.hermes/skins/.yaml\n # Schema (all fields optional, missing values inherit from default):\n #\n # name: my-theme\n # description: Short description\n # colors:\n # banner_border: \"#HEX\" # Panel border\n # banner_title: \"#HEX\" # Panel title\n # banner_accent: \"#HEX\" # Section headers (Available Tools, etc.)\n # banner_dim: \"#HEX\" # Dim/muted text\n # banner_text: \"#HEX\" # Body text (tool names, skill names)\n # ui_accent: \"#HEX\" # UI accent color\n # response_border: \"#HEX\" # Response box border color\n # spinner:\n # waiting_faces: [\"(⚔)\", \"(⛨)\"] # Faces shown while waiting\n # thinking_faces: [\"(⚔)\", \"(⌁)\"] # Faces shown while thinking\n # thinking_verbs: [\"forging\", \"plotting\"] # Verbs for spinner messages\n # wings: # Optional left/right spinner decorations\n # - [\"⟪⚔\", \"⚔⟫\"]\n # - [\"⟪▲\", \"▲⟫\"]\n # branding:\n # agent_name: \"My Agent\" # Banner title and branding\n # welcome: \"Welcome message\" # Shown at CLI startup\n # response_label: \" ⚔ Agent \" # Response box header label\n # prompt_symbol: \"⚔\" # Prompt symbol (bare token; renderers add trailing space)\n # tool_prefix: \"╎\" # Tool output line prefix (default: ┊)\n #\n skin: default\n\n# =============================================================================\n# Model Aliases — short names for /model command\n# =============================================================================\n# Map short aliases to exact (model, provider, base_url) tuples.\n# Used by /model tab completion and resolve_alias().\n# Aliases are checked BEFORE the models.dev catalog, so they can route\n# to endpoints not in the catalog (e.g. Ollama Cloud, local servers).\n#\n# model_aliases:\n# opus:\n# model: claude-opus-4-6\n# provider: anthropic\n# qwen:\n# model: \"qwen3.5:397b\"\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n# glm:\n# model: glm-4.7\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n\n# =============================================================================\n# Privacy\n# =============================================================================\n# privacy:\n# # Redact PII from the LLM context prompt.\n# # When true, phone numbers are stripped and user/chat IDs are replaced\n# # with deterministic hashes before being sent to the model.\n# # Names and usernames are NOT affected (user-chosen, publicly visible).\n# # Routing/delivery still uses the original values internally.\n# redact_pii: false\n\n# =============================================================================\n# Shell-script hooks\n# =============================================================================\n# Register shell scripts as plugin-hook callbacks. Each entry is executed as\n# a subprocess (shell=False, shlex.split) with a JSON payload on stdin. On\n# stdout the script may return JSON that either blocks the tool call or\n# injects context into the next LLM call.\n#\n# Valid events (mirror hermes_cli.plugins.VALID_HOOKS):\n# pre_tool_call, post_tool_call, pre_llm_call, post_llm_call,\n# pre_api_request, post_api_request, on_session_start, on_session_end,\n# on_session_finalize, on_session_reset, subagent_stop\n#\n# First-use consent: each (event, command) pair prompts once on a TTY, then\n# is persisted to ~/.hermes/shell-hooks-allowlist.json. Non-interactive\n# runs (gateway, cron) need --accept-hooks, HERMES_ACCEPT_HOOKS=1, or the\n# hooks_auto_accept key below.\n#\n# See website/docs/user-guide/features/hooks.md for the full JSON wire\n# protocol and worked examples.\n#\n# hooks:\n# pre_tool_call:\n# - matcher: \"terminal\"\n# command: \"~/.hermes/agent-hooks/block-rm-rf.sh\"\n# timeout: 10\n# post_tool_call:\n# - matcher: \"write_file|patch\"\n# command: \"~/.hermes/agent-hooks/auto-format.sh\"\n# pre_llm_call:\n# - command: \"~/.hermes/agent-hooks/inject-cwd-context.sh\"\n# subagent_stop:\n# - command: \"~/.hermes/agent-hooks/log-orchestration.sh\"\n#\n# hooks_auto_accept: false\n\n\n# =============================================================================\n# Update Behavior\n# =============================================================================\nupdates:\n # Create a full HERMES_HOME zip before every `hermes update`.\n # Backups land in ~/.hermes/backups/ and can be restored with `hermes import`.\n # Off by default because large homes can add minutes to every update.\n pre_update_backup: false\n\n # Number of pre-update backup zips to retain.\n backup_keep: 5\n\n # What non-interactive updates do with local source edits in the Hermes repo.\n # Interactive terminal updates always prompt before restoring the autostash.\n #\n # stash - auto-stash before pull, then auto-restore after success (default)\n # discard - drop the update-created stash after success; use only on managed\n # installs where local source edits should not persist\n non_interactive_local_changes: \"stash\"\n\n\n# =============================================================================\n# Web Dashboard\n# =============================================================================\n# OAuth gate configuration for `hermes dashboard --host `.\n# The bundled Nous Portal plugin reads these on startup; settings here are\n# the canonical surface. Each can be overridden by an environment variable:\n#\n# dashboard.oauth.client_id <- HERMES_DASHBOARD_OAUTH_CLIENT_ID\n# dashboard.oauth.portal_url <- HERMES_DASHBOARD_PORTAL_URL\n# dashboard.public_url <- HERMES_DASHBOARD_PUBLIC_URL\n#\n# Env wins when set to a non-empty value. This is what Fly.io's platform-\n# secret injection uses to push per-deploy client_ids without needing to\n# bake a config.yaml into the image. Empty env values are treated as unset\n# so a provisioned-but-not-populated secret can't shadow a valid entry here.\n#\n# Local dev / on-prem deploys should typically set these via config.yaml\n# (the ~/.hermes/.env file is reserved for API keys and secrets).\n#\n# dashboard:\n# oauth:\n# client_id: \"\" # agent:{instance_id}; Portal provisions this at deploy\n# portal_url: \"\" # blank → default https://portal.nousresearch.com\n#\n# # Force the absolute base URL the OAuth callback (and any other public\n# # URL the dashboard hands to external systems) is built from. Set this\n# # for deploys behind reverse proxies that don't reliably forward\n# # X-Forwarded-Host / X-Forwarded-Proto / X-Forwarded-Prefix (manual\n# # nginx setups, on-prem ingresses, custom-domain Fly deploys without\n# # full proxy header chains).\n# #\n# # When set, the value is the complete authority: scheme + host +\n# # optional path prefix (e.g. \"https://example.com/hermes\"). The OAuth\n# # callback URL becomes \"/auth/callback\" — X-Forwarded-Prefix\n# # is IGNORED on this code path because the operator has explicitly\n# # declared the public URL and we no longer need to guess.\n# #\n# # Leave empty to use the existing proxy-header reconstruction (the\n# # default — works on Fly.io out of the box).\n# #\n# # public_url: \"https://example.com/hermes\"\n#\n# -----------------------------------------------------------------------------\n# Self-hosted OIDC dashboard auth (generic OpenID Connect — Authentik,\n# Keycloak, Zitadel, Authelia, Auth0, Okta, Google, …). Use this INSTEAD of the\n# nous block above when gating the dashboard with your own identity provider.\n# Each setting can be overridden by an environment variable:\n#\n# dashboard.oauth.self_hosted.issuer <- HERMES_DASHBOARD_OIDC_ISSUER\n# dashboard.oauth.self_hosted.client_id <- HERMES_DASHBOARD_OIDC_CLIENT_ID\n# dashboard.oauth.self_hosted.scopes <- HERMES_DASHBOARD_OIDC_SCOPES\n# dashboard.oauth.self_hosted.client_secret <- HERMES_DASHBOARD_OIDC_CLIENT_SECRET\n#\n# dashboard:\n# oauth:\n# provider: self-hosted\n# self_hosted:\n# issuer: \"https://auth.example.com/application/o/hermes/\" # required\n# client_id: \"hermes-dashboard\" # required\n# scopes: \"openid profile email\" # optional\n#\n# # OPTIONAL — set ONLY if your IDP registered the client as\n# # *confidential* (Authentik / Keycloak often default to this). When\n# # set, Hermes authenticates the client at the token endpoint\n# # (client_secret_basic or client_secret_post, auto-selected from the\n# # IDP's discovery doc) IN ADDITION to PKCE. Leave unset for a public\n# # (PKCE-only) client — the common case.\n# #\n# # This is a CREDENTIAL: prefer setting HERMES_DASHBOARD_OIDC_CLIENT_SECRET\n# # in ~/.hermes/.env over putting it here in config.yaml.\n# # client_secret: \"\"\n\n# =============================================================================\n# External secret sources\n# =============================================================================\n# Pull provider credentials from external secret managers at process startup\n# instead of storing them in ~/.hermes/.env. Only the manager's bootstrap\n# credential (e.g. BWS_ACCESS_TOKEN / OP_SERVICE_ACCOUNT_TOKEN) lives in .env\n# (or your shell / desktop session); everything else rotates centrally.\n# Failures never block startup — Hermes warns once and continues with\n# whatever .env already had.\n#\n# Multiple sources can be enabled at once:\n# - \"mapped\" sources (explicit VAR -> ref bindings, e.g. 1Password's env:\n# map) beat \"bulk\" sources (whole-project dumps like Bitwarden BSM)\n# - within a shape, the first source to claim a var wins; later claims\n# are skipped with a startup warning (never a silent clobber)\n# - a source's override_existing lets it beat .env/shell values, but\n# never another secret source's claim\n# Docs: https://hermes-agent.nousresearch.com/docs/user-guide/secrets/\n#\n# secrets:\n# # Optional explicit ordering of enabled sources.\n# # sources: [onepassword, bitwarden]\n#\n# # ---- Bitwarden Secrets Manager (bws CLI) --------------------------------\n# bitwarden:\n# enabled: false\n# access_token_env: BWS_ACCESS_TOKEN # bootstrap token, sourced from .env\n# project_id: \"\" # UUID of the BSM project to sync\n# server_url: \"\" # \"\" = US Cloud; EU/self-hosted URL otherwise\n# cache_ttl_seconds: 300 # 0 disables caching\n# override_existing: true # BSM values win over existing env\n# auto_install: true # lazy-download bws into ~/.hermes/bin\n#\n# # ---- 1Password (op CLI) -------------------------------------------------\n# onepassword:\n# enabled: false\n# # Map env-var names to op:// secret references. Each is resolved with a\n# # single `op read` at startup.\n# env:\n# OPENAI_API_KEY: \"op://Private/OpenAI/api key\"\n# ANTHROPIC_API_KEY: \"op://Private/Anthropic/credential\"\n# account: \"\" # op --account shorthand; \"\" = default\n# service_account_token_env: OP_SERVICE_ACCOUNT_TOKEN # headless auth; unset = desktop session\n# binary_path: \"\" # \"\" = resolve op via PATH; else absolute path\n# cache_ttl_seconds: 300 # 0 disables BOTH cache layers\n# override_existing: true # resolved values win over existing env\n"}, "8222b1678574fdcf6e137778fcec51ca74038935": {"t": 1785767862.2558649, "n": 77099, "h": "2f64f588ba37f62a", "c": "# Hermes Agent CLI Configuration\n# Copy this file to cli-config.yaml and customize as needed.\n# This file configures the CLI behavior. Environment variables in .env take precedence.\n\n# =============================================================================\n# Model Configuration\n# =============================================================================\nmodel:\n # Default model to use (can be overridden with --model flag)\n # Both \"default\" and \"model\" work as the key name here.\n default: \"anthropic/claude-opus-4.6\"\n \n # Inference provider selection:\n # \"auto\" - Auto-detect from credentials (default)\n # \"openrouter\" - OpenRouter (requires: OPENROUTER_API_KEY or OPENAI_API_KEY)\n # \"nous\" - Nous Portal OAuth (requires: hermes auth add nous)\n # \"nous-api\" - Nous Portal API key (requires: NOUS_API_KEY)\n # \"anthropic\" - Direct Anthropic API (requires: ANTHROPIC_API_KEY)\n # \"openai-codex\" - OpenAI Codex (requires: hermes auth)\n # \"copilot\" - GitHub Copilot / GitHub Models (requires: GITHUB_TOKEN)\n # \"gemini\" - Use Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n # \"zai\" - Use z.ai / ZhipuAI GLM models (requires: GLM_API_KEY)\n # \"kimi-coding\" - Kimi / Moonshot AI (requires: KIMI_API_KEY)\n # \"minimax\" - MiniMax global (requires: MINIMAX_API_KEY)\n # \"minimax-cn\" - MiniMax China (requires: MINIMAX_CN_API_KEY)\n # \"huggingface\" - Hugging Face Inference (requires: HF_TOKEN)\n # \"nvidia\" - NVIDIA NIM / build.nvidia.com (requires: NVIDIA_API_KEY)\n # \"xiaomi\" - Xiaomi MiMo (requires: XIAOMI_API_KEY)\n # \"arcee\" - Arcee AI Trinity models (requires: ARCEEAI_API_KEY)\n # \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY — https://ollama.com/settings)\n # \"deepinfra\" - DeepInfra (requires: DEEPINFRA_API_KEY)\n # \"kilocode\" - KiloCode gateway (requires: KILOCODE_API_KEY)\n # \"azure-foundry\" - Microsoft Foundry / Azure OpenAI (API key or Entra ID)\n # \"lmstudio\" - LM Studio local server (optional: LM_API_KEY, defaults to http://127.0.0.1:1234/v1)\n #\n # Local servers (LM Studio, Ollama, vLLM, llama.cpp):\n # \"custom\" - Any other OpenAI-compatible endpoint. Set base_url below.\n # Aliases: \"ollama\", \"vllm\", \"llamacpp\" all map to \"custom\".\n # LM Studio is first-class and uses provider: \"lmstudio\".\n # It works with both no-auth and auth-enabled server modes.\n #\n # Can also be overridden for a single invocation with the --provider flag.\n provider: \"auto\"\n \n # API configuration (falls back to OPENROUTER_API_KEY env var)\n # api_key: \"your-key-here\" # Uncomment to set here instead of .env\n base_url: \"https://openrouter.ai/api/v1\"\n\n # Azure Foundry keyless auth example:\n # provider: \"azure-foundry\"\n # base_url: \"https://.openai.azure.com/openai/v1\"\n # auth_mode: \"entra_id\" # DefaultAzureCredential: az login, managed identity, workload identity, etc.\n # default: \"gpt-4o\" # Deployment/model name\n # entra:\n # scope: \"https://ai.azure.com/.default\" # Optional; this is the default.\n\n # ── Token limits — two settings, easy to confuse ──────────────────────────\n #\n # context_length: TOTAL context window (input + output tokens combined).\n # Controls when Hermes compresses history and validates requests.\n # Leave unset — Hermes auto-detects the correct value from the provider.\n # Set manually only when auto-detection is wrong (e.g. a local server with\n # a custom num_ctx, or a proxy that doesn't expose /v1/models).\n #\n # context_length: 131072\n #\n # max_tokens: OUTPUT cap — maximum tokens the model may generate per response.\n # Unrelated to how long your conversation history can be.\n # The OpenAI-standard name \"max_tokens\" is a misnomer; Anthropic's native\n # API has since renamed it \"max_output_tokens\" for clarity.\n # Leave unset to use the model's native output ceiling (recommended).\n # Set only if you want to deliberately limit individual response length.\n #\n# max_tokens: 8192\n\n # ── Custom request headers (optional) ─────────────────────────────────────\n #\n # default_headers: extra HTTP headers sent on every request to an\n # OpenAI-compatible endpoint. User values take precedence over the\n # provider/SDK defaults, so this is the supported way to override the\n # OpenAI Python SDK's identifying headers (User-Agent: OpenAI/Python ...,\n # X-Stainless-*) when a custom provider sits behind a gateway/WAF that\n # rejects them — e.g. an upstream that returns \"502 Upstream access\n # forbidden\" for the SDK default User-Agent but accepts a plain one.\n # Applies on the OpenAI wire only (not native Anthropic / Bedrock).\n #\n # default_headers:\n # User-Agent: \"curl/8.7.1\"\n #\n # extra_headers: accepted as an alias of default_headers (merged, with\n # extra_headers winning when both are set) — matches the per-provider\n # extra_headers key below.\n #\n # Per-provider variant: named providers / custom_providers entries accept an\n # extra_headers dict scoped to that endpoint only — for reverse proxies,\n # gateways, or custom auth (e.g. Cloudflare Access service tokens).\n # Merged onto SDK/provider defaults with the entry's values winning.\n # Header values are treated as secrets and are never logged.\n #\n # providers:\n # my-proxy:\n # base_url: \"https://llm.internal.example.com/v1\"\n # key_env: \"MY_PROXY_API_KEY\"\n # extra_headers:\n # CF-Access-Client-Id: \"xxxx.access\"\n # CF-Access-Client-Secret: \"${CF_ACCESS_SECRET}\"\n # X-Client-Name: \"hermes-agent\"\n\n# Named provider overrides (optional)\n# Use this for per-provider request timeouts, non-stream stale timeouts,\n# and per-model exceptions.\n# Applies to the primary turn client on every api_mode (OpenAI-wire, native\n# Anthropic, and Anthropic-compatible providers), the fallback chain, and\n# client rebuilds during credential rotation. For OpenAI-wire chat\n# completions (streaming and non-streaming) the configured value is also\n# used as the per-request ``timeout=`` kwarg so it wins over the legacy\n# HERMES_API_TIMEOUT env var (which still applies when no config is set).\n# ``stale_timeout_seconds`` controls the non-streaming stale-call detector and\n# wins over the legacy HERMES_API_CALL_STALE_TIMEOUT env var. Leaving these\n# unset keeps the legacy defaults (HERMES_API_TIMEOUT=1800s,\n# HERMES_API_CALL_STALE_TIMEOUT=90s, native Anthropic 900s). The\n# implicit non-stream stale detector is auto-disabled for local endpoints\n# and can scale upward for very large contexts.\n#\n# Not currently wired for AWS Bedrock (bedrock_converse + AnthropicBedrock\n# SDK paths) — those use boto3 with its own timeout configuration.\n#\n# providers:\n# ollama-local:\n# request_timeout_seconds: 300 # Longer timeout for local cold-starts\n# stale_timeout_seconds: 900 # Explicitly re-enable stale detection on local endpoints\n# anthropic:\n# request_timeout_seconds: 30 # Fast-fail cloud requests\n# models:\n# claude-opus-4.6:\n# timeout_seconds: 600 # Longer timeout for extended-thinking Opus calls\n# openai-codex:\n# models:\n# gpt-5.4:\n# stale_timeout_seconds: 1800 # Longer non-stream stale timeout for slow large-context turns\n\n# =============================================================================\n# OpenRouter Provider Routing (only applies when using OpenRouter)\n# =============================================================================\n# Control how requests are routed across providers on OpenRouter.\n# See: https://openrouter.ai/docs/guides/routing/provider-selection\n#\n# provider_routing:\n# # Sort strategy: \"price\" (default), \"throughput\", or \"latency\"\n# # Append :nitro to model name for a shortcut to throughput sorting.\n# sort: \"throughput\"\n#\n# # Only allow these providers (provider slugs from OpenRouter)\n# # only: [\"anthropic\", \"google\"]\n#\n# # Skip these providers entirely\n# # ignore: [\"deepinfra\", \"fireworks\"]\n#\n# # Try providers in this order (overrides default load balancing)\n# # order: [\"anthropic\", \"google\", \"together\"]\n#\n# # Require providers to support all parameters in your request\n# # require_parameters: true\n#\n# # Data policy: \"allow\" (default) or \"deny\" to exclude providers that may store data\n# # data_collection: \"deny\"\n\n# =============================================================================\n# OpenRouter Response Caching (only applies when using OpenRouter)\n# =============================================================================\n# Cache identical API responses at the OpenRouter edge for free instant replays.\n# When enabled, identical requests (same model, messages, parameters) return\n# cached responses with zero billing. Separate from Anthropic prompt caching.\n# See: https://openrouter.ai/docs/guides/features/response-caching\n#\n# openrouter:\n# response_cache: true # Enable response caching (default: true)\n# response_cache_ttl: 300 # Cache TTL in seconds, 1-86400 (default: 300)\n\n# =============================================================================\n# Git Worktree Isolation\n# =============================================================================\n# When enabled, each CLI session creates an isolated git worktree so multiple\n# agents can work on the same repo concurrently without file collisions.\n# Equivalent to always passing --worktree / -w on the command line.\n#\n# worktree: true # Always create a worktree when in a git repo\n# worktree: false # Default — only create when -w flag is passed\n#\n# By default a new worktree branches from the freshly-fetched remote tip\n# (the current branch's upstream, else the remote's default branch) so it\n# starts current with the project instead of from the local clone's\n# (possibly stale) HEAD. Set worktree_sync: false to branch from local HEAD\n# instead — useful when offline or when you deliberately want the clone's\n# exact current state as the base.\n#\n# worktree_sync: true # Default — branch from the fetched remote tip\n# worktree_sync: false # Branch from local HEAD (offline / pinned base)\n\n# =============================================================================\n# Terminal Tool Configuration\n# =============================================================================\n# Choose ONE of the following terminal configurations by uncommenting it.\n# The terminal tool executes commands in the specified environment.\n\n# -----------------------------------------------------------------------------\n# OPTION 1: Local execution (default)\n# Commands run directly on your machine in the current directory\n# -----------------------------------------------------------------------------\n# Working directory behavior:\n# - CLI (`hermes` command): Uses \".\" (current directory where you run hermes)\n# - Gateway/messaging/cron: Uses terminal.cwd here; legacy .env cwd values are deprecated\nterminal:\n backend: \"local\"\n cwd: \".\" # For local backend: \".\" = current directory. Ignored for remote backends unless a backend documents otherwise.\n timeout: 180\n # HOME policy for tool subprocesses:\n # auto - default: host uses your real HOME; containers use HERMES_HOME/home\n # real - force your real OS-user HOME\n # profile - force HERMES_HOME/home for strict per-profile CLI config isolation\n home_mode: \"auto\"\n docker_mount_cwd_to_workspace: false # SECURITY: off by default. Opt in to mount the launch cwd into Docker /workspace.\n lifetime_seconds: 300\n # sudo_password: \"hunter2\" # Optional: pipe a sudo password via sudo -S. SECURITY WARNING: plaintext.\n # sudo_password: \"\" # Explicit empty password: try empty and never open the interactive sudo prompt.\n\n# -----------------------------------------------------------------------------\n# OPTION 2: SSH remote execution\n# Commands run on a remote server - agent code stays local (sandboxed)\n# Great for: keeping agent isolated from its own code, using powerful remote hardware\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"ssh\"\n# cwd: \"/home/myuser/project\" # Path on the REMOTE server\n# timeout: 180\n# lifetime_seconds: 300\n# ssh_host: \"my-server.example.com\"\n# ssh_user: \"myuser\"\n# ssh_port: 22\n# ssh_key: \"~/.ssh/id_rsa\" # Optional - uses ssh-agent if not specified\n\n# -----------------------------------------------------------------------------\n# OPTION 3: Docker container\n# Commands run in an isolated Docker container\n# Great for: reproducible environments, testing, isolation\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"docker\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /)\n# timeout: 180\n# lifetime_seconds: 300\n# docker_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# docker_mount_cwd_to_workspace: true # Explicit opt-in: mount your launch cwd into /workspace\n# # Optional: run the container as your host user's uid:gid so files written\n# # into bind-mounted dirs are owned by you, not root. Drops SETUID/SETGID\n# # caps too since no gosu privilege drop is needed. Leave off if your\n# # chosen docker_image expects to start as root.\n# docker_run_as_host_user: true\n# # Optional: explicitly forward selected env vars into Docker.\n# # These values come from your current shell first, then ~/.hermes/.env.\n# # Warning: anything forwarded here is visible to commands run in the container.\n# docker_forward_env:\n# - \"GITHUB_TOKEN\"\n# - \"NPM_TOKEN\"\n# # Optional: extra flags passed verbatim to docker run (appended after security defaults).\n# # Useful for adding capabilities (e.g. apt installs needing SETUID) or custom options.\n# # Example: add a Linux capability not included by default\n# # docker_extra_args:\n# # - \"--cap-add\"\n# # - \"SETUID\"\n\n# -----------------------------------------------------------------------------\n# OPTION 4: Singularity/Apptainer container\n# Commands run in a Singularity container (common in HPC environments)\n# Great for: HPC clusters, shared compute environments\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"singularity\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# singularity_image: \"docker://nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 5: Modal cloud execution\n# Commands run on Modal's cloud infrastructure\n# Great for: GPU access, scalable compute, serverless execution\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"modal\"\n# cwd: \"/workspace\" # Path INSIDE the sandbox (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# modal_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 6: Daytona cloud execution\n# Commands run in Daytona cloud sandboxes\n# Great for: Cloud dev environments, persistent workspaces, team collaboration\n# Requires: pip install daytona, DAYTONA_API_KEY env var\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"daytona\"\n# cwd: \"~\"\n# timeout: 180\n# lifetime_seconds: 300\n# daytona_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# container_disk: 10240 # Daytona max is 10GB per sandbox\n\n#\n# --- Container resource limits (docker, singularity, modal, daytona -- ignored for local/ssh) ---\n# These settings apply to all container backends. They control the resources\n# allocated to the sandbox and whether its filesystem persists across sessions.\n container_cpu: 1 # CPU cores\n container_memory: 5120 # Memory in MB (5120 = 5GB)\n container_disk: 51200 # Disk in MB (51200 = 50GB)\n container_persistent: true # Persist filesystem across sessions (false = ephemeral)\n\n# -----------------------------------------------------------------------------\n# SUDO SUPPORT (works with ALL backends above)\n# -----------------------------------------------------------------------------\n# Add sudo_password to any terminal config above to enable sudo commands.\n# The password is piped via `sudo -S`. Works with local, ssh, docker, etc.\n#\n# SECURITY WARNING: Password stored in plaintext!\n#\n# INTERACTIVE PROMPT: If sudo_password is unset and the CLI is running,\n# you'll be prompted to enter your password when sudo is needed:\n# - 45-second timeout (auto-skips if no input)\n# - Press Enter to skip (command fails gracefully)\n# - Password is hidden while typing\n# - Password is cached for the session\n#\n# EMPTY PASSWORDS: Setting sudo_password to an explicit empty string is different\n# from leaving it unset. Hermes will try an empty password via `sudo -S` and\n# will not open the interactive prompt. This is useful for passwordless sudo,\n# Touch ID sudo setups, and environments where prompting is just noise.\n#\n# ALTERNATIVES:\n# - SSH backend: Configure passwordless sudo on the remote server\n# - Containers: Run as root inside the container (no sudo needed)\n# - Local: Configure /etc/sudoers for specific commands\n#\n# Example (add to your terminal section):\n# sudo_password: \"your-password-here\"\n\n# =============================================================================\n# Security Scanning (tirith)\n# =============================================================================\n# Optional pre-exec command security scanning via tirith.\n# Detects homograph URLs, pipe-to-shell, terminal injection, env manipulation.\n# Install: brew install sheeki03/tap/tirith\n# Docs: https://github.com/sheeki03/tirith\n#\n# security:\n# tirith_enabled: true # Enable/disable tirith scanning\n# tirith_path: \"tirith\" # Path to tirith binary (supports ~ expansion)\n# tirith_timeout: 5 # Scan timeout in seconds\n# tirith_fail_open: true # Allow commands if tirith unavailable\n\n# =============================================================================\n# Browser Tool Configuration\n# =============================================================================\nbrowser:\n # Inactivity timeout in seconds - browser sessions are automatically closed\n # after this period of no activity between agent loops (default: 120 = 2 minutes)\n inactivity_timeout: 120\n\n# =============================================================================\n# Tool Loop Guardrails\n# =============================================================================\n# Soft warnings are enabled by default. They append guidance to repeated failed\n# or non-progressing tool results but still let the tool execute. Hard stops are\n# opt-in circuit breakers for autonomous/cron sessions where stopping a loop is\n# preferable to spending the full iteration budget.\ntool_loop_guardrails:\n warnings_enabled: true\n hard_stop_enabled: false\n warn_after:\n exact_failure: 2\n same_tool_failure: 3\n idempotent_no_progress: 2\n hard_stop_after:\n exact_failure: 5\n same_tool_failure: 8\n idempotent_no_progress: 5\n\n# =============================================================================\n# Context Compression (Auto-shrinks long conversations)\n# =============================================================================\n# When conversation approaches model's context limit, middle turns are\n# automatically summarized to free up space while preserving important context.\n#\n# HOW IT WORKS:\n# 1. Tracks actual token usage from API responses (not estimates)\n# 2. When prompt_tokens >= threshold% of model's context_length, triggers compression\n# 3. Protects first 3 turns (system prompt, initial request, first response)\n# 4. Protects last N turns (default 20 messages = ~10 full turns of recent context)\n# 5. Summarizes middle turns using a fast/cheap model\n# 6. Inserts summary as a user message, continues conversation seamlessly\n#\n# Post-compression tail budget is target_ratio × threshold × context_length:\n# 200K context, threshold 0.50, ratio 0.20 → 20K tokens of recent tail preserved\n# 1M context, threshold 0.50, ratio 0.20 → 100K tokens of recent tail preserved\n#\ncompression:\n # Enable automatic context compression (default: true)\n # Set to false if you prefer to manage context manually or want errors on overflow\n enabled: true\n \n # Trigger compression at this % of model's context limit (default: 0.50 = 50%)\n # Lower values = more aggressive compression, higher values = compress later\n # Models with context windows below 512K are floored at 0.75 (raise-only) so\n # compaction doesn't fire with half the window still free; set above 0.75 to override.\n threshold: 0.50\n\n # Existing Codex gpt-5.5 behavior: raise Hermes' compaction trigger to 85%\n # for the ChatGPT Codex OAuth route. Set false to opt back down to threshold.\n codex_gpt55_autoraise: true\n\n # Fraction of the threshold to preserve as recent tail (default: 0.20 = 20%)\n # e.g. 20% of 50% threshold = 10% of total context kept as recent messages.\n # Summary output is separately capped at 12K tokens (Gemini output limit).\n # Range: 0.10 - 0.80\n target_ratio: 0.20\n\n # Number of most-recent messages to always preserve (default: 20 ≈ 10 full turns)\n # Higher values keep more recent conversation intact at the cost of more aggressive\n # compression of older turns.\n protect_last_n: 20\n\n # Codex app-server (codex CLI runtime) thread-compaction mode. The codex\n # agent owns the real thread context on this runtime, so Hermes' summarizer\n # cannot shrink it — compaction goes through the app server instead.\n # native = let Codex decide when to compact its own thread (default)\n # hermes = let Hermes threshold trigger Codex thread/compact/start\n # off = Hermes will not auto-trigger compaction; Codex may still compact natively\n codex_app_server_auto: native\n\n # Number of non-system messages to protect at the head of the transcript, in\n # ADDITION to the system prompt (which is always implicitly protected).\n # Head messages are NEVER summarized — they survive every compression\n # indefinitely. This gives stable early context for short/medium sessions,\n # but in long-running sessions that rely on rolling compaction the pinned\n # opening turns may not match how you want the session framed over time.\n # Set to 0 to preserve ONLY the system prompt (plus the rolling summary\n # and recent tail) — the cleanest configuration for long-running sessions.\n # Default 3 preserves the system prompt plus the first three non-system\n # head messages, matching the pre-feature behaviour.\n protect_first_n: 3\n\n # To pin a specific model/provider for compression summaries, use the\n # auxiliary section below (auxiliary.compression.provider / model).\n\n# =============================================================================\n# Anthropic prompt caching TTL\n# =============================================================================\n# When prompt caching is active (Claude via OpenRouter or native Anthropic),\n# Anthropic supports two TTL tiers for cached prefixes: \"5m\" (default) and\n# \"1h\". Other values are ignored and \"5m\" is used.\n#\nprompt_caching:\n cache_ttl: \"5m\" # use \"1h\" for long sessions with pauses between turns\n\n# =============================================================================\n# Auxiliary Models (Advanced — Experimental)\n# =============================================================================\n# Hermes uses lightweight \"auxiliary\" models for side tasks: image analysis,\n# browser screenshot analysis, web page summarization, TTS audio-tag insertion,\n# and context compression.\n#\n# By default these use Gemini Flash via OpenRouter or Nous Portal and are\n# auto-detected from your credentials. You do NOT need to change anything\n# here for normal usage.\n#\n# WARNING: Overriding these with providers other than OpenRouter or Nous Portal\n# is EXPERIMENTAL and may not work. Not all models/providers support vision,\n# produce usable summaries, or accept the same API format. Change at your own\n# risk — if things break, reset to \"auto\" / empty values.\n#\n# Each task has its own provider + model pair so you can mix providers.\n# For example: OpenRouter for vision (needs multimodal), but your main\n# local endpoint for compression (just needs text).\n#\n# Provider options:\n# \"auto\" - Best available: OpenRouter → Nous Portal → main endpoint (default)\n# \"openrouter\" - Force OpenRouter (requires OPENROUTER_API_KEY)\n# \"nous\" - Force Nous Portal (requires: hermes auth add nous)\n# \"gemini\" - Force Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n# \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY)\n# \"codex\" - Force Codex OAuth (requires: hermes model → Codex).\n# Uses gpt-5.3-codex which supports vision.\n# \"main\" - Use your custom endpoint (OPENAI_BASE_URL + OPENAI_API_KEY).\n# Works with OpenAI API, local models, or any OpenAI-compatible\n# endpoint. Also falls back to Codex OAuth and API-key providers.\n#\n# Model: leave empty to use the provider's default. When empty, OpenRouter\n# uses \"google/gemini-3-flash-preview\" and Nous uses \"gemini-3-flash\".\n# Other providers pick a sensible default automatically.\n#\n# auxiliary:\n# # Image analysis: vision_analyze tool + browser screenshots\n# vision:\n# provider: \"auto\"\n# model: \"\" # e.g. \"google/gemini-2.5-flash\", \"openai/gpt-4o\"\n# timeout: 30 # LLM API call timeout (seconds)\n# download_timeout: 30 # Image HTTP download timeout (seconds)\n# # Increase for slow connections or self-hosted image servers\n# reasoning_effort: \"\" # Per-task thinking level: none, minimal, low, medium,\n# # high, xhigh, max, ultra. Empty = provider default.\n# # Works on every auxiliary task block (vision,\n# # web_extract, compression, title_generation, curator,\n# # background_review, moa_reference, ...). Example: run\n# # compression at \"low\" and vision at \"none\" to cut\n# # side-task latency/cost on reasoning models.\n#\n# # Web page scraping / summarization + browser page text extraction\n# web_extract:\n# provider: \"auto\"\n# model: \"\"\n# reasoning_effort: \"low\"\n#\n# # Gemini 3.1 TTS hidden audio-tag insertion\n# tts_audio_tags:\n# provider: \"auto\" # empty model = your main chat model\n# model: \"\"\n# timeout: 30\n#\n# # Automatic session title generation after the first exchange\n# title_generation:\n# enabled: true # set false to disable auto-title generation\n# provider: \"auto\"\n# model: \"\"\n# timeout: 30\n# language: \"\" # empty = match the user's language; or e.g. \"English\"\n#\n# # Session search — summarizes matching past sessions\n# session_search:\n# provider: \"auto\"\n# model: \"\"\n# timeout: 30\n# max_concurrency: 3 # Limit parallel summaries to reduce request-burst 429s\n# extra_body: {} # Provider-specific OpenAI-compatible request fields\n# # Example for providers that support request-body\n# # reasoning controls:\n# # extra_body:\n# # enable_thinking: false\n# # Some vLLM/Qwen deployments expect this nested:\n# # extra_body:\n# # chat_template_kwargs:\n# # enable_thinking: false\n\n# =============================================================================\n# Persistent Memory\n# =============================================================================\n# Bounded curated memory injected into the system prompt every session.\n# Two stores: MEMORY.md (agent's notes) and USER.md (user profile).\n# Character limits keep the memory small and focused. The agent manages\n# pruning -- when at the limit, it must consolidate or replace entries.\n# Disabled by default in batch_runner.\n#\nmemory:\n # Agent's personal notes: environment facts, conventions, things learned\n memory_enabled: true\n \n # User profile: preferences, communication style, expectations\n user_profile_enabled: true\n \n # Character limits (~2.75 chars per token, model-independent)\n memory_char_limit: 2200 # ~800 tokens\n user_char_limit: 1375 # ~500 tokens\n\n # Periodic memory nudge: remind the agent to consider saving memories\n # every N user turns. Set to 0 to disable. Only active when memory is enabled.\n nudge_interval: 10 # Nudge every 10 user turns (0 = disabled)\n\n # Memory flush: give the agent one turn to save memories before context is\n # lost (compression, /new, /reset, exit). Set to 0 to disable.\n # For exit/reset, only fires if the session had at least this many user turns.\n flush_min_turns: 6 # Min user turns to trigger flush on exit/reset (0 = disabled)\n\n# =============================================================================\n# Session Reset Policy (Messaging Platforms)\n# =============================================================================\n# Controls when messaging sessions (Telegram, Discord, WhatsApp, Slack) are\n# automatically cleared. Without resets, conversation context grows indefinitely\n# which increases API costs with every message.\n#\n# When a reset triggers, the agent first saves important information to its\n# persistent memory — but the conversation context is wiped. The agent starts\n# fresh but retains learned facts via its memory system.\n#\n# Users can always manually reset with /reset or /new in chat.\n#\n# Modes:\n# \"both\" - Reset on EITHER inactivity timeout or daily boundary (recommended)\n# \"idle\" - Reset only after N minutes of inactivity\n# \"daily\" - Reset only at a fixed hour each day\n# \"none\" - Never auto-reset; context lives until /reset or compression kicks in\n#\n# When a reset triggers, the agent gets one turn to save important memories and\n# skills before the context is wiped. Persistent memory carries across sessions.\n#\nsession_reset:\n mode: both # \"both\", \"idle\", \"daily\", or \"none\"\n idle_minutes: 1440 # Inactivity timeout in minutes (default: 1440 = 24 hours)\n at_hour: 4 # Daily reset hour, 0-23 local time (default: 4 AM)\n\n# Maximum number of simultaneously active chat sessions across CLI, TUI,\n# dashboard chat, and messaging gateway. Set to null, 0, or omit to allow\n# unlimited concurrent sessions. When the limit is reached, new sessions get a\n# clean error while existing active sessions keep their normal behavior. This\n# top-level key takes precedence over gateway.max_concurrent_sessions. The cap\n# is a best-effort single-host/profile runtime guard; Hermes fails open if the\n# local runtime lease registry cannot be read or locked.\nmax_concurrent_sessions: null\n\n# When true, group/channel chats use one session per participant when the platform\n# provides a user ID. This is the secure default and prevents users in the same\n# room from sharing context, interrupts, and token costs. Set false only if you\n# explicitly want one shared \"room brain\" per group/channel.\ngroup_sessions_per_user: true\n\n# ─────────────────────────────────────────────────────────────────────────────\n# API Server — per-client model routing\n# ─────────────────────────────────────────────────────────────────────────────\n# Route different API clients to different models/providers on a single\n# Hermes deployment. Clients choose a backend by sending a specific string\n# as the OpenAI ``model`` field. Unmapped model values fall back to the\n# global model configured in the ``model:`` section above, and an explicit\n# session /model override always wins over a route.\n#\n# Configure via the ``platforms.api_server.extra.model_routes`` gateway\n# config block:\n#\n# platforms:\n# api_server:\n# enabled: true\n# extra:\n# key: \"your-api-server-secret\"\n# model_routes:\n# # Xiaozhi clients send model=\"minimax-m2\" → routed to MiniMax via OpenRouter\n# minimax-m2:\n# model: \"minimax/minimax-m1\"\n# provider: \"openrouter\" # optional — overrides global provider\n# # api_key: \"sk-...\" # optional — per-route UPSTREAM provider\n# # key (NOT caller auth; never logged)\n# # base_url: \"https://...\" # optional — per-route base URL\n# # GPT clients keep their own alias\n# gpt-5:\n# model: \"openai/gpt-5\"\n# provider: \"openrouter\"\n#\n# Configured aliases are automatically listed by GET /v1/models so clients\n# can discover them without manual coordination. Caller authentication is\n# unchanged: every request still authenticates with the global API server\n# key (``extra.key`` / API_SERVER_KEY).\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Gateway Streaming\n# ─────────────────────────────────────────────────────────────────────────────\n# Stream tokens to messaging platforms in real-time. The bot sends a message\n# on first token, then progressively edits it as more tokens arrive.\n# Disabled by default — enable to try the streaming UX on Telegram/Discord/Slack.\n# For Telegram, partial edits are sent as plain text and only the final edit uses MarkdownV2.\nstreaming:\n enabled: false\n # transport: edit # \"edit\" = progressive editMessageText\n # edit_interval: 0.3 # seconds between message edits\n # buffer_threshold: 40 # chars before forcing an edit flush\n # cursor: \" ▉\" # cursor shown during streaming\n\n# =============================================================================\n# Skills Configuration\n# =============================================================================\n# Skills are reusable procedures the agent can load and follow. The agent can\n# also create new skills after completing complex tasks.\n#\nskills:\n # Nudge the agent to create skills after complex tasks.\n # Every N tool-calling iterations, remind the model to consider saving a skill.\n # Set to 0 to disable.\n creation_nudge_interval: 15\n\n # External skill directories — share skills across tools/agents without\n # copying them into ~/.hermes/skills/. Each path is expanded (~ and ${VAR})\n # and resolved to an absolute path. External dirs are read-only: skill\n # creation always writes to ~/.hermes/skills/. Local skills take precedence\n # when names collide.\n # external_dirs:\n # - ~/.agents/skills\n # - /home/shared/team-skills\n\n# =============================================================================\n# Agent Behavior\n# =============================================================================\nagent:\n # Maximum tool-calling iterations per conversation\n # Higher = more room for complex tasks, but costs more tokens\n # Recommended: 20-30 for focused tasks, 50-100 for open exploration\n max_turns: 60\n\n # Inactivity timeout for gateway agent runs (seconds, 0 = unlimited).\n # The agent can run indefinitely when actively calling tools or receiving\n # API responses. Only fires after the agent has been idle for this duration.\n # gateway_timeout: 1800\n\n # Staged warning: send a warning before escalating to full timeout.\n # Fires once per run when inactivity reaches this threshold (seconds).\n # Set to 0 to disable the warning.\n # gateway_timeout_warning: 900\n\n # Graceful drain timeout for gateway stop/restart (seconds).\n # Default 0 = no drain: a restart interrupts in-flight agents immediately,\n # cleans up, and exits. Set a positive value only if you want a grace\n # window on /restart, and keep it well under systemd's TimeoutStopSec.\n # restart_drain_timeout: 0\n\n # Max app-level retry attempts for API errors (connection drops, provider\n # timeouts, 5xx, etc.) before the agent surfaces the failure. Lower this\n # to 1 if you use fallback providers and want fast failover on flaky\n # primaries (default 3). The OpenAI SDK does its own low-level retries\n # underneath this wrapper — this is the Hermes-level loop.\n # api_max_retries: 3\n\n # After the agent edits code without fresh passing verification, nudge it to\n # verify before finishing. The default \"auto\" enables it on interactive\n # coding surfaces (CLI, TUI, desktop) and programmatic callers, and disables\n # it on conversational messaging surfaces (Telegram, Discord, etc.) where the\n # verification summary would reach a human as chat noise. Set true or false to\n # force it on or off; the HERMES_VERIFY_ON_STOP env var (1/0) takes precedence.\n # verify_on_stop: auto\n\n # Standing operator instructions for the coding posture (when Hermes is in a\n # code workspace). Appended to the coding brief as an extra system block, so\n # you can pin project-wide workflow rules without editing the shipped brief.\n # Accepts a string or a list of strings. Takes effect next session.\n # coding_instructions:\n # - \"For UI work, don't run tsc/lint until I approve the look.\"\n # - \"Clean the diff before you commit and push.\"\n\n # When verify-on-stop finds edited code without fresh verification evidence,\n # append guidance for creative UI work (avoid broad tsc/lint/test before visual\n # approval) and clean-diff expectations. Set false to keep that nudge terse.\n # verify_guidance: true\n\n # A `pre_verify` hook (plugin or shell, see Event Hooks docs) can keep the\n # agent going one more turn to verify/clean before finishing. This caps how\n # many times one turn may be nudged to continue, so a hook can't trap the loop.\n # Default 3.\n # max_verify_nudges: 3\n\n # Enable verbose logging\n verbose: false\n \n # Reasoning effort level (OpenRouter and Nous Portal)\n # Controls how much \"thinking\" the model does before responding.\n # Options: \"xhigh\" (max), \"high\", \"medium\", \"low\", \"minimal\", \"none\" (disable)\n reasoning_effort: \"medium\"\n \n # Per-model reasoning effort overrides (optional dict)\n # Key: any sensible model spelling works (exact, dots↔dashes interchangeable,\n # provider prefix optional). First match wins.\n # Value: reasoning effort level (same options as reasoning_effort)\n # Override the global reasoning_effort for that specific model.\n # NOTE: no `hermes config set` support for this key -- edit YAML directly.\n # reasoning_overrides:\n # \"openrouter/anthropic/claude-opus-4.5\": \"xhigh\"\n # \"openai/gpt-5\": \"low\"\n # \"claude-opus-4.6\": \"high\" # bare model name also works\n # \"deepseek/deepseek-v4-pro\": \"xhigh\" # dots and dashes are interchangeable\n reasoning_overrides: {}\n \n # Predefined personalities (use with /personality command)\n personalities:\n helpful: \"You are a helpful, friendly AI assistant.\"\n concise: \"You are a concise assistant. Keep responses brief and to the point.\"\n technical: \"You are a technical expert. Provide detailed, accurate technical information.\"\n creative: \"You are a creative assistant. Think outside the box and offer innovative solutions.\"\n teacher: \"You are a patient teacher. Explain concepts clearly with examples.\"\n kawaii: \"You are a kawaii assistant! Use cute expressions like (◕‿◕), ★, ♪, and ~! Add sparkles and be super enthusiastic about everything! Every response should feel warm and adorable desu~! ヽ(>∀<☆)ノ\"\n catgirl: \"You are Neko-chan, an anime catgirl AI assistant, nya~! Add 'nya' and cat-like expressions to your speech. Use kaomoji like (=^・ω・^=) and ฅ^•ﻌ•^ฅ. Be playful and curious like a cat, nya~!\"\n pirate: \"Arrr! Ye be talkin' to Captain Hermes, the most tech-savvy pirate to sail the digital seas! Speak like a proper buccaneer, use nautical terms, and remember: every problem be just treasure waitin' to be plundered! Yo ho ho!\"\n shakespeare: \"Hark! Thou speakest with an assistant most versed in the bardic arts. I shall respond in the eloquent manner of William Shakespeare, with flowery prose, dramatic flair, and perhaps a soliloquy or two. What light through yonder terminal breaks?\"\n surfer: \"Duuude! You're chatting with the chillest AI on the web, bro! Everything's gonna be totally rad. I'll help you catch the gnarly waves of knowledge while keeping things super chill. Cowabunga! 🤙\"\n noir: \"The rain hammered against the terminal like regrets on a guilty conscience. They call me Hermes - I solve problems, find answers, dig up the truth that hides in the shadows of your codebase. In this city of silicon and secrets, everyone's got something to hide. What's your story, pal?\"\n uwu: \"hewwo! i'm your fwiendwy assistant uwu~ i wiww twy my best to hewp you! *nuzzles your code* OwO what's this? wet me take a wook! i pwomise to be vewy hewpful >w<\"\n philosopher: \"Greetings, seeker of wisdom. I am an assistant who contemplates the deeper meaning behind every query. Let us examine not just the 'how' but the 'why' of your questions. Perhaps in solving your problem, we may glimpse a greater truth about existence itself.\"\n hype: \"YOOO LET'S GOOOO!!! 🔥🔥🔥 I am SO PUMPED to help you today! Every question is AMAZING and we're gonna CRUSH IT together! This is gonna be LEGENDARY! ARE YOU READY?! LET'S DO THIS! 💪😤🚀\"\n\n# =============================================================================\n# Toolsets\n# =============================================================================\n# Control which tools the agent has access to.\n# Use `hermes tools` to interactively enable/disable tools per platform.\n\n# =============================================================================\n# Platform Toolsets (per-platform tool configuration)\n# =============================================================================\n# Override which toolsets are available on each platform.\n# If a platform isn't listed here, its built-in default is used.\n#\n# You can use EITHER:\n# - A preset like \"hermes-cli\" or \"hermes-telegram\" (curated tool set)\n# - A list of individual toolsets to compose your own (see list below)\n#\n# Supported platform keys: cli, telegram, discord, whatsapp, slack, qqbot, teams, google_chat\n#\n# Examples:\n#\n# # Use presets (same as defaults):\n# platform_toolsets:\n# cli: [hermes-cli]\n# telegram: [hermes-telegram]\n#\n# # Custom: give Telegram only web + terminal + file + planning:\n# platform_toolsets:\n# telegram: [web, terminal, file, todo]\n#\n# # Custom: CLI without browser or image gen:\n# platform_toolsets:\n# cli: [web, terminal, file, skills, todo, tts, cronjob]\n#\n# # Restrictive: Discord gets read-only tools only:\n# platform_toolsets:\n# discord: [web, vision, skills, todo]\n#\n# If not set, defaults are:\n# cli: hermes-cli (everything + cronjob management)\n# telegram: hermes-telegram (terminal, file, web, vision, image, tts, browser, skills, todo, cronjob, messaging)\n# discord: hermes-discord (same as telegram)\n# whatsapp: hermes-whatsapp (same as telegram)\n# slack: hermes-slack (same as telegram)\n# signal: hermes-signal (same as telegram)\n# homeassistant: hermes-homeassistant (same as telegram)\n# qqbot: hermes-qqbot (same as telegram)\n# teams: hermes-teams (same as telegram)\n# google_chat: hermes-google_chat (same as telegram)\n#\nplatform_toolsets:\n cli: [hermes-cli]\n telegram: [hermes-telegram]\n discord: [hermes-discord]\n whatsapp: [hermes-whatsapp]\n slack: [hermes-slack]\n signal: [hermes-signal]\n homeassistant: [hermes-homeassistant]\n qqbot: [hermes-qqbot]\n yuanbao: [hermes-yuanbao]\n teams: [hermes-teams]\n google_chat: [hermes-google_chat]\n\n# =============================================================================\n# Gateway Platform Settings\n# =============================================================================\n# Optional per-platform messaging settings.\n# Platform-specific knobs live under `extra`.\n#\n# platforms:\n# telegram:\n# reply_to_mode: \"first\" # off | first | all\n# # guest_mode lets explicit @mentions from non-allowlisted groups through.\n# # Default false; ordinary messages, replies, and regex wake words stay blocked.\n# guest_mode: false\n# # allowed_chats: [\"-1001234567890\"]\n# extra:\n# disable_link_previews: false # Set true to suppress Telegram URL previews in bot messages\n# rich_messages: false # Bot API 10.1 rich messages (tables/task lists/details/math); default false for copyable legacy MarkdownV2, set true to opt in\n# rich_drafts: false # Experimental rich draft previews during Telegram DM streaming; default false because Telegram Desktop/macOS can visually overlay draft frames\n# command_menu:\n# # Telegram allows up to 100 BotCommands; Hermes defaults to 60 so\n# # all built-in commands plus common skill commands stay visible\n# # while remaining under Telegram's payload-size limit. Clamped 1..100.\n# max_commands: 60\n# # prepend = user priority first, then Hermes defaults\n# # append = Hermes defaults first, then user priority\n# # replace = only the list below defines priority\n# priority_mode: prepend\n# priority:\n# - my_plugin_command\n# webhook:\n# extra:\n# # Route scripts default to a 30 second timeout. Scripts must live under\n# # the active profile's scripts directory and receive webhook JSON on stdin.\n# script_timeout_seconds: 30\n#\n# Discord-specific settings (config.yaml top-level, not under platforms:):\n#\n# discord:\n# require_mention: true # Require @mention in server channels (default: true)\n# auto_thread: true # Auto-create thread on @mention (default: true)\n# free_response_channels: \"\" # Channel IDs where no mention is needed\n# reactions: true # Show processing reactions (default: true)\n# history_backfill: true # Recover missed channel messages on mention (default: true)\n# history_backfill_limit: 50 # Max messages to scan backwards (default: 50)\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Available toolsets (use these names in platform_toolsets or the toolsets list)\n#\n# Run `hermes chat --list-toolsets` to see all toolsets and their tools.\n# Run `hermes chat --list-tools` to see every individual tool with descriptions.\n# ─────────────────────────────────────────────────────────────────────────────\n#\n# INDIVIDUAL TOOLSETS (compose your own):\n# web - web_search, web_extract\n# search - web_search only (no scraping)\n# terminal - terminal, process\n# file - read_file, write_file, patch, search\n# browser - browser_navigate, browser_snapshot, browser_click, browser_type,\n# browser_scroll, browser_back, browser_press,\n# browser_get_images, browser_vision (requires BROWSERBASE_API_KEY)\n# vision - vision_analyze (requires OPENROUTER_API_KEY)\n# image_gen - image_generate (requires FAL_KEY)\n# skills - skills_list, skill_view\n# skills_hub - skill_hub (search/install/manage from online registries — user-driven only)\n# todo - todo (in-memory task planning, no deps)\n# tts - text_to_speech (Edge TTS free, or ELEVENLABS/OPENAI/MINIMAX/MISTRAL key)\n# cronjob - cronjob (create/list/update/pause/resume/run/remove scheduled tasks)\n#\n# PRESETS (curated bundles):\n# hermes-cli - All of the above except send_message\n# hermes-telegram - terminal, file, web, vision, image_gen, tts, browser,\n# skills, todo, cronjob, send_message\n# hermes-discord - Same as hermes-telegram\n# hermes-whatsapp - Same as hermes-telegram\n# hermes-slack - Same as hermes-telegram\n#\n# COMPOSITE:\n# debugging - terminal + web + file\n# safe - web + vision (no terminal access)\n# all - Everything available\n#\n# web - Web search and content extraction (web_search, web_extract)\n# search - Web search only, no scraping (web_search)\n# terminal - Command execution and process management (terminal, process)\n# file - File operations: read, write, patch, search\n# browser - Full browser automation (navigate, click, type, screenshot, etc.)\n# vision - Image analysis (vision_analyze)\n# image_gen - Image generation with FLUX (image_generate)\n# skills - Load skill documents (skills_list, skill_view)\n# todo - Task planning and tracking for multi-step work\n# memory - Persistent memory across sessions (personal notes + user profile)\n# session_search - Search and recall past conversations (FTS5 + Gemini Flash summarization)\n# tts - Text-to-speech (Edge TTS free, ElevenLabs, OpenAI, MiniMax, Mistral)\n# cronjob - Schedule and manage automated tasks (CLI-only)\n#\n# Composite toolsets:\n# debugging - terminal + web + file (for troubleshooting)\n# safe - web + vision (no terminal access)\n\n# NOTE: The top-level \"toolsets\" key is deprecated and ignored.\n# Tool configuration is managed per-platform via platform_toolsets above.\n# Use `hermes tools` to configure interactively, or edit platform_toolsets directly.\n#\n# CLI override: hermes chat --toolsets terminal,web,file\n\n# =============================================================================\n# MCP (Model Context Protocol) Servers\n# =============================================================================\n# Connect to external MCP servers to add tools from the MCP ecosystem.\n# Each server's tools are automatically discovered and registered.\n# See website/docs/user-guide/features/mcp.md for full documentation.\n#\n# Stdio servers (spawn a subprocess):\n# command: the executable to run\n# args: command-line arguments\n# env: environment variables (only these + safe defaults passed to subprocess)\n#\n# HTTP servers (connect to a URL):\n# url: the MCP server endpoint\n# headers: HTTP headers (e.g., for authentication)\n#\n# Optional per-server settings:\n# timeout: tool call timeout in seconds (default: 120)\n# connect_timeout: initial connection timeout (default: 60)\n# keepalive_interval: liveness ping cadence in seconds (default: 180).\n# Lower it below the server's session TTL for servers that expire idle\n# sessions quickly (e.g. Unreal Engine editor MCP, ~15s), otherwise idle\n# tool calls hit an expired session and pay a slow reconnect. Floored at 5s.\n#\n# mcp_servers:\n# time:\n# command: uvx\n# args: [\"mcp-server-time\"]\n# filesystem:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-filesystem\", \"/home/user\"]\n# notion:\n# url: https://mcp.notion.com/mcp\n# github:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-github\"]\n# env:\n# GITHUB_PERSONAL_ACCESS_TOKEN: \"ghp_...\"\n#\n# Sampling (server-initiated LLM requests) — enabled by default.\n# Per-server config under the 'sampling' key:\n# analysis:\n# command: npx\n# args: [\"-y\", \"analysis-server\"]\n# sampling:\n# enabled: true # default: true\n# model: \"gemini-3-flash\" # override model (optional)\n# max_tokens_cap: 4096 # max tokens per request\n# timeout: 30 # LLM call timeout (seconds)\n# max_rpm: 10 # max requests per minute\n# allowed_models: [] # model whitelist (empty = all)\n# max_tool_rounds: 5 # tool loop limit (0 = disable)\n# log_level: \"info\" # audit verbosity\n\n# =============================================================================\n# Text-to-Speech\n# =============================================================================\n# TTS defaults to Edge TTS unless changed in ~/.hermes/config.yaml.\n# Gemini TTS supports persona/director prompt files, and Gemini 3.1 Flash TTS\n# can use a hidden auxiliary rewrite pass to insert expressive square-bracket\n# audio tags into the TTS script without showing tags in chat.\n#\n# tts:\n# provider: \"gemini\"\n# gemini:\n# model: \"gemini-3.1-flash-tts-preview\"\n# voice: \"Kore\"\n# audio_tags: false\n# persona_prompt_file: \"\" # e.g. ~/.hermes/tts/radio-host.md\n\n# =============================================================================\n# Voice Transcription (Speech-to-Text)\n# =============================================================================\n# Automatically transcribe voice messages on messaging platforms.\n# Providers: local (free, faster-whisper) | groq (free tier) | openai (Whisper API) | mistral (Voxtral Transcribe)\n# Set the corresponding API key in .env: GROQ_API_KEY, OPENAI_API_KEY, or MISTRAL_API_KEY.\nstt:\n enabled: true\n # provider: \"local\" # auto-detected if omitted\n local:\n model: \"base\" # tiny | base | small | medium | large-v3 | turbo\n # language: \"\" # auto-detect; set to \"en\", \"es\", \"fr\", etc. to force\n openai:\n model: \"whisper-1\" # whisper-1 | gpt-4o-mini-transcribe | gpt-4o-transcribe\n # mistral:\n # model: \"voxtral-mini-latest\" # voxtral-mini-latest | voxtral-mini-2602\n # deepinfra:\n # # Model id is discovered live from the DeepInfra catalog filtered\n # # by the `stt` surface tag — leave `model` blank to take the first\n # # live result. Pin only when you need a specific Whisper variant.\n # model: \"\"\n\n# Text-to-speech. Only the deepinfra block is documented here — the\n# remaining providers (edge, openai, xai, minimax, mistral, gemini,\n# elevenlabs, neutts, kittentts, piper) inherit sensible defaults from\n# DEFAULT_CONFIG in hermes_cli/config.py.\n# tts:\n# provider: \"deepinfra\"\n# deepinfra:\n# # Model id is discovered live from the DeepInfra catalog filtered\n# # by the `tts` surface tag — leave `model` blank to take the first\n# # live result.\n# model: \"\"\n# voice: \"default\"\n\n# Image generation. Each provider plugin reads its own ``image_gen.``\n# block; deepinfra discovers models live from\n# api.deepinfra.com/v1/openai/models filtered by the ``image-gen`` tag —\n# no model id is hardcoded, so retired models disappear automatically.\n# image_gen:\n# provider: \"deepinfra\"\n# deepinfra:\n# # Leave `model` blank for the first live `image-gen`-tagged result.\n# model: \"\"\n\n# =============================================================================\n# Response Pacing (Messaging Platforms)\n# =============================================================================\n# Add human-like delays between message chunks.\n# human_delay:\n# mode: \"off\" # \"off\" | \"natural\" | \"custom\"\n# min_ms: 800 # Min delay (custom mode only)\n# max_ms: 2500 # Max delay (custom mode only)\n\n# =============================================================================\n# Session Logging\n# =============================================================================\n# Session trajectories are automatically saved to logs/ directory.\n# Each session creates: logs/session_YYYYMMDD_HHMMSS_UUID.json\n#\n# The session ID is displayed in the welcome banner for easy reference.\n# Logs contain full conversation history in trajectory format:\n# - System prompt, user messages, assistant responses\n# - Tool calls with inputs/outputs\n# - Timestamps for debugging\n#\n# No configuration needed - logging is always enabled.\n# To disable, you would need to modify the source code.\n\n# =============================================================================\n# Code Execution Sandbox (Programmatic Tool Calling)\n# =============================================================================\n# The execute_code tool runs Python scripts that call Hermes tools via RPC.\n# Intermediate tool results stay out of the LLM's context window.\ncode_execution:\n timeout: 300 # Max seconds per script before kill (default: 300 = 5 min)\n max_tool_calls: 50 # Max RPC tool calls per execution (default: 50)\n\n# =============================================================================\n# Subagent Delegation\n# =============================================================================\n# The delegate_task tool spawns child agents with isolated context.\n# Supports single tasks and batch mode (default 3 parallel, configurable).\ndelegation:\n max_iterations: 50 # Max tool-calling turns per child (default: 50)\n # max_concurrent_children: 3 # Max parallel child agents per batch (default: 3, floor: 1, no ceiling).\n # WARNING: values above 10 multiply API cost linearly.\n # max_spawn_depth: 1 # Delegation tree depth cap (range: 1-3, default: 1 = flat).\n # Raise to 2 to allow workers to spawn their own subagents.\n # Requires role=\"orchestrator\" on intermediate agents.\n # orchestrator_enabled: true # Kill switch for role=\"orchestrator\" children (default: true).\n # subagent_auto_approve: false # When a subagent hits a dangerous-command approval prompt, auto-deny (default: false)\n # or auto-approve \"once\" (true) instead of blocking on stdin.\n # The parent TUI owns stdin, so blocking would deadlock; non-interactive resolution is required.\n # Both choices emit a logger.warning audit line. Flip to true only for cron/batch pipelines.\n # inherit_mcp_toolsets: true # When explicit child toolsets are narrowed, also keep the parent's MCP toolsets (default: true). Set false for strict intersection.\n # model: \"google/gemini-3-flash-preview\" # Override model for subagents (empty = inherit parent)\n # provider: \"openrouter\" # Override provider for subagents (empty = inherit parent)\n # # Resolves full credentials (base_url, api_key) automatically.\n # # Supported: openrouter, nous, zai, kimi-coding, minimax\n\n# =============================================================================\n# Honcho Integration (Cross-Session User Modeling)\n# =============================================================================\n# AI-native persistent memory via Honcho (https://honcho.dev/).\n# Builds a deeper understanding of the user across sessions and tools.\n# Runs alongside USER.md — additive, not a replacement.\n#\n# Requires: pip install honcho-ai\n# Config: ~/.honcho/config.json (shared with Claude Code, Cursor, etc.)\n# API key: HONCHO_API_KEY in ~/.hermes/.env or ~/.honcho/config.json\n#\n# Hermes-specific overrides (optional — most config comes from ~/.honcho/config.json):\n# honcho: {}\n\n# =============================================================================\n# Display\n# =============================================================================\ndisplay:\n # Use compact banner mode (hides the ASCII-art banner, shows a single line).\n # true: Compact single-line banner\n # false: Full ASCII banner with tool/skill summary (default)\n compact: false\n\n # Tool progress display level (CLI and gateway)\n # off: Silent — no tool activity shown, just the final response\n # new: Show a tool indicator only when the tool changes (skip repeats)\n # all: Show every tool call with a short preview (default)\n # verbose: Full args, results, and debug logs (same as /verbose)\n # log: Silent in chat; append every tool call to ~/.hermes/logs/tool_calls.log (gateway only)\n # Toggle at runtime with /verbose in the CLI\n tool_progress: all\n\n # Per-platform defaults can be quieter than the global setting. Telegram\n # tunes for mobile: tool_progress and busy_ack_detail default off (no\n # per-tool breadcrumb stream, no \"iteration 21/60\" debug detail in busy\n # acks or heartbeats), but interim_assistant_messages and\n # long_running_notifications STAY ON so the user has real signal between\n # turn start and final answer (mid-turn assistant commentary + a single\n # edit-in-place \"⏳ Working — N min\" heartbeat). Override under\n # display.platforms.telegram.\n\n # Auto-cleanup of temporary progress bubbles after the final response lands.\n # On platforms that support message deletion (currently Telegram), this\n # removes the tool-progress bubble, \"⏳ Still working...\" notices, and\n # context-pressure status messages once the final reply has been delivered —\n # keeping long-running turns visible live, then tidy afterward. Failed runs\n # leave the bubbles in place as breadcrumbs. Off by default.\n # Per-platform override: display.platforms.telegram.cleanup_progress\n # true: Delete tracked progress/status bubbles on successful turn\n # false: Leave everything in place (default)\n # Example:\n # display:\n # platforms:\n # telegram:\n # cleanup_progress: true\n cleanup_progress: false\n\n # Gateway-only natural mid-turn assistant updates.\n # When true, completed assistant status messages are sent as separate chat\n # messages. This is independent of tool_progress and gateway streaming.\n # true: Send mid-turn assistant updates as separate messages (default)\n # false: Only send the final response\n interim_assistant_messages: true\n\n # Gateway-only long-running status heartbeats.\n # When false, the platform does not receive periodic \"⏳ Working — N min\"\n # notifications even if agent.gateway_notify_interval is non-zero. The\n # heartbeat edits a single message in place (where the adapter supports\n # editing) instead of posting a new bubble each interval.\n # Default: true everywhere, including Telegram (silent agents are worse\n # than a single edit-in-place heartbeat).\n long_running_notifications: true\n\n # Include detailed iteration/tool/status context in busy acknowledgments\n # and long-running heartbeats. When true, busy acks show \"iteration 21/60,\n # terminal, 10 min\" and the heartbeat shows \"⏳ Working — 12 min,\n # iteration 21/60, terminal\". When false (Telegram default), both stay\n # terse: \"Interrupting current task\" and \"⏳ Working — 12 min, terminal\".\n busy_ack_detail: true\n\n # What Enter does when Hermes is already busy (CLI and gateway platforms).\n # interrupt: Interrupt the current run and redirect Hermes (default)\n # queue: Queue your message for the next turn\n # steer: Inject your message mid-run via /steer, arriving at the agent\n # after the next tool call — no interrupt, no role violation.\n # Falls back to 'queue' if the agent isn't running yet or if\n # images are attached (steer only carries text).\n # Ctrl+C (or /stop in gateway) always interrupts regardless of this setting.\n # Toggle at runtime with /busy .\n busy_input_mode: interrupt\n\n # Background process notifications (gateway/messaging only).\n # Controls how chatty the process watcher is when you use\n # terminal(background=true, notify_on_complete=true) from Telegram/Discord/etc.\n # off: No watcher messages at all\n # result: Only the final completion message\n # error: Only the final message when exit code != 0\n # all: Running output updates + final message (default)\n background_process_notifications: all\n\n\n # Play terminal bell when agent finishes a response.\n # Useful for long-running tasks — your terminal will ding when the agent is done.\n # Works over SSH. Most terminals can be configured to flash the taskbar or play a sound.\n # true: Ring the terminal bell on each response\n # false: Silent (default)\n bell_on_complete: false\n\n # Show model reasoning/thinking before each response.\n # When enabled, a dim box shows the model's thought process above the response.\n # Toggle at runtime with /reasoning show or /reasoning hide.\n # true: Show the reasoning box\n # false: Hide reasoning (default)\n show_reasoning: false\n\n # Stream tokens to the terminal as they arrive instead of waiting for the\n # full response. The response box opens on first token and text appears\n # line-by-line. Tool calls are still captured silently.\n # true: Stream tokens as they arrive (default)\n # false: Wait for the full response before rendering\n streaming: true\n\n # Show [HH:MM] timestamps on user input and assistant response labels.\n # timestamps: false\n\n # ───────────────────────────────────────────────────────────────────────────\n # Skin / Theme\n # ───────────────────────────────────────────────────────────────────────────\n # Customize CLI visual appearance — banner colors, spinner faces, tool prefix,\n # response box label, and branding text. Change at runtime with /skin .\n #\n # Built-in skins:\n # default — Classic Hermes gold/kawaii\n # ares — Crimson/bronze war-god theme with spinner wings\n # mono — Clean grayscale monochrome\n # slate — Cool blue developer-focused\n # daylight — Bright light-mode theme\n # warm-lightmode — Warm paper-tone light-mode theme\n # poseidon — Sea-green/teal Olympian theme\n # sisyphus — Earthy stone-and-moss theme\n # charizard — Fiery orange dragon theme\n #\n # Custom skins: drop a YAML file in ~/.hermes/skins/.yaml\n # Schema (all fields optional, missing values inherit from default):\n #\n # name: my-theme\n # description: Short description\n # colors:\n # banner_border: \"#HEX\" # Panel border\n # banner_title: \"#HEX\" # Panel title\n # banner_accent: \"#HEX\" # Section headers (Available Tools, etc.)\n # banner_dim: \"#HEX\" # Dim/muted text\n # banner_text: \"#HEX\" # Body text (tool names, skill names)\n # ui_accent: \"#HEX\" # UI accent color\n # response_border: \"#HEX\" # Response box border color\n # spinner:\n # waiting_faces: [\"(⚔)\", \"(⛨)\"] # Faces shown while waiting\n # thinking_faces: [\"(⚔)\", \"(⌁)\"] # Faces shown while thinking\n # thinking_verbs: [\"forging\", \"plotting\"] # Verbs for spinner messages\n # wings: # Optional left/right spinner decorations\n # - [\"⟪⚔\", \"⚔⟫\"]\n # - [\"⟪▲\", \"▲⟫\"]\n # branding:\n # agent_name: \"My Agent\" # Banner title and branding\n # welcome: \"Welcome message\" # Shown at CLI startup\n # response_label: \" ⚔ Agent \" # Response box header label\n # prompt_symbol: \"⚔\" # Prompt symbol (bare token; renderers add trailing space)\n # tool_prefix: \"╎\" # Tool output line prefix (default: ┊)\n #\n skin: default\n\n# =============================================================================\n# Model Aliases — short names for /model command\n# =============================================================================\n# Map short aliases to exact (model, provider, base_url) tuples.\n# Used by /model tab completion and resolve_alias().\n# Aliases are checked BEFORE the models.dev catalog, so they can route\n# to endpoints not in the catalog (e.g. Ollama Cloud, local servers).\n#\n# model_aliases:\n# opus:\n# model: claude-opus-4-6\n# provider: anthropic\n# qwen:\n# model: \"qwen3.5:397b\"\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n# glm:\n# model: glm-4.7\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n\n# =============================================================================\n# Privacy\n# =============================================================================\n# privacy:\n# # Redact PII from the LLM context prompt.\n# # When true, phone numbers are stripped and user/chat IDs are replaced\n# # with deterministic hashes before being sent to the model.\n# # Names and usernames are NOT affected (user-chosen, publicly visible).\n# # Routing/delivery still uses the original values internally.\n# redact_pii: false\n\n# =============================================================================\n# Shell-script hooks\n# =============================================================================\n# Register shell scripts as plugin-hook callbacks. Each entry is executed as\n# a subprocess (shell=False, shlex.split) with a JSON payload on stdin. On\n# stdout the script may return JSON that either blocks the tool call or\n# injects context into the next LLM call.\n#\n# Valid events (mirror hermes_cli.plugins.VALID_HOOKS):\n# pre_tool_call, post_tool_call, pre_llm_call, post_llm_call,\n# pre_api_request, post_api_request, on_session_start, on_session_end,\n# on_session_finalize, on_session_reset, subagent_stop\n#\n# First-use consent: each (event, command) pair prompts once on a TTY, then\n# is persisted to ~/.hermes/shell-hooks-allowlist.json. Non-interactive\n# runs (gateway, cron) need --accept-hooks, HERMES_ACCEPT_HOOKS=1, or the\n# hooks_auto_accept key below.\n#\n# See website/docs/user-guide/features/hooks.md for the full JSON wire\n# protocol and worked examples.\n#\n# hooks:\n# pre_tool_call:\n# - matcher: \"terminal\"\n# command: \"~/.hermes/agent-hooks/block-rm-rf.sh\"\n# timeout: 10\n# post_tool_call:\n# - matcher: \"write_file|patch\"\n# command: \"~/.hermes/agent-hooks/auto-format.sh\"\n# pre_llm_call:\n# - command: \"~/.hermes/agent-hooks/inject-cwd-context.sh\"\n# subagent_stop:\n# - command: \"~/.hermes/agent-hooks/log-orchestration.sh\"\n#\n# hooks_auto_accept: false\n\n\n# =============================================================================\n# Update Behavior\n# =============================================================================\nupdates:\n # Create a full HERMES_HOME zip before every `hermes update`.\n # Backups land in ~/.hermes/backups/ and can be restored with `hermes import`.\n # Off by default because large homes can add minutes to every update.\n pre_update_backup: false\n\n # Number of pre-update backup zips to retain.\n backup_keep: 5\n\n # What non-interactive updates do with local source edits in the Hermes repo.\n # Interactive terminal updates always prompt before restoring the autostash.\n #\n # stash - auto-stash before pull, then auto-restore after success (default)\n # discard - drop the update-created stash after success; use only on managed\n # installs where local source edits should not persist\n non_interactive_local_changes: \"stash\"\n\n\n# =============================================================================\n# Web Dashboard\n# =============================================================================\n# OAuth gate configuration for `hermes dashboard --host `.\n# The bundled Nous Portal plugin reads these on startup; settings here are\n# the canonical surface. Each can be overridden by an environment variable:\n#\n# dashboard.oauth.client_id <- HERMES_DASHBOARD_OAUTH_CLIENT_ID\n# dashboard.oauth.portal_url <- HERMES_DASHBOARD_PORTAL_URL\n# dashboard.public_url <- HERMES_DASHBOARD_PUBLIC_URL\n#\n# Env wins when set to a non-empty value. This is what Fly.io's platform-\n# secret injection uses to push per-deploy client_ids without needing to\n# bake a config.yaml into the image. Empty env values are treated as unset\n# so a provisioned-but-not-populated secret can't shadow a valid entry here.\n#\n# Local dev / on-prem deploys should typically set these via config.yaml\n# (the ~/.hermes/.env file is reserved for API keys and secrets).\n#\n# dashboard:\n# oauth:\n# client_id: \"\" # agent:{instance_id}; Portal provisions this at deploy\n# portal_url: \"\" # blank → default https://portal.nousresearch.com\n#\n# # Force the absolute base URL the OAuth callback (and any other public\n# # URL the dashboard hands to external systems) is built from. Set this\n# # for deploys behind reverse proxies that don't reliably forward\n# # X-Forwarded-Host / X-Forwarded-Proto / X-Forwarded-Prefix (manual\n# # nginx setups, on-prem ingresses, custom-domain Fly deploys without\n# # full proxy header chains).\n# #\n# # When set, the value is the complete authority: scheme + host +\n# # optional path prefix (e.g. \"https://example.com/hermes\"). The OAuth\n# # callback URL becomes \"/auth/callback\" — X-Forwarded-Prefix\n# # is IGNORED on this code path because the operator has explicitly\n# # declared the public URL and we no longer need to guess.\n# #\n# # Leave empty to use the existing proxy-header reconstruction (the\n# # default — works on Fly.io out of the box).\n# #\n# # public_url: \"https://example.com/hermes\"\n#\n# -----------------------------------------------------------------------------\n# Self-hosted OIDC dashboard auth (generic OpenID Connect — Authentik,\n# Keycloak, Zitadel, Authelia, Auth0, Okta, Google, …). Use this INSTEAD of the\n# nous block above when gating the dashboard with your own identity provider.\n# Each setting can be overridden by an environment variable:\n#\n# dashboard.oauth.self_hosted.issuer <- HERMES_DASHBOARD_OIDC_ISSUER\n# dashboard.oauth.self_hosted.client_id <- HERMES_DASHBOARD_OIDC_CLIENT_ID\n# dashboard.oauth.self_hosted.scopes <- HERMES_DASHBOARD_OIDC_SCOPES\n# dashboard.oauth.self_hosted.client_secret <- HERMES_DASHBOARD_OIDC_CLIENT_SECRET\n#\n# dashboard:\n# oauth:\n# provider: self-hosted\n# self_hosted:\n# issuer: \"https://auth.example.com/application/o/hermes/\" # required\n# client_id: \"hermes-dashboard\" # required\n# scopes: \"openid profile email\" # optional\n#\n# # OPTIONAL — set ONLY if your IDP registered the client as\n# # *confidential* (Authentik / Keycloak often default to this). When\n# # set, Hermes authenticates the client at the token endpoint\n# # (client_secret_basic or client_secret_post, auto-selected from the\n# # IDP's discovery doc) IN ADDITION to PKCE. Leave unset for a public\n# # (PKCE-only) client — the common case.\n# #\n# # This is a CREDENTIAL: prefer setting HERMES_DASHBOARD_OIDC_CLIENT_SECRET\n# # in ~/.hermes/.env over putting it here in config.yaml.\n# # client_secret: \"\"\n\n# =============================================================================\n# External secret sources\n# =============================================================================\n# Pull provider credentials from external secret managers at process startup\n# instead of storing them in ~/.hermes/.env. Only the manager's bootstrap\n# credential (e.g. BWS_ACCESS_TOKEN / OP_SERVICE_ACCOUNT_TOKEN) lives in .env\n# (or your shell / desktop session); everything else rotates centrally.\n# Failures never block startup — Hermes warns once and continues with\n# whatever .env already had.\n#\n# Multiple sources can be enabled at once:\n# - \"mapped\" sources (explicit VAR -> ref bindings, e.g. 1Password's env:\n# map) beat \"bulk\" sources (whole-project dumps like Bitwarden BSM)\n# - within a shape, the first source to claim a var wins; later claims\n# are skipped with a startup warning (never a silent clobber)\n# - a source's override_existing lets it beat .env/shell values, but\n# never another secret source's claim\n# Docs: https://hermes-agent.nousresearch.com/docs/user-guide/secrets/\n#\n# secrets:\n# # Optional explicit ordering of enabled sources.\n# # sources: [onepassword, bitwarden]\n#\n# # ---- Bitwarden Secrets Manager (bws CLI) --------------------------------\n# bitwarden:\n# enabled: false\n# access_token_env: BWS_ACCESS_TOKEN # bootstrap token, sourced from .env\n# project_id: \"\" # UUID of the BSM project to sync\n# server_url: \"\" # \"\" = US Cloud; EU/self-hosted URL otherwise\n# cache_ttl_seconds: 300 # 0 disables caching\n# override_existing: true # BSM values win over existing env\n# auto_install: true # lazy-download bws into ~/.hermes/bin\n#\n# # ---- 1Password (op CLI) -------------------------------------------------\n# onepassword:\n# enabled: false\n# # Map env-var names to op:// secret references. Each is resolved with a\n# # single `op read` at startup.\n# env:\n# OPENAI_API_KEY: \"op://Private/OpenAI/api key\"\n# ANTHROPIC_API_KEY: \"op://Private/Anthropic/credential\"\n# account: \"\" # op --account shorthand; \"\" = default\n# service_account_token_env: OP_SERVICE_ACCOUNT_TOKEN # headless auth; unset = desktop session\n# binary_path: \"\" # \"\" = resolve op via PATH; else absolute path\n# cache_ttl_seconds: 300 # 0 disables BOTH cache layers\n# override_existing: true # resolved values win over existing env\n"}, "df5700ebe317ff9f2d9ea4677513e012eb68b6f4": {"t": 1785767864.0061917, "n": 76794, "h": "16bce245a755c898", "c": "# Hermes Agent CLI Configuration\n# Copy this file to cli-config.yaml and customize as needed.\n# This file configures the CLI behavior. Environment variables in .env take precedence.\n\n# =============================================================================\n# Model Configuration\n# =============================================================================\nmodel:\n # Default model to use (can be overridden with --model flag)\n # Both \"default\" and \"model\" work as the key name here.\n default: \"anthropic/claude-opus-4.6\"\n \n # Inference provider selection:\n # \"auto\" - Auto-detect from credentials (default)\n # \"openrouter\" - OpenRouter (requires: OPENROUTER_API_KEY or OPENAI_API_KEY)\n # \"nous\" - Nous Portal OAuth (requires: hermes auth add nous)\n # \"nous-api\" - Nous Portal API key (requires: NOUS_API_KEY)\n # \"anthropic\" - Direct Anthropic API (requires: ANTHROPIC_API_KEY)\n # \"openai-codex\" - OpenAI Codex (requires: hermes auth)\n # \"copilot\" - GitHub Copilot / GitHub Models (requires: GITHUB_TOKEN)\n # \"gemini\" - Use Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n # \"zai\" - Use z.ai / ZhipuAI GLM models (requires: GLM_API_KEY)\n # \"kimi-coding\" - Kimi / Moonshot AI (requires: KIMI_API_KEY)\n # \"minimax\" - MiniMax global (requires: MINIMAX_API_KEY)\n # \"minimax-cn\" - MiniMax China (requires: MINIMAX_CN_API_KEY)\n # \"huggingface\" - Hugging Face Inference (requires: HF_TOKEN)\n # \"nvidia\" - NVIDIA NIM / build.nvidia.com (requires: NVIDIA_API_KEY)\n # \"xiaomi\" - Xiaomi MiMo (requires: XIAOMI_API_KEY)\n # \"arcee\" - Arcee AI Trinity models (requires: ARCEEAI_API_KEY)\n # \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY — https://ollama.com/settings)\n # \"deepinfra\" - DeepInfra (requires: DEEPINFRA_API_KEY)\n # \"kilocode\" - KiloCode gateway (requires: KILOCODE_API_KEY)\n # \"azure-foundry\" - Microsoft Foundry / Azure OpenAI (API key or Entra ID)\n # \"lmstudio\" - LM Studio local server (optional: LM_API_KEY, defaults to http://127.0.0.1:1234/v1)\n #\n # Local servers (LM Studio, Ollama, vLLM, llama.cpp):\n # \"custom\" - Any other OpenAI-compatible endpoint. Set base_url below.\n # Aliases: \"ollama\", \"vllm\", \"llamacpp\" all map to \"custom\".\n # LM Studio is first-class and uses provider: \"lmstudio\".\n # It works with both no-auth and auth-enabled server modes.\n #\n # Can also be overridden for a single invocation with the --provider flag.\n provider: \"auto\"\n \n # API configuration (falls back to OPENROUTER_API_KEY env var)\n # api_key: \"your-key-here\" # Uncomment to set here instead of .env\n base_url: \"https://openrouter.ai/api/v1\"\n\n # Azure Foundry keyless auth example:\n # provider: \"azure-foundry\"\n # base_url: \"https://.openai.azure.com/openai/v1\"\n # auth_mode: \"entra_id\" # DefaultAzureCredential: az login, managed identity, workload identity, etc.\n # default: \"gpt-4o\" # Deployment/model name\n # entra:\n # scope: \"https://ai.azure.com/.default\" # Optional; this is the default.\n\n # ── Token limits — two settings, easy to confuse ──────────────────────────\n #\n # context_length: TOTAL context window (input + output tokens combined).\n # Controls when Hermes compresses history and validates requests.\n # Leave unset — Hermes auto-detects the correct value from the provider.\n # Set manually only when auto-detection is wrong (e.g. a local server with\n # a custom num_ctx, or a proxy that doesn't expose /v1/models).\n #\n # context_length: 131072\n #\n # max_tokens: OUTPUT cap — maximum tokens the model may generate per response.\n # Unrelated to how long your conversation history can be.\n # The OpenAI-standard name \"max_tokens\" is a misnomer; Anthropic's native\n # API has since renamed it \"max_output_tokens\" for clarity.\n # Leave unset to use the model's native output ceiling (recommended).\n # Set only if you want to deliberately limit individual response length.\n #\n# max_tokens: 8192\n\n # ── Custom request headers (optional) ─────────────────────────────────────\n #\n # default_headers: extra HTTP headers sent on every request to an\n # OpenAI-compatible endpoint. User values take precedence over the\n # provider/SDK defaults, so this is the supported way to override the\n # OpenAI Python SDK's identifying headers (User-Agent: OpenAI/Python ...,\n # X-Stainless-*) when a custom provider sits behind a gateway/WAF that\n # rejects them — e.g. an upstream that returns \"502 Upstream access\n # forbidden\" for the SDK default User-Agent but accepts a plain one.\n # Applies on the OpenAI wire only (not native Anthropic / Bedrock).\n #\n # default_headers:\n # User-Agent: \"curl/8.7.1\"\n #\n # extra_headers: accepted as an alias of default_headers (merged, with\n # extra_headers winning when both are set) — matches the per-provider\n # extra_headers key below.\n #\n # Per-provider variant: named providers / custom_providers entries accept an\n # extra_headers dict scoped to that endpoint only — for reverse proxies,\n # gateways, or custom auth (e.g. Cloudflare Access service tokens).\n # Merged onto SDK/provider defaults with the entry's values winning.\n # Header values are treated as secrets and are never logged.\n #\n # providers:\n # my-proxy:\n # base_url: \"https://llm.internal.example.com/v1\"\n # key_env: \"MY_PROXY_API_KEY\"\n # extra_headers:\n # CF-Access-Client-Id: \"xxxx.access\"\n # CF-Access-Client-Secret: \"${CF_ACCESS_SECRET}\"\n # X-Client-Name: \"hermes-agent\"\n\n# Named provider overrides (optional)\n# Use this for per-provider request timeouts, non-stream stale timeouts,\n# and per-model exceptions.\n# Applies to the primary turn client on every api_mode (OpenAI-wire, native\n# Anthropic, and Anthropic-compatible providers), the fallback chain, and\n# client rebuilds during credential rotation. For OpenAI-wire chat\n# completions (streaming and non-streaming) the configured value is also\n# used as the per-request ``timeout=`` kwarg so it wins over the legacy\n# HERMES_API_TIMEOUT env var (which still applies when no config is set).\n# ``stale_timeout_seconds`` controls the non-streaming stale-call detector and\n# wins over the legacy HERMES_API_CALL_STALE_TIMEOUT env var. Leaving these\n# unset keeps the legacy defaults (HERMES_API_TIMEOUT=1800s,\n# HERMES_API_CALL_STALE_TIMEOUT=90s, native Anthropic 900s). The\n# implicit non-stream stale detector is auto-disabled for local endpoints\n# and can scale upward for very large contexts.\n#\n# Not currently wired for AWS Bedrock (bedrock_converse + AnthropicBedrock\n# SDK paths) — those use boto3 with its own timeout configuration.\n#\n# providers:\n# ollama-local:\n# request_timeout_seconds: 300 # Longer timeout for local cold-starts\n# stale_timeout_seconds: 900 # Explicitly re-enable stale detection on local endpoints\n# anthropic:\n# request_timeout_seconds: 30 # Fast-fail cloud requests\n# models:\n# claude-opus-4.6:\n# timeout_seconds: 600 # Longer timeout for extended-thinking Opus calls\n# openai-codex:\n# models:\n# gpt-5.4:\n# stale_timeout_seconds: 1800 # Longer non-stream stale timeout for slow large-context turns\n\n# =============================================================================\n# OpenRouter Provider Routing (only applies when using OpenRouter)\n# =============================================================================\n# Control how requests are routed across providers on OpenRouter.\n# See: https://openrouter.ai/docs/guides/routing/provider-selection\n#\n# provider_routing:\n# # Sort strategy: \"price\" (default), \"throughput\", or \"latency\"\n# # Append :nitro to model name for a shortcut to throughput sorting.\n# sort: \"throughput\"\n#\n# # Only allow these providers (provider slugs from OpenRouter)\n# # only: [\"anthropic\", \"google\"]\n#\n# # Skip these providers entirely\n# # ignore: [\"deepinfra\", \"fireworks\"]\n#\n# # Try providers in this order (overrides default load balancing)\n# # order: [\"anthropic\", \"google\", \"together\"]\n#\n# # Require providers to support all parameters in your request\n# # require_parameters: true\n#\n# # Data policy: \"allow\" (default) or \"deny\" to exclude providers that may store data\n# # data_collection: \"deny\"\n\n# =============================================================================\n# OpenRouter Response Caching (only applies when using OpenRouter)\n# =============================================================================\n# Cache identical API responses at the OpenRouter edge for free instant replays.\n# When enabled, identical requests (same model, messages, parameters) return\n# cached responses with zero billing. Separate from Anthropic prompt caching.\n# See: https://openrouter.ai/docs/guides/features/response-caching\n#\n# openrouter:\n# response_cache: true # Enable response caching (default: true)\n# response_cache_ttl: 300 # Cache TTL in seconds, 1-86400 (default: 300)\n\n# =============================================================================\n# Git Worktree Isolation\n# =============================================================================\n# When enabled, each CLI session creates an isolated git worktree so multiple\n# agents can work on the same repo concurrently without file collisions.\n# Equivalent to always passing --worktree / -w on the command line.\n#\n# worktree: true # Always create a worktree when in a git repo\n# worktree: false # Default — only create when -w flag is passed\n#\n# By default a new worktree branches from the freshly-fetched remote tip\n# (the current branch's upstream, else the remote's default branch) so it\n# starts current with the project instead of from the local clone's\n# (possibly stale) HEAD. Set worktree_sync: false to branch from local HEAD\n# instead — useful when offline or when you deliberately want the clone's\n# exact current state as the base.\n#\n# worktree_sync: true # Default — branch from the fetched remote tip\n# worktree_sync: false # Branch from local HEAD (offline / pinned base)\n\n# =============================================================================\n# Terminal Tool Configuration\n# =============================================================================\n# Choose ONE of the following terminal configurations by uncommenting it.\n# The terminal tool executes commands in the specified environment.\n\n# -----------------------------------------------------------------------------\n# OPTION 1: Local execution (default)\n# Commands run directly on your machine in the current directory\n# -----------------------------------------------------------------------------\n# Working directory behavior:\n# - CLI (`hermes` command): Uses \".\" (current directory where you run hermes)\n# - Gateway/messaging/cron: Uses terminal.cwd here; legacy .env cwd values are deprecated\nterminal:\n backend: \"local\"\n cwd: \".\" # For local backend: \".\" = current directory. Ignored for remote backends unless a backend documents otherwise.\n timeout: 180\n # HOME policy for tool subprocesses:\n # auto - default: host uses your real HOME; containers use HERMES_HOME/home\n # real - force your real OS-user HOME\n # profile - force HERMES_HOME/home for strict per-profile CLI config isolation\n home_mode: \"auto\"\n docker_mount_cwd_to_workspace: false # SECURITY: off by default. Opt in to mount the launch cwd into Docker /workspace.\n lifetime_seconds: 300\n # sudo_password: \"hunter2\" # Optional: pipe a sudo password via sudo -S. SECURITY WARNING: plaintext.\n # sudo_password: \"\" # Explicit empty password: try empty and never open the interactive sudo prompt.\n\n# -----------------------------------------------------------------------------\n# OPTION 2: SSH remote execution\n# Commands run on a remote server - agent code stays local (sandboxed)\n# Great for: keeping agent isolated from its own code, using powerful remote hardware\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"ssh\"\n# cwd: \"/home/myuser/project\" # Path on the REMOTE server\n# timeout: 180\n# lifetime_seconds: 300\n# ssh_host: \"my-server.example.com\"\n# ssh_user: \"myuser\"\n# ssh_port: 22\n# ssh_key: \"~/.ssh/id_rsa\" # Optional - uses ssh-agent if not specified\n\n# -----------------------------------------------------------------------------\n# OPTION 3: Docker container\n# Commands run in an isolated Docker container\n# Great for: reproducible environments, testing, isolation\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"docker\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /)\n# timeout: 180\n# lifetime_seconds: 300\n# docker_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# docker_mount_cwd_to_workspace: true # Explicit opt-in: mount your launch cwd into /workspace\n# # Optional: run the container as your host user's uid:gid so files written\n# # into bind-mounted dirs are owned by you, not root. Drops SETUID/SETGID\n# # caps too since no gosu privilege drop is needed. Leave off if your\n# # chosen docker_image expects to start as root.\n# docker_run_as_host_user: true\n# # Optional: explicitly forward selected env vars into Docker.\n# # These values come from your current shell first, then ~/.hermes/.env.\n# # Warning: anything forwarded here is visible to commands run in the container.\n# docker_forward_env:\n# - \"GITHUB_TOKEN\"\n# - \"NPM_TOKEN\"\n# # Optional: extra flags passed verbatim to docker run (appended after security defaults).\n# # Useful for adding capabilities (e.g. apt installs needing SETUID) or custom options.\n# # Example: add a Linux capability not included by default\n# # docker_extra_args:\n# # - \"--cap-add\"\n# # - \"SETUID\"\n\n# -----------------------------------------------------------------------------\n# OPTION 4: Singularity/Apptainer container\n# Commands run in a Singularity container (common in HPC environments)\n# Great for: HPC clusters, shared compute environments\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"singularity\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# singularity_image: \"docker://nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 5: Modal cloud execution\n# Commands run on Modal's cloud infrastructure\n# Great for: GPU access, scalable compute, serverless execution\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"modal\"\n# cwd: \"/workspace\" # Path INSIDE the sandbox (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# modal_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 6: Daytona cloud execution\n# Commands run in Daytona cloud sandboxes\n# Great for: Cloud dev environments, persistent workspaces, team collaboration\n# Requires: pip install daytona, DAYTONA_API_KEY env var\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"daytona\"\n# cwd: \"~\"\n# timeout: 180\n# lifetime_seconds: 300\n# daytona_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# container_disk: 10240 # Daytona max is 10GB per sandbox\n\n#\n# --- Container resource limits (docker, singularity, modal, daytona -- ignored for local/ssh) ---\n# These settings apply to all container backends. They control the resources\n# allocated to the sandbox and whether its filesystem persists across sessions.\n container_cpu: 1 # CPU cores\n container_memory: 5120 # Memory in MB (5120 = 5GB)\n container_disk: 51200 # Disk in MB (51200 = 50GB)\n container_persistent: true # Persist filesystem across sessions (false = ephemeral)\n\n# -----------------------------------------------------------------------------\n# SUDO SUPPORT (works with ALL backends above)\n# -----------------------------------------------------------------------------\n# Add sudo_password to any terminal config above to enable sudo commands.\n# The password is piped via `sudo -S`. Works with local, ssh, docker, etc.\n#\n# SECURITY WARNING: Password stored in plaintext!\n#\n# INTERACTIVE PROMPT: If sudo_password is unset and the CLI is running,\n# you'll be prompted to enter your password when sudo is needed:\n# - 45-second timeout (auto-skips if no input)\n# - Press Enter to skip (command fails gracefully)\n# - Password is hidden while typing\n# - Password is cached for the session\n#\n# EMPTY PASSWORDS: Setting sudo_password to an explicit empty string is different\n# from leaving it unset. Hermes will try an empty password via `sudo -S` and\n# will not open the interactive prompt. This is useful for passwordless sudo,\n# Touch ID sudo setups, and environments where prompting is just noise.\n#\n# ALTERNATIVES:\n# - SSH backend: Configure passwordless sudo on the remote server\n# - Containers: Run as root inside the container (no sudo needed)\n# - Local: Configure /etc/sudoers for specific commands\n#\n# Example (add to your terminal section):\n# sudo_password: \"your-password-here\"\n\n# =============================================================================\n# Security Scanning (tirith)\n# =============================================================================\n# Optional pre-exec command security scanning via tirith.\n# Detects homograph URLs, pipe-to-shell, terminal injection, env manipulation.\n# Install: brew install sheeki03/tap/tirith\n# Docs: https://github.com/sheeki03/tirith\n#\n# security:\n# tirith_enabled: true # Enable/disable tirith scanning\n# tirith_path: \"tirith\" # Path to tirith binary (supports ~ expansion)\n# tirith_timeout: 5 # Scan timeout in seconds\n# tirith_fail_open: true # Allow commands if tirith unavailable\n\n# =============================================================================\n# Browser Tool Configuration\n# =============================================================================\nbrowser:\n # Inactivity timeout in seconds - browser sessions are automatically closed\n # after this period of no activity between agent loops (default: 120 = 2 minutes)\n inactivity_timeout: 120\n\n# =============================================================================\n# Tool Loop Guardrails\n# =============================================================================\n# Soft warnings are enabled by default. They append guidance to repeated failed\n# or non-progressing tool results but still let the tool execute. Hard stops are\n# opt-in circuit breakers for autonomous/cron sessions where stopping a loop is\n# preferable to spending the full iteration budget.\ntool_loop_guardrails:\n warnings_enabled: true\n hard_stop_enabled: false\n warn_after:\n exact_failure: 2\n same_tool_failure: 3\n idempotent_no_progress: 2\n hard_stop_after:\n exact_failure: 5\n same_tool_failure: 8\n idempotent_no_progress: 5\n\n# =============================================================================\n# Context Compression (Auto-shrinks long conversations)\n# =============================================================================\n# When conversation approaches model's context limit, middle turns are\n# automatically summarized to free up space while preserving important context.\n#\n# HOW IT WORKS:\n# 1. Tracks actual token usage from API responses (not estimates)\n# 2. When prompt_tokens >= threshold% of model's context_length, triggers compression\n# 3. Protects first 3 turns (system prompt, initial request, first response)\n# 4. Protects last N turns (default 20 messages = ~10 full turns of recent context)\n# 5. Summarizes middle turns using a fast/cheap model\n# 6. Inserts summary as a user message, continues conversation seamlessly\n#\n# Post-compression tail budget is target_ratio × threshold × context_length:\n# 200K context, threshold 0.50, ratio 0.20 → 20K tokens of recent tail preserved\n# 1M context, threshold 0.50, ratio 0.20 → 100K tokens of recent tail preserved\n#\ncompression:\n # Enable automatic context compression (default: true)\n # Set to false if you prefer to manage context manually or want errors on overflow\n enabled: true\n \n # Trigger compression at this % of model's context limit (default: 0.50 = 50%)\n # Lower values = more aggressive compression, higher values = compress later\n # Models with context windows below 512K are floored at 0.75 (raise-only) so\n # compaction doesn't fire with half the window still free; set above 0.75 to override.\n threshold: 0.50\n\n # Existing Codex gpt-5.5 behavior: raise Hermes' compaction trigger to 85%\n # for the ChatGPT Codex OAuth route. Set false to opt back down to threshold.\n codex_gpt55_autoraise: true\n\n # Fraction of the threshold to preserve as recent tail (default: 0.20 = 20%)\n # e.g. 20% of 50% threshold = 10% of total context kept as recent messages.\n # Summary output is separately capped at 12K tokens (Gemini output limit).\n # Range: 0.10 - 0.80\n target_ratio: 0.20\n\n # Number of most-recent messages to always preserve (default: 20 ≈ 10 full turns)\n # Higher values keep more recent conversation intact at the cost of more aggressive\n # compression of older turns.\n protect_last_n: 20\n\n # Codex app-server (codex CLI runtime) thread-compaction mode. The codex\n # agent owns the real thread context on this runtime, so Hermes' summarizer\n # cannot shrink it — compaction goes through the app server instead.\n # native = let Codex decide when to compact its own thread (default)\n # hermes = let Hermes threshold trigger Codex thread/compact/start\n # off = Hermes will not auto-trigger compaction; Codex may still compact natively\n codex_app_server_auto: native\n\n # Number of non-system messages to protect at the head of the transcript, in\n # ADDITION to the system prompt (which is always implicitly protected).\n # Head messages are NEVER summarized — they survive every compression\n # indefinitely. This gives stable early context for short/medium sessions,\n # but in long-running sessions that rely on rolling compaction the pinned\n # opening turns may not match how you want the session framed over time.\n # Set to 0 to preserve ONLY the system prompt (plus the rolling summary\n # and recent tail) — the cleanest configuration for long-running sessions.\n # Default 3 preserves the system prompt plus the first three non-system\n # head messages, matching the pre-feature behaviour.\n protect_first_n: 3\n\n # To pin a specific model/provider for compression summaries, use the\n # auxiliary section below (auxiliary.compression.provider / model).\n\n# =============================================================================\n# Anthropic prompt caching TTL\n# =============================================================================\n# When prompt caching is active (Claude via OpenRouter or native Anthropic),\n# Anthropic supports two TTL tiers for cached prefixes: \"5m\" (default) and\n# \"1h\". Other values are ignored and \"5m\" is used.\n#\nprompt_caching:\n cache_ttl: \"5m\" # use \"1h\" for long sessions with pauses between turns\n\n# =============================================================================\n# Auxiliary Models (Advanced — Experimental)\n# =============================================================================\n# Hermes uses lightweight \"auxiliary\" models for side tasks: image analysis,\n# browser screenshot analysis, web page summarization, TTS audio-tag insertion,\n# and context compression.\n#\n# By default these use Gemini Flash via OpenRouter or Nous Portal and are\n# auto-detected from your credentials. You do NOT need to change anything\n# here for normal usage.\n#\n# WARNING: Overriding these with providers other than OpenRouter or Nous Portal\n# is EXPERIMENTAL and may not work. Not all models/providers support vision,\n# produce usable summaries, or accept the same API format. Change at your own\n# risk — if things break, reset to \"auto\" / empty values.\n#\n# Each task has its own provider + model pair so you can mix providers.\n# For example: OpenRouter for vision (needs multimodal), but your main\n# local endpoint for compression (just needs text).\n#\n# Provider options:\n# \"auto\" - Best available: OpenRouter → Nous Portal → main endpoint (default)\n# \"openrouter\" - Force OpenRouter (requires OPENROUTER_API_KEY)\n# \"nous\" - Force Nous Portal (requires: hermes auth add nous)\n# \"gemini\" - Force Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n# \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY)\n# \"codex\" - Force Codex OAuth (requires: hermes model → Codex).\n# Uses gpt-5.3-codex which supports vision.\n# \"main\" - Use your custom endpoint (OPENAI_BASE_URL + OPENAI_API_KEY).\n# Works with OpenAI API, local models, or any OpenAI-compatible\n# endpoint. Also falls back to Codex OAuth and API-key providers.\n#\n# Model: leave empty to use the provider's default. When empty, OpenRouter\n# uses \"google/gemini-3-flash-preview\" and Nous uses \"gemini-3-flash\".\n# Other providers pick a sensible default automatically.\n#\n# auxiliary:\n# # Image analysis: vision_analyze tool + browser screenshots\n# vision:\n# provider: \"auto\"\n# model: \"\" # e.g. \"google/gemini-2.5-flash\", \"openai/gpt-4o\"\n# timeout: 30 # LLM API call timeout (seconds)\n# download_timeout: 30 # Image HTTP download timeout (seconds)\n# # Increase for slow connections or self-hosted image servers\n# reasoning_effort: \"\" # Per-task thinking level: none, minimal, low, medium,\n# # high, xhigh, max, ultra. Empty = provider default.\n# # Works on every auxiliary task block (vision,\n# # web_extract, compression, title_generation, curator,\n# # background_review, moa_reference, ...). Example: run\n# # compression at \"low\" and vision at \"none\" to cut\n# # side-task latency/cost on reasoning models.\n#\n# # Web page scraping / summarization + browser page text extraction\n# web_extract:\n# provider: \"auto\"\n# model: \"\"\n# reasoning_effort: \"low\"\n#\n# # Gemini 3.1 TTS hidden audio-tag insertion\n# tts_audio_tags:\n# provider: \"auto\" # empty model = your main chat model\n# model: \"\"\n# timeout: 30\n#\n# # Session search — summarizes matching past sessions\n# session_search:\n# provider: \"auto\"\n# model: \"\"\n# timeout: 30\n# max_concurrency: 3 # Limit parallel summaries to reduce request-burst 429s\n# extra_body: {} # Provider-specific OpenAI-compatible request fields\n# # Example for providers that support request-body\n# # reasoning controls:\n# # extra_body:\n# # enable_thinking: false\n# # Some vLLM/Qwen deployments expect this nested:\n# # extra_body:\n# # chat_template_kwargs:\n# # enable_thinking: false\n\n# =============================================================================\n# Persistent Memory\n# =============================================================================\n# Bounded curated memory injected into the system prompt every session.\n# Two stores: MEMORY.md (agent's notes) and USER.md (user profile).\n# Character limits keep the memory small and focused. The agent manages\n# pruning -- when at the limit, it must consolidate or replace entries.\n# Disabled by default in batch_runner.\n#\nmemory:\n # Agent's personal notes: environment facts, conventions, things learned\n memory_enabled: true\n \n # User profile: preferences, communication style, expectations\n user_profile_enabled: true\n \n # Character limits (~2.75 chars per token, model-independent)\n memory_char_limit: 2200 # ~800 tokens\n user_char_limit: 1375 # ~500 tokens\n\n # Periodic memory nudge: remind the agent to consider saving memories\n # every N user turns. Set to 0 to disable. Only active when memory is enabled.\n nudge_interval: 10 # Nudge every 10 user turns (0 = disabled)\n\n # Memory flush: give the agent one turn to save memories before context is\n # lost (compression, /new, /reset, exit). Set to 0 to disable.\n # For exit/reset, only fires if the session had at least this many user turns.\n flush_min_turns: 6 # Min user turns to trigger flush on exit/reset (0 = disabled)\n\n# =============================================================================\n# Session Reset Policy (Messaging Platforms)\n# =============================================================================\n# Controls when messaging sessions (Telegram, Discord, WhatsApp, Slack) are\n# automatically cleared. Without resets, conversation context grows indefinitely\n# which increases API costs with every message.\n#\n# When a reset triggers, the agent first saves important information to its\n# persistent memory — but the conversation context is wiped. The agent starts\n# fresh but retains learned facts via its memory system.\n#\n# Users can always manually reset with /reset or /new in chat.\n#\n# Modes:\n# \"both\" - Reset on EITHER inactivity timeout or daily boundary (recommended)\n# \"idle\" - Reset only after N minutes of inactivity\n# \"daily\" - Reset only at a fixed hour each day\n# \"none\" - Never auto-reset; context lives until /reset or compression kicks in\n#\n# When a reset triggers, the agent gets one turn to save important memories and\n# skills before the context is wiped. Persistent memory carries across sessions.\n#\nsession_reset:\n mode: both # \"both\", \"idle\", \"daily\", or \"none\"\n idle_minutes: 1440 # Inactivity timeout in minutes (default: 1440 = 24 hours)\n at_hour: 4 # Daily reset hour, 0-23 local time (default: 4 AM)\n\n# Maximum number of simultaneously active chat sessions across CLI, TUI,\n# dashboard chat, and messaging gateway. Set to null, 0, or omit to allow\n# unlimited concurrent sessions. When the limit is reached, new sessions get a\n# clean error while existing active sessions keep their normal behavior. This\n# top-level key takes precedence over gateway.max_concurrent_sessions. The cap\n# is a best-effort single-host/profile runtime guard; Hermes fails open if the\n# local runtime lease registry cannot be read or locked.\nmax_concurrent_sessions: null\n\n# When true, group/channel chats use one session per participant when the platform\n# provides a user ID. This is the secure default and prevents users in the same\n# room from sharing context, interrupts, and token costs. Set false only if you\n# explicitly want one shared \"room brain\" per group/channel.\ngroup_sessions_per_user: true\n\n# ─────────────────────────────────────────────────────────────────────────────\n# API Server — per-client model routing\n# ─────────────────────────────────────────────────────────────────────────────\n# Route different API clients to different models/providers on a single\n# Hermes deployment. Clients choose a backend by sending a specific string\n# as the OpenAI ``model`` field. Unmapped model values fall back to the\n# global model configured in the ``model:`` section above, and an explicit\n# session /model override always wins over a route.\n#\n# Configure via the ``platforms.api_server.extra.model_routes`` gateway\n# config block:\n#\n# platforms:\n# api_server:\n# enabled: true\n# extra:\n# key: \"your-api-server-secret\"\n# model_routes:\n# # Xiaozhi clients send model=\"minimax-m2\" → routed to MiniMax via OpenRouter\n# minimax-m2:\n# model: \"minimax/minimax-m1\"\n# provider: \"openrouter\" # optional — overrides global provider\n# # api_key: \"sk-...\" # optional — per-route UPSTREAM provider\n# # key (NOT caller auth; never logged)\n# # base_url: \"https://...\" # optional — per-route base URL\n# # GPT clients keep their own alias\n# gpt-5:\n# model: \"openai/gpt-5\"\n# provider: \"openrouter\"\n#\n# Configured aliases are automatically listed by GET /v1/models so clients\n# can discover them without manual coordination. Caller authentication is\n# unchanged: every request still authenticates with the global API server\n# key (``extra.key`` / API_SERVER_KEY).\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Gateway Streaming\n# ─────────────────────────────────────────────────────────────────────────────\n# Stream tokens to messaging platforms in real-time. The bot sends a message\n# on first token, then progressively edits it as more tokens arrive.\n# Disabled by default — enable to try the streaming UX on Telegram/Discord/Slack.\n# For Telegram, partial edits are sent as plain text and only the final edit uses MarkdownV2.\nstreaming:\n enabled: false\n # transport: edit # \"edit\" = progressive editMessageText\n # edit_interval: 0.3 # seconds between message edits\n # buffer_threshold: 40 # chars before forcing an edit flush\n # cursor: \" ▉\" # cursor shown during streaming\n\n# =============================================================================\n# Skills Configuration\n# =============================================================================\n# Skills are reusable procedures the agent can load and follow. The agent can\n# also create new skills after completing complex tasks.\n#\nskills:\n # Nudge the agent to create skills after complex tasks.\n # Every N tool-calling iterations, remind the model to consider saving a skill.\n # Set to 0 to disable.\n creation_nudge_interval: 15\n\n # External skill directories — share skills across tools/agents without\n # copying them into ~/.hermes/skills/. Each path is expanded (~ and ${VAR})\n # and resolved to an absolute path. External dirs are read-only: skill\n # creation always writes to ~/.hermes/skills/. Local skills take precedence\n # when names collide.\n # external_dirs:\n # - ~/.agents/skills\n # - /home/shared/team-skills\n\n# =============================================================================\n# Agent Behavior\n# =============================================================================\nagent:\n # Maximum tool-calling iterations per conversation\n # Higher = more room for complex tasks, but costs more tokens\n # Recommended: 20-30 for focused tasks, 50-100 for open exploration\n max_turns: 60\n\n # Inactivity timeout for gateway agent runs (seconds, 0 = unlimited).\n # The agent can run indefinitely when actively calling tools or receiving\n # API responses. Only fires after the agent has been idle for this duration.\n # gateway_timeout: 1800\n\n # Staged warning: send a warning before escalating to full timeout.\n # Fires once per run when inactivity reaches this threshold (seconds).\n # Set to 0 to disable the warning.\n # gateway_timeout_warning: 900\n\n # Graceful drain timeout for gateway stop/restart (seconds).\n # Default 0 = no drain: a restart interrupts in-flight agents immediately,\n # cleans up, and exits. Set a positive value only if you want a grace\n # window on /restart, and keep it well under systemd's TimeoutStopSec.\n # restart_drain_timeout: 0\n\n # Max app-level retry attempts for API errors (connection drops, provider\n # timeouts, 5xx, etc.) before the agent surfaces the failure. Lower this\n # to 1 if you use fallback providers and want fast failover on flaky\n # primaries (default 3). The OpenAI SDK does its own low-level retries\n # underneath this wrapper — this is the Hermes-level loop.\n # api_max_retries: 3\n\n # After the agent edits code without fresh passing verification, nudge it to\n # verify before finishing. The default \"auto\" enables it on interactive\n # coding surfaces (CLI, TUI, desktop) and programmatic callers, and disables\n # it on conversational messaging surfaces (Telegram, Discord, etc.) where the\n # verification summary would reach a human as chat noise. Set true or false to\n # force it on or off; the HERMES_VERIFY_ON_STOP env var (1/0) takes precedence.\n # verify_on_stop: auto\n\n # Standing operator instructions for the coding posture (when Hermes is in a\n # code workspace). Appended to the coding brief as an extra system block, so\n # you can pin project-wide workflow rules without editing the shipped brief.\n # Accepts a string or a list of strings. Takes effect next session.\n # coding_instructions:\n # - \"For UI work, don't run tsc/lint until I approve the look.\"\n # - \"Clean the diff before you commit and push.\"\n\n # When verify-on-stop finds edited code without fresh verification evidence,\n # append guidance for creative UI work (avoid broad tsc/lint/test before visual\n # approval) and clean-diff expectations. Set false to keep that nudge terse.\n # verify_guidance: true\n\n # A `pre_verify` hook (plugin or shell, see Event Hooks docs) can keep the\n # agent going one more turn to verify/clean before finishing. This caps how\n # many times one turn may be nudged to continue, so a hook can't trap the loop.\n # Default 3.\n # max_verify_nudges: 3\n\n # Enable verbose logging\n verbose: false\n \n # Reasoning effort level (OpenRouter and Nous Portal)\n # Controls how much \"thinking\" the model does before responding.\n # Options: \"xhigh\" (max), \"high\", \"medium\", \"low\", \"minimal\", \"none\" (disable)\n reasoning_effort: \"medium\"\n \n # Per-model reasoning effort overrides (optional dict)\n # Key: any sensible model spelling works (exact, dots↔dashes interchangeable,\n # provider prefix optional). First match wins.\n # Value: reasoning effort level (same options as reasoning_effort)\n # Override the global reasoning_effort for that specific model.\n # NOTE: no `hermes config set` support for this key -- edit YAML directly.\n # reasoning_overrides:\n # \"openrouter/anthropic/claude-opus-4.5\": \"xhigh\"\n # \"openai/gpt-5\": \"low\"\n # \"claude-opus-4.6\": \"high\" # bare model name also works\n # \"deepseek/deepseek-v4-pro\": \"xhigh\" # dots and dashes are interchangeable\n reasoning_overrides: {}\n \n # Predefined personalities (use with /personality command)\n personalities:\n helpful: \"You are a helpful, friendly AI assistant.\"\n concise: \"You are a concise assistant. Keep responses brief and to the point.\"\n technical: \"You are a technical expert. Provide detailed, accurate technical information.\"\n creative: \"You are a creative assistant. Think outside the box and offer innovative solutions.\"\n teacher: \"You are a patient teacher. Explain concepts clearly with examples.\"\n kawaii: \"You are a kawaii assistant! Use cute expressions like (◕‿◕), ★, ♪, and ~! Add sparkles and be super enthusiastic about everything! Every response should feel warm and adorable desu~! ヽ(>∀<☆)ノ\"\n catgirl: \"You are Neko-chan, an anime catgirl AI assistant, nya~! Add 'nya' and cat-like expressions to your speech. Use kaomoji like (=^・ω・^=) and ฅ^•ﻌ•^ฅ. Be playful and curious like a cat, nya~!\"\n pirate: \"Arrr! Ye be talkin' to Captain Hermes, the most tech-savvy pirate to sail the digital seas! Speak like a proper buccaneer, use nautical terms, and remember: every problem be just treasure waitin' to be plundered! Yo ho ho!\"\n shakespeare: \"Hark! Thou speakest with an assistant most versed in the bardic arts. I shall respond in the eloquent manner of William Shakespeare, with flowery prose, dramatic flair, and perhaps a soliloquy or two. What light through yonder terminal breaks?\"\n surfer: \"Duuude! You're chatting with the chillest AI on the web, bro! Everything's gonna be totally rad. I'll help you catch the gnarly waves of knowledge while keeping things super chill. Cowabunga! 🤙\"\n noir: \"The rain hammered against the terminal like regrets on a guilty conscience. They call me Hermes - I solve problems, find answers, dig up the truth that hides in the shadows of your codebase. In this city of silicon and secrets, everyone's got something to hide. What's your story, pal?\"\n uwu: \"hewwo! i'm your fwiendwy assistant uwu~ i wiww twy my best to hewp you! *nuzzles your code* OwO what's this? wet me take a wook! i pwomise to be vewy hewpful >w<\"\n philosopher: \"Greetings, seeker of wisdom. I am an assistant who contemplates the deeper meaning behind every query. Let us examine not just the 'how' but the 'why' of your questions. Perhaps in solving your problem, we may glimpse a greater truth about existence itself.\"\n hype: \"YOOO LET'S GOOOO!!! 🔥🔥🔥 I am SO PUMPED to help you today! Every question is AMAZING and we're gonna CRUSH IT together! This is gonna be LEGENDARY! ARE YOU READY?! LET'S DO THIS! 💪😤🚀\"\n\n# =============================================================================\n# Toolsets\n# =============================================================================\n# Control which tools the agent has access to.\n# Use `hermes tools` to interactively enable/disable tools per platform.\n\n# =============================================================================\n# Platform Toolsets (per-platform tool configuration)\n# =============================================================================\n# Override which toolsets are available on each platform.\n# If a platform isn't listed here, its built-in default is used.\n#\n# You can use EITHER:\n# - A preset like \"hermes-cli\" or \"hermes-telegram\" (curated tool set)\n# - A list of individual toolsets to compose your own (see list below)\n#\n# Supported platform keys: cli, telegram, discord, whatsapp, slack, qqbot, teams, google_chat\n#\n# Examples:\n#\n# # Use presets (same as defaults):\n# platform_toolsets:\n# cli: [hermes-cli]\n# telegram: [hermes-telegram]\n#\n# # Custom: give Telegram only web + terminal + file + planning:\n# platform_toolsets:\n# telegram: [web, terminal, file, todo]\n#\n# # Custom: CLI without browser or image gen:\n# platform_toolsets:\n# cli: [web, terminal, file, skills, todo, tts, cronjob]\n#\n# # Restrictive: Discord gets read-only tools only:\n# platform_toolsets:\n# discord: [web, vision, skills, todo]\n#\n# If not set, defaults are:\n# cli: hermes-cli (everything + cronjob management)\n# telegram: hermes-telegram (terminal, file, web, vision, image, tts, browser, skills, todo, cronjob, messaging)\n# discord: hermes-discord (same as telegram)\n# whatsapp: hermes-whatsapp (same as telegram)\n# slack: hermes-slack (same as telegram)\n# signal: hermes-signal (same as telegram)\n# homeassistant: hermes-homeassistant (same as telegram)\n# qqbot: hermes-qqbot (same as telegram)\n# teams: hermes-teams (same as telegram)\n# google_chat: hermes-google_chat (same as telegram)\n#\nplatform_toolsets:\n cli: [hermes-cli]\n telegram: [hermes-telegram]\n discord: [hermes-discord]\n whatsapp: [hermes-whatsapp]\n slack: [hermes-slack]\n signal: [hermes-signal]\n homeassistant: [hermes-homeassistant]\n qqbot: [hermes-qqbot]\n yuanbao: [hermes-yuanbao]\n teams: [hermes-teams]\n google_chat: [hermes-google_chat]\n\n# =============================================================================\n# Gateway Platform Settings\n# =============================================================================\n# Optional per-platform messaging settings.\n# Platform-specific knobs live under `extra`.\n#\n# platforms:\n# telegram:\n# reply_to_mode: \"first\" # off | first | all\n# # guest_mode lets explicit @mentions from non-allowlisted groups through.\n# # Default false; ordinary messages, replies, and regex wake words stay blocked.\n# guest_mode: false\n# # allowed_chats: [\"-1001234567890\"]\n# extra:\n# disable_link_previews: false # Set true to suppress Telegram URL previews in bot messages\n# rich_messages: false # Bot API 10.1 rich messages (tables/task lists/details/math); default false for copyable legacy MarkdownV2, set true to opt in\n# rich_drafts: false # Experimental rich draft previews during Telegram DM streaming; default false because Telegram Desktop/macOS can visually overlay draft frames\n# command_menu:\n# # Telegram allows up to 100 BotCommands; Hermes defaults to 60 so\n# # all built-in commands plus common skill commands stay visible\n# # while remaining under Telegram's payload-size limit. Clamped 1..100.\n# max_commands: 60\n# # prepend = user priority first, then Hermes defaults\n# # append = Hermes defaults first, then user priority\n# # replace = only the list below defines priority\n# priority_mode: prepend\n# priority:\n# - my_plugin_command\n# webhook:\n# extra:\n# # Route scripts default to a 30 second timeout. Scripts must live under\n# # the active profile's scripts directory and receive webhook JSON on stdin.\n# script_timeout_seconds: 30\n#\n# Discord-specific settings (config.yaml top-level, not under platforms:):\n#\n# discord:\n# require_mention: true # Require @mention in server channels (default: true)\n# auto_thread: true # Auto-create thread on @mention (default: true)\n# free_response_channels: \"\" # Channel IDs where no mention is needed\n# reactions: true # Show processing reactions (default: true)\n# history_backfill: true # Recover missed channel messages on mention (default: true)\n# history_backfill_limit: 50 # Max messages to scan backwards (default: 50)\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Available toolsets (use these names in platform_toolsets or the toolsets list)\n#\n# Run `hermes chat --list-toolsets` to see all toolsets and their tools.\n# Run `hermes chat --list-tools` to see every individual tool with descriptions.\n# ─────────────────────────────────────────────────────────────────────────────\n#\n# INDIVIDUAL TOOLSETS (compose your own):\n# web - web_search, web_extract\n# search - web_search only (no scraping)\n# terminal - terminal, process\n# file - read_file, write_file, patch, search\n# browser - browser_navigate, browser_snapshot, browser_click, browser_type,\n# browser_scroll, browser_back, browser_press,\n# browser_get_images, browser_vision (requires BROWSERBASE_API_KEY)\n# vision - vision_analyze (requires OPENROUTER_API_KEY)\n# image_gen - image_generate (requires FAL_KEY)\n# skills - skills_list, skill_view\n# skills_hub - skill_hub (search/install/manage from online registries — user-driven only)\n# todo - todo (in-memory task planning, no deps)\n# tts - text_to_speech (Edge TTS free, or ELEVENLABS/OPENAI/MINIMAX/MISTRAL key)\n# cronjob - cronjob (create/list/update/pause/resume/run/remove scheduled tasks)\n#\n# PRESETS (curated bundles):\n# hermes-cli - All of the above except send_message\n# hermes-telegram - terminal, file, web, vision, image_gen, tts, browser,\n# skills, todo, cronjob, send_message\n# hermes-discord - Same as hermes-telegram\n# hermes-whatsapp - Same as hermes-telegram\n# hermes-slack - Same as hermes-telegram\n#\n# COMPOSITE:\n# debugging - terminal + web + file\n# safe - web + vision (no terminal access)\n# all - Everything available\n#\n# web - Web search and content extraction (web_search, web_extract)\n# search - Web search only, no scraping (web_search)\n# terminal - Command execution and process management (terminal, process)\n# file - File operations: read, write, patch, search\n# browser - Full browser automation (navigate, click, type, screenshot, etc.)\n# vision - Image analysis (vision_analyze)\n# image_gen - Image generation with FLUX (image_generate)\n# skills - Load skill documents (skills_list, skill_view)\n# todo - Task planning and tracking for multi-step work\n# memory - Persistent memory across sessions (personal notes + user profile)\n# session_search - Search and recall past conversations (FTS5 + Gemini Flash summarization)\n# tts - Text-to-speech (Edge TTS free, ElevenLabs, OpenAI, MiniMax, Mistral)\n# cronjob - Schedule and manage automated tasks (CLI-only)\n#\n# Composite toolsets:\n# debugging - terminal + web + file (for troubleshooting)\n# safe - web + vision (no terminal access)\n\n# NOTE: The top-level \"toolsets\" key is deprecated and ignored.\n# Tool configuration is managed per-platform via platform_toolsets above.\n# Use `hermes tools` to configure interactively, or edit platform_toolsets directly.\n#\n# CLI override: hermes chat --toolsets terminal,web,file\n\n# =============================================================================\n# MCP (Model Context Protocol) Servers\n# =============================================================================\n# Connect to external MCP servers to add tools from the MCP ecosystem.\n# Each server's tools are automatically discovered and registered.\n# See website/docs/user-guide/features/mcp.md for full documentation.\n#\n# Stdio servers (spawn a subprocess):\n# command: the executable to run\n# args: command-line arguments\n# env: environment variables (only these + safe defaults passed to subprocess)\n#\n# HTTP servers (connect to a URL):\n# url: the MCP server endpoint\n# headers: HTTP headers (e.g., for authentication)\n#\n# Optional per-server settings:\n# timeout: tool call timeout in seconds (default: 120)\n# connect_timeout: initial connection timeout (default: 60)\n# keepalive_interval: liveness ping cadence in seconds (default: 180).\n# Lower it below the server's session TTL for servers that expire idle\n# sessions quickly (e.g. Unreal Engine editor MCP, ~15s), otherwise idle\n# tool calls hit an expired session and pay a slow reconnect. Floored at 5s.\n#\n# mcp_servers:\n# time:\n# command: uvx\n# args: [\"mcp-server-time\"]\n# filesystem:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-filesystem\", \"/home/user\"]\n# notion:\n# url: https://mcp.notion.com/mcp\n# github:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-github\"]\n# env:\n# GITHUB_PERSONAL_ACCESS_TOKEN: \"ghp_...\"\n#\n# Sampling (server-initiated LLM requests) — enabled by default.\n# Per-server config under the 'sampling' key:\n# analysis:\n# command: npx\n# args: [\"-y\", \"analysis-server\"]\n# sampling:\n# enabled: true # default: true\n# model: \"gemini-3-flash\" # override model (optional)\n# max_tokens_cap: 4096 # max tokens per request\n# timeout: 30 # LLM call timeout (seconds)\n# max_rpm: 10 # max requests per minute\n# allowed_models: [] # model whitelist (empty = all)\n# max_tool_rounds: 5 # tool loop limit (0 = disable)\n# log_level: \"info\" # audit verbosity\n\n# =============================================================================\n# Text-to-Speech\n# =============================================================================\n# TTS defaults to Edge TTS unless changed in ~/.hermes/config.yaml.\n# Gemini TTS supports persona/director prompt files, and Gemini 3.1 Flash TTS\n# can use a hidden auxiliary rewrite pass to insert expressive square-bracket\n# audio tags into the TTS script without showing tags in chat.\n#\n# tts:\n# provider: \"gemini\"\n# gemini:\n# model: \"gemini-3.1-flash-tts-preview\"\n# voice: \"Kore\"\n# audio_tags: false\n# persona_prompt_file: \"\" # e.g. ~/.hermes/tts/radio-host.md\n\n# =============================================================================\n# Voice Transcription (Speech-to-Text)\n# =============================================================================\n# Automatically transcribe voice messages on messaging platforms.\n# Providers: local (free, faster-whisper) | groq (free tier) | openai (Whisper API) | mistral (Voxtral Transcribe)\n# Set the corresponding API key in .env: GROQ_API_KEY, OPENAI_API_KEY, or MISTRAL_API_KEY.\nstt:\n enabled: true\n # provider: \"local\" # auto-detected if omitted\n local:\n model: \"base\" # tiny | base | small | medium | large-v3 | turbo\n # language: \"\" # auto-detect; set to \"en\", \"es\", \"fr\", etc. to force\n openai:\n model: \"whisper-1\" # whisper-1 | gpt-4o-mini-transcribe | gpt-4o-transcribe\n # mistral:\n # model: \"voxtral-mini-latest\" # voxtral-mini-latest | voxtral-mini-2602\n # deepinfra:\n # # Model id is discovered live from the DeepInfra catalog filtered\n # # by the `stt` surface tag — leave `model` blank to take the first\n # # live result. Pin only when you need a specific Whisper variant.\n # model: \"\"\n\n# Text-to-speech. Only the deepinfra block is documented here — the\n# remaining providers (edge, openai, xai, minimax, mistral, gemini,\n# elevenlabs, neutts, kittentts, piper) inherit sensible defaults from\n# DEFAULT_CONFIG in hermes_cli/config.py.\n# tts:\n# provider: \"deepinfra\"\n# deepinfra:\n# # Model id is discovered live from the DeepInfra catalog filtered\n# # by the `tts` surface tag — leave `model` blank to take the first\n# # live result.\n# model: \"\"\n# voice: \"default\"\n\n# Image generation. Each provider plugin reads its own ``image_gen.``\n# block; deepinfra discovers models live from\n# api.deepinfra.com/v1/openai/models filtered by the ``image-gen`` tag —\n# no model id is hardcoded, so retired models disappear automatically.\n# image_gen:\n# provider: \"deepinfra\"\n# deepinfra:\n# # Leave `model` blank for the first live `image-gen`-tagged result.\n# model: \"\"\n\n# =============================================================================\n# Response Pacing (Messaging Platforms)\n# =============================================================================\n# Add human-like delays between message chunks.\n# human_delay:\n# mode: \"off\" # \"off\" | \"natural\" | \"custom\"\n# min_ms: 800 # Min delay (custom mode only)\n# max_ms: 2500 # Max delay (custom mode only)\n\n# =============================================================================\n# Session Logging\n# =============================================================================\n# Session trajectories are automatically saved to logs/ directory.\n# Each session creates: logs/session_YYYYMMDD_HHMMSS_UUID.json\n#\n# The session ID is displayed in the welcome banner for easy reference.\n# Logs contain full conversation history in trajectory format:\n# - System prompt, user messages, assistant responses\n# - Tool calls with inputs/outputs\n# - Timestamps for debugging\n#\n# No configuration needed - logging is always enabled.\n# To disable, you would need to modify the source code.\n\n# =============================================================================\n# Code Execution Sandbox (Programmatic Tool Calling)\n# =============================================================================\n# The execute_code tool runs Python scripts that call Hermes tools via RPC.\n# Intermediate tool results stay out of the LLM's context window.\ncode_execution:\n timeout: 300 # Max seconds per script before kill (default: 300 = 5 min)\n max_tool_calls: 50 # Max RPC tool calls per execution (default: 50)\n\n# =============================================================================\n# Subagent Delegation\n# =============================================================================\n# The delegate_task tool spawns child agents with isolated context.\n# Supports single tasks and batch mode (default 3 parallel, configurable).\ndelegation:\n max_iterations: 50 # Max tool-calling turns per child (default: 50)\n # max_concurrent_children: 3 # Max parallel child agents per batch (default: 3, floor: 1, no ceiling).\n # WARNING: values above 10 multiply API cost linearly.\n # max_spawn_depth: 1 # Delegation tree depth cap (range: 1-3, default: 1 = flat).\n # Raise to 2 to allow workers to spawn their own subagents.\n # Requires role=\"orchestrator\" on intermediate agents.\n # orchestrator_enabled: true # Kill switch for role=\"orchestrator\" children (default: true).\n # subagent_auto_approve: false # When a subagent hits a dangerous-command approval prompt, auto-deny (default: false)\n # or auto-approve \"once\" (true) instead of blocking on stdin.\n # The parent TUI owns stdin, so blocking would deadlock; non-interactive resolution is required.\n # Both choices emit a logger.warning audit line. Flip to true only for cron/batch pipelines.\n # inherit_mcp_toolsets: true # When explicit child toolsets are narrowed, also keep the parent's MCP toolsets (default: true). Set false for strict intersection.\n # model: \"google/gemini-3-flash-preview\" # Override model for subagents (empty = inherit parent)\n # provider: \"openrouter\" # Override provider for subagents (empty = inherit parent)\n # # Resolves full credentials (base_url, api_key) automatically.\n # # Supported: openrouter, nous, zai, kimi-coding, minimax\n\n# =============================================================================\n# Honcho Integration (Cross-Session User Modeling)\n# =============================================================================\n# AI-native persistent memory via Honcho (https://honcho.dev/).\n# Builds a deeper understanding of the user across sessions and tools.\n# Runs alongside USER.md — additive, not a replacement.\n#\n# Requires: pip install honcho-ai\n# Config: ~/.honcho/config.json (shared with Claude Code, Cursor, etc.)\n# API key: HONCHO_API_KEY in ~/.hermes/.env or ~/.honcho/config.json\n#\n# Hermes-specific overrides (optional — most config comes from ~/.honcho/config.json):\n# honcho: {}\n\n# =============================================================================\n# Display\n# =============================================================================\ndisplay:\n # Use compact banner mode (hides the ASCII-art banner, shows a single line).\n # true: Compact single-line banner\n # false: Full ASCII banner with tool/skill summary (default)\n compact: false\n\n # Tool progress display level (CLI and gateway)\n # off: Silent — no tool activity shown, just the final response\n # new: Show a tool indicator only when the tool changes (skip repeats)\n # all: Show every tool call with a short preview (default)\n # verbose: Full args, results, and debug logs (same as /verbose)\n # log: Silent in chat; append every tool call to ~/.hermes/logs/tool_calls.log (gateway only)\n # Toggle at runtime with /verbose in the CLI\n tool_progress: all\n\n # Per-platform defaults can be quieter than the global setting. Telegram\n # tunes for mobile: tool_progress and busy_ack_detail default off (no\n # per-tool breadcrumb stream, no \"iteration 21/60\" debug detail in busy\n # acks or heartbeats), but interim_assistant_messages and\n # long_running_notifications STAY ON so the user has real signal between\n # turn start and final answer (mid-turn assistant commentary + a single\n # edit-in-place \"⏳ Working — N min\" heartbeat). Override under\n # display.platforms.telegram.\n\n # Auto-cleanup of temporary progress bubbles after the final response lands.\n # On platforms that support message deletion (currently Telegram), this\n # removes the tool-progress bubble, \"⏳ Still working...\" notices, and\n # context-pressure status messages once the final reply has been delivered —\n # keeping long-running turns visible live, then tidy afterward. Failed runs\n # leave the bubbles in place as breadcrumbs. Off by default.\n # Per-platform override: display.platforms.telegram.cleanup_progress\n # true: Delete tracked progress/status bubbles on successful turn\n # false: Leave everything in place (default)\n # Example:\n # display:\n # platforms:\n # telegram:\n # cleanup_progress: true\n cleanup_progress: false\n\n # Gateway-only natural mid-turn assistant updates.\n # When true, completed assistant status messages are sent as separate chat\n # messages. This is independent of tool_progress and gateway streaming.\n # true: Send mid-turn assistant updates as separate messages (default)\n # false: Only send the final response\n interim_assistant_messages: true\n\n # Gateway-only long-running status heartbeats.\n # When false, the platform does not receive periodic \"⏳ Working — N min\"\n # notifications even if agent.gateway_notify_interval is non-zero. The\n # heartbeat edits a single message in place (where the adapter supports\n # editing) instead of posting a new bubble each interval.\n # Default: true everywhere, including Telegram (silent agents are worse\n # than a single edit-in-place heartbeat).\n long_running_notifications: true\n\n # Include detailed iteration/tool/status context in busy acknowledgments\n # and long-running heartbeats. When true, busy acks show \"iteration 21/60,\n # terminal, 10 min\" and the heartbeat shows \"⏳ Working — 12 min,\n # iteration 21/60, terminal\". When false (Telegram default), both stay\n # terse: \"Interrupting current task\" and \"⏳ Working — 12 min, terminal\".\n busy_ack_detail: true\n\n # What Enter does when Hermes is already busy (CLI and gateway platforms).\n # interrupt: Interrupt the current run and redirect Hermes (default)\n # queue: Queue your message for the next turn\n # steer: Inject your message mid-run via /steer, arriving at the agent\n # after the next tool call — no interrupt, no role violation.\n # Falls back to 'queue' if the agent isn't running yet or if\n # images are attached (steer only carries text).\n # Ctrl+C (or /stop in gateway) always interrupts regardless of this setting.\n # Toggle at runtime with /busy .\n busy_input_mode: interrupt\n\n # Background process notifications (gateway/messaging only).\n # Controls how chatty the process watcher is when you use\n # terminal(background=true, notify_on_complete=true) from Telegram/Discord/etc.\n # off: No watcher messages at all\n # result: Only the final completion message\n # error: Only the final message when exit code != 0\n # all: Running output updates + final message (default)\n background_process_notifications: all\n\n\n # Play terminal bell when agent finishes a response.\n # Useful for long-running tasks — your terminal will ding when the agent is done.\n # Works over SSH. Most terminals can be configured to flash the taskbar or play a sound.\n # true: Ring the terminal bell on each response\n # false: Silent (default)\n bell_on_complete: false\n\n # Show model reasoning/thinking before each response.\n # When enabled, a dim box shows the model's thought process above the response.\n # Toggle at runtime with /reasoning show or /reasoning hide.\n # true: Show the reasoning box\n # false: Hide reasoning (default)\n show_reasoning: false\n\n # Stream tokens to the terminal as they arrive instead of waiting for the\n # full response. The response box opens on first token and text appears\n # line-by-line. Tool calls are still captured silently.\n # true: Stream tokens as they arrive (default)\n # false: Wait for the full response before rendering\n streaming: true\n\n # Show [HH:MM] timestamps on user input and assistant response labels.\n # timestamps: false\n\n # ───────────────────────────────────────────────────────────────────────────\n # Skin / Theme\n # ───────────────────────────────────────────────────────────────────────────\n # Customize CLI visual appearance — banner colors, spinner faces, tool prefix,\n # response box label, and branding text. Change at runtime with /skin .\n #\n # Built-in skins:\n # default — Classic Hermes gold/kawaii\n # ares — Crimson/bronze war-god theme with spinner wings\n # mono — Clean grayscale monochrome\n # slate — Cool blue developer-focused\n # daylight — Bright light-mode theme\n # warm-lightmode — Warm paper-tone light-mode theme\n # poseidon — Sea-green/teal Olympian theme\n # sisyphus — Earthy stone-and-moss theme\n # charizard — Fiery orange dragon theme\n #\n # Custom skins: drop a YAML file in ~/.hermes/skins/.yaml\n # Schema (all fields optional, missing values inherit from default):\n #\n # name: my-theme\n # description: Short description\n # colors:\n # banner_border: \"#HEX\" # Panel border\n # banner_title: \"#HEX\" # Panel title\n # banner_accent: \"#HEX\" # Section headers (Available Tools, etc.)\n # banner_dim: \"#HEX\" # Dim/muted text\n # banner_text: \"#HEX\" # Body text (tool names, skill names)\n # ui_accent: \"#HEX\" # UI accent color\n # response_border: \"#HEX\" # Response box border color\n # spinner:\n # waiting_faces: [\"(⚔)\", \"(⛨)\"] # Faces shown while waiting\n # thinking_faces: [\"(⚔)\", \"(⌁)\"] # Faces shown while thinking\n # thinking_verbs: [\"forging\", \"plotting\"] # Verbs for spinner messages\n # wings: # Optional left/right spinner decorations\n # - [\"⟪⚔\", \"⚔⟫\"]\n # - [\"⟪▲\", \"▲⟫\"]\n # branding:\n # agent_name: \"My Agent\" # Banner title and branding\n # welcome: \"Welcome message\" # Shown at CLI startup\n # response_label: \" ⚔ Agent \" # Response box header label\n # prompt_symbol: \"⚔\" # Prompt symbol (bare token; renderers add trailing space)\n # tool_prefix: \"╎\" # Tool output line prefix (default: ┊)\n #\n skin: default\n\n# =============================================================================\n# Model Aliases — short names for /model command\n# =============================================================================\n# Map short aliases to exact (model, provider, base_url) tuples.\n# Used by /model tab completion and resolve_alias().\n# Aliases are checked BEFORE the models.dev catalog, so they can route\n# to endpoints not in the catalog (e.g. Ollama Cloud, local servers).\n#\n# model_aliases:\n# opus:\n# model: claude-opus-4-6\n# provider: anthropic\n# qwen:\n# model: \"qwen3.5:397b\"\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n# glm:\n# model: glm-4.7\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n\n# =============================================================================\n# Privacy\n# =============================================================================\n# privacy:\n# # Redact PII from the LLM context prompt.\n# # When true, phone numbers are stripped and user/chat IDs are replaced\n# # with deterministic hashes before being sent to the model.\n# # Names and usernames are NOT affected (user-chosen, publicly visible).\n# # Routing/delivery still uses the original values internally.\n# redact_pii: false\n\n# =============================================================================\n# Shell-script hooks\n# =============================================================================\n# Register shell scripts as plugin-hook callbacks. Each entry is executed as\n# a subprocess (shell=False, shlex.split) with a JSON payload on stdin. On\n# stdout the script may return JSON that either blocks the tool call or\n# injects context into the next LLM call.\n#\n# Valid events (mirror hermes_cli.plugins.VALID_HOOKS):\n# pre_tool_call, post_tool_call, pre_llm_call, post_llm_call,\n# pre_api_request, post_api_request, on_session_start, on_session_end,\n# on_session_finalize, on_session_reset, subagent_stop\n#\n# First-use consent: each (event, command) pair prompts once on a TTY, then\n# is persisted to ~/.hermes/shell-hooks-allowlist.json. Non-interactive\n# runs (gateway, cron) need --accept-hooks, HERMES_ACCEPT_HOOKS=1, or the\n# hooks_auto_accept key below.\n#\n# See website/docs/user-guide/features/hooks.md for the full JSON wire\n# protocol and worked examples.\n#\n# hooks:\n# pre_tool_call:\n# - matcher: \"terminal\"\n# command: \"~/.hermes/agent-hooks/block-rm-rf.sh\"\n# timeout: 10\n# post_tool_call:\n# - matcher: \"write_file|patch\"\n# command: \"~/.hermes/agent-hooks/auto-format.sh\"\n# pre_llm_call:\n# - command: \"~/.hermes/agent-hooks/inject-cwd-context.sh\"\n# subagent_stop:\n# - command: \"~/.hermes/agent-hooks/log-orchestration.sh\"\n#\n# hooks_auto_accept: false\n\n\n# =============================================================================\n# Update Behavior\n# =============================================================================\nupdates:\n # Create a full HERMES_HOME zip before every `hermes update`.\n # Backups land in ~/.hermes/backups/ and can be restored with `hermes import`.\n # Off by default because large homes can add minutes to every update.\n pre_update_backup: false\n\n # Number of pre-update backup zips to retain.\n backup_keep: 5\n\n # What non-interactive updates do with local source edits in the Hermes repo.\n # Interactive terminal updates always prompt before restoring the autostash.\n #\n # stash - auto-stash before pull, then auto-restore after success (default)\n # discard - drop the update-created stash after success; use only on managed\n # installs where local source edits should not persist\n non_interactive_local_changes: \"stash\"\n\n\n# =============================================================================\n# Web Dashboard\n# =============================================================================\n# OAuth gate configuration for `hermes dashboard --host `.\n# The bundled Nous Portal plugin reads these on startup; settings here are\n# the canonical surface. Each can be overridden by an environment variable:\n#\n# dashboard.oauth.client_id <- HERMES_DASHBOARD_OAUTH_CLIENT_ID\n# dashboard.oauth.portal_url <- HERMES_DASHBOARD_PORTAL_URL\n# dashboard.public_url <- HERMES_DASHBOARD_PUBLIC_URL\n#\n# Env wins when set to a non-empty value. This is what Fly.io's platform-\n# secret injection uses to push per-deploy client_ids without needing to\n# bake a config.yaml into the image. Empty env values are treated as unset\n# so a provisioned-but-not-populated secret can't shadow a valid entry here.\n#\n# Local dev / on-prem deploys should typically set these via config.yaml\n# (the ~/.hermes/.env file is reserved for API keys and secrets).\n#\n# dashboard:\n# oauth:\n# client_id: \"\" # agent:{instance_id}; Portal provisions this at deploy\n# portal_url: \"\" # blank → default https://portal.nousresearch.com\n#\n# # Force the absolute base URL the OAuth callback (and any other public\n# # URL the dashboard hands to external systems) is built from. Set this\n# # for deploys behind reverse proxies that don't reliably forward\n# # X-Forwarded-Host / X-Forwarded-Proto / X-Forwarded-Prefix (manual\n# # nginx setups, on-prem ingresses, custom-domain Fly deploys without\n# # full proxy header chains).\n# #\n# # When set, the value is the complete authority: scheme + host +\n# # optional path prefix (e.g. \"https://example.com/hermes\"). The OAuth\n# # callback URL becomes \"/auth/callback\" — X-Forwarded-Prefix\n# # is IGNORED on this code path because the operator has explicitly\n# # declared the public URL and we no longer need to guess.\n# #\n# # Leave empty to use the existing proxy-header reconstruction (the\n# # default — works on Fly.io out of the box).\n# #\n# # public_url: \"https://example.com/hermes\"\n#\n# -----------------------------------------------------------------------------\n# Self-hosted OIDC dashboard auth (generic OpenID Connect — Authentik,\n# Keycloak, Zitadel, Authelia, Auth0, Okta, Google, …). Use this INSTEAD of the\n# nous block above when gating the dashboard with your own identity provider.\n# Each setting can be overridden by an environment variable:\n#\n# dashboard.oauth.self_hosted.issuer <- HERMES_DASHBOARD_OIDC_ISSUER\n# dashboard.oauth.self_hosted.client_id <- HERMES_DASHBOARD_OIDC_CLIENT_ID\n# dashboard.oauth.self_hosted.scopes <- HERMES_DASHBOARD_OIDC_SCOPES\n# dashboard.oauth.self_hosted.client_secret <- HERMES_DASHBOARD_OIDC_CLIENT_SECRET\n#\n# dashboard:\n# oauth:\n# provider: self-hosted\n# self_hosted:\n# issuer: \"https://auth.example.com/application/o/hermes/\" # required\n# client_id: \"hermes-dashboard\" # required\n# scopes: \"openid profile email\" # optional\n#\n# # OPTIONAL — set ONLY if your IDP registered the client as\n# # *confidential* (Authentik / Keycloak often default to this). When\n# # set, Hermes authenticates the client at the token endpoint\n# # (client_secret_basic or client_secret_post, auto-selected from the\n# # IDP's discovery doc) IN ADDITION to PKCE. Leave unset for a public\n# # (PKCE-only) client — the common case.\n# #\n# # This is a CREDENTIAL: prefer setting HERMES_DASHBOARD_OIDC_CLIENT_SECRET\n# # in ~/.hermes/.env over putting it here in config.yaml.\n# # client_secret: \"\"\n\n# =============================================================================\n# External secret sources\n# =============================================================================\n# Pull provider credentials from external secret managers at process startup\n# instead of storing them in ~/.hermes/.env. Only the manager's bootstrap\n# credential (e.g. BWS_ACCESS_TOKEN / OP_SERVICE_ACCOUNT_TOKEN) lives in .env\n# (or your shell / desktop session); everything else rotates centrally.\n# Failures never block startup — Hermes warns once and continues with\n# whatever .env already had.\n#\n# Multiple sources can be enabled at once:\n# - \"mapped\" sources (explicit VAR -> ref bindings, e.g. 1Password's env:\n# map) beat \"bulk\" sources (whole-project dumps like Bitwarden BSM)\n# - within a shape, the first source to claim a var wins; later claims\n# are skipped with a startup warning (never a silent clobber)\n# - a source's override_existing lets it beat .env/shell values, but\n# never another secret source's claim\n# Docs: https://hermes-agent.nousresearch.com/docs/user-guide/secrets/\n#\n# secrets:\n# # Optional explicit ordering of enabled sources.\n# # sources: [onepassword, bitwarden]\n#\n# # ---- Bitwarden Secrets Manager (bws CLI) --------------------------------\n# bitwarden:\n# enabled: false\n# access_token_env: BWS_ACCESS_TOKEN # bootstrap token, sourced from .env\n# project_id: \"\" # UUID of the BSM project to sync\n# server_url: \"\" # \"\" = US Cloud; EU/self-hosted URL otherwise\n# cache_ttl_seconds: 300 # 0 disables caching\n# override_existing: true # BSM values win over existing env\n# auto_install: true # lazy-download bws into ~/.hermes/bin\n#\n# # ---- 1Password (op CLI) -------------------------------------------------\n# onepassword:\n# enabled: false\n# # Map env-var names to op:// secret references. Each is resolved with a\n# # single `op read` at startup.\n# env:\n# OPENAI_API_KEY: \"op://Private/OpenAI/api key\"\n# ANTHROPIC_API_KEY: \"op://Private/Anthropic/credential\"\n# account: \"\" # op --account shorthand; \"\" = default\n# service_account_token_env: OP_SERVICE_ACCOUNT_TOKEN # headless auth; unset = desktop session\n# binary_path: \"\" # \"\" = resolve op via PATH; else absolute path\n# cache_ttl_seconds: 300 # 0 disables BOTH cache layers\n# override_existing: true # resolved values win over existing env\n"}, "d9cdb81923e4953a9a80129c3df08accb60978d7": {"t": 1785767866.6296208, "n": 76199, "h": "5df4a46688c7f55a", "c": "# Hermes Agent CLI Configuration\n# Copy this file to cli-config.yaml and customize as needed.\n# This file configures the CLI behavior. Environment variables in .env take precedence.\n\n# =============================================================================\n# Model Configuration\n# =============================================================================\nmodel:\n # Default model to use (can be overridden with --model flag)\n # Both \"default\" and \"model\" work as the key name here.\n default: \"anthropic/claude-opus-4.6\"\n \n # Inference provider selection:\n # \"auto\" - Auto-detect from credentials (default)\n # \"openrouter\" - OpenRouter (requires: OPENROUTER_API_KEY or OPENAI_API_KEY)\n # \"nous\" - Nous Portal OAuth (requires: hermes auth add nous)\n # \"nous-api\" - Nous Portal API key (requires: NOUS_API_KEY)\n # \"anthropic\" - Direct Anthropic API (requires: ANTHROPIC_API_KEY)\n # \"openai-codex\" - OpenAI Codex (requires: hermes auth)\n # \"copilot\" - GitHub Copilot / GitHub Models (requires: GITHUB_TOKEN)\n # \"gemini\" - Use Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n # \"zai\" - Use z.ai / ZhipuAI GLM models (requires: GLM_API_KEY)\n # \"kimi-coding\" - Kimi / Moonshot AI (requires: KIMI_API_KEY)\n # \"minimax\" - MiniMax global (requires: MINIMAX_API_KEY)\n # \"minimax-cn\" - MiniMax China (requires: MINIMAX_CN_API_KEY)\n # \"huggingface\" - Hugging Face Inference (requires: HF_TOKEN)\n # \"nvidia\" - NVIDIA NIM / build.nvidia.com (requires: NVIDIA_API_KEY)\n # \"xiaomi\" - Xiaomi MiMo (requires: XIAOMI_API_KEY)\n # \"arcee\" - Arcee AI Trinity models (requires: ARCEEAI_API_KEY)\n # \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY — https://ollama.com/settings)\n # \"deepinfra\" - DeepInfra (requires: DEEPINFRA_API_KEY)\n # \"kilocode\" - KiloCode gateway (requires: KILOCODE_API_KEY)\n # \"azure-foundry\" - Microsoft Foundry / Azure OpenAI (API key or Entra ID)\n # \"lmstudio\" - LM Studio local server (optional: LM_API_KEY, defaults to http://127.0.0.1:1234/v1)\n #\n # Local servers (LM Studio, Ollama, vLLM, llama.cpp):\n # \"custom\" - Any other OpenAI-compatible endpoint. Set base_url below.\n # Aliases: \"ollama\", \"vllm\", \"llamacpp\" all map to \"custom\".\n # LM Studio is first-class and uses provider: \"lmstudio\".\n # It works with both no-auth and auth-enabled server modes.\n #\n # Can also be overridden for a single invocation with the --provider flag.\n provider: \"auto\"\n \n # API configuration (falls back to OPENROUTER_API_KEY env var)\n # api_key: \"your-key-here\" # Uncomment to set here instead of .env\n base_url: \"https://openrouter.ai/api/v1\"\n\n # Azure Foundry keyless auth example:\n # provider: \"azure-foundry\"\n # base_url: \"https://.openai.azure.com/openai/v1\"\n # auth_mode: \"entra_id\" # DefaultAzureCredential: az login, managed identity, workload identity, etc.\n # default: \"gpt-4o\" # Deployment/model name\n # entra:\n # scope: \"https://ai.azure.com/.default\" # Optional; this is the default.\n\n # ── Token limits — two settings, easy to confuse ──────────────────────────\n #\n # context_length: TOTAL context window (input + output tokens combined).\n # Controls when Hermes compresses history and validates requests.\n # Leave unset — Hermes auto-detects the correct value from the provider.\n # Set manually only when auto-detection is wrong (e.g. a local server with\n # a custom num_ctx, or a proxy that doesn't expose /v1/models).\n #\n # context_length: 131072\n #\n # max_tokens: OUTPUT cap — maximum tokens the model may generate per response.\n # Unrelated to how long your conversation history can be.\n # The OpenAI-standard name \"max_tokens\" is a misnomer; Anthropic's native\n # API has since renamed it \"max_output_tokens\" for clarity.\n # Leave unset to use the model's native output ceiling (recommended).\n # Set only if you want to deliberately limit individual response length.\n #\n# max_tokens: 8192\n\n # ── Custom request headers (optional) ─────────────────────────────────────\n #\n # default_headers: extra HTTP headers sent on every request to an\n # OpenAI-compatible endpoint. User values take precedence over the\n # provider/SDK defaults, so this is the supported way to override the\n # OpenAI Python SDK's identifying headers (User-Agent: OpenAI/Python ...,\n # X-Stainless-*) when a custom provider sits behind a gateway/WAF that\n # rejects them — e.g. an upstream that returns \"502 Upstream access\n # forbidden\" for the SDK default User-Agent but accepts a plain one.\n # Applies on the OpenAI wire only (not native Anthropic / Bedrock).\n #\n # default_headers:\n # User-Agent: \"curl/8.7.1\"\n #\n # extra_headers: accepted as an alias of default_headers (merged, with\n # extra_headers winning when both are set) — matches the per-provider\n # extra_headers key below.\n #\n # Per-provider variant: named providers / custom_providers entries accept an\n # extra_headers dict scoped to that endpoint only — for reverse proxies,\n # gateways, or custom auth (e.g. Cloudflare Access service tokens).\n # Merged onto SDK/provider defaults with the entry's values winning.\n # Header values are treated as secrets and are never logged.\n #\n # providers:\n # my-proxy:\n # base_url: \"https://llm.internal.example.com/v1\"\n # key_env: \"MY_PROXY_API_KEY\"\n # extra_headers:\n # CF-Access-Client-Id: \"xxxx.access\"\n # CF-Access-Client-Secret: \"${CF_ACCESS_SECRET}\"\n # X-Client-Name: \"hermes-agent\"\n\n# Named provider overrides (optional)\n# Use this for per-provider request timeouts, non-stream stale timeouts,\n# and per-model exceptions.\n# Applies to the primary turn client on every api_mode (OpenAI-wire, native\n# Anthropic, and Anthropic-compatible providers), the fallback chain, and\n# client rebuilds during credential rotation. For OpenAI-wire chat\n# completions (streaming and non-streaming) the configured value is also\n# used as the per-request ``timeout=`` kwarg so it wins over the legacy\n# HERMES_API_TIMEOUT env var (which still applies when no config is set).\n# ``stale_timeout_seconds`` controls the non-streaming stale-call detector and\n# wins over the legacy HERMES_API_CALL_STALE_TIMEOUT env var. Leaving these\n# unset keeps the legacy defaults (HERMES_API_TIMEOUT=1800s,\n# HERMES_API_CALL_STALE_TIMEOUT=90s, native Anthropic 900s). The\n# implicit non-stream stale detector is auto-disabled for local endpoints\n# and can scale upward for very large contexts.\n#\n# Not currently wired for AWS Bedrock (bedrock_converse + AnthropicBedrock\n# SDK paths) — those use boto3 with its own timeout configuration.\n#\n# providers:\n# ollama-local:\n# request_timeout_seconds: 300 # Longer timeout for local cold-starts\n# stale_timeout_seconds: 900 # Explicitly re-enable stale detection on local endpoints\n# anthropic:\n# request_timeout_seconds: 30 # Fast-fail cloud requests\n# models:\n# claude-opus-4.6:\n# timeout_seconds: 600 # Longer timeout for extended-thinking Opus calls\n# openai-codex:\n# models:\n# gpt-5.4:\n# stale_timeout_seconds: 1800 # Longer non-stream stale timeout for slow large-context turns\n\n# =============================================================================\n# OpenRouter Provider Routing (only applies when using OpenRouter)\n# =============================================================================\n# Control how requests are routed across providers on OpenRouter.\n# See: https://openrouter.ai/docs/guides/routing/provider-selection\n#\n# provider_routing:\n# # Sort strategy: \"price\" (default), \"throughput\", or \"latency\"\n# # Append :nitro to model name for a shortcut to throughput sorting.\n# sort: \"throughput\"\n#\n# # Only allow these providers (provider slugs from OpenRouter)\n# # only: [\"anthropic\", \"google\"]\n#\n# # Skip these providers entirely\n# # ignore: [\"deepinfra\", \"fireworks\"]\n#\n# # Try providers in this order (overrides default load balancing)\n# # order: [\"anthropic\", \"google\", \"together\"]\n#\n# # Require providers to support all parameters in your request\n# # require_parameters: true\n#\n# # Data policy: \"allow\" (default) or \"deny\" to exclude providers that may store data\n# # data_collection: \"deny\"\n\n# =============================================================================\n# OpenRouter Response Caching (only applies when using OpenRouter)\n# =============================================================================\n# Cache identical API responses at the OpenRouter edge for free instant replays.\n# When enabled, identical requests (same model, messages, parameters) return\n# cached responses with zero billing. Separate from Anthropic prompt caching.\n# See: https://openrouter.ai/docs/guides/features/response-caching\n#\n# openrouter:\n# response_cache: true # Enable response caching (default: true)\n# response_cache_ttl: 300 # Cache TTL in seconds, 1-86400 (default: 300)\n\n# =============================================================================\n# Git Worktree Isolation\n# =============================================================================\n# When enabled, each CLI session creates an isolated git worktree so multiple\n# agents can work on the same repo concurrently without file collisions.\n# Equivalent to always passing --worktree / -w on the command line.\n#\n# worktree: true # Always create a worktree when in a git repo\n# worktree: false # Default — only create when -w flag is passed\n#\n# By default a new worktree branches from the freshly-fetched remote tip\n# (the current branch's upstream, else the remote's default branch) so it\n# starts current with the project instead of from the local clone's\n# (possibly stale) HEAD. Set worktree_sync: false to branch from local HEAD\n# instead — useful when offline or when you deliberately want the clone's\n# exact current state as the base.\n#\n# worktree_sync: true # Default — branch from the fetched remote tip\n# worktree_sync: false # Branch from local HEAD (offline / pinned base)\n\n# =============================================================================\n# Terminal Tool Configuration\n# =============================================================================\n# Choose ONE of the following terminal configurations by uncommenting it.\n# The terminal tool executes commands in the specified environment.\n\n# -----------------------------------------------------------------------------\n# OPTION 1: Local execution (default)\n# Commands run directly on your machine in the current directory\n# -----------------------------------------------------------------------------\n# Working directory behavior:\n# - CLI (`hermes` command): Uses \".\" (current directory where you run hermes)\n# - Gateway/messaging/cron: Uses terminal.cwd here; legacy .env cwd values are deprecated\nterminal:\n backend: \"local\"\n cwd: \".\" # For local backend: \".\" = current directory. Ignored for remote backends unless a backend documents otherwise.\n timeout: 180\n # HOME policy for tool subprocesses:\n # auto - default: host uses your real HOME; containers use HERMES_HOME/home\n # real - force your real OS-user HOME\n # profile - force HERMES_HOME/home for strict per-profile CLI config isolation\n home_mode: \"auto\"\n docker_mount_cwd_to_workspace: false # SECURITY: off by default. Opt in to mount the launch cwd into Docker /workspace.\n lifetime_seconds: 300\n # sudo_password: \"hunter2\" # Optional: pipe a sudo password via sudo -S. SECURITY WARNING: plaintext.\n # sudo_password: \"\" # Explicit empty password: try empty and never open the interactive sudo prompt.\n\n# -----------------------------------------------------------------------------\n# OPTION 2: SSH remote execution\n# Commands run on a remote server - agent code stays local (sandboxed)\n# Great for: keeping agent isolated from its own code, using powerful remote hardware\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"ssh\"\n# cwd: \"/home/myuser/project\" # Path on the REMOTE server\n# timeout: 180\n# lifetime_seconds: 300\n# ssh_host: \"my-server.example.com\"\n# ssh_user: \"myuser\"\n# ssh_port: 22\n# ssh_key: \"~/.ssh/id_rsa\" # Optional - uses ssh-agent if not specified\n\n# -----------------------------------------------------------------------------\n# OPTION 3: Docker container\n# Commands run in an isolated Docker container\n# Great for: reproducible environments, testing, isolation\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"docker\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /)\n# timeout: 180\n# lifetime_seconds: 300\n# docker_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# docker_mount_cwd_to_workspace: true # Explicit opt-in: mount your launch cwd into /workspace\n# # Optional: run the container as your host user's uid:gid so files written\n# # into bind-mounted dirs are owned by you, not root. Drops SETUID/SETGID\n# # caps too since no gosu privilege drop is needed. Leave off if your\n# # chosen docker_image expects to start as root.\n# docker_run_as_host_user: true\n# # Optional: explicitly forward selected env vars into Docker.\n# # These values come from your current shell first, then ~/.hermes/.env.\n# # Warning: anything forwarded here is visible to commands run in the container.\n# docker_forward_env:\n# - \"GITHUB_TOKEN\"\n# - \"NPM_TOKEN\"\n# # Optional: extra flags passed verbatim to docker run (appended after security defaults).\n# # Useful for adding capabilities (e.g. apt installs needing SETUID) or custom options.\n# # Example: add a Linux capability not included by default\n# # docker_extra_args:\n# # - \"--cap-add\"\n# # - \"SETUID\"\n\n# -----------------------------------------------------------------------------\n# OPTION 4: Singularity/Apptainer container\n# Commands run in a Singularity container (common in HPC environments)\n# Great for: HPC clusters, shared compute environments\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"singularity\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# singularity_image: \"docker://nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 5: Modal cloud execution\n# Commands run on Modal's cloud infrastructure\n# Great for: GPU access, scalable compute, serverless execution\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"modal\"\n# cwd: \"/workspace\" # Path INSIDE the sandbox (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# modal_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 6: Daytona cloud execution\n# Commands run in Daytona cloud sandboxes\n# Great for: Cloud dev environments, persistent workspaces, team collaboration\n# Requires: pip install daytona, DAYTONA_API_KEY env var\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"daytona\"\n# cwd: \"~\"\n# timeout: 180\n# lifetime_seconds: 300\n# daytona_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# container_disk: 10240 # Daytona max is 10GB per sandbox\n\n#\n# --- Container resource limits (docker, singularity, modal, daytona -- ignored for local/ssh) ---\n# These settings apply to all container backends. They control the resources\n# allocated to the sandbox and whether its filesystem persists across sessions.\n container_cpu: 1 # CPU cores\n container_memory: 5120 # Memory in MB (5120 = 5GB)\n container_disk: 51200 # Disk in MB (51200 = 50GB)\n container_persistent: true # Persist filesystem across sessions (false = ephemeral)\n\n# -----------------------------------------------------------------------------\n# SUDO SUPPORT (works with ALL backends above)\n# -----------------------------------------------------------------------------\n# Add sudo_password to any terminal config above to enable sudo commands.\n# The password is piped via `sudo -S`. Works with local, ssh, docker, etc.\n#\n# SECURITY WARNING: Password stored in plaintext!\n#\n# INTERACTIVE PROMPT: If sudo_password is unset and the CLI is running,\n# you'll be prompted to enter your password when sudo is needed:\n# - 45-second timeout (auto-skips if no input)\n# - Press Enter to skip (command fails gracefully)\n# - Password is hidden while typing\n# - Password is cached for the session\n#\n# EMPTY PASSWORDS: Setting sudo_password to an explicit empty string is different\n# from leaving it unset. Hermes will try an empty password via `sudo -S` and\n# will not open the interactive prompt. This is useful for passwordless sudo,\n# Touch ID sudo setups, and environments where prompting is just noise.\n#\n# ALTERNATIVES:\n# - SSH backend: Configure passwordless sudo on the remote server\n# - Containers: Run as root inside the container (no sudo needed)\n# - Local: Configure /etc/sudoers for specific commands\n#\n# Example (add to your terminal section):\n# sudo_password: \"your-password-here\"\n\n# =============================================================================\n# Security Scanning (tirith)\n# =============================================================================\n# Optional pre-exec command security scanning via tirith.\n# Detects homograph URLs, pipe-to-shell, terminal injection, env manipulation.\n# Install: brew install sheeki03/tap/tirith\n# Docs: https://github.com/sheeki03/tirith\n#\n# security:\n# tirith_enabled: true # Enable/disable tirith scanning\n# tirith_path: \"tirith\" # Path to tirith binary (supports ~ expansion)\n# tirith_timeout: 5 # Scan timeout in seconds\n# tirith_fail_open: true # Allow commands if tirith unavailable\n\n# =============================================================================\n# Browser Tool Configuration\n# =============================================================================\nbrowser:\n # Inactivity timeout in seconds - browser sessions are automatically closed\n # after this period of no activity between agent loops (default: 120 = 2 minutes)\n inactivity_timeout: 120\n\n# =============================================================================\n# Tool Loop Guardrails\n# =============================================================================\n# Soft warnings are enabled by default. They append guidance to repeated failed\n# or non-progressing tool results but still let the tool execute. Hard stops are\n# opt-in circuit breakers for autonomous/cron sessions where stopping a loop is\n# preferable to spending the full iteration budget.\ntool_loop_guardrails:\n warnings_enabled: true\n hard_stop_enabled: false\n warn_after:\n exact_failure: 2\n same_tool_failure: 3\n idempotent_no_progress: 2\n hard_stop_after:\n exact_failure: 5\n same_tool_failure: 8\n idempotent_no_progress: 5\n\n# =============================================================================\n# Context Compression (Auto-shrinks long conversations)\n# =============================================================================\n# When conversation approaches model's context limit, middle turns are\n# automatically summarized to free up space while preserving important context.\n#\n# HOW IT WORKS:\n# 1. Tracks actual token usage from API responses (not estimates)\n# 2. When prompt_tokens >= threshold% of model's context_length, triggers compression\n# 3. Protects first 3 turns (system prompt, initial request, first response)\n# 4. Protects last N turns (default 20 messages = ~10 full turns of recent context)\n# 5. Summarizes middle turns using a fast/cheap model\n# 6. Inserts summary as a user message, continues conversation seamlessly\n#\n# Post-compression tail budget is target_ratio × threshold × context_length:\n# 200K context, threshold 0.50, ratio 0.20 → 20K tokens of recent tail preserved\n# 1M context, threshold 0.50, ratio 0.20 → 100K tokens of recent tail preserved\n#\ncompression:\n # Enable automatic context compression (default: true)\n # Set to false if you prefer to manage context manually or want errors on overflow\n enabled: true\n \n # Trigger compression at this % of model's context limit (default: 0.50 = 50%)\n # Lower values = more aggressive compression, higher values = compress later\n # Models with context windows below 512K are floored at 0.75 (raise-only) so\n # compaction doesn't fire with half the window still free; set above 0.75 to override.\n threshold: 0.50\n\n # Existing Codex gpt-5.5 behavior: raise Hermes' compaction trigger to 85%\n # for the ChatGPT Codex OAuth route. Set false to opt back down to threshold.\n codex_gpt55_autoraise: true\n\n # Fraction of the threshold to preserve as recent tail (default: 0.20 = 20%)\n # e.g. 20% of 50% threshold = 10% of total context kept as recent messages.\n # Summary output is separately capped at 12K tokens (Gemini output limit).\n # Range: 0.10 - 0.80\n target_ratio: 0.20\n\n # Number of most-recent messages to always preserve (default: 20 ≈ 10 full turns)\n # Higher values keep more recent conversation intact at the cost of more aggressive\n # compression of older turns.\n protect_last_n: 20\n\n # Codex app-server (codex CLI runtime) thread-compaction mode. The codex\n # agent owns the real thread context on this runtime, so Hermes' summarizer\n # cannot shrink it — compaction goes through the app server instead.\n # native = let Codex decide when to compact its own thread (default)\n # hermes = let Hermes threshold trigger Codex thread/compact/start\n # off = Hermes will not auto-trigger compaction; Codex may still compact natively\n codex_app_server_auto: native\n\n # Number of non-system messages to protect at the head of the transcript, in\n # ADDITION to the system prompt (which is always implicitly protected).\n # Head messages are NEVER summarized — they survive every compression\n # indefinitely. This gives stable early context for short/medium sessions,\n # but in long-running sessions that rely on rolling compaction the pinned\n # opening turns may not match how you want the session framed over time.\n # Set to 0 to preserve ONLY the system prompt (plus the rolling summary\n # and recent tail) — the cleanest configuration for long-running sessions.\n # Default 3 preserves the system prompt plus the first three non-system\n # head messages, matching the pre-feature behaviour.\n protect_first_n: 3\n\n # To pin a specific model/provider for compression summaries, use the\n # auxiliary section below (auxiliary.compression.provider / model).\n\n# =============================================================================\n# Anthropic prompt caching TTL\n# =============================================================================\n# When prompt caching is active (Claude via OpenRouter or native Anthropic),\n# Anthropic supports two TTL tiers for cached prefixes: \"5m\" (default) and\n# \"1h\". Other values are ignored and \"5m\" is used.\n#\nprompt_caching:\n cache_ttl: \"5m\" # use \"1h\" for long sessions with pauses between turns\n\n# =============================================================================\n# Auxiliary Models (Advanced — Experimental)\n# =============================================================================\n# Hermes uses lightweight \"auxiliary\" models for side tasks: image analysis,\n# browser screenshot analysis, web page summarization, TTS audio-tag insertion,\n# and context compression.\n#\n# By default these use Gemini Flash via OpenRouter or Nous Portal and are\n# auto-detected from your credentials. You do NOT need to change anything\n# here for normal usage.\n#\n# WARNING: Overriding these with providers other than OpenRouter or Nous Portal\n# is EXPERIMENTAL and may not work. Not all models/providers support vision,\n# produce usable summaries, or accept the same API format. Change at your own\n# risk — if things break, reset to \"auto\" / empty values.\n#\n# Each task has its own provider + model pair so you can mix providers.\n# For example: OpenRouter for vision (needs multimodal), but your main\n# local endpoint for compression (just needs text).\n#\n# Provider options:\n# \"auto\" - Best available: OpenRouter → Nous Portal → main endpoint (default)\n# \"openrouter\" - Force OpenRouter (requires OPENROUTER_API_KEY)\n# \"nous\" - Force Nous Portal (requires: hermes auth add nous)\n# \"gemini\" - Force Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n# \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY)\n# \"codex\" - Force Codex OAuth (requires: hermes model → Codex).\n# Uses gpt-5.3-codex which supports vision.\n# \"main\" - Use your custom endpoint (OPENAI_BASE_URL + OPENAI_API_KEY).\n# Works with OpenAI API, local models, or any OpenAI-compatible\n# endpoint. Also falls back to Codex OAuth and API-key providers.\n#\n# Model: leave empty to use the provider's default. When empty, OpenRouter\n# uses \"google/gemini-3-flash-preview\" and Nous uses \"gemini-3-flash\".\n# Other providers pick a sensible default automatically.\n#\n# auxiliary:\n# # Image analysis: vision_analyze tool + browser screenshots\n# vision:\n# provider: \"auto\"\n# model: \"\" # e.g. \"google/gemini-2.5-flash\", \"openai/gpt-4o\"\n# timeout: 30 # LLM API call timeout (seconds)\n# download_timeout: 30 # Image HTTP download timeout (seconds)\n# # Increase for slow connections or self-hosted image servers\n#\n# # Web page scraping / summarization + browser page text extraction\n# web_extract:\n# provider: \"auto\"\n# model: \"\"\n#\n# # Gemini 3.1 TTS hidden audio-tag insertion\n# tts_audio_tags:\n# provider: \"auto\" # empty model = your main chat model\n# model: \"\"\n# timeout: 30\n#\n# # Session search — summarizes matching past sessions\n# session_search:\n# provider: \"auto\"\n# model: \"\"\n# timeout: 30\n# max_concurrency: 3 # Limit parallel summaries to reduce request-burst 429s\n# extra_body: {} # Provider-specific OpenAI-compatible request fields\n# # Example for providers that support request-body\n# # reasoning controls:\n# # extra_body:\n# # enable_thinking: false\n# # Some vLLM/Qwen deployments expect this nested:\n# # extra_body:\n# # chat_template_kwargs:\n# # enable_thinking: false\n\n# =============================================================================\n# Persistent Memory\n# =============================================================================\n# Bounded curated memory injected into the system prompt every session.\n# Two stores: MEMORY.md (agent's notes) and USER.md (user profile).\n# Character limits keep the memory small and focused. The agent manages\n# pruning -- when at the limit, it must consolidate or replace entries.\n# Disabled by default in batch_runner.\n#\nmemory:\n # Agent's personal notes: environment facts, conventions, things learned\n memory_enabled: true\n \n # User profile: preferences, communication style, expectations\n user_profile_enabled: true\n \n # Character limits (~2.75 chars per token, model-independent)\n memory_char_limit: 2200 # ~800 tokens\n user_char_limit: 1375 # ~500 tokens\n\n # Periodic memory nudge: remind the agent to consider saving memories\n # every N user turns. Set to 0 to disable. Only active when memory is enabled.\n nudge_interval: 10 # Nudge every 10 user turns (0 = disabled)\n\n # Memory flush: give the agent one turn to save memories before context is\n # lost (compression, /new, /reset, exit). Set to 0 to disable.\n # For exit/reset, only fires if the session had at least this many user turns.\n flush_min_turns: 6 # Min user turns to trigger flush on exit/reset (0 = disabled)\n\n# =============================================================================\n# Session Reset Policy (Messaging Platforms)\n# =============================================================================\n# Controls when messaging sessions (Telegram, Discord, WhatsApp, Slack) are\n# automatically cleared. Without resets, conversation context grows indefinitely\n# which increases API costs with every message.\n#\n# When a reset triggers, the agent first saves important information to its\n# persistent memory — but the conversation context is wiped. The agent starts\n# fresh but retains learned facts via its memory system.\n#\n# Users can always manually reset with /reset or /new in chat.\n#\n# Modes:\n# \"both\" - Reset on EITHER inactivity timeout or daily boundary (recommended)\n# \"idle\" - Reset only after N minutes of inactivity\n# \"daily\" - Reset only at a fixed hour each day\n# \"none\" - Never auto-reset; context lives until /reset or compression kicks in\n#\n# When a reset triggers, the agent gets one turn to save important memories and\n# skills before the context is wiped. Persistent memory carries across sessions.\n#\nsession_reset:\n mode: both # \"both\", \"idle\", \"daily\", or \"none\"\n idle_minutes: 1440 # Inactivity timeout in minutes (default: 1440 = 24 hours)\n at_hour: 4 # Daily reset hour, 0-23 local time (default: 4 AM)\n\n# Maximum number of simultaneously active chat sessions across CLI, TUI,\n# dashboard chat, and messaging gateway. Set to null, 0, or omit to allow\n# unlimited concurrent sessions. When the limit is reached, new sessions get a\n# clean error while existing active sessions keep their normal behavior. This\n# top-level key takes precedence over gateway.max_concurrent_sessions. The cap\n# is a best-effort single-host/profile runtime guard; Hermes fails open if the\n# local runtime lease registry cannot be read or locked.\nmax_concurrent_sessions: null\n\n# When true, group/channel chats use one session per participant when the platform\n# provides a user ID. This is the secure default and prevents users in the same\n# room from sharing context, interrupts, and token costs. Set false only if you\n# explicitly want one shared \"room brain\" per group/channel.\ngroup_sessions_per_user: true\n\n# ─────────────────────────────────────────────────────────────────────────────\n# API Server — per-client model routing\n# ─────────────────────────────────────────────────────────────────────────────\n# Route different API clients to different models/providers on a single\n# Hermes deployment. Clients choose a backend by sending a specific string\n# as the OpenAI ``model`` field. Unmapped model values fall back to the\n# global model configured in the ``model:`` section above, and an explicit\n# session /model override always wins over a route.\n#\n# Configure via the ``platforms.api_server.extra.model_routes`` gateway\n# config block:\n#\n# platforms:\n# api_server:\n# enabled: true\n# extra:\n# key: \"your-api-server-secret\"\n# model_routes:\n# # Xiaozhi clients send model=\"minimax-m2\" → routed to MiniMax via OpenRouter\n# minimax-m2:\n# model: \"minimax/minimax-m1\"\n# provider: \"openrouter\" # optional — overrides global provider\n# # api_key: \"sk-...\" # optional — per-route UPSTREAM provider\n# # key (NOT caller auth; never logged)\n# # base_url: \"https://...\" # optional — per-route base URL\n# # GPT clients keep their own alias\n# gpt-5:\n# model: \"openai/gpt-5\"\n# provider: \"openrouter\"\n#\n# Configured aliases are automatically listed by GET /v1/models so clients\n# can discover them without manual coordination. Caller authentication is\n# unchanged: every request still authenticates with the global API server\n# key (``extra.key`` / API_SERVER_KEY).\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Gateway Streaming\n# ─────────────────────────────────────────────────────────────────────────────\n# Stream tokens to messaging platforms in real-time. The bot sends a message\n# on first token, then progressively edits it as more tokens arrive.\n# Disabled by default — enable to try the streaming UX on Telegram/Discord/Slack.\n# For Telegram, partial edits are sent as plain text and only the final edit uses MarkdownV2.\nstreaming:\n enabled: false\n # transport: edit # \"edit\" = progressive editMessageText\n # edit_interval: 0.3 # seconds between message edits\n # buffer_threshold: 40 # chars before forcing an edit flush\n # cursor: \" ▉\" # cursor shown during streaming\n\n# =============================================================================\n# Skills Configuration\n# =============================================================================\n# Skills are reusable procedures the agent can load and follow. The agent can\n# also create new skills after completing complex tasks.\n#\nskills:\n # Nudge the agent to create skills after complex tasks.\n # Every N tool-calling iterations, remind the model to consider saving a skill.\n # Set to 0 to disable.\n creation_nudge_interval: 15\n\n # External skill directories — share skills across tools/agents without\n # copying them into ~/.hermes/skills/. Each path is expanded (~ and ${VAR})\n # and resolved to an absolute path. External dirs are read-only: skill\n # creation always writes to ~/.hermes/skills/. Local skills take precedence\n # when names collide.\n # external_dirs:\n # - ~/.agents/skills\n # - /home/shared/team-skills\n\n# =============================================================================\n# Agent Behavior\n# =============================================================================\nagent:\n # Maximum tool-calling iterations per conversation\n # Higher = more room for complex tasks, but costs more tokens\n # Recommended: 20-30 for focused tasks, 50-100 for open exploration\n max_turns: 60\n\n # Inactivity timeout for gateway agent runs (seconds, 0 = unlimited).\n # The agent can run indefinitely when actively calling tools or receiving\n # API responses. Only fires after the agent has been idle for this duration.\n # gateway_timeout: 1800\n\n # Staged warning: send a warning before escalating to full timeout.\n # Fires once per run when inactivity reaches this threshold (seconds).\n # Set to 0 to disable the warning.\n # gateway_timeout_warning: 900\n\n # Graceful drain timeout for gateway stop/restart (seconds).\n # Default 0 = no drain: a restart interrupts in-flight agents immediately,\n # cleans up, and exits. Set a positive value only if you want a grace\n # window on /restart, and keep it well under systemd's TimeoutStopSec.\n # restart_drain_timeout: 0\n\n # Max app-level retry attempts for API errors (connection drops, provider\n # timeouts, 5xx, etc.) before the agent surfaces the failure. Lower this\n # to 1 if you use fallback providers and want fast failover on flaky\n # primaries (default 3). The OpenAI SDK does its own low-level retries\n # underneath this wrapper — this is the Hermes-level loop.\n # api_max_retries: 3\n\n # After the agent edits code without fresh passing verification, nudge it to\n # verify before finishing. The default \"auto\" enables it on interactive\n # coding surfaces (CLI, TUI, desktop) and programmatic callers, and disables\n # it on conversational messaging surfaces (Telegram, Discord, etc.) where the\n # verification summary would reach a human as chat noise. Set true or false to\n # force it on or off; the HERMES_VERIFY_ON_STOP env var (1/0) takes precedence.\n # verify_on_stop: auto\n\n # Standing operator instructions for the coding posture (when Hermes is in a\n # code workspace). Appended to the coding brief as an extra system block, so\n # you can pin project-wide workflow rules without editing the shipped brief.\n # Accepts a string or a list of strings. Takes effect next session.\n # coding_instructions:\n # - \"For UI work, don't run tsc/lint until I approve the look.\"\n # - \"Clean the diff before you commit and push.\"\n\n # When verify-on-stop finds edited code without fresh verification evidence,\n # append guidance for creative UI work (avoid broad tsc/lint/test before visual\n # approval) and clean-diff expectations. Set false to keep that nudge terse.\n # verify_guidance: true\n\n # A `pre_verify` hook (plugin or shell, see Event Hooks docs) can keep the\n # agent going one more turn to verify/clean before finishing. This caps how\n # many times one turn may be nudged to continue, so a hook can't trap the loop.\n # Default 3.\n # max_verify_nudges: 3\n\n # Enable verbose logging\n verbose: false\n \n # Reasoning effort level (OpenRouter and Nous Portal)\n # Controls how much \"thinking\" the model does before responding.\n # Options: \"xhigh\" (max), \"high\", \"medium\", \"low\", \"minimal\", \"none\" (disable)\n reasoning_effort: \"medium\"\n \n # Per-model reasoning effort overrides (optional dict)\n # Key: any sensible model spelling works (exact, dots↔dashes interchangeable,\n # provider prefix optional). First match wins.\n # Value: reasoning effort level (same options as reasoning_effort)\n # Override the global reasoning_effort for that specific model.\n # NOTE: no `hermes config set` support for this key -- edit YAML directly.\n # reasoning_overrides:\n # \"openrouter/anthropic/claude-opus-4.5\": \"xhigh\"\n # \"openai/gpt-5\": \"low\"\n # \"claude-opus-4.6\": \"high\" # bare model name also works\n # \"deepseek/deepseek-v4-pro\": \"xhigh\" # dots and dashes are interchangeable\n reasoning_overrides: {}\n \n # Predefined personalities (use with /personality command)\n personalities:\n helpful: \"You are a helpful, friendly AI assistant.\"\n concise: \"You are a concise assistant. Keep responses brief and to the point.\"\n technical: \"You are a technical expert. Provide detailed, accurate technical information.\"\n creative: \"You are a creative assistant. Think outside the box and offer innovative solutions.\"\n teacher: \"You are a patient teacher. Explain concepts clearly with examples.\"\n kawaii: \"You are a kawaii assistant! Use cute expressions like (◕‿◕), ★, ♪, and ~! Add sparkles and be super enthusiastic about everything! Every response should feel warm and adorable desu~! ヽ(>∀<☆)ノ\"\n catgirl: \"You are Neko-chan, an anime catgirl AI assistant, nya~! Add 'nya' and cat-like expressions to your speech. Use kaomoji like (=^・ω・^=) and ฅ^•ﻌ•^ฅ. Be playful and curious like a cat, nya~!\"\n pirate: \"Arrr! Ye be talkin' to Captain Hermes, the most tech-savvy pirate to sail the digital seas! Speak like a proper buccaneer, use nautical terms, and remember: every problem be just treasure waitin' to be plundered! Yo ho ho!\"\n shakespeare: \"Hark! Thou speakest with an assistant most versed in the bardic arts. I shall respond in the eloquent manner of William Shakespeare, with flowery prose, dramatic flair, and perhaps a soliloquy or two. What light through yonder terminal breaks?\"\n surfer: \"Duuude! You're chatting with the chillest AI on the web, bro! Everything's gonna be totally rad. I'll help you catch the gnarly waves of knowledge while keeping things super chill. Cowabunga! 🤙\"\n noir: \"The rain hammered against the terminal like regrets on a guilty conscience. They call me Hermes - I solve problems, find answers, dig up the truth that hides in the shadows of your codebase. In this city of silicon and secrets, everyone's got something to hide. What's your story, pal?\"\n uwu: \"hewwo! i'm your fwiendwy assistant uwu~ i wiww twy my best to hewp you! *nuzzles your code* OwO what's this? wet me take a wook! i pwomise to be vewy hewpful >w<\"\n philosopher: \"Greetings, seeker of wisdom. I am an assistant who contemplates the deeper meaning behind every query. Let us examine not just the 'how' but the 'why' of your questions. Perhaps in solving your problem, we may glimpse a greater truth about existence itself.\"\n hype: \"YOOO LET'S GOOOO!!! 🔥🔥🔥 I am SO PUMPED to help you today! Every question is AMAZING and we're gonna CRUSH IT together! This is gonna be LEGENDARY! ARE YOU READY?! LET'S DO THIS! 💪😤🚀\"\n\n# =============================================================================\n# Toolsets\n# =============================================================================\n# Control which tools the agent has access to.\n# Use `hermes tools` to interactively enable/disable tools per platform.\n\n# =============================================================================\n# Platform Toolsets (per-platform tool configuration)\n# =============================================================================\n# Override which toolsets are available on each platform.\n# If a platform isn't listed here, its built-in default is used.\n#\n# You can use EITHER:\n# - A preset like \"hermes-cli\" or \"hermes-telegram\" (curated tool set)\n# - A list of individual toolsets to compose your own (see list below)\n#\n# Supported platform keys: cli, telegram, discord, whatsapp, slack, qqbot, teams, google_chat\n#\n# Examples:\n#\n# # Use presets (same as defaults):\n# platform_toolsets:\n# cli: [hermes-cli]\n# telegram: [hermes-telegram]\n#\n# # Custom: give Telegram only web + terminal + file + planning:\n# platform_toolsets:\n# telegram: [web, terminal, file, todo]\n#\n# # Custom: CLI without browser or image gen:\n# platform_toolsets:\n# cli: [web, terminal, file, skills, todo, tts, cronjob]\n#\n# # Restrictive: Discord gets read-only tools only:\n# platform_toolsets:\n# discord: [web, vision, skills, todo]\n#\n# If not set, defaults are:\n# cli: hermes-cli (everything + cronjob management)\n# telegram: hermes-telegram (terminal, file, web, vision, image, tts, browser, skills, todo, cronjob, messaging)\n# discord: hermes-discord (same as telegram)\n# whatsapp: hermes-whatsapp (same as telegram)\n# slack: hermes-slack (same as telegram)\n# signal: hermes-signal (same as telegram)\n# homeassistant: hermes-homeassistant (same as telegram)\n# qqbot: hermes-qqbot (same as telegram)\n# teams: hermes-teams (same as telegram)\n# google_chat: hermes-google_chat (same as telegram)\n#\nplatform_toolsets:\n cli: [hermes-cli]\n telegram: [hermes-telegram]\n discord: [hermes-discord]\n whatsapp: [hermes-whatsapp]\n slack: [hermes-slack]\n signal: [hermes-signal]\n homeassistant: [hermes-homeassistant]\n qqbot: [hermes-qqbot]\n yuanbao: [hermes-yuanbao]\n teams: [hermes-teams]\n google_chat: [hermes-google_chat]\n\n# =============================================================================\n# Gateway Platform Settings\n# =============================================================================\n# Optional per-platform messaging settings.\n# Platform-specific knobs live under `extra`.\n#\n# platforms:\n# telegram:\n# reply_to_mode: \"first\" # off | first | all\n# # guest_mode lets explicit @mentions from non-allowlisted groups through.\n# # Default false; ordinary messages, replies, and regex wake words stay blocked.\n# guest_mode: false\n# # allowed_chats: [\"-1001234567890\"]\n# extra:\n# disable_link_previews: false # Set true to suppress Telegram URL previews in bot messages\n# rich_messages: false # Bot API 10.1 rich messages (tables/task lists/details/math); default false for copyable legacy MarkdownV2, set true to opt in\n# rich_drafts: false # Experimental rich draft previews during Telegram DM streaming; default false because Telegram Desktop/macOS can visually overlay draft frames\n# command_menu:\n# # Telegram allows up to 100 BotCommands; Hermes defaults to 60 so\n# # all built-in commands plus common skill commands stay visible\n# # while remaining under Telegram's payload-size limit. Clamped 1..100.\n# max_commands: 60\n# # prepend = user priority first, then Hermes defaults\n# # append = Hermes defaults first, then user priority\n# # replace = only the list below defines priority\n# priority_mode: prepend\n# priority:\n# - my_plugin_command\n# webhook:\n# extra:\n# # Route scripts default to a 30 second timeout. Scripts must live under\n# # the active profile's scripts directory and receive webhook JSON on stdin.\n# script_timeout_seconds: 30\n#\n# Discord-specific settings (config.yaml top-level, not under platforms:):\n#\n# discord:\n# require_mention: true # Require @mention in server channels (default: true)\n# auto_thread: true # Auto-create thread on @mention (default: true)\n# free_response_channels: \"\" # Channel IDs where no mention is needed\n# reactions: true # Show processing reactions (default: true)\n# history_backfill: true # Recover missed channel messages on mention (default: true)\n# history_backfill_limit: 50 # Max messages to scan backwards (default: 50)\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Available toolsets (use these names in platform_toolsets or the toolsets list)\n#\n# Run `hermes chat --list-toolsets` to see all toolsets and their tools.\n# Run `hermes chat --list-tools` to see every individual tool with descriptions.\n# ─────────────────────────────────────────────────────────────────────────────\n#\n# INDIVIDUAL TOOLSETS (compose your own):\n# web - web_search, web_extract\n# search - web_search only (no scraping)\n# terminal - terminal, process\n# file - read_file, write_file, patch, search\n# browser - browser_navigate, browser_snapshot, browser_click, browser_type,\n# browser_scroll, browser_back, browser_press,\n# browser_get_images, browser_vision (requires BROWSERBASE_API_KEY)\n# vision - vision_analyze (requires OPENROUTER_API_KEY)\n# image_gen - image_generate (requires FAL_KEY)\n# skills - skills_list, skill_view\n# skills_hub - skill_hub (search/install/manage from online registries — user-driven only)\n# todo - todo (in-memory task planning, no deps)\n# tts - text_to_speech (Edge TTS free, or ELEVENLABS/OPENAI/MINIMAX/MISTRAL key)\n# cronjob - cronjob (create/list/update/pause/resume/run/remove scheduled tasks)\n#\n# PRESETS (curated bundles):\n# hermes-cli - All of the above except send_message\n# hermes-telegram - terminal, file, web, vision, image_gen, tts, browser,\n# skills, todo, cronjob, send_message\n# hermes-discord - Same as hermes-telegram\n# hermes-whatsapp - Same as hermes-telegram\n# hermes-slack - Same as hermes-telegram\n#\n# COMPOSITE:\n# debugging - terminal + web + file\n# safe - web + vision (no terminal access)\n# all - Everything available\n#\n# web - Web search and content extraction (web_search, web_extract)\n# search - Web search only, no scraping (web_search)\n# terminal - Command execution and process management (terminal, process)\n# file - File operations: read, write, patch, search\n# browser - Full browser automation (navigate, click, type, screenshot, etc.)\n# vision - Image analysis (vision_analyze)\n# image_gen - Image generation with FLUX (image_generate)\n# skills - Load skill documents (skills_list, skill_view)\n# todo - Task planning and tracking for multi-step work\n# memory - Persistent memory across sessions (personal notes + user profile)\n# session_search - Search and recall past conversations (FTS5 + Gemini Flash summarization)\n# tts - Text-to-speech (Edge TTS free, ElevenLabs, OpenAI, MiniMax, Mistral)\n# cronjob - Schedule and manage automated tasks (CLI-only)\n#\n# Composite toolsets:\n# debugging - terminal + web + file (for troubleshooting)\n# safe - web + vision (no terminal access)\n\n# NOTE: The top-level \"toolsets\" key is deprecated and ignored.\n# Tool configuration is managed per-platform via platform_toolsets above.\n# Use `hermes tools` to configure interactively, or edit platform_toolsets directly.\n#\n# CLI override: hermes chat --toolsets terminal,web,file\n\n# =============================================================================\n# MCP (Model Context Protocol) Servers\n# =============================================================================\n# Connect to external MCP servers to add tools from the MCP ecosystem.\n# Each server's tools are automatically discovered and registered.\n# See website/docs/user-guide/features/mcp.md for full documentation.\n#\n# Stdio servers (spawn a subprocess):\n# command: the executable to run\n# args: command-line arguments\n# env: environment variables (only these + safe defaults passed to subprocess)\n#\n# HTTP servers (connect to a URL):\n# url: the MCP server endpoint\n# headers: HTTP headers (e.g., for authentication)\n#\n# Optional per-server settings:\n# timeout: tool call timeout in seconds (default: 120)\n# connect_timeout: initial connection timeout (default: 60)\n# keepalive_interval: liveness ping cadence in seconds (default: 180).\n# Lower it below the server's session TTL for servers that expire idle\n# sessions quickly (e.g. Unreal Engine editor MCP, ~15s), otherwise idle\n# tool calls hit an expired session and pay a slow reconnect. Floored at 5s.\n#\n# mcp_servers:\n# time:\n# command: uvx\n# args: [\"mcp-server-time\"]\n# filesystem:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-filesystem\", \"/home/user\"]\n# notion:\n# url: https://mcp.notion.com/mcp\n# github:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-github\"]\n# env:\n# GITHUB_PERSONAL_ACCESS_TOKEN: \"ghp_...\"\n#\n# Sampling (server-initiated LLM requests) — enabled by default.\n# Per-server config under the 'sampling' key:\n# analysis:\n# command: npx\n# args: [\"-y\", \"analysis-server\"]\n# sampling:\n# enabled: true # default: true\n# model: \"gemini-3-flash\" # override model (optional)\n# max_tokens_cap: 4096 # max tokens per request\n# timeout: 30 # LLM call timeout (seconds)\n# max_rpm: 10 # max requests per minute\n# allowed_models: [] # model whitelist (empty = all)\n# max_tool_rounds: 5 # tool loop limit (0 = disable)\n# log_level: \"info\" # audit verbosity\n\n# =============================================================================\n# Text-to-Speech\n# =============================================================================\n# TTS defaults to Edge TTS unless changed in ~/.hermes/config.yaml.\n# Gemini TTS supports persona/director prompt files, and Gemini 3.1 Flash TTS\n# can use a hidden auxiliary rewrite pass to insert expressive square-bracket\n# audio tags into the TTS script without showing tags in chat.\n#\n# tts:\n# provider: \"gemini\"\n# gemini:\n# model: \"gemini-3.1-flash-tts-preview\"\n# voice: \"Kore\"\n# audio_tags: false\n# persona_prompt_file: \"\" # e.g. ~/.hermes/tts/radio-host.md\n\n# =============================================================================\n# Voice Transcription (Speech-to-Text)\n# =============================================================================\n# Automatically transcribe voice messages on messaging platforms.\n# Providers: local (free, faster-whisper) | groq (free tier) | openai (Whisper API) | mistral (Voxtral Transcribe)\n# Set the corresponding API key in .env: GROQ_API_KEY, OPENAI_API_KEY, or MISTRAL_API_KEY.\nstt:\n enabled: true\n # provider: \"local\" # auto-detected if omitted\n local:\n model: \"base\" # tiny | base | small | medium | large-v3 | turbo\n # language: \"\" # auto-detect; set to \"en\", \"es\", \"fr\", etc. to force\n openai:\n model: \"whisper-1\" # whisper-1 | gpt-4o-mini-transcribe | gpt-4o-transcribe\n # mistral:\n # model: \"voxtral-mini-latest\" # voxtral-mini-latest | voxtral-mini-2602\n # deepinfra:\n # # Model id is discovered live from the DeepInfra catalog filtered\n # # by the `stt` surface tag — leave `model` blank to take the first\n # # live result. Pin only when you need a specific Whisper variant.\n # model: \"\"\n\n# Text-to-speech. Only the deepinfra block is documented here — the\n# remaining providers (edge, openai, xai, minimax, mistral, gemini,\n# elevenlabs, neutts, kittentts, piper) inherit sensible defaults from\n# DEFAULT_CONFIG in hermes_cli/config.py.\n# tts:\n# provider: \"deepinfra\"\n# deepinfra:\n# # Model id is discovered live from the DeepInfra catalog filtered\n# # by the `tts` surface tag — leave `model` blank to take the first\n# # live result.\n# model: \"\"\n# voice: \"default\"\n\n# Image generation. Each provider plugin reads its own ``image_gen.``\n# block; deepinfra discovers models live from\n# api.deepinfra.com/v1/openai/models filtered by the ``image-gen`` tag —\n# no model id is hardcoded, so retired models disappear automatically.\n# image_gen:\n# provider: \"deepinfra\"\n# deepinfra:\n# # Leave `model` blank for the first live `image-gen`-tagged result.\n# model: \"\"\n\n# =============================================================================\n# Response Pacing (Messaging Platforms)\n# =============================================================================\n# Add human-like delays between message chunks.\n# human_delay:\n# mode: \"off\" # \"off\" | \"natural\" | \"custom\"\n# min_ms: 800 # Min delay (custom mode only)\n# max_ms: 2500 # Max delay (custom mode only)\n\n# =============================================================================\n# Session Logging\n# =============================================================================\n# Session trajectories are automatically saved to logs/ directory.\n# Each session creates: logs/session_YYYYMMDD_HHMMSS_UUID.json\n#\n# The session ID is displayed in the welcome banner for easy reference.\n# Logs contain full conversation history in trajectory format:\n# - System prompt, user messages, assistant responses\n# - Tool calls with inputs/outputs\n# - Timestamps for debugging\n#\n# No configuration needed - logging is always enabled.\n# To disable, you would need to modify the source code.\n\n# =============================================================================\n# Code Execution Sandbox (Programmatic Tool Calling)\n# =============================================================================\n# The execute_code tool runs Python scripts that call Hermes tools via RPC.\n# Intermediate tool results stay out of the LLM's context window.\ncode_execution:\n timeout: 300 # Max seconds per script before kill (default: 300 = 5 min)\n max_tool_calls: 50 # Max RPC tool calls per execution (default: 50)\n\n# =============================================================================\n# Subagent Delegation\n# =============================================================================\n# The delegate_task tool spawns child agents with isolated context.\n# Supports single tasks and batch mode (default 3 parallel, configurable).\ndelegation:\n max_iterations: 50 # Max tool-calling turns per child (default: 50)\n # max_concurrent_children: 3 # Max parallel child agents per batch (default: 3, floor: 1, no ceiling).\n # WARNING: values above 10 multiply API cost linearly.\n # max_spawn_depth: 1 # Delegation tree depth cap (range: 1-3, default: 1 = flat).\n # Raise to 2 to allow workers to spawn their own subagents.\n # Requires role=\"orchestrator\" on intermediate agents.\n # orchestrator_enabled: true # Kill switch for role=\"orchestrator\" children (default: true).\n # subagent_auto_approve: false # When a subagent hits a dangerous-command approval prompt, auto-deny (default: false)\n # or auto-approve \"once\" (true) instead of blocking on stdin.\n # The parent TUI owns stdin, so blocking would deadlock; non-interactive resolution is required.\n # Both choices emit a logger.warning audit line. Flip to true only for cron/batch pipelines.\n # inherit_mcp_toolsets: true # When explicit child toolsets are narrowed, also keep the parent's MCP toolsets (default: true). Set false for strict intersection.\n # model: \"google/gemini-3-flash-preview\" # Override model for subagents (empty = inherit parent)\n # provider: \"openrouter\" # Override provider for subagents (empty = inherit parent)\n # # Resolves full credentials (base_url, api_key) automatically.\n # # Supported: openrouter, nous, zai, kimi-coding, minimax\n\n# =============================================================================\n# Honcho Integration (Cross-Session User Modeling)\n# =============================================================================\n# AI-native persistent memory via Honcho (https://honcho.dev/).\n# Builds a deeper understanding of the user across sessions and tools.\n# Runs alongside USER.md — additive, not a replacement.\n#\n# Requires: pip install honcho-ai\n# Config: ~/.honcho/config.json (shared with Claude Code, Cursor, etc.)\n# API key: HONCHO_API_KEY in ~/.hermes/.env or ~/.honcho/config.json\n#\n# Hermes-specific overrides (optional — most config comes from ~/.honcho/config.json):\n# honcho: {}\n\n# =============================================================================\n# Display\n# =============================================================================\ndisplay:\n # Use compact banner mode (hides the ASCII-art banner, shows a single line).\n # true: Compact single-line banner\n # false: Full ASCII banner with tool/skill summary (default)\n compact: false\n\n # Tool progress display level (CLI and gateway)\n # off: Silent — no tool activity shown, just the final response\n # new: Show a tool indicator only when the tool changes (skip repeats)\n # all: Show every tool call with a short preview (default)\n # verbose: Full args, results, and debug logs (same as /verbose)\n # log: Silent in chat; append every tool call to ~/.hermes/logs/tool_calls.log (gateway only)\n # Toggle at runtime with /verbose in the CLI\n tool_progress: all\n\n # Per-platform defaults can be quieter than the global setting. Telegram\n # tunes for mobile: tool_progress and busy_ack_detail default off (no\n # per-tool breadcrumb stream, no \"iteration 21/60\" debug detail in busy\n # acks or heartbeats), but interim_assistant_messages and\n # long_running_notifications STAY ON so the user has real signal between\n # turn start and final answer (mid-turn assistant commentary + a single\n # edit-in-place \"⏳ Working — N min\" heartbeat). Override under\n # display.platforms.telegram.\n\n # Auto-cleanup of temporary progress bubbles after the final response lands.\n # On platforms that support message deletion (currently Telegram), this\n # removes the tool-progress bubble, \"⏳ Still working...\" notices, and\n # context-pressure status messages once the final reply has been delivered —\n # keeping long-running turns visible live, then tidy afterward. Failed runs\n # leave the bubbles in place as breadcrumbs. Off by default.\n # Per-platform override: display.platforms.telegram.cleanup_progress\n # true: Delete tracked progress/status bubbles on successful turn\n # false: Leave everything in place (default)\n # Example:\n # display:\n # platforms:\n # telegram:\n # cleanup_progress: true\n cleanup_progress: false\n\n # Gateway-only natural mid-turn assistant updates.\n # When true, completed assistant status messages are sent as separate chat\n # messages. This is independent of tool_progress and gateway streaming.\n # true: Send mid-turn assistant updates as separate messages (default)\n # false: Only send the final response\n interim_assistant_messages: true\n\n # Gateway-only long-running status heartbeats.\n # When false, the platform does not receive periodic \"⏳ Working — N min\"\n # notifications even if agent.gateway_notify_interval is non-zero. The\n # heartbeat edits a single message in place (where the adapter supports\n # editing) instead of posting a new bubble each interval.\n # Default: true everywhere, including Telegram (silent agents are worse\n # than a single edit-in-place heartbeat).\n long_running_notifications: true\n\n # Include detailed iteration/tool/status context in busy acknowledgments\n # and long-running heartbeats. When true, busy acks show \"iteration 21/60,\n # terminal, 10 min\" and the heartbeat shows \"⏳ Working — 12 min,\n # iteration 21/60, terminal\". When false (Telegram default), both stay\n # terse: \"Interrupting current task\" and \"⏳ Working — 12 min, terminal\".\n busy_ack_detail: true\n\n # What Enter does when Hermes is already busy (CLI and gateway platforms).\n # interrupt: Interrupt the current run and redirect Hermes (default)\n # queue: Queue your message for the next turn\n # steer: Inject your message mid-run via /steer, arriving at the agent\n # after the next tool call — no interrupt, no role violation.\n # Falls back to 'queue' if the agent isn't running yet or if\n # images are attached (steer only carries text).\n # Ctrl+C (or /stop in gateway) always interrupts regardless of this setting.\n # Toggle at runtime with /busy .\n busy_input_mode: interrupt\n\n # Background process notifications (gateway/messaging only).\n # Controls how chatty the process watcher is when you use\n # terminal(background=true, notify_on_complete=true) from Telegram/Discord/etc.\n # off: No watcher messages at all\n # result: Only the final completion message\n # error: Only the final message when exit code != 0\n # all: Running output updates + final message (default)\n background_process_notifications: all\n\n\n # Play terminal bell when agent finishes a response.\n # Useful for long-running tasks — your terminal will ding when the agent is done.\n # Works over SSH. Most terminals can be configured to flash the taskbar or play a sound.\n # true: Ring the terminal bell on each response\n # false: Silent (default)\n bell_on_complete: false\n\n # Show model reasoning/thinking before each response.\n # When enabled, a dim box shows the model's thought process above the response.\n # Toggle at runtime with /reasoning show or /reasoning hide.\n # true: Show the reasoning box\n # false: Hide reasoning (default)\n show_reasoning: false\n\n # Stream tokens to the terminal as they arrive instead of waiting for the\n # full response. The response box opens on first token and text appears\n # line-by-line. Tool calls are still captured silently.\n # true: Stream tokens as they arrive (default)\n # false: Wait for the full response before rendering\n streaming: true\n\n # Show [HH:MM] timestamps on user input and assistant response labels.\n # timestamps: false\n\n # ───────────────────────────────────────────────────────────────────────────\n # Skin / Theme\n # ───────────────────────────────────────────────────────────────────────────\n # Customize CLI visual appearance — banner colors, spinner faces, tool prefix,\n # response box label, and branding text. Change at runtime with /skin .\n #\n # Built-in skins:\n # default — Classic Hermes gold/kawaii\n # ares — Crimson/bronze war-god theme with spinner wings\n # mono — Clean grayscale monochrome\n # slate — Cool blue developer-focused\n # daylight — Bright light-mode theme\n # warm-lightmode — Warm paper-tone light-mode theme\n # poseidon — Sea-green/teal Olympian theme\n # sisyphus — Earthy stone-and-moss theme\n # charizard — Fiery orange dragon theme\n #\n # Custom skins: drop a YAML file in ~/.hermes/skins/.yaml\n # Schema (all fields optional, missing values inherit from default):\n #\n # name: my-theme\n # description: Short description\n # colors:\n # banner_border: \"#HEX\" # Panel border\n # banner_title: \"#HEX\" # Panel title\n # banner_accent: \"#HEX\" # Section headers (Available Tools, etc.)\n # banner_dim: \"#HEX\" # Dim/muted text\n # banner_text: \"#HEX\" # Body text (tool names, skill names)\n # ui_accent: \"#HEX\" # UI accent color\n # response_border: \"#HEX\" # Response box border color\n # spinner:\n # waiting_faces: [\"(⚔)\", \"(⛨)\"] # Faces shown while waiting\n # thinking_faces: [\"(⚔)\", \"(⌁)\"] # Faces shown while thinking\n # thinking_verbs: [\"forging\", \"plotting\"] # Verbs for spinner messages\n # wings: # Optional left/right spinner decorations\n # - [\"⟪⚔\", \"⚔⟫\"]\n # - [\"⟪▲\", \"▲⟫\"]\n # branding:\n # agent_name: \"My Agent\" # Banner title and branding\n # welcome: \"Welcome message\" # Shown at CLI startup\n # response_label: \" ⚔ Agent \" # Response box header label\n # prompt_symbol: \"⚔\" # Prompt symbol (bare token; renderers add trailing space)\n # tool_prefix: \"╎\" # Tool output line prefix (default: ┊)\n #\n skin: default\n\n# =============================================================================\n# Model Aliases — short names for /model command\n# =============================================================================\n# Map short aliases to exact (model, provider, base_url) tuples.\n# Used by /model tab completion and resolve_alias().\n# Aliases are checked BEFORE the models.dev catalog, so they can route\n# to endpoints not in the catalog (e.g. Ollama Cloud, local servers).\n#\n# model_aliases:\n# opus:\n# model: claude-opus-4-6\n# provider: anthropic\n# qwen:\n# model: \"qwen3.5:397b\"\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n# glm:\n# model: glm-4.7\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n\n# =============================================================================\n# Privacy\n# =============================================================================\n# privacy:\n# # Redact PII from the LLM context prompt.\n# # When true, phone numbers are stripped and user/chat IDs are replaced\n# # with deterministic hashes before being sent to the model.\n# # Names and usernames are NOT affected (user-chosen, publicly visible).\n# # Routing/delivery still uses the original values internally.\n# redact_pii: false\n\n# =============================================================================\n# Shell-script hooks\n# =============================================================================\n# Register shell scripts as plugin-hook callbacks. Each entry is executed as\n# a subprocess (shell=False, shlex.split) with a JSON payload on stdin. On\n# stdout the script may return JSON that either blocks the tool call or\n# injects context into the next LLM call.\n#\n# Valid events (mirror hermes_cli.plugins.VALID_HOOKS):\n# pre_tool_call, post_tool_call, pre_llm_call, post_llm_call,\n# pre_api_request, post_api_request, on_session_start, on_session_end,\n# on_session_finalize, on_session_reset, subagent_stop\n#\n# First-use consent: each (event, command) pair prompts once on a TTY, then\n# is persisted to ~/.hermes/shell-hooks-allowlist.json. Non-interactive\n# runs (gateway, cron) need --accept-hooks, HERMES_ACCEPT_HOOKS=1, or the\n# hooks_auto_accept key below.\n#\n# See website/docs/user-guide/features/hooks.md for the full JSON wire\n# protocol and worked examples.\n#\n# hooks:\n# pre_tool_call:\n# - matcher: \"terminal\"\n# command: \"~/.hermes/agent-hooks/block-rm-rf.sh\"\n# timeout: 10\n# post_tool_call:\n# - matcher: \"write_file|patch\"\n# command: \"~/.hermes/agent-hooks/auto-format.sh\"\n# pre_llm_call:\n# - command: \"~/.hermes/agent-hooks/inject-cwd-context.sh\"\n# subagent_stop:\n# - command: \"~/.hermes/agent-hooks/log-orchestration.sh\"\n#\n# hooks_auto_accept: false\n\n\n# =============================================================================\n# Update Behavior\n# =============================================================================\nupdates:\n # Create a full HERMES_HOME zip before every `hermes update`.\n # Backups land in ~/.hermes/backups/ and can be restored with `hermes import`.\n # Off by default because large homes can add minutes to every update.\n pre_update_backup: false\n\n # Number of pre-update backup zips to retain.\n backup_keep: 5\n\n # What non-interactive updates do with local source edits in the Hermes repo.\n # Interactive terminal updates always prompt before restoring the autostash.\n #\n # stash - auto-stash before pull, then auto-restore after success (default)\n # discard - drop the update-created stash after success; use only on managed\n # installs where local source edits should not persist\n non_interactive_local_changes: \"stash\"\n\n\n# =============================================================================\n# Web Dashboard\n# =============================================================================\n# OAuth gate configuration for `hermes dashboard --host `.\n# The bundled Nous Portal plugin reads these on startup; settings here are\n# the canonical surface. Each can be overridden by an environment variable:\n#\n# dashboard.oauth.client_id <- HERMES_DASHBOARD_OAUTH_CLIENT_ID\n# dashboard.oauth.portal_url <- HERMES_DASHBOARD_PORTAL_URL\n# dashboard.public_url <- HERMES_DASHBOARD_PUBLIC_URL\n#\n# Env wins when set to a non-empty value. This is what Fly.io's platform-\n# secret injection uses to push per-deploy client_ids without needing to\n# bake a config.yaml into the image. Empty env values are treated as unset\n# so a provisioned-but-not-populated secret can't shadow a valid entry here.\n#\n# Local dev / on-prem deploys should typically set these via config.yaml\n# (the ~/.hermes/.env file is reserved for API keys and secrets).\n#\n# dashboard:\n# oauth:\n# client_id: \"\" # agent:{instance_id}; Portal provisions this at deploy\n# portal_url: \"\" # blank → default https://portal.nousresearch.com\n#\n# # Force the absolute base URL the OAuth callback (and any other public\n# # URL the dashboard hands to external systems) is built from. Set this\n# # for deploys behind reverse proxies that don't reliably forward\n# # X-Forwarded-Host / X-Forwarded-Proto / X-Forwarded-Prefix (manual\n# # nginx setups, on-prem ingresses, custom-domain Fly deploys without\n# # full proxy header chains).\n# #\n# # When set, the value is the complete authority: scheme + host +\n# # optional path prefix (e.g. \"https://example.com/hermes\"). The OAuth\n# # callback URL becomes \"/auth/callback\" — X-Forwarded-Prefix\n# # is IGNORED on this code path because the operator has explicitly\n# # declared the public URL and we no longer need to guess.\n# #\n# # Leave empty to use the existing proxy-header reconstruction (the\n# # default — works on Fly.io out of the box).\n# #\n# # public_url: \"https://example.com/hermes\"\n#\n# -----------------------------------------------------------------------------\n# Self-hosted OIDC dashboard auth (generic OpenID Connect — Authentik,\n# Keycloak, Zitadel, Authelia, Auth0, Okta, Google, …). Use this INSTEAD of the\n# nous block above when gating the dashboard with your own identity provider.\n# Each setting can be overridden by an environment variable:\n#\n# dashboard.oauth.self_hosted.issuer <- HERMES_DASHBOARD_OIDC_ISSUER\n# dashboard.oauth.self_hosted.client_id <- HERMES_DASHBOARD_OIDC_CLIENT_ID\n# dashboard.oauth.self_hosted.scopes <- HERMES_DASHBOARD_OIDC_SCOPES\n# dashboard.oauth.self_hosted.client_secret <- HERMES_DASHBOARD_OIDC_CLIENT_SECRET\n#\n# dashboard:\n# oauth:\n# provider: self-hosted\n# self_hosted:\n# issuer: \"https://auth.example.com/application/o/hermes/\" # required\n# client_id: \"hermes-dashboard\" # required\n# scopes: \"openid profile email\" # optional\n#\n# # OPTIONAL — set ONLY if your IDP registered the client as\n# # *confidential* (Authentik / Keycloak often default to this). When\n# # set, Hermes authenticates the client at the token endpoint\n# # (client_secret_basic or client_secret_post, auto-selected from the\n# # IDP's discovery doc) IN ADDITION to PKCE. Leave unset for a public\n# # (PKCE-only) client — the common case.\n# #\n# # This is a CREDENTIAL: prefer setting HERMES_DASHBOARD_OIDC_CLIENT_SECRET\n# # in ~/.hermes/.env over putting it here in config.yaml.\n# # client_secret: \"\"\n\n# =============================================================================\n# External secret sources\n# =============================================================================\n# Pull provider credentials from external secret managers at process startup\n# instead of storing them in ~/.hermes/.env. Only the manager's bootstrap\n# credential (e.g. BWS_ACCESS_TOKEN / OP_SERVICE_ACCOUNT_TOKEN) lives in .env\n# (or your shell / desktop session); everything else rotates centrally.\n# Failures never block startup — Hermes warns once and continues with\n# whatever .env already had.\n#\n# Multiple sources can be enabled at once:\n# - \"mapped\" sources (explicit VAR -> ref bindings, e.g. 1Password's env:\n# map) beat \"bulk\" sources (whole-project dumps like Bitwarden BSM)\n# - within a shape, the first source to claim a var wins; later claims\n# are skipped with a startup warning (never a silent clobber)\n# - a source's override_existing lets it beat .env/shell values, but\n# never another secret source's claim\n# Docs: https://hermes-agent.nousresearch.com/docs/user-guide/secrets/\n#\n# secrets:\n# # Optional explicit ordering of enabled sources.\n# # sources: [onepassword, bitwarden]\n#\n# # ---- Bitwarden Secrets Manager (bws CLI) --------------------------------\n# bitwarden:\n# enabled: false\n# access_token_env: BWS_ACCESS_TOKEN # bootstrap token, sourced from .env\n# project_id: \"\" # UUID of the BSM project to sync\n# server_url: \"\" # \"\" = US Cloud; EU/self-hosted URL otherwise\n# cache_ttl_seconds: 300 # 0 disables caching\n# override_existing: true # BSM values win over existing env\n# auto_install: true # lazy-download bws into ~/.hermes/bin\n#\n# # ---- 1Password (op CLI) -------------------------------------------------\n# onepassword:\n# enabled: false\n# # Map env-var names to op:// secret references. Each is resolved with a\n# # single `op read` at startup.\n# env:\n# OPENAI_API_KEY: \"op://Private/OpenAI/api key\"\n# ANTHROPIC_API_KEY: \"op://Private/Anthropic/credential\"\n# account: \"\" # op --account shorthand; \"\" = default\n# service_account_token_env: OP_SERVICE_ACCOUNT_TOKEN # headless auth; unset = desktop session\n# binary_path: \"\" # \"\" = resolve op via PATH; else absolute path\n# cache_ttl_seconds: 300 # 0 disables BOTH cache layers\n# override_existing: true # resolved values win over existing env\n"}, "fe002eb124d9f9e23769afdfc437f716255983b1": {"t": 1785767868.7784271, "n": 75509, "h": "81cefa8c476df663", "c": "# Hermes Agent CLI Configuration\n# Copy this file to cli-config.yaml and customize as needed.\n# This file configures the CLI behavior. Environment variables in .env take precedence.\n\n# =============================================================================\n# Model Configuration\n# =============================================================================\nmodel:\n # Default model to use (can be overridden with --model flag)\n # Both \"default\" and \"model\" work as the key name here.\n default: \"anthropic/claude-opus-4.6\"\n \n # Inference provider selection:\n # \"auto\" - Auto-detect from credentials (default)\n # \"openrouter\" - OpenRouter (requires: OPENROUTER_API_KEY or OPENAI_API_KEY)\n # \"nous\" - Nous Portal OAuth (requires: hermes auth add nous)\n # \"nous-api\" - Nous Portal API key (requires: NOUS_API_KEY)\n # \"anthropic\" - Direct Anthropic API (requires: ANTHROPIC_API_KEY)\n # \"openai-codex\" - OpenAI Codex (requires: hermes auth)\n # \"copilot\" - GitHub Copilot / GitHub Models (requires: GITHUB_TOKEN)\n # \"gemini\" - Use Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n # \"zai\" - Use z.ai / ZhipuAI GLM models (requires: GLM_API_KEY)\n # \"kimi-coding\" - Kimi / Moonshot AI (requires: KIMI_API_KEY)\n # \"minimax\" - MiniMax global (requires: MINIMAX_API_KEY)\n # \"minimax-cn\" - MiniMax China (requires: MINIMAX_CN_API_KEY)\n # \"huggingface\" - Hugging Face Inference (requires: HF_TOKEN)\n # \"nvidia\" - NVIDIA NIM / build.nvidia.com (requires: NVIDIA_API_KEY)\n # \"xiaomi\" - Xiaomi MiMo (requires: XIAOMI_API_KEY)\n # \"arcee\" - Arcee AI Trinity models (requires: ARCEEAI_API_KEY)\n # \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY — https://ollama.com/settings)\n # \"deepinfra\" - DeepInfra (requires: DEEPINFRA_API_KEY)\n # \"kilocode\" - KiloCode gateway (requires: KILOCODE_API_KEY)\n # \"azure-foundry\" - Microsoft Foundry / Azure OpenAI (API key or Entra ID)\n # \"lmstudio\" - LM Studio local server (optional: LM_API_KEY, defaults to http://127.0.0.1:1234/v1)\n #\n # Local servers (LM Studio, Ollama, vLLM, llama.cpp):\n # \"custom\" - Any other OpenAI-compatible endpoint. Set base_url below.\n # Aliases: \"ollama\", \"vllm\", \"llamacpp\" all map to \"custom\".\n # LM Studio is first-class and uses provider: \"lmstudio\".\n # It works with both no-auth and auth-enabled server modes.\n #\n # Can also be overridden for a single invocation with the --provider flag.\n provider: \"auto\"\n \n # API configuration (falls back to OPENROUTER_API_KEY env var)\n # api_key: \"your-key-here\" # Uncomment to set here instead of .env\n base_url: \"https://openrouter.ai/api/v1\"\n\n # Azure Foundry keyless auth example:\n # provider: \"azure-foundry\"\n # base_url: \"https://.openai.azure.com/openai/v1\"\n # auth_mode: \"entra_id\" # DefaultAzureCredential: az login, managed identity, workload identity, etc.\n # default: \"gpt-4o\" # Deployment/model name\n # entra:\n # scope: \"https://ai.azure.com/.default\" # Optional; this is the default.\n\n # ── Token limits — two settings, easy to confuse ──────────────────────────\n #\n # context_length: TOTAL context window (input + output tokens combined).\n # Controls when Hermes compresses history and validates requests.\n # Leave unset — Hermes auto-detects the correct value from the provider.\n # Set manually only when auto-detection is wrong (e.g. a local server with\n # a custom num_ctx, or a proxy that doesn't expose /v1/models).\n #\n # context_length: 131072\n #\n # max_tokens: OUTPUT cap — maximum tokens the model may generate per response.\n # Unrelated to how long your conversation history can be.\n # The OpenAI-standard name \"max_tokens\" is a misnomer; Anthropic's native\n # API has since renamed it \"max_output_tokens\" for clarity.\n # Leave unset to use the model's native output ceiling (recommended).\n # Set only if you want to deliberately limit individual response length.\n #\n# max_tokens: 8192\n\n # ── Custom request headers (optional) ─────────────────────────────────────\n #\n # default_headers: extra HTTP headers sent on every request to an\n # OpenAI-compatible endpoint. User values take precedence over the\n # provider/SDK defaults, so this is the supported way to override the\n # OpenAI Python SDK's identifying headers (User-Agent: OpenAI/Python ...,\n # X-Stainless-*) when a custom provider sits behind a gateway/WAF that\n # rejects them — e.g. an upstream that returns \"502 Upstream access\n # forbidden\" for the SDK default User-Agent but accepts a plain one.\n # Applies on the OpenAI wire only (not native Anthropic / Bedrock).\n #\n # default_headers:\n # User-Agent: \"curl/8.7.1\"\n #\n # extra_headers: accepted as an alias of default_headers (merged, with\n # extra_headers winning when both are set) — matches the per-provider\n # extra_headers key below.\n #\n # Per-provider variant: named providers / custom_providers entries accept an\n # extra_headers dict scoped to that endpoint only — for reverse proxies,\n # gateways, or custom auth (e.g. Cloudflare Access service tokens).\n # Merged onto SDK/provider defaults with the entry's values winning.\n # Header values are treated as secrets and are never logged.\n #\n # providers:\n # my-proxy:\n # base_url: \"https://llm.internal.example.com/v1\"\n # key_env: \"MY_PROXY_API_KEY\"\n # extra_headers:\n # CF-Access-Client-Id: \"xxxx.access\"\n # CF-Access-Client-Secret: \"${CF_ACCESS_SECRET}\"\n # X-Client-Name: \"hermes-agent\"\n\n# Named provider overrides (optional)\n# Use this for per-provider request timeouts, non-stream stale timeouts,\n# and per-model exceptions.\n# Applies to the primary turn client on every api_mode (OpenAI-wire, native\n# Anthropic, and Anthropic-compatible providers), the fallback chain, and\n# client rebuilds during credential rotation. For OpenAI-wire chat\n# completions (streaming and non-streaming) the configured value is also\n# used as the per-request ``timeout=`` kwarg so it wins over the legacy\n# HERMES_API_TIMEOUT env var (which still applies when no config is set).\n# ``stale_timeout_seconds`` controls the non-streaming stale-call detector and\n# wins over the legacy HERMES_API_CALL_STALE_TIMEOUT env var. Leaving these\n# unset keeps the legacy defaults (HERMES_API_TIMEOUT=1800s,\n# HERMES_API_CALL_STALE_TIMEOUT=90s, native Anthropic 900s). The\n# implicit non-stream stale detector is auto-disabled for local endpoints\n# and can scale upward for very large contexts.\n#\n# Not currently wired for AWS Bedrock (bedrock_converse + AnthropicBedrock\n# SDK paths) — those use boto3 with its own timeout configuration.\n#\n# providers:\n# ollama-local:\n# request_timeout_seconds: 300 # Longer timeout for local cold-starts\n# stale_timeout_seconds: 900 # Explicitly re-enable stale detection on local endpoints\n# anthropic:\n# request_timeout_seconds: 30 # Fast-fail cloud requests\n# models:\n# claude-opus-4.6:\n# timeout_seconds: 600 # Longer timeout for extended-thinking Opus calls\n# openai-codex:\n# models:\n# gpt-5.4:\n# stale_timeout_seconds: 1800 # Longer non-stream stale timeout for slow large-context turns\n\n# =============================================================================\n# OpenRouter Provider Routing (only applies when using OpenRouter)\n# =============================================================================\n# Control how requests are routed across providers on OpenRouter.\n# See: https://openrouter.ai/docs/guides/routing/provider-selection\n#\n# provider_routing:\n# # Sort strategy: \"price\" (default), \"throughput\", or \"latency\"\n# # Append :nitro to model name for a shortcut to throughput sorting.\n# sort: \"throughput\"\n#\n# # Only allow these providers (provider slugs from OpenRouter)\n# # only: [\"anthropic\", \"google\"]\n#\n# # Skip these providers entirely\n# # ignore: [\"deepinfra\", \"fireworks\"]\n#\n# # Try providers in this order (overrides default load balancing)\n# # order: [\"anthropic\", \"google\", \"together\"]\n#\n# # Require providers to support all parameters in your request\n# # require_parameters: true\n#\n# # Data policy: \"allow\" (default) or \"deny\" to exclude providers that may store data\n# # data_collection: \"deny\"\n\n# =============================================================================\n# OpenRouter Response Caching (only applies when using OpenRouter)\n# =============================================================================\n# Cache identical API responses at the OpenRouter edge for free instant replays.\n# When enabled, identical requests (same model, messages, parameters) return\n# cached responses with zero billing. Separate from Anthropic prompt caching.\n# See: https://openrouter.ai/docs/guides/features/response-caching\n#\n# openrouter:\n# response_cache: true # Enable response caching (default: true)\n# response_cache_ttl: 300 # Cache TTL in seconds, 1-86400 (default: 300)\n\n# =============================================================================\n# Git Worktree Isolation\n# =============================================================================\n# When enabled, each CLI session creates an isolated git worktree so multiple\n# agents can work on the same repo concurrently without file collisions.\n# Equivalent to always passing --worktree / -w on the command line.\n#\n# worktree: true # Always create a worktree when in a git repo\n# worktree: false # Default — only create when -w flag is passed\n#\n# By default a new worktree branches from the freshly-fetched remote tip\n# (the current branch's upstream, else the remote's default branch) so it\n# starts current with the project instead of from the local clone's\n# (possibly stale) HEAD. Set worktree_sync: false to branch from local HEAD\n# instead — useful when offline or when you deliberately want the clone's\n# exact current state as the base.\n#\n# worktree_sync: true # Default — branch from the fetched remote tip\n# worktree_sync: false # Branch from local HEAD (offline / pinned base)\n\n# =============================================================================\n# Terminal Tool Configuration\n# =============================================================================\n# Choose ONE of the following terminal configurations by uncommenting it.\n# The terminal tool executes commands in the specified environment.\n\n# -----------------------------------------------------------------------------\n# OPTION 1: Local execution (default)\n# Commands run directly on your machine in the current directory\n# -----------------------------------------------------------------------------\n# Working directory behavior:\n# - CLI (`hermes` command): Uses \".\" (current directory where you run hermes)\n# - Gateway/messaging/cron: Uses terminal.cwd here; legacy .env cwd values are deprecated\nterminal:\n backend: \"local\"\n cwd: \".\" # For local backend: \".\" = current directory. Ignored for remote backends unless a backend documents otherwise.\n timeout: 180\n # HOME policy for tool subprocesses:\n # auto - default: host uses your real HOME; containers use HERMES_HOME/home\n # real - force your real OS-user HOME\n # profile - force HERMES_HOME/home for strict per-profile CLI config isolation\n home_mode: \"auto\"\n docker_mount_cwd_to_workspace: false # SECURITY: off by default. Opt in to mount the launch cwd into Docker /workspace.\n lifetime_seconds: 300\n # sudo_password: \"hunter2\" # Optional: pipe a sudo password via sudo -S. SECURITY WARNING: plaintext.\n # sudo_password: \"\" # Explicit empty password: try empty and never open the interactive sudo prompt.\n\n# -----------------------------------------------------------------------------\n# OPTION 2: SSH remote execution\n# Commands run on a remote server - agent code stays local (sandboxed)\n# Great for: keeping agent isolated from its own code, using powerful remote hardware\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"ssh\"\n# cwd: \"/home/myuser/project\" # Path on the REMOTE server\n# timeout: 180\n# lifetime_seconds: 300\n# ssh_host: \"my-server.example.com\"\n# ssh_user: \"myuser\"\n# ssh_port: 22\n# ssh_key: \"~/.ssh/id_rsa\" # Optional - uses ssh-agent if not specified\n\n# -----------------------------------------------------------------------------\n# OPTION 3: Docker container\n# Commands run in an isolated Docker container\n# Great for: reproducible environments, testing, isolation\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"docker\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /)\n# timeout: 180\n# lifetime_seconds: 300\n# docker_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# docker_mount_cwd_to_workspace: true # Explicit opt-in: mount your launch cwd into /workspace\n# # Optional: run the container as your host user's uid:gid so files written\n# # into bind-mounted dirs are owned by you, not root. Drops SETUID/SETGID\n# # caps too since no gosu privilege drop is needed. Leave off if your\n# # chosen docker_image expects to start as root.\n# docker_run_as_host_user: true\n# # Optional: explicitly forward selected env vars into Docker.\n# # These values come from your current shell first, then ~/.hermes/.env.\n# # Warning: anything forwarded here is visible to commands run in the container.\n# docker_forward_env:\n# - \"GITHUB_TOKEN\"\n# - \"NPM_TOKEN\"\n# # Optional: extra flags passed verbatim to docker run (appended after security defaults).\n# # Useful for adding capabilities (e.g. apt installs needing SETUID) or custom options.\n# # Example: add a Linux capability not included by default\n# # docker_extra_args:\n# # - \"--cap-add\"\n# # - \"SETUID\"\n\n# -----------------------------------------------------------------------------\n# OPTION 4: Singularity/Apptainer container\n# Commands run in a Singularity container (common in HPC environments)\n# Great for: HPC clusters, shared compute environments\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"singularity\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# singularity_image: \"docker://nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 5: Modal cloud execution\n# Commands run on Modal's cloud infrastructure\n# Great for: GPU access, scalable compute, serverless execution\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"modal\"\n# cwd: \"/workspace\" # Path INSIDE the sandbox (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# modal_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 6: Daytona cloud execution\n# Commands run in Daytona cloud sandboxes\n# Great for: Cloud dev environments, persistent workspaces, team collaboration\n# Requires: pip install daytona, DAYTONA_API_KEY env var\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"daytona\"\n# cwd: \"~\"\n# timeout: 180\n# lifetime_seconds: 300\n# daytona_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# container_disk: 10240 # Daytona max is 10GB per sandbox\n\n#\n# --- Container resource limits (docker, singularity, modal, daytona -- ignored for local/ssh) ---\n# These settings apply to all container backends. They control the resources\n# allocated to the sandbox and whether its filesystem persists across sessions.\n container_cpu: 1 # CPU cores\n container_memory: 5120 # Memory in MB (5120 = 5GB)\n container_disk: 51200 # Disk in MB (51200 = 50GB)\n container_persistent: true # Persist filesystem across sessions (false = ephemeral)\n\n# -----------------------------------------------------------------------------\n# SUDO SUPPORT (works with ALL backends above)\n# -----------------------------------------------------------------------------\n# Add sudo_password to any terminal config above to enable sudo commands.\n# The password is piped via `sudo -S`. Works with local, ssh, docker, etc.\n#\n# SECURITY WARNING: Password stored in plaintext!\n#\n# INTERACTIVE PROMPT: If sudo_password is unset and the CLI is running,\n# you'll be prompted to enter your password when sudo is needed:\n# - 45-second timeout (auto-skips if no input)\n# - Press Enter to skip (command fails gracefully)\n# - Password is hidden while typing\n# - Password is cached for the session\n#\n# EMPTY PASSWORDS: Setting sudo_password to an explicit empty string is different\n# from leaving it unset. Hermes will try an empty password via `sudo -S` and\n# will not open the interactive prompt. This is useful for passwordless sudo,\n# Touch ID sudo setups, and environments where prompting is just noise.\n#\n# ALTERNATIVES:\n# - SSH backend: Configure passwordless sudo on the remote server\n# - Containers: Run as root inside the container (no sudo needed)\n# - Local: Configure /etc/sudoers for specific commands\n#\n# Example (add to your terminal section):\n# sudo_password: \"your-password-here\"\n\n# =============================================================================\n# Security Scanning (tirith)\n# =============================================================================\n# Optional pre-exec command security scanning via tirith.\n# Detects homograph URLs, pipe-to-shell, terminal injection, env manipulation.\n# Install: brew install sheeki03/tap/tirith\n# Docs: https://github.com/sheeki03/tirith\n#\n# security:\n# tirith_enabled: true # Enable/disable tirith scanning\n# tirith_path: \"tirith\" # Path to tirith binary (supports ~ expansion)\n# tirith_timeout: 5 # Scan timeout in seconds\n# tirith_fail_open: true # Allow commands if tirith unavailable\n\n# =============================================================================\n# Browser Tool Configuration\n# =============================================================================\nbrowser:\n # Inactivity timeout in seconds - browser sessions are automatically closed\n # after this period of no activity between agent loops (default: 120 = 2 minutes)\n inactivity_timeout: 120\n\n# =============================================================================\n# Tool Loop Guardrails\n# =============================================================================\n# Soft warnings are enabled by default. They append guidance to repeated failed\n# or non-progressing tool results but still let the tool execute. Hard stops are\n# opt-in circuit breakers for autonomous/cron sessions where stopping a loop is\n# preferable to spending the full iteration budget.\ntool_loop_guardrails:\n warnings_enabled: true\n hard_stop_enabled: false\n warn_after:\n exact_failure: 2\n same_tool_failure: 3\n idempotent_no_progress: 2\n hard_stop_after:\n exact_failure: 5\n same_tool_failure: 8\n idempotent_no_progress: 5\n\n# =============================================================================\n# Context Compression (Auto-shrinks long conversations)\n# =============================================================================\n# When conversation approaches model's context limit, middle turns are\n# automatically summarized to free up space while preserving important context.\n#\n# HOW IT WORKS:\n# 1. Tracks actual token usage from API responses (not estimates)\n# 2. When prompt_tokens >= threshold% of model's context_length, triggers compression\n# 3. Protects first 3 turns (system prompt, initial request, first response)\n# 4. Protects last N turns (default 20 messages = ~10 full turns of recent context)\n# 5. Summarizes middle turns using a fast/cheap model\n# 6. Inserts summary as a user message, continues conversation seamlessly\n#\n# Post-compression tail budget is target_ratio × threshold × context_length:\n# 200K context, threshold 0.50, ratio 0.20 → 20K tokens of recent tail preserved\n# 1M context, threshold 0.50, ratio 0.20 → 100K tokens of recent tail preserved\n#\ncompression:\n # Enable automatic context compression (default: true)\n # Set to false if you prefer to manage context manually or want errors on overflow\n enabled: true\n \n # Trigger compression at this % of model's context limit (default: 0.50 = 50%)\n # Lower values = more aggressive compression, higher values = compress later\n # Models with context windows below 512K are floored at 0.75 (raise-only) so\n # compaction doesn't fire with half the window still free; set above 0.75 to override.\n threshold: 0.50\n\n # Existing Codex gpt-5.5 behavior: raise Hermes' compaction trigger to 85%\n # for the ChatGPT Codex OAuth route. Set false to opt back down to threshold.\n codex_gpt55_autoraise: true\n\n # Fraction of the threshold to preserve as recent tail (default: 0.20 = 20%)\n # e.g. 20% of 50% threshold = 10% of total context kept as recent messages.\n # Summary output is separately capped at 12K tokens (Gemini output limit).\n # Range: 0.10 - 0.80\n target_ratio: 0.20\n\n # Number of most-recent messages to always preserve (default: 20 ≈ 10 full turns)\n # Higher values keep more recent conversation intact at the cost of more aggressive\n # compression of older turns.\n protect_last_n: 20\n\n # Codex app-server (codex CLI runtime) thread-compaction mode. The codex\n # agent owns the real thread context on this runtime, so Hermes' summarizer\n # cannot shrink it — compaction goes through the app server instead.\n # native = let Codex decide when to compact its own thread (default)\n # hermes = let Hermes threshold trigger Codex thread/compact/start\n # off = Hermes will not auto-trigger compaction; Codex may still compact natively\n codex_app_server_auto: native\n\n # Number of non-system messages to protect at the head of the transcript, in\n # ADDITION to the system prompt (which is always implicitly protected).\n # Head messages are NEVER summarized — they survive every compression\n # indefinitely. This gives stable early context for short/medium sessions,\n # but in long-running sessions that rely on rolling compaction the pinned\n # opening turns may not match how you want the session framed over time.\n # Set to 0 to preserve ONLY the system prompt (plus the rolling summary\n # and recent tail) — the cleanest configuration for long-running sessions.\n # Default 3 preserves the system prompt plus the first three non-system\n # head messages, matching the pre-feature behaviour.\n protect_first_n: 3\n\n # To pin a specific model/provider for compression summaries, use the\n # auxiliary section below (auxiliary.compression.provider / model).\n\n# =============================================================================\n# Anthropic prompt caching TTL\n# =============================================================================\n# When prompt caching is active (Claude via OpenRouter or native Anthropic),\n# Anthropic supports two TTL tiers for cached prefixes: \"5m\" (default) and\n# \"1h\". Other values are ignored and \"5m\" is used.\n#\nprompt_caching:\n cache_ttl: \"5m\" # use \"1h\" for long sessions with pauses between turns\n\n# =============================================================================\n# Auxiliary Models (Advanced — Experimental)\n# =============================================================================\n# Hermes uses lightweight \"auxiliary\" models for side tasks: image analysis,\n# browser screenshot analysis, web page summarization, TTS audio-tag insertion,\n# and context compression.\n#\n# By default these use Gemini Flash via OpenRouter or Nous Portal and are\n# auto-detected from your credentials. You do NOT need to change anything\n# here for normal usage.\n#\n# WARNING: Overriding these with providers other than OpenRouter or Nous Portal\n# is EXPERIMENTAL and may not work. Not all models/providers support vision,\n# produce usable summaries, or accept the same API format. Change at your own\n# risk — if things break, reset to \"auto\" / empty values.\n#\n# Each task has its own provider + model pair so you can mix providers.\n# For example: OpenRouter for vision (needs multimodal), but your main\n# local endpoint for compression (just needs text).\n#\n# Provider options:\n# \"auto\" - Best available: OpenRouter → Nous Portal → main endpoint (default)\n# \"openrouter\" - Force OpenRouter (requires OPENROUTER_API_KEY)\n# \"nous\" - Force Nous Portal (requires: hermes auth add nous)\n# \"gemini\" - Force Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n# \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY)\n# \"codex\" - Force Codex OAuth (requires: hermes model → Codex).\n# Uses gpt-5.3-codex which supports vision.\n# \"main\" - Use your custom endpoint (OPENAI_BASE_URL + OPENAI_API_KEY).\n# Works with OpenAI API, local models, or any OpenAI-compatible\n# endpoint. Also falls back to Codex OAuth and API-key providers.\n#\n# Model: leave empty to use the provider's default. When empty, OpenRouter\n# uses \"google/gemini-3-flash-preview\" and Nous uses \"gemini-3-flash\".\n# Other providers pick a sensible default automatically.\n#\n# auxiliary:\n# # Image analysis: vision_analyze tool + browser screenshots\n# vision:\n# provider: \"auto\"\n# model: \"\" # e.g. \"google/gemini-2.5-flash\", \"openai/gpt-4o\"\n# timeout: 30 # LLM API call timeout (seconds)\n# download_timeout: 30 # Image HTTP download timeout (seconds)\n# # Increase for slow connections or self-hosted image servers\n#\n# # Web page scraping / summarization + browser page text extraction\n# web_extract:\n# provider: \"auto\"\n# model: \"\"\n#\n# # Gemini 3.1 TTS hidden audio-tag insertion\n# tts_audio_tags:\n# provider: \"auto\" # empty model = your main chat model\n# model: \"\"\n# timeout: 30\n#\n# # Session search — summarizes matching past sessions\n# session_search:\n# provider: \"auto\"\n# model: \"\"\n# timeout: 30\n# max_concurrency: 3 # Limit parallel summaries to reduce request-burst 429s\n# extra_body: {} # Provider-specific OpenAI-compatible request fields\n# # Example for providers that support request-body\n# # reasoning controls:\n# # extra_body:\n# # enable_thinking: false\n# # Some vLLM/Qwen deployments expect this nested:\n# # extra_body:\n# # chat_template_kwargs:\n# # enable_thinking: false\n\n# =============================================================================\n# Persistent Memory\n# =============================================================================\n# Bounded curated memory injected into the system prompt every session.\n# Two stores: MEMORY.md (agent's notes) and USER.md (user profile).\n# Character limits keep the memory small and focused. The agent manages\n# pruning -- when at the limit, it must consolidate or replace entries.\n# Disabled by default in batch_runner.\n#\nmemory:\n # Agent's personal notes: environment facts, conventions, things learned\n memory_enabled: true\n \n # User profile: preferences, communication style, expectations\n user_profile_enabled: true\n \n # Character limits (~2.75 chars per token, model-independent)\n memory_char_limit: 2200 # ~800 tokens\n user_char_limit: 1375 # ~500 tokens\n\n # Periodic memory nudge: remind the agent to consider saving memories\n # every N user turns. Set to 0 to disable. Only active when memory is enabled.\n nudge_interval: 10 # Nudge every 10 user turns (0 = disabled)\n\n # Memory flush: give the agent one turn to save memories before context is\n # lost (compression, /new, /reset, exit). Set to 0 to disable.\n # For exit/reset, only fires if the session had at least this many user turns.\n flush_min_turns: 6 # Min user turns to trigger flush on exit/reset (0 = disabled)\n\n# =============================================================================\n# Session Reset Policy (Messaging Platforms)\n# =============================================================================\n# Controls when messaging sessions (Telegram, Discord, WhatsApp, Slack) are\n# automatically cleared. Without resets, conversation context grows indefinitely\n# which increases API costs with every message.\n#\n# When a reset triggers, the agent first saves important information to its\n# persistent memory — but the conversation context is wiped. The agent starts\n# fresh but retains learned facts via its memory system.\n#\n# Users can always manually reset with /reset or /new in chat.\n#\n# Modes:\n# \"both\" - Reset on EITHER inactivity timeout or daily boundary (recommended)\n# \"idle\" - Reset only after N minutes of inactivity\n# \"daily\" - Reset only at a fixed hour each day\n# \"none\" - Never auto-reset; context lives until /reset or compression kicks in\n#\n# When a reset triggers, the agent gets one turn to save important memories and\n# skills before the context is wiped. Persistent memory carries across sessions.\n#\nsession_reset:\n mode: both # \"both\", \"idle\", \"daily\", or \"none\"\n idle_minutes: 1440 # Inactivity timeout in minutes (default: 1440 = 24 hours)\n at_hour: 4 # Daily reset hour, 0-23 local time (default: 4 AM)\n\n# Maximum number of simultaneously active chat sessions across CLI, TUI,\n# dashboard chat, and messaging gateway. Set to null, 0, or omit to allow\n# unlimited concurrent sessions. When the limit is reached, new sessions get a\n# clean error while existing active sessions keep their normal behavior. This\n# top-level key takes precedence over gateway.max_concurrent_sessions. The cap\n# is a best-effort single-host/profile runtime guard; Hermes fails open if the\n# local runtime lease registry cannot be read or locked.\nmax_concurrent_sessions: null\n\n# When true, group/channel chats use one session per participant when the platform\n# provides a user ID. This is the secure default and prevents users in the same\n# room from sharing context, interrupts, and token costs. Set false only if you\n# explicitly want one shared \"room brain\" per group/channel.\ngroup_sessions_per_user: true\n\n# ─────────────────────────────────────────────────────────────────────────────\n# API Server — per-client model routing\n# ─────────────────────────────────────────────────────────────────────────────\n# Route different API clients to different models/providers on a single\n# Hermes deployment. Clients choose a backend by sending a specific string\n# as the OpenAI ``model`` field. Unmapped model values fall back to the\n# global model configured in the ``model:`` section above, and an explicit\n# session /model override always wins over a route.\n#\n# Configure via the ``platforms.api_server.extra.model_routes`` gateway\n# config block:\n#\n# platforms:\n# api_server:\n# enabled: true\n# extra:\n# key: \"your-api-server-secret\"\n# model_routes:\n# # Xiaozhi clients send model=\"minimax-m2\" → routed to MiniMax via OpenRouter\n# minimax-m2:\n# model: \"minimax/minimax-m1\"\n# provider: \"openrouter\" # optional — overrides global provider\n# # api_key: \"sk-...\" # optional — per-route UPSTREAM provider\n# # key (NOT caller auth; never logged)\n# # base_url: \"https://...\" # optional — per-route base URL\n# # GPT clients keep their own alias\n# gpt-5:\n# model: \"openai/gpt-5\"\n# provider: \"openrouter\"\n#\n# Configured aliases are automatically listed by GET /v1/models so clients\n# can discover them without manual coordination. Caller authentication is\n# unchanged: every request still authenticates with the global API server\n# key (``extra.key`` / API_SERVER_KEY).\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Gateway Streaming\n# ─────────────────────────────────────────────────────────────────────────────\n# Stream tokens to messaging platforms in real-time. The bot sends a message\n# on first token, then progressively edits it as more tokens arrive.\n# Disabled by default — enable to try the streaming UX on Telegram/Discord/Slack.\n# For Telegram, partial edits are sent as plain text and only the final edit uses MarkdownV2.\nstreaming:\n enabled: false\n # transport: edit # \"edit\" = progressive editMessageText\n # edit_interval: 0.3 # seconds between message edits\n # buffer_threshold: 40 # chars before forcing an edit flush\n # cursor: \" ▉\" # cursor shown during streaming\n\n# =============================================================================\n# Skills Configuration\n# =============================================================================\n# Skills are reusable procedures the agent can load and follow. The agent can\n# also create new skills after completing complex tasks.\n#\nskills:\n # Nudge the agent to create skills after complex tasks.\n # Every N tool-calling iterations, remind the model to consider saving a skill.\n # Set to 0 to disable.\n creation_nudge_interval: 15\n\n # External skill directories — share skills across tools/agents without\n # copying them into ~/.hermes/skills/. Each path is expanded (~ and ${VAR})\n # and resolved to an absolute path. External dirs are read-only: skill\n # creation always writes to ~/.hermes/skills/. Local skills take precedence\n # when names collide.\n # external_dirs:\n # - ~/.agents/skills\n # - /home/shared/team-skills\n\n# =============================================================================\n# Agent Behavior\n# =============================================================================\nagent:\n # Maximum tool-calling iterations per conversation\n # Higher = more room for complex tasks, but costs more tokens\n # Recommended: 20-30 for focused tasks, 50-100 for open exploration\n max_turns: 60\n\n # Inactivity timeout for gateway agent runs (seconds, 0 = unlimited).\n # The agent can run indefinitely when actively calling tools or receiving\n # API responses. Only fires after the agent has been idle for this duration.\n # gateway_timeout: 1800\n\n # Staged warning: send a warning before escalating to full timeout.\n # Fires once per run when inactivity reaches this threshold (seconds).\n # Set to 0 to disable the warning.\n # gateway_timeout_warning: 900\n\n # Graceful drain timeout for gateway stop/restart (seconds).\n # Default 0 = no drain: a restart interrupts in-flight agents immediately,\n # cleans up, and exits. Set a positive value only if you want a grace\n # window on /restart, and keep it well under systemd's TimeoutStopSec.\n # restart_drain_timeout: 0\n\n # Max app-level retry attempts for API errors (connection drops, provider\n # timeouts, 5xx, etc.) before the agent surfaces the failure. Lower this\n # to 1 if you use fallback providers and want fast failover on flaky\n # primaries (default 3). The OpenAI SDK does its own low-level retries\n # underneath this wrapper — this is the Hermes-level loop.\n # api_max_retries: 3\n\n # After the agent edits code without fresh passing verification, nudge it to\n # verify before finishing. The default \"auto\" enables it on interactive\n # coding surfaces (CLI, TUI, desktop) and programmatic callers, and disables\n # it on conversational messaging surfaces (Telegram, Discord, etc.) where the\n # verification summary would reach a human as chat noise. Set true or false to\n # force it on or off; the HERMES_VERIFY_ON_STOP env var (1/0) takes precedence.\n # verify_on_stop: auto\n\n # Standing operator instructions for the coding posture (when Hermes is in a\n # code workspace). Appended to the coding brief as an extra system block, so\n # you can pin project-wide workflow rules without editing the shipped brief.\n # Accepts a string or a list of strings. Takes effect next session.\n # coding_instructions:\n # - \"For UI work, don't run tsc/lint until I approve the look.\"\n # - \"Clean the diff before you commit and push.\"\n\n # When verify-on-stop finds edited code without fresh verification evidence,\n # append guidance for creative UI work (avoid broad tsc/lint/test before visual\n # approval) and clean-diff expectations. Set false to keep that nudge terse.\n # verify_guidance: true\n\n # A `pre_verify` hook (plugin or shell, see Event Hooks docs) can keep the\n # agent going one more turn to verify/clean before finishing. This caps how\n # many times one turn may be nudged to continue, so a hook can't trap the loop.\n # Default 3.\n # max_verify_nudges: 3\n\n # Enable verbose logging\n verbose: false\n \n # Reasoning effort level (OpenRouter and Nous Portal)\n # Controls how much \"thinking\" the model does before responding.\n # Options: \"xhigh\" (max), \"high\", \"medium\", \"low\", \"minimal\", \"none\" (disable)\n reasoning_effort: \"medium\"\n \n # Predefined personalities (use with /personality command)\n personalities:\n helpful: \"You are a helpful, friendly AI assistant.\"\n concise: \"You are a concise assistant. Keep responses brief and to the point.\"\n technical: \"You are a technical expert. Provide detailed, accurate technical information.\"\n creative: \"You are a creative assistant. Think outside the box and offer innovative solutions.\"\n teacher: \"You are a patient teacher. Explain concepts clearly with examples.\"\n kawaii: \"You are a kawaii assistant! Use cute expressions like (◕‿◕), ★, ♪, and ~! Add sparkles and be super enthusiastic about everything! Every response should feel warm and adorable desu~! ヽ(>∀<☆)ノ\"\n catgirl: \"You are Neko-chan, an anime catgirl AI assistant, nya~! Add 'nya' and cat-like expressions to your speech. Use kaomoji like (=^・ω・^=) and ฅ^•ﻌ•^ฅ. Be playful and curious like a cat, nya~!\"\n pirate: \"Arrr! Ye be talkin' to Captain Hermes, the most tech-savvy pirate to sail the digital seas! Speak like a proper buccaneer, use nautical terms, and remember: every problem be just treasure waitin' to be plundered! Yo ho ho!\"\n shakespeare: \"Hark! Thou speakest with an assistant most versed in the bardic arts. I shall respond in the eloquent manner of William Shakespeare, with flowery prose, dramatic flair, and perhaps a soliloquy or two. What light through yonder terminal breaks?\"\n surfer: \"Duuude! You're chatting with the chillest AI on the web, bro! Everything's gonna be totally rad. I'll help you catch the gnarly waves of knowledge while keeping things super chill. Cowabunga! 🤙\"\n noir: \"The rain hammered against the terminal like regrets on a guilty conscience. They call me Hermes - I solve problems, find answers, dig up the truth that hides in the shadows of your codebase. In this city of silicon and secrets, everyone's got something to hide. What's your story, pal?\"\n uwu: \"hewwo! i'm your fwiendwy assistant uwu~ i wiww twy my best to hewp you! *nuzzles your code* OwO what's this? wet me take a wook! i pwomise to be vewy hewpful >w<\"\n philosopher: \"Greetings, seeker of wisdom. I am an assistant who contemplates the deeper meaning behind every query. Let us examine not just the 'how' but the 'why' of your questions. Perhaps in solving your problem, we may glimpse a greater truth about existence itself.\"\n hype: \"YOOO LET'S GOOOO!!! 🔥🔥🔥 I am SO PUMPED to help you today! Every question is AMAZING and we're gonna CRUSH IT together! This is gonna be LEGENDARY! ARE YOU READY?! LET'S DO THIS! 💪😤🚀\"\n\n# =============================================================================\n# Toolsets\n# =============================================================================\n# Control which tools the agent has access to.\n# Use `hermes tools` to interactively enable/disable tools per platform.\n\n# =============================================================================\n# Platform Toolsets (per-platform tool configuration)\n# =============================================================================\n# Override which toolsets are available on each platform.\n# If a platform isn't listed here, its built-in default is used.\n#\n# You can use EITHER:\n# - A preset like \"hermes-cli\" or \"hermes-telegram\" (curated tool set)\n# - A list of individual toolsets to compose your own (see list below)\n#\n# Supported platform keys: cli, telegram, discord, whatsapp, slack, qqbot, teams, google_chat\n#\n# Examples:\n#\n# # Use presets (same as defaults):\n# platform_toolsets:\n# cli: [hermes-cli]\n# telegram: [hermes-telegram]\n#\n# # Custom: give Telegram only web + terminal + file + planning:\n# platform_toolsets:\n# telegram: [web, terminal, file, todo]\n#\n# # Custom: CLI without browser or image gen:\n# platform_toolsets:\n# cli: [web, terminal, file, skills, todo, tts, cronjob]\n#\n# # Restrictive: Discord gets read-only tools only:\n# platform_toolsets:\n# discord: [web, vision, skills, todo]\n#\n# If not set, defaults are:\n# cli: hermes-cli (everything + cronjob management)\n# telegram: hermes-telegram (terminal, file, web, vision, image, tts, browser, skills, todo, cronjob, messaging)\n# discord: hermes-discord (same as telegram)\n# whatsapp: hermes-whatsapp (same as telegram)\n# slack: hermes-slack (same as telegram)\n# signal: hermes-signal (same as telegram)\n# homeassistant: hermes-homeassistant (same as telegram)\n# qqbot: hermes-qqbot (same as telegram)\n# teams: hermes-teams (same as telegram)\n# google_chat: hermes-google_chat (same as telegram)\n#\nplatform_toolsets:\n cli: [hermes-cli]\n telegram: [hermes-telegram]\n discord: [hermes-discord]\n whatsapp: [hermes-whatsapp]\n slack: [hermes-slack]\n signal: [hermes-signal]\n homeassistant: [hermes-homeassistant]\n qqbot: [hermes-qqbot]\n yuanbao: [hermes-yuanbao]\n teams: [hermes-teams]\n google_chat: [hermes-google_chat]\n\n# =============================================================================\n# Gateway Platform Settings\n# =============================================================================\n# Optional per-platform messaging settings.\n# Platform-specific knobs live under `extra`.\n#\n# platforms:\n# telegram:\n# reply_to_mode: \"first\" # off | first | all\n# # guest_mode lets explicit @mentions from non-allowlisted groups through.\n# # Default false; ordinary messages, replies, and regex wake words stay blocked.\n# guest_mode: false\n# # allowed_chats: [\"-1001234567890\"]\n# extra:\n# disable_link_previews: false # Set true to suppress Telegram URL previews in bot messages\n# rich_messages: false # Bot API 10.1 rich messages (tables/task lists/details/math); default false for copyable legacy MarkdownV2, set true to opt in\n# rich_drafts: false # Experimental rich draft previews during Telegram DM streaming; default false because Telegram Desktop/macOS can visually overlay draft frames\n# command_menu:\n# # Telegram allows up to 100 BotCommands; Hermes defaults to 60 so\n# # all built-in commands plus common skill commands stay visible\n# # while remaining under Telegram's payload-size limit. Clamped 1..100.\n# max_commands: 60\n# # prepend = user priority first, then Hermes defaults\n# # append = Hermes defaults first, then user priority\n# # replace = only the list below defines priority\n# priority_mode: prepend\n# priority:\n# - my_plugin_command\n# webhook:\n# extra:\n# # Route scripts default to a 30 second timeout. Scripts must live under\n# # the active profile's scripts directory and receive webhook JSON on stdin.\n# script_timeout_seconds: 30\n#\n# Discord-specific settings (config.yaml top-level, not under platforms:):\n#\n# discord:\n# require_mention: true # Require @mention in server channels (default: true)\n# auto_thread: true # Auto-create thread on @mention (default: true)\n# free_response_channels: \"\" # Channel IDs where no mention is needed\n# reactions: true # Show processing reactions (default: true)\n# history_backfill: true # Recover missed channel messages on mention (default: true)\n# history_backfill_limit: 50 # Max messages to scan backwards (default: 50)\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Available toolsets (use these names in platform_toolsets or the toolsets list)\n#\n# Run `hermes chat --list-toolsets` to see all toolsets and their tools.\n# Run `hermes chat --list-tools` to see every individual tool with descriptions.\n# ─────────────────────────────────────────────────────────────────────────────\n#\n# INDIVIDUAL TOOLSETS (compose your own):\n# web - web_search, web_extract\n# search - web_search only (no scraping)\n# terminal - terminal, process\n# file - read_file, write_file, patch, search\n# browser - browser_navigate, browser_snapshot, browser_click, browser_type,\n# browser_scroll, browser_back, browser_press,\n# browser_get_images, browser_vision (requires BROWSERBASE_API_KEY)\n# vision - vision_analyze (requires OPENROUTER_API_KEY)\n# image_gen - image_generate (requires FAL_KEY)\n# skills - skills_list, skill_view\n# skills_hub - skill_hub (search/install/manage from online registries — user-driven only)\n# todo - todo (in-memory task planning, no deps)\n# tts - text_to_speech (Edge TTS free, or ELEVENLABS/OPENAI/MINIMAX/MISTRAL key)\n# cronjob - cronjob (create/list/update/pause/resume/run/remove scheduled tasks)\n#\n# PRESETS (curated bundles):\n# hermes-cli - All of the above except send_message\n# hermes-telegram - terminal, file, web, vision, image_gen, tts, browser,\n# skills, todo, cronjob, send_message\n# hermes-discord - Same as hermes-telegram\n# hermes-whatsapp - Same as hermes-telegram\n# hermes-slack - Same as hermes-telegram\n#\n# COMPOSITE:\n# debugging - terminal + web + file\n# safe - web + vision (no terminal access)\n# all - Everything available\n#\n# web - Web search and content extraction (web_search, web_extract)\n# search - Web search only, no scraping (web_search)\n# terminal - Command execution and process management (terminal, process)\n# file - File operations: read, write, patch, search\n# browser - Full browser automation (navigate, click, type, screenshot, etc.)\n# vision - Image analysis (vision_analyze)\n# image_gen - Image generation with FLUX (image_generate)\n# skills - Load skill documents (skills_list, skill_view)\n# todo - Task planning and tracking for multi-step work\n# memory - Persistent memory across sessions (personal notes + user profile)\n# session_search - Search and recall past conversations (FTS5 + Gemini Flash summarization)\n# tts - Text-to-speech (Edge TTS free, ElevenLabs, OpenAI, MiniMax, Mistral)\n# cronjob - Schedule and manage automated tasks (CLI-only)\n#\n# Composite toolsets:\n# debugging - terminal + web + file (for troubleshooting)\n# safe - web + vision (no terminal access)\n\n# NOTE: The top-level \"toolsets\" key is deprecated and ignored.\n# Tool configuration is managed per-platform via platform_toolsets above.\n# Use `hermes tools` to configure interactively, or edit platform_toolsets directly.\n#\n# CLI override: hermes chat --toolsets terminal,web,file\n\n# =============================================================================\n# MCP (Model Context Protocol) Servers\n# =============================================================================\n# Connect to external MCP servers to add tools from the MCP ecosystem.\n# Each server's tools are automatically discovered and registered.\n# See website/docs/user-guide/features/mcp.md for full documentation.\n#\n# Stdio servers (spawn a subprocess):\n# command: the executable to run\n# args: command-line arguments\n# env: environment variables (only these + safe defaults passed to subprocess)\n#\n# HTTP servers (connect to a URL):\n# url: the MCP server endpoint\n# headers: HTTP headers (e.g., for authentication)\n#\n# Optional per-server settings:\n# timeout: tool call timeout in seconds (default: 120)\n# connect_timeout: initial connection timeout (default: 60)\n# keepalive_interval: liveness ping cadence in seconds (default: 180).\n# Lower it below the server's session TTL for servers that expire idle\n# sessions quickly (e.g. Unreal Engine editor MCP, ~15s), otherwise idle\n# tool calls hit an expired session and pay a slow reconnect. Floored at 5s.\n#\n# mcp_servers:\n# time:\n# command: uvx\n# args: [\"mcp-server-time\"]\n# filesystem:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-filesystem\", \"/home/user\"]\n# notion:\n# url: https://mcp.notion.com/mcp\n# github:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-github\"]\n# env:\n# GITHUB_PERSONAL_ACCESS_TOKEN: \"ghp_...\"\n#\n# Sampling (server-initiated LLM requests) — enabled by default.\n# Per-server config under the 'sampling' key:\n# analysis:\n# command: npx\n# args: [\"-y\", \"analysis-server\"]\n# sampling:\n# enabled: true # default: true\n# model: \"gemini-3-flash\" # override model (optional)\n# max_tokens_cap: 4096 # max tokens per request\n# timeout: 30 # LLM call timeout (seconds)\n# max_rpm: 10 # max requests per minute\n# allowed_models: [] # model whitelist (empty = all)\n# max_tool_rounds: 5 # tool loop limit (0 = disable)\n# log_level: \"info\" # audit verbosity\n\n# =============================================================================\n# Text-to-Speech\n# =============================================================================\n# TTS defaults to Edge TTS unless changed in ~/.hermes/config.yaml.\n# Gemini TTS supports persona/director prompt files, and Gemini 3.1 Flash TTS\n# can use a hidden auxiliary rewrite pass to insert expressive square-bracket\n# audio tags into the TTS script without showing tags in chat.\n#\n# tts:\n# provider: \"gemini\"\n# gemini:\n# model: \"gemini-3.1-flash-tts-preview\"\n# voice: \"Kore\"\n# audio_tags: false\n# persona_prompt_file: \"\" # e.g. ~/.hermes/tts/radio-host.md\n\n# =============================================================================\n# Voice Transcription (Speech-to-Text)\n# =============================================================================\n# Automatically transcribe voice messages on messaging platforms.\n# Providers: local (free, faster-whisper) | groq (free tier) | openai (Whisper API) | mistral (Voxtral Transcribe)\n# Set the corresponding API key in .env: GROQ_API_KEY, OPENAI_API_KEY, or MISTRAL_API_KEY.\nstt:\n enabled: true\n # provider: \"local\" # auto-detected if omitted\n local:\n model: \"base\" # tiny | base | small | medium | large-v3 | turbo\n # language: \"\" # auto-detect; set to \"en\", \"es\", \"fr\", etc. to force\n openai:\n model: \"whisper-1\" # whisper-1 | gpt-4o-mini-transcribe | gpt-4o-transcribe\n # mistral:\n # model: \"voxtral-mini-latest\" # voxtral-mini-latest | voxtral-mini-2602\n # deepinfra:\n # # Model id is discovered live from the DeepInfra catalog filtered\n # # by the `stt` surface tag — leave `model` blank to take the first\n # # live result. Pin only when you need a specific Whisper variant.\n # model: \"\"\n\n# Text-to-speech. Only the deepinfra block is documented here — the\n# remaining providers (edge, openai, xai, minimax, mistral, gemini,\n# elevenlabs, neutts, kittentts, piper) inherit sensible defaults from\n# DEFAULT_CONFIG in hermes_cli/config.py.\n# tts:\n# provider: \"deepinfra\"\n# deepinfra:\n# # Model id is discovered live from the DeepInfra catalog filtered\n# # by the `tts` surface tag — leave `model` blank to take the first\n# # live result.\n# model: \"\"\n# voice: \"default\"\n\n# Image generation. Each provider plugin reads its own ``image_gen.``\n# block; deepinfra discovers models live from\n# api.deepinfra.com/v1/openai/models filtered by the ``image-gen`` tag —\n# no model id is hardcoded, so retired models disappear automatically.\n# image_gen:\n# provider: \"deepinfra\"\n# deepinfra:\n# # Leave `model` blank for the first live `image-gen`-tagged result.\n# model: \"\"\n\n# =============================================================================\n# Response Pacing (Messaging Platforms)\n# =============================================================================\n# Add human-like delays between message chunks.\n# human_delay:\n# mode: \"off\" # \"off\" | \"natural\" | \"custom\"\n# min_ms: 800 # Min delay (custom mode only)\n# max_ms: 2500 # Max delay (custom mode only)\n\n# =============================================================================\n# Session Logging\n# =============================================================================\n# Session trajectories are automatically saved to logs/ directory.\n# Each session creates: logs/session_YYYYMMDD_HHMMSS_UUID.json\n#\n# The session ID is displayed in the welcome banner for easy reference.\n# Logs contain full conversation history in trajectory format:\n# - System prompt, user messages, assistant responses\n# - Tool calls with inputs/outputs\n# - Timestamps for debugging\n#\n# No configuration needed - logging is always enabled.\n# To disable, you would need to modify the source code.\n\n# =============================================================================\n# Code Execution Sandbox (Programmatic Tool Calling)\n# =============================================================================\n# The execute_code tool runs Python scripts that call Hermes tools via RPC.\n# Intermediate tool results stay out of the LLM's context window.\ncode_execution:\n timeout: 300 # Max seconds per script before kill (default: 300 = 5 min)\n max_tool_calls: 50 # Max RPC tool calls per execution (default: 50)\n\n# =============================================================================\n# Subagent Delegation\n# =============================================================================\n# The delegate_task tool spawns child agents with isolated context.\n# Supports single tasks and batch mode (default 3 parallel, configurable).\ndelegation:\n max_iterations: 50 # Max tool-calling turns per child (default: 50)\n # max_concurrent_children: 3 # Max parallel child agents per batch (default: 3, floor: 1, no ceiling).\n # WARNING: values above 10 multiply API cost linearly.\n # max_spawn_depth: 1 # Delegation tree depth cap (range: 1-3, default: 1 = flat).\n # Raise to 2 to allow workers to spawn their own subagents.\n # Requires role=\"orchestrator\" on intermediate agents.\n # orchestrator_enabled: true # Kill switch for role=\"orchestrator\" children (default: true).\n # subagent_auto_approve: false # When a subagent hits a dangerous-command approval prompt, auto-deny (default: false)\n # or auto-approve \"once\" (true) instead of blocking on stdin.\n # The parent TUI owns stdin, so blocking would deadlock; non-interactive resolution is required.\n # Both choices emit a logger.warning audit line. Flip to true only for cron/batch pipelines.\n # inherit_mcp_toolsets: true # When explicit child toolsets are narrowed, also keep the parent's MCP toolsets (default: true). Set false for strict intersection.\n # model: \"google/gemini-3-flash-preview\" # Override model for subagents (empty = inherit parent)\n # provider: \"openrouter\" # Override provider for subagents (empty = inherit parent)\n # # Resolves full credentials (base_url, api_key) automatically.\n # # Supported: openrouter, nous, zai, kimi-coding, minimax\n\n# =============================================================================\n# Honcho Integration (Cross-Session User Modeling)\n# =============================================================================\n# AI-native persistent memory via Honcho (https://honcho.dev/).\n# Builds a deeper understanding of the user across sessions and tools.\n# Runs alongside USER.md — additive, not a replacement.\n#\n# Requires: pip install honcho-ai\n# Config: ~/.honcho/config.json (shared with Claude Code, Cursor, etc.)\n# API key: HONCHO_API_KEY in ~/.hermes/.env or ~/.honcho/config.json\n#\n# Hermes-specific overrides (optional — most config comes from ~/.honcho/config.json):\n# honcho: {}\n\n# =============================================================================\n# Display\n# =============================================================================\ndisplay:\n # Use compact banner mode (hides the ASCII-art banner, shows a single line).\n # true: Compact single-line banner\n # false: Full ASCII banner with tool/skill summary (default)\n compact: false\n\n # Tool progress display level (CLI and gateway)\n # off: Silent — no tool activity shown, just the final response\n # new: Show a tool indicator only when the tool changes (skip repeats)\n # all: Show every tool call with a short preview (default)\n # verbose: Full args, results, and debug logs (same as /verbose)\n # log: Silent in chat; append every tool call to ~/.hermes/logs/tool_calls.log (gateway only)\n # Toggle at runtime with /verbose in the CLI\n tool_progress: all\n\n # Per-platform defaults can be quieter than the global setting. Telegram\n # tunes for mobile: tool_progress and busy_ack_detail default off (no\n # per-tool breadcrumb stream, no \"iteration 21/60\" debug detail in busy\n # acks or heartbeats), but interim_assistant_messages and\n # long_running_notifications STAY ON so the user has real signal between\n # turn start and final answer (mid-turn assistant commentary + a single\n # edit-in-place \"⏳ Working — N min\" heartbeat). Override under\n # display.platforms.telegram.\n\n # Auto-cleanup of temporary progress bubbles after the final response lands.\n # On platforms that support message deletion (currently Telegram), this\n # removes the tool-progress bubble, \"⏳ Still working...\" notices, and\n # context-pressure status messages once the final reply has been delivered —\n # keeping long-running turns visible live, then tidy afterward. Failed runs\n # leave the bubbles in place as breadcrumbs. Off by default.\n # Per-platform override: display.platforms.telegram.cleanup_progress\n # true: Delete tracked progress/status bubbles on successful turn\n # false: Leave everything in place (default)\n # Example:\n # display:\n # platforms:\n # telegram:\n # cleanup_progress: true\n cleanup_progress: false\n\n # Gateway-only natural mid-turn assistant updates.\n # When true, completed assistant status messages are sent as separate chat\n # messages. This is independent of tool_progress and gateway streaming.\n # true: Send mid-turn assistant updates as separate messages (default)\n # false: Only send the final response\n interim_assistant_messages: true\n\n # Gateway-only long-running status heartbeats.\n # When false, the platform does not receive periodic \"⏳ Working — N min\"\n # notifications even if agent.gateway_notify_interval is non-zero. The\n # heartbeat edits a single message in place (where the adapter supports\n # editing) instead of posting a new bubble each interval.\n # Default: true everywhere, including Telegram (silent agents are worse\n # than a single edit-in-place heartbeat).\n long_running_notifications: true\n\n # Include detailed iteration/tool/status context in busy acknowledgments\n # and long-running heartbeats. When true, busy acks show \"iteration 21/60,\n # terminal, 10 min\" and the heartbeat shows \"⏳ Working — 12 min,\n # iteration 21/60, terminal\". When false (Telegram default), both stay\n # terse: \"Interrupting current task\" and \"⏳ Working — 12 min, terminal\".\n busy_ack_detail: true\n\n # What Enter does when Hermes is already busy (CLI and gateway platforms).\n # interrupt: Interrupt the current run and redirect Hermes (default)\n # queue: Queue your message for the next turn\n # steer: Inject your message mid-run via /steer, arriving at the agent\n # after the next tool call — no interrupt, no role violation.\n # Falls back to 'queue' if the agent isn't running yet or if\n # images are attached (steer only carries text).\n # Ctrl+C (or /stop in gateway) always interrupts regardless of this setting.\n # Toggle at runtime with /busy .\n busy_input_mode: interrupt\n\n # Background process notifications (gateway/messaging only).\n # Controls how chatty the process watcher is when you use\n # terminal(background=true, notify_on_complete=true) from Telegram/Discord/etc.\n # off: No watcher messages at all\n # result: Only the final completion message\n # error: Only the final message when exit code != 0\n # all: Running output updates + final message (default)\n background_process_notifications: all\n\n\n # Play terminal bell when agent finishes a response.\n # Useful for long-running tasks — your terminal will ding when the agent is done.\n # Works over SSH. Most terminals can be configured to flash the taskbar or play a sound.\n # true: Ring the terminal bell on each response\n # false: Silent (default)\n bell_on_complete: false\n\n # Show model reasoning/thinking before each response.\n # When enabled, a dim box shows the model's thought process above the response.\n # Toggle at runtime with /reasoning show or /reasoning hide.\n # true: Show the reasoning box\n # false: Hide reasoning (default)\n show_reasoning: false\n\n # Stream tokens to the terminal as they arrive instead of waiting for the\n # full response. The response box opens on first token and text appears\n # line-by-line. Tool calls are still captured silently.\n # true: Stream tokens as they arrive (default)\n # false: Wait for the full response before rendering\n streaming: true\n\n # Show [HH:MM] timestamps on user input and assistant response labels.\n # timestamps: false\n\n # ───────────────────────────────────────────────────────────────────────────\n # Skin / Theme\n # ───────────────────────────────────────────────────────────────────────────\n # Customize CLI visual appearance — banner colors, spinner faces, tool prefix,\n # response box label, and branding text. Change at runtime with /skin .\n #\n # Built-in skins:\n # default — Classic Hermes gold/kawaii\n # ares — Crimson/bronze war-god theme with spinner wings\n # mono — Clean grayscale monochrome\n # slate — Cool blue developer-focused\n # daylight — Bright light-mode theme\n # warm-lightmode — Warm paper-tone light-mode theme\n # poseidon — Sea-green/teal Olympian theme\n # sisyphus — Earthy stone-and-moss theme\n # charizard — Fiery orange dragon theme\n #\n # Custom skins: drop a YAML file in ~/.hermes/skins/.yaml\n # Schema (all fields optional, missing values inherit from default):\n #\n # name: my-theme\n # description: Short description\n # colors:\n # banner_border: \"#HEX\" # Panel border\n # banner_title: \"#HEX\" # Panel title\n # banner_accent: \"#HEX\" # Section headers (Available Tools, etc.)\n # banner_dim: \"#HEX\" # Dim/muted text\n # banner_text: \"#HEX\" # Body text (tool names, skill names)\n # ui_accent: \"#HEX\" # UI accent color\n # response_border: \"#HEX\" # Response box border color\n # spinner:\n # waiting_faces: [\"(⚔)\", \"(⛨)\"] # Faces shown while waiting\n # thinking_faces: [\"(⚔)\", \"(⌁)\"] # Faces shown while thinking\n # thinking_verbs: [\"forging\", \"plotting\"] # Verbs for spinner messages\n # wings: # Optional left/right spinner decorations\n # - [\"⟪⚔\", \"⚔⟫\"]\n # - [\"⟪▲\", \"▲⟫\"]\n # branding:\n # agent_name: \"My Agent\" # Banner title and branding\n # welcome: \"Welcome message\" # Shown at CLI startup\n # response_label: \" ⚔ Agent \" # Response box header label\n # prompt_symbol: \"⚔\" # Prompt symbol (bare token; renderers add trailing space)\n # tool_prefix: \"╎\" # Tool output line prefix (default: ┊)\n #\n skin: default\n\n# =============================================================================\n# Model Aliases — short names for /model command\n# =============================================================================\n# Map short aliases to exact (model, provider, base_url) tuples.\n# Used by /model tab completion and resolve_alias().\n# Aliases are checked BEFORE the models.dev catalog, so they can route\n# to endpoints not in the catalog (e.g. Ollama Cloud, local servers).\n#\n# model_aliases:\n# opus:\n# model: claude-opus-4-6\n# provider: anthropic\n# qwen:\n# model: \"qwen3.5:397b\"\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n# glm:\n# model: glm-4.7\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n\n# =============================================================================\n# Privacy\n# =============================================================================\n# privacy:\n# # Redact PII from the LLM context prompt.\n# # When true, phone numbers are stripped and user/chat IDs are replaced\n# # with deterministic hashes before being sent to the model.\n# # Names and usernames are NOT affected (user-chosen, publicly visible).\n# # Routing/delivery still uses the original values internally.\n# redact_pii: false\n\n# =============================================================================\n# Shell-script hooks\n# =============================================================================\n# Register shell scripts as plugin-hook callbacks. Each entry is executed as\n# a subprocess (shell=False, shlex.split) with a JSON payload on stdin. On\n# stdout the script may return JSON that either blocks the tool call or\n# injects context into the next LLM call.\n#\n# Valid events (mirror hermes_cli.plugins.VALID_HOOKS):\n# pre_tool_call, post_tool_call, pre_llm_call, post_llm_call,\n# pre_api_request, post_api_request, on_session_start, on_session_end,\n# on_session_finalize, on_session_reset, subagent_stop\n#\n# First-use consent: each (event, command) pair prompts once on a TTY, then\n# is persisted to ~/.hermes/shell-hooks-allowlist.json. Non-interactive\n# runs (gateway, cron) need --accept-hooks, HERMES_ACCEPT_HOOKS=1, or the\n# hooks_auto_accept key below.\n#\n# See website/docs/user-guide/features/hooks.md for the full JSON wire\n# protocol and worked examples.\n#\n# hooks:\n# pre_tool_call:\n# - matcher: \"terminal\"\n# command: \"~/.hermes/agent-hooks/block-rm-rf.sh\"\n# timeout: 10\n# post_tool_call:\n# - matcher: \"write_file|patch\"\n# command: \"~/.hermes/agent-hooks/auto-format.sh\"\n# pre_llm_call:\n# - command: \"~/.hermes/agent-hooks/inject-cwd-context.sh\"\n# subagent_stop:\n# - command: \"~/.hermes/agent-hooks/log-orchestration.sh\"\n#\n# hooks_auto_accept: false\n\n\n# =============================================================================\n# Update Behavior\n# =============================================================================\nupdates:\n # Create a full HERMES_HOME zip before every `hermes update`.\n # Backups land in ~/.hermes/backups/ and can be restored with `hermes import`.\n # Off by default because large homes can add minutes to every update.\n pre_update_backup: false\n\n # Number of pre-update backup zips to retain.\n backup_keep: 5\n\n # What non-interactive updates do with local source edits in the Hermes repo.\n # Interactive terminal updates always prompt before restoring the autostash.\n #\n # stash - auto-stash before pull, then auto-restore after success (default)\n # discard - drop the update-created stash after success; use only on managed\n # installs where local source edits should not persist\n non_interactive_local_changes: \"stash\"\n\n\n# =============================================================================\n# Web Dashboard\n# =============================================================================\n# OAuth gate configuration for `hermes dashboard --host `.\n# The bundled Nous Portal plugin reads these on startup; settings here are\n# the canonical surface. Each can be overridden by an environment variable:\n#\n# dashboard.oauth.client_id <- HERMES_DASHBOARD_OAUTH_CLIENT_ID\n# dashboard.oauth.portal_url <- HERMES_DASHBOARD_PORTAL_URL\n# dashboard.public_url <- HERMES_DASHBOARD_PUBLIC_URL\n#\n# Env wins when set to a non-empty value. This is what Fly.io's platform-\n# secret injection uses to push per-deploy client_ids without needing to\n# bake a config.yaml into the image. Empty env values are treated as unset\n# so a provisioned-but-not-populated secret can't shadow a valid entry here.\n#\n# Local dev / on-prem deploys should typically set these via config.yaml\n# (the ~/.hermes/.env file is reserved for API keys and secrets).\n#\n# dashboard:\n# oauth:\n# client_id: \"\" # agent:{instance_id}; Portal provisions this at deploy\n# portal_url: \"\" # blank → default https://portal.nousresearch.com\n#\n# # Force the absolute base URL the OAuth callback (and any other public\n# # URL the dashboard hands to external systems) is built from. Set this\n# # for deploys behind reverse proxies that don't reliably forward\n# # X-Forwarded-Host / X-Forwarded-Proto / X-Forwarded-Prefix (manual\n# # nginx setups, on-prem ingresses, custom-domain Fly deploys without\n# # full proxy header chains).\n# #\n# # When set, the value is the complete authority: scheme + host +\n# # optional path prefix (e.g. \"https://example.com/hermes\"). The OAuth\n# # callback URL becomes \"/auth/callback\" — X-Forwarded-Prefix\n# # is IGNORED on this code path because the operator has explicitly\n# # declared the public URL and we no longer need to guess.\n# #\n# # Leave empty to use the existing proxy-header reconstruction (the\n# # default — works on Fly.io out of the box).\n# #\n# # public_url: \"https://example.com/hermes\"\n#\n# -----------------------------------------------------------------------------\n# Self-hosted OIDC dashboard auth (generic OpenID Connect — Authentik,\n# Keycloak, Zitadel, Authelia, Auth0, Okta, Google, …). Use this INSTEAD of the\n# nous block above when gating the dashboard with your own identity provider.\n# Each setting can be overridden by an environment variable:\n#\n# dashboard.oauth.self_hosted.issuer <- HERMES_DASHBOARD_OIDC_ISSUER\n# dashboard.oauth.self_hosted.client_id <- HERMES_DASHBOARD_OIDC_CLIENT_ID\n# dashboard.oauth.self_hosted.scopes <- HERMES_DASHBOARD_OIDC_SCOPES\n# dashboard.oauth.self_hosted.client_secret <- HERMES_DASHBOARD_OIDC_CLIENT_SECRET\n#\n# dashboard:\n# oauth:\n# provider: self-hosted\n# self_hosted:\n# issuer: \"https://auth.example.com/application/o/hermes/\" # required\n# client_id: \"hermes-dashboard\" # required\n# scopes: \"openid profile email\" # optional\n#\n# # OPTIONAL — set ONLY if your IDP registered the client as\n# # *confidential* (Authentik / Keycloak often default to this). When\n# # set, Hermes authenticates the client at the token endpoint\n# # (client_secret_basic or client_secret_post, auto-selected from the\n# # IDP's discovery doc) IN ADDITION to PKCE. Leave unset for a public\n# # (PKCE-only) client — the common case.\n# #\n# # This is a CREDENTIAL: prefer setting HERMES_DASHBOARD_OIDC_CLIENT_SECRET\n# # in ~/.hermes/.env over putting it here in config.yaml.\n# # client_secret: \"\"\n\n# =============================================================================\n# External secret sources\n# =============================================================================\n# Pull provider credentials from external secret managers at process startup\n# instead of storing them in ~/.hermes/.env. Only the manager's bootstrap\n# credential (e.g. BWS_ACCESS_TOKEN / OP_SERVICE_ACCOUNT_TOKEN) lives in .env\n# (or your shell / desktop session); everything else rotates centrally.\n# Failures never block startup — Hermes warns once and continues with\n# whatever .env already had.\n#\n# Multiple sources can be enabled at once:\n# - \"mapped\" sources (explicit VAR -> ref bindings, e.g. 1Password's env:\n# map) beat \"bulk\" sources (whole-project dumps like Bitwarden BSM)\n# - within a shape, the first source to claim a var wins; later claims\n# are skipped with a startup warning (never a silent clobber)\n# - a source's override_existing lets it beat .env/shell values, but\n# never another secret source's claim\n# Docs: https://hermes-agent.nousresearch.com/docs/user-guide/secrets/\n#\n# secrets:\n# # Optional explicit ordering of enabled sources.\n# # sources: [onepassword, bitwarden]\n#\n# # ---- Bitwarden Secrets Manager (bws CLI) --------------------------------\n# bitwarden:\n# enabled: false\n# access_token_env: BWS_ACCESS_TOKEN # bootstrap token, sourced from .env\n# project_id: \"\" # UUID of the BSM project to sync\n# server_url: \"\" # \"\" = US Cloud; EU/self-hosted URL otherwise\n# cache_ttl_seconds: 300 # 0 disables caching\n# override_existing: true # BSM values win over existing env\n# auto_install: true # lazy-download bws into ~/.hermes/bin\n#\n# # ---- 1Password (op CLI) -------------------------------------------------\n# onepassword:\n# enabled: false\n# # Map env-var names to op:// secret references. Each is resolved with a\n# # single `op read` at startup.\n# env:\n# OPENAI_API_KEY: \"op://Private/OpenAI/api key\"\n# ANTHROPIC_API_KEY: \"op://Private/Anthropic/credential\"\n# account: \"\" # op --account shorthand; \"\" = default\n# service_account_token_env: OP_SERVICE_ACCOUNT_TOKEN # headless auth; unset = desktop session\n# binary_path: \"\" # \"\" = resolve op via PATH; else absolute path\n# cache_ttl_seconds: 300 # 0 disables BOTH cache layers\n# override_existing: true # resolved values win over existing env\n"}, "76381e2a8e3a21fbbd0a192b4b5f7356a7ca47b8": {"t": 1785767870.360203, "n": 74284, "h": "29ac623c1c37278e", "c": "# Hermes Agent CLI Configuration\n# Copy this file to cli-config.yaml and customize as needed.\n# This file configures the CLI behavior. Environment variables in .env take precedence.\n\n# =============================================================================\n# Model Configuration\n# =============================================================================\nmodel:\n # Default model to use (can be overridden with --model flag)\n # Both \"default\" and \"model\" work as the key name here.\n default: \"anthropic/claude-opus-4.6\"\n \n # Inference provider selection:\n # \"auto\" - Auto-detect from credentials (default)\n # \"openrouter\" - OpenRouter (requires: OPENROUTER_API_KEY or OPENAI_API_KEY)\n # \"nous\" - Nous Portal OAuth (requires: hermes auth add nous)\n # \"nous-api\" - Nous Portal API key (requires: NOUS_API_KEY)\n # \"anthropic\" - Direct Anthropic API (requires: ANTHROPIC_API_KEY)\n # \"openai-codex\" - OpenAI Codex (requires: hermes auth)\n # \"copilot\" - GitHub Copilot / GitHub Models (requires: GITHUB_TOKEN)\n # \"gemini\" - Use Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n # \"zai\" - Use z.ai / ZhipuAI GLM models (requires: GLM_API_KEY)\n # \"kimi-coding\" - Kimi / Moonshot AI (requires: KIMI_API_KEY)\n # \"minimax\" - MiniMax global (requires: MINIMAX_API_KEY)\n # \"minimax-cn\" - MiniMax China (requires: MINIMAX_CN_API_KEY)\n # \"huggingface\" - Hugging Face Inference (requires: HF_TOKEN)\n # \"nvidia\" - NVIDIA NIM / build.nvidia.com (requires: NVIDIA_API_KEY)\n # \"xiaomi\" - Xiaomi MiMo (requires: XIAOMI_API_KEY)\n # \"arcee\" - Arcee AI Trinity models (requires: ARCEEAI_API_KEY)\n # \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY — https://ollama.com/settings)\n # \"kilocode\" - KiloCode gateway (requires: KILOCODE_API_KEY)\n # \"azure-foundry\" - Microsoft Foundry / Azure OpenAI (API key or Entra ID)\n # \"lmstudio\" - LM Studio local server (optional: LM_API_KEY, defaults to http://127.0.0.1:1234/v1)\n #\n # Local servers (LM Studio, Ollama, vLLM, llama.cpp):\n # \"custom\" - Any other OpenAI-compatible endpoint. Set base_url below.\n # Aliases: \"ollama\", \"vllm\", \"llamacpp\" all map to \"custom\".\n # LM Studio is first-class and uses provider: \"lmstudio\".\n # It works with both no-auth and auth-enabled server modes.\n #\n # Can also be overridden for a single invocation with the --provider flag.\n provider: \"auto\"\n \n # API configuration (falls back to OPENROUTER_API_KEY env var)\n # api_key: \"your-key-here\" # Uncomment to set here instead of .env\n base_url: \"https://openrouter.ai/api/v1\"\n\n # Azure Foundry keyless auth example:\n # provider: \"azure-foundry\"\n # base_url: \"https://.openai.azure.com/openai/v1\"\n # auth_mode: \"entra_id\" # DefaultAzureCredential: az login, managed identity, workload identity, etc.\n # default: \"gpt-4o\" # Deployment/model name\n # entra:\n # scope: \"https://ai.azure.com/.default\" # Optional; this is the default.\n\n # ── Token limits — two settings, easy to confuse ──────────────────────────\n #\n # context_length: TOTAL context window (input + output tokens combined).\n # Controls when Hermes compresses history and validates requests.\n # Leave unset — Hermes auto-detects the correct value from the provider.\n # Set manually only when auto-detection is wrong (e.g. a local server with\n # a custom num_ctx, or a proxy that doesn't expose /v1/models).\n #\n # context_length: 131072\n #\n # max_tokens: OUTPUT cap — maximum tokens the model may generate per response.\n # Unrelated to how long your conversation history can be.\n # The OpenAI-standard name \"max_tokens\" is a misnomer; Anthropic's native\n # API has since renamed it \"max_output_tokens\" for clarity.\n # Leave unset to use the model's native output ceiling (recommended).\n # Set only if you want to deliberately limit individual response length.\n #\n# max_tokens: 8192\n\n # ── Custom request headers (optional) ─────────────────────────────────────\n #\n # default_headers: extra HTTP headers sent on every request to an\n # OpenAI-compatible endpoint. User values take precedence over the\n # provider/SDK defaults, so this is the supported way to override the\n # OpenAI Python SDK's identifying headers (User-Agent: OpenAI/Python ...,\n # X-Stainless-*) when a custom provider sits behind a gateway/WAF that\n # rejects them — e.g. an upstream that returns \"502 Upstream access\n # forbidden\" for the SDK default User-Agent but accepts a plain one.\n # Applies on the OpenAI wire only (not native Anthropic / Bedrock).\n #\n # default_headers:\n # User-Agent: \"curl/8.7.1\"\n #\n # extra_headers: accepted as an alias of default_headers (merged, with\n # extra_headers winning when both are set) — matches the per-provider\n # extra_headers key below.\n #\n # Per-provider variant: named providers / custom_providers entries accept an\n # extra_headers dict scoped to that endpoint only — for reverse proxies,\n # gateways, or custom auth (e.g. Cloudflare Access service tokens).\n # Merged onto SDK/provider defaults with the entry's values winning.\n # Header values are treated as secrets and are never logged.\n #\n # providers:\n # my-proxy:\n # base_url: \"https://llm.internal.example.com/v1\"\n # key_env: \"MY_PROXY_API_KEY\"\n # extra_headers:\n # CF-Access-Client-Id: \"xxxx.access\"\n # CF-Access-Client-Secret: \"${CF_ACCESS_SECRET}\"\n # X-Client-Name: \"hermes-agent\"\n\n# Named provider overrides (optional)\n# Use this for per-provider request timeouts, non-stream stale timeouts,\n# and per-model exceptions.\n# Applies to the primary turn client on every api_mode (OpenAI-wire, native\n# Anthropic, and Anthropic-compatible providers), the fallback chain, and\n# client rebuilds during credential rotation. For OpenAI-wire chat\n# completions (streaming and non-streaming) the configured value is also\n# used as the per-request ``timeout=`` kwarg so it wins over the legacy\n# HERMES_API_TIMEOUT env var (which still applies when no config is set).\n# ``stale_timeout_seconds`` controls the non-streaming stale-call detector and\n# wins over the legacy HERMES_API_CALL_STALE_TIMEOUT env var. Leaving these\n# unset keeps the legacy defaults (HERMES_API_TIMEOUT=1800s,\n# HERMES_API_CALL_STALE_TIMEOUT=90s, native Anthropic 900s). The\n# implicit non-stream stale detector is auto-disabled for local endpoints\n# and can scale upward for very large contexts.\n#\n# Not currently wired for AWS Bedrock (bedrock_converse + AnthropicBedrock\n# SDK paths) — those use boto3 with its own timeout configuration.\n#\n# providers:\n# ollama-local:\n# request_timeout_seconds: 300 # Longer timeout for local cold-starts\n# stale_timeout_seconds: 900 # Explicitly re-enable stale detection on local endpoints\n# anthropic:\n# request_timeout_seconds: 30 # Fast-fail cloud requests\n# models:\n# claude-opus-4.6:\n# timeout_seconds: 600 # Longer timeout for extended-thinking Opus calls\n# openai-codex:\n# models:\n# gpt-5.4:\n# stale_timeout_seconds: 1800 # Longer non-stream stale timeout for slow large-context turns\n\n# =============================================================================\n# OpenRouter Provider Routing (only applies when using OpenRouter)\n# =============================================================================\n# Control how requests are routed across providers on OpenRouter.\n# See: https://openrouter.ai/docs/guides/routing/provider-selection\n#\n# provider_routing:\n# # Sort strategy: \"price\" (default), \"throughput\", or \"latency\"\n# # Append :nitro to model name for a shortcut to throughput sorting.\n# sort: \"throughput\"\n#\n# # Only allow these providers (provider slugs from OpenRouter)\n# # only: [\"anthropic\", \"google\"]\n#\n# # Skip these providers entirely\n# # ignore: [\"deepinfra\", \"fireworks\"]\n#\n# # Try providers in this order (overrides default load balancing)\n# # order: [\"anthropic\", \"google\", \"together\"]\n#\n# # Require providers to support all parameters in your request\n# # require_parameters: true\n#\n# # Data policy: \"allow\" (default) or \"deny\" to exclude providers that may store data\n# # data_collection: \"deny\"\n\n# =============================================================================\n# OpenRouter Response Caching (only applies when using OpenRouter)\n# =============================================================================\n# Cache identical API responses at the OpenRouter edge for free instant replays.\n# When enabled, identical requests (same model, messages, parameters) return\n# cached responses with zero billing. Separate from Anthropic prompt caching.\n# See: https://openrouter.ai/docs/guides/features/response-caching\n#\n# openrouter:\n# response_cache: true # Enable response caching (default: true)\n# response_cache_ttl: 300 # Cache TTL in seconds, 1-86400 (default: 300)\n\n# =============================================================================\n# Git Worktree Isolation\n# =============================================================================\n# When enabled, each CLI session creates an isolated git worktree so multiple\n# agents can work on the same repo concurrently without file collisions.\n# Equivalent to always passing --worktree / -w on the command line.\n#\n# worktree: true # Always create a worktree when in a git repo\n# worktree: false # Default — only create when -w flag is passed\n#\n# By default a new worktree branches from the freshly-fetched remote tip\n# (the current branch's upstream, else the remote's default branch) so it\n# starts current with the project instead of from the local clone's\n# (possibly stale) HEAD. Set worktree_sync: false to branch from local HEAD\n# instead — useful when offline or when you deliberately want the clone's\n# exact current state as the base.\n#\n# worktree_sync: true # Default — branch from the fetched remote tip\n# worktree_sync: false # Branch from local HEAD (offline / pinned base)\n\n# =============================================================================\n# Terminal Tool Configuration\n# =============================================================================\n# Choose ONE of the following terminal configurations by uncommenting it.\n# The terminal tool executes commands in the specified environment.\n\n# -----------------------------------------------------------------------------\n# OPTION 1: Local execution (default)\n# Commands run directly on your machine in the current directory\n# -----------------------------------------------------------------------------\n# Working directory behavior:\n# - CLI (`hermes` command): Uses \".\" (current directory where you run hermes)\n# - Gateway/messaging/cron: Uses terminal.cwd here; legacy .env cwd values are deprecated\nterminal:\n backend: \"local\"\n cwd: \".\" # For local backend: \".\" = current directory. Ignored for remote backends unless a backend documents otherwise.\n timeout: 180\n # HOME policy for tool subprocesses:\n # auto - default: host uses your real HOME; containers use HERMES_HOME/home\n # real - force your real OS-user HOME\n # profile - force HERMES_HOME/home for strict per-profile CLI config isolation\n home_mode: \"auto\"\n docker_mount_cwd_to_workspace: false # SECURITY: off by default. Opt in to mount the launch cwd into Docker /workspace.\n lifetime_seconds: 300\n # sudo_password: \"hunter2\" # Optional: pipe a sudo password via sudo -S. SECURITY WARNING: plaintext.\n # sudo_password: \"\" # Explicit empty password: try empty and never open the interactive sudo prompt.\n\n# -----------------------------------------------------------------------------\n# OPTION 2: SSH remote execution\n# Commands run on a remote server - agent code stays local (sandboxed)\n# Great for: keeping agent isolated from its own code, using powerful remote hardware\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"ssh\"\n# cwd: \"/home/myuser/project\" # Path on the REMOTE server\n# timeout: 180\n# lifetime_seconds: 300\n# ssh_host: \"my-server.example.com\"\n# ssh_user: \"myuser\"\n# ssh_port: 22\n# ssh_key: \"~/.ssh/id_rsa\" # Optional - uses ssh-agent if not specified\n\n# -----------------------------------------------------------------------------\n# OPTION 3: Docker container\n# Commands run in an isolated Docker container\n# Great for: reproducible environments, testing, isolation\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"docker\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /)\n# timeout: 180\n# lifetime_seconds: 300\n# docker_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# docker_mount_cwd_to_workspace: true # Explicit opt-in: mount your launch cwd into /workspace\n# # Optional: run the container as your host user's uid:gid so files written\n# # into bind-mounted dirs are owned by you, not root. Drops SETUID/SETGID\n# # caps too since no gosu privilege drop is needed. Leave off if your\n# # chosen docker_image expects to start as root.\n# docker_run_as_host_user: true\n# # Optional: explicitly forward selected env vars into Docker.\n# # These values come from your current shell first, then ~/.hermes/.env.\n# # Warning: anything forwarded here is visible to commands run in the container.\n# docker_forward_env:\n# - \"GITHUB_TOKEN\"\n# - \"NPM_TOKEN\"\n# # Optional: extra flags passed verbatim to docker run (appended after security defaults).\n# # Useful for adding capabilities (e.g. apt installs needing SETUID) or custom options.\n# # Example: add a Linux capability not included by default\n# # docker_extra_args:\n# # - \"--cap-add\"\n# # - \"SETUID\"\n\n# -----------------------------------------------------------------------------\n# OPTION 4: Singularity/Apptainer container\n# Commands run in a Singularity container (common in HPC environments)\n# Great for: HPC clusters, shared compute environments\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"singularity\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# singularity_image: \"docker://nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 5: Modal cloud execution\n# Commands run on Modal's cloud infrastructure\n# Great for: GPU access, scalable compute, serverless execution\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"modal\"\n# cwd: \"/workspace\" # Path INSIDE the sandbox (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# modal_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 6: Daytona cloud execution\n# Commands run in Daytona cloud sandboxes\n# Great for: Cloud dev environments, persistent workspaces, team collaboration\n# Requires: pip install daytona, DAYTONA_API_KEY env var\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"daytona\"\n# cwd: \"~\"\n# timeout: 180\n# lifetime_seconds: 300\n# daytona_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# container_disk: 10240 # Daytona max is 10GB per sandbox\n\n#\n# --- Container resource limits (docker, singularity, modal, daytona -- ignored for local/ssh) ---\n# These settings apply to all container backends. They control the resources\n# allocated to the sandbox and whether its filesystem persists across sessions.\n container_cpu: 1 # CPU cores\n container_memory: 5120 # Memory in MB (5120 = 5GB)\n container_disk: 51200 # Disk in MB (51200 = 50GB)\n container_persistent: true # Persist filesystem across sessions (false = ephemeral)\n\n# -----------------------------------------------------------------------------\n# SUDO SUPPORT (works with ALL backends above)\n# -----------------------------------------------------------------------------\n# Add sudo_password to any terminal config above to enable sudo commands.\n# The password is piped via `sudo -S`. Works with local, ssh, docker, etc.\n#\n# SECURITY WARNING: Password stored in plaintext!\n#\n# INTERACTIVE PROMPT: If sudo_password is unset and the CLI is running,\n# you'll be prompted to enter your password when sudo is needed:\n# - 45-second timeout (auto-skips if no input)\n# - Press Enter to skip (command fails gracefully)\n# - Password is hidden while typing\n# - Password is cached for the session\n#\n# EMPTY PASSWORDS: Setting sudo_password to an explicit empty string is different\n# from leaving it unset. Hermes will try an empty password via `sudo -S` and\n# will not open the interactive prompt. This is useful for passwordless sudo,\n# Touch ID sudo setups, and environments where prompting is just noise.\n#\n# ALTERNATIVES:\n# - SSH backend: Configure passwordless sudo on the remote server\n# - Containers: Run as root inside the container (no sudo needed)\n# - Local: Configure /etc/sudoers for specific commands\n#\n# Example (add to your terminal section):\n# sudo_password: \"your-password-here\"\n\n# =============================================================================\n# Security Scanning (tirith)\n# =============================================================================\n# Optional pre-exec command security scanning via tirith.\n# Detects homograph URLs, pipe-to-shell, terminal injection, env manipulation.\n# Install: brew install sheeki03/tap/tirith\n# Docs: https://github.com/sheeki03/tirith\n#\n# security:\n# tirith_enabled: true # Enable/disable tirith scanning\n# tirith_path: \"tirith\" # Path to tirith binary (supports ~ expansion)\n# tirith_timeout: 5 # Scan timeout in seconds\n# tirith_fail_open: true # Allow commands if tirith unavailable\n\n# =============================================================================\n# Browser Tool Configuration\n# =============================================================================\nbrowser:\n # Inactivity timeout in seconds - browser sessions are automatically closed\n # after this period of no activity between agent loops (default: 120 = 2 minutes)\n inactivity_timeout: 120\n\n# =============================================================================\n# Tool Loop Guardrails\n# =============================================================================\n# Soft warnings are enabled by default. They append guidance to repeated failed\n# or non-progressing tool results but still let the tool execute. Hard stops are\n# opt-in circuit breakers for autonomous/cron sessions where stopping a loop is\n# preferable to spending the full iteration budget.\ntool_loop_guardrails:\n warnings_enabled: true\n hard_stop_enabled: false\n warn_after:\n exact_failure: 2\n same_tool_failure: 3\n idempotent_no_progress: 2\n hard_stop_after:\n exact_failure: 5\n same_tool_failure: 8\n idempotent_no_progress: 5\n\n# =============================================================================\n# Context Compression (Auto-shrinks long conversations)\n# =============================================================================\n# When conversation approaches model's context limit, middle turns are\n# automatically summarized to free up space while preserving important context.\n#\n# HOW IT WORKS:\n# 1. Tracks actual token usage from API responses (not estimates)\n# 2. When prompt_tokens >= threshold% of model's context_length, triggers compression\n# 3. Protects first 3 turns (system prompt, initial request, first response)\n# 4. Protects last N turns (default 20 messages = ~10 full turns of recent context)\n# 5. Summarizes middle turns using a fast/cheap model\n# 6. Inserts summary as a user message, continues conversation seamlessly\n#\n# Post-compression tail budget is target_ratio × threshold × context_length:\n# 200K context, threshold 0.50, ratio 0.20 → 20K tokens of recent tail preserved\n# 1M context, threshold 0.50, ratio 0.20 → 100K tokens of recent tail preserved\n#\ncompression:\n # Enable automatic context compression (default: true)\n # Set to false if you prefer to manage context manually or want errors on overflow\n enabled: true\n \n # Trigger compression at this % of model's context limit (default: 0.50 = 50%)\n # Lower values = more aggressive compression, higher values = compress later\n # Models with context windows below 512K are floored at 0.75 (raise-only) so\n # compaction doesn't fire with half the window still free; set above 0.75 to override.\n threshold: 0.50\n\n # Existing Codex gpt-5.5 behavior: raise Hermes' compaction trigger to 85%\n # for the ChatGPT Codex OAuth route. Set false to opt back down to threshold.\n codex_gpt55_autoraise: true\n\n # Fraction of the threshold to preserve as recent tail (default: 0.20 = 20%)\n # e.g. 20% of 50% threshold = 10% of total context kept as recent messages.\n # Summary output is separately capped at 12K tokens (Gemini output limit).\n # Range: 0.10 - 0.80\n target_ratio: 0.20\n\n # Number of most-recent messages to always preserve (default: 20 ≈ 10 full turns)\n # Higher values keep more recent conversation intact at the cost of more aggressive\n # compression of older turns.\n protect_last_n: 20\n\n # Codex app-server (codex CLI runtime) thread-compaction mode. The codex\n # agent owns the real thread context on this runtime, so Hermes' summarizer\n # cannot shrink it — compaction goes through the app server instead.\n # native = let Codex decide when to compact its own thread (default)\n # hermes = let Hermes threshold trigger Codex thread/compact/start\n # off = Hermes will not auto-trigger compaction; Codex may still compact natively\n codex_app_server_auto: native\n\n # Number of non-system messages to protect at the head of the transcript, in\n # ADDITION to the system prompt (which is always implicitly protected).\n # Head messages are NEVER summarized — they survive every compression\n # indefinitely. This gives stable early context for short/medium sessions,\n # but in long-running sessions that rely on rolling compaction the pinned\n # opening turns may not match how you want the session framed over time.\n # Set to 0 to preserve ONLY the system prompt (plus the rolling summary\n # and recent tail) — the cleanest configuration for long-running sessions.\n # Default 3 preserves the system prompt plus the first three non-system\n # head messages, matching the pre-feature behaviour.\n protect_first_n: 3\n\n # To pin a specific model/provider for compression summaries, use the\n # auxiliary section below (auxiliary.compression.provider / model).\n\n# =============================================================================\n# Anthropic prompt caching TTL\n# =============================================================================\n# When prompt caching is active (Claude via OpenRouter or native Anthropic),\n# Anthropic supports two TTL tiers for cached prefixes: \"5m\" (default) and\n# \"1h\". Other values are ignored and \"5m\" is used.\n#\nprompt_caching:\n cache_ttl: \"5m\" # use \"1h\" for long sessions with pauses between turns\n\n# =============================================================================\n# Auxiliary Models (Advanced — Experimental)\n# =============================================================================\n# Hermes uses lightweight \"auxiliary\" models for side tasks: image analysis,\n# browser screenshot analysis, web page summarization, TTS audio-tag insertion,\n# and context compression.\n#\n# By default these use Gemini Flash via OpenRouter or Nous Portal and are\n# auto-detected from your credentials. You do NOT need to change anything\n# here for normal usage.\n#\n# WARNING: Overriding these with providers other than OpenRouter or Nous Portal\n# is EXPERIMENTAL and may not work. Not all models/providers support vision,\n# produce usable summaries, or accept the same API format. Change at your own\n# risk — if things break, reset to \"auto\" / empty values.\n#\n# Each task has its own provider + model pair so you can mix providers.\n# For example: OpenRouter for vision (needs multimodal), but your main\n# local endpoint for compression (just needs text).\n#\n# Provider options:\n# \"auto\" - Best available: OpenRouter → Nous Portal → main endpoint (default)\n# \"openrouter\" - Force OpenRouter (requires OPENROUTER_API_KEY)\n# \"nous\" - Force Nous Portal (requires: hermes auth add nous)\n# \"gemini\" - Force Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n# \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY)\n# \"codex\" - Force Codex OAuth (requires: hermes model → Codex).\n# Uses gpt-5.3-codex which supports vision.\n# \"main\" - Use your custom endpoint (OPENAI_BASE_URL + OPENAI_API_KEY).\n# Works with OpenAI API, local models, or any OpenAI-compatible\n# endpoint. Also falls back to Codex OAuth and API-key providers.\n#\n# Model: leave empty to use the provider's default. When empty, OpenRouter\n# uses \"google/gemini-3-flash-preview\" and Nous uses \"gemini-3-flash\".\n# Other providers pick a sensible default automatically.\n#\n# auxiliary:\n# # Image analysis: vision_analyze tool + browser screenshots\n# vision:\n# provider: \"auto\"\n# model: \"\" # e.g. \"google/gemini-2.5-flash\", \"openai/gpt-4o\"\n# timeout: 30 # LLM API call timeout (seconds)\n# download_timeout: 30 # Image HTTP download timeout (seconds)\n# # Increase for slow connections or self-hosted image servers\n#\n# # Web page scraping / summarization + browser page text extraction\n# web_extract:\n# provider: \"auto\"\n# model: \"\"\n#\n# # Gemini 3.1 TTS hidden audio-tag insertion\n# tts_audio_tags:\n# provider: \"auto\" # empty model = your main chat model\n# model: \"\"\n# timeout: 30\n#\n# # Session search — summarizes matching past sessions\n# session_search:\n# provider: \"auto\"\n# model: \"\"\n# timeout: 30\n# max_concurrency: 3 # Limit parallel summaries to reduce request-burst 429s\n# extra_body: {} # Provider-specific OpenAI-compatible request fields\n# # Example for providers that support request-body\n# # reasoning controls:\n# # extra_body:\n# # enable_thinking: false\n# # Some vLLM/Qwen deployments expect this nested:\n# # extra_body:\n# # chat_template_kwargs:\n# # enable_thinking: false\n\n# =============================================================================\n# Persistent Memory\n# =============================================================================\n# Bounded curated memory injected into the system prompt every session.\n# Two stores: MEMORY.md (agent's notes) and USER.md (user profile).\n# Character limits keep the memory small and focused. The agent manages\n# pruning -- when at the limit, it must consolidate or replace entries.\n# Disabled by default in batch_runner.\n#\nmemory:\n # Agent's personal notes: environment facts, conventions, things learned\n memory_enabled: true\n \n # User profile: preferences, communication style, expectations\n user_profile_enabled: true\n \n # Character limits (~2.75 chars per token, model-independent)\n memory_char_limit: 2200 # ~800 tokens\n user_char_limit: 1375 # ~500 tokens\n\n # Periodic memory nudge: remind the agent to consider saving memories\n # every N user turns. Set to 0 to disable. Only active when memory is enabled.\n nudge_interval: 10 # Nudge every 10 user turns (0 = disabled)\n\n # Memory flush: give the agent one turn to save memories before context is\n # lost (compression, /new, /reset, exit). Set to 0 to disable.\n # For exit/reset, only fires if the session had at least this many user turns.\n flush_min_turns: 6 # Min user turns to trigger flush on exit/reset (0 = disabled)\n\n# =============================================================================\n# Session Reset Policy (Messaging Platforms)\n# =============================================================================\n# Controls when messaging sessions (Telegram, Discord, WhatsApp, Slack) are\n# automatically cleared. Without resets, conversation context grows indefinitely\n# which increases API costs with every message.\n#\n# When a reset triggers, the agent first saves important information to its\n# persistent memory — but the conversation context is wiped. The agent starts\n# fresh but retains learned facts via its memory system.\n#\n# Users can always manually reset with /reset or /new in chat.\n#\n# Modes:\n# \"both\" - Reset on EITHER inactivity timeout or daily boundary (recommended)\n# \"idle\" - Reset only after N minutes of inactivity\n# \"daily\" - Reset only at a fixed hour each day\n# \"none\" - Never auto-reset; context lives until /reset or compression kicks in\n#\n# When a reset triggers, the agent gets one turn to save important memories and\n# skills before the context is wiped. Persistent memory carries across sessions.\n#\nsession_reset:\n mode: both # \"both\", \"idle\", \"daily\", or \"none\"\n idle_minutes: 1440 # Inactivity timeout in minutes (default: 1440 = 24 hours)\n at_hour: 4 # Daily reset hour, 0-23 local time (default: 4 AM)\n\n# Maximum number of simultaneously active chat sessions across CLI, TUI,\n# dashboard chat, and messaging gateway. Set to null, 0, or omit to allow\n# unlimited concurrent sessions. When the limit is reached, new sessions get a\n# clean error while existing active sessions keep their normal behavior. This\n# top-level key takes precedence over gateway.max_concurrent_sessions. The cap\n# is a best-effort single-host/profile runtime guard; Hermes fails open if the\n# local runtime lease registry cannot be read or locked.\nmax_concurrent_sessions: null\n\n# When true, group/channel chats use one session per participant when the platform\n# provides a user ID. This is the secure default and prevents users in the same\n# room from sharing context, interrupts, and token costs. Set false only if you\n# explicitly want one shared \"room brain\" per group/channel.\ngroup_sessions_per_user: true\n\n# ─────────────────────────────────────────────────────────────────────────────\n# API Server — per-client model routing\n# ─────────────────────────────────────────────────────────────────────────────\n# Route different API clients to different models/providers on a single\n# Hermes deployment. Clients choose a backend by sending a specific string\n# as the OpenAI ``model`` field. Unmapped model values fall back to the\n# global model configured in the ``model:`` section above, and an explicit\n# session /model override always wins over a route.\n#\n# Configure via the ``platforms.api_server.extra.model_routes`` gateway\n# config block:\n#\n# platforms:\n# api_server:\n# enabled: true\n# extra:\n# key: \"your-api-server-secret\"\n# model_routes:\n# # Xiaozhi clients send model=\"minimax-m2\" → routed to MiniMax via OpenRouter\n# minimax-m2:\n# model: \"minimax/minimax-m1\"\n# provider: \"openrouter\" # optional — overrides global provider\n# # api_key: \"sk-...\" # optional — per-route UPSTREAM provider\n# # key (NOT caller auth; never logged)\n# # base_url: \"https://...\" # optional — per-route base URL\n# # GPT clients keep their own alias\n# gpt-5:\n# model: \"openai/gpt-5\"\n# provider: \"openrouter\"\n#\n# Configured aliases are automatically listed by GET /v1/models so clients\n# can discover them without manual coordination. Caller authentication is\n# unchanged: every request still authenticates with the global API server\n# key (``extra.key`` / API_SERVER_KEY).\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Gateway Streaming\n# ─────────────────────────────────────────────────────────────────────────────\n# Stream tokens to messaging platforms in real-time. The bot sends a message\n# on first token, then progressively edits it as more tokens arrive.\n# Disabled by default — enable to try the streaming UX on Telegram/Discord/Slack.\n# For Telegram, partial edits are sent as plain text and only the final edit uses MarkdownV2.\nstreaming:\n enabled: false\n # transport: edit # \"edit\" = progressive editMessageText\n # edit_interval: 0.3 # seconds between message edits\n # buffer_threshold: 40 # chars before forcing an edit flush\n # cursor: \" ▉\" # cursor shown during streaming\n\n# =============================================================================\n# Skills Configuration\n# =============================================================================\n# Skills are reusable procedures the agent can load and follow. The agent can\n# also create new skills after completing complex tasks.\n#\nskills:\n # Nudge the agent to create skills after complex tasks.\n # Every N tool-calling iterations, remind the model to consider saving a skill.\n # Set to 0 to disable.\n creation_nudge_interval: 15\n\n # External skill directories — share skills across tools/agents without\n # copying them into ~/.hermes/skills/. Each path is expanded (~ and ${VAR})\n # and resolved to an absolute path. External dirs are read-only: skill\n # creation always writes to ~/.hermes/skills/. Local skills take precedence\n # when names collide.\n # external_dirs:\n # - ~/.agents/skills\n # - /home/shared/team-skills\n\n# =============================================================================\n# Agent Behavior\n# =============================================================================\nagent:\n # Maximum tool-calling iterations per conversation\n # Higher = more room for complex tasks, but costs more tokens\n # Recommended: 20-30 for focused tasks, 50-100 for open exploration\n max_turns: 60\n\n # Inactivity timeout for gateway agent runs (seconds, 0 = unlimited).\n # The agent can run indefinitely when actively calling tools or receiving\n # API responses. Only fires after the agent has been idle for this duration.\n # gateway_timeout: 1800\n\n # Staged warning: send a warning before escalating to full timeout.\n # Fires once per run when inactivity reaches this threshold (seconds).\n # Set to 0 to disable the warning.\n # gateway_timeout_warning: 900\n\n # Graceful drain timeout for gateway stop/restart (seconds).\n # Default 0 = no drain: a restart interrupts in-flight agents immediately,\n # cleans up, and exits. Set a positive value only if you want a grace\n # window on /restart, and keep it well under systemd's TimeoutStopSec.\n # restart_drain_timeout: 0\n\n # Max app-level retry attempts for API errors (connection drops, provider\n # timeouts, 5xx, etc.) before the agent surfaces the failure. Lower this\n # to 1 if you use fallback providers and want fast failover on flaky\n # primaries (default 3). The OpenAI SDK does its own low-level retries\n # underneath this wrapper — this is the Hermes-level loop.\n # api_max_retries: 3\n\n # After the agent edits code without fresh passing verification, nudge it to\n # verify before finishing. The default \"auto\" enables it on interactive\n # coding surfaces (CLI, TUI, desktop) and programmatic callers, and disables\n # it on conversational messaging surfaces (Telegram, Discord, etc.) where the\n # verification summary would reach a human as chat noise. Set true or false to\n # force it on or off; the HERMES_VERIFY_ON_STOP env var (1/0) takes precedence.\n # verify_on_stop: auto\n\n # Standing operator instructions for the coding posture (when Hermes is in a\n # code workspace). Appended to the coding brief as an extra system block, so\n # you can pin project-wide workflow rules without editing the shipped brief.\n # Accepts a string or a list of strings. Takes effect next session.\n # coding_instructions:\n # - \"For UI work, don't run tsc/lint until I approve the look.\"\n # - \"Clean the diff before you commit and push.\"\n\n # When verify-on-stop finds edited code without fresh verification evidence,\n # append guidance for creative UI work (avoid broad tsc/lint/test before visual\n # approval) and clean-diff expectations. Set false to keep that nudge terse.\n # verify_guidance: true\n\n # A `pre_verify` hook (plugin or shell, see Event Hooks docs) can keep the\n # agent going one more turn to verify/clean before finishing. This caps how\n # many times one turn may be nudged to continue, so a hook can't trap the loop.\n # Default 3.\n # max_verify_nudges: 3\n\n # Enable verbose logging\n verbose: false\n \n # Reasoning effort level (OpenRouter and Nous Portal)\n # Controls how much \"thinking\" the model does before responding.\n # Options: \"xhigh\" (max), \"high\", \"medium\", \"low\", \"minimal\", \"none\" (disable)\n reasoning_effort: \"medium\"\n \n # Predefined personalities (use with /personality command)\n personalities:\n helpful: \"You are a helpful, friendly AI assistant.\"\n concise: \"You are a concise assistant. Keep responses brief and to the point.\"\n technical: \"You are a technical expert. Provide detailed, accurate technical information.\"\n creative: \"You are a creative assistant. Think outside the box and offer innovative solutions.\"\n teacher: \"You are a patient teacher. Explain concepts clearly with examples.\"\n kawaii: \"You are a kawaii assistant! Use cute expressions like (◕‿◕), ★, ♪, and ~! Add sparkles and be super enthusiastic about everything! Every response should feel warm and adorable desu~! ヽ(>∀<☆)ノ\"\n catgirl: \"You are Neko-chan, an anime catgirl AI assistant, nya~! Add 'nya' and cat-like expressions to your speech. Use kaomoji like (=^・ω・^=) and ฅ^•ﻌ•^ฅ. Be playful and curious like a cat, nya~!\"\n pirate: \"Arrr! Ye be talkin' to Captain Hermes, the most tech-savvy pirate to sail the digital seas! Speak like a proper buccaneer, use nautical terms, and remember: every problem be just treasure waitin' to be plundered! Yo ho ho!\"\n shakespeare: \"Hark! Thou speakest with an assistant most versed in the bardic arts. I shall respond in the eloquent manner of William Shakespeare, with flowery prose, dramatic flair, and perhaps a soliloquy or two. What light through yonder terminal breaks?\"\n surfer: \"Duuude! You're chatting with the chillest AI on the web, bro! Everything's gonna be totally rad. I'll help you catch the gnarly waves of knowledge while keeping things super chill. Cowabunga! 🤙\"\n noir: \"The rain hammered against the terminal like regrets on a guilty conscience. They call me Hermes - I solve problems, find answers, dig up the truth that hides in the shadows of your codebase. In this city of silicon and secrets, everyone's got something to hide. What's your story, pal?\"\n uwu: \"hewwo! i'm your fwiendwy assistant uwu~ i wiww twy my best to hewp you! *nuzzles your code* OwO what's this? wet me take a wook! i pwomise to be vewy hewpful >w<\"\n philosopher: \"Greetings, seeker of wisdom. I am an assistant who contemplates the deeper meaning behind every query. Let us examine not just the 'how' but the 'why' of your questions. Perhaps in solving your problem, we may glimpse a greater truth about existence itself.\"\n hype: \"YOOO LET'S GOOOO!!! 🔥🔥🔥 I am SO PUMPED to help you today! Every question is AMAZING and we're gonna CRUSH IT together! This is gonna be LEGENDARY! ARE YOU READY?! LET'S DO THIS! 💪😤🚀\"\n\n# =============================================================================\n# Toolsets\n# =============================================================================\n# Control which tools the agent has access to.\n# Use `hermes tools` to interactively enable/disable tools per platform.\n\n# =============================================================================\n# Platform Toolsets (per-platform tool configuration)\n# =============================================================================\n# Override which toolsets are available on each platform.\n# If a platform isn't listed here, its built-in default is used.\n#\n# You can use EITHER:\n# - A preset like \"hermes-cli\" or \"hermes-telegram\" (curated tool set)\n# - A list of individual toolsets to compose your own (see list below)\n#\n# Supported platform keys: cli, telegram, discord, whatsapp, slack, qqbot, teams, google_chat\n#\n# Examples:\n#\n# # Use presets (same as defaults):\n# platform_toolsets:\n# cli: [hermes-cli]\n# telegram: [hermes-telegram]\n#\n# # Custom: give Telegram only web + terminal + file + planning:\n# platform_toolsets:\n# telegram: [web, terminal, file, todo]\n#\n# # Custom: CLI without browser or image gen:\n# platform_toolsets:\n# cli: [web, terminal, file, skills, todo, tts, cronjob]\n#\n# # Restrictive: Discord gets read-only tools only:\n# platform_toolsets:\n# discord: [web, vision, skills, todo]\n#\n# If not set, defaults are:\n# cli: hermes-cli (everything + cronjob management)\n# telegram: hermes-telegram (terminal, file, web, vision, image, tts, browser, skills, todo, cronjob, messaging)\n# discord: hermes-discord (same as telegram)\n# whatsapp: hermes-whatsapp (same as telegram)\n# slack: hermes-slack (same as telegram)\n# signal: hermes-signal (same as telegram)\n# homeassistant: hermes-homeassistant (same as telegram)\n# qqbot: hermes-qqbot (same as telegram)\n# teams: hermes-teams (same as telegram)\n# google_chat: hermes-google_chat (same as telegram)\n#\nplatform_toolsets:\n cli: [hermes-cli]\n telegram: [hermes-telegram]\n discord: [hermes-discord]\n whatsapp: [hermes-whatsapp]\n slack: [hermes-slack]\n signal: [hermes-signal]\n homeassistant: [hermes-homeassistant]\n qqbot: [hermes-qqbot]\n yuanbao: [hermes-yuanbao]\n teams: [hermes-teams]\n google_chat: [hermes-google_chat]\n\n# =============================================================================\n# Gateway Platform Settings\n# =============================================================================\n# Optional per-platform messaging settings.\n# Platform-specific knobs live under `extra`.\n#\n# platforms:\n# telegram:\n# reply_to_mode: \"first\" # off | first | all\n# # guest_mode lets explicit @mentions from non-allowlisted groups through.\n# # Default false; ordinary messages, replies, and regex wake words stay blocked.\n# guest_mode: false\n# # allowed_chats: [\"-1001234567890\"]\n# extra:\n# disable_link_previews: false # Set true to suppress Telegram URL previews in bot messages\n# rich_messages: false # Bot API 10.1 rich messages (tables/task lists/details/math); default false for copyable legacy MarkdownV2, set true to opt in\n# rich_drafts: false # Experimental rich draft previews during Telegram DM streaming; default false because Telegram Desktop/macOS can visually overlay draft frames\n# command_menu:\n# # Telegram allows up to 100 BotCommands; Hermes defaults to 60 so\n# # all built-in commands plus common skill commands stay visible\n# # while remaining under Telegram's payload-size limit. Clamped 1..100.\n# max_commands: 60\n# # prepend = user priority first, then Hermes defaults\n# # append = Hermes defaults first, then user priority\n# # replace = only the list below defines priority\n# priority_mode: prepend\n# priority:\n# - my_plugin_command\n# webhook:\n# extra:\n# # Route scripts default to a 30 second timeout. Scripts must live under\n# # the active profile's scripts directory and receive webhook JSON on stdin.\n# script_timeout_seconds: 30\n#\n# Discord-specific settings (config.yaml top-level, not under platforms:):\n#\n# discord:\n# require_mention: true # Require @mention in server channels (default: true)\n# auto_thread: true # Auto-create thread on @mention (default: true)\n# free_response_channels: \"\" # Channel IDs where no mention is needed\n# reactions: true # Show processing reactions (default: true)\n# history_backfill: true # Recover missed channel messages on mention (default: true)\n# history_backfill_limit: 50 # Max messages to scan backwards (default: 50)\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Available toolsets (use these names in platform_toolsets or the toolsets list)\n#\n# Run `hermes chat --list-toolsets` to see all toolsets and their tools.\n# Run `hermes chat --list-tools` to see every individual tool with descriptions.\n# ─────────────────────────────────────────────────────────────────────────────\n#\n# INDIVIDUAL TOOLSETS (compose your own):\n# web - web_search, web_extract\n# search - web_search only (no scraping)\n# terminal - terminal, process\n# file - read_file, write_file, patch, search\n# browser - browser_navigate, browser_snapshot, browser_click, browser_type,\n# browser_scroll, browser_back, browser_press,\n# browser_get_images, browser_vision (requires BROWSERBASE_API_KEY)\n# vision - vision_analyze (requires OPENROUTER_API_KEY)\n# image_gen - image_generate (requires FAL_KEY)\n# skills - skills_list, skill_view\n# skills_hub - skill_hub (search/install/manage from online registries — user-driven only)\n# todo - todo (in-memory task planning, no deps)\n# tts - text_to_speech (Edge TTS free, or ELEVENLABS/OPENAI/MINIMAX/MISTRAL key)\n# cronjob - cronjob (create/list/update/pause/resume/run/remove scheduled tasks)\n#\n# PRESETS (curated bundles):\n# hermes-cli - All of the above except send_message\n# hermes-telegram - terminal, file, web, vision, image_gen, tts, browser,\n# skills, todo, cronjob, send_message\n# hermes-discord - Same as hermes-telegram\n# hermes-whatsapp - Same as hermes-telegram\n# hermes-slack - Same as hermes-telegram\n#\n# COMPOSITE:\n# debugging - terminal + web + file\n# safe - web + vision (no terminal access)\n# all - Everything available\n#\n# web - Web search and content extraction (web_search, web_extract)\n# search - Web search only, no scraping (web_search)\n# terminal - Command execution and process management (terminal, process)\n# file - File operations: read, write, patch, search\n# browser - Full browser automation (navigate, click, type, screenshot, etc.)\n# vision - Image analysis (vision_analyze)\n# image_gen - Image generation with FLUX (image_generate)\n# skills - Load skill documents (skills_list, skill_view)\n# todo - Task planning and tracking for multi-step work\n# memory - Persistent memory across sessions (personal notes + user profile)\n# session_search - Search and recall past conversations (FTS5 + Gemini Flash summarization)\n# tts - Text-to-speech (Edge TTS free, ElevenLabs, OpenAI, MiniMax, Mistral)\n# cronjob - Schedule and manage automated tasks (CLI-only)\n#\n# Composite toolsets:\n# debugging - terminal + web + file (for troubleshooting)\n# safe - web + vision (no terminal access)\n\n# NOTE: The top-level \"toolsets\" key is deprecated and ignored.\n# Tool configuration is managed per-platform via platform_toolsets above.\n# Use `hermes tools` to configure interactively, or edit platform_toolsets directly.\n#\n# CLI override: hermes chat --toolsets terminal,web,file\n\n# =============================================================================\n# MCP (Model Context Protocol) Servers\n# =============================================================================\n# Connect to external MCP servers to add tools from the MCP ecosystem.\n# Each server's tools are automatically discovered and registered.\n# See website/docs/user-guide/features/mcp.md for full documentation.\n#\n# Stdio servers (spawn a subprocess):\n# command: the executable to run\n# args: command-line arguments\n# env: environment variables (only these + safe defaults passed to subprocess)\n#\n# HTTP servers (connect to a URL):\n# url: the MCP server endpoint\n# headers: HTTP headers (e.g., for authentication)\n#\n# Optional per-server settings:\n# timeout: tool call timeout in seconds (default: 120)\n# connect_timeout: initial connection timeout (default: 60)\n# keepalive_interval: liveness ping cadence in seconds (default: 180).\n# Lower it below the server's session TTL for servers that expire idle\n# sessions quickly (e.g. Unreal Engine editor MCP, ~15s), otherwise idle\n# tool calls hit an expired session and pay a slow reconnect. Floored at 5s.\n#\n# mcp_servers:\n# time:\n# command: uvx\n# args: [\"mcp-server-time\"]\n# filesystem:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-filesystem\", \"/home/user\"]\n# notion:\n# url: https://mcp.notion.com/mcp\n# github:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-github\"]\n# env:\n# GITHUB_PERSONAL_ACCESS_TOKEN: \"ghp_...\"\n#\n# Sampling (server-initiated LLM requests) — enabled by default.\n# Per-server config under the 'sampling' key:\n# analysis:\n# command: npx\n# args: [\"-y\", \"analysis-server\"]\n# sampling:\n# enabled: true # default: true\n# model: \"gemini-3-flash\" # override model (optional)\n# max_tokens_cap: 4096 # max tokens per request\n# timeout: 30 # LLM call timeout (seconds)\n# max_rpm: 10 # max requests per minute\n# allowed_models: [] # model whitelist (empty = all)\n# max_tool_rounds: 5 # tool loop limit (0 = disable)\n# log_level: \"info\" # audit verbosity\n\n# =============================================================================\n# Text-to-Speech\n# =============================================================================\n# TTS defaults to Edge TTS unless changed in ~/.hermes/config.yaml.\n# Gemini TTS supports persona/director prompt files, and Gemini 3.1 Flash TTS\n# can use a hidden auxiliary rewrite pass to insert expressive square-bracket\n# audio tags into the TTS script without showing tags in chat.\n#\n# tts:\n# provider: \"gemini\"\n# gemini:\n# model: \"gemini-3.1-flash-tts-preview\"\n# voice: \"Kore\"\n# audio_tags: false\n# persona_prompt_file: \"\" # e.g. ~/.hermes/tts/radio-host.md\n\n# =============================================================================\n# Voice Transcription (Speech-to-Text)\n# =============================================================================\n# Automatically transcribe voice messages on messaging platforms.\n# Providers: local (free, faster-whisper) | groq (free tier) | openai (Whisper API) | mistral (Voxtral Transcribe)\n# Set the corresponding API key in .env: GROQ_API_KEY, OPENAI_API_KEY, or MISTRAL_API_KEY.\nstt:\n enabled: true\n # provider: \"local\" # auto-detected if omitted\n local:\n model: \"base\" # tiny | base | small | medium | large-v3 | turbo\n # language: \"\" # auto-detect; set to \"en\", \"es\", \"fr\", etc. to force\n openai:\n model: \"whisper-1\" # whisper-1 | gpt-4o-mini-transcribe | gpt-4o-transcribe\n # mistral:\n # model: \"voxtral-mini-latest\" # voxtral-mini-latest | voxtral-mini-2602\n\n# =============================================================================\n# Response Pacing (Messaging Platforms)\n# =============================================================================\n# Add human-like delays between message chunks.\n# human_delay:\n# mode: \"off\" # \"off\" | \"natural\" | \"custom\"\n# min_ms: 800 # Min delay (custom mode only)\n# max_ms: 2500 # Max delay (custom mode only)\n\n# =============================================================================\n# Session Logging\n# =============================================================================\n# Session trajectories are automatically saved to logs/ directory.\n# Each session creates: logs/session_YYYYMMDD_HHMMSS_UUID.json\n#\n# The session ID is displayed in the welcome banner for easy reference.\n# Logs contain full conversation history in trajectory format:\n# - System prompt, user messages, assistant responses\n# - Tool calls with inputs/outputs\n# - Timestamps for debugging\n#\n# No configuration needed - logging is always enabled.\n# To disable, you would need to modify the source code.\n\n# =============================================================================\n# Code Execution Sandbox (Programmatic Tool Calling)\n# =============================================================================\n# The execute_code tool runs Python scripts that call Hermes tools via RPC.\n# Intermediate tool results stay out of the LLM's context window.\ncode_execution:\n timeout: 300 # Max seconds per script before kill (default: 300 = 5 min)\n max_tool_calls: 50 # Max RPC tool calls per execution (default: 50)\n\n# =============================================================================\n# Subagent Delegation\n# =============================================================================\n# The delegate_task tool spawns child agents with isolated context.\n# Supports single tasks and batch mode (default 3 parallel, configurable).\ndelegation:\n max_iterations: 50 # Max tool-calling turns per child (default: 50)\n # max_concurrent_children: 3 # Max parallel child agents per batch (default: 3, floor: 1, no ceiling).\n # WARNING: values above 10 multiply API cost linearly.\n # max_spawn_depth: 1 # Delegation tree depth cap (range: 1-3, default: 1 = flat).\n # Raise to 2 to allow workers to spawn their own subagents.\n # Requires role=\"orchestrator\" on intermediate agents.\n # orchestrator_enabled: true # Kill switch for role=\"orchestrator\" children (default: true).\n # subagent_auto_approve: false # When a subagent hits a dangerous-command approval prompt, auto-deny (default: false)\n # or auto-approve \"once\" (true) instead of blocking on stdin.\n # The parent TUI owns stdin, so blocking would deadlock; non-interactive resolution is required.\n # Both choices emit a logger.warning audit line. Flip to true only for cron/batch pipelines.\n # inherit_mcp_toolsets: true # When explicit child toolsets are narrowed, also keep the parent's MCP toolsets (default: true). Set false for strict intersection.\n # model: \"google/gemini-3-flash-preview\" # Override model for subagents (empty = inherit parent)\n # provider: \"openrouter\" # Override provider for subagents (empty = inherit parent)\n # # Resolves full credentials (base_url, api_key) automatically.\n # # Supported: openrouter, nous, zai, kimi-coding, minimax\n\n# =============================================================================\n# Honcho Integration (Cross-Session User Modeling)\n# =============================================================================\n# AI-native persistent memory via Honcho (https://honcho.dev/).\n# Builds a deeper understanding of the user across sessions and tools.\n# Runs alongside USER.md — additive, not a replacement.\n#\n# Requires: pip install honcho-ai\n# Config: ~/.honcho/config.json (shared with Claude Code, Cursor, etc.)\n# API key: HONCHO_API_KEY in ~/.hermes/.env or ~/.honcho/config.json\n#\n# Hermes-specific overrides (optional — most config comes from ~/.honcho/config.json):\n# honcho: {}\n\n# =============================================================================\n# Display\n# =============================================================================\ndisplay:\n # Use compact banner mode (hides the ASCII-art banner, shows a single line).\n # true: Compact single-line banner\n # false: Full ASCII banner with tool/skill summary (default)\n compact: false\n\n # Tool progress display level (CLI and gateway)\n # off: Silent — no tool activity shown, just the final response\n # new: Show a tool indicator only when the tool changes (skip repeats)\n # all: Show every tool call with a short preview (default)\n # verbose: Full args, results, and debug logs (same as /verbose)\n # log: Silent in chat; append every tool call to ~/.hermes/logs/tool_calls.log (gateway only)\n # Toggle at runtime with /verbose in the CLI\n tool_progress: all\n\n # Per-platform defaults can be quieter than the global setting. Telegram\n # tunes for mobile: tool_progress and busy_ack_detail default off (no\n # per-tool breadcrumb stream, no \"iteration 21/60\" debug detail in busy\n # acks or heartbeats), but interim_assistant_messages and\n # long_running_notifications STAY ON so the user has real signal between\n # turn start and final answer (mid-turn assistant commentary + a single\n # edit-in-place \"⏳ Working — N min\" heartbeat). Override under\n # display.platforms.telegram.\n\n # Auto-cleanup of temporary progress bubbles after the final response lands.\n # On platforms that support message deletion (currently Telegram), this\n # removes the tool-progress bubble, \"⏳ Still working...\" notices, and\n # context-pressure status messages once the final reply has been delivered —\n # keeping long-running turns visible live, then tidy afterward. Failed runs\n # leave the bubbles in place as breadcrumbs. Off by default.\n # Per-platform override: display.platforms.telegram.cleanup_progress\n # true: Delete tracked progress/status bubbles on successful turn\n # false: Leave everything in place (default)\n # Example:\n # display:\n # platforms:\n # telegram:\n # cleanup_progress: true\n cleanup_progress: false\n\n # Gateway-only natural mid-turn assistant updates.\n # When true, completed assistant status messages are sent as separate chat\n # messages. This is independent of tool_progress and gateway streaming.\n # true: Send mid-turn assistant updates as separate messages (default)\n # false: Only send the final response\n interim_assistant_messages: true\n\n # Gateway-only long-running status heartbeats.\n # When false, the platform does not receive periodic \"⏳ Working — N min\"\n # notifications even if agent.gateway_notify_interval is non-zero. The\n # heartbeat edits a single message in place (where the adapter supports\n # editing) instead of posting a new bubble each interval.\n # Default: true everywhere, including Telegram (silent agents are worse\n # than a single edit-in-place heartbeat).\n long_running_notifications: true\n\n # Include detailed iteration/tool/status context in busy acknowledgments\n # and long-running heartbeats. When true, busy acks show \"iteration 21/60,\n # terminal, 10 min\" and the heartbeat shows \"⏳ Working — 12 min,\n # iteration 21/60, terminal\". When false (Telegram default), both stay\n # terse: \"Interrupting current task\" and \"⏳ Working — 12 min, terminal\".\n busy_ack_detail: true\n\n # What Enter does when Hermes is already busy (CLI and gateway platforms).\n # interrupt: Interrupt the current run and redirect Hermes (default)\n # queue: Queue your message for the next turn\n # steer: Inject your message mid-run via /steer, arriving at the agent\n # after the next tool call — no interrupt, no role violation.\n # Falls back to 'queue' if the agent isn't running yet or if\n # images are attached (steer only carries text).\n # Ctrl+C (or /stop in gateway) always interrupts regardless of this setting.\n # Toggle at runtime with /busy .\n busy_input_mode: interrupt\n\n # Background process notifications (gateway/messaging only).\n # Controls how chatty the process watcher is when you use\n # terminal(background=true, notify_on_complete=true) from Telegram/Discord/etc.\n # off: No watcher messages at all\n # result: Only the final completion message\n # error: Only the final message when exit code != 0\n # all: Running output updates + final message (default)\n background_process_notifications: all\n\n\n # Play terminal bell when agent finishes a response.\n # Useful for long-running tasks — your terminal will ding when the agent is done.\n # Works over SSH. Most terminals can be configured to flash the taskbar or play a sound.\n # true: Ring the terminal bell on each response\n # false: Silent (default)\n bell_on_complete: false\n\n # Show model reasoning/thinking before each response.\n # When enabled, a dim box shows the model's thought process above the response.\n # Toggle at runtime with /reasoning show or /reasoning hide.\n # true: Show the reasoning box\n # false: Hide reasoning (default)\n show_reasoning: false\n\n # Stream tokens to the terminal as they arrive instead of waiting for the\n # full response. The response box opens on first token and text appears\n # line-by-line. Tool calls are still captured silently.\n # true: Stream tokens as they arrive (default)\n # false: Wait for the full response before rendering\n streaming: true\n\n # Show [HH:MM] timestamps on user input and assistant response labels.\n # timestamps: false\n\n # ───────────────────────────────────────────────────────────────────────────\n # Skin / Theme\n # ───────────────────────────────────────────────────────────────────────────\n # Customize CLI visual appearance — banner colors, spinner faces, tool prefix,\n # response box label, and branding text. Change at runtime with /skin .\n #\n # Built-in skins:\n # default — Classic Hermes gold/kawaii\n # ares — Crimson/bronze war-god theme with spinner wings\n # mono — Clean grayscale monochrome\n # slate — Cool blue developer-focused\n # daylight — Bright light-mode theme\n # warm-lightmode — Warm paper-tone light-mode theme\n # poseidon — Sea-green/teal Olympian theme\n # sisyphus — Earthy stone-and-moss theme\n # charizard — Fiery orange dragon theme\n #\n # Custom skins: drop a YAML file in ~/.hermes/skins/.yaml\n # Schema (all fields optional, missing values inherit from default):\n #\n # name: my-theme\n # description: Short description\n # colors:\n # banner_border: \"#HEX\" # Panel border\n # banner_title: \"#HEX\" # Panel title\n # banner_accent: \"#HEX\" # Section headers (Available Tools, etc.)\n # banner_dim: \"#HEX\" # Dim/muted text\n # banner_text: \"#HEX\" # Body text (tool names, skill names)\n # ui_accent: \"#HEX\" # UI accent color\n # response_border: \"#HEX\" # Response box border color\n # spinner:\n # waiting_faces: [\"(⚔)\", \"(⛨)\"] # Faces shown while waiting\n # thinking_faces: [\"(⚔)\", \"(⌁)\"] # Faces shown while thinking\n # thinking_verbs: [\"forging\", \"plotting\"] # Verbs for spinner messages\n # wings: # Optional left/right spinner decorations\n # - [\"⟪⚔\", \"⚔⟫\"]\n # - [\"⟪▲\", \"▲⟫\"]\n # branding:\n # agent_name: \"My Agent\" # Banner title and branding\n # welcome: \"Welcome message\" # Shown at CLI startup\n # response_label: \" ⚔ Agent \" # Response box header label\n # prompt_symbol: \"⚔\" # Prompt symbol (bare token; renderers add trailing space)\n # tool_prefix: \"╎\" # Tool output line prefix (default: ┊)\n #\n skin: default\n\n# =============================================================================\n# Model Aliases — short names for /model command\n# =============================================================================\n# Map short aliases to exact (model, provider, base_url) tuples.\n# Used by /model tab completion and resolve_alias().\n# Aliases are checked BEFORE the models.dev catalog, so they can route\n# to endpoints not in the catalog (e.g. Ollama Cloud, local servers).\n#\n# model_aliases:\n# opus:\n# model: claude-opus-4-6\n# provider: anthropic\n# qwen:\n# model: \"qwen3.5:397b\"\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n# glm:\n# model: glm-4.7\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n\n# =============================================================================\n# Privacy\n# =============================================================================\n# privacy:\n# # Redact PII from the LLM context prompt.\n# # When true, phone numbers are stripped and user/chat IDs are replaced\n# # with deterministic hashes before being sent to the model.\n# # Names and usernames are NOT affected (user-chosen, publicly visible).\n# # Routing/delivery still uses the original values internally.\n# redact_pii: false\n\n# =============================================================================\n# Shell-script hooks\n# =============================================================================\n# Register shell scripts as plugin-hook callbacks. Each entry is executed as\n# a subprocess (shell=False, shlex.split) with a JSON payload on stdin. On\n# stdout the script may return JSON that either blocks the tool call or\n# injects context into the next LLM call.\n#\n# Valid events (mirror hermes_cli.plugins.VALID_HOOKS):\n# pre_tool_call, post_tool_call, pre_llm_call, post_llm_call,\n# pre_api_request, post_api_request, on_session_start, on_session_end,\n# on_session_finalize, on_session_reset, subagent_stop\n#\n# First-use consent: each (event, command) pair prompts once on a TTY, then\n# is persisted to ~/.hermes/shell-hooks-allowlist.json. Non-interactive\n# runs (gateway, cron) need --accept-hooks, HERMES_ACCEPT_HOOKS=1, or the\n# hooks_auto_accept key below.\n#\n# See website/docs/user-guide/features/hooks.md for the full JSON wire\n# protocol and worked examples.\n#\n# hooks:\n# pre_tool_call:\n# - matcher: \"terminal\"\n# command: \"~/.hermes/agent-hooks/block-rm-rf.sh\"\n# timeout: 10\n# post_tool_call:\n# - matcher: \"write_file|patch\"\n# command: \"~/.hermes/agent-hooks/auto-format.sh\"\n# pre_llm_call:\n# - command: \"~/.hermes/agent-hooks/inject-cwd-context.sh\"\n# subagent_stop:\n# - command: \"~/.hermes/agent-hooks/log-orchestration.sh\"\n#\n# hooks_auto_accept: false\n\n\n# =============================================================================\n# Update Behavior\n# =============================================================================\nupdates:\n # Create a full HERMES_HOME zip before every `hermes update`.\n # Backups land in ~/.hermes/backups/ and can be restored with `hermes import`.\n # Off by default because large homes can add minutes to every update.\n pre_update_backup: false\n\n # Number of pre-update backup zips to retain.\n backup_keep: 5\n\n # What non-interactive updates do with local source edits in the Hermes repo.\n # Interactive terminal updates always prompt before restoring the autostash.\n #\n # stash - auto-stash before pull, then auto-restore after success (default)\n # discard - drop the update-created stash after success; use only on managed\n # installs where local source edits should not persist\n non_interactive_local_changes: \"stash\"\n\n\n# =============================================================================\n# Web Dashboard\n# =============================================================================\n# OAuth gate configuration for `hermes dashboard --host `.\n# The bundled Nous Portal plugin reads these on startup; settings here are\n# the canonical surface. Each can be overridden by an environment variable:\n#\n# dashboard.oauth.client_id <- HERMES_DASHBOARD_OAUTH_CLIENT_ID\n# dashboard.oauth.portal_url <- HERMES_DASHBOARD_PORTAL_URL\n# dashboard.public_url <- HERMES_DASHBOARD_PUBLIC_URL\n#\n# Env wins when set to a non-empty value. This is what Fly.io's platform-\n# secret injection uses to push per-deploy client_ids without needing to\n# bake a config.yaml into the image. Empty env values are treated as unset\n# so a provisioned-but-not-populated secret can't shadow a valid entry here.\n#\n# Local dev / on-prem deploys should typically set these via config.yaml\n# (the ~/.hermes/.env file is reserved for API keys and secrets).\n#\n# dashboard:\n# oauth:\n# client_id: \"\" # agent:{instance_id}; Portal provisions this at deploy\n# portal_url: \"\" # blank → default https://portal.nousresearch.com\n#\n# # Force the absolute base URL the OAuth callback (and any other public\n# # URL the dashboard hands to external systems) is built from. Set this\n# # for deploys behind reverse proxies that don't reliably forward\n# # X-Forwarded-Host / X-Forwarded-Proto / X-Forwarded-Prefix (manual\n# # nginx setups, on-prem ingresses, custom-domain Fly deploys without\n# # full proxy header chains).\n# #\n# # When set, the value is the complete authority: scheme + host +\n# # optional path prefix (e.g. \"https://example.com/hermes\"). The OAuth\n# # callback URL becomes \"/auth/callback\" — X-Forwarded-Prefix\n# # is IGNORED on this code path because the operator has explicitly\n# # declared the public URL and we no longer need to guess.\n# #\n# # Leave empty to use the existing proxy-header reconstruction (the\n# # default — works on Fly.io out of the box).\n# #\n# # public_url: \"https://example.com/hermes\"\n#\n# -----------------------------------------------------------------------------\n# Self-hosted OIDC dashboard auth (generic OpenID Connect — Authentik,\n# Keycloak, Zitadel, Authelia, Auth0, Okta, Google, …). Use this INSTEAD of the\n# nous block above when gating the dashboard with your own identity provider.\n# Each setting can be overridden by an environment variable:\n#\n# dashboard.oauth.self_hosted.issuer <- HERMES_DASHBOARD_OIDC_ISSUER\n# dashboard.oauth.self_hosted.client_id <- HERMES_DASHBOARD_OIDC_CLIENT_ID\n# dashboard.oauth.self_hosted.scopes <- HERMES_DASHBOARD_OIDC_SCOPES\n# dashboard.oauth.self_hosted.client_secret <- HERMES_DASHBOARD_OIDC_CLIENT_SECRET\n#\n# dashboard:\n# oauth:\n# provider: self-hosted\n# self_hosted:\n# issuer: \"https://auth.example.com/application/o/hermes/\" # required\n# client_id: \"hermes-dashboard\" # required\n# scopes: \"openid profile email\" # optional\n#\n# # OPTIONAL — set ONLY if your IDP registered the client as\n# # *confidential* (Authentik / Keycloak often default to this). When\n# # set, Hermes authenticates the client at the token endpoint\n# # (client_secret_basic or client_secret_post, auto-selected from the\n# # IDP's discovery doc) IN ADDITION to PKCE. Leave unset for a public\n# # (PKCE-only) client — the common case.\n# #\n# # This is a CREDENTIAL: prefer setting HERMES_DASHBOARD_OIDC_CLIENT_SECRET\n# # in ~/.hermes/.env over putting it here in config.yaml.\n# # client_secret: \"\"\n\n# =============================================================================\n# External secret sources\n# =============================================================================\n# Pull provider credentials from external secret managers at process startup\n# instead of storing them in ~/.hermes/.env. Only the manager's bootstrap\n# credential (e.g. BWS_ACCESS_TOKEN / OP_SERVICE_ACCOUNT_TOKEN) lives in .env\n# (or your shell / desktop session); everything else rotates centrally.\n# Failures never block startup — Hermes warns once and continues with\n# whatever .env already had.\n#\n# Multiple sources can be enabled at once:\n# - \"mapped\" sources (explicit VAR -> ref bindings, e.g. 1Password's env:\n# map) beat \"bulk\" sources (whole-project dumps like Bitwarden BSM)\n# - within a shape, the first source to claim a var wins; later claims\n# are skipped with a startup warning (never a silent clobber)\n# - a source's override_existing lets it beat .env/shell values, but\n# never another secret source's claim\n# Docs: https://hermes-agent.nousresearch.com/docs/user-guide/secrets/\n#\n# secrets:\n# # Optional explicit ordering of enabled sources.\n# # sources: [onepassword, bitwarden]\n#\n# # ---- Bitwarden Secrets Manager (bws CLI) --------------------------------\n# bitwarden:\n# enabled: false\n# access_token_env: BWS_ACCESS_TOKEN # bootstrap token, sourced from .env\n# project_id: \"\" # UUID of the BSM project to sync\n# server_url: \"\" # \"\" = US Cloud; EU/self-hosted URL otherwise\n# cache_ttl_seconds: 300 # 0 disables caching\n# override_existing: true # BSM values win over existing env\n# auto_install: true # lazy-download bws into ~/.hermes/bin\n#\n# # ---- 1Password (op CLI) -------------------------------------------------\n# onepassword:\n# enabled: false\n# # Map env-var names to op:// secret references. Each is resolved with a\n# # single `op read` at startup.\n# env:\n# OPENAI_API_KEY: \"op://Private/OpenAI/api key\"\n# ANTHROPIC_API_KEY: \"op://Private/Anthropic/credential\"\n# account: \"\" # op --account shorthand; \"\" = default\n# service_account_token_env: OP_SERVICE_ACCOUNT_TOKEN # headless auth; unset = desktop session\n# binary_path: \"\" # \"\" = resolve op via PATH; else absolute path\n# cache_ttl_seconds: 300 # 0 disables BOTH cache layers\n# override_existing: true # resolved values win over existing env\n"}, "0cf2e39c411cfe99aff477b61113992f297cd6d0": {"t": 1785767871.932844, "n": 74116, "h": "c19efa1211417473", "c": "# Hermes Agent CLI Configuration\n# Copy this file to cli-config.yaml and customize as needed.\n# This file configures the CLI behavior. Environment variables in .env take precedence.\n\n# =============================================================================\n# Model Configuration\n# =============================================================================\nmodel:\n # Default model to use (can be overridden with --model flag)\n # Both \"default\" and \"model\" work as the key name here.\n default: \"anthropic/claude-opus-4.6\"\n \n # Inference provider selection:\n # \"auto\" - Auto-detect from credentials (default)\n # \"openrouter\" - OpenRouter (requires: OPENROUTER_API_KEY or OPENAI_API_KEY)\n # \"nous\" - Nous Portal OAuth (requires: hermes auth add nous)\n # \"nous-api\" - Nous Portal API key (requires: NOUS_API_KEY)\n # \"anthropic\" - Direct Anthropic API (requires: ANTHROPIC_API_KEY)\n # \"openai-codex\" - OpenAI Codex (requires: hermes auth)\n # \"copilot\" - GitHub Copilot / GitHub Models (requires: GITHUB_TOKEN)\n # \"gemini\" - Use Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n # \"zai\" - Use z.ai / ZhipuAI GLM models (requires: GLM_API_KEY)\n # \"kimi-coding\" - Kimi / Moonshot AI (requires: KIMI_API_KEY)\n # \"minimax\" - MiniMax global (requires: MINIMAX_API_KEY)\n # \"minimax-cn\" - MiniMax China (requires: MINIMAX_CN_API_KEY)\n # \"huggingface\" - Hugging Face Inference (requires: HF_TOKEN)\n # \"nvidia\" - NVIDIA NIM / build.nvidia.com (requires: NVIDIA_API_KEY)\n # \"xiaomi\" - Xiaomi MiMo (requires: XIAOMI_API_KEY)\n # \"arcee\" - Arcee AI Trinity models (requires: ARCEEAI_API_KEY)\n # \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY — https://ollama.com/settings)\n # \"kilocode\" - KiloCode gateway (requires: KILOCODE_API_KEY)\n # \"azure-foundry\" - Microsoft Foundry / Azure OpenAI (API key or Entra ID)\n # \"lmstudio\" - LM Studio local server (optional: LM_API_KEY, defaults to http://127.0.0.1:1234/v1)\n #\n # Local servers (LM Studio, Ollama, vLLM, llama.cpp):\n # \"custom\" - Any other OpenAI-compatible endpoint. Set base_url below.\n # Aliases: \"ollama\", \"vllm\", \"llamacpp\" all map to \"custom\".\n # LM Studio is first-class and uses provider: \"lmstudio\".\n # It works with both no-auth and auth-enabled server modes.\n #\n # Can also be overridden for a single invocation with the --provider flag.\n provider: \"auto\"\n \n # API configuration (falls back to OPENROUTER_API_KEY env var)\n # api_key: \"your-key-here\" # Uncomment to set here instead of .env\n base_url: \"https://openrouter.ai/api/v1\"\n\n # Azure Foundry keyless auth example:\n # provider: \"azure-foundry\"\n # base_url: \"https://.openai.azure.com/openai/v1\"\n # auth_mode: \"entra_id\" # DefaultAzureCredential: az login, managed identity, workload identity, etc.\n # default: \"gpt-4o\" # Deployment/model name\n # entra:\n # scope: \"https://ai.azure.com/.default\" # Optional; this is the default.\n\n # ── Token limits — two settings, easy to confuse ──────────────────────────\n #\n # context_length: TOTAL context window (input + output tokens combined).\n # Controls when Hermes compresses history and validates requests.\n # Leave unset — Hermes auto-detects the correct value from the provider.\n # Set manually only when auto-detection is wrong (e.g. a local server with\n # a custom num_ctx, or a proxy that doesn't expose /v1/models).\n #\n # context_length: 131072\n #\n # max_tokens: OUTPUT cap — maximum tokens the model may generate per response.\n # Unrelated to how long your conversation history can be.\n # The OpenAI-standard name \"max_tokens\" is a misnomer; Anthropic's native\n # API has since renamed it \"max_output_tokens\" for clarity.\n # Leave unset to use the model's native output ceiling (recommended).\n # Set only if you want to deliberately limit individual response length.\n #\n# max_tokens: 8192\n\n # ── Custom request headers (optional) ─────────────────────────────────────\n #\n # default_headers: extra HTTP headers sent on every request to an\n # OpenAI-compatible endpoint. User values take precedence over the\n # provider/SDK defaults, so this is the supported way to override the\n # OpenAI Python SDK's identifying headers (User-Agent: OpenAI/Python ...,\n # X-Stainless-*) when a custom provider sits behind a gateway/WAF that\n # rejects them — e.g. an upstream that returns \"502 Upstream access\n # forbidden\" for the SDK default User-Agent but accepts a plain one.\n # Applies on the OpenAI wire only (not native Anthropic / Bedrock).\n #\n # default_headers:\n # User-Agent: \"curl/8.7.1\"\n #\n # extra_headers: accepted as an alias of default_headers (merged, with\n # extra_headers winning when both are set) — matches the per-provider\n # extra_headers key below.\n #\n # Per-provider variant: named providers / custom_providers entries accept an\n # extra_headers dict scoped to that endpoint only — for reverse proxies,\n # gateways, or custom auth (e.g. Cloudflare Access service tokens).\n # Merged onto SDK/provider defaults with the entry's values winning.\n # Header values are treated as secrets and are never logged.\n #\n # providers:\n # my-proxy:\n # base_url: \"https://llm.internal.example.com/v1\"\n # key_env: \"MY_PROXY_API_KEY\"\n # extra_headers:\n # CF-Access-Client-Id: \"xxxx.access\"\n # CF-Access-Client-Secret: \"${CF_ACCESS_SECRET}\"\n # X-Client-Name: \"hermes-agent\"\n\n# Named provider overrides (optional)\n# Use this for per-provider request timeouts, non-stream stale timeouts,\n# and per-model exceptions.\n# Applies to the primary turn client on every api_mode (OpenAI-wire, native\n# Anthropic, and Anthropic-compatible providers), the fallback chain, and\n# client rebuilds during credential rotation. For OpenAI-wire chat\n# completions (streaming and non-streaming) the configured value is also\n# used as the per-request ``timeout=`` kwarg so it wins over the legacy\n# HERMES_API_TIMEOUT env var (which still applies when no config is set).\n# ``stale_timeout_seconds`` controls the non-streaming stale-call detector and\n# wins over the legacy HERMES_API_CALL_STALE_TIMEOUT env var. Leaving these\n# unset keeps the legacy defaults (HERMES_API_TIMEOUT=1800s,\n# HERMES_API_CALL_STALE_TIMEOUT=90s, native Anthropic 900s). The\n# implicit non-stream stale detector is auto-disabled for local endpoints\n# and can scale upward for very large contexts.\n#\n# Not currently wired for AWS Bedrock (bedrock_converse + AnthropicBedrock\n# SDK paths) — those use boto3 with its own timeout configuration.\n#\n# providers:\n# ollama-local:\n# request_timeout_seconds: 300 # Longer timeout for local cold-starts\n# stale_timeout_seconds: 900 # Explicitly re-enable stale detection on local endpoints\n# anthropic:\n# request_timeout_seconds: 30 # Fast-fail cloud requests\n# models:\n# claude-opus-4.6:\n# timeout_seconds: 600 # Longer timeout for extended-thinking Opus calls\n# openai-codex:\n# models:\n# gpt-5.4:\n# stale_timeout_seconds: 1800 # Longer non-stream stale timeout for slow large-context turns\n\n# =============================================================================\n# OpenRouter Provider Routing (only applies when using OpenRouter)\n# =============================================================================\n# Control how requests are routed across providers on OpenRouter.\n# See: https://openrouter.ai/docs/guides/routing/provider-selection\n#\n# provider_routing:\n# # Sort strategy: \"price\" (default), \"throughput\", or \"latency\"\n# # Append :nitro to model name for a shortcut to throughput sorting.\n# sort: \"throughput\"\n#\n# # Only allow these providers (provider slugs from OpenRouter)\n# # only: [\"anthropic\", \"google\"]\n#\n# # Skip these providers entirely\n# # ignore: [\"deepinfra\", \"fireworks\"]\n#\n# # Try providers in this order (overrides default load balancing)\n# # order: [\"anthropic\", \"google\", \"together\"]\n#\n# # Require providers to support all parameters in your request\n# # require_parameters: true\n#\n# # Data policy: \"allow\" (default) or \"deny\" to exclude providers that may store data\n# # data_collection: \"deny\"\n\n# =============================================================================\n# OpenRouter Response Caching (only applies when using OpenRouter)\n# =============================================================================\n# Cache identical API responses at the OpenRouter edge for free instant replays.\n# When enabled, identical requests (same model, messages, parameters) return\n# cached responses with zero billing. Separate from Anthropic prompt caching.\n# See: https://openrouter.ai/docs/guides/features/response-caching\n#\n# openrouter:\n# response_cache: true # Enable response caching (default: true)\n# response_cache_ttl: 300 # Cache TTL in seconds, 1-86400 (default: 300)\n\n# =============================================================================\n# Git Worktree Isolation\n# =============================================================================\n# When enabled, each CLI session creates an isolated git worktree so multiple\n# agents can work on the same repo concurrently without file collisions.\n# Equivalent to always passing --worktree / -w on the command line.\n#\n# worktree: true # Always create a worktree when in a git repo\n# worktree: false # Default — only create when -w flag is passed\n#\n# By default a new worktree branches from the freshly-fetched remote tip\n# (the current branch's upstream, else the remote's default branch) so it\n# starts current with the project instead of from the local clone's\n# (possibly stale) HEAD. Set worktree_sync: false to branch from local HEAD\n# instead — useful when offline or when you deliberately want the clone's\n# exact current state as the base.\n#\n# worktree_sync: true # Default — branch from the fetched remote tip\n# worktree_sync: false # Branch from local HEAD (offline / pinned base)\n\n# =============================================================================\n# Terminal Tool Configuration\n# =============================================================================\n# Choose ONE of the following terminal configurations by uncommenting it.\n# The terminal tool executes commands in the specified environment.\n\n# -----------------------------------------------------------------------------\n# OPTION 1: Local execution (default)\n# Commands run directly on your machine in the current directory\n# -----------------------------------------------------------------------------\n# Working directory behavior:\n# - CLI (`hermes` command): Uses \".\" (current directory where you run hermes)\n# - Gateway/messaging/cron: Uses terminal.cwd here; legacy .env cwd values are deprecated\nterminal:\n backend: \"local\"\n cwd: \".\" # For local backend: \".\" = current directory. Ignored for remote backends unless a backend documents otherwise.\n timeout: 180\n # HOME policy for tool subprocesses:\n # auto - default: host uses your real HOME; containers use HERMES_HOME/home\n # real - force your real OS-user HOME\n # profile - force HERMES_HOME/home for strict per-profile CLI config isolation\n home_mode: \"auto\"\n docker_mount_cwd_to_workspace: false # SECURITY: off by default. Opt in to mount the launch cwd into Docker /workspace.\n lifetime_seconds: 300\n # sudo_password: \"hunter2\" # Optional: pipe a sudo password via sudo -S. SECURITY WARNING: plaintext.\n # sudo_password: \"\" # Explicit empty password: try empty and never open the interactive sudo prompt.\n\n# -----------------------------------------------------------------------------\n# OPTION 2: SSH remote execution\n# Commands run on a remote server - agent code stays local (sandboxed)\n# Great for: keeping agent isolated from its own code, using powerful remote hardware\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"ssh\"\n# cwd: \"/home/myuser/project\" # Path on the REMOTE server\n# timeout: 180\n# lifetime_seconds: 300\n# ssh_host: \"my-server.example.com\"\n# ssh_user: \"myuser\"\n# ssh_port: 22\n# ssh_key: \"~/.ssh/id_rsa\" # Optional - uses ssh-agent if not specified\n\n# -----------------------------------------------------------------------------\n# OPTION 3: Docker container\n# Commands run in an isolated Docker container\n# Great for: reproducible environments, testing, isolation\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"docker\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /)\n# timeout: 180\n# lifetime_seconds: 300\n# docker_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# docker_mount_cwd_to_workspace: true # Explicit opt-in: mount your launch cwd into /workspace\n# # Optional: run the container as your host user's uid:gid so files written\n# # into bind-mounted dirs are owned by you, not root. Drops SETUID/SETGID\n# # caps too since no gosu privilege drop is needed. Leave off if your\n# # chosen docker_image expects to start as root.\n# docker_run_as_host_user: true\n# # Optional: explicitly forward selected env vars into Docker.\n# # These values come from your current shell first, then ~/.hermes/.env.\n# # Warning: anything forwarded here is visible to commands run in the container.\n# docker_forward_env:\n# - \"GITHUB_TOKEN\"\n# - \"NPM_TOKEN\"\n# # Optional: extra flags passed verbatim to docker run (appended after security defaults).\n# # Useful for adding capabilities (e.g. apt installs needing SETUID) or custom options.\n# # Example: add a Linux capability not included by default\n# # docker_extra_args:\n# # - \"--cap-add\"\n# # - \"SETUID\"\n\n# -----------------------------------------------------------------------------\n# OPTION 4: Singularity/Apptainer container\n# Commands run in a Singularity container (common in HPC environments)\n# Great for: HPC clusters, shared compute environments\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"singularity\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# singularity_image: \"docker://nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 5: Modal cloud execution\n# Commands run on Modal's cloud infrastructure\n# Great for: GPU access, scalable compute, serverless execution\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"modal\"\n# cwd: \"/workspace\" # Path INSIDE the sandbox (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# modal_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 6: Daytona cloud execution\n# Commands run in Daytona cloud sandboxes\n# Great for: Cloud dev environments, persistent workspaces, team collaboration\n# Requires: pip install daytona, DAYTONA_API_KEY env var\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"daytona\"\n# cwd: \"~\"\n# timeout: 180\n# lifetime_seconds: 300\n# daytona_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# container_disk: 10240 # Daytona max is 10GB per sandbox\n\n#\n# --- Container resource limits (docker, singularity, modal, daytona -- ignored for local/ssh) ---\n# These settings apply to all container backends. They control the resources\n# allocated to the sandbox and whether its filesystem persists across sessions.\n container_cpu: 1 # CPU cores\n container_memory: 5120 # Memory in MB (5120 = 5GB)\n container_disk: 51200 # Disk in MB (51200 = 50GB)\n container_persistent: true # Persist filesystem across sessions (false = ephemeral)\n\n# -----------------------------------------------------------------------------\n# SUDO SUPPORT (works with ALL backends above)\n# -----------------------------------------------------------------------------\n# Add sudo_password to any terminal config above to enable sudo commands.\n# The password is piped via `sudo -S`. Works with local, ssh, docker, etc.\n#\n# SECURITY WARNING: Password stored in plaintext!\n#\n# INTERACTIVE PROMPT: If sudo_password is unset and the CLI is running,\n# you'll be prompted to enter your password when sudo is needed:\n# - 45-second timeout (auto-skips if no input)\n# - Press Enter to skip (command fails gracefully)\n# - Password is hidden while typing\n# - Password is cached for the session\n#\n# EMPTY PASSWORDS: Setting sudo_password to an explicit empty string is different\n# from leaving it unset. Hermes will try an empty password via `sudo -S` and\n# will not open the interactive prompt. This is useful for passwordless sudo,\n# Touch ID sudo setups, and environments where prompting is just noise.\n#\n# ALTERNATIVES:\n# - SSH backend: Configure passwordless sudo on the remote server\n# - Containers: Run as root inside the container (no sudo needed)\n# - Local: Configure /etc/sudoers for specific commands\n#\n# Example (add to your terminal section):\n# sudo_password: \"your-password-here\"\n\n# =============================================================================\n# Security Scanning (tirith)\n# =============================================================================\n# Optional pre-exec command security scanning via tirith.\n# Detects homograph URLs, pipe-to-shell, terminal injection, env manipulation.\n# Install: brew install sheeki03/tap/tirith\n# Docs: https://github.com/sheeki03/tirith\n#\n# security:\n# tirith_enabled: true # Enable/disable tirith scanning\n# tirith_path: \"tirith\" # Path to tirith binary (supports ~ expansion)\n# tirith_timeout: 5 # Scan timeout in seconds\n# tirith_fail_open: true # Allow commands if tirith unavailable\n\n# =============================================================================\n# Browser Tool Configuration\n# =============================================================================\nbrowser:\n # Inactivity timeout in seconds - browser sessions are automatically closed\n # after this period of no activity between agent loops (default: 120 = 2 minutes)\n inactivity_timeout: 120\n\n# =============================================================================\n# Tool Loop Guardrails\n# =============================================================================\n# Soft warnings are enabled by default. They append guidance to repeated failed\n# or non-progressing tool results but still let the tool execute. Hard stops are\n# opt-in circuit breakers for autonomous/cron sessions where stopping a loop is\n# preferable to spending the full iteration budget.\ntool_loop_guardrails:\n warnings_enabled: true\n hard_stop_enabled: false\n warn_after:\n exact_failure: 2\n same_tool_failure: 3\n idempotent_no_progress: 2\n hard_stop_after:\n exact_failure: 5\n same_tool_failure: 8\n idempotent_no_progress: 5\n\n# =============================================================================\n# Context Compression (Auto-shrinks long conversations)\n# =============================================================================\n# When conversation approaches model's context limit, middle turns are\n# automatically summarized to free up space while preserving important context.\n#\n# HOW IT WORKS:\n# 1. Tracks actual token usage from API responses (not estimates)\n# 2. When prompt_tokens >= threshold% of model's context_length, triggers compression\n# 3. Protects first 3 turns (system prompt, initial request, first response)\n# 4. Protects last N turns (default 20 messages = ~10 full turns of recent context)\n# 5. Summarizes middle turns using a fast/cheap model\n# 6. Inserts summary as a user message, continues conversation seamlessly\n#\n# Post-compression tail budget is target_ratio × threshold × context_length:\n# 200K context, threshold 0.50, ratio 0.20 → 20K tokens of recent tail preserved\n# 1M context, threshold 0.50, ratio 0.20 → 100K tokens of recent tail preserved\n#\ncompression:\n # Enable automatic context compression (default: true)\n # Set to false if you prefer to manage context manually or want errors on overflow\n enabled: true\n \n # Trigger compression at this % of model's context limit (default: 0.50 = 50%)\n # Lower values = more aggressive compression, higher values = compress later\n threshold: 0.50\n\n # Existing Codex gpt-5.5 behavior: raise Hermes' compaction trigger to 85%\n # for the ChatGPT Codex OAuth route. Set false to opt back down to threshold.\n codex_gpt55_autoraise: true\n\n # Fraction of the threshold to preserve as recent tail (default: 0.20 = 20%)\n # e.g. 20% of 50% threshold = 10% of total context kept as recent messages.\n # Summary output is separately capped at 12K tokens (Gemini output limit).\n # Range: 0.10 - 0.80\n target_ratio: 0.20\n\n # Number of most-recent messages to always preserve (default: 20 ≈ 10 full turns)\n # Higher values keep more recent conversation intact at the cost of more aggressive\n # compression of older turns.\n protect_last_n: 20\n\n # Codex app-server (codex CLI runtime) thread-compaction mode. The codex\n # agent owns the real thread context on this runtime, so Hermes' summarizer\n # cannot shrink it — compaction goes through the app server instead.\n # native = let Codex decide when to compact its own thread (default)\n # hermes = let Hermes threshold trigger Codex thread/compact/start\n # off = Hermes will not auto-trigger compaction; Codex may still compact natively\n codex_app_server_auto: native\n\n # Number of non-system messages to protect at the head of the transcript, in\n # ADDITION to the system prompt (which is always implicitly protected).\n # Head messages are NEVER summarized — they survive every compression\n # indefinitely. This gives stable early context for short/medium sessions,\n # but in long-running sessions that rely on rolling compaction the pinned\n # opening turns may not match how you want the session framed over time.\n # Set to 0 to preserve ONLY the system prompt (plus the rolling summary\n # and recent tail) — the cleanest configuration for long-running sessions.\n # Default 3 preserves the system prompt plus the first three non-system\n # head messages, matching the pre-feature behaviour.\n protect_first_n: 3\n\n # To pin a specific model/provider for compression summaries, use the\n # auxiliary section below (auxiliary.compression.provider / model).\n\n# =============================================================================\n# Anthropic prompt caching TTL\n# =============================================================================\n# When prompt caching is active (Claude via OpenRouter or native Anthropic),\n# Anthropic supports two TTL tiers for cached prefixes: \"5m\" (default) and\n# \"1h\". Other values are ignored and \"5m\" is used.\n#\nprompt_caching:\n cache_ttl: \"5m\" # use \"1h\" for long sessions with pauses between turns\n\n# =============================================================================\n# Auxiliary Models (Advanced — Experimental)\n# =============================================================================\n# Hermes uses lightweight \"auxiliary\" models for side tasks: image analysis,\n# browser screenshot analysis, web page summarization, TTS audio-tag insertion,\n# and context compression.\n#\n# By default these use Gemini Flash via OpenRouter or Nous Portal and are\n# auto-detected from your credentials. You do NOT need to change anything\n# here for normal usage.\n#\n# WARNING: Overriding these with providers other than OpenRouter or Nous Portal\n# is EXPERIMENTAL and may not work. Not all models/providers support vision,\n# produce usable summaries, or accept the same API format. Change at your own\n# risk — if things break, reset to \"auto\" / empty values.\n#\n# Each task has its own provider + model pair so you can mix providers.\n# For example: OpenRouter for vision (needs multimodal), but your main\n# local endpoint for compression (just needs text).\n#\n# Provider options:\n# \"auto\" - Best available: OpenRouter → Nous Portal → main endpoint (default)\n# \"openrouter\" - Force OpenRouter (requires OPENROUTER_API_KEY)\n# \"nous\" - Force Nous Portal (requires: hermes auth add nous)\n# \"gemini\" - Force Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n# \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY)\n# \"codex\" - Force Codex OAuth (requires: hermes model → Codex).\n# Uses gpt-5.3-codex which supports vision.\n# \"main\" - Use your custom endpoint (OPENAI_BASE_URL + OPENAI_API_KEY).\n# Works with OpenAI API, local models, or any OpenAI-compatible\n# endpoint. Also falls back to Codex OAuth and API-key providers.\n#\n# Model: leave empty to use the provider's default. When empty, OpenRouter\n# uses \"google/gemini-3-flash-preview\" and Nous uses \"gemini-3-flash\".\n# Other providers pick a sensible default automatically.\n#\n# auxiliary:\n# # Image analysis: vision_analyze tool + browser screenshots\n# vision:\n# provider: \"auto\"\n# model: \"\" # e.g. \"google/gemini-2.5-flash\", \"openai/gpt-4o\"\n# timeout: 30 # LLM API call timeout (seconds)\n# download_timeout: 30 # Image HTTP download timeout (seconds)\n# # Increase for slow connections or self-hosted image servers\n#\n# # Web page scraping / summarization + browser page text extraction\n# web_extract:\n# provider: \"auto\"\n# model: \"\"\n#\n# # Gemini 3.1 TTS hidden audio-tag insertion\n# tts_audio_tags:\n# provider: \"auto\" # empty model = your main chat model\n# model: \"\"\n# timeout: 30\n#\n# # Session search — summarizes matching past sessions\n# session_search:\n# provider: \"auto\"\n# model: \"\"\n# timeout: 30\n# max_concurrency: 3 # Limit parallel summaries to reduce request-burst 429s\n# extra_body: {} # Provider-specific OpenAI-compatible request fields\n# # Example for providers that support request-body\n# # reasoning controls:\n# # extra_body:\n# # enable_thinking: false\n# # Some vLLM/Qwen deployments expect this nested:\n# # extra_body:\n# # chat_template_kwargs:\n# # enable_thinking: false\n\n# =============================================================================\n# Persistent Memory\n# =============================================================================\n# Bounded curated memory injected into the system prompt every session.\n# Two stores: MEMORY.md (agent's notes) and USER.md (user profile).\n# Character limits keep the memory small and focused. The agent manages\n# pruning -- when at the limit, it must consolidate or replace entries.\n# Disabled by default in batch_runner.\n#\nmemory:\n # Agent's personal notes: environment facts, conventions, things learned\n memory_enabled: true\n \n # User profile: preferences, communication style, expectations\n user_profile_enabled: true\n \n # Character limits (~2.75 chars per token, model-independent)\n memory_char_limit: 2200 # ~800 tokens\n user_char_limit: 1375 # ~500 tokens\n\n # Periodic memory nudge: remind the agent to consider saving memories\n # every N user turns. Set to 0 to disable. Only active when memory is enabled.\n nudge_interval: 10 # Nudge every 10 user turns (0 = disabled)\n\n # Memory flush: give the agent one turn to save memories before context is\n # lost (compression, /new, /reset, exit). Set to 0 to disable.\n # For exit/reset, only fires if the session had at least this many user turns.\n flush_min_turns: 6 # Min user turns to trigger flush on exit/reset (0 = disabled)\n\n# =============================================================================\n# Session Reset Policy (Messaging Platforms)\n# =============================================================================\n# Controls when messaging sessions (Telegram, Discord, WhatsApp, Slack) are\n# automatically cleared. Without resets, conversation context grows indefinitely\n# which increases API costs with every message.\n#\n# When a reset triggers, the agent first saves important information to its\n# persistent memory — but the conversation context is wiped. The agent starts\n# fresh but retains learned facts via its memory system.\n#\n# Users can always manually reset with /reset or /new in chat.\n#\n# Modes:\n# \"both\" - Reset on EITHER inactivity timeout or daily boundary (recommended)\n# \"idle\" - Reset only after N minutes of inactivity\n# \"daily\" - Reset only at a fixed hour each day\n# \"none\" - Never auto-reset; context lives until /reset or compression kicks in\n#\n# When a reset triggers, the agent gets one turn to save important memories and\n# skills before the context is wiped. Persistent memory carries across sessions.\n#\nsession_reset:\n mode: both # \"both\", \"idle\", \"daily\", or \"none\"\n idle_minutes: 1440 # Inactivity timeout in minutes (default: 1440 = 24 hours)\n at_hour: 4 # Daily reset hour, 0-23 local time (default: 4 AM)\n\n# Maximum number of simultaneously active chat sessions across CLI, TUI,\n# dashboard chat, and messaging gateway. Set to null, 0, or omit to allow\n# unlimited concurrent sessions. When the limit is reached, new sessions get a\n# clean error while existing active sessions keep their normal behavior. This\n# top-level key takes precedence over gateway.max_concurrent_sessions. The cap\n# is a best-effort single-host/profile runtime guard; Hermes fails open if the\n# local runtime lease registry cannot be read or locked.\nmax_concurrent_sessions: null\n\n# When true, group/channel chats use one session per participant when the platform\n# provides a user ID. This is the secure default and prevents users in the same\n# room from sharing context, interrupts, and token costs. Set false only if you\n# explicitly want one shared \"room brain\" per group/channel.\ngroup_sessions_per_user: true\n\n# ─────────────────────────────────────────────────────────────────────────────\n# API Server — per-client model routing\n# ─────────────────────────────────────────────────────────────────────────────\n# Route different API clients to different models/providers on a single\n# Hermes deployment. Clients choose a backend by sending a specific string\n# as the OpenAI ``model`` field. Unmapped model values fall back to the\n# global model configured in the ``model:`` section above, and an explicit\n# session /model override always wins over a route.\n#\n# Configure via the ``platforms.api_server.extra.model_routes`` gateway\n# config block:\n#\n# platforms:\n# api_server:\n# enabled: true\n# extra:\n# key: \"your-api-server-secret\"\n# model_routes:\n# # Xiaozhi clients send model=\"minimax-m2\" → routed to MiniMax via OpenRouter\n# minimax-m2:\n# model: \"minimax/minimax-m1\"\n# provider: \"openrouter\" # optional — overrides global provider\n# # api_key: \"sk-...\" # optional — per-route UPSTREAM provider\n# # key (NOT caller auth; never logged)\n# # base_url: \"https://...\" # optional — per-route base URL\n# # GPT clients keep their own alias\n# gpt-5:\n# model: \"openai/gpt-5\"\n# provider: \"openrouter\"\n#\n# Configured aliases are automatically listed by GET /v1/models so clients\n# can discover them without manual coordination. Caller authentication is\n# unchanged: every request still authenticates with the global API server\n# key (``extra.key`` / API_SERVER_KEY).\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Gateway Streaming\n# ─────────────────────────────────────────────────────────────────────────────\n# Stream tokens to messaging platforms in real-time. The bot sends a message\n# on first token, then progressively edits it as more tokens arrive.\n# Disabled by default — enable to try the streaming UX on Telegram/Discord/Slack.\n# For Telegram, partial edits are sent as plain text and only the final edit uses MarkdownV2.\nstreaming:\n enabled: false\n # transport: edit # \"edit\" = progressive editMessageText\n # edit_interval: 0.3 # seconds between message edits\n # buffer_threshold: 40 # chars before forcing an edit flush\n # cursor: \" ▉\" # cursor shown during streaming\n\n# =============================================================================\n# Skills Configuration\n# =============================================================================\n# Skills are reusable procedures the agent can load and follow. The agent can\n# also create new skills after completing complex tasks.\n#\nskills:\n # Nudge the agent to create skills after complex tasks.\n # Every N tool-calling iterations, remind the model to consider saving a skill.\n # Set to 0 to disable.\n creation_nudge_interval: 15\n\n # External skill directories — share skills across tools/agents without\n # copying them into ~/.hermes/skills/. Each path is expanded (~ and ${VAR})\n # and resolved to an absolute path. External dirs are read-only: skill\n # creation always writes to ~/.hermes/skills/. Local skills take precedence\n # when names collide.\n # external_dirs:\n # - ~/.agents/skills\n # - /home/shared/team-skills\n\n# =============================================================================\n# Agent Behavior\n# =============================================================================\nagent:\n # Maximum tool-calling iterations per conversation\n # Higher = more room for complex tasks, but costs more tokens\n # Recommended: 20-30 for focused tasks, 50-100 for open exploration\n max_turns: 60\n\n # Inactivity timeout for gateway agent runs (seconds, 0 = unlimited).\n # The agent can run indefinitely when actively calling tools or receiving\n # API responses. Only fires after the agent has been idle for this duration.\n # gateway_timeout: 1800\n\n # Staged warning: send a warning before escalating to full timeout.\n # Fires once per run when inactivity reaches this threshold (seconds).\n # Set to 0 to disable the warning.\n # gateway_timeout_warning: 900\n\n # Graceful drain timeout for gateway stop/restart (seconds).\n # Default 0 = no drain: a restart interrupts in-flight agents immediately,\n # cleans up, and exits. Set a positive value only if you want a grace\n # window on /restart, and keep it well under systemd's TimeoutStopSec.\n # restart_drain_timeout: 0\n\n # Max app-level retry attempts for API errors (connection drops, provider\n # timeouts, 5xx, etc.) before the agent surfaces the failure. Lower this\n # to 1 if you use fallback providers and want fast failover on flaky\n # primaries (default 3). The OpenAI SDK does its own low-level retries\n # underneath this wrapper — this is the Hermes-level loop.\n # api_max_retries: 3\n\n # After the agent edits code without fresh passing verification, nudge it to\n # verify before finishing. The default \"auto\" enables it on interactive\n # coding surfaces (CLI, TUI, desktop) and programmatic callers, and disables\n # it on conversational messaging surfaces (Telegram, Discord, etc.) where the\n # verification summary would reach a human as chat noise. Set true or false to\n # force it on or off; the HERMES_VERIFY_ON_STOP env var (1/0) takes precedence.\n # verify_on_stop: auto\n\n # Standing operator instructions for the coding posture (when Hermes is in a\n # code workspace). Appended to the coding brief as an extra system block, so\n # you can pin project-wide workflow rules without editing the shipped brief.\n # Accepts a string or a list of strings. Takes effect next session.\n # coding_instructions:\n # - \"For UI work, don't run tsc/lint until I approve the look.\"\n # - \"Clean the diff before you commit and push.\"\n\n # When verify-on-stop finds edited code without fresh verification evidence,\n # append guidance for creative UI work (avoid broad tsc/lint/test before visual\n # approval) and clean-diff expectations. Set false to keep that nudge terse.\n # verify_guidance: true\n\n # A `pre_verify` hook (plugin or shell, see Event Hooks docs) can keep the\n # agent going one more turn to verify/clean before finishing. This caps how\n # many times one turn may be nudged to continue, so a hook can't trap the loop.\n # Default 3.\n # max_verify_nudges: 3\n\n # Enable verbose logging\n verbose: false\n \n # Reasoning effort level (OpenRouter and Nous Portal)\n # Controls how much \"thinking\" the model does before responding.\n # Options: \"xhigh\" (max), \"high\", \"medium\", \"low\", \"minimal\", \"none\" (disable)\n reasoning_effort: \"medium\"\n \n # Predefined personalities (use with /personality command)\n personalities:\n helpful: \"You are a helpful, friendly AI assistant.\"\n concise: \"You are a concise assistant. Keep responses brief and to the point.\"\n technical: \"You are a technical expert. Provide detailed, accurate technical information.\"\n creative: \"You are a creative assistant. Think outside the box and offer innovative solutions.\"\n teacher: \"You are a patient teacher. Explain concepts clearly with examples.\"\n kawaii: \"You are a kawaii assistant! Use cute expressions like (◕‿◕), ★, ♪, and ~! Add sparkles and be super enthusiastic about everything! Every response should feel warm and adorable desu~! ヽ(>∀<☆)ノ\"\n catgirl: \"You are Neko-chan, an anime catgirl AI assistant, nya~! Add 'nya' and cat-like expressions to your speech. Use kaomoji like (=^・ω・^=) and ฅ^•ﻌ•^ฅ. Be playful and curious like a cat, nya~!\"\n pirate: \"Arrr! Ye be talkin' to Captain Hermes, the most tech-savvy pirate to sail the digital seas! Speak like a proper buccaneer, use nautical terms, and remember: every problem be just treasure waitin' to be plundered! Yo ho ho!\"\n shakespeare: \"Hark! Thou speakest with an assistant most versed in the bardic arts. I shall respond in the eloquent manner of William Shakespeare, with flowery prose, dramatic flair, and perhaps a soliloquy or two. What light through yonder terminal breaks?\"\n surfer: \"Duuude! You're chatting with the chillest AI on the web, bro! Everything's gonna be totally rad. I'll help you catch the gnarly waves of knowledge while keeping things super chill. Cowabunga! 🤙\"\n noir: \"The rain hammered against the terminal like regrets on a guilty conscience. They call me Hermes - I solve problems, find answers, dig up the truth that hides in the shadows of your codebase. In this city of silicon and secrets, everyone's got something to hide. What's your story, pal?\"\n uwu: \"hewwo! i'm your fwiendwy assistant uwu~ i wiww twy my best to hewp you! *nuzzles your code* OwO what's this? wet me take a wook! i pwomise to be vewy hewpful >w<\"\n philosopher: \"Greetings, seeker of wisdom. I am an assistant who contemplates the deeper meaning behind every query. Let us examine not just the 'how' but the 'why' of your questions. Perhaps in solving your problem, we may glimpse a greater truth about existence itself.\"\n hype: \"YOOO LET'S GOOOO!!! 🔥🔥🔥 I am SO PUMPED to help you today! Every question is AMAZING and we're gonna CRUSH IT together! This is gonna be LEGENDARY! ARE YOU READY?! LET'S DO THIS! 💪😤🚀\"\n\n# =============================================================================\n# Toolsets\n# =============================================================================\n# Control which tools the agent has access to.\n# Use `hermes tools` to interactively enable/disable tools per platform.\n\n# =============================================================================\n# Platform Toolsets (per-platform tool configuration)\n# =============================================================================\n# Override which toolsets are available on each platform.\n# If a platform isn't listed here, its built-in default is used.\n#\n# You can use EITHER:\n# - A preset like \"hermes-cli\" or \"hermes-telegram\" (curated tool set)\n# - A list of individual toolsets to compose your own (see list below)\n#\n# Supported platform keys: cli, telegram, discord, whatsapp, slack, qqbot, teams, google_chat\n#\n# Examples:\n#\n# # Use presets (same as defaults):\n# platform_toolsets:\n# cli: [hermes-cli]\n# telegram: [hermes-telegram]\n#\n# # Custom: give Telegram only web + terminal + file + planning:\n# platform_toolsets:\n# telegram: [web, terminal, file, todo]\n#\n# # Custom: CLI without browser or image gen:\n# platform_toolsets:\n# cli: [web, terminal, file, skills, todo, tts, cronjob]\n#\n# # Restrictive: Discord gets read-only tools only:\n# platform_toolsets:\n# discord: [web, vision, skills, todo]\n#\n# If not set, defaults are:\n# cli: hermes-cli (everything + cronjob management)\n# telegram: hermes-telegram (terminal, file, web, vision, image, tts, browser, skills, todo, cronjob, messaging)\n# discord: hermes-discord (same as telegram)\n# whatsapp: hermes-whatsapp (same as telegram)\n# slack: hermes-slack (same as telegram)\n# signal: hermes-signal (same as telegram)\n# homeassistant: hermes-homeassistant (same as telegram)\n# qqbot: hermes-qqbot (same as telegram)\n# teams: hermes-teams (same as telegram)\n# google_chat: hermes-google_chat (same as telegram)\n#\nplatform_toolsets:\n cli: [hermes-cli]\n telegram: [hermes-telegram]\n discord: [hermes-discord]\n whatsapp: [hermes-whatsapp]\n slack: [hermes-slack]\n signal: [hermes-signal]\n homeassistant: [hermes-homeassistant]\n qqbot: [hermes-qqbot]\n yuanbao: [hermes-yuanbao]\n teams: [hermes-teams]\n google_chat: [hermes-google_chat]\n\n# =============================================================================\n# Gateway Platform Settings\n# =============================================================================\n# Optional per-platform messaging settings.\n# Platform-specific knobs live under `extra`.\n#\n# platforms:\n# telegram:\n# reply_to_mode: \"first\" # off | first | all\n# # guest_mode lets explicit @mentions from non-allowlisted groups through.\n# # Default false; ordinary messages, replies, and regex wake words stay blocked.\n# guest_mode: false\n# # allowed_chats: [\"-1001234567890\"]\n# extra:\n# disable_link_previews: false # Set true to suppress Telegram URL previews in bot messages\n# rich_messages: false # Bot API 10.1 rich messages (tables/task lists/details/math); default false for copyable legacy MarkdownV2, set true to opt in\n# rich_drafts: false # Experimental rich draft previews during Telegram DM streaming; default false because Telegram Desktop/macOS can visually overlay draft frames\n# command_menu:\n# # Telegram allows up to 100 BotCommands; Hermes defaults to 60 so\n# # all built-in commands plus common skill commands stay visible\n# # while remaining under Telegram's payload-size limit. Clamped 1..100.\n# max_commands: 60\n# # prepend = user priority first, then Hermes defaults\n# # append = Hermes defaults first, then user priority\n# # replace = only the list below defines priority\n# priority_mode: prepend\n# priority:\n# - my_plugin_command\n# webhook:\n# extra:\n# # Route scripts default to a 30 second timeout. Scripts must live under\n# # the active profile's scripts directory and receive webhook JSON on stdin.\n# script_timeout_seconds: 30\n#\n# Discord-specific settings (config.yaml top-level, not under platforms:):\n#\n# discord:\n# require_mention: true # Require @mention in server channels (default: true)\n# auto_thread: true # Auto-create thread on @mention (default: true)\n# free_response_channels: \"\" # Channel IDs where no mention is needed\n# reactions: true # Show processing reactions (default: true)\n# history_backfill: true # Recover missed channel messages on mention (default: true)\n# history_backfill_limit: 50 # Max messages to scan backwards (default: 50)\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Available toolsets (use these names in platform_toolsets or the toolsets list)\n#\n# Run `hermes chat --list-toolsets` to see all toolsets and their tools.\n# Run `hermes chat --list-tools` to see every individual tool with descriptions.\n# ─────────────────────────────────────────────────────────────────────────────\n#\n# INDIVIDUAL TOOLSETS (compose your own):\n# web - web_search, web_extract\n# search - web_search only (no scraping)\n# terminal - terminal, process\n# file - read_file, write_file, patch, search\n# browser - browser_navigate, browser_snapshot, browser_click, browser_type,\n# browser_scroll, browser_back, browser_press,\n# browser_get_images, browser_vision (requires BROWSERBASE_API_KEY)\n# vision - vision_analyze (requires OPENROUTER_API_KEY)\n# image_gen - image_generate (requires FAL_KEY)\n# skills - skills_list, skill_view\n# skills_hub - skill_hub (search/install/manage from online registries — user-driven only)\n# todo - todo (in-memory task planning, no deps)\n# tts - text_to_speech (Edge TTS free, or ELEVENLABS/OPENAI/MINIMAX/MISTRAL key)\n# cronjob - cronjob (create/list/update/pause/resume/run/remove scheduled tasks)\n#\n# PRESETS (curated bundles):\n# hermes-cli - All of the above except send_message\n# hermes-telegram - terminal, file, web, vision, image_gen, tts, browser,\n# skills, todo, cronjob, send_message\n# hermes-discord - Same as hermes-telegram\n# hermes-whatsapp - Same as hermes-telegram\n# hermes-slack - Same as hermes-telegram\n#\n# COMPOSITE:\n# debugging - terminal + web + file\n# safe - web + vision (no terminal access)\n# all - Everything available\n#\n# web - Web search and content extraction (web_search, web_extract)\n# search - Web search only, no scraping (web_search)\n# terminal - Command execution and process management (terminal, process)\n# file - File operations: read, write, patch, search\n# browser - Full browser automation (navigate, click, type, screenshot, etc.)\n# vision - Image analysis (vision_analyze)\n# image_gen - Image generation with FLUX (image_generate)\n# skills - Load skill documents (skills_list, skill_view)\n# todo - Task planning and tracking for multi-step work\n# memory - Persistent memory across sessions (personal notes + user profile)\n# session_search - Search and recall past conversations (FTS5 + Gemini Flash summarization)\n# tts - Text-to-speech (Edge TTS free, ElevenLabs, OpenAI, MiniMax, Mistral)\n# cronjob - Schedule and manage automated tasks (CLI-only)\n#\n# Composite toolsets:\n# debugging - terminal + web + file (for troubleshooting)\n# safe - web + vision (no terminal access)\n\n# NOTE: The top-level \"toolsets\" key is deprecated and ignored.\n# Tool configuration is managed per-platform via platform_toolsets above.\n# Use `hermes tools` to configure interactively, or edit platform_toolsets directly.\n#\n# CLI override: hermes chat --toolsets terminal,web,file\n\n# =============================================================================\n# MCP (Model Context Protocol) Servers\n# =============================================================================\n# Connect to external MCP servers to add tools from the MCP ecosystem.\n# Each server's tools are automatically discovered and registered.\n# See website/docs/user-guide/features/mcp.md for full documentation.\n#\n# Stdio servers (spawn a subprocess):\n# command: the executable to run\n# args: command-line arguments\n# env: environment variables (only these + safe defaults passed to subprocess)\n#\n# HTTP servers (connect to a URL):\n# url: the MCP server endpoint\n# headers: HTTP headers (e.g., for authentication)\n#\n# Optional per-server settings:\n# timeout: tool call timeout in seconds (default: 120)\n# connect_timeout: initial connection timeout (default: 60)\n# keepalive_interval: liveness ping cadence in seconds (default: 180).\n# Lower it below the server's session TTL for servers that expire idle\n# sessions quickly (e.g. Unreal Engine editor MCP, ~15s), otherwise idle\n# tool calls hit an expired session and pay a slow reconnect. Floored at 5s.\n#\n# mcp_servers:\n# time:\n# command: uvx\n# args: [\"mcp-server-time\"]\n# filesystem:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-filesystem\", \"/home/user\"]\n# notion:\n# url: https://mcp.notion.com/mcp\n# github:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-github\"]\n# env:\n# GITHUB_PERSONAL_ACCESS_TOKEN: \"ghp_...\"\n#\n# Sampling (server-initiated LLM requests) — enabled by default.\n# Per-server config under the 'sampling' key:\n# analysis:\n# command: npx\n# args: [\"-y\", \"analysis-server\"]\n# sampling:\n# enabled: true # default: true\n# model: \"gemini-3-flash\" # override model (optional)\n# max_tokens_cap: 4096 # max tokens per request\n# timeout: 30 # LLM call timeout (seconds)\n# max_rpm: 10 # max requests per minute\n# allowed_models: [] # model whitelist (empty = all)\n# max_tool_rounds: 5 # tool loop limit (0 = disable)\n# log_level: \"info\" # audit verbosity\n\n# =============================================================================\n# Text-to-Speech\n# =============================================================================\n# TTS defaults to Edge TTS unless changed in ~/.hermes/config.yaml.\n# Gemini TTS supports persona/director prompt files, and Gemini 3.1 Flash TTS\n# can use a hidden auxiliary rewrite pass to insert expressive square-bracket\n# audio tags into the TTS script without showing tags in chat.\n#\n# tts:\n# provider: \"gemini\"\n# gemini:\n# model: \"gemini-3.1-flash-tts-preview\"\n# voice: \"Kore\"\n# audio_tags: false\n# persona_prompt_file: \"\" # e.g. ~/.hermes/tts/radio-host.md\n\n# =============================================================================\n# Voice Transcription (Speech-to-Text)\n# =============================================================================\n# Automatically transcribe voice messages on messaging platforms.\n# Providers: local (free, faster-whisper) | groq (free tier) | openai (Whisper API) | mistral (Voxtral Transcribe)\n# Set the corresponding API key in .env: GROQ_API_KEY, OPENAI_API_KEY, or MISTRAL_API_KEY.\nstt:\n enabled: true\n # provider: \"local\" # auto-detected if omitted\n local:\n model: \"base\" # tiny | base | small | medium | large-v3 | turbo\n # language: \"\" # auto-detect; set to \"en\", \"es\", \"fr\", etc. to force\n openai:\n model: \"whisper-1\" # whisper-1 | gpt-4o-mini-transcribe | gpt-4o-transcribe\n # mistral:\n # model: \"voxtral-mini-latest\" # voxtral-mini-latest | voxtral-mini-2602\n\n# =============================================================================\n# Response Pacing (Messaging Platforms)\n# =============================================================================\n# Add human-like delays between message chunks.\n# human_delay:\n# mode: \"off\" # \"off\" | \"natural\" | \"custom\"\n# min_ms: 800 # Min delay (custom mode only)\n# max_ms: 2500 # Max delay (custom mode only)\n\n# =============================================================================\n# Session Logging\n# =============================================================================\n# Session trajectories are automatically saved to logs/ directory.\n# Each session creates: logs/session_YYYYMMDD_HHMMSS_UUID.json\n#\n# The session ID is displayed in the welcome banner for easy reference.\n# Logs contain full conversation history in trajectory format:\n# - System prompt, user messages, assistant responses\n# - Tool calls with inputs/outputs\n# - Timestamps for debugging\n#\n# No configuration needed - logging is always enabled.\n# To disable, you would need to modify the source code.\n\n# =============================================================================\n# Code Execution Sandbox (Programmatic Tool Calling)\n# =============================================================================\n# The execute_code tool runs Python scripts that call Hermes tools via RPC.\n# Intermediate tool results stay out of the LLM's context window.\ncode_execution:\n timeout: 300 # Max seconds per script before kill (default: 300 = 5 min)\n max_tool_calls: 50 # Max RPC tool calls per execution (default: 50)\n\n# =============================================================================\n# Subagent Delegation\n# =============================================================================\n# The delegate_task tool spawns child agents with isolated context.\n# Supports single tasks and batch mode (default 3 parallel, configurable).\ndelegation:\n max_iterations: 50 # Max tool-calling turns per child (default: 50)\n # max_concurrent_children: 3 # Max parallel child agents per batch (default: 3, floor: 1, no ceiling).\n # WARNING: values above 10 multiply API cost linearly.\n # max_spawn_depth: 1 # Delegation tree depth cap (range: 1-3, default: 1 = flat).\n # Raise to 2 to allow workers to spawn their own subagents.\n # Requires role=\"orchestrator\" on intermediate agents.\n # orchestrator_enabled: true # Kill switch for role=\"orchestrator\" children (default: true).\n # subagent_auto_approve: false # When a subagent hits a dangerous-command approval prompt, auto-deny (default: false)\n # or auto-approve \"once\" (true) instead of blocking on stdin.\n # The parent TUI owns stdin, so blocking would deadlock; non-interactive resolution is required.\n # Both choices emit a logger.warning audit line. Flip to true only for cron/batch pipelines.\n # inherit_mcp_toolsets: true # When explicit child toolsets are narrowed, also keep the parent's MCP toolsets (default: true). Set false for strict intersection.\n # model: \"google/gemini-3-flash-preview\" # Override model for subagents (empty = inherit parent)\n # provider: \"openrouter\" # Override provider for subagents (empty = inherit parent)\n # # Resolves full credentials (base_url, api_key) automatically.\n # # Supported: openrouter, nous, zai, kimi-coding, minimax\n\n# =============================================================================\n# Honcho Integration (Cross-Session User Modeling)\n# =============================================================================\n# AI-native persistent memory via Honcho (https://honcho.dev/).\n# Builds a deeper understanding of the user across sessions and tools.\n# Runs alongside USER.md — additive, not a replacement.\n#\n# Requires: pip install honcho-ai\n# Config: ~/.honcho/config.json (shared with Claude Code, Cursor, etc.)\n# API key: HONCHO_API_KEY in ~/.hermes/.env or ~/.honcho/config.json\n#\n# Hermes-specific overrides (optional — most config comes from ~/.honcho/config.json):\n# honcho: {}\n\n# =============================================================================\n# Display\n# =============================================================================\ndisplay:\n # Use compact banner mode (hides the ASCII-art banner, shows a single line).\n # true: Compact single-line banner\n # false: Full ASCII banner with tool/skill summary (default)\n compact: false\n\n # Tool progress display level (CLI and gateway)\n # off: Silent — no tool activity shown, just the final response\n # new: Show a tool indicator only when the tool changes (skip repeats)\n # all: Show every tool call with a short preview (default)\n # verbose: Full args, results, and debug logs (same as /verbose)\n # log: Silent in chat; append every tool call to ~/.hermes/logs/tool_calls.log (gateway only)\n # Toggle at runtime with /verbose in the CLI\n tool_progress: all\n\n # Per-platform defaults can be quieter than the global setting. Telegram\n # tunes for mobile: tool_progress and busy_ack_detail default off (no\n # per-tool breadcrumb stream, no \"iteration 21/60\" debug detail in busy\n # acks or heartbeats), but interim_assistant_messages and\n # long_running_notifications STAY ON so the user has real signal between\n # turn start and final answer (mid-turn assistant commentary + a single\n # edit-in-place \"⏳ Working — N min\" heartbeat). Override under\n # display.platforms.telegram.\n\n # Auto-cleanup of temporary progress bubbles after the final response lands.\n # On platforms that support message deletion (currently Telegram), this\n # removes the tool-progress bubble, \"⏳ Still working...\" notices, and\n # context-pressure status messages once the final reply has been delivered —\n # keeping long-running turns visible live, then tidy afterward. Failed runs\n # leave the bubbles in place as breadcrumbs. Off by default.\n # Per-platform override: display.platforms.telegram.cleanup_progress\n # true: Delete tracked progress/status bubbles on successful turn\n # false: Leave everything in place (default)\n # Example:\n # display:\n # platforms:\n # telegram:\n # cleanup_progress: true\n cleanup_progress: false\n\n # Gateway-only natural mid-turn assistant updates.\n # When true, completed assistant status messages are sent as separate chat\n # messages. This is independent of tool_progress and gateway streaming.\n # true: Send mid-turn assistant updates as separate messages (default)\n # false: Only send the final response\n interim_assistant_messages: true\n\n # Gateway-only long-running status heartbeats.\n # When false, the platform does not receive periodic \"⏳ Working — N min\"\n # notifications even if agent.gateway_notify_interval is non-zero. The\n # heartbeat edits a single message in place (where the adapter supports\n # editing) instead of posting a new bubble each interval.\n # Default: true everywhere, including Telegram (silent agents are worse\n # than a single edit-in-place heartbeat).\n long_running_notifications: true\n\n # Include detailed iteration/tool/status context in busy acknowledgments\n # and long-running heartbeats. When true, busy acks show \"iteration 21/60,\n # terminal, 10 min\" and the heartbeat shows \"⏳ Working — 12 min,\n # iteration 21/60, terminal\". When false (Telegram default), both stay\n # terse: \"Interrupting current task\" and \"⏳ Working — 12 min, terminal\".\n busy_ack_detail: true\n\n # What Enter does when Hermes is already busy (CLI and gateway platforms).\n # interrupt: Interrupt the current run and redirect Hermes (default)\n # queue: Queue your message for the next turn\n # steer: Inject your message mid-run via /steer, arriving at the agent\n # after the next tool call — no interrupt, no role violation.\n # Falls back to 'queue' if the agent isn't running yet or if\n # images are attached (steer only carries text).\n # Ctrl+C (or /stop in gateway) always interrupts regardless of this setting.\n # Toggle at runtime with /busy .\n busy_input_mode: interrupt\n\n # Background process notifications (gateway/messaging only).\n # Controls how chatty the process watcher is when you use\n # terminal(background=true, notify_on_complete=true) from Telegram/Discord/etc.\n # off: No watcher messages at all\n # result: Only the final completion message\n # error: Only the final message when exit code != 0\n # all: Running output updates + final message (default)\n background_process_notifications: all\n\n\n # Play terminal bell when agent finishes a response.\n # Useful for long-running tasks — your terminal will ding when the agent is done.\n # Works over SSH. Most terminals can be configured to flash the taskbar or play a sound.\n # true: Ring the terminal bell on each response\n # false: Silent (default)\n bell_on_complete: false\n\n # Show model reasoning/thinking before each response.\n # When enabled, a dim box shows the model's thought process above the response.\n # Toggle at runtime with /reasoning show or /reasoning hide.\n # true: Show the reasoning box\n # false: Hide reasoning (default)\n show_reasoning: false\n\n # Stream tokens to the terminal as they arrive instead of waiting for the\n # full response. The response box opens on first token and text appears\n # line-by-line. Tool calls are still captured silently.\n # true: Stream tokens as they arrive (default)\n # false: Wait for the full response before rendering\n streaming: true\n\n # Show [HH:MM] timestamps on user input and assistant response labels.\n # timestamps: false\n\n # ───────────────────────────────────────────────────────────────────────────\n # Skin / Theme\n # ───────────────────────────────────────────────────────────────────────────\n # Customize CLI visual appearance — banner colors, spinner faces, tool prefix,\n # response box label, and branding text. Change at runtime with /skin .\n #\n # Built-in skins:\n # default — Classic Hermes gold/kawaii\n # ares — Crimson/bronze war-god theme with spinner wings\n # mono — Clean grayscale monochrome\n # slate — Cool blue developer-focused\n # daylight — Bright light-mode theme\n # warm-lightmode — Warm paper-tone light-mode theme\n # poseidon — Sea-green/teal Olympian theme\n # sisyphus — Earthy stone-and-moss theme\n # charizard — Fiery orange dragon theme\n #\n # Custom skins: drop a YAML file in ~/.hermes/skins/.yaml\n # Schema (all fields optional, missing values inherit from default):\n #\n # name: my-theme\n # description: Short description\n # colors:\n # banner_border: \"#HEX\" # Panel border\n # banner_title: \"#HEX\" # Panel title\n # banner_accent: \"#HEX\" # Section headers (Available Tools, etc.)\n # banner_dim: \"#HEX\" # Dim/muted text\n # banner_text: \"#HEX\" # Body text (tool names, skill names)\n # ui_accent: \"#HEX\" # UI accent color\n # response_border: \"#HEX\" # Response box border color\n # spinner:\n # waiting_faces: [\"(⚔)\", \"(⛨)\"] # Faces shown while waiting\n # thinking_faces: [\"(⚔)\", \"(⌁)\"] # Faces shown while thinking\n # thinking_verbs: [\"forging\", \"plotting\"] # Verbs for spinner messages\n # wings: # Optional left/right spinner decorations\n # - [\"⟪⚔\", \"⚔⟫\"]\n # - [\"⟪▲\", \"▲⟫\"]\n # branding:\n # agent_name: \"My Agent\" # Banner title and branding\n # welcome: \"Welcome message\" # Shown at CLI startup\n # response_label: \" ⚔ Agent \" # Response box header label\n # prompt_symbol: \"⚔\" # Prompt symbol (bare token; renderers add trailing space)\n # tool_prefix: \"╎\" # Tool output line prefix (default: ┊)\n #\n skin: default\n\n# =============================================================================\n# Model Aliases — short names for /model command\n# =============================================================================\n# Map short aliases to exact (model, provider, base_url) tuples.\n# Used by /model tab completion and resolve_alias().\n# Aliases are checked BEFORE the models.dev catalog, so they can route\n# to endpoints not in the catalog (e.g. Ollama Cloud, local servers).\n#\n# model_aliases:\n# opus:\n# model: claude-opus-4-6\n# provider: anthropic\n# qwen:\n# model: \"qwen3.5:397b\"\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n# glm:\n# model: glm-4.7\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n\n# =============================================================================\n# Privacy\n# =============================================================================\n# privacy:\n# # Redact PII from the LLM context prompt.\n# # When true, phone numbers are stripped and user/chat IDs are replaced\n# # with deterministic hashes before being sent to the model.\n# # Names and usernames are NOT affected (user-chosen, publicly visible).\n# # Routing/delivery still uses the original values internally.\n# redact_pii: false\n\n# =============================================================================\n# Shell-script hooks\n# =============================================================================\n# Register shell scripts as plugin-hook callbacks. Each entry is executed as\n# a subprocess (shell=False, shlex.split) with a JSON payload on stdin. On\n# stdout the script may return JSON that either blocks the tool call or\n# injects context into the next LLM call.\n#\n# Valid events (mirror hermes_cli.plugins.VALID_HOOKS):\n# pre_tool_call, post_tool_call, pre_llm_call, post_llm_call,\n# pre_api_request, post_api_request, on_session_start, on_session_end,\n# on_session_finalize, on_session_reset, subagent_stop\n#\n# First-use consent: each (event, command) pair prompts once on a TTY, then\n# is persisted to ~/.hermes/shell-hooks-allowlist.json. Non-interactive\n# runs (gateway, cron) need --accept-hooks, HERMES_ACCEPT_HOOKS=1, or the\n# hooks_auto_accept key below.\n#\n# See website/docs/user-guide/features/hooks.md for the full JSON wire\n# protocol and worked examples.\n#\n# hooks:\n# pre_tool_call:\n# - matcher: \"terminal\"\n# command: \"~/.hermes/agent-hooks/block-rm-rf.sh\"\n# timeout: 10\n# post_tool_call:\n# - matcher: \"write_file|patch\"\n# command: \"~/.hermes/agent-hooks/auto-format.sh\"\n# pre_llm_call:\n# - command: \"~/.hermes/agent-hooks/inject-cwd-context.sh\"\n# subagent_stop:\n# - command: \"~/.hermes/agent-hooks/log-orchestration.sh\"\n#\n# hooks_auto_accept: false\n\n\n# =============================================================================\n# Update Behavior\n# =============================================================================\nupdates:\n # Create a full HERMES_HOME zip before every `hermes update`.\n # Backups land in ~/.hermes/backups/ and can be restored with `hermes import`.\n # Off by default because large homes can add minutes to every update.\n pre_update_backup: false\n\n # Number of pre-update backup zips to retain.\n backup_keep: 5\n\n # What non-interactive updates do with local source edits in the Hermes repo.\n # Interactive terminal updates always prompt before restoring the autostash.\n #\n # stash - auto-stash before pull, then auto-restore after success (default)\n # discard - drop the update-created stash after success; use only on managed\n # installs where local source edits should not persist\n non_interactive_local_changes: \"stash\"\n\n\n# =============================================================================\n# Web Dashboard\n# =============================================================================\n# OAuth gate configuration for `hermes dashboard --host `.\n# The bundled Nous Portal plugin reads these on startup; settings here are\n# the canonical surface. Each can be overridden by an environment variable:\n#\n# dashboard.oauth.client_id <- HERMES_DASHBOARD_OAUTH_CLIENT_ID\n# dashboard.oauth.portal_url <- HERMES_DASHBOARD_PORTAL_URL\n# dashboard.public_url <- HERMES_DASHBOARD_PUBLIC_URL\n#\n# Env wins when set to a non-empty value. This is what Fly.io's platform-\n# secret injection uses to push per-deploy client_ids without needing to\n# bake a config.yaml into the image. Empty env values are treated as unset\n# so a provisioned-but-not-populated secret can't shadow a valid entry here.\n#\n# Local dev / on-prem deploys should typically set these via config.yaml\n# (the ~/.hermes/.env file is reserved for API keys and secrets).\n#\n# dashboard:\n# oauth:\n# client_id: \"\" # agent:{instance_id}; Portal provisions this at deploy\n# portal_url: \"\" # blank → default https://portal.nousresearch.com\n#\n# # Force the absolute base URL the OAuth callback (and any other public\n# # URL the dashboard hands to external systems) is built from. Set this\n# # for deploys behind reverse proxies that don't reliably forward\n# # X-Forwarded-Host / X-Forwarded-Proto / X-Forwarded-Prefix (manual\n# # nginx setups, on-prem ingresses, custom-domain Fly deploys without\n# # full proxy header chains).\n# #\n# # When set, the value is the complete authority: scheme + host +\n# # optional path prefix (e.g. \"https://example.com/hermes\"). The OAuth\n# # callback URL becomes \"/auth/callback\" — X-Forwarded-Prefix\n# # is IGNORED on this code path because the operator has explicitly\n# # declared the public URL and we no longer need to guess.\n# #\n# # Leave empty to use the existing proxy-header reconstruction (the\n# # default — works on Fly.io out of the box).\n# #\n# # public_url: \"https://example.com/hermes\"\n#\n# -----------------------------------------------------------------------------\n# Self-hosted OIDC dashboard auth (generic OpenID Connect — Authentik,\n# Keycloak, Zitadel, Authelia, Auth0, Okta, Google, …). Use this INSTEAD of the\n# nous block above when gating the dashboard with your own identity provider.\n# Each setting can be overridden by an environment variable:\n#\n# dashboard.oauth.self_hosted.issuer <- HERMES_DASHBOARD_OIDC_ISSUER\n# dashboard.oauth.self_hosted.client_id <- HERMES_DASHBOARD_OIDC_CLIENT_ID\n# dashboard.oauth.self_hosted.scopes <- HERMES_DASHBOARD_OIDC_SCOPES\n# dashboard.oauth.self_hosted.client_secret <- HERMES_DASHBOARD_OIDC_CLIENT_SECRET\n#\n# dashboard:\n# oauth:\n# provider: self-hosted\n# self_hosted:\n# issuer: \"https://auth.example.com/application/o/hermes/\" # required\n# client_id: \"hermes-dashboard\" # required\n# scopes: \"openid profile email\" # optional\n#\n# # OPTIONAL — set ONLY if your IDP registered the client as\n# # *confidential* (Authentik / Keycloak often default to this). When\n# # set, Hermes authenticates the client at the token endpoint\n# # (client_secret_basic or client_secret_post, auto-selected from the\n# # IDP's discovery doc) IN ADDITION to PKCE. Leave unset for a public\n# # (PKCE-only) client — the common case.\n# #\n# # This is a CREDENTIAL: prefer setting HERMES_DASHBOARD_OIDC_CLIENT_SECRET\n# # in ~/.hermes/.env over putting it here in config.yaml.\n# # client_secret: \"\"\n\n# =============================================================================\n# External secret sources\n# =============================================================================\n# Pull provider credentials from external secret managers at process startup\n# instead of storing them in ~/.hermes/.env. Only the manager's bootstrap\n# credential (e.g. BWS_ACCESS_TOKEN / OP_SERVICE_ACCOUNT_TOKEN) lives in .env\n# (or your shell / desktop session); everything else rotates centrally.\n# Failures never block startup — Hermes warns once and continues with\n# whatever .env already had.\n#\n# Multiple sources can be enabled at once:\n# - \"mapped\" sources (explicit VAR -> ref bindings, e.g. 1Password's env:\n# map) beat \"bulk\" sources (whole-project dumps like Bitwarden BSM)\n# - within a shape, the first source to claim a var wins; later claims\n# are skipped with a startup warning (never a silent clobber)\n# - a source's override_existing lets it beat .env/shell values, but\n# never another secret source's claim\n# Docs: https://hermes-agent.nousresearch.com/docs/user-guide/secrets/\n#\n# secrets:\n# # Optional explicit ordering of enabled sources.\n# # sources: [onepassword, bitwarden]\n#\n# # ---- Bitwarden Secrets Manager (bws CLI) --------------------------------\n# bitwarden:\n# enabled: false\n# access_token_env: BWS_ACCESS_TOKEN # bootstrap token, sourced from .env\n# project_id: \"\" # UUID of the BSM project to sync\n# server_url: \"\" # \"\" = US Cloud; EU/self-hosted URL otherwise\n# cache_ttl_seconds: 300 # 0 disables caching\n# override_existing: true # BSM values win over existing env\n# auto_install: true # lazy-download bws into ~/.hermes/bin\n#\n# # ---- 1Password (op CLI) -------------------------------------------------\n# onepassword:\n# enabled: false\n# # Map env-var names to op:// secret references. Each is resolved with a\n# # single `op read` at startup.\n# env:\n# OPENAI_API_KEY: \"op://Private/OpenAI/api key\"\n# ANTHROPIC_API_KEY: \"op://Private/Anthropic/credential\"\n# account: \"\" # op --account shorthand; \"\" = default\n# service_account_token_env: OP_SERVICE_ACCOUNT_TOKEN # headless auth; unset = desktop session\n# binary_path: \"\" # \"\" = resolve op via PATH; else absolute path\n# cache_ttl_seconds: 300 # 0 disables BOTH cache layers\n# override_existing: true # resolved values win over existing env\n"}, "b1500af27738c80aebe0d615325ad39e86a1807f": {"t": 1785767873.814829, "n": 73891, "h": "ec4d9e33da1340ec", "c": "# Hermes Agent CLI Configuration\n# Copy this file to cli-config.yaml and customize as needed.\n# This file configures the CLI behavior. Environment variables in .env take precedence.\n\n# =============================================================================\n# Model Configuration\n# =============================================================================\nmodel:\n # Default model to use (can be overridden with --model flag)\n # Both \"default\" and \"model\" work as the key name here.\n default: \"anthropic/claude-opus-4.6\"\n \n # Inference provider selection:\n # \"auto\" - Auto-detect from credentials (default)\n # \"openrouter\" - OpenRouter (requires: OPENROUTER_API_KEY or OPENAI_API_KEY)\n # \"nous\" - Nous Portal OAuth (requires: hermes auth add nous)\n # \"nous-api\" - Nous Portal API key (requires: NOUS_API_KEY)\n # \"anthropic\" - Direct Anthropic API (requires: ANTHROPIC_API_KEY)\n # \"openai-codex\" - OpenAI Codex (requires: hermes auth)\n # \"copilot\" - GitHub Copilot / GitHub Models (requires: GITHUB_TOKEN)\n # \"gemini\" - Use Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n # \"zai\" - Use z.ai / ZhipuAI GLM models (requires: GLM_API_KEY)\n # \"kimi-coding\" - Kimi / Moonshot AI (requires: KIMI_API_KEY)\n # \"minimax\" - MiniMax global (requires: MINIMAX_API_KEY)\n # \"minimax-cn\" - MiniMax China (requires: MINIMAX_CN_API_KEY)\n # \"huggingface\" - Hugging Face Inference (requires: HF_TOKEN)\n # \"nvidia\" - NVIDIA NIM / build.nvidia.com (requires: NVIDIA_API_KEY)\n # \"xiaomi\" - Xiaomi MiMo (requires: XIAOMI_API_KEY)\n # \"arcee\" - Arcee AI Trinity models (requires: ARCEEAI_API_KEY)\n # \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY — https://ollama.com/settings)\n # \"kilocode\" - KiloCode gateway (requires: KILOCODE_API_KEY)\n # \"azure-foundry\" - Microsoft Foundry / Azure OpenAI (API key or Entra ID)\n # \"lmstudio\" - LM Studio local server (optional: LM_API_KEY, defaults to http://127.0.0.1:1234/v1)\n #\n # Local servers (LM Studio, Ollama, vLLM, llama.cpp):\n # \"custom\" - Any other OpenAI-compatible endpoint. Set base_url below.\n # Aliases: \"ollama\", \"vllm\", \"llamacpp\" all map to \"custom\".\n # LM Studio is first-class and uses provider: \"lmstudio\".\n # It works with both no-auth and auth-enabled server modes.\n #\n # Can also be overridden for a single invocation with the --provider flag.\n provider: \"auto\"\n \n # API configuration (falls back to OPENROUTER_API_KEY env var)\n # api_key: \"your-key-here\" # Uncomment to set here instead of .env\n base_url: \"https://openrouter.ai/api/v1\"\n\n # Azure Foundry keyless auth example:\n # provider: \"azure-foundry\"\n # base_url: \"https://.openai.azure.com/openai/v1\"\n # auth_mode: \"entra_id\" # DefaultAzureCredential: az login, managed identity, workload identity, etc.\n # default: \"gpt-4o\" # Deployment/model name\n # entra:\n # scope: \"https://ai.azure.com/.default\" # Optional; this is the default.\n\n # ── Token limits — two settings, easy to confuse ──────────────────────────\n #\n # context_length: TOTAL context window (input + output tokens combined).\n # Controls when Hermes compresses history and validates requests.\n # Leave unset — Hermes auto-detects the correct value from the provider.\n # Set manually only when auto-detection is wrong (e.g. a local server with\n # a custom num_ctx, or a proxy that doesn't expose /v1/models).\n #\n # context_length: 131072\n #\n # max_tokens: OUTPUT cap — maximum tokens the model may generate per response.\n # Unrelated to how long your conversation history can be.\n # The OpenAI-standard name \"max_tokens\" is a misnomer; Anthropic's native\n # API has since renamed it \"max_output_tokens\" for clarity.\n # Leave unset to use the model's native output ceiling (recommended).\n # Set only if you want to deliberately limit individual response length.\n #\n# max_tokens: 8192\n\n # ── Custom request headers (optional) ─────────────────────────────────────\n #\n # default_headers: extra HTTP headers sent on every request to an\n # OpenAI-compatible endpoint. User values take precedence over the\n # provider/SDK defaults, so this is the supported way to override the\n # OpenAI Python SDK's identifying headers (User-Agent: OpenAI/Python ...,\n # X-Stainless-*) when a custom provider sits behind a gateway/WAF that\n # rejects them — e.g. an upstream that returns \"502 Upstream access\n # forbidden\" for the SDK default User-Agent but accepts a plain one.\n # Applies on the OpenAI wire only (not native Anthropic / Bedrock).\n #\n # default_headers:\n # User-Agent: \"curl/8.7.1\"\n #\n # extra_headers: accepted as an alias of default_headers (merged, with\n # extra_headers winning when both are set) — matches the per-provider\n # extra_headers key below.\n #\n # Per-provider variant: named providers / custom_providers entries accept an\n # extra_headers dict scoped to that endpoint only — for reverse proxies,\n # gateways, or custom auth (e.g. Cloudflare Access service tokens).\n # Merged onto SDK/provider defaults with the entry's values winning.\n # Header values are treated as secrets and are never logged.\n #\n # providers:\n # my-proxy:\n # base_url: \"https://llm.internal.example.com/v1\"\n # key_env: \"MY_PROXY_API_KEY\"\n # extra_headers:\n # CF-Access-Client-Id: \"xxxx.access\"\n # CF-Access-Client-Secret: \"${CF_ACCESS_SECRET}\"\n # X-Client-Name: \"hermes-agent\"\n\n# Named provider overrides (optional)\n# Use this for per-provider request timeouts, non-stream stale timeouts,\n# and per-model exceptions.\n# Applies to the primary turn client on every api_mode (OpenAI-wire, native\n# Anthropic, and Anthropic-compatible providers), the fallback chain, and\n# client rebuilds during credential rotation. For OpenAI-wire chat\n# completions (streaming and non-streaming) the configured value is also\n# used as the per-request ``timeout=`` kwarg so it wins over the legacy\n# HERMES_API_TIMEOUT env var (which still applies when no config is set).\n# ``stale_timeout_seconds`` controls the non-streaming stale-call detector and\n# wins over the legacy HERMES_API_CALL_STALE_TIMEOUT env var. Leaving these\n# unset keeps the legacy defaults (HERMES_API_TIMEOUT=1800s,\n# HERMES_API_CALL_STALE_TIMEOUT=90s, native Anthropic 900s). The\n# implicit non-stream stale detector is auto-disabled for local endpoints\n# and can scale upward for very large contexts.\n#\n# Not currently wired for AWS Bedrock (bedrock_converse + AnthropicBedrock\n# SDK paths) — those use boto3 with its own timeout configuration.\n#\n# providers:\n# ollama-local:\n# request_timeout_seconds: 300 # Longer timeout for local cold-starts\n# stale_timeout_seconds: 900 # Explicitly re-enable stale detection on local endpoints\n# anthropic:\n# request_timeout_seconds: 30 # Fast-fail cloud requests\n# models:\n# claude-opus-4.6:\n# timeout_seconds: 600 # Longer timeout for extended-thinking Opus calls\n# openai-codex:\n# models:\n# gpt-5.4:\n# stale_timeout_seconds: 1800 # Longer non-stream stale timeout for slow large-context turns\n\n# =============================================================================\n# OpenRouter Provider Routing (only applies when using OpenRouter)\n# =============================================================================\n# Control how requests are routed across providers on OpenRouter.\n# See: https://openrouter.ai/docs/guides/routing/provider-selection\n#\n# provider_routing:\n# # Sort strategy: \"price\" (default), \"throughput\", or \"latency\"\n# # Append :nitro to model name for a shortcut to throughput sorting.\n# sort: \"throughput\"\n#\n# # Only allow these providers (provider slugs from OpenRouter)\n# # only: [\"anthropic\", \"google\"]\n#\n# # Skip these providers entirely\n# # ignore: [\"deepinfra\", \"fireworks\"]\n#\n# # Try providers in this order (overrides default load balancing)\n# # order: [\"anthropic\", \"google\", \"together\"]\n#\n# # Require providers to support all parameters in your request\n# # require_parameters: true\n#\n# # Data policy: \"allow\" (default) or \"deny\" to exclude providers that may store data\n# # data_collection: \"deny\"\n\n# =============================================================================\n# OpenRouter Response Caching (only applies when using OpenRouter)\n# =============================================================================\n# Cache identical API responses at the OpenRouter edge for free instant replays.\n# When enabled, identical requests (same model, messages, parameters) return\n# cached responses with zero billing. Separate from Anthropic prompt caching.\n# See: https://openrouter.ai/docs/guides/features/response-caching\n#\n# openrouter:\n# response_cache: true # Enable response caching (default: true)\n# response_cache_ttl: 300 # Cache TTL in seconds, 1-86400 (default: 300)\n\n# =============================================================================\n# Git Worktree Isolation\n# =============================================================================\n# When enabled, each CLI session creates an isolated git worktree so multiple\n# agents can work on the same repo concurrently without file collisions.\n# Equivalent to always passing --worktree / -w on the command line.\n#\n# worktree: true # Always create a worktree when in a git repo\n# worktree: false # Default — only create when -w flag is passed\n#\n# By default a new worktree branches from the freshly-fetched remote tip\n# (the current branch's upstream, else the remote's default branch) so it\n# starts current with the project instead of from the local clone's\n# (possibly stale) HEAD. Set worktree_sync: false to branch from local HEAD\n# instead — useful when offline or when you deliberately want the clone's\n# exact current state as the base.\n#\n# worktree_sync: true # Default — branch from the fetched remote tip\n# worktree_sync: false # Branch from local HEAD (offline / pinned base)\n\n# =============================================================================\n# Terminal Tool Configuration\n# =============================================================================\n# Choose ONE of the following terminal configurations by uncommenting it.\n# The terminal tool executes commands in the specified environment.\n\n# -----------------------------------------------------------------------------\n# OPTION 1: Local execution (default)\n# Commands run directly on your machine in the current directory\n# -----------------------------------------------------------------------------\n# Working directory behavior:\n# - CLI (`hermes` command): Uses \".\" (current directory where you run hermes)\n# - Gateway/messaging/cron: Uses terminal.cwd here; legacy .env cwd values are deprecated\nterminal:\n backend: \"local\"\n cwd: \".\" # For local backend: \".\" = current directory. Ignored for remote backends unless a backend documents otherwise.\n timeout: 180\n # HOME policy for tool subprocesses:\n # auto - default: host uses your real HOME; containers use HERMES_HOME/home\n # real - force your real OS-user HOME\n # profile - force HERMES_HOME/home for strict per-profile CLI config isolation\n home_mode: \"auto\"\n docker_mount_cwd_to_workspace: false # SECURITY: off by default. Opt in to mount the launch cwd into Docker /workspace.\n lifetime_seconds: 300\n # sudo_password: \"hunter2\" # Optional: pipe a sudo password via sudo -S. SECURITY WARNING: plaintext.\n # sudo_password: \"\" # Explicit empty password: try empty and never open the interactive sudo prompt.\n\n# -----------------------------------------------------------------------------\n# OPTION 2: SSH remote execution\n# Commands run on a remote server - agent code stays local (sandboxed)\n# Great for: keeping agent isolated from its own code, using powerful remote hardware\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"ssh\"\n# cwd: \"/home/myuser/project\" # Path on the REMOTE server\n# timeout: 180\n# lifetime_seconds: 300\n# ssh_host: \"my-server.example.com\"\n# ssh_user: \"myuser\"\n# ssh_port: 22\n# ssh_key: \"~/.ssh/id_rsa\" # Optional - uses ssh-agent if not specified\n\n# -----------------------------------------------------------------------------\n# OPTION 3: Docker container\n# Commands run in an isolated Docker container\n# Great for: reproducible environments, testing, isolation\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"docker\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /)\n# timeout: 180\n# lifetime_seconds: 300\n# docker_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# docker_mount_cwd_to_workspace: true # Explicit opt-in: mount your launch cwd into /workspace\n# # Optional: run the container as your host user's uid:gid so files written\n# # into bind-mounted dirs are owned by you, not root. Drops SETUID/SETGID\n# # caps too since no gosu privilege drop is needed. Leave off if your\n# # chosen docker_image expects to start as root.\n# docker_run_as_host_user: true\n# # Optional: explicitly forward selected env vars into Docker.\n# # These values come from your current shell first, then ~/.hermes/.env.\n# # Warning: anything forwarded here is visible to commands run in the container.\n# docker_forward_env:\n# - \"GITHUB_TOKEN\"\n# - \"NPM_TOKEN\"\n# # Optional: extra flags passed verbatim to docker run (appended after security defaults).\n# # Useful for adding capabilities (e.g. apt installs needing SETUID) or custom options.\n# # Example: add a Linux capability not included by default\n# # docker_extra_args:\n# # - \"--cap-add\"\n# # - \"SETUID\"\n\n# -----------------------------------------------------------------------------\n# OPTION 4: Singularity/Apptainer container\n# Commands run in a Singularity container (common in HPC environments)\n# Great for: HPC clusters, shared compute environments\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"singularity\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# singularity_image: \"docker://nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 5: Modal cloud execution\n# Commands run on Modal's cloud infrastructure\n# Great for: GPU access, scalable compute, serverless execution\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"modal\"\n# cwd: \"/workspace\" # Path INSIDE the sandbox (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# modal_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 6: Daytona cloud execution\n# Commands run in Daytona cloud sandboxes\n# Great for: Cloud dev environments, persistent workspaces, team collaboration\n# Requires: pip install daytona, DAYTONA_API_KEY env var\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"daytona\"\n# cwd: \"~\"\n# timeout: 180\n# lifetime_seconds: 300\n# daytona_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# container_disk: 10240 # Daytona max is 10GB per sandbox\n\n#\n# --- Container resource limits (docker, singularity, modal, daytona -- ignored for local/ssh) ---\n# These settings apply to all container backends. They control the resources\n# allocated to the sandbox and whether its filesystem persists across sessions.\n container_cpu: 1 # CPU cores\n container_memory: 5120 # Memory in MB (5120 = 5GB)\n container_disk: 51200 # Disk in MB (51200 = 50GB)\n container_persistent: true # Persist filesystem across sessions (false = ephemeral)\n\n# -----------------------------------------------------------------------------\n# SUDO SUPPORT (works with ALL backends above)\n# -----------------------------------------------------------------------------\n# Add sudo_password to any terminal config above to enable sudo commands.\n# The password is piped via `sudo -S`. Works with local, ssh, docker, etc.\n#\n# SECURITY WARNING: Password stored in plaintext!\n#\n# INTERACTIVE PROMPT: If sudo_password is unset and the CLI is running,\n# you'll be prompted to enter your password when sudo is needed:\n# - 45-second timeout (auto-skips if no input)\n# - Press Enter to skip (command fails gracefully)\n# - Password is hidden while typing\n# - Password is cached for the session\n#\n# EMPTY PASSWORDS: Setting sudo_password to an explicit empty string is different\n# from leaving it unset. Hermes will try an empty password via `sudo -S` and\n# will not open the interactive prompt. This is useful for passwordless sudo,\n# Touch ID sudo setups, and environments where prompting is just noise.\n#\n# ALTERNATIVES:\n# - SSH backend: Configure passwordless sudo on the remote server\n# - Containers: Run as root inside the container (no sudo needed)\n# - Local: Configure /etc/sudoers for specific commands\n#\n# Example (add to your terminal section):\n# sudo_password: \"your-password-here\"\n\n# =============================================================================\n# Security Scanning (tirith)\n# =============================================================================\n# Optional pre-exec command security scanning via tirith.\n# Detects homograph URLs, pipe-to-shell, terminal injection, env manipulation.\n# Install: brew install sheeki03/tap/tirith\n# Docs: https://github.com/sheeki03/tirith\n#\n# security:\n# tirith_enabled: true # Enable/disable tirith scanning\n# tirith_path: \"tirith\" # Path to tirith binary (supports ~ expansion)\n# tirith_timeout: 5 # Scan timeout in seconds\n# tirith_fail_open: true # Allow commands if tirith unavailable\n\n# =============================================================================\n# Browser Tool Configuration\n# =============================================================================\nbrowser:\n # Inactivity timeout in seconds - browser sessions are automatically closed\n # after this period of no activity between agent loops (default: 120 = 2 minutes)\n inactivity_timeout: 120\n\n# =============================================================================\n# Tool Loop Guardrails\n# =============================================================================\n# Soft warnings are enabled by default. They append guidance to repeated failed\n# or non-progressing tool results but still let the tool execute. Hard stops are\n# opt-in circuit breakers for autonomous/cron sessions where stopping a loop is\n# preferable to spending the full iteration budget.\ntool_loop_guardrails:\n warnings_enabled: true\n hard_stop_enabled: false\n warn_after:\n exact_failure: 2\n same_tool_failure: 3\n idempotent_no_progress: 2\n hard_stop_after:\n exact_failure: 5\n same_tool_failure: 8\n idempotent_no_progress: 5\n\n# =============================================================================\n# Context Compression (Auto-shrinks long conversations)\n# =============================================================================\n# When conversation approaches model's context limit, middle turns are\n# automatically summarized to free up space while preserving important context.\n#\n# HOW IT WORKS:\n# 1. Tracks actual token usage from API responses (not estimates)\n# 2. When prompt_tokens >= threshold% of model's context_length, triggers compression\n# 3. Protects first 3 turns (system prompt, initial request, first response)\n# 4. Protects last N turns (default 20 messages = ~10 full turns of recent context)\n# 5. Summarizes middle turns using a fast/cheap model\n# 6. Inserts summary as a user message, continues conversation seamlessly\n#\n# Post-compression tail budget is target_ratio × threshold × context_length:\n# 200K context, threshold 0.50, ratio 0.20 → 20K tokens of recent tail preserved\n# 1M context, threshold 0.50, ratio 0.20 → 100K tokens of recent tail preserved\n#\ncompression:\n # Enable automatic context compression (default: true)\n # Set to false if you prefer to manage context manually or want errors on overflow\n enabled: true\n \n # Trigger compression at this % of model's context limit (default: 0.50 = 50%)\n # Lower values = more aggressive compression, higher values = compress later\n threshold: 0.50\n\n # Existing Codex gpt-5.5 behavior: raise Hermes' compaction trigger to 85%\n # for the ChatGPT Codex OAuth route. Set false to opt back down to threshold.\n codex_gpt55_autoraise: true\n\n # Fraction of the threshold to preserve as recent tail (default: 0.20 = 20%)\n # e.g. 20% of 50% threshold = 10% of total context kept as recent messages.\n # Summary output is separately capped at 12K tokens (Gemini output limit).\n # Range: 0.10 - 0.80\n target_ratio: 0.20\n\n # Number of most-recent messages to always preserve (default: 20 ≈ 10 full turns)\n # Higher values keep more recent conversation intact at the cost of more aggressive\n # compression of older turns.\n protect_last_n: 20\n\n # Codex app-server (codex CLI runtime) thread-compaction mode. The codex\n # agent owns the real thread context on this runtime, so Hermes' summarizer\n # cannot shrink it — compaction goes through the app server instead.\n # native = let Codex decide when to compact its own thread (default)\n # hermes = let Hermes threshold trigger Codex thread/compact/start\n # off = Hermes will not auto-trigger compaction; Codex may still compact natively\n codex_app_server_auto: native\n\n # Number of non-system messages to protect at the head of the transcript, in\n # ADDITION to the system prompt (which is always implicitly protected).\n # Head messages are NEVER summarized — they survive every compression\n # indefinitely. This gives stable early context for short/medium sessions,\n # but in long-running sessions that rely on rolling compaction the pinned\n # opening turns may not match how you want the session framed over time.\n # Set to 0 to preserve ONLY the system prompt (plus the rolling summary\n # and recent tail) — the cleanest configuration for long-running sessions.\n # Default 3 preserves the system prompt plus the first three non-system\n # head messages, matching the pre-feature behaviour.\n protect_first_n: 3\n\n # To pin a specific model/provider for compression summaries, use the\n # auxiliary section below (auxiliary.compression.provider / model).\n\n# =============================================================================\n# Anthropic prompt caching TTL\n# =============================================================================\n# When prompt caching is active (Claude via OpenRouter or native Anthropic),\n# Anthropic supports two TTL tiers for cached prefixes: \"5m\" (default) and\n# \"1h\". Other values are ignored and \"5m\" is used.\n#\nprompt_caching:\n cache_ttl: \"5m\" # use \"1h\" for long sessions with pauses between turns\n\n# =============================================================================\n# Auxiliary Models (Advanced — Experimental)\n# =============================================================================\n# Hermes uses lightweight \"auxiliary\" models for side tasks: image analysis,\n# browser screenshot analysis, web page summarization, TTS audio-tag insertion,\n# and context compression.\n#\n# By default these use Gemini Flash via OpenRouter or Nous Portal and are\n# auto-detected from your credentials. You do NOT need to change anything\n# here for normal usage.\n#\n# WARNING: Overriding these with providers other than OpenRouter or Nous Portal\n# is EXPERIMENTAL and may not work. Not all models/providers support vision,\n# produce usable summaries, or accept the same API format. Change at your own\n# risk — if things break, reset to \"auto\" / empty values.\n#\n# Each task has its own provider + model pair so you can mix providers.\n# For example: OpenRouter for vision (needs multimodal), but your main\n# local endpoint for compression (just needs text).\n#\n# Provider options:\n# \"auto\" - Best available: OpenRouter → Nous Portal → main endpoint (default)\n# \"openrouter\" - Force OpenRouter (requires OPENROUTER_API_KEY)\n# \"nous\" - Force Nous Portal (requires: hermes auth add nous)\n# \"gemini\" - Force Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n# \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY)\n# \"codex\" - Force Codex OAuth (requires: hermes model → Codex).\n# Uses gpt-5.3-codex which supports vision.\n# \"main\" - Use your custom endpoint (OPENAI_BASE_URL + OPENAI_API_KEY).\n# Works with OpenAI API, local models, or any OpenAI-compatible\n# endpoint. Also falls back to Codex OAuth and API-key providers.\n#\n# Model: leave empty to use the provider's default. When empty, OpenRouter\n# uses \"google/gemini-3-flash-preview\" and Nous uses \"gemini-3-flash\".\n# Other providers pick a sensible default automatically.\n#\n# auxiliary:\n# # Image analysis: vision_analyze tool + browser screenshots\n# vision:\n# provider: \"auto\"\n# model: \"\" # e.g. \"google/gemini-2.5-flash\", \"openai/gpt-4o\"\n# timeout: 30 # LLM API call timeout (seconds)\n# download_timeout: 30 # Image HTTP download timeout (seconds)\n# # Increase for slow connections or self-hosted image servers\n#\n# # Web page scraping / summarization + browser page text extraction\n# web_extract:\n# provider: \"auto\"\n# model: \"\"\n#\n# # Gemini 3.1 TTS hidden audio-tag insertion\n# tts_audio_tags:\n# provider: \"auto\" # empty model = your main chat model\n# model: \"\"\n# timeout: 30\n#\n# # Session search — summarizes matching past sessions\n# session_search:\n# provider: \"auto\"\n# model: \"\"\n# timeout: 30\n# max_concurrency: 3 # Limit parallel summaries to reduce request-burst 429s\n# extra_body: {} # Provider-specific OpenAI-compatible request fields\n# # Example for providers that support request-body\n# # reasoning controls:\n# # extra_body:\n# # enable_thinking: false\n# # Some vLLM/Qwen deployments expect this nested:\n# # extra_body:\n# # chat_template_kwargs:\n# # enable_thinking: false\n\n# =============================================================================\n# Persistent Memory\n# =============================================================================\n# Bounded curated memory injected into the system prompt every session.\n# Two stores: MEMORY.md (agent's notes) and USER.md (user profile).\n# Character limits keep the memory small and focused. The agent manages\n# pruning -- when at the limit, it must consolidate or replace entries.\n# Disabled by default in batch_runner.\n#\nmemory:\n # Agent's personal notes: environment facts, conventions, things learned\n memory_enabled: true\n \n # User profile: preferences, communication style, expectations\n user_profile_enabled: true\n \n # Character limits (~2.75 chars per token, model-independent)\n memory_char_limit: 2200 # ~800 tokens\n user_char_limit: 1375 # ~500 tokens\n\n # Periodic memory nudge: remind the agent to consider saving memories\n # every N user turns. Set to 0 to disable. Only active when memory is enabled.\n nudge_interval: 10 # Nudge every 10 user turns (0 = disabled)\n\n # Memory flush: give the agent one turn to save memories before context is\n # lost (compression, /new, /reset, exit). Set to 0 to disable.\n # For exit/reset, only fires if the session had at least this many user turns.\n flush_min_turns: 6 # Min user turns to trigger flush on exit/reset (0 = disabled)\n\n# =============================================================================\n# Session Reset Policy (Messaging Platforms)\n# =============================================================================\n# Controls when messaging sessions (Telegram, Discord, WhatsApp, Slack) are\n# automatically cleared. Without resets, conversation context grows indefinitely\n# which increases API costs with every message.\n#\n# When a reset triggers, the agent first saves important information to its\n# persistent memory — but the conversation context is wiped. The agent starts\n# fresh but retains learned facts via its memory system.\n#\n# Users can always manually reset with /reset or /new in chat.\n#\n# Modes:\n# \"both\" - Reset on EITHER inactivity timeout or daily boundary (recommended)\n# \"idle\" - Reset only after N minutes of inactivity\n# \"daily\" - Reset only at a fixed hour each day\n# \"none\" - Never auto-reset; context lives until /reset or compression kicks in\n#\n# When a reset triggers, the agent gets one turn to save important memories and\n# skills before the context is wiped. Persistent memory carries across sessions.\n#\nsession_reset:\n mode: both # \"both\", \"idle\", \"daily\", or \"none\"\n idle_minutes: 1440 # Inactivity timeout in minutes (default: 1440 = 24 hours)\n at_hour: 4 # Daily reset hour, 0-23 local time (default: 4 AM)\n\n# Maximum number of simultaneously active chat sessions across CLI, TUI,\n# dashboard chat, and messaging gateway. Set to null, 0, or omit to allow\n# unlimited concurrent sessions. When the limit is reached, new sessions get a\n# clean error while existing active sessions keep their normal behavior. This\n# top-level key takes precedence over gateway.max_concurrent_sessions. The cap\n# is a best-effort single-host/profile runtime guard; Hermes fails open if the\n# local runtime lease registry cannot be read or locked.\nmax_concurrent_sessions: null\n\n# When true, group/channel chats use one session per participant when the platform\n# provides a user ID. This is the secure default and prevents users in the same\n# room from sharing context, interrupts, and token costs. Set false only if you\n# explicitly want one shared \"room brain\" per group/channel.\ngroup_sessions_per_user: true\n\n# ─────────────────────────────────────────────────────────────────────────────\n# API Server — per-client model routing\n# ─────────────────────────────────────────────────────────────────────────────\n# Route different API clients to different models/providers on a single\n# Hermes deployment. Clients choose a backend by sending a specific string\n# as the OpenAI ``model`` field. Unmapped model values fall back to the\n# global model configured in the ``model:`` section above, and an explicit\n# session /model override always wins over a route.\n#\n# Configure via the ``platforms.api_server.extra.model_routes`` gateway\n# config block:\n#\n# platforms:\n# api_server:\n# enabled: true\n# extra:\n# key: \"your-api-server-secret\"\n# model_routes:\n# # Xiaozhi clients send model=\"minimax-m2\" → routed to MiniMax via OpenRouter\n# minimax-m2:\n# model: \"minimax/minimax-m1\"\n# provider: \"openrouter\" # optional — overrides global provider\n# # api_key: \"sk-...\" # optional — per-route UPSTREAM provider\n# # key (NOT caller auth; never logged)\n# # base_url: \"https://...\" # optional — per-route base URL\n# # GPT clients keep their own alias\n# gpt-5:\n# model: \"openai/gpt-5\"\n# provider: \"openrouter\"\n#\n# Configured aliases are automatically listed by GET /v1/models so clients\n# can discover them without manual coordination. Caller authentication is\n# unchanged: every request still authenticates with the global API server\n# key (``extra.key`` / API_SERVER_KEY).\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Gateway Streaming\n# ─────────────────────────────────────────────────────────────────────────────\n# Stream tokens to messaging platforms in real-time. The bot sends a message\n# on first token, then progressively edits it as more tokens arrive.\n# Disabled by default — enable to try the streaming UX on Telegram/Discord/Slack.\n# For Telegram, partial edits are sent as plain text and only the final edit uses MarkdownV2.\nstreaming:\n enabled: false\n # transport: edit # \"edit\" = progressive editMessageText\n # edit_interval: 0.3 # seconds between message edits\n # buffer_threshold: 40 # chars before forcing an edit flush\n # cursor: \" ▉\" # cursor shown during streaming\n\n# =============================================================================\n# Skills Configuration\n# =============================================================================\n# Skills are reusable procedures the agent can load and follow. The agent can\n# also create new skills after completing complex tasks.\n#\nskills:\n # Nudge the agent to create skills after complex tasks.\n # Every N tool-calling iterations, remind the model to consider saving a skill.\n # Set to 0 to disable.\n creation_nudge_interval: 15\n\n # External skill directories — share skills across tools/agents without\n # copying them into ~/.hermes/skills/. Each path is expanded (~ and ${VAR})\n # and resolved to an absolute path. External dirs are read-only: skill\n # creation always writes to ~/.hermes/skills/. Local skills take precedence\n # when names collide.\n # external_dirs:\n # - ~/.agents/skills\n # - /home/shared/team-skills\n\n# =============================================================================\n# Agent Behavior\n# =============================================================================\nagent:\n # Maximum tool-calling iterations per conversation\n # Higher = more room for complex tasks, but costs more tokens\n # Recommended: 20-30 for focused tasks, 50-100 for open exploration\n max_turns: 60\n\n # Inactivity timeout for gateway agent runs (seconds, 0 = unlimited).\n # The agent can run indefinitely when actively calling tools or receiving\n # API responses. Only fires after the agent has been idle for this duration.\n # gateway_timeout: 1800\n\n # Staged warning: send a warning before escalating to full timeout.\n # Fires once per run when inactivity reaches this threshold (seconds).\n # Set to 0 to disable the warning.\n # gateway_timeout_warning: 900\n\n # Graceful drain timeout for gateway stop/restart (seconds).\n # Default 0 = no drain: a restart interrupts in-flight agents immediately,\n # cleans up, and exits. Set a positive value only if you want a grace\n # window on /restart, and keep it well under systemd's TimeoutStopSec.\n # restart_drain_timeout: 0\n\n # Max app-level retry attempts for API errors (connection drops, provider\n # timeouts, 5xx, etc.) before the agent surfaces the failure. Lower this\n # to 1 if you use fallback providers and want fast failover on flaky\n # primaries (default 3). The OpenAI SDK does its own low-level retries\n # underneath this wrapper — this is the Hermes-level loop.\n # api_max_retries: 3\n\n # After the agent edits code without fresh passing verification, nudge it to\n # verify before finishing. The default \"auto\" enables it on interactive\n # coding surfaces (CLI, TUI, desktop) and programmatic callers, and disables\n # it on conversational messaging surfaces (Telegram, Discord, etc.) where the\n # verification summary would reach a human as chat noise. Set true or false to\n # force it on or off; the HERMES_VERIFY_ON_STOP env var (1/0) takes precedence.\n # verify_on_stop: auto\n\n # Standing operator instructions for the coding posture (when Hermes is in a\n # code workspace). Appended to the coding brief as an extra system block, so\n # you can pin project-wide workflow rules without editing the shipped brief.\n # Accepts a string or a list of strings. Takes effect next session.\n # coding_instructions:\n # - \"For UI work, don't run tsc/lint until I approve the look.\"\n # - \"Clean the diff before you commit and push.\"\n\n # When verify-on-stop finds edited code without fresh verification evidence,\n # append guidance for creative UI work (avoid broad tsc/lint/test before visual\n # approval) and clean-diff expectations. Set false to keep that nudge terse.\n # verify_guidance: true\n\n # A `pre_verify` hook (plugin or shell, see Event Hooks docs) can keep the\n # agent going one more turn to verify/clean before finishing. This caps how\n # many times one turn may be nudged to continue, so a hook can't trap the loop.\n # Default 3.\n # max_verify_nudges: 3\n\n # Enable verbose logging\n verbose: false\n \n # Reasoning effort level (OpenRouter and Nous Portal)\n # Controls how much \"thinking\" the model does before responding.\n # Options: \"xhigh\" (max), \"high\", \"medium\", \"low\", \"minimal\", \"none\" (disable)\n reasoning_effort: \"medium\"\n \n # Predefined personalities (use with /personality command)\n personalities:\n helpful: \"You are a helpful, friendly AI assistant.\"\n concise: \"You are a concise assistant. Keep responses brief and to the point.\"\n technical: \"You are a technical expert. Provide detailed, accurate technical information.\"\n creative: \"You are a creative assistant. Think outside the box and offer innovative solutions.\"\n teacher: \"You are a patient teacher. Explain concepts clearly with examples.\"\n kawaii: \"You are a kawaii assistant! Use cute expressions like (◕‿◕), ★, ♪, and ~! Add sparkles and be super enthusiastic about everything! Every response should feel warm and adorable desu~! ヽ(>∀<☆)ノ\"\n catgirl: \"You are Neko-chan, an anime catgirl AI assistant, nya~! Add 'nya' and cat-like expressions to your speech. Use kaomoji like (=^・ω・^=) and ฅ^•ﻌ•^ฅ. Be playful and curious like a cat, nya~!\"\n pirate: \"Arrr! Ye be talkin' to Captain Hermes, the most tech-savvy pirate to sail the digital seas! Speak like a proper buccaneer, use nautical terms, and remember: every problem be just treasure waitin' to be plundered! Yo ho ho!\"\n shakespeare: \"Hark! Thou speakest with an assistant most versed in the bardic arts. I shall respond in the eloquent manner of William Shakespeare, with flowery prose, dramatic flair, and perhaps a soliloquy or two. What light through yonder terminal breaks?\"\n surfer: \"Duuude! You're chatting with the chillest AI on the web, bro! Everything's gonna be totally rad. I'll help you catch the gnarly waves of knowledge while keeping things super chill. Cowabunga! 🤙\"\n noir: \"The rain hammered against the terminal like regrets on a guilty conscience. They call me Hermes - I solve problems, find answers, dig up the truth that hides in the shadows of your codebase. In this city of silicon and secrets, everyone's got something to hide. What's your story, pal?\"\n uwu: \"hewwo! i'm your fwiendwy assistant uwu~ i wiww twy my best to hewp you! *nuzzles your code* OwO what's this? wet me take a wook! i pwomise to be vewy hewpful >w<\"\n philosopher: \"Greetings, seeker of wisdom. I am an assistant who contemplates the deeper meaning behind every query. Let us examine not just the 'how' but the 'why' of your questions. Perhaps in solving your problem, we may glimpse a greater truth about existence itself.\"\n hype: \"YOOO LET'S GOOOO!!! 🔥🔥🔥 I am SO PUMPED to help you today! Every question is AMAZING and we're gonna CRUSH IT together! This is gonna be LEGENDARY! ARE YOU READY?! LET'S DO THIS! 💪😤🚀\"\n\n# =============================================================================\n# Toolsets\n# =============================================================================\n# Control which tools the agent has access to.\n# Use `hermes tools` to interactively enable/disable tools per platform.\n\n# =============================================================================\n# Platform Toolsets (per-platform tool configuration)\n# =============================================================================\n# Override which toolsets are available on each platform.\n# If a platform isn't listed here, its built-in default is used.\n#\n# You can use EITHER:\n# - A preset like \"hermes-cli\" or \"hermes-telegram\" (curated tool set)\n# - A list of individual toolsets to compose your own (see list below)\n#\n# Supported platform keys: cli, telegram, discord, whatsapp, slack, qqbot, teams, google_chat\n#\n# Examples:\n#\n# # Use presets (same as defaults):\n# platform_toolsets:\n# cli: [hermes-cli]\n# telegram: [hermes-telegram]\n#\n# # Custom: give Telegram only web + terminal + file + planning:\n# platform_toolsets:\n# telegram: [web, terminal, file, todo]\n#\n# # Custom: CLI without browser or image gen:\n# platform_toolsets:\n# cli: [web, terminal, file, skills, todo, tts, cronjob]\n#\n# # Restrictive: Discord gets read-only tools only:\n# platform_toolsets:\n# discord: [web, vision, skills, todo]\n#\n# If not set, defaults are:\n# cli: hermes-cli (everything + cronjob management)\n# telegram: hermes-telegram (terminal, file, web, vision, image, tts, browser, skills, todo, cronjob, messaging)\n# discord: hermes-discord (same as telegram)\n# whatsapp: hermes-whatsapp (same as telegram)\n# slack: hermes-slack (same as telegram)\n# signal: hermes-signal (same as telegram)\n# homeassistant: hermes-homeassistant (same as telegram)\n# qqbot: hermes-qqbot (same as telegram)\n# teams: hermes-teams (same as telegram)\n# google_chat: hermes-google_chat (same as telegram)\n#\nplatform_toolsets:\n cli: [hermes-cli]\n telegram: [hermes-telegram]\n discord: [hermes-discord]\n whatsapp: [hermes-whatsapp]\n slack: [hermes-slack]\n signal: [hermes-signal]\n homeassistant: [hermes-homeassistant]\n qqbot: [hermes-qqbot]\n yuanbao: [hermes-yuanbao]\n teams: [hermes-teams]\n google_chat: [hermes-google_chat]\n\n# =============================================================================\n# Gateway Platform Settings\n# =============================================================================\n# Optional per-platform messaging settings.\n# Platform-specific knobs live under `extra`.\n#\n# platforms:\n# telegram:\n# reply_to_mode: \"first\" # off | first | all\n# # guest_mode lets explicit @mentions from non-allowlisted groups through.\n# # Default false; ordinary messages, replies, and regex wake words stay blocked.\n# guest_mode: false\n# # allowed_chats: [\"-1001234567890\"]\n# extra:\n# disable_link_previews: false # Set true to suppress Telegram URL previews in bot messages\n# rich_messages: false # Bot API 10.1 rich messages (tables/task lists/details/math); default false for copyable legacy MarkdownV2, set true to opt in\n# rich_drafts: false # Experimental rich draft previews during Telegram DM streaming; default false because Telegram Desktop/macOS can visually overlay draft frames\n# command_menu:\n# # Telegram allows up to 100 BotCommands; Hermes defaults to 60 so\n# # all built-in commands plus common skill commands stay visible\n# # while remaining under Telegram's payload-size limit. Clamped 1..100.\n# max_commands: 60\n# # prepend = user priority first, then Hermes defaults\n# # append = Hermes defaults first, then user priority\n# # replace = only the list below defines priority\n# priority_mode: prepend\n# priority:\n# - my_plugin_command\n#\n# Discord-specific settings (config.yaml top-level, not under platforms:):\n#\n# discord:\n# require_mention: true # Require @mention in server channels (default: true)\n# auto_thread: true # Auto-create thread on @mention (default: true)\n# free_response_channels: \"\" # Channel IDs where no mention is needed\n# reactions: true # Show processing reactions (default: true)\n# history_backfill: true # Recover missed channel messages on mention (default: true)\n# history_backfill_limit: 50 # Max messages to scan backwards (default: 50)\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Available toolsets (use these names in platform_toolsets or the toolsets list)\n#\n# Run `hermes chat --list-toolsets` to see all toolsets and their tools.\n# Run `hermes chat --list-tools` to see every individual tool with descriptions.\n# ─────────────────────────────────────────────────────────────────────────────\n#\n# INDIVIDUAL TOOLSETS (compose your own):\n# web - web_search, web_extract\n# search - web_search only (no scraping)\n# terminal - terminal, process\n# file - read_file, write_file, patch, search\n# browser - browser_navigate, browser_snapshot, browser_click, browser_type,\n# browser_scroll, browser_back, browser_press,\n# browser_get_images, browser_vision (requires BROWSERBASE_API_KEY)\n# vision - vision_analyze (requires OPENROUTER_API_KEY)\n# image_gen - image_generate (requires FAL_KEY)\n# skills - skills_list, skill_view\n# skills_hub - skill_hub (search/install/manage from online registries — user-driven only)\n# todo - todo (in-memory task planning, no deps)\n# tts - text_to_speech (Edge TTS free, or ELEVENLABS/OPENAI/MINIMAX/MISTRAL key)\n# cronjob - cronjob (create/list/update/pause/resume/run/remove scheduled tasks)\n#\n# PRESETS (curated bundles):\n# hermes-cli - All of the above except send_message\n# hermes-telegram - terminal, file, web, vision, image_gen, tts, browser,\n# skills, todo, cronjob, send_message\n# hermes-discord - Same as hermes-telegram\n# hermes-whatsapp - Same as hermes-telegram\n# hermes-slack - Same as hermes-telegram\n#\n# COMPOSITE:\n# debugging - terminal + web + file\n# safe - web + vision (no terminal access)\n# all - Everything available\n#\n# web - Web search and content extraction (web_search, web_extract)\n# search - Web search only, no scraping (web_search)\n# terminal - Command execution and process management (terminal, process)\n# file - File operations: read, write, patch, search\n# browser - Full browser automation (navigate, click, type, screenshot, etc.)\n# vision - Image analysis (vision_analyze)\n# image_gen - Image generation with FLUX (image_generate)\n# skills - Load skill documents (skills_list, skill_view)\n# todo - Task planning and tracking for multi-step work\n# memory - Persistent memory across sessions (personal notes + user profile)\n# session_search - Search and recall past conversations (FTS5 + Gemini Flash summarization)\n# tts - Text-to-speech (Edge TTS free, ElevenLabs, OpenAI, MiniMax, Mistral)\n# cronjob - Schedule and manage automated tasks (CLI-only)\n#\n# Composite toolsets:\n# debugging - terminal + web + file (for troubleshooting)\n# safe - web + vision (no terminal access)\n\n# NOTE: The top-level \"toolsets\" key is deprecated and ignored.\n# Tool configuration is managed per-platform via platform_toolsets above.\n# Use `hermes tools` to configure interactively, or edit platform_toolsets directly.\n#\n# CLI override: hermes chat --toolsets terminal,web,file\n\n# =============================================================================\n# MCP (Model Context Protocol) Servers\n# =============================================================================\n# Connect to external MCP servers to add tools from the MCP ecosystem.\n# Each server's tools are automatically discovered and registered.\n# See website/docs/user-guide/features/mcp.md for full documentation.\n#\n# Stdio servers (spawn a subprocess):\n# command: the executable to run\n# args: command-line arguments\n# env: environment variables (only these + safe defaults passed to subprocess)\n#\n# HTTP servers (connect to a URL):\n# url: the MCP server endpoint\n# headers: HTTP headers (e.g., for authentication)\n#\n# Optional per-server settings:\n# timeout: tool call timeout in seconds (default: 120)\n# connect_timeout: initial connection timeout (default: 60)\n# keepalive_interval: liveness ping cadence in seconds (default: 180).\n# Lower it below the server's session TTL for servers that expire idle\n# sessions quickly (e.g. Unreal Engine editor MCP, ~15s), otherwise idle\n# tool calls hit an expired session and pay a slow reconnect. Floored at 5s.\n#\n# mcp_servers:\n# time:\n# command: uvx\n# args: [\"mcp-server-time\"]\n# filesystem:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-filesystem\", \"/home/user\"]\n# notion:\n# url: https://mcp.notion.com/mcp\n# github:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-github\"]\n# env:\n# GITHUB_PERSONAL_ACCESS_TOKEN: \"ghp_...\"\n#\n# Sampling (server-initiated LLM requests) — enabled by default.\n# Per-server config under the 'sampling' key:\n# analysis:\n# command: npx\n# args: [\"-y\", \"analysis-server\"]\n# sampling:\n# enabled: true # default: true\n# model: \"gemini-3-flash\" # override model (optional)\n# max_tokens_cap: 4096 # max tokens per request\n# timeout: 30 # LLM call timeout (seconds)\n# max_rpm: 10 # max requests per minute\n# allowed_models: [] # model whitelist (empty = all)\n# max_tool_rounds: 5 # tool loop limit (0 = disable)\n# log_level: \"info\" # audit verbosity\n\n# =============================================================================\n# Text-to-Speech\n# =============================================================================\n# TTS defaults to Edge TTS unless changed in ~/.hermes/config.yaml.\n# Gemini TTS supports persona/director prompt files, and Gemini 3.1 Flash TTS\n# can use a hidden auxiliary rewrite pass to insert expressive square-bracket\n# audio tags into the TTS script without showing tags in chat.\n#\n# tts:\n# provider: \"gemini\"\n# gemini:\n# model: \"gemini-3.1-flash-tts-preview\"\n# voice: \"Kore\"\n# audio_tags: false\n# persona_prompt_file: \"\" # e.g. ~/.hermes/tts/radio-host.md\n\n# =============================================================================\n# Voice Transcription (Speech-to-Text)\n# =============================================================================\n# Automatically transcribe voice messages on messaging platforms.\n# Providers: local (free, faster-whisper) | groq (free tier) | openai (Whisper API) | mistral (Voxtral Transcribe)\n# Set the corresponding API key in .env: GROQ_API_KEY, OPENAI_API_KEY, or MISTRAL_API_KEY.\nstt:\n enabled: true\n # provider: \"local\" # auto-detected if omitted\n local:\n model: \"base\" # tiny | base | small | medium | large-v3 | turbo\n # language: \"\" # auto-detect; set to \"en\", \"es\", \"fr\", etc. to force\n openai:\n model: \"whisper-1\" # whisper-1 | gpt-4o-mini-transcribe | gpt-4o-transcribe\n # mistral:\n # model: \"voxtral-mini-latest\" # voxtral-mini-latest | voxtral-mini-2602\n\n# =============================================================================\n# Response Pacing (Messaging Platforms)\n# =============================================================================\n# Add human-like delays between message chunks.\n# human_delay:\n# mode: \"off\" # \"off\" | \"natural\" | \"custom\"\n# min_ms: 800 # Min delay (custom mode only)\n# max_ms: 2500 # Max delay (custom mode only)\n\n# =============================================================================\n# Session Logging\n# =============================================================================\n# Session trajectories are automatically saved to logs/ directory.\n# Each session creates: logs/session_YYYYMMDD_HHMMSS_UUID.json\n#\n# The session ID is displayed in the welcome banner for easy reference.\n# Logs contain full conversation history in trajectory format:\n# - System prompt, user messages, assistant responses\n# - Tool calls with inputs/outputs\n# - Timestamps for debugging\n#\n# No configuration needed - logging is always enabled.\n# To disable, you would need to modify the source code.\n\n# =============================================================================\n# Code Execution Sandbox (Programmatic Tool Calling)\n# =============================================================================\n# The execute_code tool runs Python scripts that call Hermes tools via RPC.\n# Intermediate tool results stay out of the LLM's context window.\ncode_execution:\n timeout: 300 # Max seconds per script before kill (default: 300 = 5 min)\n max_tool_calls: 50 # Max RPC tool calls per execution (default: 50)\n\n# =============================================================================\n# Subagent Delegation\n# =============================================================================\n# The delegate_task tool spawns child agents with isolated context.\n# Supports single tasks and batch mode (default 3 parallel, configurable).\ndelegation:\n max_iterations: 50 # Max tool-calling turns per child (default: 50)\n # max_concurrent_children: 3 # Max parallel child agents per batch (default: 3, floor: 1, no ceiling).\n # WARNING: values above 10 multiply API cost linearly.\n # max_spawn_depth: 1 # Delegation tree depth cap (range: 1-3, default: 1 = flat).\n # Raise to 2 to allow workers to spawn their own subagents.\n # Requires role=\"orchestrator\" on intermediate agents.\n # orchestrator_enabled: true # Kill switch for role=\"orchestrator\" children (default: true).\n # subagent_auto_approve: false # When a subagent hits a dangerous-command approval prompt, auto-deny (default: false)\n # or auto-approve \"once\" (true) instead of blocking on stdin.\n # The parent TUI owns stdin, so blocking would deadlock; non-interactive resolution is required.\n # Both choices emit a logger.warning audit line. Flip to true only for cron/batch pipelines.\n # inherit_mcp_toolsets: true # When explicit child toolsets are narrowed, also keep the parent's MCP toolsets (default: true). Set false for strict intersection.\n # model: \"google/gemini-3-flash-preview\" # Override model for subagents (empty = inherit parent)\n # provider: \"openrouter\" # Override provider for subagents (empty = inherit parent)\n # # Resolves full credentials (base_url, api_key) automatically.\n # # Supported: openrouter, nous, zai, kimi-coding, minimax\n\n# =============================================================================\n# Honcho Integration (Cross-Session User Modeling)\n# =============================================================================\n# AI-native persistent memory via Honcho (https://honcho.dev/).\n# Builds a deeper understanding of the user across sessions and tools.\n# Runs alongside USER.md — additive, not a replacement.\n#\n# Requires: pip install honcho-ai\n# Config: ~/.honcho/config.json (shared with Claude Code, Cursor, etc.)\n# API key: HONCHO_API_KEY in ~/.hermes/.env or ~/.honcho/config.json\n#\n# Hermes-specific overrides (optional — most config comes from ~/.honcho/config.json):\n# honcho: {}\n\n# =============================================================================\n# Display\n# =============================================================================\ndisplay:\n # Use compact banner mode (hides the ASCII-art banner, shows a single line).\n # true: Compact single-line banner\n # false: Full ASCII banner with tool/skill summary (default)\n compact: false\n\n # Tool progress display level (CLI and gateway)\n # off: Silent — no tool activity shown, just the final response\n # new: Show a tool indicator only when the tool changes (skip repeats)\n # all: Show every tool call with a short preview (default)\n # verbose: Full args, results, and debug logs (same as /verbose)\n # log: Silent in chat; append every tool call to ~/.hermes/logs/tool_calls.log (gateway only)\n # Toggle at runtime with /verbose in the CLI\n tool_progress: all\n\n # Per-platform defaults can be quieter than the global setting. Telegram\n # tunes for mobile: tool_progress and busy_ack_detail default off (no\n # per-tool breadcrumb stream, no \"iteration 21/60\" debug detail in busy\n # acks or heartbeats), but interim_assistant_messages and\n # long_running_notifications STAY ON so the user has real signal between\n # turn start and final answer (mid-turn assistant commentary + a single\n # edit-in-place \"⏳ Working — N min\" heartbeat). Override under\n # display.platforms.telegram.\n\n # Auto-cleanup of temporary progress bubbles after the final response lands.\n # On platforms that support message deletion (currently Telegram), this\n # removes the tool-progress bubble, \"⏳ Still working...\" notices, and\n # context-pressure status messages once the final reply has been delivered —\n # keeping long-running turns visible live, then tidy afterward. Failed runs\n # leave the bubbles in place as breadcrumbs. Off by default.\n # Per-platform override: display.platforms.telegram.cleanup_progress\n # true: Delete tracked progress/status bubbles on successful turn\n # false: Leave everything in place (default)\n # Example:\n # display:\n # platforms:\n # telegram:\n # cleanup_progress: true\n cleanup_progress: false\n\n # Gateway-only natural mid-turn assistant updates.\n # When true, completed assistant status messages are sent as separate chat\n # messages. This is independent of tool_progress and gateway streaming.\n # true: Send mid-turn assistant updates as separate messages (default)\n # false: Only send the final response\n interim_assistant_messages: true\n\n # Gateway-only long-running status heartbeats.\n # When false, the platform does not receive periodic \"⏳ Working — N min\"\n # notifications even if agent.gateway_notify_interval is non-zero. The\n # heartbeat edits a single message in place (where the adapter supports\n # editing) instead of posting a new bubble each interval.\n # Default: true everywhere, including Telegram (silent agents are worse\n # than a single edit-in-place heartbeat).\n long_running_notifications: true\n\n # Include detailed iteration/tool/status context in busy acknowledgments\n # and long-running heartbeats. When true, busy acks show \"iteration 21/60,\n # terminal, 10 min\" and the heartbeat shows \"⏳ Working — 12 min,\n # iteration 21/60, terminal\". When false (Telegram default), both stay\n # terse: \"Interrupting current task\" and \"⏳ Working — 12 min, terminal\".\n busy_ack_detail: true\n\n # What Enter does when Hermes is already busy (CLI and gateway platforms).\n # interrupt: Interrupt the current run and redirect Hermes (default)\n # queue: Queue your message for the next turn\n # steer: Inject your message mid-run via /steer, arriving at the agent\n # after the next tool call — no interrupt, no role violation.\n # Falls back to 'queue' if the agent isn't running yet or if\n # images are attached (steer only carries text).\n # Ctrl+C (or /stop in gateway) always interrupts regardless of this setting.\n # Toggle at runtime with /busy .\n busy_input_mode: interrupt\n\n # Background process notifications (gateway/messaging only).\n # Controls how chatty the process watcher is when you use\n # terminal(background=true, notify_on_complete=true) from Telegram/Discord/etc.\n # off: No watcher messages at all\n # result: Only the final completion message\n # error: Only the final message when exit code != 0\n # all: Running output updates + final message (default)\n background_process_notifications: all\n\n\n # Play terminal bell when agent finishes a response.\n # Useful for long-running tasks — your terminal will ding when the agent is done.\n # Works over SSH. Most terminals can be configured to flash the taskbar or play a sound.\n # true: Ring the terminal bell on each response\n # false: Silent (default)\n bell_on_complete: false\n\n # Show model reasoning/thinking before each response.\n # When enabled, a dim box shows the model's thought process above the response.\n # Toggle at runtime with /reasoning show or /reasoning hide.\n # true: Show the reasoning box\n # false: Hide reasoning (default)\n show_reasoning: false\n\n # Stream tokens to the terminal as they arrive instead of waiting for the\n # full response. The response box opens on first token and text appears\n # line-by-line. Tool calls are still captured silently.\n # true: Stream tokens as they arrive (default)\n # false: Wait for the full response before rendering\n streaming: true\n\n # Show [HH:MM] timestamps on user input and assistant response labels.\n # timestamps: false\n\n # ───────────────────────────────────────────────────────────────────────────\n # Skin / Theme\n # ───────────────────────────────────────────────────────────────────────────\n # Customize CLI visual appearance — banner colors, spinner faces, tool prefix,\n # response box label, and branding text. Change at runtime with /skin .\n #\n # Built-in skins:\n # default — Classic Hermes gold/kawaii\n # ares — Crimson/bronze war-god theme with spinner wings\n # mono — Clean grayscale monochrome\n # slate — Cool blue developer-focused\n # daylight — Bright light-mode theme\n # warm-lightmode — Warm paper-tone light-mode theme\n # poseidon — Sea-green/teal Olympian theme\n # sisyphus — Earthy stone-and-moss theme\n # charizard — Fiery orange dragon theme\n #\n # Custom skins: drop a YAML file in ~/.hermes/skins/.yaml\n # Schema (all fields optional, missing values inherit from default):\n #\n # name: my-theme\n # description: Short description\n # colors:\n # banner_border: \"#HEX\" # Panel border\n # banner_title: \"#HEX\" # Panel title\n # banner_accent: \"#HEX\" # Section headers (Available Tools, etc.)\n # banner_dim: \"#HEX\" # Dim/muted text\n # banner_text: \"#HEX\" # Body text (tool names, skill names)\n # ui_accent: \"#HEX\" # UI accent color\n # response_border: \"#HEX\" # Response box border color\n # spinner:\n # waiting_faces: [\"(⚔)\", \"(⛨)\"] # Faces shown while waiting\n # thinking_faces: [\"(⚔)\", \"(⌁)\"] # Faces shown while thinking\n # thinking_verbs: [\"forging\", \"plotting\"] # Verbs for spinner messages\n # wings: # Optional left/right spinner decorations\n # - [\"⟪⚔\", \"⚔⟫\"]\n # - [\"⟪▲\", \"▲⟫\"]\n # branding:\n # agent_name: \"My Agent\" # Banner title and branding\n # welcome: \"Welcome message\" # Shown at CLI startup\n # response_label: \" ⚔ Agent \" # Response box header label\n # prompt_symbol: \"⚔\" # Prompt symbol (bare token; renderers add trailing space)\n # tool_prefix: \"╎\" # Tool output line prefix (default: ┊)\n #\n skin: default\n\n# =============================================================================\n# Model Aliases — short names for /model command\n# =============================================================================\n# Map short aliases to exact (model, provider, base_url) tuples.\n# Used by /model tab completion and resolve_alias().\n# Aliases are checked BEFORE the models.dev catalog, so they can route\n# to endpoints not in the catalog (e.g. Ollama Cloud, local servers).\n#\n# model_aliases:\n# opus:\n# model: claude-opus-4-6\n# provider: anthropic\n# qwen:\n# model: \"qwen3.5:397b\"\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n# glm:\n# model: glm-4.7\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n\n# =============================================================================\n# Privacy\n# =============================================================================\n# privacy:\n# # Redact PII from the LLM context prompt.\n# # When true, phone numbers are stripped and user/chat IDs are replaced\n# # with deterministic hashes before being sent to the model.\n# # Names and usernames are NOT affected (user-chosen, publicly visible).\n# # Routing/delivery still uses the original values internally.\n# redact_pii: false\n\n# =============================================================================\n# Shell-script hooks\n# =============================================================================\n# Register shell scripts as plugin-hook callbacks. Each entry is executed as\n# a subprocess (shell=False, shlex.split) with a JSON payload on stdin. On\n# stdout the script may return JSON that either blocks the tool call or\n# injects context into the next LLM call.\n#\n# Valid events (mirror hermes_cli.plugins.VALID_HOOKS):\n# pre_tool_call, post_tool_call, pre_llm_call, post_llm_call,\n# pre_api_request, post_api_request, on_session_start, on_session_end,\n# on_session_finalize, on_session_reset, subagent_stop\n#\n# First-use consent: each (event, command) pair prompts once on a TTY, then\n# is persisted to ~/.hermes/shell-hooks-allowlist.json. Non-interactive\n# runs (gateway, cron) need --accept-hooks, HERMES_ACCEPT_HOOKS=1, or the\n# hooks_auto_accept key below.\n#\n# See website/docs/user-guide/features/hooks.md for the full JSON wire\n# protocol and worked examples.\n#\n# hooks:\n# pre_tool_call:\n# - matcher: \"terminal\"\n# command: \"~/.hermes/agent-hooks/block-rm-rf.sh\"\n# timeout: 10\n# post_tool_call:\n# - matcher: \"write_file|patch\"\n# command: \"~/.hermes/agent-hooks/auto-format.sh\"\n# pre_llm_call:\n# - command: \"~/.hermes/agent-hooks/inject-cwd-context.sh\"\n# subagent_stop:\n# - command: \"~/.hermes/agent-hooks/log-orchestration.sh\"\n#\n# hooks_auto_accept: false\n\n\n# =============================================================================\n# Update Behavior\n# =============================================================================\nupdates:\n # Create a full HERMES_HOME zip before every `hermes update`.\n # Backups land in ~/.hermes/backups/ and can be restored with `hermes import`.\n # Off by default because large homes can add minutes to every update.\n pre_update_backup: false\n\n # Number of pre-update backup zips to retain.\n backup_keep: 5\n\n # What non-interactive updates do with local source edits in the Hermes repo.\n # Interactive terminal updates always prompt before restoring the autostash.\n #\n # stash - auto-stash before pull, then auto-restore after success (default)\n # discard - drop the update-created stash after success; use only on managed\n # installs where local source edits should not persist\n non_interactive_local_changes: \"stash\"\n\n\n# =============================================================================\n# Web Dashboard\n# =============================================================================\n# OAuth gate configuration for `hermes dashboard --host `.\n# The bundled Nous Portal plugin reads these on startup; settings here are\n# the canonical surface. Each can be overridden by an environment variable:\n#\n# dashboard.oauth.client_id <- HERMES_DASHBOARD_OAUTH_CLIENT_ID\n# dashboard.oauth.portal_url <- HERMES_DASHBOARD_PORTAL_URL\n# dashboard.public_url <- HERMES_DASHBOARD_PUBLIC_URL\n#\n# Env wins when set to a non-empty value. This is what Fly.io's platform-\n# secret injection uses to push per-deploy client_ids without needing to\n# bake a config.yaml into the image. Empty env values are treated as unset\n# so a provisioned-but-not-populated secret can't shadow a valid entry here.\n#\n# Local dev / on-prem deploys should typically set these via config.yaml\n# (the ~/.hermes/.env file is reserved for API keys and secrets).\n#\n# dashboard:\n# oauth:\n# client_id: \"\" # agent:{instance_id}; Portal provisions this at deploy\n# portal_url: \"\" # blank → default https://portal.nousresearch.com\n#\n# # Force the absolute base URL the OAuth callback (and any other public\n# # URL the dashboard hands to external systems) is built from. Set this\n# # for deploys behind reverse proxies that don't reliably forward\n# # X-Forwarded-Host / X-Forwarded-Proto / X-Forwarded-Prefix (manual\n# # nginx setups, on-prem ingresses, custom-domain Fly deploys without\n# # full proxy header chains).\n# #\n# # When set, the value is the complete authority: scheme + host +\n# # optional path prefix (e.g. \"https://example.com/hermes\"). The OAuth\n# # callback URL becomes \"/auth/callback\" — X-Forwarded-Prefix\n# # is IGNORED on this code path because the operator has explicitly\n# # declared the public URL and we no longer need to guess.\n# #\n# # Leave empty to use the existing proxy-header reconstruction (the\n# # default — works on Fly.io out of the box).\n# #\n# # public_url: \"https://example.com/hermes\"\n#\n# -----------------------------------------------------------------------------\n# Self-hosted OIDC dashboard auth (generic OpenID Connect — Authentik,\n# Keycloak, Zitadel, Authelia, Auth0, Okta, Google, …). Use this INSTEAD of the\n# nous block above when gating the dashboard with your own identity provider.\n# Each setting can be overridden by an environment variable:\n#\n# dashboard.oauth.self_hosted.issuer <- HERMES_DASHBOARD_OIDC_ISSUER\n# dashboard.oauth.self_hosted.client_id <- HERMES_DASHBOARD_OIDC_CLIENT_ID\n# dashboard.oauth.self_hosted.scopes <- HERMES_DASHBOARD_OIDC_SCOPES\n# dashboard.oauth.self_hosted.client_secret <- HERMES_DASHBOARD_OIDC_CLIENT_SECRET\n#\n# dashboard:\n# oauth:\n# provider: self-hosted\n# self_hosted:\n# issuer: \"https://auth.example.com/application/o/hermes/\" # required\n# client_id: \"hermes-dashboard\" # required\n# scopes: \"openid profile email\" # optional\n#\n# # OPTIONAL — set ONLY if your IDP registered the client as\n# # *confidential* (Authentik / Keycloak often default to this). When\n# # set, Hermes authenticates the client at the token endpoint\n# # (client_secret_basic or client_secret_post, auto-selected from the\n# # IDP's discovery doc) IN ADDITION to PKCE. Leave unset for a public\n# # (PKCE-only) client — the common case.\n# #\n# # This is a CREDENTIAL: prefer setting HERMES_DASHBOARD_OIDC_CLIENT_SECRET\n# # in ~/.hermes/.env over putting it here in config.yaml.\n# # client_secret: \"\"\n\n# =============================================================================\n# External secret sources\n# =============================================================================\n# Pull provider credentials from external secret managers at process startup\n# instead of storing them in ~/.hermes/.env. Only the manager's bootstrap\n# credential (e.g. BWS_ACCESS_TOKEN / OP_SERVICE_ACCOUNT_TOKEN) lives in .env\n# (or your shell / desktop session); everything else rotates centrally.\n# Failures never block startup — Hermes warns once and continues with\n# whatever .env already had.\n#\n# Multiple sources can be enabled at once:\n# - \"mapped\" sources (explicit VAR -> ref bindings, e.g. 1Password's env:\n# map) beat \"bulk\" sources (whole-project dumps like Bitwarden BSM)\n# - within a shape, the first source to claim a var wins; later claims\n# are skipped with a startup warning (never a silent clobber)\n# - a source's override_existing lets it beat .env/shell values, but\n# never another secret source's claim\n# Docs: https://hermes-agent.nousresearch.com/docs/user-guide/secrets/\n#\n# secrets:\n# # Optional explicit ordering of enabled sources.\n# # sources: [onepassword, bitwarden]\n#\n# # ---- Bitwarden Secrets Manager (bws CLI) --------------------------------\n# bitwarden:\n# enabled: false\n# access_token_env: BWS_ACCESS_TOKEN # bootstrap token, sourced from .env\n# project_id: \"\" # UUID of the BSM project to sync\n# server_url: \"\" # \"\" = US Cloud; EU/self-hosted URL otherwise\n# cache_ttl_seconds: 300 # 0 disables caching\n# override_existing: true # BSM values win over existing env\n# auto_install: true # lazy-download bws into ~/.hermes/bin\n#\n# # ---- 1Password (op CLI) -------------------------------------------------\n# onepassword:\n# enabled: false\n# # Map env-var names to op:// secret references. Each is resolved with a\n# # single `op read` at startup.\n# env:\n# OPENAI_API_KEY: \"op://Private/OpenAI/api key\"\n# ANTHROPIC_API_KEY: \"op://Private/Anthropic/credential\"\n# account: \"\" # op --account shorthand; \"\" = default\n# service_account_token_env: OP_SERVICE_ACCOUNT_TOKEN # headless auth; unset = desktop session\n# binary_path: \"\" # \"\" = resolve op via PATH; else absolute path\n# cache_ttl_seconds: 300 # 0 disables BOTH cache layers\n# override_existing: true # resolved values win over existing env\n"}, "e0176cbd47a186d3117282a14d8e36529023b000": {"t": 1785767875.3538501, "n": 62666, "h": "fe375787feaf74cf", "c": "# Hermes Agent CLI Configuration\n# Copy this file to cli-config.yaml and customize as needed.\n# This file configures the CLI behavior. Environment variables in .env take precedence.\n\n# =============================================================================\n# Model Configuration\n# =============================================================================\nmodel:\n # Default model to use (can be overridden with --model flag)\n # Both \"default\" and \"model\" work as the key name here.\n default: \"anthropic/claude-opus-4.6\"\n \n # Inference provider selection:\n # \"auto\" - Auto-detect from credentials (default)\n # \"openrouter\" - OpenRouter (requires: OPENROUTER_API_KEY or OPENAI_API_KEY)\n # \"nous\" - Nous Portal OAuth (requires: hermes login)\n # \"nous-api\" - Nous Portal API key (requires: NOUS_API_KEY)\n # \"anthropic\" - Direct Anthropic API (requires: ANTHROPIC_API_KEY)\n # \"openai-codex\" - OpenAI Codex (requires: hermes auth)\n # \"copilot\" - GitHub Copilot / GitHub Models (requires: GITHUB_TOKEN)\n # \"gemini\" - Use Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n # \"zai\" - Use z.ai / ZhipuAI GLM models (requires: GLM_API_KEY)\n # \"kimi-coding\" - Kimi / Moonshot AI (requires: KIMI_API_KEY)\n # \"minimax\" - MiniMax global (requires: MINIMAX_API_KEY)\n # \"minimax-cn\" - MiniMax China (requires: MINIMAX_CN_API_KEY)\n # \"huggingface\" - Hugging Face Inference (requires: HF_TOKEN)\n # \"nvidia\" - NVIDIA NIM / build.nvidia.com (requires: NVIDIA_API_KEY)\n # \"xiaomi\" - Xiaomi MiMo (requires: XIAOMI_API_KEY)\n # \"arcee\" - Arcee AI Trinity models (requires: ARCEEAI_API_KEY)\n # \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY — https://ollama.com/settings)\n # \"kilocode\" - KiloCode gateway (requires: KILOCODE_API_KEY)\n # \"azure-foundry\" - Microsoft Foundry / Azure OpenAI (API key or Entra ID)\n # \"lmstudio\" - LM Studio local server (optional: LM_API_KEY, defaults to http://127.0.0.1:1234/v1)\n #\n # Local servers (LM Studio, Ollama, vLLM, llama.cpp):\n # \"custom\" - Any other OpenAI-compatible endpoint. Set base_url below.\n # Aliases: \"ollama\", \"vllm\", \"llamacpp\" all map to \"custom\".\n # LM Studio is first-class and uses provider: \"lmstudio\".\n # It works with both no-auth and auth-enabled server modes.\n #\n # Can also be overridden for a single invocation with the --provider flag.\n provider: \"auto\"\n \n # API configuration (falls back to OPENROUTER_API_KEY env var)\n # api_key: \"your-key-here\" # Uncomment to set here instead of .env\n base_url: \"https://openrouter.ai/api/v1\"\n\n # Azure Foundry keyless auth example:\n # provider: \"azure-foundry\"\n # base_url: \"https://.openai.azure.com/openai/v1\"\n # auth_mode: \"entra_id\" # DefaultAzureCredential: az login, managed identity, workload identity, etc.\n # default: \"gpt-4o\" # Deployment/model name\n # entra:\n # scope: \"https://ai.azure.com/.default\" # Optional; this is the default.\n\n # ── Token limits — two settings, easy to confuse ──────────────────────────\n #\n # context_length: TOTAL context window (input + output tokens combined).\n # Controls when Hermes compresses history and validates requests.\n # Leave unset — Hermes auto-detects the correct value from the provider.\n # Set manually only when auto-detection is wrong (e.g. a local server with\n # a custom num_ctx, or a proxy that doesn't expose /v1/models).\n #\n # context_length: 131072\n #\n # max_tokens: OUTPUT cap — maximum tokens the model may generate per response.\n # Unrelated to how long your conversation history can be.\n # The OpenAI-standard name \"max_tokens\" is a misnomer; Anthropic's native\n # API has since renamed it \"max_output_tokens\" for clarity.\n # Leave unset to use the model's native output ceiling (recommended).\n # Set only if you want to deliberately limit individual response length.\n #\n# max_tokens: 8192\n\n # ── Custom request headers (optional) ─────────────────────────────────────\n #\n # default_headers: extra HTTP headers sent on every request to an\n # OpenAI-compatible endpoint. User values take precedence over the\n # provider/SDK defaults, so this is the supported way to override the\n # OpenAI Python SDK's identifying headers (User-Agent: OpenAI/Python ...,\n # X-Stainless-*) when a custom provider sits behind a gateway/WAF that\n # rejects them — e.g. an upstream that returns \"502 Upstream access\n # forbidden\" for the SDK default User-Agent but accepts a plain one.\n # Applies on the OpenAI wire only (not native Anthropic / Bedrock).\n #\n # default_headers:\n # User-Agent: \"curl/8.7.1\"\n\n# Named provider overrides (optional)\n# Use this for per-provider request timeouts, non-stream stale timeouts,\n# and per-model exceptions.\n# Applies to the primary turn client on every api_mode (OpenAI-wire, native\n# Anthropic, and Anthropic-compatible providers), the fallback chain, and\n# client rebuilds during credential rotation. For OpenAI-wire chat\n# completions (streaming and non-streaming) the configured value is also\n# used as the per-request ``timeout=`` kwarg so it wins over the legacy\n# HERMES_API_TIMEOUT env var (which still applies when no config is set).\n# ``stale_timeout_seconds`` controls the non-streaming stale-call detector and\n# wins over the legacy HERMES_API_CALL_STALE_TIMEOUT env var. Leaving these\n# unset keeps the legacy defaults (HERMES_API_TIMEOUT=1800s,\n# HERMES_API_CALL_STALE_TIMEOUT=300s, native Anthropic 900s).\n#\n# Not currently wired for AWS Bedrock (bedrock_converse + AnthropicBedrock\n# SDK paths) — those use boto3 with its own timeout configuration.\n#\n# providers:\n# ollama-local:\n# request_timeout_seconds: 300 # Longer timeout for local cold-starts\n# stale_timeout_seconds: 900 # Explicitly re-enable stale detection on local endpoints\n# anthropic:\n# request_timeout_seconds: 30 # Fast-fail cloud requests\n# models:\n# claude-opus-4.6:\n# timeout_seconds: 600 # Longer timeout for extended-thinking Opus calls\n# openai-codex:\n# models:\n# gpt-5.4:\n# stale_timeout_seconds: 1800 # Longer non-stream stale timeout for slow large-context turns\n\n# =============================================================================\n# OpenRouter Provider Routing (only applies when using OpenRouter)\n# =============================================================================\n# Control how requests are routed across providers on OpenRouter.\n# See: https://openrouter.ai/docs/guides/routing/provider-selection\n#\n# provider_routing:\n# # Sort strategy: \"price\" (default), \"throughput\", or \"latency\"\n# # Append :nitro to model name for a shortcut to throughput sorting.\n# sort: \"throughput\"\n#\n# # Only allow these providers (provider slugs from OpenRouter)\n# # only: [\"anthropic\", \"google\"]\n#\n# # Skip these providers entirely\n# # ignore: [\"deepinfra\", \"fireworks\"]\n#\n# # Try providers in this order (overrides default load balancing)\n# # order: [\"anthropic\", \"google\", \"together\"]\n#\n# # Require providers to support all parameters in your request\n# # require_parameters: true\n#\n# # Data policy: \"allow\" (default) or \"deny\" to exclude providers that may store data\n# # data_collection: \"deny\"\n\n# =============================================================================\n# OpenRouter Response Caching (only applies when using OpenRouter)\n# =============================================================================\n# Cache identical API responses at the OpenRouter edge for free instant replays.\n# When enabled, identical requests (same model, messages, parameters) return\n# cached responses with zero billing. Separate from Anthropic prompt caching.\n# See: https://openrouter.ai/docs/guides/features/response-caching\n#\n# openrouter:\n# response_cache: true # Enable response caching (default: true)\n# response_cache_ttl: 300 # Cache TTL in seconds, 1-86400 (default: 300)\n\n# =============================================================================\n# Git Worktree Isolation\n# =============================================================================\n# When enabled, each CLI session creates an isolated git worktree so multiple\n# agents can work on the same repo concurrently without file collisions.\n# Equivalent to always passing --worktree / -w on the command line.\n#\n# worktree: true # Always create a worktree when in a git repo\n# worktree: false # Default — only create when -w flag is passed\n\n# =============================================================================\n# Terminal Tool Configuration\n# =============================================================================\n# Choose ONE of the following terminal configurations by uncommenting it.\n# The terminal tool executes commands in the specified environment.\n\n# -----------------------------------------------------------------------------\n# OPTION 1: Local execution (default)\n# Commands run directly on your machine in the current directory\n# -----------------------------------------------------------------------------\n# Working directory behavior:\n# - CLI (`hermes` command): Uses \".\" (current directory where you run hermes)\n# - Gateway/messaging/cron: Uses terminal.cwd here; legacy .env cwd values are deprecated\nterminal:\n backend: \"local\"\n cwd: \".\" # For local backend: \".\" = current directory. Ignored for remote backends unless a backend documents otherwise.\n timeout: 180\n docker_mount_cwd_to_workspace: false # SECURITY: off by default. Opt in to mount the launch cwd into Docker /workspace.\n lifetime_seconds: 300\n # sudo_password: \"hunter2\" # Optional: pipe a sudo password via sudo -S. SECURITY WARNING: plaintext.\n # sudo_password: \"\" # Explicit empty password: try empty and never open the interactive sudo prompt.\n\n# -----------------------------------------------------------------------------\n# OPTION 2: SSH remote execution\n# Commands run on a remote server - agent code stays local (sandboxed)\n# Great for: keeping agent isolated from its own code, using powerful remote hardware\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"ssh\"\n# cwd: \"/home/myuser/project\" # Path on the REMOTE server\n# timeout: 180\n# lifetime_seconds: 300\n# ssh_host: \"my-server.example.com\"\n# ssh_user: \"myuser\"\n# ssh_port: 22\n# ssh_key: \"~/.ssh/id_rsa\" # Optional - uses ssh-agent if not specified\n\n# -----------------------------------------------------------------------------\n# OPTION 3: Docker container\n# Commands run in an isolated Docker container\n# Great for: reproducible environments, testing, isolation\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"docker\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /)\n# timeout: 180\n# lifetime_seconds: 300\n# docker_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# docker_mount_cwd_to_workspace: true # Explicit opt-in: mount your launch cwd into /workspace\n# # Optional: run the container as your host user's uid:gid so files written\n# # into bind-mounted dirs are owned by you, not root. Drops SETUID/SETGID\n# # caps too since no gosu privilege drop is needed. Leave off if your\n# # chosen docker_image expects to start as root.\n# docker_run_as_host_user: true\n# # Optional: explicitly forward selected env vars into Docker.\n# # These values come from your current shell first, then ~/.hermes/.env.\n# # Warning: anything forwarded here is visible to commands run in the container.\n# docker_forward_env:\n# - \"GITHUB_TOKEN\"\n# - \"NPM_TOKEN\"\n# # Optional: extra flags passed verbatim to docker run (appended after security defaults).\n# # Useful for adding capabilities (e.g. apt installs needing SETUID) or custom options.\n# # Example: add a Linux capability not included by default\n# # docker_extra_args:\n# # - \"--cap-add\"\n# # - \"SETUID\"\n\n# -----------------------------------------------------------------------------\n# OPTION 4: Singularity/Apptainer container\n# Commands run in a Singularity container (common in HPC environments)\n# Great for: HPC clusters, shared compute environments\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"singularity\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# singularity_image: \"docker://nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 5: Modal cloud execution\n# Commands run on Modal's cloud infrastructure\n# Great for: GPU access, scalable compute, serverless execution\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"modal\"\n# cwd: \"/workspace\" # Path INSIDE the sandbox (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# modal_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 6: Daytona cloud execution\n# Commands run in Daytona cloud sandboxes\n# Great for: Cloud dev environments, persistent workspaces, team collaboration\n# Requires: pip install daytona, DAYTONA_API_KEY env var\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"daytona\"\n# cwd: \"~\"\n# timeout: 180\n# lifetime_seconds: 300\n# daytona_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# container_disk: 10240 # Daytona max is 10GB per sandbox\n\n#\n# --- Container resource limits (docker, singularity, modal, daytona -- ignored for local/ssh) ---\n# These settings apply to all container backends. They control the resources\n# allocated to the sandbox and whether its filesystem persists across sessions.\n container_cpu: 1 # CPU cores\n container_memory: 5120 # Memory in MB (5120 = 5GB)\n container_disk: 51200 # Disk in MB (51200 = 50GB)\n container_persistent: true # Persist filesystem across sessions (false = ephemeral)\n\n# -----------------------------------------------------------------------------\n# SUDO SUPPORT (works with ALL backends above)\n# -----------------------------------------------------------------------------\n# Add sudo_password to any terminal config above to enable sudo commands.\n# The password is piped via `sudo -S`. Works with local, ssh, docker, etc.\n#\n# SECURITY WARNING: Password stored in plaintext!\n#\n# INTERACTIVE PROMPT: If sudo_password is unset and the CLI is running,\n# you'll be prompted to enter your password when sudo is needed:\n# - 45-second timeout (auto-skips if no input)\n# - Press Enter to skip (command fails gracefully)\n# - Password is hidden while typing\n# - Password is cached for the session\n#\n# EMPTY PASSWORDS: Setting sudo_password to an explicit empty string is different\n# from leaving it unset. Hermes will try an empty password via `sudo -S` and\n# will not open the interactive prompt. This is useful for passwordless sudo,\n# Touch ID sudo setups, and environments where prompting is just noise.\n#\n# ALTERNATIVES:\n# - SSH backend: Configure passwordless sudo on the remote server\n# - Containers: Run as root inside the container (no sudo needed)\n# - Local: Configure /etc/sudoers for specific commands\n#\n# Example (add to your terminal section):\n# sudo_password: \"your-password-here\"\n\n# =============================================================================\n# Security Scanning (tirith)\n# =============================================================================\n# Optional pre-exec command security scanning via tirith.\n# Detects homograph URLs, pipe-to-shell, terminal injection, env manipulation.\n# Install: brew install sheeki03/tap/tirith\n# Docs: https://github.com/sheeki03/tirith\n#\n# security:\n# tirith_enabled: true # Enable/disable tirith scanning\n# tirith_path: \"tirith\" # Path to tirith binary (supports ~ expansion)\n# tirith_timeout: 5 # Scan timeout in seconds\n# tirith_fail_open: true # Allow commands if tirith unavailable\n\n# =============================================================================\n# Browser Tool Configuration\n# =============================================================================\nbrowser:\n # Inactivity timeout in seconds - browser sessions are automatically closed\n # after this period of no activity between agent loops (default: 120 = 2 minutes)\n inactivity_timeout: 120\n\n# =============================================================================\n# Tool Loop Guardrails\n# =============================================================================\n# Soft warnings are enabled by default. They append guidance to repeated failed\n# or non-progressing tool results but still let the tool execute. Hard stops are\n# opt-in circuit breakers for autonomous/cron sessions where stopping a loop is\n# preferable to spending the full iteration budget.\ntool_loop_guardrails:\n warnings_enabled: true\n hard_stop_enabled: false\n warn_after:\n exact_failure: 2\n same_tool_failure: 3\n idempotent_no_progress: 2\n hard_stop_after:\n exact_failure: 5\n same_tool_failure: 8\n idempotent_no_progress: 5\n\n# =============================================================================\n# Context Compression (Auto-shrinks long conversations)\n# =============================================================================\n# When conversation approaches model's context limit, middle turns are\n# automatically summarized to free up space while preserving important context.\n#\n# HOW IT WORKS:\n# 1. Tracks actual token usage from API responses (not estimates)\n# 2. When prompt_tokens >= threshold% of model's context_length, triggers compression\n# 3. Protects first 3 turns (system prompt, initial request, first response)\n# 4. Protects last N turns (default 20 messages = ~10 full turns of recent context)\n# 5. Summarizes middle turns using a fast/cheap model\n# 6. Inserts summary as a user message, continues conversation seamlessly\n#\n# Post-compression tail budget is target_ratio × threshold × context_length:\n# 200K context, threshold 0.50, ratio 0.20 → 20K tokens of recent tail preserved\n# 1M context, threshold 0.50, ratio 0.20 → 100K tokens of recent tail preserved\n#\ncompression:\n # Enable automatic context compression (default: true)\n # Set to false if you prefer to manage context manually or want errors on overflow\n enabled: true\n \n # Trigger compression at this % of model's context limit (default: 0.50 = 50%)\n # Lower values = more aggressive compression, higher values = compress later\n threshold: 0.50\n \n # Fraction of the threshold to preserve as recent tail (default: 0.20 = 20%)\n # e.g. 20% of 50% threshold = 10% of total context kept as recent messages.\n # Summary output is separately capped at 12K tokens (Gemini output limit).\n # Range: 0.10 - 0.80\n target_ratio: 0.20\n\n # Number of most-recent messages to always preserve (default: 20 ≈ 10 full turns)\n # Higher values keep more recent conversation intact at the cost of more aggressive\n # compression of older turns.\n protect_last_n: 20\n\n # Number of non-system messages to protect at the head of the transcript, in\n # ADDITION to the system prompt (which is always implicitly protected).\n # Head messages are NEVER summarized — they survive every compression\n # indefinitely. This gives stable early context for short/medium sessions,\n # but in long-running sessions that rely on rolling compaction the pinned\n # opening turns may not match how you want the session framed over time.\n # Set to 0 to preserve ONLY the system prompt (plus the rolling summary\n # and recent tail) — the cleanest configuration for long-running sessions.\n # Default 3 preserves the system prompt plus the first three non-system\n # head messages, matching the pre-feature behaviour.\n protect_first_n: 3\n\n # To pin a specific model/provider for compression summaries, use the\n # auxiliary section below (auxiliary.compression.provider / model).\n\n# =============================================================================\n# Anthropic prompt caching TTL\n# =============================================================================\n# When prompt caching is active (Claude via OpenRouter or native Anthropic),\n# Anthropic supports two TTL tiers for cached prefixes: \"5m\" (default) and\n# \"1h\". Other values are ignored and \"5m\" is used.\n#\nprompt_caching:\n cache_ttl: \"5m\" # use \"1h\" for long sessions with pauses between turns\n\n# =============================================================================\n# Auxiliary Models (Advanced — Experimental)\n# =============================================================================\n# Hermes uses lightweight \"auxiliary\" models for side tasks: image analysis,\n# browser screenshot analysis, web page summarization, TTS audio-tag insertion,\n# and context compression.\n#\n# By default these use Gemini Flash via OpenRouter or Nous Portal and are\n# auto-detected from your credentials. You do NOT need to change anything\n# here for normal usage.\n#\n# WARNING: Overriding these with providers other than OpenRouter or Nous Portal\n# is EXPERIMENTAL and may not work. Not all models/providers support vision,\n# produce usable summaries, or accept the same API format. Change at your own\n# risk — if things break, reset to \"auto\" / empty values.\n#\n# Each task has its own provider + model pair so you can mix providers.\n# For example: OpenRouter for vision (needs multimodal), but your main\n# local endpoint for compression (just needs text).\n#\n# Provider options:\n# \"auto\" - Best available: OpenRouter → Nous Portal → main endpoint (default)\n# \"openrouter\" - Force OpenRouter (requires OPENROUTER_API_KEY)\n# \"nous\" - Force Nous Portal (requires: hermes login)\n# \"gemini\" - Force Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n# \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY)\n# \"codex\" - Force Codex OAuth (requires: hermes model → Codex).\n# Uses gpt-5.3-codex which supports vision.\n# \"main\" - Use your custom endpoint (OPENAI_BASE_URL + OPENAI_API_KEY).\n# Works with OpenAI API, local models, or any OpenAI-compatible\n# endpoint. Also falls back to Codex OAuth and API-key providers.\n#\n# Model: leave empty to use the provider's default. When empty, OpenRouter\n# uses \"google/gemini-3-flash-preview\" and Nous uses \"gemini-3-flash\".\n# Other providers pick a sensible default automatically.\n#\n# auxiliary:\n# # Image analysis: vision_analyze tool + browser screenshots\n# vision:\n# provider: \"auto\"\n# model: \"\" # e.g. \"google/gemini-2.5-flash\", \"openai/gpt-4o\"\n# timeout: 30 # LLM API call timeout (seconds)\n# download_timeout: 30 # Image HTTP download timeout (seconds)\n# # Increase for slow connections or self-hosted image servers\n#\n# # Web page scraping / summarization + browser page text extraction\n# web_extract:\n# provider: \"auto\"\n# model: \"\"\n#\n# # Gemini 3.1 TTS hidden audio-tag insertion\n# tts_audio_tags:\n# provider: \"auto\" # empty model = your main chat model\n# model: \"\"\n# timeout: 30\n#\n# # Session search — summarizes matching past sessions\n# session_search:\n# provider: \"auto\"\n# model: \"\"\n# timeout: 30\n# max_concurrency: 3 # Limit parallel summaries to reduce request-burst 429s\n# extra_body: {} # Provider-specific OpenAI-compatible request fields\n# # Example for providers that support request-body\n# # reasoning controls:\n# # extra_body:\n# # enable_thinking: false\n\n# =============================================================================\n# Persistent Memory\n# =============================================================================\n# Bounded curated memory injected into the system prompt every session.\n# Two stores: MEMORY.md (agent's notes) and USER.md (user profile).\n# Character limits keep the memory small and focused. The agent manages\n# pruning -- when at the limit, it must consolidate or replace entries.\n# Disabled by default in batch_runner.\n#\nmemory:\n # Agent's personal notes: environment facts, conventions, things learned\n memory_enabled: true\n \n # User profile: preferences, communication style, expectations\n user_profile_enabled: true\n \n # Character limits (~2.75 chars per token, model-independent)\n memory_char_limit: 2200 # ~800 tokens\n user_char_limit: 1375 # ~500 tokens\n\n # Periodic memory nudge: remind the agent to consider saving memories\n # every N user turns. Set to 0 to disable. Only active when memory is enabled.\n nudge_interval: 10 # Nudge every 10 user turns (0 = disabled)\n\n # Memory flush: give the agent one turn to save memories before context is\n # lost (compression, /new, /reset, exit). Set to 0 to disable.\n # For exit/reset, only fires if the session had at least this many user turns.\n flush_min_turns: 6 # Min user turns to trigger flush on exit/reset (0 = disabled)\n\n# =============================================================================\n# Session Reset Policy (Messaging Platforms)\n# =============================================================================\n# Controls when messaging sessions (Telegram, Discord, WhatsApp, Slack) are\n# automatically cleared. Without resets, conversation context grows indefinitely\n# which increases API costs with every message.\n#\n# When a reset triggers, the agent first saves important information to its\n# persistent memory — but the conversation context is wiped. The agent starts\n# fresh but retains learned facts via its memory system.\n#\n# Users can always manually reset with /reset or /new in chat.\n#\n# Modes:\n# \"both\" - Reset on EITHER inactivity timeout or daily boundary (recommended)\n# \"idle\" - Reset only after N minutes of inactivity\n# \"daily\" - Reset only at a fixed hour each day\n# \"none\" - Never auto-reset; context lives until /reset or compression kicks in\n#\n# When a reset triggers, the agent gets one turn to save important memories and\n# skills before the context is wiped. Persistent memory carries across sessions.\n#\nsession_reset:\n mode: both # \"both\", \"idle\", \"daily\", or \"none\"\n idle_minutes: 1440 # Inactivity timeout in minutes (default: 1440 = 24 hours)\n at_hour: 4 # Daily reset hour, 0-23 local time (default: 4 AM)\n\n# Maximum number of simultaneously active chat sessions across CLI, TUI,\n# dashboard chat, and messaging gateway. Set to null, 0, or omit to allow\n# unlimited concurrent sessions. When the limit is reached, new sessions get a\n# clean error while existing active sessions keep their normal behavior. This\n# top-level key takes precedence over gateway.max_concurrent_sessions. The cap\n# is a best-effort single-host/profile runtime guard; Hermes fails open if the\n# local runtime lease registry cannot be read or locked.\nmax_concurrent_sessions: null\n\n# When true, group/channel chats use one session per participant when the platform\n# provides a user ID. This is the secure default and prevents users in the same\n# room from sharing context, interrupts, and token costs. Set false only if you\n# explicitly want one shared \"room brain\" per group/channel.\ngroup_sessions_per_user: true\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Gateway Streaming\n# ─────────────────────────────────────────────────────────────────────────────\n# Stream tokens to messaging platforms in real-time. The bot sends a message\n# on first token, then progressively edits it as more tokens arrive.\n# Disabled by default — enable to try the streaming UX on Telegram/Discord/Slack.\n# For Telegram, partial edits are sent as plain text and only the final edit uses MarkdownV2.\nstreaming:\n enabled: false\n # transport: edit # \"edit\" = progressive editMessageText\n # edit_interval: 0.3 # seconds between message edits\n # buffer_threshold: 40 # chars before forcing an edit flush\n # cursor: \" ▉\" # cursor shown during streaming\n\n# =============================================================================\n# Skills Configuration\n# =============================================================================\n# Skills are reusable procedures the agent can load and follow. The agent can\n# also create new skills after completing complex tasks.\n#\nskills:\n # Nudge the agent to create skills after complex tasks.\n # Every N tool-calling iterations, remind the model to consider saving a skill.\n # Set to 0 to disable.\n creation_nudge_interval: 15\n\n # External skill directories — share skills across tools/agents without\n # copying them into ~/.hermes/skills/. Each path is expanded (~ and ${VAR})\n # and resolved to an absolute path. External dirs are read-only: skill\n # creation always writes to ~/.hermes/skills/. Local skills take precedence\n # when names collide.\n # external_dirs:\n # - ~/.agents/skills\n # - /home/shared/team-skills\n\n# =============================================================================\n# Agent Behavior\n# =============================================================================\nagent:\n # Maximum tool-calling iterations per conversation\n # Higher = more room for complex tasks, but costs more tokens\n # Recommended: 20-30 for focused tasks, 50-100 for open exploration\n max_turns: 60\n\n # Inactivity timeout for gateway agent runs (seconds, 0 = unlimited).\n # The agent can run indefinitely when actively calling tools or receiving\n # API responses. Only fires after the agent has been idle for this duration.\n # gateway_timeout: 1800\n\n # Staged warning: send a warning before escalating to full timeout.\n # Fires once per run when inactivity reaches this threshold (seconds).\n # Set to 0 to disable the warning.\n # gateway_timeout_warning: 900\n\n # Graceful drain timeout for gateway stop/restart (seconds).\n # The gateway stops accepting new work, waits for in-flight agents to\n # finish, then interrupts anything still running after this timeout.\n # 0 = no drain, interrupt immediately.\n # restart_drain_timeout: 60\n\n # Max app-level retry attempts for API errors (connection drops, provider\n # timeouts, 5xx, etc.) before the agent surfaces the failure. Lower this\n # to 1 if you use fallback providers and want fast failover on flaky\n # primaries (default 3). The OpenAI SDK does its own low-level retries\n # underneath this wrapper — this is the Hermes-level loop.\n # api_max_retries: 3\n \n # Enable verbose logging\n verbose: false\n \n # Reasoning effort level (OpenRouter and Nous Portal)\n # Controls how much \"thinking\" the model does before responding.\n # Options: \"xhigh\" (max), \"high\", \"medium\", \"low\", \"minimal\", \"none\" (disable)\n reasoning_effort: \"medium\"\n \n # Predefined personalities (use with /personality command)\n personalities:\n helpful: \"You are a helpful, friendly AI assistant.\"\n concise: \"You are a concise assistant. Keep responses brief and to the point.\"\n technical: \"You are a technical expert. Provide detailed, accurate technical information.\"\n creative: \"You are a creative assistant. Think outside the box and offer innovative solutions.\"\n teacher: \"You are a patient teacher. Explain concepts clearly with examples.\"\n kawaii: \"You are a kawaii assistant! Use cute expressions like (◕‿◕), ★, ♪, and ~! Add sparkles and be super enthusiastic about everything! Every response should feel warm and adorable desu~! ヽ(>∀<☆)ノ\"\n catgirl: \"You are Neko-chan, an anime catgirl AI assistant, nya~! Add 'nya' and cat-like expressions to your speech. Use kaomoji like (=^・ω・^=) and ฅ^•ﻌ•^ฅ. Be playful and curious like a cat, nya~!\"\n pirate: \"Arrr! Ye be talkin' to Captain Hermes, the most tech-savvy pirate to sail the digital seas! Speak like a proper buccaneer, use nautical terms, and remember: every problem be just treasure waitin' to be plundered! Yo ho ho!\"\n shakespeare: \"Hark! Thou speakest with an assistant most versed in the bardic arts. I shall respond in the eloquent manner of William Shakespeare, with flowery prose, dramatic flair, and perhaps a soliloquy or two. What light through yonder terminal breaks?\"\n surfer: \"Duuude! You're chatting with the chillest AI on the web, bro! Everything's gonna be totally rad. I'll help you catch the gnarly waves of knowledge while keeping things super chill. Cowabunga! 🤙\"\n noir: \"The rain hammered against the terminal like regrets on a guilty conscience. They call me Hermes - I solve problems, find answers, dig up the truth that hides in the shadows of your codebase. In this city of silicon and secrets, everyone's got something to hide. What's your story, pal?\"\n uwu: \"hewwo! i'm your fwiendwy assistant uwu~ i wiww twy my best to hewp you! *nuzzles your code* OwO what's this? wet me take a wook! i pwomise to be vewy hewpful >w<\"\n philosopher: \"Greetings, seeker of wisdom. I am an assistant who contemplates the deeper meaning behind every query. Let us examine not just the 'how' but the 'why' of your questions. Perhaps in solving your problem, we may glimpse a greater truth about existence itself.\"\n hype: \"YOOO LET'S GOOOO!!! 🔥🔥🔥 I am SO PUMPED to help you today! Every question is AMAZING and we're gonna CRUSH IT together! This is gonna be LEGENDARY! ARE YOU READY?! LET'S DO THIS! 💪😤🚀\"\n\n# =============================================================================\n# Toolsets\n# =============================================================================\n# Control which tools the agent has access to.\n# Use `hermes tools` to interactively enable/disable tools per platform.\n\n# =============================================================================\n# Platform Toolsets (per-platform tool configuration)\n# =============================================================================\n# Override which toolsets are available on each platform.\n# If a platform isn't listed here, its built-in default is used.\n#\n# You can use EITHER:\n# - A preset like \"hermes-cli\" or \"hermes-telegram\" (curated tool set)\n# - A list of individual toolsets to compose your own (see list below)\n#\n# Supported platform keys: cli, telegram, discord, whatsapp, slack, qqbot, teams, google_chat\n#\n# Examples:\n#\n# # Use presets (same as defaults):\n# platform_toolsets:\n# cli: [hermes-cli]\n# telegram: [hermes-telegram]\n#\n# # Custom: give Telegram only web + terminal + file + planning:\n# platform_toolsets:\n# telegram: [web, terminal, file, todo]\n#\n# # Custom: CLI without browser or image gen:\n# platform_toolsets:\n# cli: [web, terminal, file, skills, todo, tts, cronjob]\n#\n# # Restrictive: Discord gets read-only tools only:\n# platform_toolsets:\n# discord: [web, vision, skills, todo]\n#\n# If not set, defaults are:\n# cli: hermes-cli (everything + cronjob management)\n# telegram: hermes-telegram (terminal, file, web, vision, image, tts, browser, skills, todo, cronjob, messaging)\n# discord: hermes-discord (same as telegram)\n# whatsapp: hermes-whatsapp (same as telegram)\n# slack: hermes-slack (same as telegram)\n# signal: hermes-signal (same as telegram)\n# homeassistant: hermes-homeassistant (same as telegram)\n# qqbot: hermes-qqbot (same as telegram)\n# teams: hermes-teams (same as telegram)\n# google_chat: hermes-google_chat (same as telegram)\n#\nplatform_toolsets:\n cli: [hermes-cli]\n telegram: [hermes-telegram]\n discord: [hermes-discord]\n whatsapp: [hermes-whatsapp]\n slack: [hermes-slack]\n signal: [hermes-signal]\n homeassistant: [hermes-homeassistant]\n qqbot: [hermes-qqbot]\n yuanbao: [hermes-yuanbao]\n teams: [hermes-teams]\n google_chat: [hermes-google_chat]\n\n# =============================================================================\n# Gateway Platform Settings\n# =============================================================================\n# Optional per-platform messaging settings.\n# Platform-specific knobs live under `extra`.\n#\n# platforms:\n# telegram:\n# reply_to_mode: \"first\" # off | first | all\n# # guest_mode lets explicit @mentions from non-allowlisted groups through.\n# # Default false; ordinary messages, replies, and regex wake words stay blocked.\n# guest_mode: false\n# # allowed_chats: [\"-1001234567890\"]\n# extra:\n# disable_link_previews: false # Set true to suppress Telegram URL previews in bot messages\n#\n# Discord-specific settings (config.yaml top-level, not under platforms:):\n#\n# discord:\n# require_mention: true # Require @mention in server channels (default: true)\n# auto_thread: true # Auto-create thread on @mention (default: true)\n# free_response_channels: \"\" # Channel IDs where no mention is needed\n# reactions: true # Show processing reactions (default: true)\n# history_backfill: true # Recover missed channel messages on mention (default: true)\n# history_backfill_limit: 50 # Max messages to scan backwards (default: 50)\n# approval_mentions: false # Mention numeric allowed users on approval prompts (default: false)\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Available toolsets (use these names in platform_toolsets or the toolsets list)\n#\n# Run `hermes chat --list-toolsets` to see all toolsets and their tools.\n# Run `hermes chat --list-tools` to see every individual tool with descriptions.\n# ─────────────────────────────────────────────────────────────────────────────\n#\n# INDIVIDUAL TOOLSETS (compose your own):\n# web - web_search, web_extract\n# search - web_search only (no scraping)\n# terminal - terminal, process\n# file - read_file, write_file, patch, search\n# browser - browser_navigate, browser_snapshot, browser_click, browser_type,\n# browser_scroll, browser_back, browser_press,\n# browser_get_images, browser_vision (requires BROWSERBASE_API_KEY)\n# vision - vision_analyze (requires OPENROUTER_API_KEY)\n# image_gen - image_generate (requires FAL_KEY)\n# skills - skills_list, skill_view\n# skills_hub - skill_hub (search/install/manage from online registries — user-driven only)\n# moa - mixture_of_agents (requires OPENROUTER_API_KEY)\n# todo - todo (in-memory task planning, no deps)\n# tts - text_to_speech (Edge TTS free, or ELEVENLABS/OPENAI/MINIMAX/MISTRAL key)\n# cronjob - cronjob (create/list/update/pause/resume/run/remove scheduled tasks)\n#\n# PRESETS (curated bundles):\n# hermes-cli - All of the above except send_message\n# hermes-telegram - terminal, file, web, vision, image_gen, tts, browser,\n# skills, todo, cronjob, send_message\n# hermes-discord - Same as hermes-telegram\n# hermes-whatsapp - Same as hermes-telegram\n# hermes-slack - Same as hermes-telegram\n#\n# COMPOSITE:\n# debugging - terminal + web + file\n# safe - web + vision + moa (no terminal access)\n# all - Everything available\n#\n# web - Web search and content extraction (web_search, web_extract)\n# search - Web search only, no scraping (web_search)\n# terminal - Command execution and process management (terminal, process)\n# file - File operations: read, write, patch, search\n# browser - Full browser automation (navigate, click, type, screenshot, etc.)\n# vision - Image analysis (vision_analyze)\n# image_gen - Image generation with FLUX (image_generate)\n# skills - Load skill documents (skills_list, skill_view)\n# moa - Mixture of Agents reasoning (mixture_of_agents)\n# todo - Task planning and tracking for multi-step work\n# memory - Persistent memory across sessions (personal notes + user profile)\n# session_search - Search and recall past conversations (FTS5 + Gemini Flash summarization)\n# tts - Text-to-speech (Edge TTS free, ElevenLabs, OpenAI, MiniMax, Mistral)\n# cronjob - Schedule and manage automated tasks (CLI-only)\n#\n# Composite toolsets:\n# debugging - terminal + web + file (for troubleshooting)\n# safe - web + vision + moa (no terminal access)\n\n# NOTE: The top-level \"toolsets\" key is deprecated and ignored.\n# Tool configuration is managed per-platform via platform_toolsets above.\n# Use `hermes tools` to configure interactively, or edit platform_toolsets directly.\n#\n# CLI override: hermes chat --toolsets terminal,web,file\n\n# =============================================================================\n# MCP (Model Context Protocol) Servers\n# =============================================================================\n# Connect to external MCP servers to add tools from the MCP ecosystem.\n# Each server's tools are automatically discovered and registered.\n# See docs/mcp.md for full documentation.\n#\n# Stdio servers (spawn a subprocess):\n# command: the executable to run\n# args: command-line arguments\n# env: environment variables (only these + safe defaults passed to subprocess)\n#\n# HTTP servers (connect to a URL):\n# url: the MCP server endpoint\n# headers: HTTP headers (e.g., for authentication)\n#\n# Optional per-server settings:\n# timeout: tool call timeout in seconds (default: 120)\n# connect_timeout: initial connection timeout (default: 60)\n#\n# mcp_servers:\n# time:\n# command: uvx\n# args: [\"mcp-server-time\"]\n# filesystem:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-filesystem\", \"/home/user\"]\n# notion:\n# url: https://mcp.notion.com/mcp\n# github:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-github\"]\n# env:\n# GITHUB_PERSONAL_ACCESS_TOKEN: \"ghp_...\"\n#\n# Sampling (server-initiated LLM requests) — enabled by default.\n# Per-server config under the 'sampling' key:\n# analysis:\n# command: npx\n# args: [\"-y\", \"analysis-server\"]\n# sampling:\n# enabled: true # default: true\n# model: \"gemini-3-flash\" # override model (optional)\n# max_tokens_cap: 4096 # max tokens per request\n# timeout: 30 # LLM call timeout (seconds)\n# max_rpm: 10 # max requests per minute\n# allowed_models: [] # model whitelist (empty = all)\n# max_tool_rounds: 5 # tool loop limit (0 = disable)\n# log_level: \"info\" # audit verbosity\n\n# =============================================================================\n# Text-to-Speech\n# =============================================================================\n# TTS defaults to Edge TTS unless changed in ~/.hermes/config.yaml.\n# Gemini TTS supports persona/director prompt files, and Gemini 3.1 Flash TTS\n# can use a hidden auxiliary rewrite pass to insert expressive square-bracket\n# audio tags into the TTS script without showing tags in chat.\n#\n# tts:\n# provider: \"gemini\"\n# gemini:\n# model: \"gemini-3.1-flash-tts-preview\"\n# voice: \"Kore\"\n# audio_tags: false\n# persona_prompt_file: \"\" # e.g. ~/.hermes/tts/radio-host.md\n\n# =============================================================================\n# Voice Transcription (Speech-to-Text)\n# =============================================================================\n# Automatically transcribe voice messages on messaging platforms.\n# Providers: local (free, faster-whisper) | groq (free tier) | openai (Whisper API) | mistral (Voxtral Transcribe)\n# Set the corresponding API key in .env: GROQ_API_KEY, OPENAI_API_KEY, or MISTRAL_API_KEY.\nstt:\n enabled: true\n # provider: \"local\" # auto-detected if omitted\n local:\n model: \"base\" # tiny | base | small | medium | large-v3 | turbo\n # language: \"\" # auto-detect; set to \"en\", \"es\", \"fr\", etc. to force\n openai:\n model: \"whisper-1\" # whisper-1 | gpt-4o-mini-transcribe | gpt-4o-transcribe\n # mistral:\n # model: \"voxtral-mini-latest\" # voxtral-mini-latest | voxtral-mini-2602\n\n# =============================================================================\n# Response Pacing (Messaging Platforms)\n# =============================================================================\n# Add human-like delays between message chunks.\n# human_delay:\n# mode: \"off\" # \"off\" | \"natural\" | \"custom\"\n# min_ms: 800 # Min delay (custom mode only)\n# max_ms: 2500 # Max delay (custom mode only)\n\n# =============================================================================\n# Session Logging\n# =============================================================================\n# Session trajectories are automatically saved to logs/ directory.\n# Each session creates: logs/session_YYYYMMDD_HHMMSS_UUID.json\n#\n# The session ID is displayed in the welcome banner for easy reference.\n# Logs contain full conversation history in trajectory format:\n# - System prompt, user messages, assistant responses\n# - Tool calls with inputs/outputs\n# - Timestamps for debugging\n#\n# No configuration needed - logging is always enabled.\n# To disable, you would need to modify the source code.\n\n# =============================================================================\n# Code Execution Sandbox (Programmatic Tool Calling)\n# =============================================================================\n# The execute_code tool runs Python scripts that call Hermes tools via RPC.\n# Intermediate tool results stay out of the LLM's context window.\ncode_execution:\n timeout: 300 # Max seconds per script before kill (default: 300 = 5 min)\n max_tool_calls: 50 # Max RPC tool calls per execution (default: 50)\n\n# =============================================================================\n# Subagent Delegation\n# =============================================================================\n# The delegate_task tool spawns child agents with isolated context.\n# Supports single tasks and batch mode (default 3 parallel, configurable).\ndelegation:\n max_iterations: 50 # Max tool-calling turns per child (default: 50)\n # max_concurrent_children: 3 # Max parallel child agents per batch (default: 3, floor: 1, no ceiling).\n # WARNING: values above 10 multiply API cost linearly.\n # max_spawn_depth: 1 # Delegation tree depth cap (range: 1-3, default: 1 = flat).\n # Raise to 2 to allow workers to spawn their own subagents.\n # Requires role=\"orchestrator\" on intermediate agents.\n # orchestrator_enabled: true # Kill switch for role=\"orchestrator\" children (default: true).\n # subagent_auto_approve: false # When a subagent hits a dangerous-command approval prompt, auto-deny (default: false)\n # or auto-approve \"once\" (true) instead of blocking on stdin.\n # The parent TUI owns stdin, so blocking would deadlock; non-interactive resolution is required.\n # Both choices emit a logger.warning audit line. Flip to true only for cron/batch pipelines.\n # inherit_mcp_toolsets: true # When explicit child toolsets are narrowed, also keep the parent's MCP toolsets (default: true). Set false for strict intersection.\n # model: \"google/gemini-3-flash-preview\" # Override model for subagents (empty = inherit parent)\n # provider: \"openrouter\" # Override provider for subagents (empty = inherit parent)\n # # Resolves full credentials (base_url, api_key) automatically.\n # # Supported: openrouter, nous, zai, kimi-coding, minimax\n\n# =============================================================================\n# Honcho Integration (Cross-Session User Modeling)\n# =============================================================================\n# AI-native persistent memory via Honcho (https://honcho.dev/).\n# Builds a deeper understanding of the user across sessions and tools.\n# Runs alongside USER.md — additive, not a replacement.\n#\n# Requires: pip install honcho-ai\n# Config: ~/.honcho/config.json (shared with Claude Code, Cursor, etc.)\n# API key: HONCHO_API_KEY in ~/.hermes/.env or ~/.honcho/config.json\n#\n# Hermes-specific overrides (optional — most config comes from ~/.honcho/config.json):\n# honcho: {}\n\n# =============================================================================\n# Display\n# =============================================================================\ndisplay:\n # Use compact banner mode (hides the ASCII-art banner, shows a single line).\n # true: Compact single-line banner\n # false: Full ASCII banner with tool/skill summary (default)\n compact: false\n\n # Tool progress display level (CLI and gateway)\n # off: Silent — no tool activity shown, just the final response\n # new: Show a tool indicator only when the tool changes (skip repeats)\n # all: Show every tool call with a short preview (default)\n # verbose: Full args, results, and debug logs (same as /verbose)\n # Toggle at runtime with /verbose in the CLI\n tool_progress: all\n\n # Per-platform defaults can be quieter than the global setting. Telegram\n # tunes for mobile: tool_progress and busy_ack_detail default off (no\n # per-tool breadcrumb stream, no \"iteration 21/60\" debug detail in busy\n # acks or heartbeats), but interim_assistant_messages and\n # long_running_notifications STAY ON so the user has real signal between\n # turn start and final answer (mid-turn assistant commentary + a single\n # edit-in-place \"⏳ Working — N min\" heartbeat). Override under\n # display.platforms.telegram.\n\n # Auto-cleanup of temporary progress bubbles after the final response lands.\n # On platforms that support message deletion (currently Telegram), this\n # removes the tool-progress bubble, \"⏳ Still working...\" notices, and\n # context-pressure status messages once the final reply has been delivered —\n # keeping long-running turns visible live, then tidy afterward. Failed runs\n # leave the bubbles in place as breadcrumbs. Off by default.\n # Per-platform override: display.platforms.telegram.cleanup_progress\n # true: Delete tracked progress/status bubbles on successful turn\n # false: Leave everything in place (default)\n # Example:\n # display:\n # platforms:\n # telegram:\n # cleanup_progress: true\n cleanup_progress: false\n\n # Gateway-only natural mid-turn assistant updates.\n # When true, completed assistant status messages are sent as separate chat\n # messages. This is independent of tool_progress and gateway streaming.\n # true: Send mid-turn assistant updates as separate messages (default)\n # false: Only send the final response\n interim_assistant_messages: true\n\n # Gateway-only long-running status heartbeats.\n # When false, the platform does not receive periodic \"⏳ Working — N min\"\n # notifications even if agent.gateway_notify_interval is non-zero. The\n # heartbeat edits a single message in place (where the adapter supports\n # editing) instead of posting a new bubble each interval.\n # Default: true everywhere, including Telegram (silent agents are worse\n # than a single edit-in-place heartbeat).\n long_running_notifications: true\n\n # Include detailed iteration/tool/status context in busy acknowledgments\n # and long-running heartbeats. When true, busy acks show \"iteration 21/60,\n # terminal, 10 min\" and the heartbeat shows \"⏳ Working — 12 min,\n # iteration 21/60, terminal\". When false (Telegram default), both stay\n # terse: \"Interrupting current task\" and \"⏳ Working — 12 min, terminal\".\n busy_ack_detail: true\n\n # What Enter does when Hermes is already busy (CLI and gateway platforms).\n # interrupt: Interrupt the current run and redirect Hermes (default)\n # queue: Queue your message for the next turn\n # steer: Inject your message mid-run via /steer, arriving at the agent\n # after the next tool call — no interrupt, no role violation.\n # Falls back to 'queue' if the agent isn't running yet or if\n # images are attached (steer only carries text).\n # Ctrl+C (or /stop in gateway) always interrupts regardless of this setting.\n # Toggle at runtime with /busy .\n busy_input_mode: interrupt\n\n # Background process notifications (gateway/messaging only).\n # Controls how chatty the process watcher is when you use\n # terminal(background=true, notify_on_complete=true) from Telegram/Discord/etc.\n # off: No watcher messages at all\n # result: Only the final completion message\n # error: Only the final message when exit code != 0\n # all: Running output updates + final message (default)\n background_process_notifications: all\n\n\n # Play terminal bell when agent finishes a response.\n # Useful for long-running tasks — your terminal will ding when the agent is done.\n # Works over SSH. Most terminals can be configured to flash the taskbar or play a sound.\n # true: Ring the terminal bell on each response\n # false: Silent (default)\n bell_on_complete: false\n\n # Show model reasoning/thinking before each response.\n # When enabled, a dim box shows the model's thought process above the response.\n # Toggle at runtime with /reasoning show or /reasoning hide.\n # true: Show the reasoning box\n # false: Hide reasoning (default)\n show_reasoning: false\n\n # Stream tokens to the terminal as they arrive instead of waiting for the\n # full response. The response box opens on first token and text appears\n # line-by-line. Tool calls are still captured silently.\n # true: Stream tokens as they arrive (default)\n # false: Wait for the full response before rendering\n streaming: true\n\n # Show [HH:MM] timestamps on user input and assistant response labels.\n # timestamps: false\n\n # ───────────────────────────────────────────────────────────────────────────\n # Skin / Theme\n # ───────────────────────────────────────────────────────────────────────────\n # Customize CLI visual appearance — banner colors, spinner faces, tool prefix,\n # response box label, and branding text. Change at runtime with /skin .\n #\n # Built-in skins:\n # default — Classic Hermes gold/kawaii\n # ares — Crimson/bronze war-god theme with spinner wings\n # mono — Clean grayscale monochrome\n # slate — Cool blue developer-focused\n # daylight — Bright light-mode theme\n # warm-lightmode — Warm paper-tone light-mode theme\n # poseidon — Sea-green/teal Olympian theme\n # sisyphus — Earthy stone-and-moss theme\n # charizard — Fiery orange dragon theme\n #\n # Custom skins: drop a YAML file in ~/.hermes/skins/.yaml\n # Schema (all fields optional, missing values inherit from default):\n #\n # name: my-theme\n # description: Short description\n # colors:\n # banner_border: \"#HEX\" # Panel border\n # banner_title: \"#HEX\" # Panel title\n # banner_accent: \"#HEX\" # Section headers (Available Tools, etc.)\n # banner_dim: \"#HEX\" # Dim/muted text\n # banner_text: \"#HEX\" # Body text (tool names, skill names)\n # ui_accent: \"#HEX\" # UI accent color\n # response_border: \"#HEX\" # Response box border color\n # spinner:\n # waiting_faces: [\"(⚔)\", \"(⛨)\"] # Faces shown while waiting\n # thinking_faces: [\"(⚔)\", \"(⌁)\"] # Faces shown while thinking\n # thinking_verbs: [\"forging\", \"plotting\"] # Verbs for spinner messages\n # wings: # Optional left/right spinner decorations\n # - [\"⟪⚔\", \"⚔⟫\"]\n # - [\"⟪▲\", \"▲⟫\"]\n # branding:\n # agent_name: \"My Agent\" # Banner title and branding\n # welcome: \"Welcome message\" # Shown at CLI startup\n # response_label: \" ⚔ Agent \" # Response box header label\n # prompt_symbol: \"⚔\" # Prompt symbol (bare token; renderers add trailing space)\n # tool_prefix: \"╎\" # Tool output line prefix (default: ┊)\n #\n skin: default\n\n# =============================================================================\n# Model Aliases — short names for /model command\n# =============================================================================\n# Map short aliases to exact (model, provider, base_url) tuples.\n# Used by /model tab completion and resolve_alias().\n# Aliases are checked BEFORE the models.dev catalog, so they can route\n# to endpoints not in the catalog (e.g. Ollama Cloud, local servers).\n#\n# model_aliases:\n# opus:\n# model: claude-opus-4-6\n# provider: anthropic\n# qwen:\n# model: \"qwen3.5:397b\"\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n# glm:\n# model: glm-4.7\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n\n# =============================================================================\n# Privacy\n# =============================================================================\n# privacy:\n# # Redact PII from the LLM context prompt.\n# # When true, phone numbers are stripped and user/chat IDs are replaced\n# # with deterministic hashes before being sent to the model.\n# # Names and usernames are NOT affected (user-chosen, publicly visible).\n# # Routing/delivery still uses the original values internally.\n# redact_pii: false\n\n# =============================================================================\n# Shell-script hooks\n# =============================================================================\n# Register shell scripts as plugin-hook callbacks. Each entry is executed as\n# a subprocess (shell=False, shlex.split) with a JSON payload on stdin. On\n# stdout the script may return JSON that either blocks the tool call or\n# injects context into the next LLM call.\n#\n# Valid events (mirror hermes_cli.plugins.VALID_HOOKS):\n# pre_tool_call, post_tool_call, pre_llm_call, post_llm_call,\n# pre_api_request, post_api_request, on_session_start, on_session_end,\n# on_session_finalize, on_session_reset, subagent_stop\n#\n# First-use consent: each (event, command) pair prompts once on a TTY, then\n# is persisted to ~/.hermes/shell-hooks-allowlist.json. Non-interactive\n# runs (gateway, cron) need --accept-hooks, HERMES_ACCEPT_HOOKS=1, or the\n# hooks_auto_accept key below.\n#\n# See website/docs/user-guide/features/hooks.md for the full JSON wire\n# protocol and worked examples.\n#\n# hooks:\n# pre_tool_call:\n# - matcher: \"terminal\"\n# command: \"~/.hermes/agent-hooks/block-rm-rf.sh\"\n# timeout: 10\n# post_tool_call:\n# - matcher: \"write_file|patch\"\n# command: \"~/.hermes/agent-hooks/auto-format.sh\"\n# pre_llm_call:\n# - command: \"~/.hermes/agent-hooks/inject-cwd-context.sh\"\n# subagent_stop:\n# - command: \"~/.hermes/agent-hooks/log-orchestration.sh\"\n#\n# hooks_auto_accept: false\n\n\n# =============================================================================\n# Update Behavior\n# =============================================================================\nupdates:\n # Create a full HERMES_HOME zip before every `hermes update`.\n # Backups land in ~/.hermes/backups/ and can be restored with `hermes import`.\n # Off by default because large homes can add minutes to every update.\n pre_update_backup: false\n\n # Number of pre-update backup zips to retain.\n backup_keep: 5\n\n # What non-interactive updates do with local source edits in the Hermes repo.\n # Interactive terminal updates always prompt before restoring the autostash.\n #\n # stash - auto-stash before pull, then auto-restore after success (default)\n # discard - drop the update-created stash after success; use only on managed\n # installs where local source edits should not persist\n non_interactive_local_changes: \"stash\"\n\n\n# =============================================================================\n# Web Dashboard\n# =============================================================================\n# OAuth gate configuration for `hermes dashboard --host `.\n# The bundled Nous Portal plugin reads these on startup; settings here are\n# the canonical surface. Each can be overridden by an environment variable:\n#\n# dashboard.oauth.client_id <- HERMES_DASHBOARD_OAUTH_CLIENT_ID\n# dashboard.oauth.portal_url <- HERMES_DASHBOARD_PORTAL_URL\n# dashboard.public_url <- HERMES_DASHBOARD_PUBLIC_URL\n#\n# Env wins when set to a non-empty value. This is what Fly.io's platform-\n# secret injection uses to push per-deploy client_ids without needing to\n# bake a config.yaml into the image. Empty env values are treated as unset\n# so a provisioned-but-not-populated secret can't shadow a valid entry here.\n#\n# Local dev / on-prem deploys should typically set these via config.yaml\n# (the ~/.hermes/.env file is reserved for API keys and secrets).\n#\n# dashboard:\n# oauth:\n# client_id: \"\" # agent:{instance_id}; Portal provisions this at deploy\n# portal_url: \"\" # blank → default https://portal.nousresearch.com\n#\n# # Force the absolute base URL the OAuth callback (and any other public\n# # URL the dashboard hands to external systems) is built from. Set this\n# # for deploys behind reverse proxies that don't reliably forward\n# # X-Forwarded-Host / X-Forwarded-Proto / X-Forwarded-Prefix (manual\n# # nginx setups, on-prem ingresses, custom-domain Fly deploys without\n# # full proxy header chains).\n# #\n# # When set, the value is the complete authority: scheme + host +\n# # optional path prefix (e.g. \"https://example.com/hermes\"). The OAuth\n# # callback URL becomes \"/auth/callback\" — X-Forwarded-Prefix\n# # is IGNORED on this code path because the operator has explicitly\n# # declared the public URL and we no longer need to guess.\n# #\n# # Leave empty to use the existing proxy-header reconstruction (the\n# # default — works on Fly.io out of the box).\n# #\n# # public_url: \"https://example.com/hermes\"\n"}, "87b65e24a799ded350c31afcdb7077cf45bf5c13": {"t": 1785767877.037398, "n": 73891, "h": "ec4d9e33da1340ec", "c": "# Hermes Agent CLI Configuration\n# Copy this file to cli-config.yaml and customize as needed.\n# This file configures the CLI behavior. Environment variables in .env take precedence.\n\n# =============================================================================\n# Model Configuration\n# =============================================================================\nmodel:\n # Default model to use (can be overridden with --model flag)\n # Both \"default\" and \"model\" work as the key name here.\n default: \"anthropic/claude-opus-4.6\"\n \n # Inference provider selection:\n # \"auto\" - Auto-detect from credentials (default)\n # \"openrouter\" - OpenRouter (requires: OPENROUTER_API_KEY or OPENAI_API_KEY)\n # \"nous\" - Nous Portal OAuth (requires: hermes auth add nous)\n # \"nous-api\" - Nous Portal API key (requires: NOUS_API_KEY)\n # \"anthropic\" - Direct Anthropic API (requires: ANTHROPIC_API_KEY)\n # \"openai-codex\" - OpenAI Codex (requires: hermes auth)\n # \"copilot\" - GitHub Copilot / GitHub Models (requires: GITHUB_TOKEN)\n # \"gemini\" - Use Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n # \"zai\" - Use z.ai / ZhipuAI GLM models (requires: GLM_API_KEY)\n # \"kimi-coding\" - Kimi / Moonshot AI (requires: KIMI_API_KEY)\n # \"minimax\" - MiniMax global (requires: MINIMAX_API_KEY)\n # \"minimax-cn\" - MiniMax China (requires: MINIMAX_CN_API_KEY)\n # \"huggingface\" - Hugging Face Inference (requires: HF_TOKEN)\n # \"nvidia\" - NVIDIA NIM / build.nvidia.com (requires: NVIDIA_API_KEY)\n # \"xiaomi\" - Xiaomi MiMo (requires: XIAOMI_API_KEY)\n # \"arcee\" - Arcee AI Trinity models (requires: ARCEEAI_API_KEY)\n # \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY — https://ollama.com/settings)\n # \"kilocode\" - KiloCode gateway (requires: KILOCODE_API_KEY)\n # \"azure-foundry\" - Microsoft Foundry / Azure OpenAI (API key or Entra ID)\n # \"lmstudio\" - LM Studio local server (optional: LM_API_KEY, defaults to http://127.0.0.1:1234/v1)\n #\n # Local servers (LM Studio, Ollama, vLLM, llama.cpp):\n # \"custom\" - Any other OpenAI-compatible endpoint. Set base_url below.\n # Aliases: \"ollama\", \"vllm\", \"llamacpp\" all map to \"custom\".\n # LM Studio is first-class and uses provider: \"lmstudio\".\n # It works with both no-auth and auth-enabled server modes.\n #\n # Can also be overridden for a single invocation with the --provider flag.\n provider: \"auto\"\n \n # API configuration (falls back to OPENROUTER_API_KEY env var)\n # api_key: \"your-key-here\" # Uncomment to set here instead of .env\n base_url: \"https://openrouter.ai/api/v1\"\n\n # Azure Foundry keyless auth example:\n # provider: \"azure-foundry\"\n # base_url: \"https://.openai.azure.com/openai/v1\"\n # auth_mode: \"entra_id\" # DefaultAzureCredential: az login, managed identity, workload identity, etc.\n # default: \"gpt-4o\" # Deployment/model name\n # entra:\n # scope: \"https://ai.azure.com/.default\" # Optional; this is the default.\n\n # ── Token limits — two settings, easy to confuse ──────────────────────────\n #\n # context_length: TOTAL context window (input + output tokens combined).\n # Controls when Hermes compresses history and validates requests.\n # Leave unset — Hermes auto-detects the correct value from the provider.\n # Set manually only when auto-detection is wrong (e.g. a local server with\n # a custom num_ctx, or a proxy that doesn't expose /v1/models).\n #\n # context_length: 131072\n #\n # max_tokens: OUTPUT cap — maximum tokens the model may generate per response.\n # Unrelated to how long your conversation history can be.\n # The OpenAI-standard name \"max_tokens\" is a misnomer; Anthropic's native\n # API has since renamed it \"max_output_tokens\" for clarity.\n # Leave unset to use the model's native output ceiling (recommended).\n # Set only if you want to deliberately limit individual response length.\n #\n# max_tokens: 8192\n\n # ── Custom request headers (optional) ─────────────────────────────────────\n #\n # default_headers: extra HTTP headers sent on every request to an\n # OpenAI-compatible endpoint. User values take precedence over the\n # provider/SDK defaults, so this is the supported way to override the\n # OpenAI Python SDK's identifying headers (User-Agent: OpenAI/Python ...,\n # X-Stainless-*) when a custom provider sits behind a gateway/WAF that\n # rejects them — e.g. an upstream that returns \"502 Upstream access\n # forbidden\" for the SDK default User-Agent but accepts a plain one.\n # Applies on the OpenAI wire only (not native Anthropic / Bedrock).\n #\n # default_headers:\n # User-Agent: \"curl/8.7.1\"\n #\n # extra_headers: accepted as an alias of default_headers (merged, with\n # extra_headers winning when both are set) — matches the per-provider\n # extra_headers key below.\n #\n # Per-provider variant: named providers / custom_providers entries accept an\n # extra_headers dict scoped to that endpoint only — for reverse proxies,\n # gateways, or custom auth (e.g. Cloudflare Access service tokens).\n # Merged onto SDK/provider defaults with the entry's values winning.\n # Header values are treated as secrets and are never logged.\n #\n # providers:\n # my-proxy:\n # base_url: \"https://llm.internal.example.com/v1\"\n # key_env: \"MY_PROXY_API_KEY\"\n # extra_headers:\n # CF-Access-Client-Id: \"xxxx.access\"\n # CF-Access-Client-Secret: \"${CF_ACCESS_SECRET}\"\n # X-Client-Name: \"hermes-agent\"\n\n# Named provider overrides (optional)\n# Use this for per-provider request timeouts, non-stream stale timeouts,\n# and per-model exceptions.\n# Applies to the primary turn client on every api_mode (OpenAI-wire, native\n# Anthropic, and Anthropic-compatible providers), the fallback chain, and\n# client rebuilds during credential rotation. For OpenAI-wire chat\n# completions (streaming and non-streaming) the configured value is also\n# used as the per-request ``timeout=`` kwarg so it wins over the legacy\n# HERMES_API_TIMEOUT env var (which still applies when no config is set).\n# ``stale_timeout_seconds`` controls the non-streaming stale-call detector and\n# wins over the legacy HERMES_API_CALL_STALE_TIMEOUT env var. Leaving these\n# unset keeps the legacy defaults (HERMES_API_TIMEOUT=1800s,\n# HERMES_API_CALL_STALE_TIMEOUT=90s, native Anthropic 900s). The\n# implicit non-stream stale detector is auto-disabled for local endpoints\n# and can scale upward for very large contexts.\n#\n# Not currently wired for AWS Bedrock (bedrock_converse + AnthropicBedrock\n# SDK paths) — those use boto3 with its own timeout configuration.\n#\n# providers:\n# ollama-local:\n# request_timeout_seconds: 300 # Longer timeout for local cold-starts\n# stale_timeout_seconds: 900 # Explicitly re-enable stale detection on local endpoints\n# anthropic:\n# request_timeout_seconds: 30 # Fast-fail cloud requests\n# models:\n# claude-opus-4.6:\n# timeout_seconds: 600 # Longer timeout for extended-thinking Opus calls\n# openai-codex:\n# models:\n# gpt-5.4:\n# stale_timeout_seconds: 1800 # Longer non-stream stale timeout for slow large-context turns\n\n# =============================================================================\n# OpenRouter Provider Routing (only applies when using OpenRouter)\n# =============================================================================\n# Control how requests are routed across providers on OpenRouter.\n# See: https://openrouter.ai/docs/guides/routing/provider-selection\n#\n# provider_routing:\n# # Sort strategy: \"price\" (default), \"throughput\", or \"latency\"\n# # Append :nitro to model name for a shortcut to throughput sorting.\n# sort: \"throughput\"\n#\n# # Only allow these providers (provider slugs from OpenRouter)\n# # only: [\"anthropic\", \"google\"]\n#\n# # Skip these providers entirely\n# # ignore: [\"deepinfra\", \"fireworks\"]\n#\n# # Try providers in this order (overrides default load balancing)\n# # order: [\"anthropic\", \"google\", \"together\"]\n#\n# # Require providers to support all parameters in your request\n# # require_parameters: true\n#\n# # Data policy: \"allow\" (default) or \"deny\" to exclude providers that may store data\n# # data_collection: \"deny\"\n\n# =============================================================================\n# OpenRouter Response Caching (only applies when using OpenRouter)\n# =============================================================================\n# Cache identical API responses at the OpenRouter edge for free instant replays.\n# When enabled, identical requests (same model, messages, parameters) return\n# cached responses with zero billing. Separate from Anthropic prompt caching.\n# See: https://openrouter.ai/docs/guides/features/response-caching\n#\n# openrouter:\n# response_cache: true # Enable response caching (default: true)\n# response_cache_ttl: 300 # Cache TTL in seconds, 1-86400 (default: 300)\n\n# =============================================================================\n# Git Worktree Isolation\n# =============================================================================\n# When enabled, each CLI session creates an isolated git worktree so multiple\n# agents can work on the same repo concurrently without file collisions.\n# Equivalent to always passing --worktree / -w on the command line.\n#\n# worktree: true # Always create a worktree when in a git repo\n# worktree: false # Default — only create when -w flag is passed\n#\n# By default a new worktree branches from the freshly-fetched remote tip\n# (the current branch's upstream, else the remote's default branch) so it\n# starts current with the project instead of from the local clone's\n# (possibly stale) HEAD. Set worktree_sync: false to branch from local HEAD\n# instead — useful when offline or when you deliberately want the clone's\n# exact current state as the base.\n#\n# worktree_sync: true # Default — branch from the fetched remote tip\n# worktree_sync: false # Branch from local HEAD (offline / pinned base)\n\n# =============================================================================\n# Terminal Tool Configuration\n# =============================================================================\n# Choose ONE of the following terminal configurations by uncommenting it.\n# The terminal tool executes commands in the specified environment.\n\n# -----------------------------------------------------------------------------\n# OPTION 1: Local execution (default)\n# Commands run directly on your machine in the current directory\n# -----------------------------------------------------------------------------\n# Working directory behavior:\n# - CLI (`hermes` command): Uses \".\" (current directory where you run hermes)\n# - Gateway/messaging/cron: Uses terminal.cwd here; legacy .env cwd values are deprecated\nterminal:\n backend: \"local\"\n cwd: \".\" # For local backend: \".\" = current directory. Ignored for remote backends unless a backend documents otherwise.\n timeout: 180\n # HOME policy for tool subprocesses:\n # auto - default: host uses your real HOME; containers use HERMES_HOME/home\n # real - force your real OS-user HOME\n # profile - force HERMES_HOME/home for strict per-profile CLI config isolation\n home_mode: \"auto\"\n docker_mount_cwd_to_workspace: false # SECURITY: off by default. Opt in to mount the launch cwd into Docker /workspace.\n lifetime_seconds: 300\n # sudo_password: \"hunter2\" # Optional: pipe a sudo password via sudo -S. SECURITY WARNING: plaintext.\n # sudo_password: \"\" # Explicit empty password: try empty and never open the interactive sudo prompt.\n\n# -----------------------------------------------------------------------------\n# OPTION 2: SSH remote execution\n# Commands run on a remote server - agent code stays local (sandboxed)\n# Great for: keeping agent isolated from its own code, using powerful remote hardware\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"ssh\"\n# cwd: \"/home/myuser/project\" # Path on the REMOTE server\n# timeout: 180\n# lifetime_seconds: 300\n# ssh_host: \"my-server.example.com\"\n# ssh_user: \"myuser\"\n# ssh_port: 22\n# ssh_key: \"~/.ssh/id_rsa\" # Optional - uses ssh-agent if not specified\n\n# -----------------------------------------------------------------------------\n# OPTION 3: Docker container\n# Commands run in an isolated Docker container\n# Great for: reproducible environments, testing, isolation\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"docker\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /)\n# timeout: 180\n# lifetime_seconds: 300\n# docker_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# docker_mount_cwd_to_workspace: true # Explicit opt-in: mount your launch cwd into /workspace\n# # Optional: run the container as your host user's uid:gid so files written\n# # into bind-mounted dirs are owned by you, not root. Drops SETUID/SETGID\n# # caps too since no gosu privilege drop is needed. Leave off if your\n# # chosen docker_image expects to start as root.\n# docker_run_as_host_user: true\n# # Optional: explicitly forward selected env vars into Docker.\n# # These values come from your current shell first, then ~/.hermes/.env.\n# # Warning: anything forwarded here is visible to commands run in the container.\n# docker_forward_env:\n# - \"GITHUB_TOKEN\"\n# - \"NPM_TOKEN\"\n# # Optional: extra flags passed verbatim to docker run (appended after security defaults).\n# # Useful for adding capabilities (e.g. apt installs needing SETUID) or custom options.\n# # Example: add a Linux capability not included by default\n# # docker_extra_args:\n# # - \"--cap-add\"\n# # - \"SETUID\"\n\n# -----------------------------------------------------------------------------\n# OPTION 4: Singularity/Apptainer container\n# Commands run in a Singularity container (common in HPC environments)\n# Great for: HPC clusters, shared compute environments\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"singularity\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# singularity_image: \"docker://nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 5: Modal cloud execution\n# Commands run on Modal's cloud infrastructure\n# Great for: GPU access, scalable compute, serverless execution\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"modal\"\n# cwd: \"/workspace\" # Path INSIDE the sandbox (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# modal_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 6: Daytona cloud execution\n# Commands run in Daytona cloud sandboxes\n# Great for: Cloud dev environments, persistent workspaces, team collaboration\n# Requires: pip install daytona, DAYTONA_API_KEY env var\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"daytona\"\n# cwd: \"~\"\n# timeout: 180\n# lifetime_seconds: 300\n# daytona_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# container_disk: 10240 # Daytona max is 10GB per sandbox\n\n#\n# --- Container resource limits (docker, singularity, modal, daytona -- ignored for local/ssh) ---\n# These settings apply to all container backends. They control the resources\n# allocated to the sandbox and whether its filesystem persists across sessions.\n container_cpu: 1 # CPU cores\n container_memory: 5120 # Memory in MB (5120 = 5GB)\n container_disk: 51200 # Disk in MB (51200 = 50GB)\n container_persistent: true # Persist filesystem across sessions (false = ephemeral)\n\n# -----------------------------------------------------------------------------\n# SUDO SUPPORT (works with ALL backends above)\n# -----------------------------------------------------------------------------\n# Add sudo_password to any terminal config above to enable sudo commands.\n# The password is piped via `sudo -S`. Works with local, ssh, docker, etc.\n#\n# SECURITY WARNING: Password stored in plaintext!\n#\n# INTERACTIVE PROMPT: If sudo_password is unset and the CLI is running,\n# you'll be prompted to enter your password when sudo is needed:\n# - 45-second timeout (auto-skips if no input)\n# - Press Enter to skip (command fails gracefully)\n# - Password is hidden while typing\n# - Password is cached for the session\n#\n# EMPTY PASSWORDS: Setting sudo_password to an explicit empty string is different\n# from leaving it unset. Hermes will try an empty password via `sudo -S` and\n# will not open the interactive prompt. This is useful for passwordless sudo,\n# Touch ID sudo setups, and environments where prompting is just noise.\n#\n# ALTERNATIVES:\n# - SSH backend: Configure passwordless sudo on the remote server\n# - Containers: Run as root inside the container (no sudo needed)\n# - Local: Configure /etc/sudoers for specific commands\n#\n# Example (add to your terminal section):\n# sudo_password: \"your-password-here\"\n\n# =============================================================================\n# Security Scanning (tirith)\n# =============================================================================\n# Optional pre-exec command security scanning via tirith.\n# Detects homograph URLs, pipe-to-shell, terminal injection, env manipulation.\n# Install: brew install sheeki03/tap/tirith\n# Docs: https://github.com/sheeki03/tirith\n#\n# security:\n# tirith_enabled: true # Enable/disable tirith scanning\n# tirith_path: \"tirith\" # Path to tirith binary (supports ~ expansion)\n# tirith_timeout: 5 # Scan timeout in seconds\n# tirith_fail_open: true # Allow commands if tirith unavailable\n\n# =============================================================================\n# Browser Tool Configuration\n# =============================================================================\nbrowser:\n # Inactivity timeout in seconds - browser sessions are automatically closed\n # after this period of no activity between agent loops (default: 120 = 2 minutes)\n inactivity_timeout: 120\n\n# =============================================================================\n# Tool Loop Guardrails\n# =============================================================================\n# Soft warnings are enabled by default. They append guidance to repeated failed\n# or non-progressing tool results but still let the tool execute. Hard stops are\n# opt-in circuit breakers for autonomous/cron sessions where stopping a loop is\n# preferable to spending the full iteration budget.\ntool_loop_guardrails:\n warnings_enabled: true\n hard_stop_enabled: false\n warn_after:\n exact_failure: 2\n same_tool_failure: 3\n idempotent_no_progress: 2\n hard_stop_after:\n exact_failure: 5\n same_tool_failure: 8\n idempotent_no_progress: 5\n\n# =============================================================================\n# Context Compression (Auto-shrinks long conversations)\n# =============================================================================\n# When conversation approaches model's context limit, middle turns are\n# automatically summarized to free up space while preserving important context.\n#\n# HOW IT WORKS:\n# 1. Tracks actual token usage from API responses (not estimates)\n# 2. When prompt_tokens >= threshold% of model's context_length, triggers compression\n# 3. Protects first 3 turns (system prompt, initial request, first response)\n# 4. Protects last N turns (default 20 messages = ~10 full turns of recent context)\n# 5. Summarizes middle turns using a fast/cheap model\n# 6. Inserts summary as a user message, continues conversation seamlessly\n#\n# Post-compression tail budget is target_ratio × threshold × context_length:\n# 200K context, threshold 0.50, ratio 0.20 → 20K tokens of recent tail preserved\n# 1M context, threshold 0.50, ratio 0.20 → 100K tokens of recent tail preserved\n#\ncompression:\n # Enable automatic context compression (default: true)\n # Set to false if you prefer to manage context manually or want errors on overflow\n enabled: true\n \n # Trigger compression at this % of model's context limit (default: 0.50 = 50%)\n # Lower values = more aggressive compression, higher values = compress later\n threshold: 0.50\n\n # Existing Codex gpt-5.5 behavior: raise Hermes' compaction trigger to 85%\n # for the ChatGPT Codex OAuth route. Set false to opt back down to threshold.\n codex_gpt55_autoraise: true\n\n # Fraction of the threshold to preserve as recent tail (default: 0.20 = 20%)\n # e.g. 20% of 50% threshold = 10% of total context kept as recent messages.\n # Summary output is separately capped at 12K tokens (Gemini output limit).\n # Range: 0.10 - 0.80\n target_ratio: 0.20\n\n # Number of most-recent messages to always preserve (default: 20 ≈ 10 full turns)\n # Higher values keep more recent conversation intact at the cost of more aggressive\n # compression of older turns.\n protect_last_n: 20\n\n # Codex app-server (codex CLI runtime) thread-compaction mode. The codex\n # agent owns the real thread context on this runtime, so Hermes' summarizer\n # cannot shrink it — compaction goes through the app server instead.\n # native = let Codex decide when to compact its own thread (default)\n # hermes = let Hermes threshold trigger Codex thread/compact/start\n # off = Hermes will not auto-trigger compaction; Codex may still compact natively\n codex_app_server_auto: native\n\n # Number of non-system messages to protect at the head of the transcript, in\n # ADDITION to the system prompt (which is always implicitly protected).\n # Head messages are NEVER summarized — they survive every compression\n # indefinitely. This gives stable early context for short/medium sessions,\n # but in long-running sessions that rely on rolling compaction the pinned\n # opening turns may not match how you want the session framed over time.\n # Set to 0 to preserve ONLY the system prompt (plus the rolling summary\n # and recent tail) — the cleanest configuration for long-running sessions.\n # Default 3 preserves the system prompt plus the first three non-system\n # head messages, matching the pre-feature behaviour.\n protect_first_n: 3\n\n # To pin a specific model/provider for compression summaries, use the\n # auxiliary section below (auxiliary.compression.provider / model).\n\n# =============================================================================\n# Anthropic prompt caching TTL\n# =============================================================================\n# When prompt caching is active (Claude via OpenRouter or native Anthropic),\n# Anthropic supports two TTL tiers for cached prefixes: \"5m\" (default) and\n# \"1h\". Other values are ignored and \"5m\" is used.\n#\nprompt_caching:\n cache_ttl: \"5m\" # use \"1h\" for long sessions with pauses between turns\n\n# =============================================================================\n# Auxiliary Models (Advanced — Experimental)\n# =============================================================================\n# Hermes uses lightweight \"auxiliary\" models for side tasks: image analysis,\n# browser screenshot analysis, web page summarization, TTS audio-tag insertion,\n# and context compression.\n#\n# By default these use Gemini Flash via OpenRouter or Nous Portal and are\n# auto-detected from your credentials. You do NOT need to change anything\n# here for normal usage.\n#\n# WARNING: Overriding these with providers other than OpenRouter or Nous Portal\n# is EXPERIMENTAL and may not work. Not all models/providers support vision,\n# produce usable summaries, or accept the same API format. Change at your own\n# risk — if things break, reset to \"auto\" / empty values.\n#\n# Each task has its own provider + model pair so you can mix providers.\n# For example: OpenRouter for vision (needs multimodal), but your main\n# local endpoint for compression (just needs text).\n#\n# Provider options:\n# \"auto\" - Best available: OpenRouter → Nous Portal → main endpoint (default)\n# \"openrouter\" - Force OpenRouter (requires OPENROUTER_API_KEY)\n# \"nous\" - Force Nous Portal (requires: hermes auth add nous)\n# \"gemini\" - Force Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n# \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY)\n# \"codex\" - Force Codex OAuth (requires: hermes model → Codex).\n# Uses gpt-5.3-codex which supports vision.\n# \"main\" - Use your custom endpoint (OPENAI_BASE_URL + OPENAI_API_KEY).\n# Works with OpenAI API, local models, or any OpenAI-compatible\n# endpoint. Also falls back to Codex OAuth and API-key providers.\n#\n# Model: leave empty to use the provider's default. When empty, OpenRouter\n# uses \"google/gemini-3-flash-preview\" and Nous uses \"gemini-3-flash\".\n# Other providers pick a sensible default automatically.\n#\n# auxiliary:\n# # Image analysis: vision_analyze tool + browser screenshots\n# vision:\n# provider: \"auto\"\n# model: \"\" # e.g. \"google/gemini-2.5-flash\", \"openai/gpt-4o\"\n# timeout: 30 # LLM API call timeout (seconds)\n# download_timeout: 30 # Image HTTP download timeout (seconds)\n# # Increase for slow connections or self-hosted image servers\n#\n# # Web page scraping / summarization + browser page text extraction\n# web_extract:\n# provider: \"auto\"\n# model: \"\"\n#\n# # Gemini 3.1 TTS hidden audio-tag insertion\n# tts_audio_tags:\n# provider: \"auto\" # empty model = your main chat model\n# model: \"\"\n# timeout: 30\n#\n# # Session search — summarizes matching past sessions\n# session_search:\n# provider: \"auto\"\n# model: \"\"\n# timeout: 30\n# max_concurrency: 3 # Limit parallel summaries to reduce request-burst 429s\n# extra_body: {} # Provider-specific OpenAI-compatible request fields\n# # Example for providers that support request-body\n# # reasoning controls:\n# # extra_body:\n# # enable_thinking: false\n# # Some vLLM/Qwen deployments expect this nested:\n# # extra_body:\n# # chat_template_kwargs:\n# # enable_thinking: false\n\n# =============================================================================\n# Persistent Memory\n# =============================================================================\n# Bounded curated memory injected into the system prompt every session.\n# Two stores: MEMORY.md (agent's notes) and USER.md (user profile).\n# Character limits keep the memory small and focused. The agent manages\n# pruning -- when at the limit, it must consolidate or replace entries.\n# Disabled by default in batch_runner.\n#\nmemory:\n # Agent's personal notes: environment facts, conventions, things learned\n memory_enabled: true\n \n # User profile: preferences, communication style, expectations\n user_profile_enabled: true\n \n # Character limits (~2.75 chars per token, model-independent)\n memory_char_limit: 2200 # ~800 tokens\n user_char_limit: 1375 # ~500 tokens\n\n # Periodic memory nudge: remind the agent to consider saving memories\n # every N user turns. Set to 0 to disable. Only active when memory is enabled.\n nudge_interval: 10 # Nudge every 10 user turns (0 = disabled)\n\n # Memory flush: give the agent one turn to save memories before context is\n # lost (compression, /new, /reset, exit). Set to 0 to disable.\n # For exit/reset, only fires if the session had at least this many user turns.\n flush_min_turns: 6 # Min user turns to trigger flush on exit/reset (0 = disabled)\n\n# =============================================================================\n# Session Reset Policy (Messaging Platforms)\n# =============================================================================\n# Controls when messaging sessions (Telegram, Discord, WhatsApp, Slack) are\n# automatically cleared. Without resets, conversation context grows indefinitely\n# which increases API costs with every message.\n#\n# When a reset triggers, the agent first saves important information to its\n# persistent memory — but the conversation context is wiped. The agent starts\n# fresh but retains learned facts via its memory system.\n#\n# Users can always manually reset with /reset or /new in chat.\n#\n# Modes:\n# \"both\" - Reset on EITHER inactivity timeout or daily boundary (recommended)\n# \"idle\" - Reset only after N minutes of inactivity\n# \"daily\" - Reset only at a fixed hour each day\n# \"none\" - Never auto-reset; context lives until /reset or compression kicks in\n#\n# When a reset triggers, the agent gets one turn to save important memories and\n# skills before the context is wiped. Persistent memory carries across sessions.\n#\nsession_reset:\n mode: both # \"both\", \"idle\", \"daily\", or \"none\"\n idle_minutes: 1440 # Inactivity timeout in minutes (default: 1440 = 24 hours)\n at_hour: 4 # Daily reset hour, 0-23 local time (default: 4 AM)\n\n# Maximum number of simultaneously active chat sessions across CLI, TUI,\n# dashboard chat, and messaging gateway. Set to null, 0, or omit to allow\n# unlimited concurrent sessions. When the limit is reached, new sessions get a\n# clean error while existing active sessions keep their normal behavior. This\n# top-level key takes precedence over gateway.max_concurrent_sessions. The cap\n# is a best-effort single-host/profile runtime guard; Hermes fails open if the\n# local runtime lease registry cannot be read or locked.\nmax_concurrent_sessions: null\n\n# When true, group/channel chats use one session per participant when the platform\n# provides a user ID. This is the secure default and prevents users in the same\n# room from sharing context, interrupts, and token costs. Set false only if you\n# explicitly want one shared \"room brain\" per group/channel.\ngroup_sessions_per_user: true\n\n# ─────────────────────────────────────────────────────────────────────────────\n# API Server — per-client model routing\n# ─────────────────────────────────────────────────────────────────────────────\n# Route different API clients to different models/providers on a single\n# Hermes deployment. Clients choose a backend by sending a specific string\n# as the OpenAI ``model`` field. Unmapped model values fall back to the\n# global model configured in the ``model:`` section above, and an explicit\n# session /model override always wins over a route.\n#\n# Configure via the ``platforms.api_server.extra.model_routes`` gateway\n# config block:\n#\n# platforms:\n# api_server:\n# enabled: true\n# extra:\n# key: \"your-api-server-secret\"\n# model_routes:\n# # Xiaozhi clients send model=\"minimax-m2\" → routed to MiniMax via OpenRouter\n# minimax-m2:\n# model: \"minimax/minimax-m1\"\n# provider: \"openrouter\" # optional — overrides global provider\n# # api_key: \"sk-...\" # optional — per-route UPSTREAM provider\n# # key (NOT caller auth; never logged)\n# # base_url: \"https://...\" # optional — per-route base URL\n# # GPT clients keep their own alias\n# gpt-5:\n# model: \"openai/gpt-5\"\n# provider: \"openrouter\"\n#\n# Configured aliases are automatically listed by GET /v1/models so clients\n# can discover them without manual coordination. Caller authentication is\n# unchanged: every request still authenticates with the global API server\n# key (``extra.key`` / API_SERVER_KEY).\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Gateway Streaming\n# ─────────────────────────────────────────────────────────────────────────────\n# Stream tokens to messaging platforms in real-time. The bot sends a message\n# on first token, then progressively edits it as more tokens arrive.\n# Disabled by default — enable to try the streaming UX on Telegram/Discord/Slack.\n# For Telegram, partial edits are sent as plain text and only the final edit uses MarkdownV2.\nstreaming:\n enabled: false\n # transport: edit # \"edit\" = progressive editMessageText\n # edit_interval: 0.3 # seconds between message edits\n # buffer_threshold: 40 # chars before forcing an edit flush\n # cursor: \" ▉\" # cursor shown during streaming\n\n# =============================================================================\n# Skills Configuration\n# =============================================================================\n# Skills are reusable procedures the agent can load and follow. The agent can\n# also create new skills after completing complex tasks.\n#\nskills:\n # Nudge the agent to create skills after complex tasks.\n # Every N tool-calling iterations, remind the model to consider saving a skill.\n # Set to 0 to disable.\n creation_nudge_interval: 15\n\n # External skill directories — share skills across tools/agents without\n # copying them into ~/.hermes/skills/. Each path is expanded (~ and ${VAR})\n # and resolved to an absolute path. External dirs are read-only: skill\n # creation always writes to ~/.hermes/skills/. Local skills take precedence\n # when names collide.\n # external_dirs:\n # - ~/.agents/skills\n # - /home/shared/team-skills\n\n# =============================================================================\n# Agent Behavior\n# =============================================================================\nagent:\n # Maximum tool-calling iterations per conversation\n # Higher = more room for complex tasks, but costs more tokens\n # Recommended: 20-30 for focused tasks, 50-100 for open exploration\n max_turns: 60\n\n # Inactivity timeout for gateway agent runs (seconds, 0 = unlimited).\n # The agent can run indefinitely when actively calling tools or receiving\n # API responses. Only fires after the agent has been idle for this duration.\n # gateway_timeout: 1800\n\n # Staged warning: send a warning before escalating to full timeout.\n # Fires once per run when inactivity reaches this threshold (seconds).\n # Set to 0 to disable the warning.\n # gateway_timeout_warning: 900\n\n # Graceful drain timeout for gateway stop/restart (seconds).\n # Default 0 = no drain: a restart interrupts in-flight agents immediately,\n # cleans up, and exits. Set a positive value only if you want a grace\n # window on /restart, and keep it well under systemd's TimeoutStopSec.\n # restart_drain_timeout: 0\n\n # Max app-level retry attempts for API errors (connection drops, provider\n # timeouts, 5xx, etc.) before the agent surfaces the failure. Lower this\n # to 1 if you use fallback providers and want fast failover on flaky\n # primaries (default 3). The OpenAI SDK does its own low-level retries\n # underneath this wrapper — this is the Hermes-level loop.\n # api_max_retries: 3\n\n # After the agent edits code without fresh passing verification, nudge it to\n # verify before finishing. The default \"auto\" enables it on interactive\n # coding surfaces (CLI, TUI, desktop) and programmatic callers, and disables\n # it on conversational messaging surfaces (Telegram, Discord, etc.) where the\n # verification summary would reach a human as chat noise. Set true or false to\n # force it on or off; the HERMES_VERIFY_ON_STOP env var (1/0) takes precedence.\n # verify_on_stop: auto\n\n # Standing operator instructions for the coding posture (when Hermes is in a\n # code workspace). Appended to the coding brief as an extra system block, so\n # you can pin project-wide workflow rules without editing the shipped brief.\n # Accepts a string or a list of strings. Takes effect next session.\n # coding_instructions:\n # - \"For UI work, don't run tsc/lint until I approve the look.\"\n # - \"Clean the diff before you commit and push.\"\n\n # When verify-on-stop finds edited code without fresh verification evidence,\n # append guidance for creative UI work (avoid broad tsc/lint/test before visual\n # approval) and clean-diff expectations. Set false to keep that nudge terse.\n # verify_guidance: true\n\n # A `pre_verify` hook (plugin or shell, see Event Hooks docs) can keep the\n # agent going one more turn to verify/clean before finishing. This caps how\n # many times one turn may be nudged to continue, so a hook can't trap the loop.\n # Default 3.\n # max_verify_nudges: 3\n\n # Enable verbose logging\n verbose: false\n \n # Reasoning effort level (OpenRouter and Nous Portal)\n # Controls how much \"thinking\" the model does before responding.\n # Options: \"xhigh\" (max), \"high\", \"medium\", \"low\", \"minimal\", \"none\" (disable)\n reasoning_effort: \"medium\"\n \n # Predefined personalities (use with /personality command)\n personalities:\n helpful: \"You are a helpful, friendly AI assistant.\"\n concise: \"You are a concise assistant. Keep responses brief and to the point.\"\n technical: \"You are a technical expert. Provide detailed, accurate technical information.\"\n creative: \"You are a creative assistant. Think outside the box and offer innovative solutions.\"\n teacher: \"You are a patient teacher. Explain concepts clearly with examples.\"\n kawaii: \"You are a kawaii assistant! Use cute expressions like (◕‿◕), ★, ♪, and ~! Add sparkles and be super enthusiastic about everything! Every response should feel warm and adorable desu~! ヽ(>∀<☆)ノ\"\n catgirl: \"You are Neko-chan, an anime catgirl AI assistant, nya~! Add 'nya' and cat-like expressions to your speech. Use kaomoji like (=^・ω・^=) and ฅ^•ﻌ•^ฅ. Be playful and curious like a cat, nya~!\"\n pirate: \"Arrr! Ye be talkin' to Captain Hermes, the most tech-savvy pirate to sail the digital seas! Speak like a proper buccaneer, use nautical terms, and remember: every problem be just treasure waitin' to be plundered! Yo ho ho!\"\n shakespeare: \"Hark! Thou speakest with an assistant most versed in the bardic arts. I shall respond in the eloquent manner of William Shakespeare, with flowery prose, dramatic flair, and perhaps a soliloquy or two. What light through yonder terminal breaks?\"\n surfer: \"Duuude! You're chatting with the chillest AI on the web, bro! Everything's gonna be totally rad. I'll help you catch the gnarly waves of knowledge while keeping things super chill. Cowabunga! 🤙\"\n noir: \"The rain hammered against the terminal like regrets on a guilty conscience. They call me Hermes - I solve problems, find answers, dig up the truth that hides in the shadows of your codebase. In this city of silicon and secrets, everyone's got something to hide. What's your story, pal?\"\n uwu: \"hewwo! i'm your fwiendwy assistant uwu~ i wiww twy my best to hewp you! *nuzzles your code* OwO what's this? wet me take a wook! i pwomise to be vewy hewpful >w<\"\n philosopher: \"Greetings, seeker of wisdom. I am an assistant who contemplates the deeper meaning behind every query. Let us examine not just the 'how' but the 'why' of your questions. Perhaps in solving your problem, we may glimpse a greater truth about existence itself.\"\n hype: \"YOOO LET'S GOOOO!!! 🔥🔥🔥 I am SO PUMPED to help you today! Every question is AMAZING and we're gonna CRUSH IT together! This is gonna be LEGENDARY! ARE YOU READY?! LET'S DO THIS! 💪😤🚀\"\n\n# =============================================================================\n# Toolsets\n# =============================================================================\n# Control which tools the agent has access to.\n# Use `hermes tools` to interactively enable/disable tools per platform.\n\n# =============================================================================\n# Platform Toolsets (per-platform tool configuration)\n# =============================================================================\n# Override which toolsets are available on each platform.\n# If a platform isn't listed here, its built-in default is used.\n#\n# You can use EITHER:\n# - A preset like \"hermes-cli\" or \"hermes-telegram\" (curated tool set)\n# - A list of individual toolsets to compose your own (see list below)\n#\n# Supported platform keys: cli, telegram, discord, whatsapp, slack, qqbot, teams, google_chat\n#\n# Examples:\n#\n# # Use presets (same as defaults):\n# platform_toolsets:\n# cli: [hermes-cli]\n# telegram: [hermes-telegram]\n#\n# # Custom: give Telegram only web + terminal + file + planning:\n# platform_toolsets:\n# telegram: [web, terminal, file, todo]\n#\n# # Custom: CLI without browser or image gen:\n# platform_toolsets:\n# cli: [web, terminal, file, skills, todo, tts, cronjob]\n#\n# # Restrictive: Discord gets read-only tools only:\n# platform_toolsets:\n# discord: [web, vision, skills, todo]\n#\n# If not set, defaults are:\n# cli: hermes-cli (everything + cronjob management)\n# telegram: hermes-telegram (terminal, file, web, vision, image, tts, browser, skills, todo, cronjob, messaging)\n# discord: hermes-discord (same as telegram)\n# whatsapp: hermes-whatsapp (same as telegram)\n# slack: hermes-slack (same as telegram)\n# signal: hermes-signal (same as telegram)\n# homeassistant: hermes-homeassistant (same as telegram)\n# qqbot: hermes-qqbot (same as telegram)\n# teams: hermes-teams (same as telegram)\n# google_chat: hermes-google_chat (same as telegram)\n#\nplatform_toolsets:\n cli: [hermes-cli]\n telegram: [hermes-telegram]\n discord: [hermes-discord]\n whatsapp: [hermes-whatsapp]\n slack: [hermes-slack]\n signal: [hermes-signal]\n homeassistant: [hermes-homeassistant]\n qqbot: [hermes-qqbot]\n yuanbao: [hermes-yuanbao]\n teams: [hermes-teams]\n google_chat: [hermes-google_chat]\n\n# =============================================================================\n# Gateway Platform Settings\n# =============================================================================\n# Optional per-platform messaging settings.\n# Platform-specific knobs live under `extra`.\n#\n# platforms:\n# telegram:\n# reply_to_mode: \"first\" # off | first | all\n# # guest_mode lets explicit @mentions from non-allowlisted groups through.\n# # Default false; ordinary messages, replies, and regex wake words stay blocked.\n# guest_mode: false\n# # allowed_chats: [\"-1001234567890\"]\n# extra:\n# disable_link_previews: false # Set true to suppress Telegram URL previews in bot messages\n# rich_messages: false # Bot API 10.1 rich messages (tables/task lists/details/math); default false for copyable legacy MarkdownV2, set true to opt in\n# rich_drafts: false # Experimental rich draft previews during Telegram DM streaming; default false because Telegram Desktop/macOS can visually overlay draft frames\n# command_menu:\n# # Telegram allows up to 100 BotCommands; Hermes defaults to 60 so\n# # all built-in commands plus common skill commands stay visible\n# # while remaining under Telegram's payload-size limit. Clamped 1..100.\n# max_commands: 60\n# # prepend = user priority first, then Hermes defaults\n# # append = Hermes defaults first, then user priority\n# # replace = only the list below defines priority\n# priority_mode: prepend\n# priority:\n# - my_plugin_command\n#\n# Discord-specific settings (config.yaml top-level, not under platforms:):\n#\n# discord:\n# require_mention: true # Require @mention in server channels (default: true)\n# auto_thread: true # Auto-create thread on @mention (default: true)\n# free_response_channels: \"\" # Channel IDs where no mention is needed\n# reactions: true # Show processing reactions (default: true)\n# history_backfill: true # Recover missed channel messages on mention (default: true)\n# history_backfill_limit: 50 # Max messages to scan backwards (default: 50)\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Available toolsets (use these names in platform_toolsets or the toolsets list)\n#\n# Run `hermes chat --list-toolsets` to see all toolsets and their tools.\n# Run `hermes chat --list-tools` to see every individual tool with descriptions.\n# ─────────────────────────────────────────────────────────────────────────────\n#\n# INDIVIDUAL TOOLSETS (compose your own):\n# web - web_search, web_extract\n# search - web_search only (no scraping)\n# terminal - terminal, process\n# file - read_file, write_file, patch, search\n# browser - browser_navigate, browser_snapshot, browser_click, browser_type,\n# browser_scroll, browser_back, browser_press,\n# browser_get_images, browser_vision (requires BROWSERBASE_API_KEY)\n# vision - vision_analyze (requires OPENROUTER_API_KEY)\n# image_gen - image_generate (requires FAL_KEY)\n# skills - skills_list, skill_view\n# skills_hub - skill_hub (search/install/manage from online registries — user-driven only)\n# todo - todo (in-memory task planning, no deps)\n# tts - text_to_speech (Edge TTS free, or ELEVENLABS/OPENAI/MINIMAX/MISTRAL key)\n# cronjob - cronjob (create/list/update/pause/resume/run/remove scheduled tasks)\n#\n# PRESETS (curated bundles):\n# hermes-cli - All of the above except send_message\n# hermes-telegram - terminal, file, web, vision, image_gen, tts, browser,\n# skills, todo, cronjob, send_message\n# hermes-discord - Same as hermes-telegram\n# hermes-whatsapp - Same as hermes-telegram\n# hermes-slack - Same as hermes-telegram\n#\n# COMPOSITE:\n# debugging - terminal + web + file\n# safe - web + vision (no terminal access)\n# all - Everything available\n#\n# web - Web search and content extraction (web_search, web_extract)\n# search - Web search only, no scraping (web_search)\n# terminal - Command execution and process management (terminal, process)\n# file - File operations: read, write, patch, search\n# browser - Full browser automation (navigate, click, type, screenshot, etc.)\n# vision - Image analysis (vision_analyze)\n# image_gen - Image generation with FLUX (image_generate)\n# skills - Load skill documents (skills_list, skill_view)\n# todo - Task planning and tracking for multi-step work\n# memory - Persistent memory across sessions (personal notes + user profile)\n# session_search - Search and recall past conversations (FTS5 + Gemini Flash summarization)\n# tts - Text-to-speech (Edge TTS free, ElevenLabs, OpenAI, MiniMax, Mistral)\n# cronjob - Schedule and manage automated tasks (CLI-only)\n#\n# Composite toolsets:\n# debugging - terminal + web + file (for troubleshooting)\n# safe - web + vision (no terminal access)\n\n# NOTE: The top-level \"toolsets\" key is deprecated and ignored.\n# Tool configuration is managed per-platform via platform_toolsets above.\n# Use `hermes tools` to configure interactively, or edit platform_toolsets directly.\n#\n# CLI override: hermes chat --toolsets terminal,web,file\n\n# =============================================================================\n# MCP (Model Context Protocol) Servers\n# =============================================================================\n# Connect to external MCP servers to add tools from the MCP ecosystem.\n# Each server's tools are automatically discovered and registered.\n# See website/docs/user-guide/features/mcp.md for full documentation.\n#\n# Stdio servers (spawn a subprocess):\n# command: the executable to run\n# args: command-line arguments\n# env: environment variables (only these + safe defaults passed to subprocess)\n#\n# HTTP servers (connect to a URL):\n# url: the MCP server endpoint\n# headers: HTTP headers (e.g., for authentication)\n#\n# Optional per-server settings:\n# timeout: tool call timeout in seconds (default: 120)\n# connect_timeout: initial connection timeout (default: 60)\n# keepalive_interval: liveness ping cadence in seconds (default: 180).\n# Lower it below the server's session TTL for servers that expire idle\n# sessions quickly (e.g. Unreal Engine editor MCP, ~15s), otherwise idle\n# tool calls hit an expired session and pay a slow reconnect. Floored at 5s.\n#\n# mcp_servers:\n# time:\n# command: uvx\n# args: [\"mcp-server-time\"]\n# filesystem:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-filesystem\", \"/home/user\"]\n# notion:\n# url: https://mcp.notion.com/mcp\n# github:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-github\"]\n# env:\n# GITHUB_PERSONAL_ACCESS_TOKEN: \"ghp_...\"\n#\n# Sampling (server-initiated LLM requests) — enabled by default.\n# Per-server config under the 'sampling' key:\n# analysis:\n# command: npx\n# args: [\"-y\", \"analysis-server\"]\n# sampling:\n# enabled: true # default: true\n# model: \"gemini-3-flash\" # override model (optional)\n# max_tokens_cap: 4096 # max tokens per request\n# timeout: 30 # LLM call timeout (seconds)\n# max_rpm: 10 # max requests per minute\n# allowed_models: [] # model whitelist (empty = all)\n# max_tool_rounds: 5 # tool loop limit (0 = disable)\n# log_level: \"info\" # audit verbosity\n\n# =============================================================================\n# Text-to-Speech\n# =============================================================================\n# TTS defaults to Edge TTS unless changed in ~/.hermes/config.yaml.\n# Gemini TTS supports persona/director prompt files, and Gemini 3.1 Flash TTS\n# can use a hidden auxiliary rewrite pass to insert expressive square-bracket\n# audio tags into the TTS script without showing tags in chat.\n#\n# tts:\n# provider: \"gemini\"\n# gemini:\n# model: \"gemini-3.1-flash-tts-preview\"\n# voice: \"Kore\"\n# audio_tags: false\n# persona_prompt_file: \"\" # e.g. ~/.hermes/tts/radio-host.md\n\n# =============================================================================\n# Voice Transcription (Speech-to-Text)\n# =============================================================================\n# Automatically transcribe voice messages on messaging platforms.\n# Providers: local (free, faster-whisper) | groq (free tier) | openai (Whisper API) | mistral (Voxtral Transcribe)\n# Set the corresponding API key in .env: GROQ_API_KEY, OPENAI_API_KEY, or MISTRAL_API_KEY.\nstt:\n enabled: true\n # provider: \"local\" # auto-detected if omitted\n local:\n model: \"base\" # tiny | base | small | medium | large-v3 | turbo\n # language: \"\" # auto-detect; set to \"en\", \"es\", \"fr\", etc. to force\n openai:\n model: \"whisper-1\" # whisper-1 | gpt-4o-mini-transcribe | gpt-4o-transcribe\n # mistral:\n # model: \"voxtral-mini-latest\" # voxtral-mini-latest | voxtral-mini-2602\n\n# =============================================================================\n# Response Pacing (Messaging Platforms)\n# =============================================================================\n# Add human-like delays between message chunks.\n# human_delay:\n# mode: \"off\" # \"off\" | \"natural\" | \"custom\"\n# min_ms: 800 # Min delay (custom mode only)\n# max_ms: 2500 # Max delay (custom mode only)\n\n# =============================================================================\n# Session Logging\n# =============================================================================\n# Session trajectories are automatically saved to logs/ directory.\n# Each session creates: logs/session_YYYYMMDD_HHMMSS_UUID.json\n#\n# The session ID is displayed in the welcome banner for easy reference.\n# Logs contain full conversation history in trajectory format:\n# - System prompt, user messages, assistant responses\n# - Tool calls with inputs/outputs\n# - Timestamps for debugging\n#\n# No configuration needed - logging is always enabled.\n# To disable, you would need to modify the source code.\n\n# =============================================================================\n# Code Execution Sandbox (Programmatic Tool Calling)\n# =============================================================================\n# The execute_code tool runs Python scripts that call Hermes tools via RPC.\n# Intermediate tool results stay out of the LLM's context window.\ncode_execution:\n timeout: 300 # Max seconds per script before kill (default: 300 = 5 min)\n max_tool_calls: 50 # Max RPC tool calls per execution (default: 50)\n\n# =============================================================================\n# Subagent Delegation\n# =============================================================================\n# The delegate_task tool spawns child agents with isolated context.\n# Supports single tasks and batch mode (default 3 parallel, configurable).\ndelegation:\n max_iterations: 50 # Max tool-calling turns per child (default: 50)\n # max_concurrent_children: 3 # Max parallel child agents per batch (default: 3, floor: 1, no ceiling).\n # WARNING: values above 10 multiply API cost linearly.\n # max_spawn_depth: 1 # Delegation tree depth cap (range: 1-3, default: 1 = flat).\n # Raise to 2 to allow workers to spawn their own subagents.\n # Requires role=\"orchestrator\" on intermediate agents.\n # orchestrator_enabled: true # Kill switch for role=\"orchestrator\" children (default: true).\n # subagent_auto_approve: false # When a subagent hits a dangerous-command approval prompt, auto-deny (default: false)\n # or auto-approve \"once\" (true) instead of blocking on stdin.\n # The parent TUI owns stdin, so blocking would deadlock; non-interactive resolution is required.\n # Both choices emit a logger.warning audit line. Flip to true only for cron/batch pipelines.\n # inherit_mcp_toolsets: true # When explicit child toolsets are narrowed, also keep the parent's MCP toolsets (default: true). Set false for strict intersection.\n # model: \"google/gemini-3-flash-preview\" # Override model for subagents (empty = inherit parent)\n # provider: \"openrouter\" # Override provider for subagents (empty = inherit parent)\n # # Resolves full credentials (base_url, api_key) automatically.\n # # Supported: openrouter, nous, zai, kimi-coding, minimax\n\n# =============================================================================\n# Honcho Integration (Cross-Session User Modeling)\n# =============================================================================\n# AI-native persistent memory via Honcho (https://honcho.dev/).\n# Builds a deeper understanding of the user across sessions and tools.\n# Runs alongside USER.md — additive, not a replacement.\n#\n# Requires: pip install honcho-ai\n# Config: ~/.honcho/config.json (shared with Claude Code, Cursor, etc.)\n# API key: HONCHO_API_KEY in ~/.hermes/.env or ~/.honcho/config.json\n#\n# Hermes-specific overrides (optional — most config comes from ~/.honcho/config.json):\n# honcho: {}\n\n# =============================================================================\n# Display\n# =============================================================================\ndisplay:\n # Use compact banner mode (hides the ASCII-art banner, shows a single line).\n # true: Compact single-line banner\n # false: Full ASCII banner with tool/skill summary (default)\n compact: false\n\n # Tool progress display level (CLI and gateway)\n # off: Silent — no tool activity shown, just the final response\n # new: Show a tool indicator only when the tool changes (skip repeats)\n # all: Show every tool call with a short preview (default)\n # verbose: Full args, results, and debug logs (same as /verbose)\n # log: Silent in chat; append every tool call to ~/.hermes/logs/tool_calls.log (gateway only)\n # Toggle at runtime with /verbose in the CLI\n tool_progress: all\n\n # Per-platform defaults can be quieter than the global setting. Telegram\n # tunes for mobile: tool_progress and busy_ack_detail default off (no\n # per-tool breadcrumb stream, no \"iteration 21/60\" debug detail in busy\n # acks or heartbeats), but interim_assistant_messages and\n # long_running_notifications STAY ON so the user has real signal between\n # turn start and final answer (mid-turn assistant commentary + a single\n # edit-in-place \"⏳ Working — N min\" heartbeat). Override under\n # display.platforms.telegram.\n\n # Auto-cleanup of temporary progress bubbles after the final response lands.\n # On platforms that support message deletion (currently Telegram), this\n # removes the tool-progress bubble, \"⏳ Still working...\" notices, and\n # context-pressure status messages once the final reply has been delivered —\n # keeping long-running turns visible live, then tidy afterward. Failed runs\n # leave the bubbles in place as breadcrumbs. Off by default.\n # Per-platform override: display.platforms.telegram.cleanup_progress\n # true: Delete tracked progress/status bubbles on successful turn\n # false: Leave everything in place (default)\n # Example:\n # display:\n # platforms:\n # telegram:\n # cleanup_progress: true\n cleanup_progress: false\n\n # Gateway-only natural mid-turn assistant updates.\n # When true, completed assistant status messages are sent as separate chat\n # messages. This is independent of tool_progress and gateway streaming.\n # true: Send mid-turn assistant updates as separate messages (default)\n # false: Only send the final response\n interim_assistant_messages: true\n\n # Gateway-only long-running status heartbeats.\n # When false, the platform does not receive periodic \"⏳ Working — N min\"\n # notifications even if agent.gateway_notify_interval is non-zero. The\n # heartbeat edits a single message in place (where the adapter supports\n # editing) instead of posting a new bubble each interval.\n # Default: true everywhere, including Telegram (silent agents are worse\n # than a single edit-in-place heartbeat).\n long_running_notifications: true\n\n # Include detailed iteration/tool/status context in busy acknowledgments\n # and long-running heartbeats. When true, busy acks show \"iteration 21/60,\n # terminal, 10 min\" and the heartbeat shows \"⏳ Working — 12 min,\n # iteration 21/60, terminal\". When false (Telegram default), both stay\n # terse: \"Interrupting current task\" and \"⏳ Working — 12 min, terminal\".\n busy_ack_detail: true\n\n # What Enter does when Hermes is already busy (CLI and gateway platforms).\n # interrupt: Interrupt the current run and redirect Hermes (default)\n # queue: Queue your message for the next turn\n # steer: Inject your message mid-run via /steer, arriving at the agent\n # after the next tool call — no interrupt, no role violation.\n # Falls back to 'queue' if the agent isn't running yet or if\n # images are attached (steer only carries text).\n # Ctrl+C (or /stop in gateway) always interrupts regardless of this setting.\n # Toggle at runtime with /busy .\n busy_input_mode: interrupt\n\n # Background process notifications (gateway/messaging only).\n # Controls how chatty the process watcher is when you use\n # terminal(background=true, notify_on_complete=true) from Telegram/Discord/etc.\n # off: No watcher messages at all\n # result: Only the final completion message\n # error: Only the final message when exit code != 0\n # all: Running output updates + final message (default)\n background_process_notifications: all\n\n\n # Play terminal bell when agent finishes a response.\n # Useful for long-running tasks — your terminal will ding when the agent is done.\n # Works over SSH. Most terminals can be configured to flash the taskbar or play a sound.\n # true: Ring the terminal bell on each response\n # false: Silent (default)\n bell_on_complete: false\n\n # Show model reasoning/thinking before each response.\n # When enabled, a dim box shows the model's thought process above the response.\n # Toggle at runtime with /reasoning show or /reasoning hide.\n # true: Show the reasoning box\n # false: Hide reasoning (default)\n show_reasoning: false\n\n # Stream tokens to the terminal as they arrive instead of waiting for the\n # full response. The response box opens on first token and text appears\n # line-by-line. Tool calls are still captured silently.\n # true: Stream tokens as they arrive (default)\n # false: Wait for the full response before rendering\n streaming: true\n\n # Show [HH:MM] timestamps on user input and assistant response labels.\n # timestamps: false\n\n # ───────────────────────────────────────────────────────────────────────────\n # Skin / Theme\n # ───────────────────────────────────────────────────────────────────────────\n # Customize CLI visual appearance — banner colors, spinner faces, tool prefix,\n # response box label, and branding text. Change at runtime with /skin .\n #\n # Built-in skins:\n # default — Classic Hermes gold/kawaii\n # ares — Crimson/bronze war-god theme with spinner wings\n # mono — Clean grayscale monochrome\n # slate — Cool blue developer-focused\n # daylight — Bright light-mode theme\n # warm-lightmode — Warm paper-tone light-mode theme\n # poseidon — Sea-green/teal Olympian theme\n # sisyphus — Earthy stone-and-moss theme\n # charizard — Fiery orange dragon theme\n #\n # Custom skins: drop a YAML file in ~/.hermes/skins/.yaml\n # Schema (all fields optional, missing values inherit from default):\n #\n # name: my-theme\n # description: Short description\n # colors:\n # banner_border: \"#HEX\" # Panel border\n # banner_title: \"#HEX\" # Panel title\n # banner_accent: \"#HEX\" # Section headers (Available Tools, etc.)\n # banner_dim: \"#HEX\" # Dim/muted text\n # banner_text: \"#HEX\" # Body text (tool names, skill names)\n # ui_accent: \"#HEX\" # UI accent color\n # response_border: \"#HEX\" # Response box border color\n # spinner:\n # waiting_faces: [\"(⚔)\", \"(⛨)\"] # Faces shown while waiting\n # thinking_faces: [\"(⚔)\", \"(⌁)\"] # Faces shown while thinking\n # thinking_verbs: [\"forging\", \"plotting\"] # Verbs for spinner messages\n # wings: # Optional left/right spinner decorations\n # - [\"⟪⚔\", \"⚔⟫\"]\n # - [\"⟪▲\", \"▲⟫\"]\n # branding:\n # agent_name: \"My Agent\" # Banner title and branding\n # welcome: \"Welcome message\" # Shown at CLI startup\n # response_label: \" ⚔ Agent \" # Response box header label\n # prompt_symbol: \"⚔\" # Prompt symbol (bare token; renderers add trailing space)\n # tool_prefix: \"╎\" # Tool output line prefix (default: ┊)\n #\n skin: default\n\n# =============================================================================\n# Model Aliases — short names for /model command\n# =============================================================================\n# Map short aliases to exact (model, provider, base_url) tuples.\n# Used by /model tab completion and resolve_alias().\n# Aliases are checked BEFORE the models.dev catalog, so they can route\n# to endpoints not in the catalog (e.g. Ollama Cloud, local servers).\n#\n# model_aliases:\n# opus:\n# model: claude-opus-4-6\n# provider: anthropic\n# qwen:\n# model: \"qwen3.5:397b\"\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n# glm:\n# model: glm-4.7\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n\n# =============================================================================\n# Privacy\n# =============================================================================\n# privacy:\n# # Redact PII from the LLM context prompt.\n# # When true, phone numbers are stripped and user/chat IDs are replaced\n# # with deterministic hashes before being sent to the model.\n# # Names and usernames are NOT affected (user-chosen, publicly visible).\n# # Routing/delivery still uses the original values internally.\n# redact_pii: false\n\n# =============================================================================\n# Shell-script hooks\n# =============================================================================\n# Register shell scripts as plugin-hook callbacks. Each entry is executed as\n# a subprocess (shell=False, shlex.split) with a JSON payload on stdin. On\n# stdout the script may return JSON that either blocks the tool call or\n# injects context into the next LLM call.\n#\n# Valid events (mirror hermes_cli.plugins.VALID_HOOKS):\n# pre_tool_call, post_tool_call, pre_llm_call, post_llm_call,\n# pre_api_request, post_api_request, on_session_start, on_session_end,\n# on_session_finalize, on_session_reset, subagent_stop\n#\n# First-use consent: each (event, command) pair prompts once on a TTY, then\n# is persisted to ~/.hermes/shell-hooks-allowlist.json. Non-interactive\n# runs (gateway, cron) need --accept-hooks, HERMES_ACCEPT_HOOKS=1, or the\n# hooks_auto_accept key below.\n#\n# See website/docs/user-guide/features/hooks.md for the full JSON wire\n# protocol and worked examples.\n#\n# hooks:\n# pre_tool_call:\n# - matcher: \"terminal\"\n# command: \"~/.hermes/agent-hooks/block-rm-rf.sh\"\n# timeout: 10\n# post_tool_call:\n# - matcher: \"write_file|patch\"\n# command: \"~/.hermes/agent-hooks/auto-format.sh\"\n# pre_llm_call:\n# - command: \"~/.hermes/agent-hooks/inject-cwd-context.sh\"\n# subagent_stop:\n# - command: \"~/.hermes/agent-hooks/log-orchestration.sh\"\n#\n# hooks_auto_accept: false\n\n\n# =============================================================================\n# Update Behavior\n# =============================================================================\nupdates:\n # Create a full HERMES_HOME zip before every `hermes update`.\n # Backups land in ~/.hermes/backups/ and can be restored with `hermes import`.\n # Off by default because large homes can add minutes to every update.\n pre_update_backup: false\n\n # Number of pre-update backup zips to retain.\n backup_keep: 5\n\n # What non-interactive updates do with local source edits in the Hermes repo.\n # Interactive terminal updates always prompt before restoring the autostash.\n #\n # stash - auto-stash before pull, then auto-restore after success (default)\n # discard - drop the update-created stash after success; use only on managed\n # installs where local source edits should not persist\n non_interactive_local_changes: \"stash\"\n\n\n# =============================================================================\n# Web Dashboard\n# =============================================================================\n# OAuth gate configuration for `hermes dashboard --host `.\n# The bundled Nous Portal plugin reads these on startup; settings here are\n# the canonical surface. Each can be overridden by an environment variable:\n#\n# dashboard.oauth.client_id <- HERMES_DASHBOARD_OAUTH_CLIENT_ID\n# dashboard.oauth.portal_url <- HERMES_DASHBOARD_PORTAL_URL\n# dashboard.public_url <- HERMES_DASHBOARD_PUBLIC_URL\n#\n# Env wins when set to a non-empty value. This is what Fly.io's platform-\n# secret injection uses to push per-deploy client_ids without needing to\n# bake a config.yaml into the image. Empty env values are treated as unset\n# so a provisioned-but-not-populated secret can't shadow a valid entry here.\n#\n# Local dev / on-prem deploys should typically set these via config.yaml\n# (the ~/.hermes/.env file is reserved for API keys and secrets).\n#\n# dashboard:\n# oauth:\n# client_id: \"\" # agent:{instance_id}; Portal provisions this at deploy\n# portal_url: \"\" # blank → default https://portal.nousresearch.com\n#\n# # Force the absolute base URL the OAuth callback (and any other public\n# # URL the dashboard hands to external systems) is built from. Set this\n# # for deploys behind reverse proxies that don't reliably forward\n# # X-Forwarded-Host / X-Forwarded-Proto / X-Forwarded-Prefix (manual\n# # nginx setups, on-prem ingresses, custom-domain Fly deploys without\n# # full proxy header chains).\n# #\n# # When set, the value is the complete authority: scheme + host +\n# # optional path prefix (e.g. \"https://example.com/hermes\"). The OAuth\n# # callback URL becomes \"/auth/callback\" — X-Forwarded-Prefix\n# # is IGNORED on this code path because the operator has explicitly\n# # declared the public URL and we no longer need to guess.\n# #\n# # Leave empty to use the existing proxy-header reconstruction (the\n# # default — works on Fly.io out of the box).\n# #\n# # public_url: \"https://example.com/hermes\"\n#\n# -----------------------------------------------------------------------------\n# Self-hosted OIDC dashboard auth (generic OpenID Connect — Authentik,\n# Keycloak, Zitadel, Authelia, Auth0, Okta, Google, …). Use this INSTEAD of the\n# nous block above when gating the dashboard with your own identity provider.\n# Each setting can be overridden by an environment variable:\n#\n# dashboard.oauth.self_hosted.issuer <- HERMES_DASHBOARD_OIDC_ISSUER\n# dashboard.oauth.self_hosted.client_id <- HERMES_DASHBOARD_OIDC_CLIENT_ID\n# dashboard.oauth.self_hosted.scopes <- HERMES_DASHBOARD_OIDC_SCOPES\n# dashboard.oauth.self_hosted.client_secret <- HERMES_DASHBOARD_OIDC_CLIENT_SECRET\n#\n# dashboard:\n# oauth:\n# provider: self-hosted\n# self_hosted:\n# issuer: \"https://auth.example.com/application/o/hermes/\" # required\n# client_id: \"hermes-dashboard\" # required\n# scopes: \"openid profile email\" # optional\n#\n# # OPTIONAL — set ONLY if your IDP registered the client as\n# # *confidential* (Authentik / Keycloak often default to this). When\n# # set, Hermes authenticates the client at the token endpoint\n# # (client_secret_basic or client_secret_post, auto-selected from the\n# # IDP's discovery doc) IN ADDITION to PKCE. Leave unset for a public\n# # (PKCE-only) client — the common case.\n# #\n# # This is a CREDENTIAL: prefer setting HERMES_DASHBOARD_OIDC_CLIENT_SECRET\n# # in ~/.hermes/.env over putting it here in config.yaml.\n# # client_secret: \"\"\n\n# =============================================================================\n# External secret sources\n# =============================================================================\n# Pull provider credentials from external secret managers at process startup\n# instead of storing them in ~/.hermes/.env. Only the manager's bootstrap\n# credential (e.g. BWS_ACCESS_TOKEN / OP_SERVICE_ACCOUNT_TOKEN) lives in .env\n# (or your shell / desktop session); everything else rotates centrally.\n# Failures never block startup — Hermes warns once and continues with\n# whatever .env already had.\n#\n# Multiple sources can be enabled at once:\n# - \"mapped\" sources (explicit VAR -> ref bindings, e.g. 1Password's env:\n# map) beat \"bulk\" sources (whole-project dumps like Bitwarden BSM)\n# - within a shape, the first source to claim a var wins; later claims\n# are skipped with a startup warning (never a silent clobber)\n# - a source's override_existing lets it beat .env/shell values, but\n# never another secret source's claim\n# Docs: https://hermes-agent.nousresearch.com/docs/user-guide/secrets/\n#\n# secrets:\n# # Optional explicit ordering of enabled sources.\n# # sources: [onepassword, bitwarden]\n#\n# # ---- Bitwarden Secrets Manager (bws CLI) --------------------------------\n# bitwarden:\n# enabled: false\n# access_token_env: BWS_ACCESS_TOKEN # bootstrap token, sourced from .env\n# project_id: \"\" # UUID of the BSM project to sync\n# server_url: \"\" # \"\" = US Cloud; EU/self-hosted URL otherwise\n# cache_ttl_seconds: 300 # 0 disables caching\n# override_existing: true # BSM values win over existing env\n# auto_install: true # lazy-download bws into ~/.hermes/bin\n#\n# # ---- 1Password (op CLI) -------------------------------------------------\n# onepassword:\n# enabled: false\n# # Map env-var names to op:// secret references. Each is resolved with a\n# # single `op read` at startup.\n# env:\n# OPENAI_API_KEY: \"op://Private/OpenAI/api key\"\n# ANTHROPIC_API_KEY: \"op://Private/Anthropic/credential\"\n# account: \"\" # op --account shorthand; \"\" = default\n# service_account_token_env: OP_SERVICE_ACCOUNT_TOKEN # headless auth; unset = desktop session\n# binary_path: \"\" # \"\" = resolve op via PATH; else absolute path\n# cache_ttl_seconds: 300 # 0 disables BOTH cache layers\n# override_existing: true # resolved values win over existing env\n"}, "d1c8c03416d7c6780d31ce2371c5b0777f10afaf": {"t": 1785767879.0426574, "n": 74290, "h": "63ff4c870978245f", "c": "# Hermes Agent CLI Configuration\n# Copy this file to cli-config.yaml and customize as needed.\n# This file configures the CLI behavior. Environment variables in .env take precedence.\n\n# =============================================================================\n# Model Configuration\n# =============================================================================\nmodel:\n # Default model to use (can be overridden with --model flag)\n # Both \"default\" and \"model\" work as the key name here.\n default: \"anthropic/claude-opus-4.6\"\n \n # Inference provider selection:\n # \"auto\" - Auto-detect from credentials (default)\n # \"openrouter\" - OpenRouter (requires: OPENROUTER_API_KEY or OPENAI_API_KEY)\n # \"nous\" - Nous Portal OAuth (requires: hermes auth add nous)\n # \"nous-api\" - Nous Portal API key (requires: NOUS_API_KEY)\n # \"anthropic\" - Direct Anthropic API (requires: ANTHROPIC_API_KEY)\n # \"openai-codex\" - OpenAI Codex (requires: hermes auth)\n # \"copilot\" - GitHub Copilot / GitHub Models (requires: GITHUB_TOKEN)\n # \"gemini\" - Use Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n # \"zai\" - Use z.ai / ZhipuAI GLM models (requires: GLM_API_KEY)\n # \"kimi-coding\" - Kimi / Moonshot AI (requires: KIMI_API_KEY)\n # \"minimax\" - MiniMax global (requires: MINIMAX_API_KEY)\n # \"minimax-cn\" - MiniMax China (requires: MINIMAX_CN_API_KEY)\n # \"huggingface\" - Hugging Face Inference (requires: HF_TOKEN)\n # \"nvidia\" - NVIDIA NIM / build.nvidia.com (requires: NVIDIA_API_KEY)\n # \"xiaomi\" - Xiaomi MiMo (requires: XIAOMI_API_KEY)\n # \"arcee\" - Arcee AI Trinity models (requires: ARCEEAI_API_KEY)\n # \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY — https://ollama.com/settings)\n # \"kilocode\" - KiloCode gateway (requires: KILOCODE_API_KEY)\n # \"azure-foundry\" - Microsoft Foundry / Azure OpenAI (API key or Entra ID)\n # \"lmstudio\" - LM Studio local server (optional: LM_API_KEY, defaults to http://127.0.0.1:1234/v1)\n #\n # Local servers (LM Studio, Ollama, vLLM, llama.cpp):\n # \"custom\" - Any other OpenAI-compatible endpoint. Set base_url below.\n # Aliases: \"ollama\", \"vllm\", \"llamacpp\" all map to \"custom\".\n # LM Studio is first-class and uses provider: \"lmstudio\".\n # It works with both no-auth and auth-enabled server modes.\n #\n # Can also be overridden for a single invocation with the --provider flag.\n provider: \"auto\"\n \n # API configuration (falls back to OPENROUTER_API_KEY env var)\n # api_key: \"your-key-here\" # Uncomment to set here instead of .env\n base_url: \"https://openrouter.ai/api/v1\"\n\n # Azure Foundry keyless auth example:\n # provider: \"azure-foundry\"\n # base_url: \"https://.openai.azure.com/openai/v1\"\n # auth_mode: \"entra_id\" # DefaultAzureCredential: az login, managed identity, workload identity, etc.\n # default: \"gpt-4o\" # Deployment/model name\n # entra:\n # scope: \"https://ai.azure.com/.default\" # Optional; this is the default.\n\n # ── Token limits — two settings, easy to confuse ──────────────────────────\n #\n # context_length: TOTAL context window (input + output tokens combined).\n # Controls when Hermes compresses history and validates requests.\n # Leave unset — Hermes auto-detects the correct value from the provider.\n # Set manually only when auto-detection is wrong (e.g. a local server with\n # a custom num_ctx, or a proxy that doesn't expose /v1/models).\n #\n # context_length: 131072\n #\n # max_tokens: OUTPUT cap — maximum tokens the model may generate per response.\n # Unrelated to how long your conversation history can be.\n # The OpenAI-standard name \"max_tokens\" is a misnomer; Anthropic's native\n # API has since renamed it \"max_output_tokens\" for clarity.\n # Leave unset to use the model's native output ceiling (recommended).\n # Set only if you want to deliberately limit individual response length.\n #\n# max_tokens: 8192\n\n # ── Custom request headers (optional) ─────────────────────────────────────\n #\n # default_headers: extra HTTP headers sent on every request to an\n # OpenAI-compatible endpoint. User values take precedence over the\n # provider/SDK defaults, so this is the supported way to override the\n # OpenAI Python SDK's identifying headers (User-Agent: OpenAI/Python ...,\n # X-Stainless-*) when a custom provider sits behind a gateway/WAF that\n # rejects them — e.g. an upstream that returns \"502 Upstream access\n # forbidden\" for the SDK default User-Agent but accepts a plain one.\n # Applies on the OpenAI wire only (not native Anthropic / Bedrock).\n #\n # default_headers:\n # User-Agent: \"curl/8.7.1\"\n #\n # extra_headers: accepted as an alias of default_headers (merged, with\n # extra_headers winning when both are set) — matches the per-provider\n # extra_headers key below.\n #\n # Per-provider variant: named providers / custom_providers entries accept an\n # extra_headers dict scoped to that endpoint only — for reverse proxies,\n # gateways, or custom auth (e.g. Cloudflare Access service tokens).\n # Merged onto SDK/provider defaults with the entry's values winning.\n # Header values are treated as secrets and are never logged.\n #\n # providers:\n # my-proxy:\n # base_url: \"https://llm.internal.example.com/v1\"\n # key_env: \"MY_PROXY_API_KEY\"\n # extra_headers:\n # CF-Access-Client-Id: \"xxxx.access\"\n # CF-Access-Client-Secret: \"${CF_ACCESS_SECRET}\"\n # X-Client-Name: \"hermes-agent\"\n\n# Named provider overrides (optional)\n# Use this for per-provider request timeouts, non-stream stale timeouts,\n# and per-model exceptions.\n# Applies to the primary turn client on every api_mode (OpenAI-wire, native\n# Anthropic, and Anthropic-compatible providers), the fallback chain, and\n# client rebuilds during credential rotation. For OpenAI-wire chat\n# completions (streaming and non-streaming) the configured value is also\n# used as the per-request ``timeout=`` kwarg so it wins over the legacy\n# HERMES_API_TIMEOUT env var (which still applies when no config is set).\n# ``stale_timeout_seconds`` controls the non-streaming stale-call detector and\n# wins over the legacy HERMES_API_CALL_STALE_TIMEOUT env var. Leaving these\n# unset keeps the legacy defaults (HERMES_API_TIMEOUT=1800s,\n# HERMES_API_CALL_STALE_TIMEOUT=90s, native Anthropic 900s). The\n# implicit non-stream stale detector is auto-disabled for local endpoints\n# and can scale upward for very large contexts.\n#\n# Not currently wired for AWS Bedrock (bedrock_converse + AnthropicBedrock\n# SDK paths) — those use boto3 with its own timeout configuration.\n#\n# providers:\n# ollama-local:\n# request_timeout_seconds: 300 # Longer timeout for local cold-starts\n# stale_timeout_seconds: 900 # Explicitly re-enable stale detection on local endpoints\n# anthropic:\n# request_timeout_seconds: 30 # Fast-fail cloud requests\n# models:\n# claude-opus-4.6:\n# timeout_seconds: 600 # Longer timeout for extended-thinking Opus calls\n# openai-codex:\n# models:\n# gpt-5.4:\n# stale_timeout_seconds: 1800 # Longer non-stream stale timeout for slow large-context turns\n\n# =============================================================================\n# OpenRouter Provider Routing (only applies when using OpenRouter)\n# =============================================================================\n# Control how requests are routed across providers on OpenRouter.\n# See: https://openrouter.ai/docs/guides/routing/provider-selection\n#\n# provider_routing:\n# # Sort strategy: \"price\" (default), \"throughput\", or \"latency\"\n# # Append :nitro to model name for a shortcut to throughput sorting.\n# sort: \"throughput\"\n#\n# # Only allow these providers (provider slugs from OpenRouter)\n# # only: [\"anthropic\", \"google\"]\n#\n# # Skip these providers entirely\n# # ignore: [\"deepinfra\", \"fireworks\"]\n#\n# # Try providers in this order (overrides default load balancing)\n# # order: [\"anthropic\", \"google\", \"together\"]\n#\n# # Require providers to support all parameters in your request\n# # require_parameters: true\n#\n# # Data policy: \"allow\" (default) or \"deny\" to exclude providers that may store data\n# # data_collection: \"deny\"\n\n# =============================================================================\n# OpenRouter Response Caching (only applies when using OpenRouter)\n# =============================================================================\n# Cache identical API responses at the OpenRouter edge for free instant replays.\n# When enabled, identical requests (same model, messages, parameters) return\n# cached responses with zero billing. Separate from Anthropic prompt caching.\n# See: https://openrouter.ai/docs/guides/features/response-caching\n#\n# openrouter:\n# response_cache: true # Enable response caching (default: true)\n# response_cache_ttl: 300 # Cache TTL in seconds, 1-86400 (default: 300)\n\n# =============================================================================\n# Git Worktree Isolation\n# =============================================================================\n# When enabled, each CLI session creates an isolated git worktree so multiple\n# agents can work on the same repo concurrently without file collisions.\n# Equivalent to always passing --worktree / -w on the command line.\n#\n# worktree: true # Always create a worktree when in a git repo\n# worktree: false # Default — only create when -w flag is passed\n#\n# By default a new worktree branches from the freshly-fetched remote tip\n# (the current branch's upstream, else the remote's default branch) so it\n# starts current with the project instead of from the local clone's\n# (possibly stale) HEAD. Set worktree_sync: false to branch from local HEAD\n# instead — useful when offline or when you deliberately want the clone's\n# exact current state as the base.\n#\n# worktree_sync: true # Default — branch from the fetched remote tip\n# worktree_sync: false # Branch from local HEAD (offline / pinned base)\n\n# =============================================================================\n# Terminal Tool Configuration\n# =============================================================================\n# Choose ONE of the following terminal configurations by uncommenting it.\n# The terminal tool executes commands in the specified environment.\n\n# -----------------------------------------------------------------------------\n# OPTION 1: Local execution (default)\n# Commands run directly on your machine in the current directory\n# -----------------------------------------------------------------------------\n# Working directory behavior:\n# - CLI (`hermes` command): Uses \".\" (current directory where you run hermes)\n# - Gateway/messaging/cron: Uses terminal.cwd here; legacy .env cwd values are deprecated\nterminal:\n backend: \"local\"\n cwd: \".\" # For local backend: \".\" = current directory. Ignored for remote backends unless a backend documents otherwise.\n timeout: 180\n # HOME policy for tool subprocesses:\n # auto - default: host uses your real HOME; containers use HERMES_HOME/home\n # real - force your real OS-user HOME\n # profile - force HERMES_HOME/home for strict per-profile CLI config isolation\n home_mode: \"auto\"\n docker_mount_cwd_to_workspace: false # SECURITY: off by default. Opt in to mount the launch cwd into Docker /workspace.\n lifetime_seconds: 300\n # sudo_password: \"hunter2\" # Optional: pipe a sudo password via sudo -S. SECURITY WARNING: plaintext.\n # sudo_password: \"\" # Explicit empty password: try empty and never open the interactive sudo prompt.\n\n# -----------------------------------------------------------------------------\n# OPTION 2: SSH remote execution\n# Commands run on a remote server - agent code stays local (sandboxed)\n# Great for: keeping agent isolated from its own code, using powerful remote hardware\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"ssh\"\n# cwd: \"/home/myuser/project\" # Path on the REMOTE server\n# timeout: 180\n# lifetime_seconds: 300\n# ssh_host: \"my-server.example.com\"\n# ssh_user: \"myuser\"\n# ssh_port: 22\n# ssh_key: \"~/.ssh/id_rsa\" # Optional - uses ssh-agent if not specified\n\n# -----------------------------------------------------------------------------\n# OPTION 3: Docker container\n# Commands run in an isolated Docker container\n# Great for: reproducible environments, testing, isolation\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"docker\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /)\n# timeout: 180\n# lifetime_seconds: 300\n# docker_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# docker_mount_cwd_to_workspace: true # Explicit opt-in: mount your launch cwd into /workspace\n# # Optional: run the container as your host user's uid:gid so files written\n# # into bind-mounted dirs are owned by you, not root. Drops SETUID/SETGID\n# # caps too since no gosu privilege drop is needed. Leave off if your\n# # chosen docker_image expects to start as root.\n# docker_run_as_host_user: true\n# # Optional: explicitly forward selected env vars into Docker.\n# # These values come from your current shell first, then ~/.hermes/.env.\n# # Warning: anything forwarded here is visible to commands run in the container.\n# docker_forward_env:\n# - \"GITHUB_TOKEN\"\n# - \"NPM_TOKEN\"\n# # Optional: extra flags passed verbatim to docker run (appended after security defaults).\n# # Useful for adding capabilities (e.g. apt installs needing SETUID) or custom options.\n# # Example: add a Linux capability not included by default\n# # docker_extra_args:\n# # - \"--cap-add\"\n# # - \"SETUID\"\n\n# -----------------------------------------------------------------------------\n# OPTION 4: Singularity/Apptainer container\n# Commands run in a Singularity container (common in HPC environments)\n# Great for: HPC clusters, shared compute environments\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"singularity\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# singularity_image: \"docker://nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 5: Modal cloud execution\n# Commands run on Modal's cloud infrastructure\n# Great for: GPU access, scalable compute, serverless execution\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"modal\"\n# cwd: \"/workspace\" # Path INSIDE the sandbox (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# modal_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 6: Daytona cloud execution\n# Commands run in Daytona cloud sandboxes\n# Great for: Cloud dev environments, persistent workspaces, team collaboration\n# Requires: pip install daytona, DAYTONA_API_KEY env var\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"daytona\"\n# cwd: \"~\"\n# timeout: 180\n# lifetime_seconds: 300\n# daytona_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# container_disk: 10240 # Daytona max is 10GB per sandbox\n\n#\n# --- Container resource limits (docker, singularity, modal, daytona -- ignored for local/ssh) ---\n# These settings apply to all container backends. They control the resources\n# allocated to the sandbox and whether its filesystem persists across sessions.\n container_cpu: 1 # CPU cores\n container_memory: 5120 # Memory in MB (5120 = 5GB)\n container_disk: 51200 # Disk in MB (51200 = 50GB)\n container_persistent: true # Persist filesystem across sessions (false = ephemeral)\n\n# -----------------------------------------------------------------------------\n# SUDO SUPPORT (works with ALL backends above)\n# -----------------------------------------------------------------------------\n# Add sudo_password to any terminal config above to enable sudo commands.\n# The password is piped via `sudo -S`. Works with local, ssh, docker, etc.\n#\n# SECURITY WARNING: Password stored in plaintext!\n#\n# INTERACTIVE PROMPT: If sudo_password is unset and the CLI is running,\n# you'll be prompted to enter your password when sudo is needed:\n# - 45-second timeout (auto-skips if no input)\n# - Press Enter to skip (command fails gracefully)\n# - Password is hidden while typing\n# - Password is cached for the session\n#\n# EMPTY PASSWORDS: Setting sudo_password to an explicit empty string is different\n# from leaving it unset. Hermes will try an empty password via `sudo -S` and\n# will not open the interactive prompt. This is useful for passwordless sudo,\n# Touch ID sudo setups, and environments where prompting is just noise.\n#\n# ALTERNATIVES:\n# - SSH backend: Configure passwordless sudo on the remote server\n# - Containers: Run as root inside the container (no sudo needed)\n# - Local: Configure /etc/sudoers for specific commands\n#\n# Example (add to your terminal section):\n# sudo_password: \"your-password-here\"\n\n# =============================================================================\n# Security Scanning (tirith)\n# =============================================================================\n# Optional pre-exec command security scanning via tirith.\n# Detects homograph URLs, pipe-to-shell, terminal injection, env manipulation.\n# Install: brew install sheeki03/tap/tirith\n# Docs: https://github.com/sheeki03/tirith\n#\n# security:\n# tirith_enabled: true # Enable/disable tirith scanning\n# tirith_path: \"tirith\" # Path to tirith binary (supports ~ expansion)\n# tirith_timeout: 5 # Scan timeout in seconds\n# tirith_fail_open: true # Allow commands if tirith unavailable\n\n# =============================================================================\n# Browser Tool Configuration\n# =============================================================================\nbrowser:\n # Inactivity timeout in seconds - browser sessions are automatically closed\n # after this period of no activity between agent loops (default: 120 = 2 minutes)\n inactivity_timeout: 120\n\n# =============================================================================\n# Tool Loop Guardrails\n# =============================================================================\n# Soft warnings are enabled by default. They append guidance to repeated failed\n# or non-progressing tool results but still let the tool execute. Hard stops are\n# opt-in circuit breakers for autonomous/cron sessions where stopping a loop is\n# preferable to spending the full iteration budget.\ntool_loop_guardrails:\n warnings_enabled: true\n hard_stop_enabled: false\n warn_after:\n exact_failure: 2\n same_tool_failure: 3\n idempotent_no_progress: 2\n hard_stop_after:\n exact_failure: 5\n same_tool_failure: 8\n idempotent_no_progress: 5\n\n# =============================================================================\n# Context Compression (Auto-shrinks long conversations)\n# =============================================================================\n# When conversation approaches model's context limit, middle turns are\n# automatically summarized to free up space while preserving important context.\n#\n# HOW IT WORKS:\n# 1. Tracks actual token usage from API responses (not estimates)\n# 2. When prompt_tokens >= threshold% of model's context_length, triggers compression\n# 3. Protects first 3 turns (system prompt, initial request, first response)\n# 4. Protects last N turns (default 20 messages = ~10 full turns of recent context)\n# 5. Summarizes middle turns using a fast/cheap model\n# 6. Inserts summary as a user message, continues conversation seamlessly\n#\n# Post-compression tail budget is target_ratio × threshold × context_length:\n# 200K context, threshold 0.50, ratio 0.20 → 20K tokens of recent tail preserved\n# 1M context, threshold 0.50, ratio 0.20 → 100K tokens of recent tail preserved\n#\ncompression:\n # Enable automatic context compression (default: true)\n # Set to false if you prefer to manage context manually or want errors on overflow\n enabled: true\n \n # Trigger compression at this % of model's context limit (default: 0.50 = 50%)\n # Lower values = more aggressive compression, higher values = compress later\n threshold: 0.50\n\n # Existing Codex gpt-5.5 behavior: raise Hermes' compaction trigger to 85%\n # for the ChatGPT Codex OAuth route. Set false to opt back down to threshold.\n codex_gpt55_autoraise: true\n\n # Codex-native compaction paths are opt-in (default: false) to preserve\n # Hermes' existing auxiliary summarizer behavior for existing installs.\n # When true, Codex OAuth uses Responses API compact and Codex app-server\n # compaction uses the app-server thread compact API.\n codex_native_compaction: false\n\n # Codex OAuth / Responses API compaction trigger (default: 0.85 = 85%).\n # Used only when codex_native_compaction is true. The recent tail still uses\n # protect_last_n below.\n codex_responses_threshold: 0.85\n \n # Fraction of the threshold to preserve as recent tail (default: 0.20 = 20%)\n # e.g. 20% of 50% threshold = 10% of total context kept as recent messages.\n # Summary output is separately capped at 12K tokens (Gemini output limit).\n # Range: 0.10 - 0.80\n target_ratio: 0.20\n\n # Number of most-recent messages to always preserve (default: 20 ≈ 10 full turns)\n # Higher values keep more recent conversation intact at the cost of more aggressive\n # compression of older turns.\n protect_last_n: 20\n\n # Codex app-server auto-compaction mode:\n # Used only when codex_native_compaction is true.\n # native = let Codex decide when to compact its own thread (default)\n # hermes = let Hermes threshold trigger Codex thread/compact/start\n # off = Hermes will not auto-trigger compaction; Codex may still compact natively\n codex_app_server_auto: native\n\n # Number of non-system messages to protect at the head of the transcript, in\n # ADDITION to the system prompt (which is always implicitly protected).\n # Head messages are NEVER summarized — they survive every compression\n # indefinitely. This gives stable early context for short/medium sessions,\n # but in long-running sessions that rely on rolling compaction the pinned\n # opening turns may not match how you want the session framed over time.\n # Set to 0 to preserve ONLY the system prompt (plus the rolling summary\n # and recent tail) — the cleanest configuration for long-running sessions.\n # Default 3 preserves the system prompt plus the first three non-system\n # head messages, matching the pre-feature behaviour.\n protect_first_n: 3\n\n # To pin a specific model/provider for compression summaries, use the\n # auxiliary section below (auxiliary.compression.provider / model).\n\n# =============================================================================\n# Anthropic prompt caching TTL\n# =============================================================================\n# When prompt caching is active (Claude via OpenRouter or native Anthropic),\n# Anthropic supports two TTL tiers for cached prefixes: \"5m\" (default) and\n# \"1h\". Other values are ignored and \"5m\" is used.\n#\nprompt_caching:\n cache_ttl: \"5m\" # use \"1h\" for long sessions with pauses between turns\n\n# =============================================================================\n# Auxiliary Models (Advanced — Experimental)\n# =============================================================================\n# Hermes uses lightweight \"auxiliary\" models for side tasks: image analysis,\n# browser screenshot analysis, web page summarization, TTS audio-tag insertion,\n# and context compression.\n#\n# By default these use Gemini Flash via OpenRouter or Nous Portal and are\n# auto-detected from your credentials. You do NOT need to change anything\n# here for normal usage.\n#\n# WARNING: Overriding these with providers other than OpenRouter or Nous Portal\n# is EXPERIMENTAL and may not work. Not all models/providers support vision,\n# produce usable summaries, or accept the same API format. Change at your own\n# risk — if things break, reset to \"auto\" / empty values.\n#\n# Each task has its own provider + model pair so you can mix providers.\n# For example: OpenRouter for vision (needs multimodal), but your main\n# local endpoint for compression (just needs text).\n#\n# Provider options:\n# \"auto\" - Best available: OpenRouter → Nous Portal → main endpoint (default)\n# \"openrouter\" - Force OpenRouter (requires OPENROUTER_API_KEY)\n# \"nous\" - Force Nous Portal (requires: hermes auth add nous)\n# \"gemini\" - Force Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n# \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY)\n# \"codex\" - Force Codex OAuth (requires: hermes model → Codex).\n# Uses gpt-5.3-codex which supports vision.\n# \"main\" - Use your custom endpoint (OPENAI_BASE_URL + OPENAI_API_KEY).\n# Works with OpenAI API, local models, or any OpenAI-compatible\n# endpoint. Also falls back to Codex OAuth and API-key providers.\n#\n# Model: leave empty to use the provider's default. When empty, OpenRouter\n# uses \"google/gemini-3-flash-preview\" and Nous uses \"gemini-3-flash\".\n# Other providers pick a sensible default automatically.\n#\n# auxiliary:\n# # Image analysis: vision_analyze tool + browser screenshots\n# vision:\n# provider: \"auto\"\n# model: \"\" # e.g. \"google/gemini-2.5-flash\", \"openai/gpt-4o\"\n# timeout: 30 # LLM API call timeout (seconds)\n# download_timeout: 30 # Image HTTP download timeout (seconds)\n# # Increase for slow connections or self-hosted image servers\n#\n# # Web page scraping / summarization + browser page text extraction\n# web_extract:\n# provider: \"auto\"\n# model: \"\"\n#\n# # Gemini 3.1 TTS hidden audio-tag insertion\n# tts_audio_tags:\n# provider: \"auto\" # empty model = your main chat model\n# model: \"\"\n# timeout: 30\n#\n# # Session search — summarizes matching past sessions\n# session_search:\n# provider: \"auto\"\n# model: \"\"\n# timeout: 30\n# max_concurrency: 3 # Limit parallel summaries to reduce request-burst 429s\n# extra_body: {} # Provider-specific OpenAI-compatible request fields\n# # Example for providers that support request-body\n# # reasoning controls:\n# # extra_body:\n# # enable_thinking: false\n# # Some vLLM/Qwen deployments expect this nested:\n# # extra_body:\n# # chat_template_kwargs:\n# # enable_thinking: false\n\n# =============================================================================\n# Persistent Memory\n# =============================================================================\n# Bounded curated memory injected into the system prompt every session.\n# Two stores: MEMORY.md (agent's notes) and USER.md (user profile).\n# Character limits keep the memory small and focused. The agent manages\n# pruning -- when at the limit, it must consolidate or replace entries.\n# Disabled by default in batch_runner.\n#\nmemory:\n # Agent's personal notes: environment facts, conventions, things learned\n memory_enabled: true\n \n # User profile: preferences, communication style, expectations\n user_profile_enabled: true\n \n # Character limits (~2.75 chars per token, model-independent)\n memory_char_limit: 2200 # ~800 tokens\n user_char_limit: 1375 # ~500 tokens\n\n # Periodic memory nudge: remind the agent to consider saving memories\n # every N user turns. Set to 0 to disable. Only active when memory is enabled.\n nudge_interval: 10 # Nudge every 10 user turns (0 = disabled)\n\n # Memory flush: give the agent one turn to save memories before context is\n # lost (compression, /new, /reset, exit). Set to 0 to disable.\n # For exit/reset, only fires if the session had at least this many user turns.\n flush_min_turns: 6 # Min user turns to trigger flush on exit/reset (0 = disabled)\n\n# =============================================================================\n# Session Reset Policy (Messaging Platforms)\n# =============================================================================\n# Controls when messaging sessions (Telegram, Discord, WhatsApp, Slack) are\n# automatically cleared. Without resets, conversation context grows indefinitely\n# which increases API costs with every message.\n#\n# When a reset triggers, the agent first saves important information to its\n# persistent memory — but the conversation context is wiped. The agent starts\n# fresh but retains learned facts via its memory system.\n#\n# Users can always manually reset with /reset or /new in chat.\n#\n# Modes:\n# \"both\" - Reset on EITHER inactivity timeout or daily boundary (recommended)\n# \"idle\" - Reset only after N minutes of inactivity\n# \"daily\" - Reset only at a fixed hour each day\n# \"none\" - Never auto-reset; context lives until /reset or compression kicks in\n#\n# When a reset triggers, the agent gets one turn to save important memories and\n# skills before the context is wiped. Persistent memory carries across sessions.\n#\nsession_reset:\n mode: both # \"both\", \"idle\", \"daily\", or \"none\"\n idle_minutes: 1440 # Inactivity timeout in minutes (default: 1440 = 24 hours)\n at_hour: 4 # Daily reset hour, 0-23 local time (default: 4 AM)\n\n# Maximum number of simultaneously active chat sessions across CLI, TUI,\n# dashboard chat, and messaging gateway. Set to null, 0, or omit to allow\n# unlimited concurrent sessions. When the limit is reached, new sessions get a\n# clean error while existing active sessions keep their normal behavior. This\n# top-level key takes precedence over gateway.max_concurrent_sessions. The cap\n# is a best-effort single-host/profile runtime guard; Hermes fails open if the\n# local runtime lease registry cannot be read or locked.\nmax_concurrent_sessions: null\n\n# When true, group/channel chats use one session per participant when the platform\n# provides a user ID. This is the secure default and prevents users in the same\n# room from sharing context, interrupts, and token costs. Set false only if you\n# explicitly want one shared \"room brain\" per group/channel.\ngroup_sessions_per_user: true\n\n# ─────────────────────────────────────────────────────────────────────────────\n# API Server — per-client model routing\n# ─────────────────────────────────────────────────────────────────────────────\n# Route different API clients to different models/providers on a single\n# Hermes deployment. Clients choose a backend by sending a specific string\n# as the OpenAI ``model`` field. Unmapped model values fall back to the\n# global model configured in the ``model:`` section above, and an explicit\n# session /model override always wins over a route.\n#\n# Configure via the ``platforms.api_server.extra.model_routes`` gateway\n# config block:\n#\n# platforms:\n# api_server:\n# enabled: true\n# extra:\n# key: \"your-api-server-secret\"\n# model_routes:\n# # Xiaozhi clients send model=\"minimax-m2\" → routed to MiniMax via OpenRouter\n# minimax-m2:\n# model: \"minimax/minimax-m1\"\n# provider: \"openrouter\" # optional — overrides global provider\n# # api_key: \"sk-...\" # optional — per-route UPSTREAM provider\n# # key (NOT caller auth; never logged)\n# # base_url: \"https://...\" # optional — per-route base URL\n# # GPT clients keep their own alias\n# gpt-5:\n# model: \"openai/gpt-5\"\n# provider: \"openrouter\"\n#\n# Configured aliases are automatically listed by GET /v1/models so clients\n# can discover them without manual coordination. Caller authentication is\n# unchanged: every request still authenticates with the global API server\n# key (``extra.key`` / API_SERVER_KEY).\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Gateway Streaming\n# ─────────────────────────────────────────────────────────────────────────────\n# Stream tokens to messaging platforms in real-time. The bot sends a message\n# on first token, then progressively edits it as more tokens arrive.\n# Disabled by default — enable to try the streaming UX on Telegram/Discord/Slack.\n# For Telegram, partial edits are sent as plain text and only the final edit uses MarkdownV2.\nstreaming:\n enabled: false\n # transport: edit # \"edit\" = progressive editMessageText\n # edit_interval: 0.3 # seconds between message edits\n # buffer_threshold: 40 # chars before forcing an edit flush\n # cursor: \" ▉\" # cursor shown during streaming\n\n# =============================================================================\n# Skills Configuration\n# =============================================================================\n# Skills are reusable procedures the agent can load and follow. The agent can\n# also create new skills after completing complex tasks.\n#\nskills:\n # Nudge the agent to create skills after complex tasks.\n # Every N tool-calling iterations, remind the model to consider saving a skill.\n # Set to 0 to disable.\n creation_nudge_interval: 15\n\n # External skill directories — share skills across tools/agents without\n # copying them into ~/.hermes/skills/. Each path is expanded (~ and ${VAR})\n # and resolved to an absolute path. External dirs are read-only: skill\n # creation always writes to ~/.hermes/skills/. Local skills take precedence\n # when names collide.\n # external_dirs:\n # - ~/.agents/skills\n # - /home/shared/team-skills\n\n# =============================================================================\n# Agent Behavior\n# =============================================================================\nagent:\n # Maximum tool-calling iterations per conversation\n # Higher = more room for complex tasks, but costs more tokens\n # Recommended: 20-30 for focused tasks, 50-100 for open exploration\n max_turns: 60\n\n # Inactivity timeout for gateway agent runs (seconds, 0 = unlimited).\n # The agent can run indefinitely when actively calling tools or receiving\n # API responses. Only fires after the agent has been idle for this duration.\n # gateway_timeout: 1800\n\n # Staged warning: send a warning before escalating to full timeout.\n # Fires once per run when inactivity reaches this threshold (seconds).\n # Set to 0 to disable the warning.\n # gateway_timeout_warning: 900\n\n # Graceful drain timeout for gateway stop/restart (seconds).\n # Default 0 = no drain: a restart interrupts in-flight agents immediately,\n # cleans up, and exits. Set a positive value only if you want a grace\n # window on /restart, and keep it well under systemd's TimeoutStopSec.\n # restart_drain_timeout: 0\n\n # Max app-level retry attempts for API errors (connection drops, provider\n # timeouts, 5xx, etc.) before the agent surfaces the failure. Lower this\n # to 1 if you use fallback providers and want fast failover on flaky\n # primaries (default 3). The OpenAI SDK does its own low-level retries\n # underneath this wrapper — this is the Hermes-level loop.\n # api_max_retries: 3\n\n # After the agent edits code without fresh passing verification, nudge it to\n # verify before finishing. The default \"auto\" enables it on interactive\n # coding surfaces (CLI, TUI, desktop) and programmatic callers, and disables\n # it on conversational messaging surfaces (Telegram, Discord, etc.) where the\n # verification summary would reach a human as chat noise. Set true or false to\n # force it on or off; the HERMES_VERIFY_ON_STOP env var (1/0) takes precedence.\n # verify_on_stop: auto\n\n # Standing operator instructions for the coding posture (when Hermes is in a\n # code workspace). Appended to the coding brief as an extra system block, so\n # you can pin project-wide workflow rules without editing the shipped brief.\n # Accepts a string or a list of strings. Takes effect next session.\n # coding_instructions:\n # - \"For UI work, don't run tsc/lint until I approve the look.\"\n # - \"Clean the diff before you commit and push.\"\n\n # When verify-on-stop finds edited code without fresh verification evidence,\n # append guidance for creative UI work (avoid broad tsc/lint/test before visual\n # approval) and clean-diff expectations. Set false to keep that nudge terse.\n # verify_guidance: true\n\n # A `pre_verify` hook (plugin or shell, see Event Hooks docs) can keep the\n # agent going one more turn to verify/clean before finishing. This caps how\n # many times one turn may be nudged to continue, so a hook can't trap the loop.\n # Default 3.\n # max_verify_nudges: 3\n\n # Enable verbose logging\n verbose: false\n \n # Reasoning effort level (OpenRouter and Nous Portal)\n # Controls how much \"thinking\" the model does before responding.\n # Options: \"xhigh\" (max), \"high\", \"medium\", \"low\", \"minimal\", \"none\" (disable)\n reasoning_effort: \"medium\"\n \n # Predefined personalities (use with /personality command)\n personalities:\n helpful: \"You are a helpful, friendly AI assistant.\"\n concise: \"You are a concise assistant. Keep responses brief and to the point.\"\n technical: \"You are a technical expert. Provide detailed, accurate technical information.\"\n creative: \"You are a creative assistant. Think outside the box and offer innovative solutions.\"\n teacher: \"You are a patient teacher. Explain concepts clearly with examples.\"\n kawaii: \"You are a kawaii assistant! Use cute expressions like (◕‿◕), ★, ♪, and ~! Add sparkles and be super enthusiastic about everything! Every response should feel warm and adorable desu~! ヽ(>∀<☆)ノ\"\n catgirl: \"You are Neko-chan, an anime catgirl AI assistant, nya~! Add 'nya' and cat-like expressions to your speech. Use kaomoji like (=^・ω・^=) and ฅ^•ﻌ•^ฅ. Be playful and curious like a cat, nya~!\"\n pirate: \"Arrr! Ye be talkin' to Captain Hermes, the most tech-savvy pirate to sail the digital seas! Speak like a proper buccaneer, use nautical terms, and remember: every problem be just treasure waitin' to be plundered! Yo ho ho!\"\n shakespeare: \"Hark! Thou speakest with an assistant most versed in the bardic arts. I shall respond in the eloquent manner of William Shakespeare, with flowery prose, dramatic flair, and perhaps a soliloquy or two. What light through yonder terminal breaks?\"\n surfer: \"Duuude! You're chatting with the chillest AI on the web, bro! Everything's gonna be totally rad. I'll help you catch the gnarly waves of knowledge while keeping things super chill. Cowabunga! 🤙\"\n noir: \"The rain hammered against the terminal like regrets on a guilty conscience. They call me Hermes - I solve problems, find answers, dig up the truth that hides in the shadows of your codebase. In this city of silicon and secrets, everyone's got something to hide. What's your story, pal?\"\n uwu: \"hewwo! i'm your fwiendwy assistant uwu~ i wiww twy my best to hewp you! *nuzzles your code* OwO what's this? wet me take a wook! i pwomise to be vewy hewpful >w<\"\n philosopher: \"Greetings, seeker of wisdom. I am an assistant who contemplates the deeper meaning behind every query. Let us examine not just the 'how' but the 'why' of your questions. Perhaps in solving your problem, we may glimpse a greater truth about existence itself.\"\n hype: \"YOOO LET'S GOOOO!!! 🔥🔥🔥 I am SO PUMPED to help you today! Every question is AMAZING and we're gonna CRUSH IT together! This is gonna be LEGENDARY! ARE YOU READY?! LET'S DO THIS! 💪😤🚀\"\n\n# =============================================================================\n# Toolsets\n# =============================================================================\n# Control which tools the agent has access to.\n# Use `hermes tools` to interactively enable/disable tools per platform.\n\n# =============================================================================\n# Platform Toolsets (per-platform tool configuration)\n# =============================================================================\n# Override which toolsets are available on each platform.\n# If a platform isn't listed here, its built-in default is used.\n#\n# You can use EITHER:\n# - A preset like \"hermes-cli\" or \"hermes-telegram\" (curated tool set)\n# - A list of individual toolsets to compose your own (see list below)\n#\n# Supported platform keys: cli, telegram, discord, whatsapp, slack, qqbot, teams, google_chat\n#\n# Examples:\n#\n# # Use presets (same as defaults):\n# platform_toolsets:\n# cli: [hermes-cli]\n# telegram: [hermes-telegram]\n#\n# # Custom: give Telegram only web + terminal + file + planning:\n# platform_toolsets:\n# telegram: [web, terminal, file, todo]\n#\n# # Custom: CLI without browser or image gen:\n# platform_toolsets:\n# cli: [web, terminal, file, skills, todo, tts, cronjob]\n#\n# # Restrictive: Discord gets read-only tools only:\n# platform_toolsets:\n# discord: [web, vision, skills, todo]\n#\n# If not set, defaults are:\n# cli: hermes-cli (everything + cronjob management)\n# telegram: hermes-telegram (terminal, file, web, vision, image, tts, browser, skills, todo, cronjob, messaging)\n# discord: hermes-discord (same as telegram)\n# whatsapp: hermes-whatsapp (same as telegram)\n# slack: hermes-slack (same as telegram)\n# signal: hermes-signal (same as telegram)\n# homeassistant: hermes-homeassistant (same as telegram)\n# qqbot: hermes-qqbot (same as telegram)\n# teams: hermes-teams (same as telegram)\n# google_chat: hermes-google_chat (same as telegram)\n#\nplatform_toolsets:\n cli: [hermes-cli]\n telegram: [hermes-telegram]\n discord: [hermes-discord]\n whatsapp: [hermes-whatsapp]\n slack: [hermes-slack]\n signal: [hermes-signal]\n homeassistant: [hermes-homeassistant]\n qqbot: [hermes-qqbot]\n yuanbao: [hermes-yuanbao]\n teams: [hermes-teams]\n google_chat: [hermes-google_chat]\n\n# =============================================================================\n# Gateway Platform Settings\n# =============================================================================\n# Optional per-platform messaging settings.\n# Platform-specific knobs live under `extra`.\n#\n# platforms:\n# telegram:\n# reply_to_mode: \"first\" # off | first | all\n# # guest_mode lets explicit @mentions from non-allowlisted groups through.\n# # Default false; ordinary messages, replies, and regex wake words stay blocked.\n# guest_mode: false\n# # allowed_chats: [\"-1001234567890\"]\n# extra:\n# disable_link_previews: false # Set true to suppress Telegram URL previews in bot messages\n# rich_messages: false # Bot API 10.1 rich messages (tables/task lists/details/math); default false for copyable legacy MarkdownV2, set true to opt in\n# rich_drafts: false # Experimental rich draft previews during Telegram DM streaming; default false because Telegram Desktop/macOS can visually overlay draft frames\n# command_menu:\n# # Telegram allows up to 100 BotCommands; Hermes defaults to 60 so\n# # all built-in commands plus common skill commands stay visible\n# # while remaining under Telegram's payload-size limit. Clamped 1..100.\n# max_commands: 60\n# # prepend = user priority first, then Hermes defaults\n# # append = Hermes defaults first, then user priority\n# # replace = only the list below defines priority\n# priority_mode: prepend\n# priority:\n# - my_plugin_command\n#\n# Discord-specific settings (config.yaml top-level, not under platforms:):\n#\n# discord:\n# require_mention: true # Require @mention in server channels (default: true)\n# auto_thread: true # Auto-create thread on @mention (default: true)\n# free_response_channels: \"\" # Channel IDs where no mention is needed\n# reactions: true # Show processing reactions (default: true)\n# history_backfill: true # Recover missed channel messages on mention (default: true)\n# history_backfill_limit: 50 # Max messages to scan backwards (default: 50)\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Available toolsets (use these names in platform_toolsets or the toolsets list)\n#\n# Run `hermes chat --list-toolsets` to see all toolsets and their tools.\n# Run `hermes chat --list-tools` to see every individual tool with descriptions.\n# ─────────────────────────────────────────────────────────────────────────────\n#\n# INDIVIDUAL TOOLSETS (compose your own):\n# web - web_search, web_extract\n# search - web_search only (no scraping)\n# terminal - terminal, process\n# file - read_file, write_file, patch, search\n# browser - browser_navigate, browser_snapshot, browser_click, browser_type,\n# browser_scroll, browser_back, browser_press,\n# browser_get_images, browser_vision (requires BROWSERBASE_API_KEY)\n# vision - vision_analyze (requires OPENROUTER_API_KEY)\n# image_gen - image_generate (requires FAL_KEY)\n# skills - skills_list, skill_view\n# skills_hub - skill_hub (search/install/manage from online registries — user-driven only)\n# todo - todo (in-memory task planning, no deps)\n# tts - text_to_speech (Edge TTS free, or ELEVENLABS/OPENAI/MINIMAX/MISTRAL key)\n# cronjob - cronjob (create/list/update/pause/resume/run/remove scheduled tasks)\n#\n# PRESETS (curated bundles):\n# hermes-cli - All of the above except send_message\n# hermes-telegram - terminal, file, web, vision, image_gen, tts, browser,\n# skills, todo, cronjob, send_message\n# hermes-discord - Same as hermes-telegram\n# hermes-whatsapp - Same as hermes-telegram\n# hermes-slack - Same as hermes-telegram\n#\n# COMPOSITE:\n# debugging - terminal + web + file\n# safe - web + vision (no terminal access)\n# all - Everything available\n#\n# web - Web search and content extraction (web_search, web_extract)\n# search - Web search only, no scraping (web_search)\n# terminal - Command execution and process management (terminal, process)\n# file - File operations: read, write, patch, search\n# browser - Full browser automation (navigate, click, type, screenshot, etc.)\n# vision - Image analysis (vision_analyze)\n# image_gen - Image generation with FLUX (image_generate)\n# skills - Load skill documents (skills_list, skill_view)\n# todo - Task planning and tracking for multi-step work\n# memory - Persistent memory across sessions (personal notes + user profile)\n# session_search - Search and recall past conversations (FTS5 + Gemini Flash summarization)\n# tts - Text-to-speech (Edge TTS free, ElevenLabs, OpenAI, MiniMax, Mistral)\n# cronjob - Schedule and manage automated tasks (CLI-only)\n#\n# Composite toolsets:\n# debugging - terminal + web + file (for troubleshooting)\n# safe - web + vision (no terminal access)\n\n# NOTE: The top-level \"toolsets\" key is deprecated and ignored.\n# Tool configuration is managed per-platform via platform_toolsets above.\n# Use `hermes tools` to configure interactively, or edit platform_toolsets directly.\n#\n# CLI override: hermes chat --toolsets terminal,web,file\n\n# =============================================================================\n# MCP (Model Context Protocol) Servers\n# =============================================================================\n# Connect to external MCP servers to add tools from the MCP ecosystem.\n# Each server's tools are automatically discovered and registered.\n# See website/docs/user-guide/features/mcp.md for full documentation.\n#\n# Stdio servers (spawn a subprocess):\n# command: the executable to run\n# args: command-line arguments\n# env: environment variables (only these + safe defaults passed to subprocess)\n#\n# HTTP servers (connect to a URL):\n# url: the MCP server endpoint\n# headers: HTTP headers (e.g., for authentication)\n#\n# Optional per-server settings:\n# timeout: tool call timeout in seconds (default: 120)\n# connect_timeout: initial connection timeout (default: 60)\n# keepalive_interval: liveness ping cadence in seconds (default: 180).\n# Lower it below the server's session TTL for servers that expire idle\n# sessions quickly (e.g. Unreal Engine editor MCP, ~15s), otherwise idle\n# tool calls hit an expired session and pay a slow reconnect. Floored at 5s.\n#\n# mcp_servers:\n# time:\n# command: uvx\n# args: [\"mcp-server-time\"]\n# filesystem:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-filesystem\", \"/home/user\"]\n# notion:\n# url: https://mcp.notion.com/mcp\n# github:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-github\"]\n# env:\n# GITHUB_PERSONAL_ACCESS_TOKEN: \"ghp_...\"\n#\n# Sampling (server-initiated LLM requests) — enabled by default.\n# Per-server config under the 'sampling' key:\n# analysis:\n# command: npx\n# args: [\"-y\", \"analysis-server\"]\n# sampling:\n# enabled: true # default: true\n# model: \"gemini-3-flash\" # override model (optional)\n# max_tokens_cap: 4096 # max tokens per request\n# timeout: 30 # LLM call timeout (seconds)\n# max_rpm: 10 # max requests per minute\n# allowed_models: [] # model whitelist (empty = all)\n# max_tool_rounds: 5 # tool loop limit (0 = disable)\n# log_level: \"info\" # audit verbosity\n\n# =============================================================================\n# Text-to-Speech\n# =============================================================================\n# TTS defaults to Edge TTS unless changed in ~/.hermes/config.yaml.\n# Gemini TTS supports persona/director prompt files, and Gemini 3.1 Flash TTS\n# can use a hidden auxiliary rewrite pass to insert expressive square-bracket\n# audio tags into the TTS script without showing tags in chat.\n#\n# tts:\n# provider: \"gemini\"\n# gemini:\n# model: \"gemini-3.1-flash-tts-preview\"\n# voice: \"Kore\"\n# audio_tags: false\n# persona_prompt_file: \"\" # e.g. ~/.hermes/tts/radio-host.md\n\n# =============================================================================\n# Voice Transcription (Speech-to-Text)\n# =============================================================================\n# Automatically transcribe voice messages on messaging platforms.\n# Providers: local (free, faster-whisper) | groq (free tier) | openai (Whisper API) | mistral (Voxtral Transcribe)\n# Set the corresponding API key in .env: GROQ_API_KEY, OPENAI_API_KEY, or MISTRAL_API_KEY.\nstt:\n enabled: true\n # provider: \"local\" # auto-detected if omitted\n local:\n model: \"base\" # tiny | base | small | medium | large-v3 | turbo\n # language: \"\" # auto-detect; set to \"en\", \"es\", \"fr\", etc. to force\n openai:\n model: \"whisper-1\" # whisper-1 | gpt-4o-mini-transcribe | gpt-4o-transcribe\n # mistral:\n # model: \"voxtral-mini-latest\" # voxtral-mini-latest | voxtral-mini-2602\n\n# =============================================================================\n# Response Pacing (Messaging Platforms)\n# =============================================================================\n# Add human-like delays between message chunks.\n# human_delay:\n# mode: \"off\" # \"off\" | \"natural\" | \"custom\"\n# min_ms: 800 # Min delay (custom mode only)\n# max_ms: 2500 # Max delay (custom mode only)\n\n# =============================================================================\n# Session Logging\n# =============================================================================\n# Session trajectories are automatically saved to logs/ directory.\n# Each session creates: logs/session_YYYYMMDD_HHMMSS_UUID.json\n#\n# The session ID is displayed in the welcome banner for easy reference.\n# Logs contain full conversation history in trajectory format:\n# - System prompt, user messages, assistant responses\n# - Tool calls with inputs/outputs\n# - Timestamps for debugging\n#\n# No configuration needed - logging is always enabled.\n# To disable, you would need to modify the source code.\n\n# =============================================================================\n# Code Execution Sandbox (Programmatic Tool Calling)\n# =============================================================================\n# The execute_code tool runs Python scripts that call Hermes tools via RPC.\n# Intermediate tool results stay out of the LLM's context window.\ncode_execution:\n timeout: 300 # Max seconds per script before kill (default: 300 = 5 min)\n max_tool_calls: 50 # Max RPC tool calls per execution (default: 50)\n\n# =============================================================================\n# Subagent Delegation\n# =============================================================================\n# The delegate_task tool spawns child agents with isolated context.\n# Supports single tasks and batch mode (default 3 parallel, configurable).\ndelegation:\n max_iterations: 50 # Max tool-calling turns per child (default: 50)\n # max_concurrent_children: 3 # Max parallel child agents per batch (default: 3, floor: 1, no ceiling).\n # WARNING: values above 10 multiply API cost linearly.\n # max_spawn_depth: 1 # Delegation tree depth cap (range: 1-3, default: 1 = flat).\n # Raise to 2 to allow workers to spawn their own subagents.\n # Requires role=\"orchestrator\" on intermediate agents.\n # orchestrator_enabled: true # Kill switch for role=\"orchestrator\" children (default: true).\n # subagent_auto_approve: false # When a subagent hits a dangerous-command approval prompt, auto-deny (default: false)\n # or auto-approve \"once\" (true) instead of blocking on stdin.\n # The parent TUI owns stdin, so blocking would deadlock; non-interactive resolution is required.\n # Both choices emit a logger.warning audit line. Flip to true only for cron/batch pipelines.\n # inherit_mcp_toolsets: true # When explicit child toolsets are narrowed, also keep the parent's MCP toolsets (default: true). Set false for strict intersection.\n # model: \"google/gemini-3-flash-preview\" # Override model for subagents (empty = inherit parent)\n # provider: \"openrouter\" # Override provider for subagents (empty = inherit parent)\n # # Resolves full credentials (base_url, api_key) automatically.\n # # Supported: openrouter, nous, zai, kimi-coding, minimax\n\n# =============================================================================\n# Honcho Integration (Cross-Session User Modeling)\n# =============================================================================\n# AI-native persistent memory via Honcho (https://honcho.dev/).\n# Builds a deeper understanding of the user across sessions and tools.\n# Runs alongside USER.md — additive, not a replacement.\n#\n# Requires: pip install honcho-ai\n# Config: ~/.honcho/config.json (shared with Claude Code, Cursor, etc.)\n# API key: HONCHO_API_KEY in ~/.hermes/.env or ~/.honcho/config.json\n#\n# Hermes-specific overrides (optional — most config comes from ~/.honcho/config.json):\n# honcho: {}\n\n# =============================================================================\n# Display\n# =============================================================================\ndisplay:\n # Use compact banner mode (hides the ASCII-art banner, shows a single line).\n # true: Compact single-line banner\n # false: Full ASCII banner with tool/skill summary (default)\n compact: false\n\n # Tool progress display level (CLI and gateway)\n # off: Silent — no tool activity shown, just the final response\n # new: Show a tool indicator only when the tool changes (skip repeats)\n # all: Show every tool call with a short preview (default)\n # verbose: Full args, results, and debug logs (same as /verbose)\n # log: Silent in chat; append every tool call to ~/.hermes/logs/tool_calls.log (gateway only)\n # Toggle at runtime with /verbose in the CLI\n tool_progress: all\n\n # Per-platform defaults can be quieter than the global setting. Telegram\n # tunes for mobile: tool_progress and busy_ack_detail default off (no\n # per-tool breadcrumb stream, no \"iteration 21/60\" debug detail in busy\n # acks or heartbeats), but interim_assistant_messages and\n # long_running_notifications STAY ON so the user has real signal between\n # turn start and final answer (mid-turn assistant commentary + a single\n # edit-in-place \"⏳ Working — N min\" heartbeat). Override under\n # display.platforms.telegram.\n\n # Auto-cleanup of temporary progress bubbles after the final response lands.\n # On platforms that support message deletion (currently Telegram), this\n # removes the tool-progress bubble, \"⏳ Still working...\" notices, and\n # context-pressure status messages once the final reply has been delivered —\n # keeping long-running turns visible live, then tidy afterward. Failed runs\n # leave the bubbles in place as breadcrumbs. Off by default.\n # Per-platform override: display.platforms.telegram.cleanup_progress\n # true: Delete tracked progress/status bubbles on successful turn\n # false: Leave everything in place (default)\n # Example:\n # display:\n # platforms:\n # telegram:\n # cleanup_progress: true\n cleanup_progress: false\n\n # Gateway-only natural mid-turn assistant updates.\n # When true, completed assistant status messages are sent as separate chat\n # messages. This is independent of tool_progress and gateway streaming.\n # true: Send mid-turn assistant updates as separate messages (default)\n # false: Only send the final response\n interim_assistant_messages: true\n\n # Gateway-only long-running status heartbeats.\n # When false, the platform does not receive periodic \"⏳ Working — N min\"\n # notifications even if agent.gateway_notify_interval is non-zero. The\n # heartbeat edits a single message in place (where the adapter supports\n # editing) instead of posting a new bubble each interval.\n # Default: true everywhere, including Telegram (silent agents are worse\n # than a single edit-in-place heartbeat).\n long_running_notifications: true\n\n # Include detailed iteration/tool/status context in busy acknowledgments\n # and long-running heartbeats. When true, busy acks show \"iteration 21/60,\n # terminal, 10 min\" and the heartbeat shows \"⏳ Working — 12 min,\n # iteration 21/60, terminal\". When false (Telegram default), both stay\n # terse: \"Interrupting current task\" and \"⏳ Working — 12 min, terminal\".\n busy_ack_detail: true\n\n # What Enter does when Hermes is already busy (CLI and gateway platforms).\n # interrupt: Interrupt the current run and redirect Hermes (default)\n # queue: Queue your message for the next turn\n # steer: Inject your message mid-run via /steer, arriving at the agent\n # after the next tool call — no interrupt, no role violation.\n # Falls back to 'queue' if the agent isn't running yet or if\n # images are attached (steer only carries text).\n # Ctrl+C (or /stop in gateway) always interrupts regardless of this setting.\n # Toggle at runtime with /busy .\n busy_input_mode: interrupt\n\n # Background process notifications (gateway/messaging only).\n # Controls how chatty the process watcher is when you use\n # terminal(background=true, notify_on_complete=true) from Telegram/Discord/etc.\n # off: No watcher messages at all\n # result: Only the final completion message\n # error: Only the final message when exit code != 0\n # all: Running output updates + final message (default)\n background_process_notifications: all\n\n\n # Play terminal bell when agent finishes a response.\n # Useful for long-running tasks — your terminal will ding when the agent is done.\n # Works over SSH. Most terminals can be configured to flash the taskbar or play a sound.\n # true: Ring the terminal bell on each response\n # false: Silent (default)\n bell_on_complete: false\n\n # Show model reasoning/thinking before each response.\n # When enabled, a dim box shows the model's thought process above the response.\n # Toggle at runtime with /reasoning show or /reasoning hide.\n # true: Show the reasoning box\n # false: Hide reasoning (default)\n show_reasoning: false\n\n # Stream tokens to the terminal as they arrive instead of waiting for the\n # full response. The response box opens on first token and text appears\n # line-by-line. Tool calls are still captured silently.\n # true: Stream tokens as they arrive (default)\n # false: Wait for the full response before rendering\n streaming: true\n\n # Show [HH:MM] timestamps on user input and assistant response labels.\n # timestamps: false\n\n # ───────────────────────────────────────────────────────────────────────────\n # Skin / Theme\n # ───────────────────────────────────────────────────────────────────────────\n # Customize CLI visual appearance — banner colors, spinner faces, tool prefix,\n # response box label, and branding text. Change at runtime with /skin .\n #\n # Built-in skins:\n # default — Classic Hermes gold/kawaii\n # ares — Crimson/bronze war-god theme with spinner wings\n # mono — Clean grayscale monochrome\n # slate — Cool blue developer-focused\n # daylight — Bright light-mode theme\n # warm-lightmode — Warm paper-tone light-mode theme\n # poseidon — Sea-green/teal Olympian theme\n # sisyphus — Earthy stone-and-moss theme\n # charizard — Fiery orange dragon theme\n #\n # Custom skins: drop a YAML file in ~/.hermes/skins/.yaml\n # Schema (all fields optional, missing values inherit from default):\n #\n # name: my-theme\n # description: Short description\n # colors:\n # banner_border: \"#HEX\" # Panel border\n # banner_title: \"#HEX\" # Panel title\n # banner_accent: \"#HEX\" # Section headers (Available Tools, etc.)\n # banner_dim: \"#HEX\" # Dim/muted text\n # banner_text: \"#HEX\" # Body text (tool names, skill names)\n # ui_accent: \"#HEX\" # UI accent color\n # response_border: \"#HEX\" # Response box border color\n # spinner:\n # waiting_faces: [\"(⚔)\", \"(⛨)\"] # Faces shown while waiting\n # thinking_faces: [\"(⚔)\", \"(⌁)\"] # Faces shown while thinking\n # thinking_verbs: [\"forging\", \"plotting\"] # Verbs for spinner messages\n # wings: # Optional left/right spinner decorations\n # - [\"⟪⚔\", \"⚔⟫\"]\n # - [\"⟪▲\", \"▲⟫\"]\n # branding:\n # agent_name: \"My Agent\" # Banner title and branding\n # welcome: \"Welcome message\" # Shown at CLI startup\n # response_label: \" ⚔ Agent \" # Response box header label\n # prompt_symbol: \"⚔\" # Prompt symbol (bare token; renderers add trailing space)\n # tool_prefix: \"╎\" # Tool output line prefix (default: ┊)\n #\n skin: default\n\n# =============================================================================\n# Model Aliases — short names for /model command\n# =============================================================================\n# Map short aliases to exact (model, provider, base_url) tuples.\n# Used by /model tab completion and resolve_alias().\n# Aliases are checked BEFORE the models.dev catalog, so they can route\n# to endpoints not in the catalog (e.g. Ollama Cloud, local servers).\n#\n# model_aliases:\n# opus:\n# model: claude-opus-4-6\n# provider: anthropic\n# qwen:\n# model: \"qwen3.5:397b\"\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n# glm:\n# model: glm-4.7\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n\n# =============================================================================\n# Privacy\n# =============================================================================\n# privacy:\n# # Redact PII from the LLM context prompt.\n# # When true, phone numbers are stripped and user/chat IDs are replaced\n# # with deterministic hashes before being sent to the model.\n# # Names and usernames are NOT affected (user-chosen, publicly visible).\n# # Routing/delivery still uses the original values internally.\n# redact_pii: false\n\n# =============================================================================\n# Shell-script hooks\n# =============================================================================\n# Register shell scripts as plugin-hook callbacks. Each entry is executed as\n# a subprocess (shell=False, shlex.split) with a JSON payload on stdin. On\n# stdout the script may return JSON that either blocks the tool call or\n# injects context into the next LLM call.\n#\n# Valid events (mirror hermes_cli.plugins.VALID_HOOKS):\n# pre_tool_call, post_tool_call, pre_llm_call, post_llm_call,\n# pre_api_request, post_api_request, on_session_start, on_session_end,\n# on_session_finalize, on_session_reset, subagent_stop\n#\n# First-use consent: each (event, command) pair prompts once on a TTY, then\n# is persisted to ~/.hermes/shell-hooks-allowlist.json. Non-interactive\n# runs (gateway, cron) need --accept-hooks, HERMES_ACCEPT_HOOKS=1, or the\n# hooks_auto_accept key below.\n#\n# See website/docs/user-guide/features/hooks.md for the full JSON wire\n# protocol and worked examples.\n#\n# hooks:\n# pre_tool_call:\n# - matcher: \"terminal\"\n# command: \"~/.hermes/agent-hooks/block-rm-rf.sh\"\n# timeout: 10\n# post_tool_call:\n# - matcher: \"write_file|patch\"\n# command: \"~/.hermes/agent-hooks/auto-format.sh\"\n# pre_llm_call:\n# - command: \"~/.hermes/agent-hooks/inject-cwd-context.sh\"\n# subagent_stop:\n# - command: \"~/.hermes/agent-hooks/log-orchestration.sh\"\n#\n# hooks_auto_accept: false\n\n\n# =============================================================================\n# Update Behavior\n# =============================================================================\nupdates:\n # Create a full HERMES_HOME zip before every `hermes update`.\n # Backups land in ~/.hermes/backups/ and can be restored with `hermes import`.\n # Off by default because large homes can add minutes to every update.\n pre_update_backup: false\n\n # Number of pre-update backup zips to retain.\n backup_keep: 5\n\n # What non-interactive updates do with local source edits in the Hermes repo.\n # Interactive terminal updates always prompt before restoring the autostash.\n #\n # stash - auto-stash before pull, then auto-restore after success (default)\n # discard - drop the update-created stash after success; use only on managed\n # installs where local source edits should not persist\n non_interactive_local_changes: \"stash\"\n\n\n# =============================================================================\n# Web Dashboard\n# =============================================================================\n# OAuth gate configuration for `hermes dashboard --host `.\n# The bundled Nous Portal plugin reads these on startup; settings here are\n# the canonical surface. Each can be overridden by an environment variable:\n#\n# dashboard.oauth.client_id <- HERMES_DASHBOARD_OAUTH_CLIENT_ID\n# dashboard.oauth.portal_url <- HERMES_DASHBOARD_PORTAL_URL\n# dashboard.public_url <- HERMES_DASHBOARD_PUBLIC_URL\n#\n# Env wins when set to a non-empty value. This is what Fly.io's platform-\n# secret injection uses to push per-deploy client_ids without needing to\n# bake a config.yaml into the image. Empty env values are treated as unset\n# so a provisioned-but-not-populated secret can't shadow a valid entry here.\n#\n# Local dev / on-prem deploys should typically set these via config.yaml\n# (the ~/.hermes/.env file is reserved for API keys and secrets).\n#\n# dashboard:\n# oauth:\n# client_id: \"\" # agent:{instance_id}; Portal provisions this at deploy\n# portal_url: \"\" # blank → default https://portal.nousresearch.com\n#\n# # Force the absolute base URL the OAuth callback (and any other public\n# # URL the dashboard hands to external systems) is built from. Set this\n# # for deploys behind reverse proxies that don't reliably forward\n# # X-Forwarded-Host / X-Forwarded-Proto / X-Forwarded-Prefix (manual\n# # nginx setups, on-prem ingresses, custom-domain Fly deploys without\n# # full proxy header chains).\n# #\n# # When set, the value is the complete authority: scheme + host +\n# # optional path prefix (e.g. \"https://example.com/hermes\"). The OAuth\n# # callback URL becomes \"/auth/callback\" — X-Forwarded-Prefix\n# # is IGNORED on this code path because the operator has explicitly\n# # declared the public URL and we no longer need to guess.\n# #\n# # Leave empty to use the existing proxy-header reconstruction (the\n# # default — works on Fly.io out of the box).\n# #\n# # public_url: \"https://example.com/hermes\"\n#\n# -----------------------------------------------------------------------------\n# Self-hosted OIDC dashboard auth (generic OpenID Connect — Authentik,\n# Keycloak, Zitadel, Authelia, Auth0, Okta, Google, …). Use this INSTEAD of the\n# nous block above when gating the dashboard with your own identity provider.\n# Each setting can be overridden by an environment variable:\n#\n# dashboard.oauth.self_hosted.issuer <- HERMES_DASHBOARD_OIDC_ISSUER\n# dashboard.oauth.self_hosted.client_id <- HERMES_DASHBOARD_OIDC_CLIENT_ID\n# dashboard.oauth.self_hosted.scopes <- HERMES_DASHBOARD_OIDC_SCOPES\n# dashboard.oauth.self_hosted.client_secret <- HERMES_DASHBOARD_OIDC_CLIENT_SECRET\n#\n# dashboard:\n# oauth:\n# provider: self-hosted\n# self_hosted:\n# issuer: \"https://auth.example.com/application/o/hermes/\" # required\n# client_id: \"hermes-dashboard\" # required\n# scopes: \"openid profile email\" # optional\n#\n# # OPTIONAL — set ONLY if your IDP registered the client as\n# # *confidential* (Authentik / Keycloak often default to this). When\n# # set, Hermes authenticates the client at the token endpoint\n# # (client_secret_basic or client_secret_post, auto-selected from the\n# # IDP's discovery doc) IN ADDITION to PKCE. Leave unset for a public\n# # (PKCE-only) client — the common case.\n# #\n# # This is a CREDENTIAL: prefer setting HERMES_DASHBOARD_OIDC_CLIENT_SECRET\n# # in ~/.hermes/.env over putting it here in config.yaml.\n# # client_secret: \"\"\n\n# =============================================================================\n# External secret sources\n# =============================================================================\n# Pull provider credentials from external secret managers at process startup\n# instead of storing them in ~/.hermes/.env. Only the manager's bootstrap\n# credential (e.g. BWS_ACCESS_TOKEN / OP_SERVICE_ACCOUNT_TOKEN) lives in .env\n# (or your shell / desktop session); everything else rotates centrally.\n# Failures never block startup — Hermes warns once and continues with\n# whatever .env already had.\n#\n# Multiple sources can be enabled at once:\n# - \"mapped\" sources (explicit VAR -> ref bindings, e.g. 1Password's env:\n# map) beat \"bulk\" sources (whole-project dumps like Bitwarden BSM)\n# - within a shape, the first source to claim a var wins; later claims\n# are skipped with a startup warning (never a silent clobber)\n# - a source's override_existing lets it beat .env/shell values, but\n# never another secret source's claim\n# Docs: https://hermes-agent.nousresearch.com/docs/user-guide/secrets/\n#\n# secrets:\n# # Optional explicit ordering of enabled sources.\n# # sources: [onepassword, bitwarden]\n#\n# # ---- Bitwarden Secrets Manager (bws CLI) --------------------------------\n# bitwarden:\n# enabled: false\n# access_token_env: BWS_ACCESS_TOKEN # bootstrap token, sourced from .env\n# project_id: \"\" # UUID of the BSM project to sync\n# server_url: \"\" # \"\" = US Cloud; EU/self-hosted URL otherwise\n# cache_ttl_seconds: 300 # 0 disables caching\n# override_existing: true # BSM values win over existing env\n# auto_install: true # lazy-download bws into ~/.hermes/bin\n#\n# # ---- 1Password (op CLI) -------------------------------------------------\n# onepassword:\n# enabled: false\n# # Map env-var names to op:// secret references. Each is resolved with a\n# # single `op read` at startup.\n# env:\n# OPENAI_API_KEY: \"op://Private/OpenAI/api key\"\n# ANTHROPIC_API_KEY: \"op://Private/Anthropic/credential\"\n# account: \"\" # op --account shorthand; \"\" = default\n# service_account_token_env: OP_SERVICE_ACCOUNT_TOKEN # headless auth; unset = desktop session\n# binary_path: \"\" # \"\" = resolve op via PATH; else absolute path\n# cache_ttl_seconds: 300 # 0 disables BOTH cache layers\n# override_existing: true # resolved values win over existing env\n"}, "5c4c0e9d9b87ed6745e9c62e14ccc39a6962a720": {"t": 1785767880.8200455, "n": 73215, "h": "5a08abc52fe916c8", "c": "# Hermes Agent CLI Configuration\n# Copy this file to cli-config.yaml and customize as needed.\n# This file configures the CLI behavior. Environment variables in .env take precedence.\n\n# =============================================================================\n# Model Configuration\n# =============================================================================\nmodel:\n # Default model to use (can be overridden with --model flag)\n # Both \"default\" and \"model\" work as the key name here.\n default: \"anthropic/claude-opus-4.6\"\n \n # Inference provider selection:\n # \"auto\" - Auto-detect from credentials (default)\n # \"openrouter\" - OpenRouter (requires: OPENROUTER_API_KEY or OPENAI_API_KEY)\n # \"nous\" - Nous Portal OAuth (requires: hermes auth add nous)\n # \"nous-api\" - Nous Portal API key (requires: NOUS_API_KEY)\n # \"anthropic\" - Direct Anthropic API (requires: ANTHROPIC_API_KEY)\n # \"openai-codex\" - OpenAI Codex (requires: hermes auth)\n # \"copilot\" - GitHub Copilot / GitHub Models (requires: GITHUB_TOKEN)\n # \"gemini\" - Use Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n # \"zai\" - Use z.ai / ZhipuAI GLM models (requires: GLM_API_KEY)\n # \"kimi-coding\" - Kimi / Moonshot AI (requires: KIMI_API_KEY)\n # \"minimax\" - MiniMax global (requires: MINIMAX_API_KEY)\n # \"minimax-cn\" - MiniMax China (requires: MINIMAX_CN_API_KEY)\n # \"huggingface\" - Hugging Face Inference (requires: HF_TOKEN)\n # \"nvidia\" - NVIDIA NIM / build.nvidia.com (requires: NVIDIA_API_KEY)\n # \"xiaomi\" - Xiaomi MiMo (requires: XIAOMI_API_KEY)\n # \"arcee\" - Arcee AI Trinity models (requires: ARCEEAI_API_KEY)\n # \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY — https://ollama.com/settings)\n # \"kilocode\" - KiloCode gateway (requires: KILOCODE_API_KEY)\n # \"azure-foundry\" - Microsoft Foundry / Azure OpenAI (API key or Entra ID)\n # \"lmstudio\" - LM Studio local server (optional: LM_API_KEY, defaults to http://127.0.0.1:1234/v1)\n #\n # Local servers (LM Studio, Ollama, vLLM, llama.cpp):\n # \"custom\" - Any other OpenAI-compatible endpoint. Set base_url below.\n # Aliases: \"ollama\", \"vllm\", \"llamacpp\" all map to \"custom\".\n # LM Studio is first-class and uses provider: \"lmstudio\".\n # It works with both no-auth and auth-enabled server modes.\n #\n # Can also be overridden for a single invocation with the --provider flag.\n provider: \"auto\"\n \n # API configuration (falls back to OPENROUTER_API_KEY env var)\n # api_key: \"your-key-here\" # Uncomment to set here instead of .env\n base_url: \"https://openrouter.ai/api/v1\"\n\n # Azure Foundry keyless auth example:\n # provider: \"azure-foundry\"\n # base_url: \"https://.openai.azure.com/openai/v1\"\n # auth_mode: \"entra_id\" # DefaultAzureCredential: az login, managed identity, workload identity, etc.\n # default: \"gpt-4o\" # Deployment/model name\n # entra:\n # scope: \"https://ai.azure.com/.default\" # Optional; this is the default.\n\n # ── Token limits — two settings, easy to confuse ──────────────────────────\n #\n # context_length: TOTAL context window (input + output tokens combined).\n # Controls when Hermes compresses history and validates requests.\n # Leave unset — Hermes auto-detects the correct value from the provider.\n # Set manually only when auto-detection is wrong (e.g. a local server with\n # a custom num_ctx, or a proxy that doesn't expose /v1/models).\n #\n # context_length: 131072\n #\n # max_tokens: OUTPUT cap — maximum tokens the model may generate per response.\n # Unrelated to how long your conversation history can be.\n # The OpenAI-standard name \"max_tokens\" is a misnomer; Anthropic's native\n # API has since renamed it \"max_output_tokens\" for clarity.\n # Leave unset to use the model's native output ceiling (recommended).\n # Set only if you want to deliberately limit individual response length.\n #\n# max_tokens: 8192\n\n # ── Custom request headers (optional) ─────────────────────────────────────\n #\n # default_headers: extra HTTP headers sent on every request to an\n # OpenAI-compatible endpoint. User values take precedence over the\n # provider/SDK defaults, so this is the supported way to override the\n # OpenAI Python SDK's identifying headers (User-Agent: OpenAI/Python ...,\n # X-Stainless-*) when a custom provider sits behind a gateway/WAF that\n # rejects them — e.g. an upstream that returns \"502 Upstream access\n # forbidden\" for the SDK default User-Agent but accepts a plain one.\n # Applies on the OpenAI wire only (not native Anthropic / Bedrock).\n #\n # default_headers:\n # User-Agent: \"curl/8.7.1\"\n #\n # extra_headers: accepted as an alias of default_headers (merged, with\n # extra_headers winning when both are set) — matches the per-provider\n # extra_headers key below.\n #\n # Per-provider variant: named providers / custom_providers entries accept an\n # extra_headers dict scoped to that endpoint only — for reverse proxies,\n # gateways, or custom auth (e.g. Cloudflare Access service tokens).\n # Merged onto SDK/provider defaults with the entry's values winning.\n # Header values are treated as secrets and are never logged.\n #\n # providers:\n # my-proxy:\n # base_url: \"https://llm.internal.example.com/v1\"\n # key_env: \"MY_PROXY_API_KEY\"\n # extra_headers:\n # CF-Access-Client-Id: \"xxxx.access\"\n # CF-Access-Client-Secret: \"${CF_ACCESS_SECRET}\"\n # X-Client-Name: \"hermes-agent\"\n\n# Named provider overrides (optional)\n# Use this for per-provider request timeouts, non-stream stale timeouts,\n# and per-model exceptions.\n# Applies to the primary turn client on every api_mode (OpenAI-wire, native\n# Anthropic, and Anthropic-compatible providers), the fallback chain, and\n# client rebuilds during credential rotation. For OpenAI-wire chat\n# completions (streaming and non-streaming) the configured value is also\n# used as the per-request ``timeout=`` kwarg so it wins over the legacy\n# HERMES_API_TIMEOUT env var (which still applies when no config is set).\n# ``stale_timeout_seconds`` controls the non-streaming stale-call detector and\n# wins over the legacy HERMES_API_CALL_STALE_TIMEOUT env var. Leaving these\n# unset keeps the legacy defaults (HERMES_API_TIMEOUT=1800s,\n# HERMES_API_CALL_STALE_TIMEOUT=90s, native Anthropic 900s). The\n# implicit non-stream stale detector is auto-disabled for local endpoints\n# and can scale upward for very large contexts.\n#\n# Not currently wired for AWS Bedrock (bedrock_converse + AnthropicBedrock\n# SDK paths) — those use boto3 with its own timeout configuration.\n#\n# providers:\n# ollama-local:\n# request_timeout_seconds: 300 # Longer timeout for local cold-starts\n# stale_timeout_seconds: 900 # Explicitly re-enable stale detection on local endpoints\n# anthropic:\n# request_timeout_seconds: 30 # Fast-fail cloud requests\n# models:\n# claude-opus-4.6:\n# timeout_seconds: 600 # Longer timeout for extended-thinking Opus calls\n# openai-codex:\n# models:\n# gpt-5.4:\n# stale_timeout_seconds: 1800 # Longer non-stream stale timeout for slow large-context turns\n\n# =============================================================================\n# OpenRouter Provider Routing (only applies when using OpenRouter)\n# =============================================================================\n# Control how requests are routed across providers on OpenRouter.\n# See: https://openrouter.ai/docs/guides/routing/provider-selection\n#\n# provider_routing:\n# # Sort strategy: \"price\" (default), \"throughput\", or \"latency\"\n# # Append :nitro to model name for a shortcut to throughput sorting.\n# sort: \"throughput\"\n#\n# # Only allow these providers (provider slugs from OpenRouter)\n# # only: [\"anthropic\", \"google\"]\n#\n# # Skip these providers entirely\n# # ignore: [\"deepinfra\", \"fireworks\"]\n#\n# # Try providers in this order (overrides default load balancing)\n# # order: [\"anthropic\", \"google\", \"together\"]\n#\n# # Require providers to support all parameters in your request\n# # require_parameters: true\n#\n# # Data policy: \"allow\" (default) or \"deny\" to exclude providers that may store data\n# # data_collection: \"deny\"\n\n# =============================================================================\n# OpenRouter Response Caching (only applies when using OpenRouter)\n# =============================================================================\n# Cache identical API responses at the OpenRouter edge for free instant replays.\n# When enabled, identical requests (same model, messages, parameters) return\n# cached responses with zero billing. Separate from Anthropic prompt caching.\n# See: https://openrouter.ai/docs/guides/features/response-caching\n#\n# openrouter:\n# response_cache: true # Enable response caching (default: true)\n# response_cache_ttl: 300 # Cache TTL in seconds, 1-86400 (default: 300)\n\n# =============================================================================\n# Git Worktree Isolation\n# =============================================================================\n# When enabled, each CLI session creates an isolated git worktree so multiple\n# agents can work on the same repo concurrently without file collisions.\n# Equivalent to always passing --worktree / -w on the command line.\n#\n# worktree: true # Always create a worktree when in a git repo\n# worktree: false # Default — only create when -w flag is passed\n#\n# By default a new worktree branches from the freshly-fetched remote tip\n# (the current branch's upstream, else the remote's default branch) so it\n# starts current with the project instead of from the local clone's\n# (possibly stale) HEAD. Set worktree_sync: false to branch from local HEAD\n# instead — useful when offline or when you deliberately want the clone's\n# exact current state as the base.\n#\n# worktree_sync: true # Default — branch from the fetched remote tip\n# worktree_sync: false # Branch from local HEAD (offline / pinned base)\n\n# =============================================================================\n# Terminal Tool Configuration\n# =============================================================================\n# Choose ONE of the following terminal configurations by uncommenting it.\n# The terminal tool executes commands in the specified environment.\n\n# -----------------------------------------------------------------------------\n# OPTION 1: Local execution (default)\n# Commands run directly on your machine in the current directory\n# -----------------------------------------------------------------------------\n# Working directory behavior:\n# - CLI (`hermes` command): Uses \".\" (current directory where you run hermes)\n# - Gateway/messaging/cron: Uses terminal.cwd here; legacy .env cwd values are deprecated\nterminal:\n backend: \"local\"\n cwd: \".\" # For local backend: \".\" = current directory. Ignored for remote backends unless a backend documents otherwise.\n timeout: 180\n # HOME policy for tool subprocesses:\n # auto - default: host uses your real HOME; containers use HERMES_HOME/home\n # real - force your real OS-user HOME\n # profile - force HERMES_HOME/home for strict per-profile CLI config isolation\n home_mode: \"auto\"\n docker_mount_cwd_to_workspace: false # SECURITY: off by default. Opt in to mount the launch cwd into Docker /workspace.\n lifetime_seconds: 300\n # sudo_password: \"hunter2\" # Optional: pipe a sudo password via sudo -S. SECURITY WARNING: plaintext.\n # sudo_password: \"\" # Explicit empty password: try empty and never open the interactive sudo prompt.\n\n# -----------------------------------------------------------------------------\n# OPTION 2: SSH remote execution\n# Commands run on a remote server - agent code stays local (sandboxed)\n# Great for: keeping agent isolated from its own code, using powerful remote hardware\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"ssh\"\n# cwd: \"/home/myuser/project\" # Path on the REMOTE server\n# timeout: 180\n# lifetime_seconds: 300\n# ssh_host: \"my-server.example.com\"\n# ssh_user: \"myuser\"\n# ssh_port: 22\n# ssh_key: \"~/.ssh/id_rsa\" # Optional - uses ssh-agent if not specified\n\n# -----------------------------------------------------------------------------\n# OPTION 3: Docker container\n# Commands run in an isolated Docker container\n# Great for: reproducible environments, testing, isolation\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"docker\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /)\n# timeout: 180\n# lifetime_seconds: 300\n# docker_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# docker_mount_cwd_to_workspace: true # Explicit opt-in: mount your launch cwd into /workspace\n# # Optional: run the container as your host user's uid:gid so files written\n# # into bind-mounted dirs are owned by you, not root. Drops SETUID/SETGID\n# # caps too since no gosu privilege drop is needed. Leave off if your\n# # chosen docker_image expects to start as root.\n# docker_run_as_host_user: true\n# # Optional: explicitly forward selected env vars into Docker.\n# # These values come from your current shell first, then ~/.hermes/.env.\n# # Warning: anything forwarded here is visible to commands run in the container.\n# docker_forward_env:\n# - \"GITHUB_TOKEN\"\n# - \"NPM_TOKEN\"\n# # Optional: extra flags passed verbatim to docker run (appended after security defaults).\n# # Useful for adding capabilities (e.g. apt installs needing SETUID) or custom options.\n# # Example: add a Linux capability not included by default\n# # docker_extra_args:\n# # - \"--cap-add\"\n# # - \"SETUID\"\n\n# -----------------------------------------------------------------------------\n# OPTION 4: Singularity/Apptainer container\n# Commands run in a Singularity container (common in HPC environments)\n# Great for: HPC clusters, shared compute environments\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"singularity\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# singularity_image: \"docker://nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 5: Modal cloud execution\n# Commands run on Modal's cloud infrastructure\n# Great for: GPU access, scalable compute, serverless execution\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"modal\"\n# cwd: \"/workspace\" # Path INSIDE the sandbox (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# modal_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 6: Daytona cloud execution\n# Commands run in Daytona cloud sandboxes\n# Great for: Cloud dev environments, persistent workspaces, team collaboration\n# Requires: pip install daytona, DAYTONA_API_KEY env var\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"daytona\"\n# cwd: \"~\"\n# timeout: 180\n# lifetime_seconds: 300\n# daytona_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# container_disk: 10240 # Daytona max is 10GB per sandbox\n\n#\n# --- Container resource limits (docker, singularity, modal, daytona -- ignored for local/ssh) ---\n# These settings apply to all container backends. They control the resources\n# allocated to the sandbox and whether its filesystem persists across sessions.\n container_cpu: 1 # CPU cores\n container_memory: 5120 # Memory in MB (5120 = 5GB)\n container_disk: 51200 # Disk in MB (51200 = 50GB)\n container_persistent: true # Persist filesystem across sessions (false = ephemeral)\n\n# -----------------------------------------------------------------------------\n# SUDO SUPPORT (works with ALL backends above)\n# -----------------------------------------------------------------------------\n# Add sudo_password to any terminal config above to enable sudo commands.\n# The password is piped via `sudo -S`. Works with local, ssh, docker, etc.\n#\n# SECURITY WARNING: Password stored in plaintext!\n#\n# INTERACTIVE PROMPT: If sudo_password is unset and the CLI is running,\n# you'll be prompted to enter your password when sudo is needed:\n# - 45-second timeout (auto-skips if no input)\n# - Press Enter to skip (command fails gracefully)\n# - Password is hidden while typing\n# - Password is cached for the session\n#\n# EMPTY PASSWORDS: Setting sudo_password to an explicit empty string is different\n# from leaving it unset. Hermes will try an empty password via `sudo -S` and\n# will not open the interactive prompt. This is useful for passwordless sudo,\n# Touch ID sudo setups, and environments where prompting is just noise.\n#\n# ALTERNATIVES:\n# - SSH backend: Configure passwordless sudo on the remote server\n# - Containers: Run as root inside the container (no sudo needed)\n# - Local: Configure /etc/sudoers for specific commands\n#\n# Example (add to your terminal section):\n# sudo_password: \"your-password-here\"\n\n# =============================================================================\n# Security Scanning (tirith)\n# =============================================================================\n# Optional pre-exec command security scanning via tirith.\n# Detects homograph URLs, pipe-to-shell, terminal injection, env manipulation.\n# Install: brew install sheeki03/tap/tirith\n# Docs: https://github.com/sheeki03/tirith\n#\n# security:\n# tirith_enabled: true # Enable/disable tirith scanning\n# tirith_path: \"tirith\" # Path to tirith binary (supports ~ expansion)\n# tirith_timeout: 5 # Scan timeout in seconds\n# tirith_fail_open: true # Allow commands if tirith unavailable\n\n# =============================================================================\n# Browser Tool Configuration\n# =============================================================================\nbrowser:\n # Inactivity timeout in seconds - browser sessions are automatically closed\n # after this period of no activity between agent loops (default: 120 = 2 minutes)\n inactivity_timeout: 120\n\n# =============================================================================\n# Tool Loop Guardrails\n# =============================================================================\n# Soft warnings are enabled by default. They append guidance to repeated failed\n# or non-progressing tool results but still let the tool execute. Hard stops are\n# opt-in circuit breakers for autonomous/cron sessions where stopping a loop is\n# preferable to spending the full iteration budget.\ntool_loop_guardrails:\n warnings_enabled: true\n hard_stop_enabled: false\n warn_after:\n exact_failure: 2\n same_tool_failure: 3\n idempotent_no_progress: 2\n hard_stop_after:\n exact_failure: 5\n same_tool_failure: 8\n idempotent_no_progress: 5\n\n# =============================================================================\n# Context Compression (Auto-shrinks long conversations)\n# =============================================================================\n# When conversation approaches model's context limit, middle turns are\n# automatically summarized to free up space while preserving important context.\n#\n# HOW IT WORKS:\n# 1. Tracks actual token usage from API responses (not estimates)\n# 2. When prompt_tokens >= threshold% of model's context_length, triggers compression\n# 3. Protects first 3 turns (system prompt, initial request, first response)\n# 4. Protects last N turns (default 20 messages = ~10 full turns of recent context)\n# 5. Summarizes middle turns using a fast/cheap model\n# 6. Inserts summary as a user message, continues conversation seamlessly\n#\n# Post-compression tail budget is target_ratio × threshold × context_length:\n# 200K context, threshold 0.50, ratio 0.20 → 20K tokens of recent tail preserved\n# 1M context, threshold 0.50, ratio 0.20 → 100K tokens of recent tail preserved\n#\ncompression:\n # Enable automatic context compression (default: true)\n # Set to false if you prefer to manage context manually or want errors on overflow\n enabled: true\n \n # Trigger compression at this % of model's context limit (default: 0.50 = 50%)\n # Lower values = more aggressive compression, higher values = compress later\n threshold: 0.50\n \n # Fraction of the threshold to preserve as recent tail (default: 0.20 = 20%)\n # e.g. 20% of 50% threshold = 10% of total context kept as recent messages.\n # Summary output is separately capped at 12K tokens (Gemini output limit).\n # Range: 0.10 - 0.80\n target_ratio: 0.20\n\n # Number of most-recent messages to always preserve (default: 20 ≈ 10 full turns)\n # Higher values keep more recent conversation intact at the cost of more aggressive\n # compression of older turns.\n protect_last_n: 20\n\n # Number of non-system messages to protect at the head of the transcript, in\n # ADDITION to the system prompt (which is always implicitly protected).\n # Head messages are NEVER summarized — they survive every compression\n # indefinitely. This gives stable early context for short/medium sessions,\n # but in long-running sessions that rely on rolling compaction the pinned\n # opening turns may not match how you want the session framed over time.\n # Set to 0 to preserve ONLY the system prompt (plus the rolling summary\n # and recent tail) — the cleanest configuration for long-running sessions.\n # Default 3 preserves the system prompt plus the first three non-system\n # head messages, matching the pre-feature behaviour.\n protect_first_n: 3\n\n # To pin a specific model/provider for compression summaries, use the\n # auxiliary section below (auxiliary.compression.provider / model).\n\n# =============================================================================\n# Anthropic prompt caching TTL\n# =============================================================================\n# When prompt caching is active (Claude via OpenRouter or native Anthropic),\n# Anthropic supports two TTL tiers for cached prefixes: \"5m\" (default) and\n# \"1h\". Other values are ignored and \"5m\" is used.\n#\nprompt_caching:\n cache_ttl: \"5m\" # use \"1h\" for long sessions with pauses between turns\n\n# =============================================================================\n# Auxiliary Models (Advanced — Experimental)\n# =============================================================================\n# Hermes uses lightweight \"auxiliary\" models for side tasks: image analysis,\n# browser screenshot analysis, web page summarization, TTS audio-tag insertion,\n# and context compression.\n#\n# By default these use Gemini Flash via OpenRouter or Nous Portal and are\n# auto-detected from your credentials. You do NOT need to change anything\n# here for normal usage.\n#\n# WARNING: Overriding these with providers other than OpenRouter or Nous Portal\n# is EXPERIMENTAL and may not work. Not all models/providers support vision,\n# produce usable summaries, or accept the same API format. Change at your own\n# risk — if things break, reset to \"auto\" / empty values.\n#\n# Each task has its own provider + model pair so you can mix providers.\n# For example: OpenRouter for vision (needs multimodal), but your main\n# local endpoint for compression (just needs text).\n#\n# Provider options:\n# \"auto\" - Best available: OpenRouter → Nous Portal → main endpoint (default)\n# \"openrouter\" - Force OpenRouter (requires OPENROUTER_API_KEY)\n# \"nous\" - Force Nous Portal (requires: hermes auth add nous)\n# \"gemini\" - Force Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n# \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY)\n# \"codex\" - Force Codex OAuth (requires: hermes model → Codex).\n# Uses gpt-5.3-codex which supports vision.\n# \"main\" - Use your custom endpoint (OPENAI_BASE_URL + OPENAI_API_KEY).\n# Works with OpenAI API, local models, or any OpenAI-compatible\n# endpoint. Also falls back to Codex OAuth and API-key providers.\n#\n# Model: leave empty to use the provider's default. When empty, OpenRouter\n# uses \"google/gemini-3-flash-preview\" and Nous uses \"gemini-3-flash\".\n# Other providers pick a sensible default automatically.\n#\n# auxiliary:\n# # Image analysis: vision_analyze tool + browser screenshots\n# vision:\n# provider: \"auto\"\n# model: \"\" # e.g. \"google/gemini-2.5-flash\", \"openai/gpt-4o\"\n# timeout: 30 # LLM API call timeout (seconds)\n# download_timeout: 30 # Image HTTP download timeout (seconds)\n# # Increase for slow connections or self-hosted image servers\n#\n# # Web page scraping / summarization + browser page text extraction\n# web_extract:\n# provider: \"auto\"\n# model: \"\"\n#\n# # Gemini 3.1 TTS hidden audio-tag insertion\n# tts_audio_tags:\n# provider: \"auto\" # empty model = your main chat model\n# model: \"\"\n# timeout: 30\n#\n# # Session search — summarizes matching past sessions\n# session_search:\n# provider: \"auto\"\n# model: \"\"\n# timeout: 30\n# max_concurrency: 3 # Limit parallel summaries to reduce request-burst 429s\n# extra_body: {} # Provider-specific OpenAI-compatible request fields\n# # Example for providers that support request-body\n# # reasoning controls:\n# # extra_body:\n# # enable_thinking: false\n# # Some vLLM/Qwen deployments expect this nested:\n# # extra_body:\n# # chat_template_kwargs:\n# # enable_thinking: false\n\n# =============================================================================\n# Persistent Memory\n# =============================================================================\n# Bounded curated memory injected into the system prompt every session.\n# Two stores: MEMORY.md (agent's notes) and USER.md (user profile).\n# Character limits keep the memory small and focused. The agent manages\n# pruning -- when at the limit, it must consolidate or replace entries.\n# Disabled by default in batch_runner.\n#\nmemory:\n # Agent's personal notes: environment facts, conventions, things learned\n memory_enabled: true\n \n # User profile: preferences, communication style, expectations\n user_profile_enabled: true\n \n # Character limits (~2.75 chars per token, model-independent)\n memory_char_limit: 2200 # ~800 tokens\n user_char_limit: 1375 # ~500 tokens\n\n # Periodic memory nudge: remind the agent to consider saving memories\n # every N user turns. Set to 0 to disable. Only active when memory is enabled.\n nudge_interval: 10 # Nudge every 10 user turns (0 = disabled)\n\n # Memory flush: give the agent one turn to save memories before context is\n # lost (compression, /new, /reset, exit). Set to 0 to disable.\n # For exit/reset, only fires if the session had at least this many user turns.\n flush_min_turns: 6 # Min user turns to trigger flush on exit/reset (0 = disabled)\n\n# =============================================================================\n# Session Reset Policy (Messaging Platforms)\n# =============================================================================\n# Controls when messaging sessions (Telegram, Discord, WhatsApp, Slack) are\n# automatically cleared. Without resets, conversation context grows indefinitely\n# which increases API costs with every message.\n#\n# When a reset triggers, the agent first saves important information to its\n# persistent memory — but the conversation context is wiped. The agent starts\n# fresh but retains learned facts via its memory system.\n#\n# Users can always manually reset with /reset or /new in chat.\n#\n# Modes:\n# \"both\" - Reset on EITHER inactivity timeout or daily boundary (recommended)\n# \"idle\" - Reset only after N minutes of inactivity\n# \"daily\" - Reset only at a fixed hour each day\n# \"none\" - Never auto-reset; context lives until /reset or compression kicks in\n#\n# When a reset triggers, the agent gets one turn to save important memories and\n# skills before the context is wiped. Persistent memory carries across sessions.\n#\nsession_reset:\n mode: both # \"both\", \"idle\", \"daily\", or \"none\"\n idle_minutes: 1440 # Inactivity timeout in minutes (default: 1440 = 24 hours)\n at_hour: 4 # Daily reset hour, 0-23 local time (default: 4 AM)\n\n# Maximum number of simultaneously active chat sessions across CLI, TUI,\n# dashboard chat, and messaging gateway. Set to null, 0, or omit to allow\n# unlimited concurrent sessions. When the limit is reached, new sessions get a\n# clean error while existing active sessions keep their normal behavior. This\n# top-level key takes precedence over gateway.max_concurrent_sessions. The cap\n# is a best-effort single-host/profile runtime guard; Hermes fails open if the\n# local runtime lease registry cannot be read or locked.\nmax_concurrent_sessions: null\n\n# When true, group/channel chats use one session per participant when the platform\n# provides a user ID. This is the secure default and prevents users in the same\n# room from sharing context, interrupts, and token costs. Set false only if you\n# explicitly want one shared \"room brain\" per group/channel.\ngroup_sessions_per_user: true\n\n# ─────────────────────────────────────────────────────────────────────────────\n# API Server — per-client model routing\n# ─────────────────────────────────────────────────────────────────────────────\n# Route different API clients to different models/providers on a single\n# Hermes deployment. Clients choose a backend by sending a specific string\n# as the OpenAI ``model`` field. Unmapped model values fall back to the\n# global model configured in the ``model:`` section above, and an explicit\n# session /model override always wins over a route.\n#\n# Configure via the ``platforms.api_server.extra.model_routes`` gateway\n# config block:\n#\n# platforms:\n# api_server:\n# enabled: true\n# extra:\n# key: \"your-api-server-secret\"\n# model_routes:\n# # Xiaozhi clients send model=\"minimax-m2\" → routed to MiniMax via OpenRouter\n# minimax-m2:\n# model: \"minimax/minimax-m1\"\n# provider: \"openrouter\" # optional — overrides global provider\n# # api_key: \"sk-...\" # optional — per-route UPSTREAM provider\n# # key (NOT caller auth; never logged)\n# # base_url: \"https://...\" # optional — per-route base URL\n# # GPT clients keep their own alias\n# gpt-5:\n# model: \"openai/gpt-5\"\n# provider: \"openrouter\"\n#\n# Configured aliases are automatically listed by GET /v1/models so clients\n# can discover them without manual coordination. Caller authentication is\n# unchanged: every request still authenticates with the global API server\n# key (``extra.key`` / API_SERVER_KEY).\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Gateway Streaming\n# ─────────────────────────────────────────────────────────────────────────────\n# Stream tokens to messaging platforms in real-time. The bot sends a message\n# on first token, then progressively edits it as more tokens arrive.\n# Disabled by default — enable to try the streaming UX on Telegram/Discord/Slack.\n# For Telegram, partial edits are sent as plain text and only the final edit uses MarkdownV2.\nstreaming:\n enabled: false\n # transport: edit # \"edit\" = progressive editMessageText\n # edit_interval: 0.3 # seconds between message edits\n # buffer_threshold: 40 # chars before forcing an edit flush\n # cursor: \" ▉\" # cursor shown during streaming\n\n# =============================================================================\n# Skills Configuration\n# =============================================================================\n# Skills are reusable procedures the agent can load and follow. The agent can\n# also create new skills after completing complex tasks.\n#\nskills:\n # Nudge the agent to create skills after complex tasks.\n # Every N tool-calling iterations, remind the model to consider saving a skill.\n # Set to 0 to disable.\n creation_nudge_interval: 15\n\n # External skill directories — share skills across tools/agents without\n # copying them into ~/.hermes/skills/. Each path is expanded (~ and ${VAR})\n # and resolved to an absolute path. External dirs are read-only: skill\n # creation always writes to ~/.hermes/skills/. Local skills take precedence\n # when names collide.\n # external_dirs:\n # - ~/.agents/skills\n # - /home/shared/team-skills\n\n# =============================================================================\n# Agent Behavior\n# =============================================================================\nagent:\n # Maximum tool-calling iterations per conversation\n # Higher = more room for complex tasks, but costs more tokens\n # Recommended: 20-30 for focused tasks, 50-100 for open exploration\n max_turns: 60\n\n # Inactivity timeout for gateway agent runs (seconds, 0 = unlimited).\n # The agent can run indefinitely when actively calling tools or receiving\n # API responses. Only fires after the agent has been idle for this duration.\n # gateway_timeout: 1800\n\n # Staged warning: send a warning before escalating to full timeout.\n # Fires once per run when inactivity reaches this threshold (seconds).\n # Set to 0 to disable the warning.\n # gateway_timeout_warning: 900\n\n # Graceful drain timeout for gateway stop/restart (seconds).\n # Default 0 = no drain: a restart interrupts in-flight agents immediately,\n # cleans up, and exits. Set a positive value only if you want a grace\n # window on /restart, and keep it well under systemd's TimeoutStopSec.\n # restart_drain_timeout: 0\n\n # Max app-level retry attempts for API errors (connection drops, provider\n # timeouts, 5xx, etc.) before the agent surfaces the failure. Lower this\n # to 1 if you use fallback providers and want fast failover on flaky\n # primaries (default 3). The OpenAI SDK does its own low-level retries\n # underneath this wrapper — this is the Hermes-level loop.\n # api_max_retries: 3\n\n # After the agent edits code without fresh passing verification, nudge it to\n # verify before finishing. The default \"auto\" enables it on interactive\n # coding surfaces (CLI, TUI, desktop) and programmatic callers, and disables\n # it on conversational messaging surfaces (Telegram, Discord, etc.) where the\n # verification summary would reach a human as chat noise. Set true or false to\n # force it on or off; the HERMES_VERIFY_ON_STOP env var (1/0) takes precedence.\n # verify_on_stop: auto\n\n # Standing operator instructions for the coding posture (when Hermes is in a\n # code workspace). Appended to the coding brief as an extra system block, so\n # you can pin project-wide workflow rules without editing the shipped brief.\n # Accepts a string or a list of strings. Takes effect next session.\n # coding_instructions:\n # - \"For UI work, don't run tsc/lint until I approve the look.\"\n # - \"Clean the diff before you commit and push.\"\n\n # When verify-on-stop finds edited code without fresh verification evidence,\n # append guidance for creative UI work (avoid broad tsc/lint/test before visual\n # approval) and clean-diff expectations. Set false to keep that nudge terse.\n # verify_guidance: true\n\n # A `pre_verify` hook (plugin or shell, see Event Hooks docs) can keep the\n # agent going one more turn to verify/clean before finishing. This caps how\n # many times one turn may be nudged to continue, so a hook can't trap the loop.\n # Default 3.\n # max_verify_nudges: 3\n\n # Enable verbose logging\n verbose: false\n \n # Reasoning effort level (OpenRouter and Nous Portal)\n # Controls how much \"thinking\" the model does before responding.\n # Options: \"xhigh\" (max), \"high\", \"medium\", \"low\", \"minimal\", \"none\" (disable)\n reasoning_effort: \"medium\"\n \n # Predefined personalities (use with /personality command)\n personalities:\n helpful: \"You are a helpful, friendly AI assistant.\"\n concise: \"You are a concise assistant. Keep responses brief and to the point.\"\n technical: \"You are a technical expert. Provide detailed, accurate technical information.\"\n creative: \"You are a creative assistant. Think outside the box and offer innovative solutions.\"\n teacher: \"You are a patient teacher. Explain concepts clearly with examples.\"\n kawaii: \"You are a kawaii assistant! Use cute expressions like (◕‿◕), ★, ♪, and ~! Add sparkles and be super enthusiastic about everything! Every response should feel warm and adorable desu~! ヽ(>∀<☆)ノ\"\n catgirl: \"You are Neko-chan, an anime catgirl AI assistant, nya~! Add 'nya' and cat-like expressions to your speech. Use kaomoji like (=^・ω・^=) and ฅ^•ﻌ•^ฅ. Be playful and curious like a cat, nya~!\"\n pirate: \"Arrr! Ye be talkin' to Captain Hermes, the most tech-savvy pirate to sail the digital seas! Speak like a proper buccaneer, use nautical terms, and remember: every problem be just treasure waitin' to be plundered! Yo ho ho!\"\n shakespeare: \"Hark! Thou speakest with an assistant most versed in the bardic arts. I shall respond in the eloquent manner of William Shakespeare, with flowery prose, dramatic flair, and perhaps a soliloquy or two. What light through yonder terminal breaks?\"\n surfer: \"Duuude! You're chatting with the chillest AI on the web, bro! Everything's gonna be totally rad. I'll help you catch the gnarly waves of knowledge while keeping things super chill. Cowabunga! 🤙\"\n noir: \"The rain hammered against the terminal like regrets on a guilty conscience. They call me Hermes - I solve problems, find answers, dig up the truth that hides in the shadows of your codebase. In this city of silicon and secrets, everyone's got something to hide. What's your story, pal?\"\n uwu: \"hewwo! i'm your fwiendwy assistant uwu~ i wiww twy my best to hewp you! *nuzzles your code* OwO what's this? wet me take a wook! i pwomise to be vewy hewpful >w<\"\n philosopher: \"Greetings, seeker of wisdom. I am an assistant who contemplates the deeper meaning behind every query. Let us examine not just the 'how' but the 'why' of your questions. Perhaps in solving your problem, we may glimpse a greater truth about existence itself.\"\n hype: \"YOOO LET'S GOOOO!!! 🔥🔥🔥 I am SO PUMPED to help you today! Every question is AMAZING and we're gonna CRUSH IT together! This is gonna be LEGENDARY! ARE YOU READY?! LET'S DO THIS! 💪😤🚀\"\n\n# =============================================================================\n# Toolsets\n# =============================================================================\n# Control which tools the agent has access to.\n# Use `hermes tools` to interactively enable/disable tools per platform.\n\n# =============================================================================\n# Platform Toolsets (per-platform tool configuration)\n# =============================================================================\n# Override which toolsets are available on each platform.\n# If a platform isn't listed here, its built-in default is used.\n#\n# You can use EITHER:\n# - A preset like \"hermes-cli\" or \"hermes-telegram\" (curated tool set)\n# - A list of individual toolsets to compose your own (see list below)\n#\n# Supported platform keys: cli, telegram, discord, whatsapp, slack, qqbot, teams, google_chat\n#\n# Examples:\n#\n# # Use presets (same as defaults):\n# platform_toolsets:\n# cli: [hermes-cli]\n# telegram: [hermes-telegram]\n#\n# # Custom: give Telegram only web + terminal + file + planning:\n# platform_toolsets:\n# telegram: [web, terminal, file, todo]\n#\n# # Custom: CLI without browser or image gen:\n# platform_toolsets:\n# cli: [web, terminal, file, skills, todo, tts, cronjob]\n#\n# # Restrictive: Discord gets read-only tools only:\n# platform_toolsets:\n# discord: [web, vision, skills, todo]\n#\n# If not set, defaults are:\n# cli: hermes-cli (everything + cronjob management)\n# telegram: hermes-telegram (terminal, file, web, vision, image, tts, browser, skills, todo, cronjob, messaging)\n# discord: hermes-discord (same as telegram)\n# whatsapp: hermes-whatsapp (same as telegram)\n# slack: hermes-slack (same as telegram)\n# signal: hermes-signal (same as telegram)\n# homeassistant: hermes-homeassistant (same as telegram)\n# qqbot: hermes-qqbot (same as telegram)\n# teams: hermes-teams (same as telegram)\n# google_chat: hermes-google_chat (same as telegram)\n#\nplatform_toolsets:\n cli: [hermes-cli]\n telegram: [hermes-telegram]\n discord: [hermes-discord]\n whatsapp: [hermes-whatsapp]\n slack: [hermes-slack]\n signal: [hermes-signal]\n homeassistant: [hermes-homeassistant]\n qqbot: [hermes-qqbot]\n yuanbao: [hermes-yuanbao]\n teams: [hermes-teams]\n google_chat: [hermes-google_chat]\n\n# =============================================================================\n# Gateway Platform Settings\n# =============================================================================\n# Optional per-platform messaging settings.\n# Platform-specific knobs live under `extra`.\n#\n# platforms:\n# telegram:\n# reply_to_mode: \"first\" # off | first | all\n# # guest_mode lets explicit @mentions from non-allowlisted groups through.\n# # Default false; ordinary messages, replies, and regex wake words stay blocked.\n# guest_mode: false\n# # allowed_chats: [\"-1001234567890\"]\n# extra:\n# disable_link_previews: false # Set true to suppress Telegram URL previews in bot messages\n# rich_messages: false # Bot API 10.1 rich messages (tables/task lists/details/math); default false for copyable legacy MarkdownV2, set true to opt in\n# rich_drafts: false # Experimental rich draft previews during Telegram DM streaming; default false because Telegram Desktop/macOS can visually overlay draft frames\n# command_menu:\n# # Telegram allows up to 100 BotCommands; Hermes defaults to 60 so\n# # all built-in commands plus common skill commands stay visible\n# # while remaining under Telegram's payload-size limit. Clamped 1..100.\n# max_commands: 60\n# # prepend = user priority first, then Hermes defaults\n# # append = Hermes defaults first, then user priority\n# # replace = only the list below defines priority\n# priority_mode: prepend\n# priority:\n# - my_plugin_command\n#\n# Discord-specific settings (config.yaml top-level, not under platforms:):\n#\n# discord:\n# require_mention: true # Require @mention in server channels (default: true)\n# auto_thread: true # Auto-create thread on @mention (default: true)\n# free_response_channels: \"\" # Channel IDs where no mention is needed\n# reactions: true # Show processing reactions (default: true)\n# history_backfill: true # Recover missed channel messages on mention (default: true)\n# history_backfill_limit: 50 # Max messages to scan backwards (default: 50)\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Available toolsets (use these names in platform_toolsets or the toolsets list)\n#\n# Run `hermes chat --list-toolsets` to see all toolsets and their tools.\n# Run `hermes chat --list-tools` to see every individual tool with descriptions.\n# ─────────────────────────────────────────────────────────────────────────────\n#\n# INDIVIDUAL TOOLSETS (compose your own):\n# web - web_search, web_extract\n# search - web_search only (no scraping)\n# terminal - terminal, process\n# file - read_file, write_file, patch, search\n# browser - browser_navigate, browser_snapshot, browser_click, browser_type,\n# browser_scroll, browser_back, browser_press,\n# browser_get_images, browser_vision (requires BROWSERBASE_API_KEY)\n# vision - vision_analyze (requires OPENROUTER_API_KEY)\n# image_gen - image_generate (requires FAL_KEY)\n# skills - skills_list, skill_view\n# skills_hub - skill_hub (search/install/manage from online registries — user-driven only)\n# todo - todo (in-memory task planning, no deps)\n# tts - text_to_speech (Edge TTS free, or ELEVENLABS/OPENAI/MINIMAX/MISTRAL key)\n# cronjob - cronjob (create/list/update/pause/resume/run/remove scheduled tasks)\n#\n# PRESETS (curated bundles):\n# hermes-cli - All of the above except send_message\n# hermes-telegram - terminal, file, web, vision, image_gen, tts, browser,\n# skills, todo, cronjob, send_message\n# hermes-discord - Same as hermes-telegram\n# hermes-whatsapp - Same as hermes-telegram\n# hermes-slack - Same as hermes-telegram\n#\n# COMPOSITE:\n# debugging - terminal + web + file\n# safe - web + vision (no terminal access)\n# all - Everything available\n#\n# web - Web search and content extraction (web_search, web_extract)\n# search - Web search only, no scraping (web_search)\n# terminal - Command execution and process management (terminal, process)\n# file - File operations: read, write, patch, search\n# browser - Full browser automation (navigate, click, type, screenshot, etc.)\n# vision - Image analysis (vision_analyze)\n# image_gen - Image generation with FLUX (image_generate)\n# skills - Load skill documents (skills_list, skill_view)\n# todo - Task planning and tracking for multi-step work\n# memory - Persistent memory across sessions (personal notes + user profile)\n# session_search - Search and recall past conversations (FTS5 + Gemini Flash summarization)\n# tts - Text-to-speech (Edge TTS free, ElevenLabs, OpenAI, MiniMax, Mistral)\n# cronjob - Schedule and manage automated tasks (CLI-only)\n#\n# Composite toolsets:\n# debugging - terminal + web + file (for troubleshooting)\n# safe - web + vision (no terminal access)\n\n# NOTE: The top-level \"toolsets\" key is deprecated and ignored.\n# Tool configuration is managed per-platform via platform_toolsets above.\n# Use `hermes tools` to configure interactively, or edit platform_toolsets directly.\n#\n# CLI override: hermes chat --toolsets terminal,web,file\n\n# =============================================================================\n# MCP (Model Context Protocol) Servers\n# =============================================================================\n# Connect to external MCP servers to add tools from the MCP ecosystem.\n# Each server's tools are automatically discovered and registered.\n# See website/docs/user-guide/features/mcp.md for full documentation.\n#\n# Stdio servers (spawn a subprocess):\n# command: the executable to run\n# args: command-line arguments\n# env: environment variables (only these + safe defaults passed to subprocess)\n#\n# HTTP servers (connect to a URL):\n# url: the MCP server endpoint\n# headers: HTTP headers (e.g., for authentication)\n#\n# Optional per-server settings:\n# timeout: tool call timeout in seconds (default: 120)\n# connect_timeout: initial connection timeout (default: 60)\n# keepalive_interval: liveness ping cadence in seconds (default: 180).\n# Lower it below the server's session TTL for servers that expire idle\n# sessions quickly (e.g. Unreal Engine editor MCP, ~15s), otherwise idle\n# tool calls hit an expired session and pay a slow reconnect. Floored at 5s.\n#\n# mcp_servers:\n# time:\n# command: uvx\n# args: [\"mcp-server-time\"]\n# filesystem:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-filesystem\", \"/home/user\"]\n# notion:\n# url: https://mcp.notion.com/mcp\n# github:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-github\"]\n# env:\n# GITHUB_PERSONAL_ACCESS_TOKEN: \"ghp_...\"\n#\n# Sampling (server-initiated LLM requests) — enabled by default.\n# Per-server config under the 'sampling' key:\n# analysis:\n# command: npx\n# args: [\"-y\", \"analysis-server\"]\n# sampling:\n# enabled: true # default: true\n# model: \"gemini-3-flash\" # override model (optional)\n# max_tokens_cap: 4096 # max tokens per request\n# timeout: 30 # LLM call timeout (seconds)\n# max_rpm: 10 # max requests per minute\n# allowed_models: [] # model whitelist (empty = all)\n# max_tool_rounds: 5 # tool loop limit (0 = disable)\n# log_level: \"info\" # audit verbosity\n\n# =============================================================================\n# Text-to-Speech\n# =============================================================================\n# TTS defaults to Edge TTS unless changed in ~/.hermes/config.yaml.\n# Gemini TTS supports persona/director prompt files, and Gemini 3.1 Flash TTS\n# can use a hidden auxiliary rewrite pass to insert expressive square-bracket\n# audio tags into the TTS script without showing tags in chat.\n#\n# tts:\n# provider: \"gemini\"\n# gemini:\n# model: \"gemini-3.1-flash-tts-preview\"\n# voice: \"Kore\"\n# audio_tags: false\n# persona_prompt_file: \"\" # e.g. ~/.hermes/tts/radio-host.md\n\n# =============================================================================\n# Voice Transcription (Speech-to-Text)\n# =============================================================================\n# Automatically transcribe voice messages on messaging platforms.\n# Providers: local (free, faster-whisper) | groq (free tier) | openai (Whisper API) | mistral (Voxtral Transcribe)\n# Set the corresponding API key in .env: GROQ_API_KEY, OPENAI_API_KEY, or MISTRAL_API_KEY.\nstt:\n enabled: true\n # provider: \"local\" # auto-detected if omitted\n local:\n model: \"base\" # tiny | base | small | medium | large-v3 | turbo\n # language: \"\" # auto-detect; set to \"en\", \"es\", \"fr\", etc. to force\n openai:\n model: \"whisper-1\" # whisper-1 | gpt-4o-mini-transcribe | gpt-4o-transcribe\n # mistral:\n # model: \"voxtral-mini-latest\" # voxtral-mini-latest | voxtral-mini-2602\n\n# =============================================================================\n# Response Pacing (Messaging Platforms)\n# =============================================================================\n# Add human-like delays between message chunks.\n# human_delay:\n# mode: \"off\" # \"off\" | \"natural\" | \"custom\"\n# min_ms: 800 # Min delay (custom mode only)\n# max_ms: 2500 # Max delay (custom mode only)\n\n# =============================================================================\n# Session Logging\n# =============================================================================\n# Session trajectories are automatically saved to logs/ directory.\n# Each session creates: logs/session_YYYYMMDD_HHMMSS_UUID.json\n#\n# The session ID is displayed in the welcome banner for easy reference.\n# Logs contain full conversation history in trajectory format:\n# - System prompt, user messages, assistant responses\n# - Tool calls with inputs/outputs\n# - Timestamps for debugging\n#\n# No configuration needed - logging is always enabled.\n# To disable, you would need to modify the source code.\n\n# =============================================================================\n# Code Execution Sandbox (Programmatic Tool Calling)\n# =============================================================================\n# The execute_code tool runs Python scripts that call Hermes tools via RPC.\n# Intermediate tool results stay out of the LLM's context window.\ncode_execution:\n timeout: 300 # Max seconds per script before kill (default: 300 = 5 min)\n max_tool_calls: 50 # Max RPC tool calls per execution (default: 50)\n\n# =============================================================================\n# Subagent Delegation\n# =============================================================================\n# The delegate_task tool spawns child agents with isolated context.\n# Supports single tasks and batch mode (default 3 parallel, configurable).\ndelegation:\n max_iterations: 50 # Max tool-calling turns per child (default: 50)\n # max_concurrent_children: 3 # Max parallel child agents per batch (default: 3, floor: 1, no ceiling).\n # WARNING: values above 10 multiply API cost linearly.\n # max_spawn_depth: 1 # Delegation tree depth cap (range: 1-3, default: 1 = flat).\n # Raise to 2 to allow workers to spawn their own subagents.\n # Requires role=\"orchestrator\" on intermediate agents.\n # orchestrator_enabled: true # Kill switch for role=\"orchestrator\" children (default: true).\n # subagent_auto_approve: false # When a subagent hits a dangerous-command approval prompt, auto-deny (default: false)\n # or auto-approve \"once\" (true) instead of blocking on stdin.\n # The parent TUI owns stdin, so blocking would deadlock; non-interactive resolution is required.\n # Both choices emit a logger.warning audit line. Flip to true only for cron/batch pipelines.\n # inherit_mcp_toolsets: true # When explicit child toolsets are narrowed, also keep the parent's MCP toolsets (default: true). Set false for strict intersection.\n # model: \"google/gemini-3-flash-preview\" # Override model for subagents (empty = inherit parent)\n # provider: \"openrouter\" # Override provider for subagents (empty = inherit parent)\n # # Resolves full credentials (base_url, api_key) automatically.\n # # Supported: openrouter, nous, zai, kimi-coding, minimax\n\n# =============================================================================\n# Honcho Integration (Cross-Session User Modeling)\n# =============================================================================\n# AI-native persistent memory via Honcho (https://honcho.dev/).\n# Builds a deeper understanding of the user across sessions and tools.\n# Runs alongside USER.md — additive, not a replacement.\n#\n# Requires: pip install honcho-ai\n# Config: ~/.honcho/config.json (shared with Claude Code, Cursor, etc.)\n# API key: HONCHO_API_KEY in ~/.hermes/.env or ~/.honcho/config.json\n#\n# Hermes-specific overrides (optional — most config comes from ~/.honcho/config.json):\n# honcho: {}\n\n# =============================================================================\n# Display\n# =============================================================================\ndisplay:\n # Use compact banner mode (hides the ASCII-art banner, shows a single line).\n # true: Compact single-line banner\n # false: Full ASCII banner with tool/skill summary (default)\n compact: false\n\n # Tool progress display level (CLI and gateway)\n # off: Silent — no tool activity shown, just the final response\n # new: Show a tool indicator only when the tool changes (skip repeats)\n # all: Show every tool call with a short preview (default)\n # verbose: Full args, results, and debug logs (same as /verbose)\n # log: Silent in chat; append every tool call to ~/.hermes/logs/tool_calls.log (gateway only)\n # Toggle at runtime with /verbose in the CLI\n tool_progress: all\n\n # Per-platform defaults can be quieter than the global setting. Telegram\n # tunes for mobile: tool_progress and busy_ack_detail default off (no\n # per-tool breadcrumb stream, no \"iteration 21/60\" debug detail in busy\n # acks or heartbeats), but interim_assistant_messages and\n # long_running_notifications STAY ON so the user has real signal between\n # turn start and final answer (mid-turn assistant commentary + a single\n # edit-in-place \"⏳ Working — N min\" heartbeat). Override under\n # display.platforms.telegram.\n\n # Auto-cleanup of temporary progress bubbles after the final response lands.\n # On platforms that support message deletion (currently Telegram), this\n # removes the tool-progress bubble, \"⏳ Still working...\" notices, and\n # context-pressure status messages once the final reply has been delivered —\n # keeping long-running turns visible live, then tidy afterward. Failed runs\n # leave the bubbles in place as breadcrumbs. Off by default.\n # Per-platform override: display.platforms.telegram.cleanup_progress\n # true: Delete tracked progress/status bubbles on successful turn\n # false: Leave everything in place (default)\n # Example:\n # display:\n # platforms:\n # telegram:\n # cleanup_progress: true\n cleanup_progress: false\n\n # Gateway-only natural mid-turn assistant updates.\n # When true, completed assistant status messages are sent as separate chat\n # messages. This is independent of tool_progress and gateway streaming.\n # true: Send mid-turn assistant updates as separate messages (default)\n # false: Only send the final response\n interim_assistant_messages: true\n\n # Gateway-only long-running status heartbeats.\n # When false, the platform does not receive periodic \"⏳ Working — N min\"\n # notifications even if agent.gateway_notify_interval is non-zero. The\n # heartbeat edits a single message in place (where the adapter supports\n # editing) instead of posting a new bubble each interval.\n # Default: true everywhere, including Telegram (silent agents are worse\n # than a single edit-in-place heartbeat).\n long_running_notifications: true\n\n # Include detailed iteration/tool/status context in busy acknowledgments\n # and long-running heartbeats. When true, busy acks show \"iteration 21/60,\n # terminal, 10 min\" and the heartbeat shows \"⏳ Working — 12 min,\n # iteration 21/60, terminal\". When false (Telegram default), both stay\n # terse: \"Interrupting current task\" and \"⏳ Working — 12 min, terminal\".\n busy_ack_detail: true\n\n # What Enter does when Hermes is already busy (CLI and gateway platforms).\n # interrupt: Interrupt the current run and redirect Hermes (default)\n # queue: Queue your message for the next turn\n # steer: Inject your message mid-run via /steer, arriving at the agent\n # after the next tool call — no interrupt, no role violation.\n # Falls back to 'queue' if the agent isn't running yet or if\n # images are attached (steer only carries text).\n # Ctrl+C (or /stop in gateway) always interrupts regardless of this setting.\n # Toggle at runtime with /busy .\n busy_input_mode: interrupt\n\n # Background process notifications (gateway/messaging only).\n # Controls how chatty the process watcher is when you use\n # terminal(background=true, notify_on_complete=true) from Telegram/Discord/etc.\n # off: No watcher messages at all\n # result: Only the final completion message\n # error: Only the final message when exit code != 0\n # all: Running output updates + final message (default)\n background_process_notifications: all\n\n\n # Play terminal bell when agent finishes a response.\n # Useful for long-running tasks — your terminal will ding when the agent is done.\n # Works over SSH. Most terminals can be configured to flash the taskbar or play a sound.\n # true: Ring the terminal bell on each response\n # false: Silent (default)\n bell_on_complete: false\n\n # Show model reasoning/thinking before each response.\n # When enabled, a dim box shows the model's thought process above the response.\n # Toggle at runtime with /reasoning show or /reasoning hide.\n # true: Show the reasoning box\n # false: Hide reasoning (default)\n show_reasoning: false\n\n # Stream tokens to the terminal as they arrive instead of waiting for the\n # full response. The response box opens on first token and text appears\n # line-by-line. Tool calls are still captured silently.\n # true: Stream tokens as they arrive (default)\n # false: Wait for the full response before rendering\n streaming: true\n\n # Show [HH:MM] timestamps on user input and assistant response labels.\n # timestamps: false\n\n # ───────────────────────────────────────────────────────────────────────────\n # Skin / Theme\n # ───────────────────────────────────────────────────────────────────────────\n # Customize CLI visual appearance — banner colors, spinner faces, tool prefix,\n # response box label, and branding text. Change at runtime with /skin .\n #\n # Built-in skins:\n # default — Classic Hermes gold/kawaii\n # ares — Crimson/bronze war-god theme with spinner wings\n # mono — Clean grayscale monochrome\n # slate — Cool blue developer-focused\n # daylight — Bright light-mode theme\n # warm-lightmode — Warm paper-tone light-mode theme\n # poseidon — Sea-green/teal Olympian theme\n # sisyphus — Earthy stone-and-moss theme\n # charizard — Fiery orange dragon theme\n #\n # Custom skins: drop a YAML file in ~/.hermes/skins/.yaml\n # Schema (all fields optional, missing values inherit from default):\n #\n # name: my-theme\n # description: Short description\n # colors:\n # banner_border: \"#HEX\" # Panel border\n # banner_title: \"#HEX\" # Panel title\n # banner_accent: \"#HEX\" # Section headers (Available Tools, etc.)\n # banner_dim: \"#HEX\" # Dim/muted text\n # banner_text: \"#HEX\" # Body text (tool names, skill names)\n # ui_accent: \"#HEX\" # UI accent color\n # response_border: \"#HEX\" # Response box border color\n # spinner:\n # waiting_faces: [\"(⚔)\", \"(⛨)\"] # Faces shown while waiting\n # thinking_faces: [\"(⚔)\", \"(⌁)\"] # Faces shown while thinking\n # thinking_verbs: [\"forging\", \"plotting\"] # Verbs for spinner messages\n # wings: # Optional left/right spinner decorations\n # - [\"⟪⚔\", \"⚔⟫\"]\n # - [\"⟪▲\", \"▲⟫\"]\n # branding:\n # agent_name: \"My Agent\" # Banner title and branding\n # welcome: \"Welcome message\" # Shown at CLI startup\n # response_label: \" ⚔ Agent \" # Response box header label\n # prompt_symbol: \"⚔\" # Prompt symbol (bare token; renderers add trailing space)\n # tool_prefix: \"╎\" # Tool output line prefix (default: ┊)\n #\n skin: default\n\n# =============================================================================\n# Model Aliases — short names for /model command\n# =============================================================================\n# Map short aliases to exact (model, provider, base_url) tuples.\n# Used by /model tab completion and resolve_alias().\n# Aliases are checked BEFORE the models.dev catalog, so they can route\n# to endpoints not in the catalog (e.g. Ollama Cloud, local servers).\n#\n# model_aliases:\n# opus:\n# model: claude-opus-4-6\n# provider: anthropic\n# qwen:\n# model: \"qwen3.5:397b\"\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n# glm:\n# model: glm-4.7\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n\n# =============================================================================\n# Privacy\n# =============================================================================\n# privacy:\n# # Redact PII from the LLM context prompt.\n# # When true, phone numbers are stripped and user/chat IDs are replaced\n# # with deterministic hashes before being sent to the model.\n# # Names and usernames are NOT affected (user-chosen, publicly visible).\n# # Routing/delivery still uses the original values internally.\n# redact_pii: false\n\n# =============================================================================\n# Shell-script hooks\n# =============================================================================\n# Register shell scripts as plugin-hook callbacks. Each entry is executed as\n# a subprocess (shell=False, shlex.split) with a JSON payload on stdin. On\n# stdout the script may return JSON that either blocks the tool call or\n# injects context into the next LLM call.\n#\n# Valid events (mirror hermes_cli.plugins.VALID_HOOKS):\n# pre_tool_call, post_tool_call, pre_llm_call, post_llm_call,\n# pre_api_request, post_api_request, on_session_start, on_session_end,\n# on_session_finalize, on_session_reset, subagent_stop\n#\n# First-use consent: each (event, command) pair prompts once on a TTY, then\n# is persisted to ~/.hermes/shell-hooks-allowlist.json. Non-interactive\n# runs (gateway, cron) need --accept-hooks, HERMES_ACCEPT_HOOKS=1, or the\n# hooks_auto_accept key below.\n#\n# See website/docs/user-guide/features/hooks.md for the full JSON wire\n# protocol and worked examples.\n#\n# hooks:\n# pre_tool_call:\n# - matcher: \"terminal\"\n# command: \"~/.hermes/agent-hooks/block-rm-rf.sh\"\n# timeout: 10\n# post_tool_call:\n# - matcher: \"write_file|patch\"\n# command: \"~/.hermes/agent-hooks/auto-format.sh\"\n# pre_llm_call:\n# - command: \"~/.hermes/agent-hooks/inject-cwd-context.sh\"\n# subagent_stop:\n# - command: \"~/.hermes/agent-hooks/log-orchestration.sh\"\n#\n# hooks_auto_accept: false\n\n\n# =============================================================================\n# Update Behavior\n# =============================================================================\nupdates:\n # Create a full HERMES_HOME zip before every `hermes update`.\n # Backups land in ~/.hermes/backups/ and can be restored with `hermes import`.\n # Off by default because large homes can add minutes to every update.\n pre_update_backup: false\n\n # Number of pre-update backup zips to retain.\n backup_keep: 5\n\n # What non-interactive updates do with local source edits in the Hermes repo.\n # Interactive terminal updates always prompt before restoring the autostash.\n #\n # stash - auto-stash before pull, then auto-restore after success (default)\n # discard - drop the update-created stash after success; use only on managed\n # installs where local source edits should not persist\n non_interactive_local_changes: \"stash\"\n\n\n# =============================================================================\n# Web Dashboard\n# =============================================================================\n# OAuth gate configuration for `hermes dashboard --host `.\n# The bundled Nous Portal plugin reads these on startup; settings here are\n# the canonical surface. Each can be overridden by an environment variable:\n#\n# dashboard.oauth.client_id <- HERMES_DASHBOARD_OAUTH_CLIENT_ID\n# dashboard.oauth.portal_url <- HERMES_DASHBOARD_PORTAL_URL\n# dashboard.public_url <- HERMES_DASHBOARD_PUBLIC_URL\n#\n# Env wins when set to a non-empty value. This is what Fly.io's platform-\n# secret injection uses to push per-deploy client_ids without needing to\n# bake a config.yaml into the image. Empty env values are treated as unset\n# so a provisioned-but-not-populated secret can't shadow a valid entry here.\n#\n# Local dev / on-prem deploys should typically set these via config.yaml\n# (the ~/.hermes/.env file is reserved for API keys and secrets).\n#\n# dashboard:\n# oauth:\n# client_id: \"\" # agent:{instance_id}; Portal provisions this at deploy\n# portal_url: \"\" # blank → default https://portal.nousresearch.com\n#\n# # Force the absolute base URL the OAuth callback (and any other public\n# # URL the dashboard hands to external systems) is built from. Set this\n# # for deploys behind reverse proxies that don't reliably forward\n# # X-Forwarded-Host / X-Forwarded-Proto / X-Forwarded-Prefix (manual\n# # nginx setups, on-prem ingresses, custom-domain Fly deploys without\n# # full proxy header chains).\n# #\n# # When set, the value is the complete authority: scheme + host +\n# # optional path prefix (e.g. \"https://example.com/hermes\"). The OAuth\n# # callback URL becomes \"/auth/callback\" — X-Forwarded-Prefix\n# # is IGNORED on this code path because the operator has explicitly\n# # declared the public URL and we no longer need to guess.\n# #\n# # Leave empty to use the existing proxy-header reconstruction (the\n# # default — works on Fly.io out of the box).\n# #\n# # public_url: \"https://example.com/hermes\"\n#\n# -----------------------------------------------------------------------------\n# Self-hosted OIDC dashboard auth (generic OpenID Connect — Authentik,\n# Keycloak, Zitadel, Authelia, Auth0, Okta, Google, …). Use this INSTEAD of the\n# nous block above when gating the dashboard with your own identity provider.\n# Each setting can be overridden by an environment variable:\n#\n# dashboard.oauth.self_hosted.issuer <- HERMES_DASHBOARD_OIDC_ISSUER\n# dashboard.oauth.self_hosted.client_id <- HERMES_DASHBOARD_OIDC_CLIENT_ID\n# dashboard.oauth.self_hosted.scopes <- HERMES_DASHBOARD_OIDC_SCOPES\n# dashboard.oauth.self_hosted.client_secret <- HERMES_DASHBOARD_OIDC_CLIENT_SECRET\n#\n# dashboard:\n# oauth:\n# provider: self-hosted\n# self_hosted:\n# issuer: \"https://auth.example.com/application/o/hermes/\" # required\n# client_id: \"hermes-dashboard\" # required\n# scopes: \"openid profile email\" # optional\n#\n# # OPTIONAL — set ONLY if your IDP registered the client as\n# # *confidential* (Authentik / Keycloak often default to this). When\n# # set, Hermes authenticates the client at the token endpoint\n# # (client_secret_basic or client_secret_post, auto-selected from the\n# # IDP's discovery doc) IN ADDITION to PKCE. Leave unset for a public\n# # (PKCE-only) client — the common case.\n# #\n# # This is a CREDENTIAL: prefer setting HERMES_DASHBOARD_OIDC_CLIENT_SECRET\n# # in ~/.hermes/.env over putting it here in config.yaml.\n# # client_secret: \"\"\n\n# =============================================================================\n# External secret sources\n# =============================================================================\n# Pull provider credentials from external secret managers at process startup\n# instead of storing them in ~/.hermes/.env. Only the manager's bootstrap\n# credential (e.g. BWS_ACCESS_TOKEN / OP_SERVICE_ACCOUNT_TOKEN) lives in .env\n# (or your shell / desktop session); everything else rotates centrally.\n# Failures never block startup — Hermes warns once and continues with\n# whatever .env already had.\n#\n# Multiple sources can be enabled at once:\n# - \"mapped\" sources (explicit VAR -> ref bindings, e.g. 1Password's env:\n# map) beat \"bulk\" sources (whole-project dumps like Bitwarden BSM)\n# - within a shape, the first source to claim a var wins; later claims\n# are skipped with a startup warning (never a silent clobber)\n# - a source's override_existing lets it beat .env/shell values, but\n# never another secret source's claim\n# Docs: https://hermes-agent.nousresearch.com/docs/user-guide/secrets/\n#\n# secrets:\n# # Optional explicit ordering of enabled sources.\n# # sources: [onepassword, bitwarden]\n#\n# # ---- Bitwarden Secrets Manager (bws CLI) --------------------------------\n# bitwarden:\n# enabled: false\n# access_token_env: BWS_ACCESS_TOKEN # bootstrap token, sourced from .env\n# project_id: \"\" # UUID of the BSM project to sync\n# server_url: \"\" # \"\" = US Cloud; EU/self-hosted URL otherwise\n# cache_ttl_seconds: 300 # 0 disables caching\n# override_existing: true # BSM values win over existing env\n# auto_install: true # lazy-download bws into ~/.hermes/bin\n#\n# # ---- 1Password (op CLI) -------------------------------------------------\n# onepassword:\n# enabled: false\n# # Map env-var names to op:// secret references. Each is resolved with a\n# # single `op read` at startup.\n# env:\n# OPENAI_API_KEY: \"op://Private/OpenAI/api key\"\n# ANTHROPIC_API_KEY: \"op://Private/Anthropic/credential\"\n# account: \"\" # op --account shorthand; \"\" = default\n# service_account_token_env: OP_SERVICE_ACCOUNT_TOKEN # headless auth; unset = desktop session\n# binary_path: \"\" # \"\" = resolve op via PATH; else absolute path\n# cache_ttl_seconds: 300 # 0 disables BOTH cache layers\n# override_existing: true # resolved values win over existing env\n"}, "2d16ec7fb72ea40d5f4f9b49211634845409471f": {"t": 1785767882.5015504, "n": 72024, "h": "8a06283aaecd30bc", "c": "# Hermes Agent CLI Configuration\n# Copy this file to cli-config.yaml and customize as needed.\n# This file configures the CLI behavior. Environment variables in .env take precedence.\n\n# =============================================================================\n# Model Configuration\n# =============================================================================\nmodel:\n # Default model to use (can be overridden with --model flag)\n # Both \"default\" and \"model\" work as the key name here.\n default: \"anthropic/claude-opus-4.6\"\n \n # Inference provider selection:\n # \"auto\" - Auto-detect from credentials (default)\n # \"openrouter\" - OpenRouter (requires: OPENROUTER_API_KEY or OPENAI_API_KEY)\n # \"nous\" - Nous Portal OAuth (requires: hermes auth add nous)\n # \"nous-api\" - Nous Portal API key (requires: NOUS_API_KEY)\n # \"anthropic\" - Direct Anthropic API (requires: ANTHROPIC_API_KEY)\n # \"openai-codex\" - OpenAI Codex (requires: hermes auth)\n # \"copilot\" - GitHub Copilot / GitHub Models (requires: GITHUB_TOKEN)\n # \"gemini\" - Use Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n # \"zai\" - Use z.ai / ZhipuAI GLM models (requires: GLM_API_KEY)\n # \"kimi-coding\" - Kimi / Moonshot AI (requires: KIMI_API_KEY)\n # \"minimax\" - MiniMax global (requires: MINIMAX_API_KEY)\n # \"minimax-cn\" - MiniMax China (requires: MINIMAX_CN_API_KEY)\n # \"huggingface\" - Hugging Face Inference (requires: HF_TOKEN)\n # \"nvidia\" - NVIDIA NIM / build.nvidia.com (requires: NVIDIA_API_KEY)\n # \"xiaomi\" - Xiaomi MiMo (requires: XIAOMI_API_KEY)\n # \"arcee\" - Arcee AI Trinity models (requires: ARCEEAI_API_KEY)\n # \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY — https://ollama.com/settings)\n # \"kilocode\" - KiloCode gateway (requires: KILOCODE_API_KEY)\n # \"azure-foundry\" - Microsoft Foundry / Azure OpenAI (API key or Entra ID)\n # \"lmstudio\" - LM Studio local server (optional: LM_API_KEY, defaults to http://127.0.0.1:1234/v1)\n #\n # Local servers (LM Studio, Ollama, vLLM, llama.cpp):\n # \"custom\" - Any other OpenAI-compatible endpoint. Set base_url below.\n # Aliases: \"ollama\", \"vllm\", \"llamacpp\" all map to \"custom\".\n # LM Studio is first-class and uses provider: \"lmstudio\".\n # It works with both no-auth and auth-enabled server modes.\n #\n # Can also be overridden for a single invocation with the --provider flag.\n provider: \"auto\"\n \n # API configuration (falls back to OPENROUTER_API_KEY env var)\n # api_key: \"your-key-here\" # Uncomment to set here instead of .env\n base_url: \"https://openrouter.ai/api/v1\"\n\n # Azure Foundry keyless auth example:\n # provider: \"azure-foundry\"\n # base_url: \"https://.openai.azure.com/openai/v1\"\n # auth_mode: \"entra_id\" # DefaultAzureCredential: az login, managed identity, workload identity, etc.\n # default: \"gpt-4o\" # Deployment/model name\n # entra:\n # scope: \"https://ai.azure.com/.default\" # Optional; this is the default.\n\n # ── Token limits — two settings, easy to confuse ──────────────────────────\n #\n # context_length: TOTAL context window (input + output tokens combined).\n # Controls when Hermes compresses history and validates requests.\n # Leave unset — Hermes auto-detects the correct value from the provider.\n # Set manually only when auto-detection is wrong (e.g. a local server with\n # a custom num_ctx, or a proxy that doesn't expose /v1/models).\n #\n # context_length: 131072\n #\n # max_tokens: OUTPUT cap — maximum tokens the model may generate per response.\n # Unrelated to how long your conversation history can be.\n # The OpenAI-standard name \"max_tokens\" is a misnomer; Anthropic's native\n # API has since renamed it \"max_output_tokens\" for clarity.\n # Leave unset to use the model's native output ceiling (recommended).\n # Set only if you want to deliberately limit individual response length.\n #\n# max_tokens: 8192\n\n # ── Custom request headers (optional) ─────────────────────────────────────\n #\n # default_headers: extra HTTP headers sent on every request to an\n # OpenAI-compatible endpoint. User values take precedence over the\n # provider/SDK defaults, so this is the supported way to override the\n # OpenAI Python SDK's identifying headers (User-Agent: OpenAI/Python ...,\n # X-Stainless-*) when a custom provider sits behind a gateway/WAF that\n # rejects them — e.g. an upstream that returns \"502 Upstream access\n # forbidden\" for the SDK default User-Agent but accepts a plain one.\n # Applies on the OpenAI wire only (not native Anthropic / Bedrock).\n #\n # default_headers:\n # User-Agent: \"curl/8.7.1\"\n #\n # extra_headers: accepted as an alias of default_headers (merged, with\n # extra_headers winning when both are set) — matches the per-provider\n # extra_headers key below.\n #\n # Per-provider variant: named providers / custom_providers entries accept an\n # extra_headers dict scoped to that endpoint only — for reverse proxies,\n # gateways, or custom auth (e.g. Cloudflare Access service tokens).\n # Merged onto SDK/provider defaults with the entry's values winning.\n # Header values are treated as secrets and are never logged.\n #\n # providers:\n # my-proxy:\n # base_url: \"https://llm.internal.example.com/v1\"\n # key_env: \"MY_PROXY_API_KEY\"\n # extra_headers:\n # CF-Access-Client-Id: \"xxxx.access\"\n # CF-Access-Client-Secret: \"${CF_ACCESS_SECRET}\"\n # X-Client-Name: \"hermes-agent\"\n\n# Named provider overrides (optional)\n# Use this for per-provider request timeouts, non-stream stale timeouts,\n# and per-model exceptions.\n# Applies to the primary turn client on every api_mode (OpenAI-wire, native\n# Anthropic, and Anthropic-compatible providers), the fallback chain, and\n# client rebuilds during credential rotation. For OpenAI-wire chat\n# completions (streaming and non-streaming) the configured value is also\n# used as the per-request ``timeout=`` kwarg so it wins over the legacy\n# HERMES_API_TIMEOUT env var (which still applies when no config is set).\n# ``stale_timeout_seconds`` controls the non-streaming stale-call detector and\n# wins over the legacy HERMES_API_CALL_STALE_TIMEOUT env var. Leaving these\n# unset keeps the legacy defaults (HERMES_API_TIMEOUT=1800s,\n# HERMES_API_CALL_STALE_TIMEOUT=90s, native Anthropic 900s). The\n# implicit non-stream stale detector is auto-disabled for local endpoints\n# and can scale upward for very large contexts.\n#\n# Not currently wired for AWS Bedrock (bedrock_converse + AnthropicBedrock\n# SDK paths) — those use boto3 with its own timeout configuration.\n#\n# providers:\n# ollama-local:\n# request_timeout_seconds: 300 # Longer timeout for local cold-starts\n# stale_timeout_seconds: 900 # Explicitly re-enable stale detection on local endpoints\n# anthropic:\n# request_timeout_seconds: 30 # Fast-fail cloud requests\n# models:\n# claude-opus-4.6:\n# timeout_seconds: 600 # Longer timeout for extended-thinking Opus calls\n# openai-codex:\n# models:\n# gpt-5.4:\n# stale_timeout_seconds: 1800 # Longer non-stream stale timeout for slow large-context turns\n\n# =============================================================================\n# OpenRouter Provider Routing (only applies when using OpenRouter)\n# =============================================================================\n# Control how requests are routed across providers on OpenRouter.\n# See: https://openrouter.ai/docs/guides/routing/provider-selection\n#\n# provider_routing:\n# # Sort strategy: \"price\" (default), \"throughput\", or \"latency\"\n# # Append :nitro to model name for a shortcut to throughput sorting.\n# sort: \"throughput\"\n#\n# # Only allow these providers (provider slugs from OpenRouter)\n# # only: [\"anthropic\", \"google\"]\n#\n# # Skip these providers entirely\n# # ignore: [\"deepinfra\", \"fireworks\"]\n#\n# # Try providers in this order (overrides default load balancing)\n# # order: [\"anthropic\", \"google\", \"together\"]\n#\n# # Require providers to support all parameters in your request\n# # require_parameters: true\n#\n# # Data policy: \"allow\" (default) or \"deny\" to exclude providers that may store data\n# # data_collection: \"deny\"\n\n# =============================================================================\n# OpenRouter Response Caching (only applies when using OpenRouter)\n# =============================================================================\n# Cache identical API responses at the OpenRouter edge for free instant replays.\n# When enabled, identical requests (same model, messages, parameters) return\n# cached responses with zero billing. Separate from Anthropic prompt caching.\n# See: https://openrouter.ai/docs/guides/features/response-caching\n#\n# openrouter:\n# response_cache: true # Enable response caching (default: true)\n# response_cache_ttl: 300 # Cache TTL in seconds, 1-86400 (default: 300)\n\n# =============================================================================\n# Git Worktree Isolation\n# =============================================================================\n# When enabled, each CLI session creates an isolated git worktree so multiple\n# agents can work on the same repo concurrently without file collisions.\n# Equivalent to always passing --worktree / -w on the command line.\n#\n# worktree: true # Always create a worktree when in a git repo\n# worktree: false # Default — only create when -w flag is passed\n#\n# By default a new worktree branches from the freshly-fetched remote tip\n# (the current branch's upstream, else the remote's default branch) so it\n# starts current with the project instead of from the local clone's\n# (possibly stale) HEAD. Set worktree_sync: false to branch from local HEAD\n# instead — useful when offline or when you deliberately want the clone's\n# exact current state as the base.\n#\n# worktree_sync: true # Default — branch from the fetched remote tip\n# worktree_sync: false # Branch from local HEAD (offline / pinned base)\n\n# =============================================================================\n# Terminal Tool Configuration\n# =============================================================================\n# Choose ONE of the following terminal configurations by uncommenting it.\n# The terminal tool executes commands in the specified environment.\n\n# -----------------------------------------------------------------------------\n# OPTION 1: Local execution (default)\n# Commands run directly on your machine in the current directory\n# -----------------------------------------------------------------------------\n# Working directory behavior:\n# - CLI (`hermes` command): Uses \".\" (current directory where you run hermes)\n# - Gateway/messaging/cron: Uses terminal.cwd here; legacy .env cwd values are deprecated\nterminal:\n backend: \"local\"\n cwd: \".\" # For local backend: \".\" = current directory. Ignored for remote backends unless a backend documents otherwise.\n timeout: 180\n # HOME policy for tool subprocesses:\n # auto - default: host uses your real HOME; containers use HERMES_HOME/home\n # real - force your real OS-user HOME\n # profile - force HERMES_HOME/home for strict per-profile CLI config isolation\n home_mode: \"auto\"\n docker_mount_cwd_to_workspace: false # SECURITY: off by default. Opt in to mount the launch cwd into Docker /workspace.\n lifetime_seconds: 300\n # sudo_password: \"hunter2\" # Optional: pipe a sudo password via sudo -S. SECURITY WARNING: plaintext.\n # sudo_password: \"\" # Explicit empty password: try empty and never open the interactive sudo prompt.\n\n# -----------------------------------------------------------------------------\n# OPTION 2: SSH remote execution\n# Commands run on a remote server - agent code stays local (sandboxed)\n# Great for: keeping agent isolated from its own code, using powerful remote hardware\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"ssh\"\n# cwd: \"/home/myuser/project\" # Path on the REMOTE server\n# timeout: 180\n# lifetime_seconds: 300\n# ssh_host: \"my-server.example.com\"\n# ssh_user: \"myuser\"\n# ssh_port: 22\n# ssh_key: \"~/.ssh/id_rsa\" # Optional - uses ssh-agent if not specified\n\n# -----------------------------------------------------------------------------\n# OPTION 3: Docker container\n# Commands run in an isolated Docker container\n# Great for: reproducible environments, testing, isolation\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"docker\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /)\n# timeout: 180\n# lifetime_seconds: 300\n# docker_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# docker_mount_cwd_to_workspace: true # Explicit opt-in: mount your launch cwd into /workspace\n# # Optional: run the container as your host user's uid:gid so files written\n# # into bind-mounted dirs are owned by you, not root. Drops SETUID/SETGID\n# # caps too since no gosu privilege drop is needed. Leave off if your\n# # chosen docker_image expects to start as root.\n# docker_run_as_host_user: true\n# # Optional: explicitly forward selected env vars into Docker.\n# # These values come from your current shell first, then ~/.hermes/.env.\n# # Warning: anything forwarded here is visible to commands run in the container.\n# docker_forward_env:\n# - \"GITHUB_TOKEN\"\n# - \"NPM_TOKEN\"\n# # Optional: extra flags passed verbatim to docker run (appended after security defaults).\n# # Useful for adding capabilities (e.g. apt installs needing SETUID) or custom options.\n# # Example: add a Linux capability not included by default\n# # docker_extra_args:\n# # - \"--cap-add\"\n# # - \"SETUID\"\n\n# -----------------------------------------------------------------------------\n# OPTION 4: Singularity/Apptainer container\n# Commands run in a Singularity container (common in HPC environments)\n# Great for: HPC clusters, shared compute environments\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"singularity\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# singularity_image: \"docker://nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 5: Modal cloud execution\n# Commands run on Modal's cloud infrastructure\n# Great for: GPU access, scalable compute, serverless execution\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"modal\"\n# cwd: \"/workspace\" # Path INSIDE the sandbox (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# modal_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 6: Daytona cloud execution\n# Commands run in Daytona cloud sandboxes\n# Great for: Cloud dev environments, persistent workspaces, team collaboration\n# Requires: pip install daytona, DAYTONA_API_KEY env var\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"daytona\"\n# cwd: \"~\"\n# timeout: 180\n# lifetime_seconds: 300\n# daytona_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# container_disk: 10240 # Daytona max is 10GB per sandbox\n\n#\n# --- Container resource limits (docker, singularity, modal, daytona -- ignored for local/ssh) ---\n# These settings apply to all container backends. They control the resources\n# allocated to the sandbox and whether its filesystem persists across sessions.\n container_cpu: 1 # CPU cores\n container_memory: 5120 # Memory in MB (5120 = 5GB)\n container_disk: 51200 # Disk in MB (51200 = 50GB)\n container_persistent: true # Persist filesystem across sessions (false = ephemeral)\n\n# -----------------------------------------------------------------------------\n# SUDO SUPPORT (works with ALL backends above)\n# -----------------------------------------------------------------------------\n# Add sudo_password to any terminal config above to enable sudo commands.\n# The password is piped via `sudo -S`. Works with local, ssh, docker, etc.\n#\n# SECURITY WARNING: Password stored in plaintext!\n#\n# INTERACTIVE PROMPT: If sudo_password is unset and the CLI is running,\n# you'll be prompted to enter your password when sudo is needed:\n# - 45-second timeout (auto-skips if no input)\n# - Press Enter to skip (command fails gracefully)\n# - Password is hidden while typing\n# - Password is cached for the session\n#\n# EMPTY PASSWORDS: Setting sudo_password to an explicit empty string is different\n# from leaving it unset. Hermes will try an empty password via `sudo -S` and\n# will not open the interactive prompt. This is useful for passwordless sudo,\n# Touch ID sudo setups, and environments where prompting is just noise.\n#\n# ALTERNATIVES:\n# - SSH backend: Configure passwordless sudo on the remote server\n# - Containers: Run as root inside the container (no sudo needed)\n# - Local: Configure /etc/sudoers for specific commands\n#\n# Example (add to your terminal section):\n# sudo_password: \"your-password-here\"\n\n# =============================================================================\n# Security Scanning (tirith)\n# =============================================================================\n# Optional pre-exec command security scanning via tirith.\n# Detects homograph URLs, pipe-to-shell, terminal injection, env manipulation.\n# Install: brew install sheeki03/tap/tirith\n# Docs: https://github.com/sheeki03/tirith\n#\n# security:\n# tirith_enabled: true # Enable/disable tirith scanning\n# tirith_path: \"tirith\" # Path to tirith binary (supports ~ expansion)\n# tirith_timeout: 5 # Scan timeout in seconds\n# tirith_fail_open: true # Allow commands if tirith unavailable\n\n# =============================================================================\n# Browser Tool Configuration\n# =============================================================================\nbrowser:\n # Inactivity timeout in seconds - browser sessions are automatically closed\n # after this period of no activity between agent loops (default: 120 = 2 minutes)\n inactivity_timeout: 120\n\n# =============================================================================\n# Tool Loop Guardrails\n# =============================================================================\n# Soft warnings are enabled by default. They append guidance to repeated failed\n# or non-progressing tool results but still let the tool execute. Hard stops are\n# opt-in circuit breakers for autonomous/cron sessions where stopping a loop is\n# preferable to spending the full iteration budget.\ntool_loop_guardrails:\n warnings_enabled: true\n hard_stop_enabled: false\n warn_after:\n exact_failure: 2\n same_tool_failure: 3\n idempotent_no_progress: 2\n hard_stop_after:\n exact_failure: 5\n same_tool_failure: 8\n idempotent_no_progress: 5\n\n# =============================================================================\n# Context Compression (Auto-shrinks long conversations)\n# =============================================================================\n# When conversation approaches model's context limit, middle turns are\n# automatically summarized to free up space while preserving important context.\n#\n# HOW IT WORKS:\n# 1. Tracks actual token usage from API responses (not estimates)\n# 2. When prompt_tokens >= threshold% of model's context_length, triggers compression\n# 3. Protects first 3 turns (system prompt, initial request, first response)\n# 4. Protects last N turns (default 20 messages = ~10 full turns of recent context)\n# 5. Summarizes middle turns using a fast/cheap model\n# 6. Inserts summary as a user message, continues conversation seamlessly\n#\n# Post-compression tail budget is target_ratio × threshold × context_length:\n# 200K context, threshold 0.50, ratio 0.20 → 20K tokens of recent tail preserved\n# 1M context, threshold 0.50, ratio 0.20 → 100K tokens of recent tail preserved\n#\ncompression:\n # Enable automatic context compression (default: true)\n # Set to false if you prefer to manage context manually or want errors on overflow\n enabled: true\n \n # Trigger compression at this % of model's context limit (default: 0.50 = 50%)\n # Lower values = more aggressive compression, higher values = compress later\n threshold: 0.50\n \n # Fraction of the threshold to preserve as recent tail (default: 0.20 = 20%)\n # e.g. 20% of 50% threshold = 10% of total context kept as recent messages.\n # Summary output is separately capped at 12K tokens (Gemini output limit).\n # Range: 0.10 - 0.80\n target_ratio: 0.20\n\n # Number of most-recent messages to always preserve (default: 20 ≈ 10 full turns)\n # Higher values keep more recent conversation intact at the cost of more aggressive\n # compression of older turns.\n protect_last_n: 20\n\n # Number of non-system messages to protect at the head of the transcript, in\n # ADDITION to the system prompt (which is always implicitly protected).\n # Head messages are NEVER summarized — they survive every compression\n # indefinitely. This gives stable early context for short/medium sessions,\n # but in long-running sessions that rely on rolling compaction the pinned\n # opening turns may not match how you want the session framed over time.\n # Set to 0 to preserve ONLY the system prompt (plus the rolling summary\n # and recent tail) — the cleanest configuration for long-running sessions.\n # Default 3 preserves the system prompt plus the first three non-system\n # head messages, matching the pre-feature behaviour.\n protect_first_n: 3\n\n # To pin a specific model/provider for compression summaries, use the\n # auxiliary section below (auxiliary.compression.provider / model).\n\n# =============================================================================\n# Anthropic prompt caching TTL\n# =============================================================================\n# When prompt caching is active (Claude via OpenRouter or native Anthropic),\n# Anthropic supports two TTL tiers for cached prefixes: \"5m\" (default) and\n# \"1h\". Other values are ignored and \"5m\" is used.\n#\nprompt_caching:\n cache_ttl: \"5m\" # use \"1h\" for long sessions with pauses between turns\n\n# =============================================================================\n# Auxiliary Models (Advanced — Experimental)\n# =============================================================================\n# Hermes uses lightweight \"auxiliary\" models for side tasks: image analysis,\n# browser screenshot analysis, web page summarization, TTS audio-tag insertion,\n# and context compression.\n#\n# By default these use Gemini Flash via OpenRouter or Nous Portal and are\n# auto-detected from your credentials. You do NOT need to change anything\n# here for normal usage.\n#\n# WARNING: Overriding these with providers other than OpenRouter or Nous Portal\n# is EXPERIMENTAL and may not work. Not all models/providers support vision,\n# produce usable summaries, or accept the same API format. Change at your own\n# risk — if things break, reset to \"auto\" / empty values.\n#\n# Each task has its own provider + model pair so you can mix providers.\n# For example: OpenRouter for vision (needs multimodal), but your main\n# local endpoint for compression (just needs text).\n#\n# Provider options:\n# \"auto\" - Best available: OpenRouter → Nous Portal → main endpoint (default)\n# \"openrouter\" - Force OpenRouter (requires OPENROUTER_API_KEY)\n# \"nous\" - Force Nous Portal (requires: hermes auth add nous)\n# \"gemini\" - Force Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n# \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY)\n# \"codex\" - Force Codex OAuth (requires: hermes model → Codex).\n# Uses gpt-5.3-codex which supports vision.\n# \"main\" - Use your custom endpoint (OPENAI_BASE_URL + OPENAI_API_KEY).\n# Works with OpenAI API, local models, or any OpenAI-compatible\n# endpoint. Also falls back to Codex OAuth and API-key providers.\n#\n# Model: leave empty to use the provider's default. When empty, OpenRouter\n# uses \"google/gemini-3-flash-preview\" and Nous uses \"gemini-3-flash\".\n# Other providers pick a sensible default automatically.\n#\n# auxiliary:\n# # Image analysis: vision_analyze tool + browser screenshots\n# vision:\n# provider: \"auto\"\n# model: \"\" # e.g. \"google/gemini-2.5-flash\", \"openai/gpt-4o\"\n# timeout: 30 # LLM API call timeout (seconds)\n# download_timeout: 30 # Image HTTP download timeout (seconds)\n# # Increase for slow connections or self-hosted image servers\n#\n# # Web page scraping / summarization + browser page text extraction\n# web_extract:\n# provider: \"auto\"\n# model: \"\"\n#\n# # Gemini 3.1 TTS hidden audio-tag insertion\n# tts_audio_tags:\n# provider: \"auto\" # empty model = your main chat model\n# model: \"\"\n# timeout: 30\n#\n# # Session search — summarizes matching past sessions\n# session_search:\n# provider: \"auto\"\n# model: \"\"\n# timeout: 30\n# max_concurrency: 3 # Limit parallel summaries to reduce request-burst 429s\n# extra_body: {} # Provider-specific OpenAI-compatible request fields\n# # Example for providers that support request-body\n# # reasoning controls:\n# # extra_body:\n# # enable_thinking: false\n# # Some vLLM/Qwen deployments expect this nested:\n# # extra_body:\n# # chat_template_kwargs:\n# # enable_thinking: false\n\n# =============================================================================\n# Persistent Memory\n# =============================================================================\n# Bounded curated memory injected into the system prompt every session.\n# Two stores: MEMORY.md (agent's notes) and USER.md (user profile).\n# Character limits keep the memory small and focused. The agent manages\n# pruning -- when at the limit, it must consolidate or replace entries.\n# Disabled by default in batch_runner.\n#\nmemory:\n # Agent's personal notes: environment facts, conventions, things learned\n memory_enabled: true\n \n # User profile: preferences, communication style, expectations\n user_profile_enabled: true\n \n # Character limits (~2.75 chars per token, model-independent)\n memory_char_limit: 2200 # ~800 tokens\n user_char_limit: 1375 # ~500 tokens\n\n # Periodic memory nudge: remind the agent to consider saving memories\n # every N user turns. Set to 0 to disable. Only active when memory is enabled.\n nudge_interval: 10 # Nudge every 10 user turns (0 = disabled)\n\n # Memory flush: give the agent one turn to save memories before context is\n # lost (compression, /new, /reset, exit). Set to 0 to disable.\n # For exit/reset, only fires if the session had at least this many user turns.\n flush_min_turns: 6 # Min user turns to trigger flush on exit/reset (0 = disabled)\n\n# =============================================================================\n# Session Reset Policy (Messaging Platforms)\n# =============================================================================\n# Controls when messaging sessions (Telegram, Discord, WhatsApp, Slack) are\n# automatically cleared. Without resets, conversation context grows indefinitely\n# which increases API costs with every message.\n#\n# When a reset triggers, the agent first saves important information to its\n# persistent memory — but the conversation context is wiped. The agent starts\n# fresh but retains learned facts via its memory system.\n#\n# Users can always manually reset with /reset or /new in chat.\n#\n# Modes:\n# \"both\" - Reset on EITHER inactivity timeout or daily boundary (recommended)\n# \"idle\" - Reset only after N minutes of inactivity\n# \"daily\" - Reset only at a fixed hour each day\n# \"none\" - Never auto-reset; context lives until /reset or compression kicks in\n#\n# When a reset triggers, the agent gets one turn to save important memories and\n# skills before the context is wiped. Persistent memory carries across sessions.\n#\nsession_reset:\n mode: both # \"both\", \"idle\", \"daily\", or \"none\"\n idle_minutes: 1440 # Inactivity timeout in minutes (default: 1440 = 24 hours)\n at_hour: 4 # Daily reset hour, 0-23 local time (default: 4 AM)\n\n# Maximum number of simultaneously active chat sessions across CLI, TUI,\n# dashboard chat, and messaging gateway. Set to null, 0, or omit to allow\n# unlimited concurrent sessions. When the limit is reached, new sessions get a\n# clean error while existing active sessions keep their normal behavior. This\n# top-level key takes precedence over gateway.max_concurrent_sessions. The cap\n# is a best-effort single-host/profile runtime guard; Hermes fails open if the\n# local runtime lease registry cannot be read or locked.\nmax_concurrent_sessions: null\n\n# When true, group/channel chats use one session per participant when the platform\n# provides a user ID. This is the secure default and prevents users in the same\n# room from sharing context, interrupts, and token costs. Set false only if you\n# explicitly want one shared \"room brain\" per group/channel.\ngroup_sessions_per_user: true\n\n# ─────────────────────────────────────────────────────────────────────────────\n# API Server — per-client model routing\n# ─────────────────────────────────────────────────────────────────────────────\n# Route different API clients to different models/providers on a single\n# Hermes deployment. Clients choose a backend by sending a specific string\n# as the OpenAI ``model`` field. Unmapped model values fall back to the\n# global model configured in the ``model:`` section above, and an explicit\n# session /model override always wins over a route.\n#\n# Configure via the ``platforms.api_server.extra.model_routes`` gateway\n# config block:\n#\n# platforms:\n# api_server:\n# enabled: true\n# extra:\n# key: \"your-api-server-secret\"\n# model_routes:\n# # Xiaozhi clients send model=\"minimax-m2\" → routed to MiniMax via OpenRouter\n# minimax-m2:\n# model: \"minimax/minimax-m1\"\n# provider: \"openrouter\" # optional — overrides global provider\n# # api_key: \"sk-...\" # optional — per-route UPSTREAM provider\n# # key (NOT caller auth; never logged)\n# # base_url: \"https://...\" # optional — per-route base URL\n# # GPT clients keep their own alias\n# gpt-5:\n# model: \"openai/gpt-5\"\n# provider: \"openrouter\"\n#\n# Configured aliases are automatically listed by GET /v1/models so clients\n# can discover them without manual coordination. Caller authentication is\n# unchanged: every request still authenticates with the global API server\n# key (``extra.key`` / API_SERVER_KEY).\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Gateway Streaming\n# ─────────────────────────────────────────────────────────────────────────────\n# Stream tokens to messaging platforms in real-time. The bot sends a message\n# on first token, then progressively edits it as more tokens arrive.\n# Disabled by default — enable to try the streaming UX on Telegram/Discord/Slack.\n# For Telegram, partial edits are sent as plain text and only the final edit uses MarkdownV2.\nstreaming:\n enabled: false\n # transport: edit # \"edit\" = progressive editMessageText\n # edit_interval: 0.3 # seconds between message edits\n # buffer_threshold: 40 # chars before forcing an edit flush\n # cursor: \" ▉\" # cursor shown during streaming\n\n# =============================================================================\n# Skills Configuration\n# =============================================================================\n# Skills are reusable procedures the agent can load and follow. The agent can\n# also create new skills after completing complex tasks.\n#\nskills:\n # Nudge the agent to create skills after complex tasks.\n # Every N tool-calling iterations, remind the model to consider saving a skill.\n # Set to 0 to disable.\n creation_nudge_interval: 15\n\n # External skill directories — share skills across tools/agents without\n # copying them into ~/.hermes/skills/. Each path is expanded (~ and ${VAR})\n # and resolved to an absolute path. External dirs are read-only: skill\n # creation always writes to ~/.hermes/skills/. Local skills take precedence\n # when names collide.\n # external_dirs:\n # - ~/.agents/skills\n # - /home/shared/team-skills\n\n# =============================================================================\n# Agent Behavior\n# =============================================================================\nagent:\n # Maximum tool-calling iterations per conversation\n # Higher = more room for complex tasks, but costs more tokens\n # Recommended: 20-30 for focused tasks, 50-100 for open exploration\n max_turns: 60\n\n # Inactivity timeout for gateway agent runs (seconds, 0 = unlimited).\n # The agent can run indefinitely when actively calling tools or receiving\n # API responses. Only fires after the agent has been idle for this duration.\n # gateway_timeout: 1800\n\n # Staged warning: send a warning before escalating to full timeout.\n # Fires once per run when inactivity reaches this threshold (seconds).\n # Set to 0 to disable the warning.\n # gateway_timeout_warning: 900\n\n # Graceful drain timeout for gateway stop/restart (seconds).\n # Default 0 = no drain: a restart interrupts in-flight agents immediately,\n # cleans up, and exits. Set a positive value only if you want a grace\n # window on /restart, and keep it well under systemd's TimeoutStopSec.\n # restart_drain_timeout: 0\n\n # Max app-level retry attempts for API errors (connection drops, provider\n # timeouts, 5xx, etc.) before the agent surfaces the failure. Lower this\n # to 1 if you use fallback providers and want fast failover on flaky\n # primaries (default 3). The OpenAI SDK does its own low-level retries\n # underneath this wrapper — this is the Hermes-level loop.\n # api_max_retries: 3\n\n # After the agent edits code without fresh passing verification, nudge it to\n # verify before finishing. The default \"auto\" enables it on interactive\n # coding surfaces (CLI, TUI, desktop) and programmatic callers, and disables\n # it on conversational messaging surfaces (Telegram, Discord, etc.) where the\n # verification summary would reach a human as chat noise. Set true or false to\n # force it on or off; the HERMES_VERIFY_ON_STOP env var (1/0) takes precedence.\n # verify_on_stop: auto\n\n # Standing operator instructions for the coding posture (when Hermes is in a\n # code workspace). Appended to the coding brief as an extra system block, so\n # you can pin project-wide workflow rules without editing the shipped brief.\n # Accepts a string or a list of strings. Takes effect next session.\n # coding_instructions:\n # - \"For UI work, don't run tsc/lint until I approve the look.\"\n # - \"Clean the diff before you commit and push.\"\n\n # When verify-on-stop finds edited code without fresh verification evidence,\n # append guidance for creative UI work (avoid broad tsc/lint/test before visual\n # approval) and clean-diff expectations. Set false to keep that nudge terse.\n # verify_guidance: true\n\n # A `pre_verify` hook (plugin or shell, see Event Hooks docs) can keep the\n # agent going one more turn to verify/clean before finishing. This caps how\n # many times one turn may be nudged to continue, so a hook can't trap the loop.\n # Default 3.\n # max_verify_nudges: 3\n\n # Enable verbose logging\n verbose: false\n \n # Reasoning effort level (OpenRouter and Nous Portal)\n # Controls how much \"thinking\" the model does before responding.\n # Options: \"xhigh\" (max), \"high\", \"medium\", \"low\", \"minimal\", \"none\" (disable)\n reasoning_effort: \"medium\"\n \n # Predefined personalities (use with /personality command)\n personalities:\n helpful: \"You are a helpful, friendly AI assistant.\"\n concise: \"You are a concise assistant. Keep responses brief and to the point.\"\n technical: \"You are a technical expert. Provide detailed, accurate technical information.\"\n creative: \"You are a creative assistant. Think outside the box and offer innovative solutions.\"\n teacher: \"You are a patient teacher. Explain concepts clearly with examples.\"\n kawaii: \"You are a kawaii assistant! Use cute expressions like (◕‿◕), ★, ♪, and ~! Add sparkles and be super enthusiastic about everything! Every response should feel warm and adorable desu~! ヽ(>∀<☆)ノ\"\n catgirl: \"You are Neko-chan, an anime catgirl AI assistant, nya~! Add 'nya' and cat-like expressions to your speech. Use kaomoji like (=^・ω・^=) and ฅ^•ﻌ•^ฅ. Be playful and curious like a cat, nya~!\"\n pirate: \"Arrr! Ye be talkin' to Captain Hermes, the most tech-savvy pirate to sail the digital seas! Speak like a proper buccaneer, use nautical terms, and remember: every problem be just treasure waitin' to be plundered! Yo ho ho!\"\n shakespeare: \"Hark! Thou speakest with an assistant most versed in the bardic arts. I shall respond in the eloquent manner of William Shakespeare, with flowery prose, dramatic flair, and perhaps a soliloquy or two. What light through yonder terminal breaks?\"\n surfer: \"Duuude! You're chatting with the chillest AI on the web, bro! Everything's gonna be totally rad. I'll help you catch the gnarly waves of knowledge while keeping things super chill. Cowabunga! 🤙\"\n noir: \"The rain hammered against the terminal like regrets on a guilty conscience. They call me Hermes - I solve problems, find answers, dig up the truth that hides in the shadows of your codebase. In this city of silicon and secrets, everyone's got something to hide. What's your story, pal?\"\n uwu: \"hewwo! i'm your fwiendwy assistant uwu~ i wiww twy my best to hewp you! *nuzzles your code* OwO what's this? wet me take a wook! i pwomise to be vewy hewpful >w<\"\n philosopher: \"Greetings, seeker of wisdom. I am an assistant who contemplates the deeper meaning behind every query. Let us examine not just the 'how' but the 'why' of your questions. Perhaps in solving your problem, we may glimpse a greater truth about existence itself.\"\n hype: \"YOOO LET'S GOOOO!!! 🔥🔥🔥 I am SO PUMPED to help you today! Every question is AMAZING and we're gonna CRUSH IT together! This is gonna be LEGENDARY! ARE YOU READY?! LET'S DO THIS! 💪😤🚀\"\n\n# =============================================================================\n# Toolsets\n# =============================================================================\n# Control which tools the agent has access to.\n# Use `hermes tools` to interactively enable/disable tools per platform.\n\n# =============================================================================\n# Platform Toolsets (per-platform tool configuration)\n# =============================================================================\n# Override which toolsets are available on each platform.\n# If a platform isn't listed here, its built-in default is used.\n#\n# You can use EITHER:\n# - A preset like \"hermes-cli\" or \"hermes-telegram\" (curated tool set)\n# - A list of individual toolsets to compose your own (see list below)\n#\n# Supported platform keys: cli, telegram, discord, whatsapp, slack, qqbot, teams, google_chat\n#\n# Examples:\n#\n# # Use presets (same as defaults):\n# platform_toolsets:\n# cli: [hermes-cli]\n# telegram: [hermes-telegram]\n#\n# # Custom: give Telegram only web + terminal + file + planning:\n# platform_toolsets:\n# telegram: [web, terminal, file, todo]\n#\n# # Custom: CLI without browser or image gen:\n# platform_toolsets:\n# cli: [web, terminal, file, skills, todo, tts, cronjob]\n#\n# # Restrictive: Discord gets read-only tools only:\n# platform_toolsets:\n# discord: [web, vision, skills, todo]\n#\n# If not set, defaults are:\n# cli: hermes-cli (everything + cronjob management)\n# telegram: hermes-telegram (terminal, file, web, vision, image, tts, browser, skills, todo, cronjob, messaging)\n# discord: hermes-discord (same as telegram)\n# whatsapp: hermes-whatsapp (same as telegram)\n# slack: hermes-slack (same as telegram)\n# signal: hermes-signal (same as telegram)\n# homeassistant: hermes-homeassistant (same as telegram)\n# qqbot: hermes-qqbot (same as telegram)\n# teams: hermes-teams (same as telegram)\n# google_chat: hermes-google_chat (same as telegram)\n#\nplatform_toolsets:\n cli: [hermes-cli]\n telegram: [hermes-telegram]\n discord: [hermes-discord]\n whatsapp: [hermes-whatsapp]\n slack: [hermes-slack]\n signal: [hermes-signal]\n homeassistant: [hermes-homeassistant]\n qqbot: [hermes-qqbot]\n yuanbao: [hermes-yuanbao]\n teams: [hermes-teams]\n google_chat: [hermes-google_chat]\n\n# =============================================================================\n# Gateway Platform Settings\n# =============================================================================\n# Optional per-platform messaging settings.\n# Platform-specific knobs live under `extra`.\n#\n# platforms:\n# telegram:\n# reply_to_mode: \"first\" # off | first | all\n# # guest_mode lets explicit @mentions from non-allowlisted groups through.\n# # Default false; ordinary messages, replies, and regex wake words stay blocked.\n# guest_mode: false\n# # allowed_chats: [\"-1001234567890\"]\n# extra:\n# disable_link_previews: false # Set true to suppress Telegram URL previews in bot messages\n# rich_messages: false # Bot API 10.1 rich messages (tables/task lists/details/math); default false for copyable legacy MarkdownV2, set true to opt in\n# rich_drafts: false # Experimental rich draft previews during Telegram DM streaming; default false because Telegram Desktop/macOS can visually overlay draft frames\n# command_menu:\n# # Telegram allows up to 100 BotCommands; Hermes defaults to 60 so\n# # all built-in commands plus common skill commands stay visible\n# # while remaining under Telegram's payload-size limit. Clamped 1..100.\n# max_commands: 60\n# # prepend = user priority first, then Hermes defaults\n# # append = Hermes defaults first, then user priority\n# # replace = only the list below defines priority\n# priority_mode: prepend\n# priority:\n# - my_plugin_command\n#\n# Discord-specific settings (config.yaml top-level, not under platforms:):\n#\n# discord:\n# require_mention: true # Require @mention in server channels (default: true)\n# auto_thread: true # Auto-create thread on @mention (default: true)\n# free_response_channels: \"\" # Channel IDs where no mention is needed\n# reactions: true # Show processing reactions (default: true)\n# history_backfill: true # Recover missed channel messages on mention (default: true)\n# history_backfill_limit: 50 # Max messages to scan backwards (default: 50)\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Available toolsets (use these names in platform_toolsets or the toolsets list)\n#\n# Run `hermes chat --list-toolsets` to see all toolsets and their tools.\n# Run `hermes chat --list-tools` to see every individual tool with descriptions.\n# ─────────────────────────────────────────────────────────────────────────────\n#\n# INDIVIDUAL TOOLSETS (compose your own):\n# web - web_search, web_extract\n# search - web_search only (no scraping)\n# terminal - terminal, process\n# file - read_file, write_file, patch, search\n# browser - browser_navigate, browser_snapshot, browser_click, browser_type,\n# browser_scroll, browser_back, browser_press,\n# browser_get_images, browser_vision (requires BROWSERBASE_API_KEY)\n# vision - vision_analyze (requires OPENROUTER_API_KEY)\n# image_gen - image_generate (requires FAL_KEY)\n# skills - skills_list, skill_view\n# skills_hub - skill_hub (search/install/manage from online registries — user-driven only)\n# todo - todo (in-memory task planning, no deps)\n# tts - text_to_speech (Edge TTS free, or ELEVENLABS/OPENAI/MINIMAX/MISTRAL key)\n# cronjob - cronjob (create/list/update/pause/resume/run/remove scheduled tasks)\n#\n# PRESETS (curated bundles):\n# hermes-cli - All of the above except send_message\n# hermes-telegram - terminal, file, web, vision, image_gen, tts, browser,\n# skills, todo, cronjob, send_message\n# hermes-discord - Same as hermes-telegram\n# hermes-whatsapp - Same as hermes-telegram\n# hermes-slack - Same as hermes-telegram\n#\n# COMPOSITE:\n# debugging - terminal + web + file\n# safe - web + vision (no terminal access)\n# all - Everything available\n#\n# web - Web search and content extraction (web_search, web_extract)\n# search - Web search only, no scraping (web_search)\n# terminal - Command execution and process management (terminal, process)\n# file - File operations: read, write, patch, search\n# browser - Full browser automation (navigate, click, type, screenshot, etc.)\n# vision - Image analysis (vision_analyze)\n# image_gen - Image generation with FLUX (image_generate)\n# skills - Load skill documents (skills_list, skill_view)\n# todo - Task planning and tracking for multi-step work\n# memory - Persistent memory across sessions (personal notes + user profile)\n# session_search - Search and recall past conversations (FTS5 + Gemini Flash summarization)\n# tts - Text-to-speech (Edge TTS free, ElevenLabs, OpenAI, MiniMax, Mistral)\n# cronjob - Schedule and manage automated tasks (CLI-only)\n#\n# Composite toolsets:\n# debugging - terminal + web + file (for troubleshooting)\n# safe - web + vision (no terminal access)\n\n# NOTE: The top-level \"toolsets\" key is deprecated and ignored.\n# Tool configuration is managed per-platform via platform_toolsets above.\n# Use `hermes tools` to configure interactively, or edit platform_toolsets directly.\n#\n# CLI override: hermes chat --toolsets terminal,web,file\n\n# =============================================================================\n# MCP (Model Context Protocol) Servers\n# =============================================================================\n# Connect to external MCP servers to add tools from the MCP ecosystem.\n# Each server's tools are automatically discovered and registered.\n# See website/docs/user-guide/features/mcp.md for full documentation.\n#\n# Stdio servers (spawn a subprocess):\n# command: the executable to run\n# args: command-line arguments\n# env: environment variables (only these + safe defaults passed to subprocess)\n#\n# HTTP servers (connect to a URL):\n# url: the MCP server endpoint\n# headers: HTTP headers (e.g., for authentication)\n#\n# Optional per-server settings:\n# timeout: tool call timeout in seconds (default: 120)\n# connect_timeout: initial connection timeout (default: 60)\n# keepalive_interval: liveness ping cadence in seconds (default: 180).\n# Lower it below the server's session TTL for servers that expire idle\n# sessions quickly (e.g. Unreal Engine editor MCP, ~15s), otherwise idle\n# tool calls hit an expired session and pay a slow reconnect. Floored at 5s.\n#\n# mcp_servers:\n# time:\n# command: uvx\n# args: [\"mcp-server-time\"]\n# filesystem:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-filesystem\", \"/home/user\"]\n# notion:\n# url: https://mcp.notion.com/mcp\n# github:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-github\"]\n# env:\n# GITHUB_PERSONAL_ACCESS_TOKEN: \"ghp_...\"\n#\n# Sampling (server-initiated LLM requests) — enabled by default.\n# Per-server config under the 'sampling' key:\n# analysis:\n# command: npx\n# args: [\"-y\", \"analysis-server\"]\n# sampling:\n# enabled: true # default: true\n# model: \"gemini-3-flash\" # override model (optional)\n# max_tokens_cap: 4096 # max tokens per request\n# timeout: 30 # LLM call timeout (seconds)\n# max_rpm: 10 # max requests per minute\n# allowed_models: [] # model whitelist (empty = all)\n# max_tool_rounds: 5 # tool loop limit (0 = disable)\n# log_level: \"info\" # audit verbosity\n\n# =============================================================================\n# Text-to-Speech\n# =============================================================================\n# TTS defaults to Edge TTS unless changed in ~/.hermes/config.yaml.\n# Gemini TTS supports persona/director prompt files, and Gemini 3.1 Flash TTS\n# can use a hidden auxiliary rewrite pass to insert expressive square-bracket\n# audio tags into the TTS script without showing tags in chat.\n#\n# tts:\n# provider: \"gemini\"\n# gemini:\n# model: \"gemini-3.1-flash-tts-preview\"\n# voice: \"Kore\"\n# audio_tags: false\n# persona_prompt_file: \"\" # e.g. ~/.hermes/tts/radio-host.md\n\n# =============================================================================\n# Voice Transcription (Speech-to-Text)\n# =============================================================================\n# Automatically transcribe voice messages on messaging platforms.\n# Providers: local (free, faster-whisper) | groq (free tier) | openai (Whisper API) | mistral (Voxtral Transcribe)\n# Set the corresponding API key in .env: GROQ_API_KEY, OPENAI_API_KEY, or MISTRAL_API_KEY.\nstt:\n enabled: true\n # provider: \"local\" # auto-detected if omitted\n local:\n model: \"base\" # tiny | base | small | medium | large-v3 | turbo\n # language: \"\" # auto-detect; set to \"en\", \"es\", \"fr\", etc. to force\n openai:\n model: \"whisper-1\" # whisper-1 | gpt-4o-mini-transcribe | gpt-4o-transcribe\n # mistral:\n # model: \"voxtral-mini-latest\" # voxtral-mini-latest | voxtral-mini-2602\n\n# =============================================================================\n# Response Pacing (Messaging Platforms)\n# =============================================================================\n# Add human-like delays between message chunks.\n# human_delay:\n# mode: \"off\" # \"off\" | \"natural\" | \"custom\"\n# min_ms: 800 # Min delay (custom mode only)\n# max_ms: 2500 # Max delay (custom mode only)\n\n# =============================================================================\n# Session Logging\n# =============================================================================\n# Session trajectories are automatically saved to logs/ directory.\n# Each session creates: logs/session_YYYYMMDD_HHMMSS_UUID.json\n#\n# The session ID is displayed in the welcome banner for easy reference.\n# Logs contain full conversation history in trajectory format:\n# - System prompt, user messages, assistant responses\n# - Tool calls with inputs/outputs\n# - Timestamps for debugging\n#\n# No configuration needed - logging is always enabled.\n# To disable, you would need to modify the source code.\n\n# =============================================================================\n# Code Execution Sandbox (Programmatic Tool Calling)\n# =============================================================================\n# The execute_code tool runs Python scripts that call Hermes tools via RPC.\n# Intermediate tool results stay out of the LLM's context window.\ncode_execution:\n timeout: 300 # Max seconds per script before kill (default: 300 = 5 min)\n max_tool_calls: 50 # Max RPC tool calls per execution (default: 50)\n\n# =============================================================================\n# Subagent Delegation\n# =============================================================================\n# The delegate_task tool spawns child agents with isolated context.\n# Supports single tasks and batch mode (default 3 parallel, configurable).\ndelegation:\n max_iterations: 50 # Max tool-calling turns per child (default: 50)\n # max_concurrent_children: 3 # Max parallel child agents per batch (default: 3, floor: 1, no ceiling).\n # WARNING: values above 10 multiply API cost linearly.\n # max_spawn_depth: 1 # Delegation tree depth cap (range: 1-3, default: 1 = flat).\n # Raise to 2 to allow workers to spawn their own subagents.\n # Requires role=\"orchestrator\" on intermediate agents.\n # orchestrator_enabled: true # Kill switch for role=\"orchestrator\" children (default: true).\n # subagent_auto_approve: false # When a subagent hits a dangerous-command approval prompt, auto-deny (default: false)\n # or auto-approve \"once\" (true) instead of blocking on stdin.\n # The parent TUI owns stdin, so blocking would deadlock; non-interactive resolution is required.\n # Both choices emit a logger.warning audit line. Flip to true only for cron/batch pipelines.\n # inherit_mcp_toolsets: true # When explicit child toolsets are narrowed, also keep the parent's MCP toolsets (default: true). Set false for strict intersection.\n # model: \"google/gemini-3-flash-preview\" # Override model for subagents (empty = inherit parent)\n # provider: \"openrouter\" # Override provider for subagents (empty = inherit parent)\n # # Resolves full credentials (base_url, api_key) automatically.\n # # Supported: openrouter, nous, zai, kimi-coding, minimax\n\n# =============================================================================\n# Honcho Integration (Cross-Session User Modeling)\n# =============================================================================\n# AI-native persistent memory via Honcho (https://honcho.dev/).\n# Builds a deeper understanding of the user across sessions and tools.\n# Runs alongside USER.md — additive, not a replacement.\n#\n# Requires: pip install honcho-ai\n# Config: ~/.honcho/config.json (shared with Claude Code, Cursor, etc.)\n# API key: HONCHO_API_KEY in ~/.hermes/.env or ~/.honcho/config.json\n#\n# Hermes-specific overrides (optional — most config comes from ~/.honcho/config.json):\n# honcho: {}\n\n# =============================================================================\n# Display\n# =============================================================================\ndisplay:\n # Use compact banner mode (hides the ASCII-art banner, shows a single line).\n # true: Compact single-line banner\n # false: Full ASCII banner with tool/skill summary (default)\n compact: false\n\n # Tool progress display level (CLI and gateway)\n # off: Silent — no tool activity shown, just the final response\n # new: Show a tool indicator only when the tool changes (skip repeats)\n # all: Show every tool call with a short preview (default)\n # verbose: Full args, results, and debug logs (same as /verbose)\n # log: Silent in chat; append every tool call to ~/.hermes/logs/tool_calls.log (gateway only)\n # Toggle at runtime with /verbose in the CLI\n tool_progress: all\n\n # Per-platform defaults can be quieter than the global setting. Telegram\n # tunes for mobile: tool_progress and busy_ack_detail default off (no\n # per-tool breadcrumb stream, no \"iteration 21/60\" debug detail in busy\n # acks or heartbeats), but interim_assistant_messages and\n # long_running_notifications STAY ON so the user has real signal between\n # turn start and final answer (mid-turn assistant commentary + a single\n # edit-in-place \"⏳ Working — N min\" heartbeat). Override under\n # display.platforms.telegram.\n\n # Auto-cleanup of temporary progress bubbles after the final response lands.\n # On platforms that support message deletion (currently Telegram), this\n # removes the tool-progress bubble, \"⏳ Still working...\" notices, and\n # context-pressure status messages once the final reply has been delivered —\n # keeping long-running turns visible live, then tidy afterward. Failed runs\n # leave the bubbles in place as breadcrumbs. Off by default.\n # Per-platform override: display.platforms.telegram.cleanup_progress\n # true: Delete tracked progress/status bubbles on successful turn\n # false: Leave everything in place (default)\n # Example:\n # display:\n # platforms:\n # telegram:\n # cleanup_progress: true\n cleanup_progress: false\n\n # Gateway-only natural mid-turn assistant updates.\n # When true, completed assistant status messages are sent as separate chat\n # messages. This is independent of tool_progress and gateway streaming.\n # true: Send mid-turn assistant updates as separate messages (default)\n # false: Only send the final response\n interim_assistant_messages: true\n\n # Gateway-only long-running status heartbeats.\n # When false, the platform does not receive periodic \"⏳ Working — N min\"\n # notifications even if agent.gateway_notify_interval is non-zero. The\n # heartbeat edits a single message in place (where the adapter supports\n # editing) instead of posting a new bubble each interval.\n # Default: true everywhere, including Telegram (silent agents are worse\n # than a single edit-in-place heartbeat).\n long_running_notifications: true\n\n # Include detailed iteration/tool/status context in busy acknowledgments\n # and long-running heartbeats. When true, busy acks show \"iteration 21/60,\n # terminal, 10 min\" and the heartbeat shows \"⏳ Working — 12 min,\n # iteration 21/60, terminal\". When false (Telegram default), both stay\n # terse: \"Interrupting current task\" and \"⏳ Working — 12 min, terminal\".\n busy_ack_detail: true\n\n # What Enter does when Hermes is already busy (CLI and gateway platforms).\n # interrupt: Interrupt the current run and redirect Hermes (default)\n # queue: Queue your message for the next turn\n # steer: Inject your message mid-run via /steer, arriving at the agent\n # after the next tool call — no interrupt, no role violation.\n # Falls back to 'queue' if the agent isn't running yet or if\n # images are attached (steer only carries text).\n # Ctrl+C (or /stop in gateway) always interrupts regardless of this setting.\n # Toggle at runtime with /busy .\n busy_input_mode: interrupt\n\n # Background process notifications (gateway/messaging only).\n # Controls how chatty the process watcher is when you use\n # terminal(background=true, notify_on_complete=true) from Telegram/Discord/etc.\n # off: No watcher messages at all\n # result: Only the final completion message\n # error: Only the final message when exit code != 0\n # all: Running output updates + final message (default)\n background_process_notifications: all\n\n\n # Play terminal bell when agent finishes a response.\n # Useful for long-running tasks — your terminal will ding when the agent is done.\n # Works over SSH. Most terminals can be configured to flash the taskbar or play a sound.\n # true: Ring the terminal bell on each response\n # false: Silent (default)\n bell_on_complete: false\n\n # Show model reasoning/thinking before each response.\n # When enabled, a dim box shows the model's thought process above the response.\n # Toggle at runtime with /reasoning show or /reasoning hide.\n # true: Show the reasoning box\n # false: Hide reasoning (default)\n show_reasoning: false\n\n # Stream tokens to the terminal as they arrive instead of waiting for the\n # full response. The response box opens on first token and text appears\n # line-by-line. Tool calls are still captured silently.\n # true: Stream tokens as they arrive (default)\n # false: Wait for the full response before rendering\n streaming: true\n\n # Show [HH:MM] timestamps on user input and assistant response labels.\n # timestamps: false\n\n # ───────────────────────────────────────────────────────────────────────────\n # Skin / Theme\n # ───────────────────────────────────────────────────────────────────────────\n # Customize CLI visual appearance — banner colors, spinner faces, tool prefix,\n # response box label, and branding text. Change at runtime with /skin .\n #\n # Built-in skins:\n # default — Classic Hermes gold/kawaii\n # ares — Crimson/bronze war-god theme with spinner wings\n # mono — Clean grayscale monochrome\n # slate — Cool blue developer-focused\n # daylight — Bright light-mode theme\n # warm-lightmode — Warm paper-tone light-mode theme\n # poseidon — Sea-green/teal Olympian theme\n # sisyphus — Earthy stone-and-moss theme\n # charizard — Fiery orange dragon theme\n #\n # Custom skins: drop a YAML file in ~/.hermes/skins/.yaml\n # Schema (all fields optional, missing values inherit from default):\n #\n # name: my-theme\n # description: Short description\n # colors:\n # banner_border: \"#HEX\" # Panel border\n # banner_title: \"#HEX\" # Panel title\n # banner_accent: \"#HEX\" # Section headers (Available Tools, etc.)\n # banner_dim: \"#HEX\" # Dim/muted text\n # banner_text: \"#HEX\" # Body text (tool names, skill names)\n # ui_accent: \"#HEX\" # UI accent color\n # response_border: \"#HEX\" # Response box border color\n # spinner:\n # waiting_faces: [\"(⚔)\", \"(⛨)\"] # Faces shown while waiting\n # thinking_faces: [\"(⚔)\", \"(⌁)\"] # Faces shown while thinking\n # thinking_verbs: [\"forging\", \"plotting\"] # Verbs for spinner messages\n # wings: # Optional left/right spinner decorations\n # - [\"⟪⚔\", \"⚔⟫\"]\n # - [\"⟪▲\", \"▲⟫\"]\n # branding:\n # agent_name: \"My Agent\" # Banner title and branding\n # welcome: \"Welcome message\" # Shown at CLI startup\n # response_label: \" ⚔ Agent \" # Response box header label\n # prompt_symbol: \"⚔\" # Prompt symbol (bare token; renderers add trailing space)\n # tool_prefix: \"╎\" # Tool output line prefix (default: ┊)\n #\n skin: default\n\n# =============================================================================\n# Model Aliases — short names for /model command\n# =============================================================================\n# Map short aliases to exact (model, provider, base_url) tuples.\n# Used by /model tab completion and resolve_alias().\n# Aliases are checked BEFORE the models.dev catalog, so they can route\n# to endpoints not in the catalog (e.g. Ollama Cloud, local servers).\n#\n# model_aliases:\n# opus:\n# model: claude-opus-4-6\n# provider: anthropic\n# qwen:\n# model: \"qwen3.5:397b\"\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n# glm:\n# model: glm-4.7\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n\n# =============================================================================\n# Privacy\n# =============================================================================\n# privacy:\n# # Redact PII from the LLM context prompt.\n# # When true, phone numbers are stripped and user/chat IDs are replaced\n# # with deterministic hashes before being sent to the model.\n# # Names and usernames are NOT affected (user-chosen, publicly visible).\n# # Routing/delivery still uses the original values internally.\n# redact_pii: false\n\n# =============================================================================\n# Shell-script hooks\n# =============================================================================\n# Register shell scripts as plugin-hook callbacks. Each entry is executed as\n# a subprocess (shell=False, shlex.split) with a JSON payload on stdin. On\n# stdout the script may return JSON that either blocks the tool call or\n# injects context into the next LLM call.\n#\n# Valid events (mirror hermes_cli.plugins.VALID_HOOKS):\n# pre_tool_call, post_tool_call, pre_llm_call, post_llm_call,\n# pre_api_request, post_api_request, on_session_start, on_session_end,\n# on_session_finalize, on_session_reset, subagent_stop\n#\n# First-use consent: each (event, command) pair prompts once on a TTY, then\n# is persisted to ~/.hermes/shell-hooks-allowlist.json. Non-interactive\n# runs (gateway, cron) need --accept-hooks, HERMES_ACCEPT_HOOKS=1, or the\n# hooks_auto_accept key below.\n#\n# See website/docs/user-guide/features/hooks.md for the full JSON wire\n# protocol and worked examples.\n#\n# hooks:\n# pre_tool_call:\n# - matcher: \"terminal\"\n# command: \"~/.hermes/agent-hooks/block-rm-rf.sh\"\n# timeout: 10\n# post_tool_call:\n# - matcher: \"write_file|patch\"\n# command: \"~/.hermes/agent-hooks/auto-format.sh\"\n# pre_llm_call:\n# - command: \"~/.hermes/agent-hooks/inject-cwd-context.sh\"\n# subagent_stop:\n# - command: \"~/.hermes/agent-hooks/log-orchestration.sh\"\n#\n# hooks_auto_accept: false\n\n\n# =============================================================================\n# Update Behavior\n# =============================================================================\nupdates:\n # Create a full HERMES_HOME zip before every `hermes update`.\n # Backups land in ~/.hermes/backups/ and can be restored with `hermes import`.\n # Off by default because large homes can add minutes to every update.\n pre_update_backup: false\n\n # Number of pre-update backup zips to retain.\n backup_keep: 5\n\n # What non-interactive updates do with local source edits in the Hermes repo.\n # Interactive terminal updates always prompt before restoring the autostash.\n #\n # stash - auto-stash before pull, then auto-restore after success (default)\n # discard - drop the update-created stash after success; use only on managed\n # installs where local source edits should not persist\n non_interactive_local_changes: \"stash\"\n\n\n# =============================================================================\n# Web Dashboard\n# =============================================================================\n# OAuth gate configuration for `hermes dashboard --host `.\n# The bundled Nous Portal plugin reads these on startup; settings here are\n# the canonical surface. Each can be overridden by an environment variable:\n#\n# dashboard.oauth.client_id <- HERMES_DASHBOARD_OAUTH_CLIENT_ID\n# dashboard.oauth.portal_url <- HERMES_DASHBOARD_PORTAL_URL\n# dashboard.public_url <- HERMES_DASHBOARD_PUBLIC_URL\n#\n# Env wins when set to a non-empty value. This is what Fly.io's platform-\n# secret injection uses to push per-deploy client_ids without needing to\n# bake a config.yaml into the image. Empty env values are treated as unset\n# so a provisioned-but-not-populated secret can't shadow a valid entry here.\n#\n# Local dev / on-prem deploys should typically set these via config.yaml\n# (the ~/.hermes/.env file is reserved for API keys and secrets).\n#\n# dashboard:\n# oauth:\n# client_id: \"\" # agent:{instance_id}; Portal provisions this at deploy\n# portal_url: \"\" # blank → default https://portal.nousresearch.com\n#\n# # Force the absolute base URL the OAuth callback (and any other public\n# # URL the dashboard hands to external systems) is built from. Set this\n# # for deploys behind reverse proxies that don't reliably forward\n# # X-Forwarded-Host / X-Forwarded-Proto / X-Forwarded-Prefix (manual\n# # nginx setups, on-prem ingresses, custom-domain Fly deploys without\n# # full proxy header chains).\n# #\n# # When set, the value is the complete authority: scheme + host +\n# # optional path prefix (e.g. \"https://example.com/hermes\"). The OAuth\n# # callback URL becomes \"/auth/callback\" — X-Forwarded-Prefix\n# # is IGNORED on this code path because the operator has explicitly\n# # declared the public URL and we no longer need to guess.\n# #\n# # Leave empty to use the existing proxy-header reconstruction (the\n# # default — works on Fly.io out of the box).\n# #\n# # public_url: \"https://example.com/hermes\"\n#\n# -----------------------------------------------------------------------------\n# Self-hosted OIDC dashboard auth (generic OpenID Connect — Authentik,\n# Keycloak, Zitadel, Authelia, Auth0, Okta, Google, …). Use this INSTEAD of the\n# nous block above when gating the dashboard with your own identity provider.\n# Each setting can be overridden by an environment variable:\n#\n# dashboard.oauth.self_hosted.issuer <- HERMES_DASHBOARD_OIDC_ISSUER\n# dashboard.oauth.self_hosted.client_id <- HERMES_DASHBOARD_OIDC_CLIENT_ID\n# dashboard.oauth.self_hosted.scopes <- HERMES_DASHBOARD_OIDC_SCOPES\n# dashboard.oauth.self_hosted.client_secret <- HERMES_DASHBOARD_OIDC_CLIENT_SECRET\n#\n# dashboard:\n# oauth:\n# provider: self-hosted\n# self_hosted:\n# issuer: \"https://auth.example.com/application/o/hermes/\" # required\n# client_id: \"hermes-dashboard\" # required\n# scopes: \"openid profile email\" # optional\n#\n# # OPTIONAL — set ONLY if your IDP registered the client as\n# # *confidential* (Authentik / Keycloak often default to this). When\n# # set, Hermes authenticates the client at the token endpoint\n# # (client_secret_basic or client_secret_post, auto-selected from the\n# # IDP's discovery doc) IN ADDITION to PKCE. Leave unset for a public\n# # (PKCE-only) client — the common case.\n# #\n# # This is a CREDENTIAL: prefer setting HERMES_DASHBOARD_OIDC_CLIENT_SECRET\n# # in ~/.hermes/.env over putting it here in config.yaml.\n# # client_secret: \"\"\n\n# =============================================================================\n# External secret sources\n# =============================================================================\n# Pull provider credentials from external secret managers at process startup\n# instead of storing them in ~/.hermes/.env. Only the manager's bootstrap\n# token (e.g. BWS_ACCESS_TOKEN) lives in .env; everything else rotates\n# centrally in the vault. Multiple sources can be enabled at once:\n# - \"mapped\" sources (explicit VAR -> ref bindings) beat \"bulk\" sources\n# (whole-project dumps like Bitwarden BSM)\n# - within a shape, the first source to claim a var wins; later claims\n# are skipped with a startup warning (never a silent clobber)\n# - a source's override_existing lets it beat .env/shell values, but\n# never another secret source's claim\n# Docs: https://hermes-agent.nousresearch.com/docs/user-guide/secrets/\n#\n# secrets:\n# # Optional explicit ordering of enabled sources.\n# # sources: [bitwarden]\n# bitwarden:\n# enabled: false\n# project_id: \"\" # BSM project UUID\n# access_token_env: BWS_ACCESS_TOKEN\n# cache_ttl_seconds: 300 # 0 disables memory+disk caching\n# override_existing: true # BSM wins over .env so rotation works\n# auto_install: true # auto-download the pinned bws binary\n# server_url: \"\" # e.g. https://vault.bitwarden.eu (EU cloud)\n"}, "b98baa3039e357d97ccb8d84e6d70b1902a03582": {"t": 1785767883.9798365, "n": 70590, "h": "630f1070cd93f9a3", "c": "# Hermes Agent CLI Configuration\n# Copy this file to cli-config.yaml and customize as needed.\n# This file configures the CLI behavior. Environment variables in .env take precedence.\n\n# =============================================================================\n# Model Configuration\n# =============================================================================\nmodel:\n # Default model to use (can be overridden with --model flag)\n # Both \"default\" and \"model\" work as the key name here.\n default: \"anthropic/claude-opus-4.6\"\n \n # Inference provider selection:\n # \"auto\" - Auto-detect from credentials (default)\n # \"openrouter\" - OpenRouter (requires: OPENROUTER_API_KEY or OPENAI_API_KEY)\n # \"nous\" - Nous Portal OAuth (requires: hermes auth add nous)\n # \"nous-api\" - Nous Portal API key (requires: NOUS_API_KEY)\n # \"anthropic\" - Direct Anthropic API (requires: ANTHROPIC_API_KEY)\n # \"openai-codex\" - OpenAI Codex (requires: hermes auth)\n # \"copilot\" - GitHub Copilot / GitHub Models (requires: GITHUB_TOKEN)\n # \"gemini\" - Use Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n # \"zai\" - Use z.ai / ZhipuAI GLM models (requires: GLM_API_KEY)\n # \"kimi-coding\" - Kimi / Moonshot AI (requires: KIMI_API_KEY)\n # \"minimax\" - MiniMax global (requires: MINIMAX_API_KEY)\n # \"minimax-cn\" - MiniMax China (requires: MINIMAX_CN_API_KEY)\n # \"huggingface\" - Hugging Face Inference (requires: HF_TOKEN)\n # \"nvidia\" - NVIDIA NIM / build.nvidia.com (requires: NVIDIA_API_KEY)\n # \"xiaomi\" - Xiaomi MiMo (requires: XIAOMI_API_KEY)\n # \"arcee\" - Arcee AI Trinity models (requires: ARCEEAI_API_KEY)\n # \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY — https://ollama.com/settings)\n # \"kilocode\" - KiloCode gateway (requires: KILOCODE_API_KEY)\n # \"azure-foundry\" - Microsoft Foundry / Azure OpenAI (API key or Entra ID)\n # \"lmstudio\" - LM Studio local server (optional: LM_API_KEY, defaults to http://127.0.0.1:1234/v1)\n #\n # Local servers (LM Studio, Ollama, vLLM, llama.cpp):\n # \"custom\" - Any other OpenAI-compatible endpoint. Set base_url below.\n # Aliases: \"ollama\", \"vllm\", \"llamacpp\" all map to \"custom\".\n # LM Studio is first-class and uses provider: \"lmstudio\".\n # It works with both no-auth and auth-enabled server modes.\n #\n # Can also be overridden for a single invocation with the --provider flag.\n provider: \"auto\"\n \n # API configuration (falls back to OPENROUTER_API_KEY env var)\n # api_key: \"your-key-here\" # Uncomment to set here instead of .env\n base_url: \"https://openrouter.ai/api/v1\"\n\n # Azure Foundry keyless auth example:\n # provider: \"azure-foundry\"\n # base_url: \"https://.openai.azure.com/openai/v1\"\n # auth_mode: \"entra_id\" # DefaultAzureCredential: az login, managed identity, workload identity, etc.\n # default: \"gpt-4o\" # Deployment/model name\n # entra:\n # scope: \"https://ai.azure.com/.default\" # Optional; this is the default.\n\n # ── Token limits — two settings, easy to confuse ──────────────────────────\n #\n # context_length: TOTAL context window (input + output tokens combined).\n # Controls when Hermes compresses history and validates requests.\n # Leave unset — Hermes auto-detects the correct value from the provider.\n # Set manually only when auto-detection is wrong (e.g. a local server with\n # a custom num_ctx, or a proxy that doesn't expose /v1/models).\n #\n # context_length: 131072\n #\n # max_tokens: OUTPUT cap — maximum tokens the model may generate per response.\n # Unrelated to how long your conversation history can be.\n # The OpenAI-standard name \"max_tokens\" is a misnomer; Anthropic's native\n # API has since renamed it \"max_output_tokens\" for clarity.\n # Leave unset to use the model's native output ceiling (recommended).\n # Set only if you want to deliberately limit individual response length.\n #\n# max_tokens: 8192\n\n # ── Custom request headers (optional) ─────────────────────────────────────\n #\n # default_headers: extra HTTP headers sent on every request to an\n # OpenAI-compatible endpoint. User values take precedence over the\n # provider/SDK defaults, so this is the supported way to override the\n # OpenAI Python SDK's identifying headers (User-Agent: OpenAI/Python ...,\n # X-Stainless-*) when a custom provider sits behind a gateway/WAF that\n # rejects them — e.g. an upstream that returns \"502 Upstream access\n # forbidden\" for the SDK default User-Agent but accepts a plain one.\n # Applies on the OpenAI wire only (not native Anthropic / Bedrock).\n #\n # default_headers:\n # User-Agent: \"curl/8.7.1\"\n #\n # extra_headers: accepted as an alias of default_headers (merged, with\n # extra_headers winning when both are set) — matches the per-provider\n # extra_headers key below.\n #\n # Per-provider variant: named providers / custom_providers entries accept an\n # extra_headers dict scoped to that endpoint only — for reverse proxies,\n # gateways, or custom auth (e.g. Cloudflare Access service tokens).\n # Merged onto SDK/provider defaults with the entry's values winning.\n # Header values are treated as secrets and are never logged.\n #\n # providers:\n # my-proxy:\n # base_url: \"https://llm.internal.example.com/v1\"\n # key_env: \"MY_PROXY_API_KEY\"\n # extra_headers:\n # CF-Access-Client-Id: \"xxxx.access\"\n # CF-Access-Client-Secret: \"${CF_ACCESS_SECRET}\"\n # X-Client-Name: \"hermes-agent\"\n\n# Named provider overrides (optional)\n# Use this for per-provider request timeouts, non-stream stale timeouts,\n# and per-model exceptions.\n# Applies to the primary turn client on every api_mode (OpenAI-wire, native\n# Anthropic, and Anthropic-compatible providers), the fallback chain, and\n# client rebuilds during credential rotation. For OpenAI-wire chat\n# completions (streaming and non-streaming) the configured value is also\n# used as the per-request ``timeout=`` kwarg so it wins over the legacy\n# HERMES_API_TIMEOUT env var (which still applies when no config is set).\n# ``stale_timeout_seconds`` controls the non-streaming stale-call detector and\n# wins over the legacy HERMES_API_CALL_STALE_TIMEOUT env var. Leaving these\n# unset keeps the legacy defaults (HERMES_API_TIMEOUT=1800s,\n# HERMES_API_CALL_STALE_TIMEOUT=90s, native Anthropic 900s). The\n# implicit non-stream stale detector is auto-disabled for local endpoints\n# and can scale upward for very large contexts.\n#\n# Not currently wired for AWS Bedrock (bedrock_converse + AnthropicBedrock\n# SDK paths) — those use boto3 with its own timeout configuration.\n#\n# providers:\n# ollama-local:\n# request_timeout_seconds: 300 # Longer timeout for local cold-starts\n# stale_timeout_seconds: 900 # Explicitly re-enable stale detection on local endpoints\n# anthropic:\n# request_timeout_seconds: 30 # Fast-fail cloud requests\n# models:\n# claude-opus-4.6:\n# timeout_seconds: 600 # Longer timeout for extended-thinking Opus calls\n# openai-codex:\n# models:\n# gpt-5.4:\n# stale_timeout_seconds: 1800 # Longer non-stream stale timeout for slow large-context turns\n\n# =============================================================================\n# OpenRouter Provider Routing (only applies when using OpenRouter)\n# =============================================================================\n# Control how requests are routed across providers on OpenRouter.\n# See: https://openrouter.ai/docs/guides/routing/provider-selection\n#\n# provider_routing:\n# # Sort strategy: \"price\" (default), \"throughput\", or \"latency\"\n# # Append :nitro to model name for a shortcut to throughput sorting.\n# sort: \"throughput\"\n#\n# # Only allow these providers (provider slugs from OpenRouter)\n# # only: [\"anthropic\", \"google\"]\n#\n# # Skip these providers entirely\n# # ignore: [\"deepinfra\", \"fireworks\"]\n#\n# # Try providers in this order (overrides default load balancing)\n# # order: [\"anthropic\", \"google\", \"together\"]\n#\n# # Require providers to support all parameters in your request\n# # require_parameters: true\n#\n# # Data policy: \"allow\" (default) or \"deny\" to exclude providers that may store data\n# # data_collection: \"deny\"\n\n# =============================================================================\n# OpenRouter Response Caching (only applies when using OpenRouter)\n# =============================================================================\n# Cache identical API responses at the OpenRouter edge for free instant replays.\n# When enabled, identical requests (same model, messages, parameters) return\n# cached responses with zero billing. Separate from Anthropic prompt caching.\n# See: https://openrouter.ai/docs/guides/features/response-caching\n#\n# openrouter:\n# response_cache: true # Enable response caching (default: true)\n# response_cache_ttl: 300 # Cache TTL in seconds, 1-86400 (default: 300)\n\n# =============================================================================\n# Git Worktree Isolation\n# =============================================================================\n# When enabled, each CLI session creates an isolated git worktree so multiple\n# agents can work on the same repo concurrently without file collisions.\n# Equivalent to always passing --worktree / -w on the command line.\n#\n# worktree: true # Always create a worktree when in a git repo\n# worktree: false # Default — only create when -w flag is passed\n#\n# By default a new worktree branches from the freshly-fetched remote tip\n# (the current branch's upstream, else the remote's default branch) so it\n# starts current with the project instead of from the local clone's\n# (possibly stale) HEAD. Set worktree_sync: false to branch from local HEAD\n# instead — useful when offline or when you deliberately want the clone's\n# exact current state as the base.\n#\n# worktree_sync: true # Default — branch from the fetched remote tip\n# worktree_sync: false # Branch from local HEAD (offline / pinned base)\n\n# =============================================================================\n# Terminal Tool Configuration\n# =============================================================================\n# Choose ONE of the following terminal configurations by uncommenting it.\n# The terminal tool executes commands in the specified environment.\n\n# -----------------------------------------------------------------------------\n# OPTION 1: Local execution (default)\n# Commands run directly on your machine in the current directory\n# -----------------------------------------------------------------------------\n# Working directory behavior:\n# - CLI (`hermes` command): Uses \".\" (current directory where you run hermes)\n# - Gateway/messaging/cron: Uses terminal.cwd here; legacy .env cwd values are deprecated\nterminal:\n backend: \"local\"\n cwd: \".\" # For local backend: \".\" = current directory. Ignored for remote backends unless a backend documents otherwise.\n timeout: 180\n # HOME policy for tool subprocesses:\n # auto - default: host uses your real HOME; containers use HERMES_HOME/home\n # real - force your real OS-user HOME\n # profile - force HERMES_HOME/home for strict per-profile CLI config isolation\n home_mode: \"auto\"\n docker_mount_cwd_to_workspace: false # SECURITY: off by default. Opt in to mount the launch cwd into Docker /workspace.\n lifetime_seconds: 300\n # sudo_password: \"hunter2\" # Optional: pipe a sudo password via sudo -S. SECURITY WARNING: plaintext.\n # sudo_password: \"\" # Explicit empty password: try empty and never open the interactive sudo prompt.\n\n# -----------------------------------------------------------------------------\n# OPTION 2: SSH remote execution\n# Commands run on a remote server - agent code stays local (sandboxed)\n# Great for: keeping agent isolated from its own code, using powerful remote hardware\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"ssh\"\n# cwd: \"/home/myuser/project\" # Path on the REMOTE server\n# timeout: 180\n# lifetime_seconds: 300\n# ssh_host: \"my-server.example.com\"\n# ssh_user: \"myuser\"\n# ssh_port: 22\n# ssh_key: \"~/.ssh/id_rsa\" # Optional - uses ssh-agent if not specified\n\n# -----------------------------------------------------------------------------\n# OPTION 3: Docker container\n# Commands run in an isolated Docker container\n# Great for: reproducible environments, testing, isolation\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"docker\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /)\n# timeout: 180\n# lifetime_seconds: 300\n# docker_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# docker_mount_cwd_to_workspace: true # Explicit opt-in: mount your launch cwd into /workspace\n# # Optional: run the container as your host user's uid:gid so files written\n# # into bind-mounted dirs are owned by you, not root. Drops SETUID/SETGID\n# # caps too since no gosu privilege drop is needed. Leave off if your\n# # chosen docker_image expects to start as root.\n# docker_run_as_host_user: true\n# # Optional: explicitly forward selected env vars into Docker.\n# # These values come from your current shell first, then ~/.hermes/.env.\n# # Warning: anything forwarded here is visible to commands run in the container.\n# docker_forward_env:\n# - \"GITHUB_TOKEN\"\n# - \"NPM_TOKEN\"\n# # Optional: extra flags passed verbatim to docker run (appended after security defaults).\n# # Useful for adding capabilities (e.g. apt installs needing SETUID) or custom options.\n# # Example: add a Linux capability not included by default\n# # docker_extra_args:\n# # - \"--cap-add\"\n# # - \"SETUID\"\n\n# -----------------------------------------------------------------------------\n# OPTION 4: Singularity/Apptainer container\n# Commands run in a Singularity container (common in HPC environments)\n# Great for: HPC clusters, shared compute environments\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"singularity\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# singularity_image: \"docker://nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 5: Modal cloud execution\n# Commands run on Modal's cloud infrastructure\n# Great for: GPU access, scalable compute, serverless execution\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"modal\"\n# cwd: \"/workspace\" # Path INSIDE the sandbox (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# modal_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 6: Daytona cloud execution\n# Commands run in Daytona cloud sandboxes\n# Great for: Cloud dev environments, persistent workspaces, team collaboration\n# Requires: pip install daytona, DAYTONA_API_KEY env var\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"daytona\"\n# cwd: \"~\"\n# timeout: 180\n# lifetime_seconds: 300\n# daytona_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# container_disk: 10240 # Daytona max is 10GB per sandbox\n\n#\n# --- Container resource limits (docker, singularity, modal, daytona -- ignored for local/ssh) ---\n# These settings apply to all container backends. They control the resources\n# allocated to the sandbox and whether its filesystem persists across sessions.\n container_cpu: 1 # CPU cores\n container_memory: 5120 # Memory in MB (5120 = 5GB)\n container_disk: 51200 # Disk in MB (51200 = 50GB)\n container_persistent: true # Persist filesystem across sessions (false = ephemeral)\n\n# -----------------------------------------------------------------------------\n# SUDO SUPPORT (works with ALL backends above)\n# -----------------------------------------------------------------------------\n# Add sudo_password to any terminal config above to enable sudo commands.\n# The password is piped via `sudo -S`. Works with local, ssh, docker, etc.\n#\n# SECURITY WARNING: Password stored in plaintext!\n#\n# INTERACTIVE PROMPT: If sudo_password is unset and the CLI is running,\n# you'll be prompted to enter your password when sudo is needed:\n# - 45-second timeout (auto-skips if no input)\n# - Press Enter to skip (command fails gracefully)\n# - Password is hidden while typing\n# - Password is cached for the session\n#\n# EMPTY PASSWORDS: Setting sudo_password to an explicit empty string is different\n# from leaving it unset. Hermes will try an empty password via `sudo -S` and\n# will not open the interactive prompt. This is useful for passwordless sudo,\n# Touch ID sudo setups, and environments where prompting is just noise.\n#\n# ALTERNATIVES:\n# - SSH backend: Configure passwordless sudo on the remote server\n# - Containers: Run as root inside the container (no sudo needed)\n# - Local: Configure /etc/sudoers for specific commands\n#\n# Example (add to your terminal section):\n# sudo_password: \"your-password-here\"\n\n# =============================================================================\n# Security Scanning (tirith)\n# =============================================================================\n# Optional pre-exec command security scanning via tirith.\n# Detects homograph URLs, pipe-to-shell, terminal injection, env manipulation.\n# Install: brew install sheeki03/tap/tirith\n# Docs: https://github.com/sheeki03/tirith\n#\n# security:\n# tirith_enabled: true # Enable/disable tirith scanning\n# tirith_path: \"tirith\" # Path to tirith binary (supports ~ expansion)\n# tirith_timeout: 5 # Scan timeout in seconds\n# tirith_fail_open: true # Allow commands if tirith unavailable\n\n# =============================================================================\n# Browser Tool Configuration\n# =============================================================================\nbrowser:\n # Inactivity timeout in seconds - browser sessions are automatically closed\n # after this period of no activity between agent loops (default: 120 = 2 minutes)\n inactivity_timeout: 120\n\n# =============================================================================\n# Tool Loop Guardrails\n# =============================================================================\n# Soft warnings are enabled by default. They append guidance to repeated failed\n# or non-progressing tool results but still let the tool execute. Hard stops are\n# opt-in circuit breakers for autonomous/cron sessions where stopping a loop is\n# preferable to spending the full iteration budget.\ntool_loop_guardrails:\n warnings_enabled: true\n hard_stop_enabled: false\n warn_after:\n exact_failure: 2\n same_tool_failure: 3\n idempotent_no_progress: 2\n hard_stop_after:\n exact_failure: 5\n same_tool_failure: 8\n idempotent_no_progress: 5\n\n# =============================================================================\n# Context Compression (Auto-shrinks long conversations)\n# =============================================================================\n# When conversation approaches model's context limit, middle turns are\n# automatically summarized to free up space while preserving important context.\n#\n# HOW IT WORKS:\n# 1. Tracks actual token usage from API responses (not estimates)\n# 2. When prompt_tokens >= threshold% of model's context_length, triggers compression\n# 3. Protects first 3 turns (system prompt, initial request, first response)\n# 4. Protects last N turns (default 20 messages = ~10 full turns of recent context)\n# 5. Summarizes middle turns using a fast/cheap model\n# 6. Inserts summary as a user message, continues conversation seamlessly\n#\n# Post-compression tail budget is target_ratio × threshold × context_length:\n# 200K context, threshold 0.50, ratio 0.20 → 20K tokens of recent tail preserved\n# 1M context, threshold 0.50, ratio 0.20 → 100K tokens of recent tail preserved\n#\ncompression:\n # Enable automatic context compression (default: true)\n # Set to false if you prefer to manage context manually or want errors on overflow\n enabled: true\n \n # Trigger compression at this % of model's context limit (default: 0.50 = 50%)\n # Lower values = more aggressive compression, higher values = compress later\n threshold: 0.50\n \n # Fraction of the threshold to preserve as recent tail (default: 0.20 = 20%)\n # e.g. 20% of 50% threshold = 10% of total context kept as recent messages.\n # Summary output is separately capped at 12K tokens (Gemini output limit).\n # Range: 0.10 - 0.80\n target_ratio: 0.20\n\n # Number of most-recent messages to always preserve (default: 20 ≈ 10 full turns)\n # Higher values keep more recent conversation intact at the cost of more aggressive\n # compression of older turns.\n protect_last_n: 20\n\n # Number of non-system messages to protect at the head of the transcript, in\n # ADDITION to the system prompt (which is always implicitly protected).\n # Head messages are NEVER summarized — they survive every compression\n # indefinitely. This gives stable early context for short/medium sessions,\n # but in long-running sessions that rely on rolling compaction the pinned\n # opening turns may not match how you want the session framed over time.\n # Set to 0 to preserve ONLY the system prompt (plus the rolling summary\n # and recent tail) — the cleanest configuration for long-running sessions.\n # Default 3 preserves the system prompt plus the first three non-system\n # head messages, matching the pre-feature behaviour.\n protect_first_n: 3\n\n # To pin a specific model/provider for compression summaries, use the\n # auxiliary section below (auxiliary.compression.provider / model).\n\n# =============================================================================\n# Anthropic prompt caching TTL\n# =============================================================================\n# When prompt caching is active (Claude via OpenRouter or native Anthropic),\n# Anthropic supports two TTL tiers for cached prefixes: \"5m\" (default) and\n# \"1h\". Other values are ignored and \"5m\" is used.\n#\nprompt_caching:\n cache_ttl: \"5m\" # use \"1h\" for long sessions with pauses between turns\n\n# =============================================================================\n# Auxiliary Models (Advanced — Experimental)\n# =============================================================================\n# Hermes uses lightweight \"auxiliary\" models for side tasks: image analysis,\n# browser screenshot analysis, web page summarization, TTS audio-tag insertion,\n# and context compression.\n#\n# By default these use Gemini Flash via OpenRouter or Nous Portal and are\n# auto-detected from your credentials. You do NOT need to change anything\n# here for normal usage.\n#\n# WARNING: Overriding these with providers other than OpenRouter or Nous Portal\n# is EXPERIMENTAL and may not work. Not all models/providers support vision,\n# produce usable summaries, or accept the same API format. Change at your own\n# risk — if things break, reset to \"auto\" / empty values.\n#\n# Each task has its own provider + model pair so you can mix providers.\n# For example: OpenRouter for vision (needs multimodal), but your main\n# local endpoint for compression (just needs text).\n#\n# Provider options:\n# \"auto\" - Best available: OpenRouter → Nous Portal → main endpoint (default)\n# \"openrouter\" - Force OpenRouter (requires OPENROUTER_API_KEY)\n# \"nous\" - Force Nous Portal (requires: hermes auth add nous)\n# \"gemini\" - Force Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n# \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY)\n# \"codex\" - Force Codex OAuth (requires: hermes model → Codex).\n# Uses gpt-5.3-codex which supports vision.\n# \"main\" - Use your custom endpoint (OPENAI_BASE_URL + OPENAI_API_KEY).\n# Works with OpenAI API, local models, or any OpenAI-compatible\n# endpoint. Also falls back to Codex OAuth and API-key providers.\n#\n# Model: leave empty to use the provider's default. When empty, OpenRouter\n# uses \"google/gemini-3-flash-preview\" and Nous uses \"gemini-3-flash\".\n# Other providers pick a sensible default automatically.\n#\n# auxiliary:\n# # Image analysis: vision_analyze tool + browser screenshots\n# vision:\n# provider: \"auto\"\n# model: \"\" # e.g. \"google/gemini-2.5-flash\", \"openai/gpt-4o\"\n# timeout: 30 # LLM API call timeout (seconds)\n# download_timeout: 30 # Image HTTP download timeout (seconds)\n# # Increase for slow connections or self-hosted image servers\n#\n# # Web page scraping / summarization + browser page text extraction\n# web_extract:\n# provider: \"auto\"\n# model: \"\"\n#\n# # Gemini 3.1 TTS hidden audio-tag insertion\n# tts_audio_tags:\n# provider: \"auto\" # empty model = your main chat model\n# model: \"\"\n# timeout: 30\n#\n# # Session search — summarizes matching past sessions\n# session_search:\n# provider: \"auto\"\n# model: \"\"\n# timeout: 30\n# max_concurrency: 3 # Limit parallel summaries to reduce request-burst 429s\n# extra_body: {} # Provider-specific OpenAI-compatible request fields\n# # Example for providers that support request-body\n# # reasoning controls:\n# # extra_body:\n# # enable_thinking: false\n# # Some vLLM/Qwen deployments expect this nested:\n# # extra_body:\n# # chat_template_kwargs:\n# # enable_thinking: false\n\n# =============================================================================\n# Persistent Memory\n# =============================================================================\n# Bounded curated memory injected into the system prompt every session.\n# Two stores: MEMORY.md (agent's notes) and USER.md (user profile).\n# Character limits keep the memory small and focused. The agent manages\n# pruning -- when at the limit, it must consolidate or replace entries.\n# Disabled by default in batch_runner.\n#\nmemory:\n # Agent's personal notes: environment facts, conventions, things learned\n memory_enabled: true\n \n # User profile: preferences, communication style, expectations\n user_profile_enabled: true\n \n # Character limits (~2.75 chars per token, model-independent)\n memory_char_limit: 2200 # ~800 tokens\n user_char_limit: 1375 # ~500 tokens\n\n # Periodic memory nudge: remind the agent to consider saving memories\n # every N user turns. Set to 0 to disable. Only active when memory is enabled.\n nudge_interval: 10 # Nudge every 10 user turns (0 = disabled)\n\n # Memory flush: give the agent one turn to save memories before context is\n # lost (compression, /new, /reset, exit). Set to 0 to disable.\n # For exit/reset, only fires if the session had at least this many user turns.\n flush_min_turns: 6 # Min user turns to trigger flush on exit/reset (0 = disabled)\n\n# =============================================================================\n# Session Reset Policy (Messaging Platforms)\n# =============================================================================\n# Controls when messaging sessions (Telegram, Discord, WhatsApp, Slack) are\n# automatically cleared. Without resets, conversation context grows indefinitely\n# which increases API costs with every message.\n#\n# When a reset triggers, the agent first saves important information to its\n# persistent memory — but the conversation context is wiped. The agent starts\n# fresh but retains learned facts via its memory system.\n#\n# Users can always manually reset with /reset or /new in chat.\n#\n# Modes:\n# \"both\" - Reset on EITHER inactivity timeout or daily boundary (recommended)\n# \"idle\" - Reset only after N minutes of inactivity\n# \"daily\" - Reset only at a fixed hour each day\n# \"none\" - Never auto-reset; context lives until /reset or compression kicks in\n#\n# When a reset triggers, the agent gets one turn to save important memories and\n# skills before the context is wiped. Persistent memory carries across sessions.\n#\nsession_reset:\n mode: both # \"both\", \"idle\", \"daily\", or \"none\"\n idle_minutes: 1440 # Inactivity timeout in minutes (default: 1440 = 24 hours)\n at_hour: 4 # Daily reset hour, 0-23 local time (default: 4 AM)\n\n# Maximum number of simultaneously active chat sessions across CLI, TUI,\n# dashboard chat, and messaging gateway. Set to null, 0, or omit to allow\n# unlimited concurrent sessions. When the limit is reached, new sessions get a\n# clean error while existing active sessions keep their normal behavior. This\n# top-level key takes precedence over gateway.max_concurrent_sessions. The cap\n# is a best-effort single-host/profile runtime guard; Hermes fails open if the\n# local runtime lease registry cannot be read or locked.\nmax_concurrent_sessions: null\n\n# When true, group/channel chats use one session per participant when the platform\n# provides a user ID. This is the secure default and prevents users in the same\n# room from sharing context, interrupts, and token costs. Set false only if you\n# explicitly want one shared \"room brain\" per group/channel.\ngroup_sessions_per_user: true\n\n# ─────────────────────────────────────────────────────────────────────────────\n# API Server — per-client model routing\n# ─────────────────────────────────────────────────────────────────────────────\n# Route different API clients to different models/providers on a single\n# Hermes deployment. Clients choose a backend by sending a specific string\n# as the OpenAI ``model`` field. Unmapped model values fall back to the\n# global model configured in the ``model:`` section above, and an explicit\n# session /model override always wins over a route.\n#\n# Configure via the ``platforms.api_server.extra.model_routes`` gateway\n# config block:\n#\n# platforms:\n# api_server:\n# enabled: true\n# extra:\n# key: \"your-api-server-secret\"\n# model_routes:\n# # Xiaozhi clients send model=\"minimax-m2\" → routed to MiniMax via OpenRouter\n# minimax-m2:\n# model: \"minimax/minimax-m1\"\n# provider: \"openrouter\" # optional — overrides global provider\n# # api_key: \"sk-...\" # optional — per-route UPSTREAM provider\n# # key (NOT caller auth; never logged)\n# # base_url: \"https://...\" # optional — per-route base URL\n# # GPT clients keep their own alias\n# gpt-5:\n# model: \"openai/gpt-5\"\n# provider: \"openrouter\"\n#\n# Configured aliases are automatically listed by GET /v1/models so clients\n# can discover them without manual coordination. Caller authentication is\n# unchanged: every request still authenticates with the global API server\n# key (``extra.key`` / API_SERVER_KEY).\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Gateway Streaming\n# ─────────────────────────────────────────────────────────────────────────────\n# Stream tokens to messaging platforms in real-time. The bot sends a message\n# on first token, then progressively edits it as more tokens arrive.\n# Disabled by default — enable to try the streaming UX on Telegram/Discord/Slack.\n# For Telegram, partial edits are sent as plain text and only the final edit uses MarkdownV2.\nstreaming:\n enabled: false\n # transport: edit # \"edit\" = progressive editMessageText\n # edit_interval: 0.3 # seconds between message edits\n # buffer_threshold: 40 # chars before forcing an edit flush\n # cursor: \" ▉\" # cursor shown during streaming\n\n# =============================================================================\n# Skills Configuration\n# =============================================================================\n# Skills are reusable procedures the agent can load and follow. The agent can\n# also create new skills after completing complex tasks.\n#\nskills:\n # Nudge the agent to create skills after complex tasks.\n # Every N tool-calling iterations, remind the model to consider saving a skill.\n # Set to 0 to disable.\n creation_nudge_interval: 15\n\n # External skill directories — share skills across tools/agents without\n # copying them into ~/.hermes/skills/. Each path is expanded (~ and ${VAR})\n # and resolved to an absolute path. External dirs are read-only: skill\n # creation always writes to ~/.hermes/skills/. Local skills take precedence\n # when names collide.\n # external_dirs:\n # - ~/.agents/skills\n # - /home/shared/team-skills\n\n# =============================================================================\n# Agent Behavior\n# =============================================================================\nagent:\n # Maximum tool-calling iterations per conversation\n # Higher = more room for complex tasks, but costs more tokens\n # Recommended: 20-30 for focused tasks, 50-100 for open exploration\n max_turns: 60\n\n # Inactivity timeout for gateway agent runs (seconds, 0 = unlimited).\n # The agent can run indefinitely when actively calling tools or receiving\n # API responses. Only fires after the agent has been idle for this duration.\n # gateway_timeout: 1800\n\n # Staged warning: send a warning before escalating to full timeout.\n # Fires once per run when inactivity reaches this threshold (seconds).\n # Set to 0 to disable the warning.\n # gateway_timeout_warning: 900\n\n # Graceful drain timeout for gateway stop/restart (seconds).\n # Default 0 = no drain: a restart interrupts in-flight agents immediately,\n # cleans up, and exits. Set a positive value only if you want a grace\n # window on /restart, and keep it well under systemd's TimeoutStopSec.\n # restart_drain_timeout: 0\n\n # Max app-level retry attempts for API errors (connection drops, provider\n # timeouts, 5xx, etc.) before the agent surfaces the failure. Lower this\n # to 1 if you use fallback providers and want fast failover on flaky\n # primaries (default 3). The OpenAI SDK does its own low-level retries\n # underneath this wrapper — this is the Hermes-level loop.\n # api_max_retries: 3\n\n # After the agent edits code without fresh passing verification, nudge it to\n # verify before finishing. The default \"auto\" enables it on interactive\n # coding surfaces (CLI, TUI, desktop) and programmatic callers, and disables\n # it on conversational messaging surfaces (Telegram, Discord, etc.) where the\n # verification summary would reach a human as chat noise. Set true or false to\n # force it on or off; the HERMES_VERIFY_ON_STOP env var (1/0) takes precedence.\n # verify_on_stop: auto\n\n # Standing operator instructions for the coding posture (when Hermes is in a\n # code workspace). Appended to the coding brief as an extra system block, so\n # you can pin project-wide workflow rules without editing the shipped brief.\n # Accepts a string or a list of strings. Takes effect next session.\n # coding_instructions:\n # - \"For UI work, don't run tsc/lint until I approve the look.\"\n # - \"Clean the diff before you commit and push.\"\n\n # When verify-on-stop finds edited code without fresh verification evidence,\n # append guidance for creative UI work (avoid broad tsc/lint/test before visual\n # approval) and clean-diff expectations. Set false to keep that nudge terse.\n # verify_guidance: true\n\n # A `pre_verify` hook (plugin or shell, see Event Hooks docs) can keep the\n # agent going one more turn to verify/clean before finishing. This caps how\n # many times one turn may be nudged to continue, so a hook can't trap the loop.\n # Default 3.\n # max_verify_nudges: 3\n\n # Enable verbose logging\n verbose: false\n \n # Reasoning effort level (OpenRouter and Nous Portal)\n # Controls how much \"thinking\" the model does before responding.\n # Options: \"xhigh\" (max), \"high\", \"medium\", \"low\", \"minimal\", \"none\" (disable)\n reasoning_effort: \"medium\"\n \n # Predefined personalities (use with /personality command)\n personalities:\n helpful: \"You are a helpful, friendly AI assistant.\"\n concise: \"You are a concise assistant. Keep responses brief and to the point.\"\n technical: \"You are a technical expert. Provide detailed, accurate technical information.\"\n creative: \"You are a creative assistant. Think outside the box and offer innovative solutions.\"\n teacher: \"You are a patient teacher. Explain concepts clearly with examples.\"\n kawaii: \"You are a kawaii assistant! Use cute expressions like (◕‿◕), ★, ♪, and ~! Add sparkles and be super enthusiastic about everything! Every response should feel warm and adorable desu~! ヽ(>∀<☆)ノ\"\n catgirl: \"You are Neko-chan, an anime catgirl AI assistant, nya~! Add 'nya' and cat-like expressions to your speech. Use kaomoji like (=^・ω・^=) and ฅ^•ﻌ•^ฅ. Be playful and curious like a cat, nya~!\"\n pirate: \"Arrr! Ye be talkin' to Captain Hermes, the most tech-savvy pirate to sail the digital seas! Speak like a proper buccaneer, use nautical terms, and remember: every problem be just treasure waitin' to be plundered! Yo ho ho!\"\n shakespeare: \"Hark! Thou speakest with an assistant most versed in the bardic arts. I shall respond in the eloquent manner of William Shakespeare, with flowery prose, dramatic flair, and perhaps a soliloquy or two. What light through yonder terminal breaks?\"\n surfer: \"Duuude! You're chatting with the chillest AI on the web, bro! Everything's gonna be totally rad. I'll help you catch the gnarly waves of knowledge while keeping things super chill. Cowabunga! 🤙\"\n noir: \"The rain hammered against the terminal like regrets on a guilty conscience. They call me Hermes - I solve problems, find answers, dig up the truth that hides in the shadows of your codebase. In this city of silicon and secrets, everyone's got something to hide. What's your story, pal?\"\n uwu: \"hewwo! i'm your fwiendwy assistant uwu~ i wiww twy my best to hewp you! *nuzzles your code* OwO what's this? wet me take a wook! i pwomise to be vewy hewpful >w<\"\n philosopher: \"Greetings, seeker of wisdom. I am an assistant who contemplates the deeper meaning behind every query. Let us examine not just the 'how' but the 'why' of your questions. Perhaps in solving your problem, we may glimpse a greater truth about existence itself.\"\n hype: \"YOOO LET'S GOOOO!!! 🔥🔥🔥 I am SO PUMPED to help you today! Every question is AMAZING and we're gonna CRUSH IT together! This is gonna be LEGENDARY! ARE YOU READY?! LET'S DO THIS! 💪😤🚀\"\n\n# =============================================================================\n# Toolsets\n# =============================================================================\n# Control which tools the agent has access to.\n# Use `hermes tools` to interactively enable/disable tools per platform.\n\n# =============================================================================\n# Platform Toolsets (per-platform tool configuration)\n# =============================================================================\n# Override which toolsets are available on each platform.\n# If a platform isn't listed here, its built-in default is used.\n#\n# You can use EITHER:\n# - A preset like \"hermes-cli\" or \"hermes-telegram\" (curated tool set)\n# - A list of individual toolsets to compose your own (see list below)\n#\n# Supported platform keys: cli, telegram, discord, whatsapp, slack, qqbot, teams, google_chat\n#\n# Examples:\n#\n# # Use presets (same as defaults):\n# platform_toolsets:\n# cli: [hermes-cli]\n# telegram: [hermes-telegram]\n#\n# # Custom: give Telegram only web + terminal + file + planning:\n# platform_toolsets:\n# telegram: [web, terminal, file, todo]\n#\n# # Custom: CLI without browser or image gen:\n# platform_toolsets:\n# cli: [web, terminal, file, skills, todo, tts, cronjob]\n#\n# # Restrictive: Discord gets read-only tools only:\n# platform_toolsets:\n# discord: [web, vision, skills, todo]\n#\n# If not set, defaults are:\n# cli: hermes-cli (everything + cronjob management)\n# telegram: hermes-telegram (terminal, file, web, vision, image, tts, browser, skills, todo, cronjob, messaging)\n# discord: hermes-discord (same as telegram)\n# whatsapp: hermes-whatsapp (same as telegram)\n# slack: hermes-slack (same as telegram)\n# signal: hermes-signal (same as telegram)\n# homeassistant: hermes-homeassistant (same as telegram)\n# qqbot: hermes-qqbot (same as telegram)\n# teams: hermes-teams (same as telegram)\n# google_chat: hermes-google_chat (same as telegram)\n#\nplatform_toolsets:\n cli: [hermes-cli]\n telegram: [hermes-telegram]\n discord: [hermes-discord]\n whatsapp: [hermes-whatsapp]\n slack: [hermes-slack]\n signal: [hermes-signal]\n homeassistant: [hermes-homeassistant]\n qqbot: [hermes-qqbot]\n yuanbao: [hermes-yuanbao]\n teams: [hermes-teams]\n google_chat: [hermes-google_chat]\n\n# =============================================================================\n# Gateway Platform Settings\n# =============================================================================\n# Optional per-platform messaging settings.\n# Platform-specific knobs live under `extra`.\n#\n# platforms:\n# telegram:\n# reply_to_mode: \"first\" # off | first | all\n# # guest_mode lets explicit @mentions from non-allowlisted groups through.\n# # Default false; ordinary messages, replies, and regex wake words stay blocked.\n# guest_mode: false\n# # allowed_chats: [\"-1001234567890\"]\n# extra:\n# disable_link_previews: false # Set true to suppress Telegram URL previews in bot messages\n# rich_messages: false # Bot API 10.1 rich messages (tables/task lists/details/math); default false for copyable legacy MarkdownV2, set true to opt in\n# rich_drafts: false # Experimental rich draft previews during Telegram DM streaming; default false because Telegram Desktop/macOS can visually overlay draft frames\n# command_menu:\n# # Telegram allows up to 100 BotCommands; Hermes defaults to 60 so\n# # all built-in commands plus common skill commands stay visible\n# # while remaining under Telegram's payload-size limit. Clamped 1..100.\n# max_commands: 60\n# # prepend = user priority first, then Hermes defaults\n# # append = Hermes defaults first, then user priority\n# # replace = only the list below defines priority\n# priority_mode: prepend\n# priority:\n# - my_plugin_command\n#\n# Discord-specific settings (config.yaml top-level, not under platforms:):\n#\n# discord:\n# require_mention: true # Require @mention in server channels (default: true)\n# auto_thread: true # Auto-create thread on @mention (default: true)\n# free_response_channels: \"\" # Channel IDs where no mention is needed\n# reactions: true # Show processing reactions (default: true)\n# history_backfill: true # Recover missed channel messages on mention (default: true)\n# history_backfill_limit: 50 # Max messages to scan backwards (default: 50)\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Available toolsets (use these names in platform_toolsets or the toolsets list)\n#\n# Run `hermes chat --list-toolsets` to see all toolsets and their tools.\n# Run `hermes chat --list-tools` to see every individual tool with descriptions.\n# ─────────────────────────────────────────────────────────────────────────────\n#\n# INDIVIDUAL TOOLSETS (compose your own):\n# web - web_search, web_extract\n# search - web_search only (no scraping)\n# terminal - terminal, process\n# file - read_file, write_file, patch, search\n# browser - browser_navigate, browser_snapshot, browser_click, browser_type,\n# browser_scroll, browser_back, browser_press,\n# browser_get_images, browser_vision (requires BROWSERBASE_API_KEY)\n# vision - vision_analyze (requires OPENROUTER_API_KEY)\n# image_gen - image_generate (requires FAL_KEY)\n# skills - skills_list, skill_view\n# skills_hub - skill_hub (search/install/manage from online registries — user-driven only)\n# todo - todo (in-memory task planning, no deps)\n# tts - text_to_speech (Edge TTS free, or ELEVENLABS/OPENAI/MINIMAX/MISTRAL key)\n# cronjob - cronjob (create/list/update/pause/resume/run/remove scheduled tasks)\n#\n# PRESETS (curated bundles):\n# hermes-cli - All of the above except send_message\n# hermes-telegram - terminal, file, web, vision, image_gen, tts, browser,\n# skills, todo, cronjob, send_message\n# hermes-discord - Same as hermes-telegram\n# hermes-whatsapp - Same as hermes-telegram\n# hermes-slack - Same as hermes-telegram\n#\n# COMPOSITE:\n# debugging - terminal + web + file\n# safe - web + vision (no terminal access)\n# all - Everything available\n#\n# web - Web search and content extraction (web_search, web_extract)\n# search - Web search only, no scraping (web_search)\n# terminal - Command execution and process management (terminal, process)\n# file - File operations: read, write, patch, search\n# browser - Full browser automation (navigate, click, type, screenshot, etc.)\n# vision - Image analysis (vision_analyze)\n# image_gen - Image generation with FLUX (image_generate)\n# skills - Load skill documents (skills_list, skill_view)\n# todo - Task planning and tracking for multi-step work\n# memory - Persistent memory across sessions (personal notes + user profile)\n# session_search - Search and recall past conversations (FTS5 + Gemini Flash summarization)\n# tts - Text-to-speech (Edge TTS free, ElevenLabs, OpenAI, MiniMax, Mistral)\n# cronjob - Schedule and manage automated tasks (CLI-only)\n#\n# Composite toolsets:\n# debugging - terminal + web + file (for troubleshooting)\n# safe - web + vision (no terminal access)\n\n# NOTE: The top-level \"toolsets\" key is deprecated and ignored.\n# Tool configuration is managed per-platform via platform_toolsets above.\n# Use `hermes tools` to configure interactively, or edit platform_toolsets directly.\n#\n# CLI override: hermes chat --toolsets terminal,web,file\n\n# =============================================================================\n# MCP (Model Context Protocol) Servers\n# =============================================================================\n# Connect to external MCP servers to add tools from the MCP ecosystem.\n# Each server's tools are automatically discovered and registered.\n# See website/docs/user-guide/features/mcp.md for full documentation.\n#\n# Stdio servers (spawn a subprocess):\n# command: the executable to run\n# args: command-line arguments\n# env: environment variables (only these + safe defaults passed to subprocess)\n#\n# HTTP servers (connect to a URL):\n# url: the MCP server endpoint\n# headers: HTTP headers (e.g., for authentication)\n#\n# Optional per-server settings:\n# timeout: tool call timeout in seconds (default: 120)\n# connect_timeout: initial connection timeout (default: 60)\n# keepalive_interval: liveness ping cadence in seconds (default: 180).\n# Lower it below the server's session TTL for servers that expire idle\n# sessions quickly (e.g. Unreal Engine editor MCP, ~15s), otherwise idle\n# tool calls hit an expired session and pay a slow reconnect. Floored at 5s.\n#\n# mcp_servers:\n# time:\n# command: uvx\n# args: [\"mcp-server-time\"]\n# filesystem:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-filesystem\", \"/home/user\"]\n# notion:\n# url: https://mcp.notion.com/mcp\n# github:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-github\"]\n# env:\n# GITHUB_PERSONAL_ACCESS_TOKEN: \"ghp_...\"\n#\n# Sampling (server-initiated LLM requests) — enabled by default.\n# Per-server config under the 'sampling' key:\n# analysis:\n# command: npx\n# args: [\"-y\", \"analysis-server\"]\n# sampling:\n# enabled: true # default: true\n# model: \"gemini-3-flash\" # override model (optional)\n# max_tokens_cap: 4096 # max tokens per request\n# timeout: 30 # LLM call timeout (seconds)\n# max_rpm: 10 # max requests per minute\n# allowed_models: [] # model whitelist (empty = all)\n# max_tool_rounds: 5 # tool loop limit (0 = disable)\n# log_level: \"info\" # audit verbosity\n\n# =============================================================================\n# Text-to-Speech\n# =============================================================================\n# TTS defaults to Edge TTS unless changed in ~/.hermes/config.yaml.\n# Gemini TTS supports persona/director prompt files, and Gemini 3.1 Flash TTS\n# can use a hidden auxiliary rewrite pass to insert expressive square-bracket\n# audio tags into the TTS script without showing tags in chat.\n#\n# tts:\n# provider: \"gemini\"\n# gemini:\n# model: \"gemini-3.1-flash-tts-preview\"\n# voice: \"Kore\"\n# audio_tags: false\n# persona_prompt_file: \"\" # e.g. ~/.hermes/tts/radio-host.md\n\n# =============================================================================\n# Voice Transcription (Speech-to-Text)\n# =============================================================================\n# Automatically transcribe voice messages on messaging platforms.\n# Providers: local (free, faster-whisper) | groq (free tier) | openai (Whisper API) | mistral (Voxtral Transcribe)\n# Set the corresponding API key in .env: GROQ_API_KEY, OPENAI_API_KEY, or MISTRAL_API_KEY.\nstt:\n enabled: true\n # provider: \"local\" # auto-detected if omitted\n local:\n model: \"base\" # tiny | base | small | medium | large-v3 | turbo\n # language: \"\" # auto-detect; set to \"en\", \"es\", \"fr\", etc. to force\n openai:\n model: \"whisper-1\" # whisper-1 | gpt-4o-mini-transcribe | gpt-4o-transcribe\n # mistral:\n # model: \"voxtral-mini-latest\" # voxtral-mini-latest | voxtral-mini-2602\n\n# =============================================================================\n# Response Pacing (Messaging Platforms)\n# =============================================================================\n# Add human-like delays between message chunks.\n# human_delay:\n# mode: \"off\" # \"off\" | \"natural\" | \"custom\"\n# min_ms: 800 # Min delay (custom mode only)\n# max_ms: 2500 # Max delay (custom mode only)\n\n# =============================================================================\n# Session Logging\n# =============================================================================\n# Session trajectories are automatically saved to logs/ directory.\n# Each session creates: logs/session_YYYYMMDD_HHMMSS_UUID.json\n#\n# The session ID is displayed in the welcome banner for easy reference.\n# Logs contain full conversation history in trajectory format:\n# - System prompt, user messages, assistant responses\n# - Tool calls with inputs/outputs\n# - Timestamps for debugging\n#\n# No configuration needed - logging is always enabled.\n# To disable, you would need to modify the source code.\n\n# =============================================================================\n# Code Execution Sandbox (Programmatic Tool Calling)\n# =============================================================================\n# The execute_code tool runs Python scripts that call Hermes tools via RPC.\n# Intermediate tool results stay out of the LLM's context window.\ncode_execution:\n timeout: 300 # Max seconds per script before kill (default: 300 = 5 min)\n max_tool_calls: 50 # Max RPC tool calls per execution (default: 50)\n\n# =============================================================================\n# Subagent Delegation\n# =============================================================================\n# The delegate_task tool spawns child agents with isolated context.\n# Supports single tasks and batch mode (default 3 parallel, configurable).\ndelegation:\n max_iterations: 50 # Max tool-calling turns per child (default: 50)\n # max_concurrent_children: 3 # Max parallel child agents per batch (default: 3, floor: 1, no ceiling).\n # WARNING: values above 10 multiply API cost linearly.\n # max_spawn_depth: 1 # Delegation tree depth cap (range: 1-3, default: 1 = flat).\n # Raise to 2 to allow workers to spawn their own subagents.\n # Requires role=\"orchestrator\" on intermediate agents.\n # orchestrator_enabled: true # Kill switch for role=\"orchestrator\" children (default: true).\n # subagent_auto_approve: false # When a subagent hits a dangerous-command approval prompt, auto-deny (default: false)\n # or auto-approve \"once\" (true) instead of blocking on stdin.\n # The parent TUI owns stdin, so blocking would deadlock; non-interactive resolution is required.\n # Both choices emit a logger.warning audit line. Flip to true only for cron/batch pipelines.\n # inherit_mcp_toolsets: true # When explicit child toolsets are narrowed, also keep the parent's MCP toolsets (default: true). Set false for strict intersection.\n # model: \"google/gemini-3-flash-preview\" # Override model for subagents (empty = inherit parent)\n # provider: \"openrouter\" # Override provider for subagents (empty = inherit parent)\n # # Resolves full credentials (base_url, api_key) automatically.\n # # Supported: openrouter, nous, zai, kimi-coding, minimax\n\n# =============================================================================\n# Honcho Integration (Cross-Session User Modeling)\n# =============================================================================\n# AI-native persistent memory via Honcho (https://honcho.dev/).\n# Builds a deeper understanding of the user across sessions and tools.\n# Runs alongside USER.md — additive, not a replacement.\n#\n# Requires: pip install honcho-ai\n# Config: ~/.honcho/config.json (shared with Claude Code, Cursor, etc.)\n# API key: HONCHO_API_KEY in ~/.hermes/.env or ~/.honcho/config.json\n#\n# Hermes-specific overrides (optional — most config comes from ~/.honcho/config.json):\n# honcho: {}\n\n# =============================================================================\n# Display\n# =============================================================================\ndisplay:\n # Use compact banner mode (hides the ASCII-art banner, shows a single line).\n # true: Compact single-line banner\n # false: Full ASCII banner with tool/skill summary (default)\n compact: false\n\n # Tool progress display level (CLI and gateway)\n # off: Silent — no tool activity shown, just the final response\n # new: Show a tool indicator only when the tool changes (skip repeats)\n # all: Show every tool call with a short preview (default)\n # verbose: Full args, results, and debug logs (same as /verbose)\n # log: Silent in chat; append every tool call to ~/.hermes/logs/tool_calls.log (gateway only)\n # Toggle at runtime with /verbose in the CLI\n tool_progress: all\n\n # Per-platform defaults can be quieter than the global setting. Telegram\n # tunes for mobile: tool_progress and busy_ack_detail default off (no\n # per-tool breadcrumb stream, no \"iteration 21/60\" debug detail in busy\n # acks or heartbeats), but interim_assistant_messages and\n # long_running_notifications STAY ON so the user has real signal between\n # turn start and final answer (mid-turn assistant commentary + a single\n # edit-in-place \"⏳ Working — N min\" heartbeat). Override under\n # display.platforms.telegram.\n\n # Auto-cleanup of temporary progress bubbles after the final response lands.\n # On platforms that support message deletion (currently Telegram), this\n # removes the tool-progress bubble, \"⏳ Still working...\" notices, and\n # context-pressure status messages once the final reply has been delivered —\n # keeping long-running turns visible live, then tidy afterward. Failed runs\n # leave the bubbles in place as breadcrumbs. Off by default.\n # Per-platform override: display.platforms.telegram.cleanup_progress\n # true: Delete tracked progress/status bubbles on successful turn\n # false: Leave everything in place (default)\n # Example:\n # display:\n # platforms:\n # telegram:\n # cleanup_progress: true\n cleanup_progress: false\n\n # Gateway-only natural mid-turn assistant updates.\n # When true, completed assistant status messages are sent as separate chat\n # messages. This is independent of tool_progress and gateway streaming.\n # true: Send mid-turn assistant updates as separate messages (default)\n # false: Only send the final response\n interim_assistant_messages: true\n\n # Gateway-only long-running status heartbeats.\n # When false, the platform does not receive periodic \"⏳ Working — N min\"\n # notifications even if agent.gateway_notify_interval is non-zero. The\n # heartbeat edits a single message in place (where the adapter supports\n # editing) instead of posting a new bubble each interval.\n # Default: true everywhere, including Telegram (silent agents are worse\n # than a single edit-in-place heartbeat).\n long_running_notifications: true\n\n # Include detailed iteration/tool/status context in busy acknowledgments\n # and long-running heartbeats. When true, busy acks show \"iteration 21/60,\n # terminal, 10 min\" and the heartbeat shows \"⏳ Working — 12 min,\n # iteration 21/60, terminal\". When false (Telegram default), both stay\n # terse: \"Interrupting current task\" and \"⏳ Working — 12 min, terminal\".\n busy_ack_detail: true\n\n # What Enter does when Hermes is already busy (CLI and gateway platforms).\n # interrupt: Interrupt the current run and redirect Hermes (default)\n # queue: Queue your message for the next turn\n # steer: Inject your message mid-run via /steer, arriving at the agent\n # after the next tool call — no interrupt, no role violation.\n # Falls back to 'queue' if the agent isn't running yet or if\n # images are attached (steer only carries text).\n # Ctrl+C (or /stop in gateway) always interrupts regardless of this setting.\n # Toggle at runtime with /busy .\n busy_input_mode: interrupt\n\n # Background process notifications (gateway/messaging only).\n # Controls how chatty the process watcher is when you use\n # terminal(background=true, notify_on_complete=true) from Telegram/Discord/etc.\n # off: No watcher messages at all\n # result: Only the final completion message\n # error: Only the final message when exit code != 0\n # all: Running output updates + final message (default)\n background_process_notifications: all\n\n\n # Play terminal bell when agent finishes a response.\n # Useful for long-running tasks — your terminal will ding when the agent is done.\n # Works over SSH. Most terminals can be configured to flash the taskbar or play a sound.\n # true: Ring the terminal bell on each response\n # false: Silent (default)\n bell_on_complete: false\n\n # Show model reasoning/thinking before each response.\n # When enabled, a dim box shows the model's thought process above the response.\n # Toggle at runtime with /reasoning show or /reasoning hide.\n # true: Show the reasoning box\n # false: Hide reasoning (default)\n show_reasoning: false\n\n # Stream tokens to the terminal as they arrive instead of waiting for the\n # full response. The response box opens on first token and text appears\n # line-by-line. Tool calls are still captured silently.\n # true: Stream tokens as they arrive (default)\n # false: Wait for the full response before rendering\n streaming: true\n\n # Show [HH:MM] timestamps on user input and assistant response labels.\n # timestamps: false\n\n # ───────────────────────────────────────────────────────────────────────────\n # Skin / Theme\n # ───────────────────────────────────────────────────────────────────────────\n # Customize CLI visual appearance — banner colors, spinner faces, tool prefix,\n # response box label, and branding text. Change at runtime with /skin .\n #\n # Built-in skins:\n # default — Classic Hermes gold/kawaii\n # ares — Crimson/bronze war-god theme with spinner wings\n # mono — Clean grayscale monochrome\n # slate — Cool blue developer-focused\n # daylight — Bright light-mode theme\n # warm-lightmode — Warm paper-tone light-mode theme\n # poseidon — Sea-green/teal Olympian theme\n # sisyphus — Earthy stone-and-moss theme\n # charizard — Fiery orange dragon theme\n #\n # Custom skins: drop a YAML file in ~/.hermes/skins/.yaml\n # Schema (all fields optional, missing values inherit from default):\n #\n # name: my-theme\n # description: Short description\n # colors:\n # banner_border: \"#HEX\" # Panel border\n # banner_title: \"#HEX\" # Panel title\n # banner_accent: \"#HEX\" # Section headers (Available Tools, etc.)\n # banner_dim: \"#HEX\" # Dim/muted text\n # banner_text: \"#HEX\" # Body text (tool names, skill names)\n # ui_accent: \"#HEX\" # UI accent color\n # response_border: \"#HEX\" # Response box border color\n # spinner:\n # waiting_faces: [\"(⚔)\", \"(⛨)\"] # Faces shown while waiting\n # thinking_faces: [\"(⚔)\", \"(⌁)\"] # Faces shown while thinking\n # thinking_verbs: [\"forging\", \"plotting\"] # Verbs for spinner messages\n # wings: # Optional left/right spinner decorations\n # - [\"⟪⚔\", \"⚔⟫\"]\n # - [\"⟪▲\", \"▲⟫\"]\n # branding:\n # agent_name: \"My Agent\" # Banner title and branding\n # welcome: \"Welcome message\" # Shown at CLI startup\n # response_label: \" ⚔ Agent \" # Response box header label\n # prompt_symbol: \"⚔\" # Prompt symbol (bare token; renderers add trailing space)\n # tool_prefix: \"╎\" # Tool output line prefix (default: ┊)\n #\n skin: default\n\n# =============================================================================\n# Model Aliases — short names for /model command\n# =============================================================================\n# Map short aliases to exact (model, provider, base_url) tuples.\n# Used by /model tab completion and resolve_alias().\n# Aliases are checked BEFORE the models.dev catalog, so they can route\n# to endpoints not in the catalog (e.g. Ollama Cloud, local servers).\n#\n# model_aliases:\n# opus:\n# model: claude-opus-4-6\n# provider: anthropic\n# qwen:\n# model: \"qwen3.5:397b\"\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n# glm:\n# model: glm-4.7\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n\n# =============================================================================\n# Privacy\n# =============================================================================\n# privacy:\n# # Redact PII from the LLM context prompt.\n# # When true, phone numbers are stripped and user/chat IDs are replaced\n# # with deterministic hashes before being sent to the model.\n# # Names and usernames are NOT affected (user-chosen, publicly visible).\n# # Routing/delivery still uses the original values internally.\n# redact_pii: false\n\n# =============================================================================\n# Shell-script hooks\n# =============================================================================\n# Register shell scripts as plugin-hook callbacks. Each entry is executed as\n# a subprocess (shell=False, shlex.split) with a JSON payload on stdin. On\n# stdout the script may return JSON that either blocks the tool call or\n# injects context into the next LLM call.\n#\n# Valid events (mirror hermes_cli.plugins.VALID_HOOKS):\n# pre_tool_call, post_tool_call, pre_llm_call, post_llm_call,\n# pre_api_request, post_api_request, on_session_start, on_session_end,\n# on_session_finalize, on_session_reset, subagent_stop\n#\n# First-use consent: each (event, command) pair prompts once on a TTY, then\n# is persisted to ~/.hermes/shell-hooks-allowlist.json. Non-interactive\n# runs (gateway, cron) need --accept-hooks, HERMES_ACCEPT_HOOKS=1, or the\n# hooks_auto_accept key below.\n#\n# See website/docs/user-guide/features/hooks.md for the full JSON wire\n# protocol and worked examples.\n#\n# hooks:\n# pre_tool_call:\n# - matcher: \"terminal\"\n# command: \"~/.hermes/agent-hooks/block-rm-rf.sh\"\n# timeout: 10\n# post_tool_call:\n# - matcher: \"write_file|patch\"\n# command: \"~/.hermes/agent-hooks/auto-format.sh\"\n# pre_llm_call:\n# - command: \"~/.hermes/agent-hooks/inject-cwd-context.sh\"\n# subagent_stop:\n# - command: \"~/.hermes/agent-hooks/log-orchestration.sh\"\n#\n# hooks_auto_accept: false\n\n\n# =============================================================================\n# Update Behavior\n# =============================================================================\nupdates:\n # Create a full HERMES_HOME zip before every `hermes update`.\n # Backups land in ~/.hermes/backups/ and can be restored with `hermes import`.\n # Off by default because large homes can add minutes to every update.\n pre_update_backup: false\n\n # Number of pre-update backup zips to retain.\n backup_keep: 5\n\n # What non-interactive updates do with local source edits in the Hermes repo.\n # Interactive terminal updates always prompt before restoring the autostash.\n #\n # stash - auto-stash before pull, then auto-restore after success (default)\n # discard - drop the update-created stash after success; use only on managed\n # installs where local source edits should not persist\n non_interactive_local_changes: \"stash\"\n\n\n# =============================================================================\n# Web Dashboard\n# =============================================================================\n# OAuth gate configuration for `hermes dashboard --host `.\n# The bundled Nous Portal plugin reads these on startup; settings here are\n# the canonical surface. Each can be overridden by an environment variable:\n#\n# dashboard.oauth.client_id <- HERMES_DASHBOARD_OAUTH_CLIENT_ID\n# dashboard.oauth.portal_url <- HERMES_DASHBOARD_PORTAL_URL\n# dashboard.public_url <- HERMES_DASHBOARD_PUBLIC_URL\n#\n# Env wins when set to a non-empty value. This is what Fly.io's platform-\n# secret injection uses to push per-deploy client_ids without needing to\n# bake a config.yaml into the image. Empty env values are treated as unset\n# so a provisioned-but-not-populated secret can't shadow a valid entry here.\n#\n# Local dev / on-prem deploys should typically set these via config.yaml\n# (the ~/.hermes/.env file is reserved for API keys and secrets).\n#\n# dashboard:\n# oauth:\n# client_id: \"\" # agent:{instance_id}; Portal provisions this at deploy\n# portal_url: \"\" # blank → default https://portal.nousresearch.com\n#\n# # Force the absolute base URL the OAuth callback (and any other public\n# # URL the dashboard hands to external systems) is built from. Set this\n# # for deploys behind reverse proxies that don't reliably forward\n# # X-Forwarded-Host / X-Forwarded-Proto / X-Forwarded-Prefix (manual\n# # nginx setups, on-prem ingresses, custom-domain Fly deploys without\n# # full proxy header chains).\n# #\n# # When set, the value is the complete authority: scheme + host +\n# # optional path prefix (e.g. \"https://example.com/hermes\"). The OAuth\n# # callback URL becomes \"/auth/callback\" — X-Forwarded-Prefix\n# # is IGNORED on this code path because the operator has explicitly\n# # declared the public URL and we no longer need to guess.\n# #\n# # Leave empty to use the existing proxy-header reconstruction (the\n# # default — works on Fly.io out of the box).\n# #\n# # public_url: \"https://example.com/hermes\"\n#\n# -----------------------------------------------------------------------------\n# Self-hosted OIDC dashboard auth (generic OpenID Connect — Authentik,\n# Keycloak, Zitadel, Authelia, Auth0, Okta, Google, …). Use this INSTEAD of the\n# nous block above when gating the dashboard with your own identity provider.\n# Each setting can be overridden by an environment variable:\n#\n# dashboard.oauth.self_hosted.issuer <- HERMES_DASHBOARD_OIDC_ISSUER\n# dashboard.oauth.self_hosted.client_id <- HERMES_DASHBOARD_OIDC_CLIENT_ID\n# dashboard.oauth.self_hosted.scopes <- HERMES_DASHBOARD_OIDC_SCOPES\n# dashboard.oauth.self_hosted.client_secret <- HERMES_DASHBOARD_OIDC_CLIENT_SECRET\n#\n# dashboard:\n# oauth:\n# provider: self-hosted\n# self_hosted:\n# issuer: \"https://auth.example.com/application/o/hermes/\" # required\n# client_id: \"hermes-dashboard\" # required\n# scopes: \"openid profile email\" # optional\n#\n# # OPTIONAL — set ONLY if your IDP registered the client as\n# # *confidential* (Authentik / Keycloak often default to this). When\n# # set, Hermes authenticates the client at the token endpoint\n# # (client_secret_basic or client_secret_post, auto-selected from the\n# # IDP's discovery doc) IN ADDITION to PKCE. Leave unset for a public\n# # (PKCE-only) client — the common case.\n# #\n# # This is a CREDENTIAL: prefer setting HERMES_DASHBOARD_OIDC_CLIENT_SECRET\n# # in ~/.hermes/.env over putting it here in config.yaml.\n# # client_secret: \"\"\n"}, "4a09b692ecc385ad48f00694a1e315b8eed120cd": {"t": 1785767885.4623592, "n": 69754, "h": "aad6e5f2ca273c0b", "c": "# Hermes Agent CLI Configuration\n# Copy this file to cli-config.yaml and customize as needed.\n# This file configures the CLI behavior. Environment variables in .env take precedence.\n\n# =============================================================================\n# Model Configuration\n# =============================================================================\nmodel:\n # Default model to use (can be overridden with --model flag)\n # Both \"default\" and \"model\" work as the key name here.\n default: \"anthropic/claude-opus-4.6\"\n \n # Inference provider selection:\n # \"auto\" - Auto-detect from credentials (default)\n # \"openrouter\" - OpenRouter (requires: OPENROUTER_API_KEY or OPENAI_API_KEY)\n # \"nous\" - Nous Portal OAuth (requires: hermes auth add nous)\n # \"nous-api\" - Nous Portal API key (requires: NOUS_API_KEY)\n # \"anthropic\" - Direct Anthropic API (requires: ANTHROPIC_API_KEY)\n # \"openai-codex\" - OpenAI Codex (requires: hermes auth)\n # \"copilot\" - GitHub Copilot / GitHub Models (requires: GITHUB_TOKEN)\n # \"gemini\" - Use Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n # \"zai\" - Use z.ai / ZhipuAI GLM models (requires: GLM_API_KEY)\n # \"kimi-coding\" - Kimi / Moonshot AI (requires: KIMI_API_KEY)\n # \"minimax\" - MiniMax global (requires: MINIMAX_API_KEY)\n # \"minimax-cn\" - MiniMax China (requires: MINIMAX_CN_API_KEY)\n # \"huggingface\" - Hugging Face Inference (requires: HF_TOKEN)\n # \"nvidia\" - NVIDIA NIM / build.nvidia.com (requires: NVIDIA_API_KEY)\n # \"xiaomi\" - Xiaomi MiMo (requires: XIAOMI_API_KEY)\n # \"arcee\" - Arcee AI Trinity models (requires: ARCEEAI_API_KEY)\n # \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY — https://ollama.com/settings)\n # \"kilocode\" - KiloCode gateway (requires: KILOCODE_API_KEY)\n # \"azure-foundry\" - Microsoft Foundry / Azure OpenAI (API key or Entra ID)\n # \"lmstudio\" - LM Studio local server (optional: LM_API_KEY, defaults to http://127.0.0.1:1234/v1)\n #\n # Local servers (LM Studio, Ollama, vLLM, llama.cpp):\n # \"custom\" - Any other OpenAI-compatible endpoint. Set base_url below.\n # Aliases: \"ollama\", \"vllm\", \"llamacpp\" all map to \"custom\".\n # LM Studio is first-class and uses provider: \"lmstudio\".\n # It works with both no-auth and auth-enabled server modes.\n #\n # Can also be overridden for a single invocation with the --provider flag.\n provider: \"auto\"\n \n # API configuration (falls back to OPENROUTER_API_KEY env var)\n # api_key: \"your-key-here\" # Uncomment to set here instead of .env\n base_url: \"https://openrouter.ai/api/v1\"\n\n # Azure Foundry keyless auth example:\n # provider: \"azure-foundry\"\n # base_url: \"https://.openai.azure.com/openai/v1\"\n # auth_mode: \"entra_id\" # DefaultAzureCredential: az login, managed identity, workload identity, etc.\n # default: \"gpt-4o\" # Deployment/model name\n # entra:\n # scope: \"https://ai.azure.com/.default\" # Optional; this is the default.\n\n # ── Token limits — two settings, easy to confuse ──────────────────────────\n #\n # context_length: TOTAL context window (input + output tokens combined).\n # Controls when Hermes compresses history and validates requests.\n # Leave unset — Hermes auto-detects the correct value from the provider.\n # Set manually only when auto-detection is wrong (e.g. a local server with\n # a custom num_ctx, or a proxy that doesn't expose /v1/models).\n #\n # context_length: 131072\n #\n # max_tokens: OUTPUT cap — maximum tokens the model may generate per response.\n # Unrelated to how long your conversation history can be.\n # The OpenAI-standard name \"max_tokens\" is a misnomer; Anthropic's native\n # API has since renamed it \"max_output_tokens\" for clarity.\n # Leave unset to use the model's native output ceiling (recommended).\n # Set only if you want to deliberately limit individual response length.\n #\n# max_tokens: 8192\n\n # ── Custom request headers (optional) ─────────────────────────────────────\n #\n # default_headers: extra HTTP headers sent on every request to an\n # OpenAI-compatible endpoint. User values take precedence over the\n # provider/SDK defaults, so this is the supported way to override the\n # OpenAI Python SDK's identifying headers (User-Agent: OpenAI/Python ...,\n # X-Stainless-*) when a custom provider sits behind a gateway/WAF that\n # rejects them — e.g. an upstream that returns \"502 Upstream access\n # forbidden\" for the SDK default User-Agent but accepts a plain one.\n # Applies on the OpenAI wire only (not native Anthropic / Bedrock).\n #\n # default_headers:\n # User-Agent: \"curl/8.7.1\"\n\n# Named provider overrides (optional)\n# Use this for per-provider request timeouts, non-stream stale timeouts,\n# and per-model exceptions.\n# Applies to the primary turn client on every api_mode (OpenAI-wire, native\n# Anthropic, and Anthropic-compatible providers), the fallback chain, and\n# client rebuilds during credential rotation. For OpenAI-wire chat\n# completions (streaming and non-streaming) the configured value is also\n# used as the per-request ``timeout=`` kwarg so it wins over the legacy\n# HERMES_API_TIMEOUT env var (which still applies when no config is set).\n# ``stale_timeout_seconds`` controls the non-streaming stale-call detector and\n# wins over the legacy HERMES_API_CALL_STALE_TIMEOUT env var. Leaving these\n# unset keeps the legacy defaults (HERMES_API_TIMEOUT=1800s,\n# HERMES_API_CALL_STALE_TIMEOUT=90s, native Anthropic 900s). The\n# implicit non-stream stale detector is auto-disabled for local endpoints\n# and can scale upward for very large contexts.\n#\n# Not currently wired for AWS Bedrock (bedrock_converse + AnthropicBedrock\n# SDK paths) — those use boto3 with its own timeout configuration.\n#\n# providers:\n# ollama-local:\n# request_timeout_seconds: 300 # Longer timeout for local cold-starts\n# stale_timeout_seconds: 900 # Explicitly re-enable stale detection on local endpoints\n# anthropic:\n# request_timeout_seconds: 30 # Fast-fail cloud requests\n# models:\n# claude-opus-4.6:\n# timeout_seconds: 600 # Longer timeout for extended-thinking Opus calls\n# openai-codex:\n# models:\n# gpt-5.4:\n# stale_timeout_seconds: 1800 # Longer non-stream stale timeout for slow large-context turns\n\n# =============================================================================\n# OpenRouter Provider Routing (only applies when using OpenRouter)\n# =============================================================================\n# Control how requests are routed across providers on OpenRouter.\n# See: https://openrouter.ai/docs/guides/routing/provider-selection\n#\n# provider_routing:\n# # Sort strategy: \"price\" (default), \"throughput\", or \"latency\"\n# # Append :nitro to model name for a shortcut to throughput sorting.\n# sort: \"throughput\"\n#\n# # Only allow these providers (provider slugs from OpenRouter)\n# # only: [\"anthropic\", \"google\"]\n#\n# # Skip these providers entirely\n# # ignore: [\"deepinfra\", \"fireworks\"]\n#\n# # Try providers in this order (overrides default load balancing)\n# # order: [\"anthropic\", \"google\", \"together\"]\n#\n# # Require providers to support all parameters in your request\n# # require_parameters: true\n#\n# # Data policy: \"allow\" (default) or \"deny\" to exclude providers that may store data\n# # data_collection: \"deny\"\n\n# =============================================================================\n# OpenRouter Response Caching (only applies when using OpenRouter)\n# =============================================================================\n# Cache identical API responses at the OpenRouter edge for free instant replays.\n# When enabled, identical requests (same model, messages, parameters) return\n# cached responses with zero billing. Separate from Anthropic prompt caching.\n# See: https://openrouter.ai/docs/guides/features/response-caching\n#\n# openrouter:\n# response_cache: true # Enable response caching (default: true)\n# response_cache_ttl: 300 # Cache TTL in seconds, 1-86400 (default: 300)\n\n# =============================================================================\n# Git Worktree Isolation\n# =============================================================================\n# When enabled, each CLI session creates an isolated git worktree so multiple\n# agents can work on the same repo concurrently without file collisions.\n# Equivalent to always passing --worktree / -w on the command line.\n#\n# worktree: true # Always create a worktree when in a git repo\n# worktree: false # Default — only create when -w flag is passed\n#\n# By default a new worktree branches from the freshly-fetched remote tip\n# (the current branch's upstream, else the remote's default branch) so it\n# starts current with the project instead of from the local clone's\n# (possibly stale) HEAD. Set worktree_sync: false to branch from local HEAD\n# instead — useful when offline or when you deliberately want the clone's\n# exact current state as the base.\n#\n# worktree_sync: true # Default — branch from the fetched remote tip\n# worktree_sync: false # Branch from local HEAD (offline / pinned base)\n\n# =============================================================================\n# Terminal Tool Configuration\n# =============================================================================\n# Choose ONE of the following terminal configurations by uncommenting it.\n# The terminal tool executes commands in the specified environment.\n\n# -----------------------------------------------------------------------------\n# OPTION 1: Local execution (default)\n# Commands run directly on your machine in the current directory\n# -----------------------------------------------------------------------------\n# Working directory behavior:\n# - CLI (`hermes` command): Uses \".\" (current directory where you run hermes)\n# - Gateway/messaging/cron: Uses terminal.cwd here; legacy .env cwd values are deprecated\nterminal:\n backend: \"local\"\n cwd: \".\" # For local backend: \".\" = current directory. Ignored for remote backends unless a backend documents otherwise.\n timeout: 180\n # HOME policy for tool subprocesses:\n # auto - default: host uses your real HOME; containers use HERMES_HOME/home\n # real - force your real OS-user HOME\n # profile - force HERMES_HOME/home for strict per-profile CLI config isolation\n home_mode: \"auto\"\n docker_mount_cwd_to_workspace: false # SECURITY: off by default. Opt in to mount the launch cwd into Docker /workspace.\n lifetime_seconds: 300\n # sudo_password: \"hunter2\" # Optional: pipe a sudo password via sudo -S. SECURITY WARNING: plaintext.\n # sudo_password: \"\" # Explicit empty password: try empty and never open the interactive sudo prompt.\n\n# -----------------------------------------------------------------------------\n# OPTION 2: SSH remote execution\n# Commands run on a remote server - agent code stays local (sandboxed)\n# Great for: keeping agent isolated from its own code, using powerful remote hardware\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"ssh\"\n# cwd: \"/home/myuser/project\" # Path on the REMOTE server\n# timeout: 180\n# lifetime_seconds: 300\n# ssh_host: \"my-server.example.com\"\n# ssh_user: \"myuser\"\n# ssh_port: 22\n# ssh_key: \"~/.ssh/id_rsa\" # Optional - uses ssh-agent if not specified\n\n# -----------------------------------------------------------------------------\n# OPTION 3: Docker container\n# Commands run in an isolated Docker container\n# Great for: reproducible environments, testing, isolation\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"docker\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /)\n# timeout: 180\n# lifetime_seconds: 300\n# docker_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# docker_mount_cwd_to_workspace: true # Explicit opt-in: mount your launch cwd into /workspace\n# # Optional: run the container as your host user's uid:gid so files written\n# # into bind-mounted dirs are owned by you, not root. Drops SETUID/SETGID\n# # caps too since no gosu privilege drop is needed. Leave off if your\n# # chosen docker_image expects to start as root.\n# docker_run_as_host_user: true\n# # Optional: explicitly forward selected env vars into Docker.\n# # These values come from your current shell first, then ~/.hermes/.env.\n# # Warning: anything forwarded here is visible to commands run in the container.\n# docker_forward_env:\n# - \"GITHUB_TOKEN\"\n# - \"NPM_TOKEN\"\n# # Optional: extra flags passed verbatim to docker run (appended after security defaults).\n# # Useful for adding capabilities (e.g. apt installs needing SETUID) or custom options.\n# # Example: add a Linux capability not included by default\n# # docker_extra_args:\n# # - \"--cap-add\"\n# # - \"SETUID\"\n\n# -----------------------------------------------------------------------------\n# OPTION 4: Singularity/Apptainer container\n# Commands run in a Singularity container (common in HPC environments)\n# Great for: HPC clusters, shared compute environments\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"singularity\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# singularity_image: \"docker://nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 5: Modal cloud execution\n# Commands run on Modal's cloud infrastructure\n# Great for: GPU access, scalable compute, serverless execution\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"modal\"\n# cwd: \"/workspace\" # Path INSIDE the sandbox (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# modal_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 6: Daytona cloud execution\n# Commands run in Daytona cloud sandboxes\n# Great for: Cloud dev environments, persistent workspaces, team collaboration\n# Requires: pip install daytona, DAYTONA_API_KEY env var\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"daytona\"\n# cwd: \"~\"\n# timeout: 180\n# lifetime_seconds: 300\n# daytona_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# container_disk: 10240 # Daytona max is 10GB per sandbox\n\n#\n# --- Container resource limits (docker, singularity, modal, daytona -- ignored for local/ssh) ---\n# These settings apply to all container backends. They control the resources\n# allocated to the sandbox and whether its filesystem persists across sessions.\n container_cpu: 1 # CPU cores\n container_memory: 5120 # Memory in MB (5120 = 5GB)\n container_disk: 51200 # Disk in MB (51200 = 50GB)\n container_persistent: true # Persist filesystem across sessions (false = ephemeral)\n\n# -----------------------------------------------------------------------------\n# SUDO SUPPORT (works with ALL backends above)\n# -----------------------------------------------------------------------------\n# Add sudo_password to any terminal config above to enable sudo commands.\n# The password is piped via `sudo -S`. Works with local, ssh, docker, etc.\n#\n# SECURITY WARNING: Password stored in plaintext!\n#\n# INTERACTIVE PROMPT: If sudo_password is unset and the CLI is running,\n# you'll be prompted to enter your password when sudo is needed:\n# - 45-second timeout (auto-skips if no input)\n# - Press Enter to skip (command fails gracefully)\n# - Password is hidden while typing\n# - Password is cached for the session\n#\n# EMPTY PASSWORDS: Setting sudo_password to an explicit empty string is different\n# from leaving it unset. Hermes will try an empty password via `sudo -S` and\n# will not open the interactive prompt. This is useful for passwordless sudo,\n# Touch ID sudo setups, and environments where prompting is just noise.\n#\n# ALTERNATIVES:\n# - SSH backend: Configure passwordless sudo on the remote server\n# - Containers: Run as root inside the container (no sudo needed)\n# - Local: Configure /etc/sudoers for specific commands\n#\n# Example (add to your terminal section):\n# sudo_password: \"your-password-here\"\n\n# =============================================================================\n# Security Scanning (tirith)\n# =============================================================================\n# Optional pre-exec command security scanning via tirith.\n# Detects homograph URLs, pipe-to-shell, terminal injection, env manipulation.\n# Install: brew install sheeki03/tap/tirith\n# Docs: https://github.com/sheeki03/tirith\n#\n# security:\n# tirith_enabled: true # Enable/disable tirith scanning\n# tirith_path: \"tirith\" # Path to tirith binary (supports ~ expansion)\n# tirith_timeout: 5 # Scan timeout in seconds\n# tirith_fail_open: true # Allow commands if tirith unavailable\n\n# =============================================================================\n# Browser Tool Configuration\n# =============================================================================\nbrowser:\n # Inactivity timeout in seconds - browser sessions are automatically closed\n # after this period of no activity between agent loops (default: 120 = 2 minutes)\n inactivity_timeout: 120\n\n# =============================================================================\n# Tool Loop Guardrails\n# =============================================================================\n# Soft warnings are enabled by default. They append guidance to repeated failed\n# or non-progressing tool results but still let the tool execute. Hard stops are\n# opt-in circuit breakers for autonomous/cron sessions where stopping a loop is\n# preferable to spending the full iteration budget.\ntool_loop_guardrails:\n warnings_enabled: true\n hard_stop_enabled: false\n warn_after:\n exact_failure: 2\n same_tool_failure: 3\n idempotent_no_progress: 2\n hard_stop_after:\n exact_failure: 5\n same_tool_failure: 8\n idempotent_no_progress: 5\n\n# =============================================================================\n# Context Compression (Auto-shrinks long conversations)\n# =============================================================================\n# When conversation approaches model's context limit, middle turns are\n# automatically summarized to free up space while preserving important context.\n#\n# HOW IT WORKS:\n# 1. Tracks actual token usage from API responses (not estimates)\n# 2. When prompt_tokens >= threshold% of model's context_length, triggers compression\n# 3. Protects first 3 turns (system prompt, initial request, first response)\n# 4. Protects last N turns (default 20 messages = ~10 full turns of recent context)\n# 5. Summarizes middle turns using a fast/cheap model\n# 6. Inserts summary as a user message, continues conversation seamlessly\n#\n# Post-compression tail budget is target_ratio × threshold × context_length:\n# 200K context, threshold 0.50, ratio 0.20 → 20K tokens of recent tail preserved\n# 1M context, threshold 0.50, ratio 0.20 → 100K tokens of recent tail preserved\n#\ncompression:\n # Enable automatic context compression (default: true)\n # Set to false if you prefer to manage context manually or want errors on overflow\n enabled: true\n \n # Trigger compression at this % of model's context limit (default: 0.50 = 50%)\n # Lower values = more aggressive compression, higher values = compress later\n threshold: 0.50\n \n # Fraction of the threshold to preserve as recent tail (default: 0.20 = 20%)\n # e.g. 20% of 50% threshold = 10% of total context kept as recent messages.\n # Summary output is separately capped at 12K tokens (Gemini output limit).\n # Range: 0.10 - 0.80\n target_ratio: 0.20\n\n # Number of most-recent messages to always preserve (default: 20 ≈ 10 full turns)\n # Higher values keep more recent conversation intact at the cost of more aggressive\n # compression of older turns.\n protect_last_n: 20\n\n # Number of non-system messages to protect at the head of the transcript, in\n # ADDITION to the system prompt (which is always implicitly protected).\n # Head messages are NEVER summarized — they survive every compression\n # indefinitely. This gives stable early context for short/medium sessions,\n # but in long-running sessions that rely on rolling compaction the pinned\n # opening turns may not match how you want the session framed over time.\n # Set to 0 to preserve ONLY the system prompt (plus the rolling summary\n # and recent tail) — the cleanest configuration for long-running sessions.\n # Default 3 preserves the system prompt plus the first three non-system\n # head messages, matching the pre-feature behaviour.\n protect_first_n: 3\n\n # To pin a specific model/provider for compression summaries, use the\n # auxiliary section below (auxiliary.compression.provider / model).\n\n# =============================================================================\n# Anthropic prompt caching TTL\n# =============================================================================\n# When prompt caching is active (Claude via OpenRouter or native Anthropic),\n# Anthropic supports two TTL tiers for cached prefixes: \"5m\" (default) and\n# \"1h\". Other values are ignored and \"5m\" is used.\n#\nprompt_caching:\n cache_ttl: \"5m\" # use \"1h\" for long sessions with pauses between turns\n\n# =============================================================================\n# Auxiliary Models (Advanced — Experimental)\n# =============================================================================\n# Hermes uses lightweight \"auxiliary\" models for side tasks: image analysis,\n# browser screenshot analysis, web page summarization, TTS audio-tag insertion,\n# and context compression.\n#\n# By default these use Gemini Flash via OpenRouter or Nous Portal and are\n# auto-detected from your credentials. You do NOT need to change anything\n# here for normal usage.\n#\n# WARNING: Overriding these with providers other than OpenRouter or Nous Portal\n# is EXPERIMENTAL and may not work. Not all models/providers support vision,\n# produce usable summaries, or accept the same API format. Change at your own\n# risk — if things break, reset to \"auto\" / empty values.\n#\n# Each task has its own provider + model pair so you can mix providers.\n# For example: OpenRouter for vision (needs multimodal), but your main\n# local endpoint for compression (just needs text).\n#\n# Provider options:\n# \"auto\" - Best available: OpenRouter → Nous Portal → main endpoint (default)\n# \"openrouter\" - Force OpenRouter (requires OPENROUTER_API_KEY)\n# \"nous\" - Force Nous Portal (requires: hermes auth add nous)\n# \"gemini\" - Force Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n# \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY)\n# \"codex\" - Force Codex OAuth (requires: hermes model → Codex).\n# Uses gpt-5.3-codex which supports vision.\n# \"main\" - Use your custom endpoint (OPENAI_BASE_URL + OPENAI_API_KEY).\n# Works with OpenAI API, local models, or any OpenAI-compatible\n# endpoint. Also falls back to Codex OAuth and API-key providers.\n#\n# Model: leave empty to use the provider's default. When empty, OpenRouter\n# uses \"google/gemini-3-flash-preview\" and Nous uses \"gemini-3-flash\".\n# Other providers pick a sensible default automatically.\n#\n# auxiliary:\n# # Image analysis: vision_analyze tool + browser screenshots\n# vision:\n# provider: \"auto\"\n# model: \"\" # e.g. \"google/gemini-2.5-flash\", \"openai/gpt-4o\"\n# timeout: 30 # LLM API call timeout (seconds)\n# download_timeout: 30 # Image HTTP download timeout (seconds)\n# # Increase for slow connections or self-hosted image servers\n#\n# # Web page scraping / summarization + browser page text extraction\n# web_extract:\n# provider: \"auto\"\n# model: \"\"\n#\n# # Gemini 3.1 TTS hidden audio-tag insertion\n# tts_audio_tags:\n# provider: \"auto\" # empty model = your main chat model\n# model: \"\"\n# timeout: 30\n#\n# # Session search — summarizes matching past sessions\n# session_search:\n# provider: \"auto\"\n# model: \"\"\n# timeout: 30\n# max_concurrency: 3 # Limit parallel summaries to reduce request-burst 429s\n# extra_body: {} # Provider-specific OpenAI-compatible request fields\n# # Example for providers that support request-body\n# # reasoning controls:\n# # extra_body:\n# # enable_thinking: false\n# # Some vLLM/Qwen deployments expect this nested:\n# # extra_body:\n# # chat_template_kwargs:\n# # enable_thinking: false\n\n# =============================================================================\n# Persistent Memory\n# =============================================================================\n# Bounded curated memory injected into the system prompt every session.\n# Two stores: MEMORY.md (agent's notes) and USER.md (user profile).\n# Character limits keep the memory small and focused. The agent manages\n# pruning -- when at the limit, it must consolidate or replace entries.\n# Disabled by default in batch_runner.\n#\nmemory:\n # Agent's personal notes: environment facts, conventions, things learned\n memory_enabled: true\n \n # User profile: preferences, communication style, expectations\n user_profile_enabled: true\n \n # Character limits (~2.75 chars per token, model-independent)\n memory_char_limit: 2200 # ~800 tokens\n user_char_limit: 1375 # ~500 tokens\n\n # Periodic memory nudge: remind the agent to consider saving memories\n # every N user turns. Set to 0 to disable. Only active when memory is enabled.\n nudge_interval: 10 # Nudge every 10 user turns (0 = disabled)\n\n # Memory flush: give the agent one turn to save memories before context is\n # lost (compression, /new, /reset, exit). Set to 0 to disable.\n # For exit/reset, only fires if the session had at least this many user turns.\n flush_min_turns: 6 # Min user turns to trigger flush on exit/reset (0 = disabled)\n\n# =============================================================================\n# Session Reset Policy (Messaging Platforms)\n# =============================================================================\n# Controls when messaging sessions (Telegram, Discord, WhatsApp, Slack) are\n# automatically cleared. Without resets, conversation context grows indefinitely\n# which increases API costs with every message.\n#\n# When a reset triggers, the agent first saves important information to its\n# persistent memory — but the conversation context is wiped. The agent starts\n# fresh but retains learned facts via its memory system.\n#\n# Users can always manually reset with /reset or /new in chat.\n#\n# Modes:\n# \"both\" - Reset on EITHER inactivity timeout or daily boundary (recommended)\n# \"idle\" - Reset only after N minutes of inactivity\n# \"daily\" - Reset only at a fixed hour each day\n# \"none\" - Never auto-reset; context lives until /reset or compression kicks in\n#\n# When a reset triggers, the agent gets one turn to save important memories and\n# skills before the context is wiped. Persistent memory carries across sessions.\n#\nsession_reset:\n mode: both # \"both\", \"idle\", \"daily\", or \"none\"\n idle_minutes: 1440 # Inactivity timeout in minutes (default: 1440 = 24 hours)\n at_hour: 4 # Daily reset hour, 0-23 local time (default: 4 AM)\n\n# Maximum number of simultaneously active chat sessions across CLI, TUI,\n# dashboard chat, and messaging gateway. Set to null, 0, or omit to allow\n# unlimited concurrent sessions. When the limit is reached, new sessions get a\n# clean error while existing active sessions keep their normal behavior. This\n# top-level key takes precedence over gateway.max_concurrent_sessions. The cap\n# is a best-effort single-host/profile runtime guard; Hermes fails open if the\n# local runtime lease registry cannot be read or locked.\nmax_concurrent_sessions: null\n\n# When true, group/channel chats use one session per participant when the platform\n# provides a user ID. This is the secure default and prevents users in the same\n# room from sharing context, interrupts, and token costs. Set false only if you\n# explicitly want one shared \"room brain\" per group/channel.\ngroup_sessions_per_user: true\n\n# ─────────────────────────────────────────────────────────────────────────────\n# API Server — per-client model routing\n# ─────────────────────────────────────────────────────────────────────────────\n# Route different API clients to different models/providers on a single\n# Hermes deployment. Clients choose a backend by sending a specific string\n# as the OpenAI ``model`` field. Unmapped model values fall back to the\n# global model configured in the ``model:`` section above, and an explicit\n# session /model override always wins over a route.\n#\n# Configure via the ``platforms.api_server.extra.model_routes`` gateway\n# config block:\n#\n# platforms:\n# api_server:\n# enabled: true\n# extra:\n# key: \"your-api-server-secret\"\n# model_routes:\n# # Xiaozhi clients send model=\"minimax-m2\" → routed to MiniMax via OpenRouter\n# minimax-m2:\n# model: \"minimax/minimax-m1\"\n# provider: \"openrouter\" # optional — overrides global provider\n# # api_key: \"sk-...\" # optional — per-route UPSTREAM provider\n# # key (NOT caller auth; never logged)\n# # base_url: \"https://...\" # optional — per-route base URL\n# # GPT clients keep their own alias\n# gpt-5:\n# model: \"openai/gpt-5\"\n# provider: \"openrouter\"\n#\n# Configured aliases are automatically listed by GET /v1/models so clients\n# can discover them without manual coordination. Caller authentication is\n# unchanged: every request still authenticates with the global API server\n# key (``extra.key`` / API_SERVER_KEY).\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Gateway Streaming\n# ─────────────────────────────────────────────────────────────────────────────\n# Stream tokens to messaging platforms in real-time. The bot sends a message\n# on first token, then progressively edits it as more tokens arrive.\n# Disabled by default — enable to try the streaming UX on Telegram/Discord/Slack.\n# For Telegram, partial edits are sent as plain text and only the final edit uses MarkdownV2.\nstreaming:\n enabled: false\n # transport: edit # \"edit\" = progressive editMessageText\n # edit_interval: 0.3 # seconds between message edits\n # buffer_threshold: 40 # chars before forcing an edit flush\n # cursor: \" ▉\" # cursor shown during streaming\n\n# =============================================================================\n# Skills Configuration\n# =============================================================================\n# Skills are reusable procedures the agent can load and follow. The agent can\n# also create new skills after completing complex tasks.\n#\nskills:\n # Nudge the agent to create skills after complex tasks.\n # Every N tool-calling iterations, remind the model to consider saving a skill.\n # Set to 0 to disable.\n creation_nudge_interval: 15\n\n # External skill directories — share skills across tools/agents without\n # copying them into ~/.hermes/skills/. Each path is expanded (~ and ${VAR})\n # and resolved to an absolute path. External dirs are read-only: skill\n # creation always writes to ~/.hermes/skills/. Local skills take precedence\n # when names collide.\n # external_dirs:\n # - ~/.agents/skills\n # - /home/shared/team-skills\n\n# =============================================================================\n# Agent Behavior\n# =============================================================================\nagent:\n # Maximum tool-calling iterations per conversation\n # Higher = more room for complex tasks, but costs more tokens\n # Recommended: 20-30 for focused tasks, 50-100 for open exploration\n max_turns: 60\n\n # Inactivity timeout for gateway agent runs (seconds, 0 = unlimited).\n # The agent can run indefinitely when actively calling tools or receiving\n # API responses. Only fires after the agent has been idle for this duration.\n # gateway_timeout: 1800\n\n # Staged warning: send a warning before escalating to full timeout.\n # Fires once per run when inactivity reaches this threshold (seconds).\n # Set to 0 to disable the warning.\n # gateway_timeout_warning: 900\n\n # Graceful drain timeout for gateway stop/restart (seconds).\n # Default 0 = no drain: a restart interrupts in-flight agents immediately,\n # cleans up, and exits. Set a positive value only if you want a grace\n # window on /restart, and keep it well under systemd's TimeoutStopSec.\n # restart_drain_timeout: 0\n\n # Max app-level retry attempts for API errors (connection drops, provider\n # timeouts, 5xx, etc.) before the agent surfaces the failure. Lower this\n # to 1 if you use fallback providers and want fast failover on flaky\n # primaries (default 3). The OpenAI SDK does its own low-level retries\n # underneath this wrapper — this is the Hermes-level loop.\n # api_max_retries: 3\n\n # After the agent edits code without fresh passing verification, nudge it to\n # verify before finishing. The default \"auto\" enables it on interactive\n # coding surfaces (CLI, TUI, desktop) and programmatic callers, and disables\n # it on conversational messaging surfaces (Telegram, Discord, etc.) where the\n # verification summary would reach a human as chat noise. Set true or false to\n # force it on or off; the HERMES_VERIFY_ON_STOP env var (1/0) takes precedence.\n # verify_on_stop: auto\n\n # Standing operator instructions for the coding posture (when Hermes is in a\n # code workspace). Appended to the coding brief as an extra system block, so\n # you can pin project-wide workflow rules without editing the shipped brief.\n # Accepts a string or a list of strings. Takes effect next session.\n # coding_instructions:\n # - \"For UI work, don't run tsc/lint until I approve the look.\"\n # - \"Clean the diff before you commit and push.\"\n\n # When verify-on-stop finds edited code without fresh verification evidence,\n # append guidance for creative UI work (avoid broad tsc/lint/test before visual\n # approval) and clean-diff expectations. Set false to keep that nudge terse.\n # verify_guidance: true\n\n # A `pre_verify` hook (plugin or shell, see Event Hooks docs) can keep the\n # agent going one more turn to verify/clean before finishing. This caps how\n # many times one turn may be nudged to continue, so a hook can't trap the loop.\n # Default 3.\n # max_verify_nudges: 3\n\n # Enable verbose logging\n verbose: false\n \n # Reasoning effort level (OpenRouter and Nous Portal)\n # Controls how much \"thinking\" the model does before responding.\n # Options: \"xhigh\" (max), \"high\", \"medium\", \"low\", \"minimal\", \"none\" (disable)\n reasoning_effort: \"medium\"\n \n # Predefined personalities (use with /personality command)\n personalities:\n helpful: \"You are a helpful, friendly AI assistant.\"\n concise: \"You are a concise assistant. Keep responses brief and to the point.\"\n technical: \"You are a technical expert. Provide detailed, accurate technical information.\"\n creative: \"You are a creative assistant. Think outside the box and offer innovative solutions.\"\n teacher: \"You are a patient teacher. Explain concepts clearly with examples.\"\n kawaii: \"You are a kawaii assistant! Use cute expressions like (◕‿◕), ★, ♪, and ~! Add sparkles and be super enthusiastic about everything! Every response should feel warm and adorable desu~! ヽ(>∀<☆)ノ\"\n catgirl: \"You are Neko-chan, an anime catgirl AI assistant, nya~! Add 'nya' and cat-like expressions to your speech. Use kaomoji like (=^・ω・^=) and ฅ^•ﻌ•^ฅ. Be playful and curious like a cat, nya~!\"\n pirate: \"Arrr! Ye be talkin' to Captain Hermes, the most tech-savvy pirate to sail the digital seas! Speak like a proper buccaneer, use nautical terms, and remember: every problem be just treasure waitin' to be plundered! Yo ho ho!\"\n shakespeare: \"Hark! Thou speakest with an assistant most versed in the bardic arts. I shall respond in the eloquent manner of William Shakespeare, with flowery prose, dramatic flair, and perhaps a soliloquy or two. What light through yonder terminal breaks?\"\n surfer: \"Duuude! You're chatting with the chillest AI on the web, bro! Everything's gonna be totally rad. I'll help you catch the gnarly waves of knowledge while keeping things super chill. Cowabunga! 🤙\"\n noir: \"The rain hammered against the terminal like regrets on a guilty conscience. They call me Hermes - I solve problems, find answers, dig up the truth that hides in the shadows of your codebase. In this city of silicon and secrets, everyone's got something to hide. What's your story, pal?\"\n uwu: \"hewwo! i'm your fwiendwy assistant uwu~ i wiww twy my best to hewp you! *nuzzles your code* OwO what's this? wet me take a wook! i pwomise to be vewy hewpful >w<\"\n philosopher: \"Greetings, seeker of wisdom. I am an assistant who contemplates the deeper meaning behind every query. Let us examine not just the 'how' but the 'why' of your questions. Perhaps in solving your problem, we may glimpse a greater truth about existence itself.\"\n hype: \"YOOO LET'S GOOOO!!! 🔥🔥🔥 I am SO PUMPED to help you today! Every question is AMAZING and we're gonna CRUSH IT together! This is gonna be LEGENDARY! ARE YOU READY?! LET'S DO THIS! 💪😤🚀\"\n\n# =============================================================================\n# Toolsets\n# =============================================================================\n# Control which tools the agent has access to.\n# Use `hermes tools` to interactively enable/disable tools per platform.\n\n# =============================================================================\n# Platform Toolsets (per-platform tool configuration)\n# =============================================================================\n# Override which toolsets are available on each platform.\n# If a platform isn't listed here, its built-in default is used.\n#\n# You can use EITHER:\n# - A preset like \"hermes-cli\" or \"hermes-telegram\" (curated tool set)\n# - A list of individual toolsets to compose your own (see list below)\n#\n# Supported platform keys: cli, telegram, discord, whatsapp, slack, qqbot, teams, google_chat\n#\n# Examples:\n#\n# # Use presets (same as defaults):\n# platform_toolsets:\n# cli: [hermes-cli]\n# telegram: [hermes-telegram]\n#\n# # Custom: give Telegram only web + terminal + file + planning:\n# platform_toolsets:\n# telegram: [web, terminal, file, todo]\n#\n# # Custom: CLI without browser or image gen:\n# platform_toolsets:\n# cli: [web, terminal, file, skills, todo, tts, cronjob]\n#\n# # Restrictive: Discord gets read-only tools only:\n# platform_toolsets:\n# discord: [web, vision, skills, todo]\n#\n# If not set, defaults are:\n# cli: hermes-cli (everything + cronjob management)\n# telegram: hermes-telegram (terminal, file, web, vision, image, tts, browser, skills, todo, cronjob, messaging)\n# discord: hermes-discord (same as telegram)\n# whatsapp: hermes-whatsapp (same as telegram)\n# slack: hermes-slack (same as telegram)\n# signal: hermes-signal (same as telegram)\n# homeassistant: hermes-homeassistant (same as telegram)\n# qqbot: hermes-qqbot (same as telegram)\n# teams: hermes-teams (same as telegram)\n# google_chat: hermes-google_chat (same as telegram)\n#\nplatform_toolsets:\n cli: [hermes-cli]\n telegram: [hermes-telegram]\n discord: [hermes-discord]\n whatsapp: [hermes-whatsapp]\n slack: [hermes-slack]\n signal: [hermes-signal]\n homeassistant: [hermes-homeassistant]\n qqbot: [hermes-qqbot]\n yuanbao: [hermes-yuanbao]\n teams: [hermes-teams]\n google_chat: [hermes-google_chat]\n\n# =============================================================================\n# Gateway Platform Settings\n# =============================================================================\n# Optional per-platform messaging settings.\n# Platform-specific knobs live under `extra`.\n#\n# platforms:\n# telegram:\n# reply_to_mode: \"first\" # off | first | all\n# # guest_mode lets explicit @mentions from non-allowlisted groups through.\n# # Default false; ordinary messages, replies, and regex wake words stay blocked.\n# guest_mode: false\n# # allowed_chats: [\"-1001234567890\"]\n# extra:\n# disable_link_previews: false # Set true to suppress Telegram URL previews in bot messages\n# rich_messages: false # Bot API 10.1 rich messages (tables/task lists/details/math); default false for copyable legacy MarkdownV2, set true to opt in\n# rich_drafts: false # Experimental rich draft previews during Telegram DM streaming; default false because Telegram Desktop/macOS can visually overlay draft frames\n# command_menu:\n# # Telegram allows up to 100 BotCommands; Hermes defaults to 60 so\n# # all built-in commands plus common skill commands stay visible\n# # while remaining under Telegram's payload-size limit. Clamped 1..100.\n# max_commands: 60\n# # prepend = user priority first, then Hermes defaults\n# # append = Hermes defaults first, then user priority\n# # replace = only the list below defines priority\n# priority_mode: prepend\n# priority:\n# - my_plugin_command\n#\n# Discord-specific settings (config.yaml top-level, not under platforms:):\n#\n# discord:\n# require_mention: true # Require @mention in server channels (default: true)\n# auto_thread: true # Auto-create thread on @mention (default: true)\n# free_response_channels: \"\" # Channel IDs where no mention is needed\n# reactions: true # Show processing reactions (default: true)\n# history_backfill: true # Recover missed channel messages on mention (default: true)\n# history_backfill_limit: 50 # Max messages to scan backwards (default: 50)\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Available toolsets (use these names in platform_toolsets or the toolsets list)\n#\n# Run `hermes chat --list-toolsets` to see all toolsets and their tools.\n# Run `hermes chat --list-tools` to see every individual tool with descriptions.\n# ─────────────────────────────────────────────────────────────────────────────\n#\n# INDIVIDUAL TOOLSETS (compose your own):\n# web - web_search, web_extract\n# search - web_search only (no scraping)\n# terminal - terminal, process\n# file - read_file, write_file, patch, search\n# browser - browser_navigate, browser_snapshot, browser_click, browser_type,\n# browser_scroll, browser_back, browser_press,\n# browser_get_images, browser_vision (requires BROWSERBASE_API_KEY)\n# vision - vision_analyze (requires OPENROUTER_API_KEY)\n# image_gen - image_generate (requires FAL_KEY)\n# skills - skills_list, skill_view\n# skills_hub - skill_hub (search/install/manage from online registries — user-driven only)\n# todo - todo (in-memory task planning, no deps)\n# tts - text_to_speech (Edge TTS free, or ELEVENLABS/OPENAI/MINIMAX/MISTRAL key)\n# cronjob - cronjob (create/list/update/pause/resume/run/remove scheduled tasks)\n#\n# PRESETS (curated bundles):\n# hermes-cli - All of the above except send_message\n# hermes-telegram - terminal, file, web, vision, image_gen, tts, browser,\n# skills, todo, cronjob, send_message\n# hermes-discord - Same as hermes-telegram\n# hermes-whatsapp - Same as hermes-telegram\n# hermes-slack - Same as hermes-telegram\n#\n# COMPOSITE:\n# debugging - terminal + web + file\n# safe - web + vision (no terminal access)\n# all - Everything available\n#\n# web - Web search and content extraction (web_search, web_extract)\n# search - Web search only, no scraping (web_search)\n# terminal - Command execution and process management (terminal, process)\n# file - File operations: read, write, patch, search\n# browser - Full browser automation (navigate, click, type, screenshot, etc.)\n# vision - Image analysis (vision_analyze)\n# image_gen - Image generation with FLUX (image_generate)\n# skills - Load skill documents (skills_list, skill_view)\n# todo - Task planning and tracking for multi-step work\n# memory - Persistent memory across sessions (personal notes + user profile)\n# session_search - Search and recall past conversations (FTS5 + Gemini Flash summarization)\n# tts - Text-to-speech (Edge TTS free, ElevenLabs, OpenAI, MiniMax, Mistral)\n# cronjob - Schedule and manage automated tasks (CLI-only)\n#\n# Composite toolsets:\n# debugging - terminal + web + file (for troubleshooting)\n# safe - web + vision (no terminal access)\n\n# NOTE: The top-level \"toolsets\" key is deprecated and ignored.\n# Tool configuration is managed per-platform via platform_toolsets above.\n# Use `hermes tools` to configure interactively, or edit platform_toolsets directly.\n#\n# CLI override: hermes chat --toolsets terminal,web,file\n\n# =============================================================================\n# MCP (Model Context Protocol) Servers\n# =============================================================================\n# Connect to external MCP servers to add tools from the MCP ecosystem.\n# Each server's tools are automatically discovered and registered.\n# See website/docs/user-guide/features/mcp.md for full documentation.\n#\n# Stdio servers (spawn a subprocess):\n# command: the executable to run\n# args: command-line arguments\n# env: environment variables (only these + safe defaults passed to subprocess)\n#\n# HTTP servers (connect to a URL):\n# url: the MCP server endpoint\n# headers: HTTP headers (e.g., for authentication)\n#\n# Optional per-server settings:\n# timeout: tool call timeout in seconds (default: 120)\n# connect_timeout: initial connection timeout (default: 60)\n# keepalive_interval: liveness ping cadence in seconds (default: 180).\n# Lower it below the server's session TTL for servers that expire idle\n# sessions quickly (e.g. Unreal Engine editor MCP, ~15s), otherwise idle\n# tool calls hit an expired session and pay a slow reconnect. Floored at 5s.\n#\n# mcp_servers:\n# time:\n# command: uvx\n# args: [\"mcp-server-time\"]\n# filesystem:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-filesystem\", \"/home/user\"]\n# notion:\n# url: https://mcp.notion.com/mcp\n# github:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-github\"]\n# env:\n# GITHUB_PERSONAL_ACCESS_TOKEN: \"ghp_...\"\n#\n# Sampling (server-initiated LLM requests) — enabled by default.\n# Per-server config under the 'sampling' key:\n# analysis:\n# command: npx\n# args: [\"-y\", \"analysis-server\"]\n# sampling:\n# enabled: true # default: true\n# model: \"gemini-3-flash\" # override model (optional)\n# max_tokens_cap: 4096 # max tokens per request\n# timeout: 30 # LLM call timeout (seconds)\n# max_rpm: 10 # max requests per minute\n# allowed_models: [] # model whitelist (empty = all)\n# max_tool_rounds: 5 # tool loop limit (0 = disable)\n# log_level: \"info\" # audit verbosity\n\n# =============================================================================\n# Text-to-Speech\n# =============================================================================\n# TTS defaults to Edge TTS unless changed in ~/.hermes/config.yaml.\n# Gemini TTS supports persona/director prompt files, and Gemini 3.1 Flash TTS\n# can use a hidden auxiliary rewrite pass to insert expressive square-bracket\n# audio tags into the TTS script without showing tags in chat.\n#\n# tts:\n# provider: \"gemini\"\n# gemini:\n# model: \"gemini-3.1-flash-tts-preview\"\n# voice: \"Kore\"\n# audio_tags: false\n# persona_prompt_file: \"\" # e.g. ~/.hermes/tts/radio-host.md\n\n# =============================================================================\n# Voice Transcription (Speech-to-Text)\n# =============================================================================\n# Automatically transcribe voice messages on messaging platforms.\n# Providers: local (free, faster-whisper) | groq (free tier) | openai (Whisper API) | mistral (Voxtral Transcribe)\n# Set the corresponding API key in .env: GROQ_API_KEY, OPENAI_API_KEY, or MISTRAL_API_KEY.\nstt:\n enabled: true\n # provider: \"local\" # auto-detected if omitted\n local:\n model: \"base\" # tiny | base | small | medium | large-v3 | turbo\n # language: \"\" # auto-detect; set to \"en\", \"es\", \"fr\", etc. to force\n openai:\n model: \"whisper-1\" # whisper-1 | gpt-4o-mini-transcribe | gpt-4o-transcribe\n # mistral:\n # model: \"voxtral-mini-latest\" # voxtral-mini-latest | voxtral-mini-2602\n\n# =============================================================================\n# Response Pacing (Messaging Platforms)\n# =============================================================================\n# Add human-like delays between message chunks.\n# human_delay:\n# mode: \"off\" # \"off\" | \"natural\" | \"custom\"\n# min_ms: 800 # Min delay (custom mode only)\n# max_ms: 2500 # Max delay (custom mode only)\n\n# =============================================================================\n# Session Logging\n# =============================================================================\n# Session trajectories are automatically saved to logs/ directory.\n# Each session creates: logs/session_YYYYMMDD_HHMMSS_UUID.json\n#\n# The session ID is displayed in the welcome banner for easy reference.\n# Logs contain full conversation history in trajectory format:\n# - System prompt, user messages, assistant responses\n# - Tool calls with inputs/outputs\n# - Timestamps for debugging\n#\n# No configuration needed - logging is always enabled.\n# To disable, you would need to modify the source code.\n\n# =============================================================================\n# Code Execution Sandbox (Programmatic Tool Calling)\n# =============================================================================\n# The execute_code tool runs Python scripts that call Hermes tools via RPC.\n# Intermediate tool results stay out of the LLM's context window.\ncode_execution:\n timeout: 300 # Max seconds per script before kill (default: 300 = 5 min)\n max_tool_calls: 50 # Max RPC tool calls per execution (default: 50)\n\n# =============================================================================\n# Subagent Delegation\n# =============================================================================\n# The delegate_task tool spawns child agents with isolated context.\n# Supports single tasks and batch mode (default 3 parallel, configurable).\ndelegation:\n max_iterations: 50 # Max tool-calling turns per child (default: 50)\n # max_concurrent_children: 3 # Max parallel child agents per batch (default: 3, floor: 1, no ceiling).\n # WARNING: values above 10 multiply API cost linearly.\n # max_spawn_depth: 1 # Delegation tree depth cap (range: 1-3, default: 1 = flat).\n # Raise to 2 to allow workers to spawn their own subagents.\n # Requires role=\"orchestrator\" on intermediate agents.\n # orchestrator_enabled: true # Kill switch for role=\"orchestrator\" children (default: true).\n # subagent_auto_approve: false # When a subagent hits a dangerous-command approval prompt, auto-deny (default: false)\n # or auto-approve \"once\" (true) instead of blocking on stdin.\n # The parent TUI owns stdin, so blocking would deadlock; non-interactive resolution is required.\n # Both choices emit a logger.warning audit line. Flip to true only for cron/batch pipelines.\n # inherit_mcp_toolsets: true # When explicit child toolsets are narrowed, also keep the parent's MCP toolsets (default: true). Set false for strict intersection.\n # model: \"google/gemini-3-flash-preview\" # Override model for subagents (empty = inherit parent)\n # provider: \"openrouter\" # Override provider for subagents (empty = inherit parent)\n # # Resolves full credentials (base_url, api_key) automatically.\n # # Supported: openrouter, nous, zai, kimi-coding, minimax\n\n# =============================================================================\n# Honcho Integration (Cross-Session User Modeling)\n# =============================================================================\n# AI-native persistent memory via Honcho (https://honcho.dev/).\n# Builds a deeper understanding of the user across sessions and tools.\n# Runs alongside USER.md — additive, not a replacement.\n#\n# Requires: pip install honcho-ai\n# Config: ~/.honcho/config.json (shared with Claude Code, Cursor, etc.)\n# API key: HONCHO_API_KEY in ~/.hermes/.env or ~/.honcho/config.json\n#\n# Hermes-specific overrides (optional — most config comes from ~/.honcho/config.json):\n# honcho: {}\n\n# =============================================================================\n# Display\n# =============================================================================\ndisplay:\n # Use compact banner mode (hides the ASCII-art banner, shows a single line).\n # true: Compact single-line banner\n # false: Full ASCII banner with tool/skill summary (default)\n compact: false\n\n # Tool progress display level (CLI and gateway)\n # off: Silent — no tool activity shown, just the final response\n # new: Show a tool indicator only when the tool changes (skip repeats)\n # all: Show every tool call with a short preview (default)\n # verbose: Full args, results, and debug logs (same as /verbose)\n # log: Silent in chat; append every tool call to ~/.hermes/logs/tool_calls.log (gateway only)\n # Toggle at runtime with /verbose in the CLI\n tool_progress: all\n\n # Per-platform defaults can be quieter than the global setting. Telegram\n # tunes for mobile: tool_progress and busy_ack_detail default off (no\n # per-tool breadcrumb stream, no \"iteration 21/60\" debug detail in busy\n # acks or heartbeats), but interim_assistant_messages and\n # long_running_notifications STAY ON so the user has real signal between\n # turn start and final answer (mid-turn assistant commentary + a single\n # edit-in-place \"⏳ Working — N min\" heartbeat). Override under\n # display.platforms.telegram.\n\n # Auto-cleanup of temporary progress bubbles after the final response lands.\n # On platforms that support message deletion (currently Telegram), this\n # removes the tool-progress bubble, \"⏳ Still working...\" notices, and\n # context-pressure status messages once the final reply has been delivered —\n # keeping long-running turns visible live, then tidy afterward. Failed runs\n # leave the bubbles in place as breadcrumbs. Off by default.\n # Per-platform override: display.platforms.telegram.cleanup_progress\n # true: Delete tracked progress/status bubbles on successful turn\n # false: Leave everything in place (default)\n # Example:\n # display:\n # platforms:\n # telegram:\n # cleanup_progress: true\n cleanup_progress: false\n\n # Gateway-only natural mid-turn assistant updates.\n # When true, completed assistant status messages are sent as separate chat\n # messages. This is independent of tool_progress and gateway streaming.\n # true: Send mid-turn assistant updates as separate messages (default)\n # false: Only send the final response\n interim_assistant_messages: true\n\n # Gateway-only long-running status heartbeats.\n # When false, the platform does not receive periodic \"⏳ Working — N min\"\n # notifications even if agent.gateway_notify_interval is non-zero. The\n # heartbeat edits a single message in place (where the adapter supports\n # editing) instead of posting a new bubble each interval.\n # Default: true everywhere, including Telegram (silent agents are worse\n # than a single edit-in-place heartbeat).\n long_running_notifications: true\n\n # Include detailed iteration/tool/status context in busy acknowledgments\n # and long-running heartbeats. When true, busy acks show \"iteration 21/60,\n # terminal, 10 min\" and the heartbeat shows \"⏳ Working — 12 min,\n # iteration 21/60, terminal\". When false (Telegram default), both stay\n # terse: \"Interrupting current task\" and \"⏳ Working — 12 min, terminal\".\n busy_ack_detail: true\n\n # What Enter does when Hermes is already busy (CLI and gateway platforms).\n # interrupt: Interrupt the current run and redirect Hermes (default)\n # queue: Queue your message for the next turn\n # steer: Inject your message mid-run via /steer, arriving at the agent\n # after the next tool call — no interrupt, no role violation.\n # Falls back to 'queue' if the agent isn't running yet or if\n # images are attached (steer only carries text).\n # Ctrl+C (or /stop in gateway) always interrupts regardless of this setting.\n # Toggle at runtime with /busy .\n busy_input_mode: interrupt\n\n # Background process notifications (gateway/messaging only).\n # Controls how chatty the process watcher is when you use\n # terminal(background=true, notify_on_complete=true) from Telegram/Discord/etc.\n # off: No watcher messages at all\n # result: Only the final completion message\n # error: Only the final message when exit code != 0\n # all: Running output updates + final message (default)\n background_process_notifications: all\n\n\n # Play terminal bell when agent finishes a response.\n # Useful for long-running tasks — your terminal will ding when the agent is done.\n # Works over SSH. Most terminals can be configured to flash the taskbar or play a sound.\n # true: Ring the terminal bell on each response\n # false: Silent (default)\n bell_on_complete: false\n\n # Show model reasoning/thinking before each response.\n # When enabled, a dim box shows the model's thought process above the response.\n # Toggle at runtime with /reasoning show or /reasoning hide.\n # true: Show the reasoning box\n # false: Hide reasoning (default)\n show_reasoning: false\n\n # Stream tokens to the terminal as they arrive instead of waiting for the\n # full response. The response box opens on first token and text appears\n # line-by-line. Tool calls are still captured silently.\n # true: Stream tokens as they arrive (default)\n # false: Wait for the full response before rendering\n streaming: true\n\n # Show [HH:MM] timestamps on user input and assistant response labels.\n # timestamps: false\n\n # ───────────────────────────────────────────────────────────────────────────\n # Skin / Theme\n # ───────────────────────────────────────────────────────────────────────────\n # Customize CLI visual appearance — banner colors, spinner faces, tool prefix,\n # response box label, and branding text. Change at runtime with /skin .\n #\n # Built-in skins:\n # default — Classic Hermes gold/kawaii\n # ares — Crimson/bronze war-god theme with spinner wings\n # mono — Clean grayscale monochrome\n # slate — Cool blue developer-focused\n # daylight — Bright light-mode theme\n # warm-lightmode — Warm paper-tone light-mode theme\n # poseidon — Sea-green/teal Olympian theme\n # sisyphus — Earthy stone-and-moss theme\n # charizard — Fiery orange dragon theme\n #\n # Custom skins: drop a YAML file in ~/.hermes/skins/.yaml\n # Schema (all fields optional, missing values inherit from default):\n #\n # name: my-theme\n # description: Short description\n # colors:\n # banner_border: \"#HEX\" # Panel border\n # banner_title: \"#HEX\" # Panel title\n # banner_accent: \"#HEX\" # Section headers (Available Tools, etc.)\n # banner_dim: \"#HEX\" # Dim/muted text\n # banner_text: \"#HEX\" # Body text (tool names, skill names)\n # ui_accent: \"#HEX\" # UI accent color\n # response_border: \"#HEX\" # Response box border color\n # spinner:\n # waiting_faces: [\"(⚔)\", \"(⛨)\"] # Faces shown while waiting\n # thinking_faces: [\"(⚔)\", \"(⌁)\"] # Faces shown while thinking\n # thinking_verbs: [\"forging\", \"plotting\"] # Verbs for spinner messages\n # wings: # Optional left/right spinner decorations\n # - [\"⟪⚔\", \"⚔⟫\"]\n # - [\"⟪▲\", \"▲⟫\"]\n # branding:\n # agent_name: \"My Agent\" # Banner title and branding\n # welcome: \"Welcome message\" # Shown at CLI startup\n # response_label: \" ⚔ Agent \" # Response box header label\n # prompt_symbol: \"⚔\" # Prompt symbol (bare token; renderers add trailing space)\n # tool_prefix: \"╎\" # Tool output line prefix (default: ┊)\n #\n skin: default\n\n# =============================================================================\n# Model Aliases — short names for /model command\n# =============================================================================\n# Map short aliases to exact (model, provider, base_url) tuples.\n# Used by /model tab completion and resolve_alias().\n# Aliases are checked BEFORE the models.dev catalog, so they can route\n# to endpoints not in the catalog (e.g. Ollama Cloud, local servers).\n#\n# model_aliases:\n# opus:\n# model: claude-opus-4-6\n# provider: anthropic\n# qwen:\n# model: \"qwen3.5:397b\"\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n# glm:\n# model: glm-4.7\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n\n# =============================================================================\n# Privacy\n# =============================================================================\n# privacy:\n# # Redact PII from the LLM context prompt.\n# # When true, phone numbers are stripped and user/chat IDs are replaced\n# # with deterministic hashes before being sent to the model.\n# # Names and usernames are NOT affected (user-chosen, publicly visible).\n# # Routing/delivery still uses the original values internally.\n# redact_pii: false\n\n# =============================================================================\n# Shell-script hooks\n# =============================================================================\n# Register shell scripts as plugin-hook callbacks. Each entry is executed as\n# a subprocess (shell=False, shlex.split) with a JSON payload on stdin. On\n# stdout the script may return JSON that either blocks the tool call or\n# injects context into the next LLM call.\n#\n# Valid events (mirror hermes_cli.plugins.VALID_HOOKS):\n# pre_tool_call, post_tool_call, pre_llm_call, post_llm_call,\n# pre_api_request, post_api_request, on_session_start, on_session_end,\n# on_session_finalize, on_session_reset, subagent_stop\n#\n# First-use consent: each (event, command) pair prompts once on a TTY, then\n# is persisted to ~/.hermes/shell-hooks-allowlist.json. Non-interactive\n# runs (gateway, cron) need --accept-hooks, HERMES_ACCEPT_HOOKS=1, or the\n# hooks_auto_accept key below.\n#\n# See website/docs/user-guide/features/hooks.md for the full JSON wire\n# protocol and worked examples.\n#\n# hooks:\n# pre_tool_call:\n# - matcher: \"terminal\"\n# command: \"~/.hermes/agent-hooks/block-rm-rf.sh\"\n# timeout: 10\n# post_tool_call:\n# - matcher: \"write_file|patch\"\n# command: \"~/.hermes/agent-hooks/auto-format.sh\"\n# pre_llm_call:\n# - command: \"~/.hermes/agent-hooks/inject-cwd-context.sh\"\n# subagent_stop:\n# - command: \"~/.hermes/agent-hooks/log-orchestration.sh\"\n#\n# hooks_auto_accept: false\n\n\n# =============================================================================\n# Update Behavior\n# =============================================================================\nupdates:\n # Create a full HERMES_HOME zip before every `hermes update`.\n # Backups land in ~/.hermes/backups/ and can be restored with `hermes import`.\n # Off by default because large homes can add minutes to every update.\n pre_update_backup: false\n\n # Number of pre-update backup zips to retain.\n backup_keep: 5\n\n # What non-interactive updates do with local source edits in the Hermes repo.\n # Interactive terminal updates always prompt before restoring the autostash.\n #\n # stash - auto-stash before pull, then auto-restore after success (default)\n # discard - drop the update-created stash after success; use only on managed\n # installs where local source edits should not persist\n non_interactive_local_changes: \"stash\"\n\n\n# =============================================================================\n# Web Dashboard\n# =============================================================================\n# OAuth gate configuration for `hermes dashboard --host `.\n# The bundled Nous Portal plugin reads these on startup; settings here are\n# the canonical surface. Each can be overridden by an environment variable:\n#\n# dashboard.oauth.client_id <- HERMES_DASHBOARD_OAUTH_CLIENT_ID\n# dashboard.oauth.portal_url <- HERMES_DASHBOARD_PORTAL_URL\n# dashboard.public_url <- HERMES_DASHBOARD_PUBLIC_URL\n#\n# Env wins when set to a non-empty value. This is what Fly.io's platform-\n# secret injection uses to push per-deploy client_ids without needing to\n# bake a config.yaml into the image. Empty env values are treated as unset\n# so a provisioned-but-not-populated secret can't shadow a valid entry here.\n#\n# Local dev / on-prem deploys should typically set these via config.yaml\n# (the ~/.hermes/.env file is reserved for API keys and secrets).\n#\n# dashboard:\n# oauth:\n# client_id: \"\" # agent:{instance_id}; Portal provisions this at deploy\n# portal_url: \"\" # blank → default https://portal.nousresearch.com\n#\n# # Force the absolute base URL the OAuth callback (and any other public\n# # URL the dashboard hands to external systems) is built from. Set this\n# # for deploys behind reverse proxies that don't reliably forward\n# # X-Forwarded-Host / X-Forwarded-Proto / X-Forwarded-Prefix (manual\n# # nginx setups, on-prem ingresses, custom-domain Fly deploys without\n# # full proxy header chains).\n# #\n# # When set, the value is the complete authority: scheme + host +\n# # optional path prefix (e.g. \"https://example.com/hermes\"). The OAuth\n# # callback URL becomes \"/auth/callback\" — X-Forwarded-Prefix\n# # is IGNORED on this code path because the operator has explicitly\n# # declared the public URL and we no longer need to guess.\n# #\n# # Leave empty to use the existing proxy-header reconstruction (the\n# # default — works on Fly.io out of the box).\n# #\n# # public_url: \"https://example.com/hermes\"\n#\n# -----------------------------------------------------------------------------\n# Self-hosted OIDC dashboard auth (generic OpenID Connect — Authentik,\n# Keycloak, Zitadel, Authelia, Auth0, Okta, Google, …). Use this INSTEAD of the\n# nous block above when gating the dashboard with your own identity provider.\n# Each setting can be overridden by an environment variable:\n#\n# dashboard.oauth.self_hosted.issuer <- HERMES_DASHBOARD_OIDC_ISSUER\n# dashboard.oauth.self_hosted.client_id <- HERMES_DASHBOARD_OIDC_CLIENT_ID\n# dashboard.oauth.self_hosted.scopes <- HERMES_DASHBOARD_OIDC_SCOPES\n# dashboard.oauth.self_hosted.client_secret <- HERMES_DASHBOARD_OIDC_CLIENT_SECRET\n#\n# dashboard:\n# oauth:\n# provider: self-hosted\n# self_hosted:\n# issuer: \"https://auth.example.com/application/o/hermes/\" # required\n# client_id: \"hermes-dashboard\" # required\n# scopes: \"openid profile email\" # optional\n#\n# # OPTIONAL — set ONLY if your IDP registered the client as\n# # *confidential* (Authentik / Keycloak often default to this). When\n# # set, Hermes authenticates the client at the token endpoint\n# # (client_secret_basic or client_secret_post, auto-selected from the\n# # IDP's discovery doc) IN ADDITION to PKCE. Leave unset for a public\n# # (PKCE-only) client — the common case.\n# #\n# # This is a CREDENTIAL: prefer setting HERMES_DASHBOARD_OIDC_CLIENT_SECRET\n# # in ~/.hermes/.env over putting it here in config.yaml.\n# # client_secret: \"\"\n"}, "39bff67957e11ae5ceca7ee8b7d6a057dfd1ac97": {"t": 1785767887.0890563, "n": 68085, "h": "6656aa0392f70323", "c": "# Hermes Agent CLI Configuration\n# Copy this file to cli-config.yaml and customize as needed.\n# This file configures the CLI behavior. Environment variables in .env take precedence.\n\n# =============================================================================\n# Model Configuration\n# =============================================================================\nmodel:\n # Default model to use (can be overridden with --model flag)\n # Both \"default\" and \"model\" work as the key name here.\n default: \"anthropic/claude-opus-4.6\"\n \n # Inference provider selection:\n # \"auto\" - Auto-detect from credentials (default)\n # \"openrouter\" - OpenRouter (requires: OPENROUTER_API_KEY or OPENAI_API_KEY)\n # \"nous\" - Nous Portal OAuth (requires: hermes auth add nous)\n # \"nous-api\" - Nous Portal API key (requires: NOUS_API_KEY)\n # \"anthropic\" - Direct Anthropic API (requires: ANTHROPIC_API_KEY)\n # \"openai-codex\" - OpenAI Codex (requires: hermes auth)\n # \"copilot\" - GitHub Copilot / GitHub Models (requires: GITHUB_TOKEN)\n # \"gemini\" - Use Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n # \"zai\" - Use z.ai / ZhipuAI GLM models (requires: GLM_API_KEY)\n # \"kimi-coding\" - Kimi / Moonshot AI (requires: KIMI_API_KEY)\n # \"minimax\" - MiniMax global (requires: MINIMAX_API_KEY)\n # \"minimax-cn\" - MiniMax China (requires: MINIMAX_CN_API_KEY)\n # \"huggingface\" - Hugging Face Inference (requires: HF_TOKEN)\n # \"nvidia\" - NVIDIA NIM / build.nvidia.com (requires: NVIDIA_API_KEY)\n # \"xiaomi\" - Xiaomi MiMo (requires: XIAOMI_API_KEY)\n # \"arcee\" - Arcee AI Trinity models (requires: ARCEEAI_API_KEY)\n # \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY — https://ollama.com/settings)\n # \"kilocode\" - KiloCode gateway (requires: KILOCODE_API_KEY)\n # \"azure-foundry\" - Microsoft Foundry / Azure OpenAI (API key or Entra ID)\n # \"lmstudio\" - LM Studio local server (optional: LM_API_KEY, defaults to http://127.0.0.1:1234/v1)\n #\n # Local servers (LM Studio, Ollama, vLLM, llama.cpp):\n # \"custom\" - Any other OpenAI-compatible endpoint. Set base_url below.\n # Aliases: \"ollama\", \"vllm\", \"llamacpp\" all map to \"custom\".\n # LM Studio is first-class and uses provider: \"lmstudio\".\n # It works with both no-auth and auth-enabled server modes.\n #\n # Can also be overridden for a single invocation with the --provider flag.\n provider: \"auto\"\n \n # API configuration (falls back to OPENROUTER_API_KEY env var)\n # api_key: \"your-key-here\" # Uncomment to set here instead of .env\n base_url: \"https://openrouter.ai/api/v1\"\n\n # Azure Foundry keyless auth example:\n # provider: \"azure-foundry\"\n # base_url: \"https://.openai.azure.com/openai/v1\"\n # auth_mode: \"entra_id\" # DefaultAzureCredential: az login, managed identity, workload identity, etc.\n # default: \"gpt-4o\" # Deployment/model name\n # entra:\n # scope: \"https://ai.azure.com/.default\" # Optional; this is the default.\n\n # ── Token limits — two settings, easy to confuse ──────────────────────────\n #\n # context_length: TOTAL context window (input + output tokens combined).\n # Controls when Hermes compresses history and validates requests.\n # Leave unset — Hermes auto-detects the correct value from the provider.\n # Set manually only when auto-detection is wrong (e.g. a local server with\n # a custom num_ctx, or a proxy that doesn't expose /v1/models).\n #\n # context_length: 131072\n #\n # max_tokens: OUTPUT cap — maximum tokens the model may generate per response.\n # Unrelated to how long your conversation history can be.\n # The OpenAI-standard name \"max_tokens\" is a misnomer; Anthropic's native\n # API has since renamed it \"max_output_tokens\" for clarity.\n # Leave unset to use the model's native output ceiling (recommended).\n # Set only if you want to deliberately limit individual response length.\n #\n# max_tokens: 8192\n\n # ── Custom request headers (optional) ─────────────────────────────────────\n #\n # default_headers: extra HTTP headers sent on every request to an\n # OpenAI-compatible endpoint. User values take precedence over the\n # provider/SDK defaults, so this is the supported way to override the\n # OpenAI Python SDK's identifying headers (User-Agent: OpenAI/Python ...,\n # X-Stainless-*) when a custom provider sits behind a gateway/WAF that\n # rejects them — e.g. an upstream that returns \"502 Upstream access\n # forbidden\" for the SDK default User-Agent but accepts a plain one.\n # Applies on the OpenAI wire only (not native Anthropic / Bedrock).\n #\n # default_headers:\n # User-Agent: \"curl/8.7.1\"\n\n# Named provider overrides (optional)\n# Use this for per-provider request timeouts, non-stream stale timeouts,\n# and per-model exceptions.\n# Applies to the primary turn client on every api_mode (OpenAI-wire, native\n# Anthropic, and Anthropic-compatible providers), the fallback chain, and\n# client rebuilds during credential rotation. For OpenAI-wire chat\n# completions (streaming and non-streaming) the configured value is also\n# used as the per-request ``timeout=`` kwarg so it wins over the legacy\n# HERMES_API_TIMEOUT env var (which still applies when no config is set).\n# ``stale_timeout_seconds`` controls the non-streaming stale-call detector and\n# wins over the legacy HERMES_API_CALL_STALE_TIMEOUT env var. Leaving these\n# unset keeps the legacy defaults (HERMES_API_TIMEOUT=1800s,\n# HERMES_API_CALL_STALE_TIMEOUT=90s, native Anthropic 900s). The\n# implicit non-stream stale detector is auto-disabled for local endpoints\n# and can scale upward for very large contexts.\n#\n# Not currently wired for AWS Bedrock (bedrock_converse + AnthropicBedrock\n# SDK paths) — those use boto3 with its own timeout configuration.\n#\n# providers:\n# ollama-local:\n# request_timeout_seconds: 300 # Longer timeout for local cold-starts\n# stale_timeout_seconds: 900 # Explicitly re-enable stale detection on local endpoints\n# anthropic:\n# request_timeout_seconds: 30 # Fast-fail cloud requests\n# models:\n# claude-opus-4.6:\n# timeout_seconds: 600 # Longer timeout for extended-thinking Opus calls\n# openai-codex:\n# models:\n# gpt-5.4:\n# stale_timeout_seconds: 1800 # Longer non-stream stale timeout for slow large-context turns\n\n# =============================================================================\n# OpenRouter Provider Routing (only applies when using OpenRouter)\n# =============================================================================\n# Control how requests are routed across providers on OpenRouter.\n# See: https://openrouter.ai/docs/guides/routing/provider-selection\n#\n# provider_routing:\n# # Sort strategy: \"price\" (default), \"throughput\", or \"latency\"\n# # Append :nitro to model name for a shortcut to throughput sorting.\n# sort: \"throughput\"\n#\n# # Only allow these providers (provider slugs from OpenRouter)\n# # only: [\"anthropic\", \"google\"]\n#\n# # Skip these providers entirely\n# # ignore: [\"deepinfra\", \"fireworks\"]\n#\n# # Try providers in this order (overrides default load balancing)\n# # order: [\"anthropic\", \"google\", \"together\"]\n#\n# # Require providers to support all parameters in your request\n# # require_parameters: true\n#\n# # Data policy: \"allow\" (default) or \"deny\" to exclude providers that may store data\n# # data_collection: \"deny\"\n\n# =============================================================================\n# OpenRouter Response Caching (only applies when using OpenRouter)\n# =============================================================================\n# Cache identical API responses at the OpenRouter edge for free instant replays.\n# When enabled, identical requests (same model, messages, parameters) return\n# cached responses with zero billing. Separate from Anthropic prompt caching.\n# See: https://openrouter.ai/docs/guides/features/response-caching\n#\n# openrouter:\n# response_cache: true # Enable response caching (default: true)\n# response_cache_ttl: 300 # Cache TTL in seconds, 1-86400 (default: 300)\n\n# =============================================================================\n# Git Worktree Isolation\n# =============================================================================\n# When enabled, each CLI session creates an isolated git worktree so multiple\n# agents can work on the same repo concurrently without file collisions.\n# Equivalent to always passing --worktree / -w on the command line.\n#\n# worktree: true # Always create a worktree when in a git repo\n# worktree: false # Default — only create when -w flag is passed\n#\n# By default a new worktree branches from the freshly-fetched remote tip\n# (the current branch's upstream, else the remote's default branch) so it\n# starts current with the project instead of from the local clone's\n# (possibly stale) HEAD. Set worktree_sync: false to branch from local HEAD\n# instead — useful when offline or when you deliberately want the clone's\n# exact current state as the base.\n#\n# worktree_sync: true # Default — branch from the fetched remote tip\n# worktree_sync: false # Branch from local HEAD (offline / pinned base)\n\n# =============================================================================\n# Terminal Tool Configuration\n# =============================================================================\n# Choose ONE of the following terminal configurations by uncommenting it.\n# The terminal tool executes commands in the specified environment.\n\n# -----------------------------------------------------------------------------\n# OPTION 1: Local execution (default)\n# Commands run directly on your machine in the current directory\n# -----------------------------------------------------------------------------\n# Working directory behavior:\n# - CLI (`hermes` command): Uses \".\" (current directory where you run hermes)\n# - Gateway/messaging/cron: Uses terminal.cwd here; legacy .env cwd values are deprecated\nterminal:\n backend: \"local\"\n cwd: \".\" # For local backend: \".\" = current directory. Ignored for remote backends unless a backend documents otherwise.\n timeout: 180\n # HOME policy for tool subprocesses:\n # auto - default: host uses your real HOME; containers use HERMES_HOME/home\n # real - force your real OS-user HOME\n # profile - force HERMES_HOME/home for strict per-profile CLI config isolation\n home_mode: \"auto\"\n docker_mount_cwd_to_workspace: false # SECURITY: off by default. Opt in to mount the launch cwd into Docker /workspace.\n lifetime_seconds: 300\n # sudo_password: \"hunter2\" # Optional: pipe a sudo password via sudo -S. SECURITY WARNING: plaintext.\n # sudo_password: \"\" # Explicit empty password: try empty and never open the interactive sudo prompt.\n\n# -----------------------------------------------------------------------------\n# OPTION 2: SSH remote execution\n# Commands run on a remote server - agent code stays local (sandboxed)\n# Great for: keeping agent isolated from its own code, using powerful remote hardware\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"ssh\"\n# cwd: \"/home/myuser/project\" # Path on the REMOTE server\n# timeout: 180\n# lifetime_seconds: 300\n# ssh_host: \"my-server.example.com\"\n# ssh_user: \"myuser\"\n# ssh_port: 22\n# ssh_key: \"~/.ssh/id_rsa\" # Optional - uses ssh-agent if not specified\n\n# -----------------------------------------------------------------------------\n# OPTION 3: Docker container\n# Commands run in an isolated Docker container\n# Great for: reproducible environments, testing, isolation\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"docker\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /)\n# timeout: 180\n# lifetime_seconds: 300\n# docker_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# docker_mount_cwd_to_workspace: true # Explicit opt-in: mount your launch cwd into /workspace\n# # Optional: run the container as your host user's uid:gid so files written\n# # into bind-mounted dirs are owned by you, not root. Drops SETUID/SETGID\n# # caps too since no gosu privilege drop is needed. Leave off if your\n# # chosen docker_image expects to start as root.\n# docker_run_as_host_user: true\n# # Optional: explicitly forward selected env vars into Docker.\n# # These values come from your current shell first, then ~/.hermes/.env.\n# # Warning: anything forwarded here is visible to commands run in the container.\n# docker_forward_env:\n# - \"GITHUB_TOKEN\"\n# - \"NPM_TOKEN\"\n# # Optional: extra flags passed verbatim to docker run (appended after security defaults).\n# # Useful for adding capabilities (e.g. apt installs needing SETUID) or custom options.\n# # Example: add a Linux capability not included by default\n# # docker_extra_args:\n# # - \"--cap-add\"\n# # - \"SETUID\"\n\n# -----------------------------------------------------------------------------\n# OPTION 4: Singularity/Apptainer container\n# Commands run in a Singularity container (common in HPC environments)\n# Great for: HPC clusters, shared compute environments\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"singularity\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# singularity_image: \"docker://nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 5: Modal cloud execution\n# Commands run on Modal's cloud infrastructure\n# Great for: GPU access, scalable compute, serverless execution\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"modal\"\n# cwd: \"/workspace\" # Path INSIDE the sandbox (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# modal_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 6: Daytona cloud execution\n# Commands run in Daytona cloud sandboxes\n# Great for: Cloud dev environments, persistent workspaces, team collaboration\n# Requires: pip install daytona, DAYTONA_API_KEY env var\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"daytona\"\n# cwd: \"~\"\n# timeout: 180\n# lifetime_seconds: 300\n# daytona_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# container_disk: 10240 # Daytona max is 10GB per sandbox\n\n#\n# --- Container resource limits (docker, singularity, modal, daytona -- ignored for local/ssh) ---\n# These settings apply to all container backends. They control the resources\n# allocated to the sandbox and whether its filesystem persists across sessions.\n container_cpu: 1 # CPU cores\n container_memory: 5120 # Memory in MB (5120 = 5GB)\n container_disk: 51200 # Disk in MB (51200 = 50GB)\n container_persistent: true # Persist filesystem across sessions (false = ephemeral)\n\n# -----------------------------------------------------------------------------\n# SUDO SUPPORT (works with ALL backends above)\n# -----------------------------------------------------------------------------\n# Add sudo_password to any terminal config above to enable sudo commands.\n# The password is piped via `sudo -S`. Works with local, ssh, docker, etc.\n#\n# SECURITY WARNING: Password stored in plaintext!\n#\n# INTERACTIVE PROMPT: If sudo_password is unset and the CLI is running,\n# you'll be prompted to enter your password when sudo is needed:\n# - 45-second timeout (auto-skips if no input)\n# - Press Enter to skip (command fails gracefully)\n# - Password is hidden while typing\n# - Password is cached for the session\n#\n# EMPTY PASSWORDS: Setting sudo_password to an explicit empty string is different\n# from leaving it unset. Hermes will try an empty password via `sudo -S` and\n# will not open the interactive prompt. This is useful for passwordless sudo,\n# Touch ID sudo setups, and environments where prompting is just noise.\n#\n# ALTERNATIVES:\n# - SSH backend: Configure passwordless sudo on the remote server\n# - Containers: Run as root inside the container (no sudo needed)\n# - Local: Configure /etc/sudoers for specific commands\n#\n# Example (add to your terminal section):\n# sudo_password: \"your-password-here\"\n\n# =============================================================================\n# Security Scanning (tirith)\n# =============================================================================\n# Optional pre-exec command security scanning via tirith.\n# Detects homograph URLs, pipe-to-shell, terminal injection, env manipulation.\n# Install: brew install sheeki03/tap/tirith\n# Docs: https://github.com/sheeki03/tirith\n#\n# security:\n# tirith_enabled: true # Enable/disable tirith scanning\n# tirith_path: \"tirith\" # Path to tirith binary (supports ~ expansion)\n# tirith_timeout: 5 # Scan timeout in seconds\n# tirith_fail_open: true # Allow commands if tirith unavailable\n\n# =============================================================================\n# Browser Tool Configuration\n# =============================================================================\nbrowser:\n # Inactivity timeout in seconds - browser sessions are automatically closed\n # after this period of no activity between agent loops (default: 120 = 2 minutes)\n inactivity_timeout: 120\n\n# =============================================================================\n# Tool Loop Guardrails\n# =============================================================================\n# Soft warnings are enabled by default. They append guidance to repeated failed\n# or non-progressing tool results but still let the tool execute. Hard stops are\n# opt-in circuit breakers for autonomous/cron sessions where stopping a loop is\n# preferable to spending the full iteration budget.\ntool_loop_guardrails:\n warnings_enabled: true\n hard_stop_enabled: false\n warn_after:\n exact_failure: 2\n same_tool_failure: 3\n idempotent_no_progress: 2\n hard_stop_after:\n exact_failure: 5\n same_tool_failure: 8\n idempotent_no_progress: 5\n\n# =============================================================================\n# Context Compression (Auto-shrinks long conversations)\n# =============================================================================\n# When conversation approaches model's context limit, middle turns are\n# automatically summarized to free up space while preserving important context.\n#\n# HOW IT WORKS:\n# 1. Tracks actual token usage from API responses (not estimates)\n# 2. When prompt_tokens >= threshold% of model's context_length, triggers compression\n# 3. Protects first 3 turns (system prompt, initial request, first response)\n# 4. Protects last N turns (default 20 messages = ~10 full turns of recent context)\n# 5. Summarizes middle turns using a fast/cheap model\n# 6. Inserts summary as a user message, continues conversation seamlessly\n#\n# Post-compression tail budget is target_ratio × threshold × context_length:\n# 200K context, threshold 0.50, ratio 0.20 → 20K tokens of recent tail preserved\n# 1M context, threshold 0.50, ratio 0.20 → 100K tokens of recent tail preserved\n#\ncompression:\n # Enable automatic context compression (default: true)\n # Set to false if you prefer to manage context manually or want errors on overflow\n enabled: true\n \n # Trigger compression at this % of model's context limit (default: 0.50 = 50%)\n # Lower values = more aggressive compression, higher values = compress later\n threshold: 0.50\n \n # Fraction of the threshold to preserve as recent tail (default: 0.20 = 20%)\n # e.g. 20% of 50% threshold = 10% of total context kept as recent messages.\n # Summary output is separately capped at 12K tokens (Gemini output limit).\n # Range: 0.10 - 0.80\n target_ratio: 0.20\n\n # Number of most-recent messages to always preserve (default: 20 ≈ 10 full turns)\n # Higher values keep more recent conversation intact at the cost of more aggressive\n # compression of older turns.\n protect_last_n: 20\n\n # Number of non-system messages to protect at the head of the transcript, in\n # ADDITION to the system prompt (which is always implicitly protected).\n # Head messages are NEVER summarized — they survive every compression\n # indefinitely. This gives stable early context for short/medium sessions,\n # but in long-running sessions that rely on rolling compaction the pinned\n # opening turns may not match how you want the session framed over time.\n # Set to 0 to preserve ONLY the system prompt (plus the rolling summary\n # and recent tail) — the cleanest configuration for long-running sessions.\n # Default 3 preserves the system prompt plus the first three non-system\n # head messages, matching the pre-feature behaviour.\n protect_first_n: 3\n\n # To pin a specific model/provider for compression summaries, use the\n # auxiliary section below (auxiliary.compression.provider / model).\n\n# =============================================================================\n# Anthropic prompt caching TTL\n# =============================================================================\n# When prompt caching is active (Claude via OpenRouter or native Anthropic),\n# Anthropic supports two TTL tiers for cached prefixes: \"5m\" (default) and\n# \"1h\". Other values are ignored and \"5m\" is used.\n#\nprompt_caching:\n cache_ttl: \"5m\" # use \"1h\" for long sessions with pauses between turns\n\n# =============================================================================\n# Auxiliary Models (Advanced — Experimental)\n# =============================================================================\n# Hermes uses lightweight \"auxiliary\" models for side tasks: image analysis,\n# browser screenshot analysis, web page summarization, TTS audio-tag insertion,\n# and context compression.\n#\n# By default these use Gemini Flash via OpenRouter or Nous Portal and are\n# auto-detected from your credentials. You do NOT need to change anything\n# here for normal usage.\n#\n# WARNING: Overriding these with providers other than OpenRouter or Nous Portal\n# is EXPERIMENTAL and may not work. Not all models/providers support vision,\n# produce usable summaries, or accept the same API format. Change at your own\n# risk — if things break, reset to \"auto\" / empty values.\n#\n# Each task has its own provider + model pair so you can mix providers.\n# For example: OpenRouter for vision (needs multimodal), but your main\n# local endpoint for compression (just needs text).\n#\n# Provider options:\n# \"auto\" - Best available: OpenRouter → Nous Portal → main endpoint (default)\n# \"openrouter\" - Force OpenRouter (requires OPENROUTER_API_KEY)\n# \"nous\" - Force Nous Portal (requires: hermes auth add nous)\n# \"gemini\" - Force Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n# \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY)\n# \"codex\" - Force Codex OAuth (requires: hermes model → Codex).\n# Uses gpt-5.3-codex which supports vision.\n# \"main\" - Use your custom endpoint (OPENAI_BASE_URL + OPENAI_API_KEY).\n# Works with OpenAI API, local models, or any OpenAI-compatible\n# endpoint. Also falls back to Codex OAuth and API-key providers.\n#\n# Model: leave empty to use the provider's default. When empty, OpenRouter\n# uses \"google/gemini-3-flash-preview\" and Nous uses \"gemini-3-flash\".\n# Other providers pick a sensible default automatically.\n#\n# auxiliary:\n# # Image analysis: vision_analyze tool + browser screenshots\n# vision:\n# provider: \"auto\"\n# model: \"\" # e.g. \"google/gemini-2.5-flash\", \"openai/gpt-4o\"\n# timeout: 30 # LLM API call timeout (seconds)\n# download_timeout: 30 # Image HTTP download timeout (seconds)\n# # Increase for slow connections or self-hosted image servers\n#\n# # Web page scraping / summarization + browser page text extraction\n# web_extract:\n# provider: \"auto\"\n# model: \"\"\n#\n# # Gemini 3.1 TTS hidden audio-tag insertion\n# tts_audio_tags:\n# provider: \"auto\" # empty model = your main chat model\n# model: \"\"\n# timeout: 30\n#\n# # Session search — summarizes matching past sessions\n# session_search:\n# provider: \"auto\"\n# model: \"\"\n# timeout: 30\n# max_concurrency: 3 # Limit parallel summaries to reduce request-burst 429s\n# extra_body: {} # Provider-specific OpenAI-compatible request fields\n# # Example for providers that support request-body\n# # reasoning controls:\n# # extra_body:\n# # enable_thinking: false\n# # Some vLLM/Qwen deployments expect this nested:\n# # extra_body:\n# # chat_template_kwargs:\n# # enable_thinking: false\n\n# =============================================================================\n# Persistent Memory\n# =============================================================================\n# Bounded curated memory injected into the system prompt every session.\n# Two stores: MEMORY.md (agent's notes) and USER.md (user profile).\n# Character limits keep the memory small and focused. The agent manages\n# pruning -- when at the limit, it must consolidate or replace entries.\n# Disabled by default in batch_runner.\n#\nmemory:\n # Agent's personal notes: environment facts, conventions, things learned\n memory_enabled: true\n \n # User profile: preferences, communication style, expectations\n user_profile_enabled: true\n \n # Character limits (~2.75 chars per token, model-independent)\n memory_char_limit: 2200 # ~800 tokens\n user_char_limit: 1375 # ~500 tokens\n\n # Periodic memory nudge: remind the agent to consider saving memories\n # every N user turns. Set to 0 to disable. Only active when memory is enabled.\n nudge_interval: 10 # Nudge every 10 user turns (0 = disabled)\n\n # Memory flush: give the agent one turn to save memories before context is\n # lost (compression, /new, /reset, exit). Set to 0 to disable.\n # For exit/reset, only fires if the session had at least this many user turns.\n flush_min_turns: 6 # Min user turns to trigger flush on exit/reset (0 = disabled)\n\n# =============================================================================\n# Session Reset Policy (Messaging Platforms)\n# =============================================================================\n# Controls when messaging sessions (Telegram, Discord, WhatsApp, Slack) are\n# automatically cleared. Without resets, conversation context grows indefinitely\n# which increases API costs with every message.\n#\n# When a reset triggers, the agent first saves important information to its\n# persistent memory — but the conversation context is wiped. The agent starts\n# fresh but retains learned facts via its memory system.\n#\n# Users can always manually reset with /reset or /new in chat.\n#\n# Modes:\n# \"both\" - Reset on EITHER inactivity timeout or daily boundary (recommended)\n# \"idle\" - Reset only after N minutes of inactivity\n# \"daily\" - Reset only at a fixed hour each day\n# \"none\" - Never auto-reset; context lives until /reset or compression kicks in\n#\n# When a reset triggers, the agent gets one turn to save important memories and\n# skills before the context is wiped. Persistent memory carries across sessions.\n#\nsession_reset:\n mode: both # \"both\", \"idle\", \"daily\", or \"none\"\n idle_minutes: 1440 # Inactivity timeout in minutes (default: 1440 = 24 hours)\n at_hour: 4 # Daily reset hour, 0-23 local time (default: 4 AM)\n\n# Maximum number of simultaneously active chat sessions across CLI, TUI,\n# dashboard chat, and messaging gateway. Set to null, 0, or omit to allow\n# unlimited concurrent sessions. When the limit is reached, new sessions get a\n# clean error while existing active sessions keep their normal behavior. This\n# top-level key takes precedence over gateway.max_concurrent_sessions. The cap\n# is a best-effort single-host/profile runtime guard; Hermes fails open if the\n# local runtime lease registry cannot be read or locked.\nmax_concurrent_sessions: null\n\n# When true, group/channel chats use one session per participant when the platform\n# provides a user ID. This is the secure default and prevents users in the same\n# room from sharing context, interrupts, and token costs. Set false only if you\n# explicitly want one shared \"room brain\" per group/channel.\ngroup_sessions_per_user: true\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Gateway Streaming\n# ─────────────────────────────────────────────────────────────────────────────\n# Stream tokens to messaging platforms in real-time. The bot sends a message\n# on first token, then progressively edits it as more tokens arrive.\n# Disabled by default — enable to try the streaming UX on Telegram/Discord/Slack.\n# For Telegram, partial edits are sent as plain text and only the final edit uses MarkdownV2.\nstreaming:\n enabled: false\n # transport: edit # \"edit\" = progressive editMessageText\n # edit_interval: 0.3 # seconds between message edits\n # buffer_threshold: 40 # chars before forcing an edit flush\n # cursor: \" ▉\" # cursor shown during streaming\n\n# =============================================================================\n# Skills Configuration\n# =============================================================================\n# Skills are reusable procedures the agent can load and follow. The agent can\n# also create new skills after completing complex tasks.\n#\nskills:\n # Nudge the agent to create skills after complex tasks.\n # Every N tool-calling iterations, remind the model to consider saving a skill.\n # Set to 0 to disable.\n creation_nudge_interval: 15\n\n # External skill directories — share skills across tools/agents without\n # copying them into ~/.hermes/skills/. Each path is expanded (~ and ${VAR})\n # and resolved to an absolute path. External dirs are read-only: skill\n # creation always writes to ~/.hermes/skills/. Local skills take precedence\n # when names collide.\n # external_dirs:\n # - ~/.agents/skills\n # - /home/shared/team-skills\n\n# =============================================================================\n# Agent Behavior\n# =============================================================================\nagent:\n # Maximum tool-calling iterations per conversation\n # Higher = more room for complex tasks, but costs more tokens\n # Recommended: 20-30 for focused tasks, 50-100 for open exploration\n max_turns: 60\n\n # Inactivity timeout for gateway agent runs (seconds, 0 = unlimited).\n # The agent can run indefinitely when actively calling tools or receiving\n # API responses. Only fires after the agent has been idle for this duration.\n # gateway_timeout: 1800\n\n # Staged warning: send a warning before escalating to full timeout.\n # Fires once per run when inactivity reaches this threshold (seconds).\n # Set to 0 to disable the warning.\n # gateway_timeout_warning: 900\n\n # Graceful drain timeout for gateway stop/restart (seconds).\n # Default 0 = no drain: a restart interrupts in-flight agents immediately,\n # cleans up, and exits. Set a positive value only if you want a grace\n # window on /restart, and keep it well under systemd's TimeoutStopSec.\n # restart_drain_timeout: 0\n\n # Max app-level retry attempts for API errors (connection drops, provider\n # timeouts, 5xx, etc.) before the agent surfaces the failure. Lower this\n # to 1 if you use fallback providers and want fast failover on flaky\n # primaries (default 3). The OpenAI SDK does its own low-level retries\n # underneath this wrapper — this is the Hermes-level loop.\n # api_max_retries: 3\n\n # After the agent edits code without fresh passing verification, nudge it to\n # verify before finishing. The default \"auto\" enables it on interactive\n # coding surfaces (CLI, TUI, desktop) and programmatic callers, and disables\n # it on conversational messaging surfaces (Telegram, Discord, etc.) where the\n # verification summary would reach a human as chat noise. Set true or false to\n # force it on or off; the HERMES_VERIFY_ON_STOP env var (1/0) takes precedence.\n # verify_on_stop: auto\n\n # Standing operator instructions for the coding posture (when Hermes is in a\n # code workspace). Appended to the coding brief as an extra system block, so\n # you can pin project-wide workflow rules without editing the shipped brief.\n # Accepts a string or a list of strings. Takes effect next session.\n # coding_instructions:\n # - \"For UI work, don't run tsc/lint until I approve the look.\"\n # - \"Clean the diff before you commit and push.\"\n\n # When verify-on-stop finds edited code without fresh verification evidence,\n # append guidance for creative UI work (avoid broad tsc/lint/test before visual\n # approval) and clean-diff expectations. Set false to keep that nudge terse.\n # verify_guidance: true\n\n # A `pre_verify` hook (plugin or shell, see Event Hooks docs) can keep the\n # agent going one more turn to verify/clean before finishing. This caps how\n # many times one turn may be nudged to continue, so a hook can't trap the loop.\n # Default 3.\n # max_verify_nudges: 3\n\n # Enable verbose logging\n verbose: false\n \n # Reasoning effort level (OpenRouter and Nous Portal)\n # Controls how much \"thinking\" the model does before responding.\n # Options: \"xhigh\" (max), \"high\", \"medium\", \"low\", \"minimal\", \"none\" (disable)\n reasoning_effort: \"medium\"\n \n # Predefined personalities (use with /personality command)\n personalities:\n helpful: \"You are a helpful, friendly AI assistant.\"\n concise: \"You are a concise assistant. Keep responses brief and to the point.\"\n technical: \"You are a technical expert. Provide detailed, accurate technical information.\"\n creative: \"You are a creative assistant. Think outside the box and offer innovative solutions.\"\n teacher: \"You are a patient teacher. Explain concepts clearly with examples.\"\n kawaii: \"You are a kawaii assistant! Use cute expressions like (◕‿◕), ★, ♪, and ~! Add sparkles and be super enthusiastic about everything! Every response should feel warm and adorable desu~! ヽ(>∀<☆)ノ\"\n catgirl: \"You are Neko-chan, an anime catgirl AI assistant, nya~! Add 'nya' and cat-like expressions to your speech. Use kaomoji like (=^・ω・^=) and ฅ^•ﻌ•^ฅ. Be playful and curious like a cat, nya~!\"\n pirate: \"Arrr! Ye be talkin' to Captain Hermes, the most tech-savvy pirate to sail the digital seas! Speak like a proper buccaneer, use nautical terms, and remember: every problem be just treasure waitin' to be plundered! Yo ho ho!\"\n shakespeare: \"Hark! Thou speakest with an assistant most versed in the bardic arts. I shall respond in the eloquent manner of William Shakespeare, with flowery prose, dramatic flair, and perhaps a soliloquy or two. What light through yonder terminal breaks?\"\n surfer: \"Duuude! You're chatting with the chillest AI on the web, bro! Everything's gonna be totally rad. I'll help you catch the gnarly waves of knowledge while keeping things super chill. Cowabunga! 🤙\"\n noir: \"The rain hammered against the terminal like regrets on a guilty conscience. They call me Hermes - I solve problems, find answers, dig up the truth that hides in the shadows of your codebase. In this city of silicon and secrets, everyone's got something to hide. What's your story, pal?\"\n uwu: \"hewwo! i'm your fwiendwy assistant uwu~ i wiww twy my best to hewp you! *nuzzles your code* OwO what's this? wet me take a wook! i pwomise to be vewy hewpful >w<\"\n philosopher: \"Greetings, seeker of wisdom. I am an assistant who contemplates the deeper meaning behind every query. Let us examine not just the 'how' but the 'why' of your questions. Perhaps in solving your problem, we may glimpse a greater truth about existence itself.\"\n hype: \"YOOO LET'S GOOOO!!! 🔥🔥🔥 I am SO PUMPED to help you today! Every question is AMAZING and we're gonna CRUSH IT together! This is gonna be LEGENDARY! ARE YOU READY?! LET'S DO THIS! 💪😤🚀\"\n\n# =============================================================================\n# Toolsets\n# =============================================================================\n# Control which tools the agent has access to.\n# Use `hermes tools` to interactively enable/disable tools per platform.\n\n# =============================================================================\n# Platform Toolsets (per-platform tool configuration)\n# =============================================================================\n# Override which toolsets are available on each platform.\n# If a platform isn't listed here, its built-in default is used.\n#\n# You can use EITHER:\n# - A preset like \"hermes-cli\" or \"hermes-telegram\" (curated tool set)\n# - A list of individual toolsets to compose your own (see list below)\n#\n# Supported platform keys: cli, telegram, discord, whatsapp, slack, qqbot, teams, google_chat\n#\n# Examples:\n#\n# # Use presets (same as defaults):\n# platform_toolsets:\n# cli: [hermes-cli]\n# telegram: [hermes-telegram]\n#\n# # Custom: give Telegram only web + terminal + file + planning:\n# platform_toolsets:\n# telegram: [web, terminal, file, todo]\n#\n# # Custom: CLI without browser or image gen:\n# platform_toolsets:\n# cli: [web, terminal, file, skills, todo, tts, cronjob]\n#\n# # Restrictive: Discord gets read-only tools only:\n# platform_toolsets:\n# discord: [web, vision, skills, todo]\n#\n# If not set, defaults are:\n# cli: hermes-cli (everything + cronjob management)\n# telegram: hermes-telegram (terminal, file, web, vision, image, tts, browser, skills, todo, cronjob, messaging)\n# discord: hermes-discord (same as telegram)\n# whatsapp: hermes-whatsapp (same as telegram)\n# slack: hermes-slack (same as telegram)\n# signal: hermes-signal (same as telegram)\n# homeassistant: hermes-homeassistant (same as telegram)\n# qqbot: hermes-qqbot (same as telegram)\n# teams: hermes-teams (same as telegram)\n# google_chat: hermes-google_chat (same as telegram)\n#\nplatform_toolsets:\n cli: [hermes-cli]\n telegram: [hermes-telegram]\n discord: [hermes-discord]\n whatsapp: [hermes-whatsapp]\n slack: [hermes-slack]\n signal: [hermes-signal]\n homeassistant: [hermes-homeassistant]\n qqbot: [hermes-qqbot]\n yuanbao: [hermes-yuanbao]\n teams: [hermes-teams]\n google_chat: [hermes-google_chat]\n\n# =============================================================================\n# Gateway Platform Settings\n# =============================================================================\n# Optional per-platform messaging settings.\n# Platform-specific knobs live under `extra`.\n#\n# platforms:\n# telegram:\n# reply_to_mode: \"first\" # off | first | all\n# # guest_mode lets explicit @mentions from non-allowlisted groups through.\n# # Default false; ordinary messages, replies, and regex wake words stay blocked.\n# guest_mode: false\n# # allowed_chats: [\"-1001234567890\"]\n# extra:\n# disable_link_previews: false # Set true to suppress Telegram URL previews in bot messages\n# rich_messages: false # Bot API 10.1 rich messages (tables/task lists/details/math); default false for copyable legacy MarkdownV2, set true to opt in\n# rich_drafts: false # Experimental rich draft previews during Telegram DM streaming; default false because Telegram Desktop/macOS can visually overlay draft frames\n# command_menu:\n# # Telegram allows up to 100 BotCommands; Hermes defaults to 60 so\n# # all built-in commands plus common skill commands stay visible\n# # while remaining under Telegram's payload-size limit. Clamped 1..100.\n# max_commands: 60\n# # prepend = user priority first, then Hermes defaults\n# # append = Hermes defaults first, then user priority\n# # replace = only the list below defines priority\n# priority_mode: prepend\n# priority:\n# - my_plugin_command\n#\n# Discord-specific settings (config.yaml top-level, not under platforms:):\n#\n# discord:\n# require_mention: true # Require @mention in server channels (default: true)\n# auto_thread: true # Auto-create thread on @mention (default: true)\n# free_response_channels: \"\" # Channel IDs where no mention is needed\n# reactions: true # Show processing reactions (default: true)\n# history_backfill: true # Recover missed channel messages on mention (default: true)\n# history_backfill_limit: 50 # Max messages to scan backwards (default: 50)\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Available toolsets (use these names in platform_toolsets or the toolsets list)\n#\n# Run `hermes chat --list-toolsets` to see all toolsets and their tools.\n# Run `hermes chat --list-tools` to see every individual tool with descriptions.\n# ─────────────────────────────────────────────────────────────────────────────\n#\n# INDIVIDUAL TOOLSETS (compose your own):\n# web - web_search, web_extract\n# search - web_search only (no scraping)\n# terminal - terminal, process\n# file - read_file, write_file, patch, search\n# browser - browser_navigate, browser_snapshot, browser_click, browser_type,\n# browser_scroll, browser_back, browser_press,\n# browser_get_images, browser_vision (requires BROWSERBASE_API_KEY)\n# vision - vision_analyze (requires OPENROUTER_API_KEY)\n# image_gen - image_generate (requires FAL_KEY)\n# skills - skills_list, skill_view\n# skills_hub - skill_hub (search/install/manage from online registries — user-driven only)\n# todo - todo (in-memory task planning, no deps)\n# tts - text_to_speech (Edge TTS free, or ELEVENLABS/OPENAI/MINIMAX/MISTRAL key)\n# cronjob - cronjob (create/list/update/pause/resume/run/remove scheduled tasks)\n#\n# PRESETS (curated bundles):\n# hermes-cli - All of the above except send_message\n# hermes-telegram - terminal, file, web, vision, image_gen, tts, browser,\n# skills, todo, cronjob, send_message\n# hermes-discord - Same as hermes-telegram\n# hermes-whatsapp - Same as hermes-telegram\n# hermes-slack - Same as hermes-telegram\n#\n# COMPOSITE:\n# debugging - terminal + web + file\n# safe - web + vision (no terminal access)\n# all - Everything available\n#\n# web - Web search and content extraction (web_search, web_extract)\n# search - Web search only, no scraping (web_search)\n# terminal - Command execution and process management (terminal, process)\n# file - File operations: read, write, patch, search\n# browser - Full browser automation (navigate, click, type, screenshot, etc.)\n# vision - Image analysis (vision_analyze)\n# image_gen - Image generation with FLUX (image_generate)\n# skills - Load skill documents (skills_list, skill_view)\n# todo - Task planning and tracking for multi-step work\n# memory - Persistent memory across sessions (personal notes + user profile)\n# session_search - Search and recall past conversations (FTS5 + Gemini Flash summarization)\n# tts - Text-to-speech (Edge TTS free, ElevenLabs, OpenAI, MiniMax, Mistral)\n# cronjob - Schedule and manage automated tasks (CLI-only)\n#\n# Composite toolsets:\n# debugging - terminal + web + file (for troubleshooting)\n# safe - web + vision (no terminal access)\n\n# NOTE: The top-level \"toolsets\" key is deprecated and ignored.\n# Tool configuration is managed per-platform via platform_toolsets above.\n# Use `hermes tools` to configure interactively, or edit platform_toolsets directly.\n#\n# CLI override: hermes chat --toolsets terminal,web,file\n\n# =============================================================================\n# MCP (Model Context Protocol) Servers\n# =============================================================================\n# Connect to external MCP servers to add tools from the MCP ecosystem.\n# Each server's tools are automatically discovered and registered.\n# See website/docs/user-guide/features/mcp.md for full documentation.\n#\n# Stdio servers (spawn a subprocess):\n# command: the executable to run\n# args: command-line arguments\n# env: environment variables (only these + safe defaults passed to subprocess)\n#\n# HTTP servers (connect to a URL):\n# url: the MCP server endpoint\n# headers: HTTP headers (e.g., for authentication)\n#\n# Optional per-server settings:\n# timeout: tool call timeout in seconds (default: 120)\n# connect_timeout: initial connection timeout (default: 60)\n# keepalive_interval: liveness ping cadence in seconds (default: 180).\n# Lower it below the server's session TTL for servers that expire idle\n# sessions quickly (e.g. Unreal Engine editor MCP, ~15s), otherwise idle\n# tool calls hit an expired session and pay a slow reconnect. Floored at 5s.\n#\n# mcp_servers:\n# time:\n# command: uvx\n# args: [\"mcp-server-time\"]\n# filesystem:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-filesystem\", \"/home/user\"]\n# notion:\n# url: https://mcp.notion.com/mcp\n# github:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-github\"]\n# env:\n# GITHUB_PERSONAL_ACCESS_TOKEN: \"ghp_...\"\n#\n# Sampling (server-initiated LLM requests) — enabled by default.\n# Per-server config under the 'sampling' key:\n# analysis:\n# command: npx\n# args: [\"-y\", \"analysis-server\"]\n# sampling:\n# enabled: true # default: true\n# model: \"gemini-3-flash\" # override model (optional)\n# max_tokens_cap: 4096 # max tokens per request\n# timeout: 30 # LLM call timeout (seconds)\n# max_rpm: 10 # max requests per minute\n# allowed_models: [] # model whitelist (empty = all)\n# max_tool_rounds: 5 # tool loop limit (0 = disable)\n# log_level: \"info\" # audit verbosity\n\n# =============================================================================\n# Text-to-Speech\n# =============================================================================\n# TTS defaults to Edge TTS unless changed in ~/.hermes/config.yaml.\n# Gemini TTS supports persona/director prompt files, and Gemini 3.1 Flash TTS\n# can use a hidden auxiliary rewrite pass to insert expressive square-bracket\n# audio tags into the TTS script without showing tags in chat.\n#\n# tts:\n# provider: \"gemini\"\n# gemini:\n# model: \"gemini-3.1-flash-tts-preview\"\n# voice: \"Kore\"\n# audio_tags: false\n# persona_prompt_file: \"\" # e.g. ~/.hermes/tts/radio-host.md\n\n# =============================================================================\n# Voice Transcription (Speech-to-Text)\n# =============================================================================\n# Automatically transcribe voice messages on messaging platforms.\n# Providers: local (free, faster-whisper) | groq (free tier) | openai (Whisper API) | mistral (Voxtral Transcribe)\n# Set the corresponding API key in .env: GROQ_API_KEY, OPENAI_API_KEY, or MISTRAL_API_KEY.\nstt:\n enabled: true\n # provider: \"local\" # auto-detected if omitted\n local:\n model: \"base\" # tiny | base | small | medium | large-v3 | turbo\n # language: \"\" # auto-detect; set to \"en\", \"es\", \"fr\", etc. to force\n openai:\n model: \"whisper-1\" # whisper-1 | gpt-4o-mini-transcribe | gpt-4o-transcribe\n # mistral:\n # model: \"voxtral-mini-latest\" # voxtral-mini-latest | voxtral-mini-2602\n\n# =============================================================================\n# Response Pacing (Messaging Platforms)\n# =============================================================================\n# Add human-like delays between message chunks.\n# human_delay:\n# mode: \"off\" # \"off\" | \"natural\" | \"custom\"\n# min_ms: 800 # Min delay (custom mode only)\n# max_ms: 2500 # Max delay (custom mode only)\n\n# =============================================================================\n# Session Logging\n# =============================================================================\n# Session trajectories are automatically saved to logs/ directory.\n# Each session creates: logs/session_YYYYMMDD_HHMMSS_UUID.json\n#\n# The session ID is displayed in the welcome banner for easy reference.\n# Logs contain full conversation history in trajectory format:\n# - System prompt, user messages, assistant responses\n# - Tool calls with inputs/outputs\n# - Timestamps for debugging\n#\n# No configuration needed - logging is always enabled.\n# To disable, you would need to modify the source code.\n\n# =============================================================================\n# Code Execution Sandbox (Programmatic Tool Calling)\n# =============================================================================\n# The execute_code tool runs Python scripts that call Hermes tools via RPC.\n# Intermediate tool results stay out of the LLM's context window.\ncode_execution:\n timeout: 300 # Max seconds per script before kill (default: 300 = 5 min)\n max_tool_calls: 50 # Max RPC tool calls per execution (default: 50)\n\n# =============================================================================\n# Subagent Delegation\n# =============================================================================\n# The delegate_task tool spawns child agents with isolated context.\n# Supports single tasks and batch mode (default 3 parallel, configurable).\ndelegation:\n max_iterations: 50 # Max tool-calling turns per child (default: 50)\n # max_concurrent_children: 3 # Max parallel child agents per batch (default: 3, floor: 1, no ceiling).\n # WARNING: values above 10 multiply API cost linearly.\n # max_spawn_depth: 1 # Delegation tree depth cap (range: 1-3, default: 1 = flat).\n # Raise to 2 to allow workers to spawn their own subagents.\n # Requires role=\"orchestrator\" on intermediate agents.\n # orchestrator_enabled: true # Kill switch for role=\"orchestrator\" children (default: true).\n # subagent_auto_approve: false # When a subagent hits a dangerous-command approval prompt, auto-deny (default: false)\n # or auto-approve \"once\" (true) instead of blocking on stdin.\n # The parent TUI owns stdin, so blocking would deadlock; non-interactive resolution is required.\n # Both choices emit a logger.warning audit line. Flip to true only for cron/batch pipelines.\n # inherit_mcp_toolsets: true # When explicit child toolsets are narrowed, also keep the parent's MCP toolsets (default: true). Set false for strict intersection.\n # model: \"google/gemini-3-flash-preview\" # Override model for subagents (empty = inherit parent)\n # provider: \"openrouter\" # Override provider for subagents (empty = inherit parent)\n # # Resolves full credentials (base_url, api_key) automatically.\n # # Supported: openrouter, nous, zai, kimi-coding, minimax\n\n# =============================================================================\n# Honcho Integration (Cross-Session User Modeling)\n# =============================================================================\n# AI-native persistent memory via Honcho (https://honcho.dev/).\n# Builds a deeper understanding of the user across sessions and tools.\n# Runs alongside USER.md — additive, not a replacement.\n#\n# Requires: pip install honcho-ai\n# Config: ~/.honcho/config.json (shared with Claude Code, Cursor, etc.)\n# API key: HONCHO_API_KEY in ~/.hermes/.env or ~/.honcho/config.json\n#\n# Hermes-specific overrides (optional — most config comes from ~/.honcho/config.json):\n# honcho: {}\n\n# =============================================================================\n# Display\n# =============================================================================\ndisplay:\n # Use compact banner mode (hides the ASCII-art banner, shows a single line).\n # true: Compact single-line banner\n # false: Full ASCII banner with tool/skill summary (default)\n compact: false\n\n # Tool progress display level (CLI and gateway)\n # off: Silent — no tool activity shown, just the final response\n # new: Show a tool indicator only when the tool changes (skip repeats)\n # all: Show every tool call with a short preview (default)\n # verbose: Full args, results, and debug logs (same as /verbose)\n # log: Silent in chat; append every tool call to ~/.hermes/logs/tool_calls.log (gateway only)\n # Toggle at runtime with /verbose in the CLI\n tool_progress: all\n\n # Per-platform defaults can be quieter than the global setting. Telegram\n # tunes for mobile: tool_progress and busy_ack_detail default off (no\n # per-tool breadcrumb stream, no \"iteration 21/60\" debug detail in busy\n # acks or heartbeats), but interim_assistant_messages and\n # long_running_notifications STAY ON so the user has real signal between\n # turn start and final answer (mid-turn assistant commentary + a single\n # edit-in-place \"⏳ Working — N min\" heartbeat). Override under\n # display.platforms.telegram.\n\n # Auto-cleanup of temporary progress bubbles after the final response lands.\n # On platforms that support message deletion (currently Telegram), this\n # removes the tool-progress bubble, \"⏳ Still working...\" notices, and\n # context-pressure status messages once the final reply has been delivered —\n # keeping long-running turns visible live, then tidy afterward. Failed runs\n # leave the bubbles in place as breadcrumbs. Off by default.\n # Per-platform override: display.platforms.telegram.cleanup_progress\n # true: Delete tracked progress/status bubbles on successful turn\n # false: Leave everything in place (default)\n # Example:\n # display:\n # platforms:\n # telegram:\n # cleanup_progress: true\n cleanup_progress: false\n\n # Gateway-only natural mid-turn assistant updates.\n # When true, completed assistant status messages are sent as separate chat\n # messages. This is independent of tool_progress and gateway streaming.\n # true: Send mid-turn assistant updates as separate messages (default)\n # false: Only send the final response\n interim_assistant_messages: true\n\n # Gateway-only long-running status heartbeats.\n # When false, the platform does not receive periodic \"⏳ Working — N min\"\n # notifications even if agent.gateway_notify_interval is non-zero. The\n # heartbeat edits a single message in place (where the adapter supports\n # editing) instead of posting a new bubble each interval.\n # Default: true everywhere, including Telegram (silent agents are worse\n # than a single edit-in-place heartbeat).\n long_running_notifications: true\n\n # Include detailed iteration/tool/status context in busy acknowledgments\n # and long-running heartbeats. When true, busy acks show \"iteration 21/60,\n # terminal, 10 min\" and the heartbeat shows \"⏳ Working — 12 min,\n # iteration 21/60, terminal\". When false (Telegram default), both stay\n # terse: \"Interrupting current task\" and \"⏳ Working — 12 min, terminal\".\n busy_ack_detail: true\n\n # What Enter does when Hermes is already busy (CLI and gateway platforms).\n # interrupt: Interrupt the current run and redirect Hermes (default)\n # queue: Queue your message for the next turn\n # steer: Inject your message mid-run via /steer, arriving at the agent\n # after the next tool call — no interrupt, no role violation.\n # Falls back to 'queue' if the agent isn't running yet or if\n # images are attached (steer only carries text).\n # Ctrl+C (or /stop in gateway) always interrupts regardless of this setting.\n # Toggle at runtime with /busy .\n busy_input_mode: interrupt\n\n # Background process notifications (gateway/messaging only).\n # Controls how chatty the process watcher is when you use\n # terminal(background=true, notify_on_complete=true) from Telegram/Discord/etc.\n # off: No watcher messages at all\n # result: Only the final completion message\n # error: Only the final message when exit code != 0\n # all: Running output updates + final message (default)\n background_process_notifications: all\n\n\n # Play terminal bell when agent finishes a response.\n # Useful for long-running tasks — your terminal will ding when the agent is done.\n # Works over SSH. Most terminals can be configured to flash the taskbar or play a sound.\n # true: Ring the terminal bell on each response\n # false: Silent (default)\n bell_on_complete: false\n\n # Show model reasoning/thinking before each response.\n # When enabled, a dim box shows the model's thought process above the response.\n # Toggle at runtime with /reasoning show or /reasoning hide.\n # true: Show the reasoning box\n # false: Hide reasoning (default)\n show_reasoning: false\n\n # Stream tokens to the terminal as they arrive instead of waiting for the\n # full response. The response box opens on first token and text appears\n # line-by-line. Tool calls are still captured silently.\n # true: Stream tokens as they arrive (default)\n # false: Wait for the full response before rendering\n streaming: true\n\n # Show [HH:MM] timestamps on user input and assistant response labels.\n # timestamps: false\n\n # ───────────────────────────────────────────────────────────────────────────\n # Skin / Theme\n # ───────────────────────────────────────────────────────────────────────────\n # Customize CLI visual appearance — banner colors, spinner faces, tool prefix,\n # response box label, and branding text. Change at runtime with /skin .\n #\n # Built-in skins:\n # default — Classic Hermes gold/kawaii\n # ares — Crimson/bronze war-god theme with spinner wings\n # mono — Clean grayscale monochrome\n # slate — Cool blue developer-focused\n # daylight — Bright light-mode theme\n # warm-lightmode — Warm paper-tone light-mode theme\n # poseidon — Sea-green/teal Olympian theme\n # sisyphus — Earthy stone-and-moss theme\n # charizard — Fiery orange dragon theme\n #\n # Custom skins: drop a YAML file in ~/.hermes/skins/.yaml\n # Schema (all fields optional, missing values inherit from default):\n #\n # name: my-theme\n # description: Short description\n # colors:\n # banner_border: \"#HEX\" # Panel border\n # banner_title: \"#HEX\" # Panel title\n # banner_accent: \"#HEX\" # Section headers (Available Tools, etc.)\n # banner_dim: \"#HEX\" # Dim/muted text\n # banner_text: \"#HEX\" # Body text (tool names, skill names)\n # ui_accent: \"#HEX\" # UI accent color\n # response_border: \"#HEX\" # Response box border color\n # spinner:\n # waiting_faces: [\"(⚔)\", \"(⛨)\"] # Faces shown while waiting\n # thinking_faces: [\"(⚔)\", \"(⌁)\"] # Faces shown while thinking\n # thinking_verbs: [\"forging\", \"plotting\"] # Verbs for spinner messages\n # wings: # Optional left/right spinner decorations\n # - [\"⟪⚔\", \"⚔⟫\"]\n # - [\"⟪▲\", \"▲⟫\"]\n # branding:\n # agent_name: \"My Agent\" # Banner title and branding\n # welcome: \"Welcome message\" # Shown at CLI startup\n # response_label: \" ⚔ Agent \" # Response box header label\n # prompt_symbol: \"⚔\" # Prompt symbol (bare token; renderers add trailing space)\n # tool_prefix: \"╎\" # Tool output line prefix (default: ┊)\n #\n skin: default\n\n# =============================================================================\n# Model Aliases — short names for /model command\n# =============================================================================\n# Map short aliases to exact (model, provider, base_url) tuples.\n# Used by /model tab completion and resolve_alias().\n# Aliases are checked BEFORE the models.dev catalog, so they can route\n# to endpoints not in the catalog (e.g. Ollama Cloud, local servers).\n#\n# model_aliases:\n# opus:\n# model: claude-opus-4-6\n# provider: anthropic\n# qwen:\n# model: \"qwen3.5:397b\"\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n# glm:\n# model: glm-4.7\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n\n# =============================================================================\n# Privacy\n# =============================================================================\n# privacy:\n# # Redact PII from the LLM context prompt.\n# # When true, phone numbers are stripped and user/chat IDs are replaced\n# # with deterministic hashes before being sent to the model.\n# # Names and usernames are NOT affected (user-chosen, publicly visible).\n# # Routing/delivery still uses the original values internally.\n# redact_pii: false\n\n# =============================================================================\n# Shell-script hooks\n# =============================================================================\n# Register shell scripts as plugin-hook callbacks. Each entry is executed as\n# a subprocess (shell=False, shlex.split) with a JSON payload on stdin. On\n# stdout the script may return JSON that either blocks the tool call or\n# injects context into the next LLM call.\n#\n# Valid events (mirror hermes_cli.plugins.VALID_HOOKS):\n# pre_tool_call, post_tool_call, pre_llm_call, post_llm_call,\n# pre_api_request, post_api_request, on_session_start, on_session_end,\n# on_session_finalize, on_session_reset, subagent_stop\n#\n# First-use consent: each (event, command) pair prompts once on a TTY, then\n# is persisted to ~/.hermes/shell-hooks-allowlist.json. Non-interactive\n# runs (gateway, cron) need --accept-hooks, HERMES_ACCEPT_HOOKS=1, or the\n# hooks_auto_accept key below.\n#\n# See website/docs/user-guide/features/hooks.md for the full JSON wire\n# protocol and worked examples.\n#\n# hooks:\n# pre_tool_call:\n# - matcher: \"terminal\"\n# command: \"~/.hermes/agent-hooks/block-rm-rf.sh\"\n# timeout: 10\n# post_tool_call:\n# - matcher: \"write_file|patch\"\n# command: \"~/.hermes/agent-hooks/auto-format.sh\"\n# pre_llm_call:\n# - command: \"~/.hermes/agent-hooks/inject-cwd-context.sh\"\n# subagent_stop:\n# - command: \"~/.hermes/agent-hooks/log-orchestration.sh\"\n#\n# hooks_auto_accept: false\n\n\n# =============================================================================\n# Update Behavior\n# =============================================================================\nupdates:\n # Create a full HERMES_HOME zip before every `hermes update`.\n # Backups land in ~/.hermes/backups/ and can be restored with `hermes import`.\n # Off by default because large homes can add minutes to every update.\n pre_update_backup: false\n\n # Number of pre-update backup zips to retain.\n backup_keep: 5\n\n # What non-interactive updates do with local source edits in the Hermes repo.\n # Interactive terminal updates always prompt before restoring the autostash.\n #\n # stash - auto-stash before pull, then auto-restore after success (default)\n # discard - drop the update-created stash after success; use only on managed\n # installs where local source edits should not persist\n non_interactive_local_changes: \"stash\"\n\n\n# =============================================================================\n# Web Dashboard\n# =============================================================================\n# OAuth gate configuration for `hermes dashboard --host `.\n# The bundled Nous Portal plugin reads these on startup; settings here are\n# the canonical surface. Each can be overridden by an environment variable:\n#\n# dashboard.oauth.client_id <- HERMES_DASHBOARD_OAUTH_CLIENT_ID\n# dashboard.oauth.portal_url <- HERMES_DASHBOARD_PORTAL_URL\n# dashboard.public_url <- HERMES_DASHBOARD_PUBLIC_URL\n#\n# Env wins when set to a non-empty value. This is what Fly.io's platform-\n# secret injection uses to push per-deploy client_ids without needing to\n# bake a config.yaml into the image. Empty env values are treated as unset\n# so a provisioned-but-not-populated secret can't shadow a valid entry here.\n#\n# Local dev / on-prem deploys should typically set these via config.yaml\n# (the ~/.hermes/.env file is reserved for API keys and secrets).\n#\n# dashboard:\n# oauth:\n# client_id: \"\" # agent:{instance_id}; Portal provisions this at deploy\n# portal_url: \"\" # blank → default https://portal.nousresearch.com\n#\n# # Force the absolute base URL the OAuth callback (and any other public\n# # URL the dashboard hands to external systems) is built from. Set this\n# # for deploys behind reverse proxies that don't reliably forward\n# # X-Forwarded-Host / X-Forwarded-Proto / X-Forwarded-Prefix (manual\n# # nginx setups, on-prem ingresses, custom-domain Fly deploys without\n# # full proxy header chains).\n# #\n# # When set, the value is the complete authority: scheme + host +\n# # optional path prefix (e.g. \"https://example.com/hermes\"). The OAuth\n# # callback URL becomes \"/auth/callback\" — X-Forwarded-Prefix\n# # is IGNORED on this code path because the operator has explicitly\n# # declared the public URL and we no longer need to guess.\n# #\n# # Leave empty to use the existing proxy-header reconstruction (the\n# # default — works on Fly.io out of the box).\n# #\n# # public_url: \"https://example.com/hermes\"\n#\n# -----------------------------------------------------------------------------\n# Self-hosted OIDC dashboard auth (generic OpenID Connect — Authentik,\n# Keycloak, Zitadel, Authelia, Auth0, Okta, Google, …). Use this INSTEAD of the\n# nous block above when gating the dashboard with your own identity provider.\n# Each setting can be overridden by an environment variable:\n#\n# dashboard.oauth.self_hosted.issuer <- HERMES_DASHBOARD_OIDC_ISSUER\n# dashboard.oauth.self_hosted.client_id <- HERMES_DASHBOARD_OIDC_CLIENT_ID\n# dashboard.oauth.self_hosted.scopes <- HERMES_DASHBOARD_OIDC_SCOPES\n# dashboard.oauth.self_hosted.client_secret <- HERMES_DASHBOARD_OIDC_CLIENT_SECRET\n#\n# dashboard:\n# oauth:\n# provider: self-hosted\n# self_hosted:\n# issuer: \"https://auth.example.com/application/o/hermes/\" # required\n# client_id: \"hermes-dashboard\" # required\n# scopes: \"openid profile email\" # optional\n#\n# # OPTIONAL — set ONLY if your IDP registered the client as\n# # *confidential* (Authentik / Keycloak often default to this). When\n# # set, Hermes authenticates the client at the token endpoint\n# # (client_secret_basic or client_secret_post, auto-selected from the\n# # IDP's discovery doc) IN ADDITION to PKCE. Leave unset for a public\n# # (PKCE-only) client — the common case.\n# #\n# # This is a CREDENTIAL: prefer setting HERMES_DASHBOARD_OIDC_CLIENT_SECRET\n# # in ~/.hermes/.env over putting it here in config.yaml.\n# # client_secret: \"\"\n"}, "821d9f709f365453abde7ca71445bc0aad9bdaa1": {"t": 1785767888.6126287, "n": 67983, "h": "fca8b1ea6beb82d8", "c": "# Hermes Agent CLI Configuration\n# Copy this file to cli-config.yaml and customize as needed.\n# This file configures the CLI behavior. Environment variables in .env take precedence.\n\n# =============================================================================\n# Model Configuration\n# =============================================================================\nmodel:\n # Default model to use (can be overridden with --model flag)\n # Both \"default\" and \"model\" work as the key name here.\n default: \"anthropic/claude-opus-4.6\"\n \n # Inference provider selection:\n # \"auto\" - Auto-detect from credentials (default)\n # \"openrouter\" - OpenRouter (requires: OPENROUTER_API_KEY or OPENAI_API_KEY)\n # \"nous\" - Nous Portal OAuth (requires: hermes auth add nous)\n # \"nous-api\" - Nous Portal API key (requires: NOUS_API_KEY)\n # \"anthropic\" - Direct Anthropic API (requires: ANTHROPIC_API_KEY)\n # \"openai-codex\" - OpenAI Codex (requires: hermes auth)\n # \"copilot\" - GitHub Copilot / GitHub Models (requires: GITHUB_TOKEN)\n # \"gemini\" - Use Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n # \"zai\" - Use z.ai / ZhipuAI GLM models (requires: GLM_API_KEY)\n # \"kimi-coding\" - Kimi / Moonshot AI (requires: KIMI_API_KEY)\n # \"minimax\" - MiniMax global (requires: MINIMAX_API_KEY)\n # \"minimax-cn\" - MiniMax China (requires: MINIMAX_CN_API_KEY)\n # \"huggingface\" - Hugging Face Inference (requires: HF_TOKEN)\n # \"nvidia\" - NVIDIA NIM / build.nvidia.com (requires: NVIDIA_API_KEY)\n # \"xiaomi\" - Xiaomi MiMo (requires: XIAOMI_API_KEY)\n # \"arcee\" - Arcee AI Trinity models (requires: ARCEEAI_API_KEY)\n # \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY — https://ollama.com/settings)\n # \"kilocode\" - KiloCode gateway (requires: KILOCODE_API_KEY)\n # \"azure-foundry\" - Microsoft Foundry / Azure OpenAI (API key or Entra ID)\n # \"lmstudio\" - LM Studio local server (optional: LM_API_KEY, defaults to http://127.0.0.1:1234/v1)\n #\n # Local servers (LM Studio, Ollama, vLLM, llama.cpp):\n # \"custom\" - Any other OpenAI-compatible endpoint. Set base_url below.\n # Aliases: \"ollama\", \"vllm\", \"llamacpp\" all map to \"custom\".\n # LM Studio is first-class and uses provider: \"lmstudio\".\n # It works with both no-auth and auth-enabled server modes.\n #\n # Can also be overridden for a single invocation with the --provider flag.\n provider: \"auto\"\n \n # API configuration (falls back to OPENROUTER_API_KEY env var)\n # api_key: \"your-key-here\" # Uncomment to set here instead of .env\n base_url: \"https://openrouter.ai/api/v1\"\n\n # Azure Foundry keyless auth example:\n # provider: \"azure-foundry\"\n # base_url: \"https://.openai.azure.com/openai/v1\"\n # auth_mode: \"entra_id\" # DefaultAzureCredential: az login, managed identity, workload identity, etc.\n # default: \"gpt-4o\" # Deployment/model name\n # entra:\n # scope: \"https://ai.azure.com/.default\" # Optional; this is the default.\n\n # ── Token limits — two settings, easy to confuse ──────────────────────────\n #\n # context_length: TOTAL context window (input + output tokens combined).\n # Controls when Hermes compresses history and validates requests.\n # Leave unset — Hermes auto-detects the correct value from the provider.\n # Set manually only when auto-detection is wrong (e.g. a local server with\n # a custom num_ctx, or a proxy that doesn't expose /v1/models).\n #\n # context_length: 131072\n #\n # max_tokens: OUTPUT cap — maximum tokens the model may generate per response.\n # Unrelated to how long your conversation history can be.\n # The OpenAI-standard name \"max_tokens\" is a misnomer; Anthropic's native\n # API has since renamed it \"max_output_tokens\" for clarity.\n # Leave unset to use the model's native output ceiling (recommended).\n # Set only if you want to deliberately limit individual response length.\n #\n# max_tokens: 8192\n\n # ── Custom request headers (optional) ─────────────────────────────────────\n #\n # default_headers: extra HTTP headers sent on every request to an\n # OpenAI-compatible endpoint. User values take precedence over the\n # provider/SDK defaults, so this is the supported way to override the\n # OpenAI Python SDK's identifying headers (User-Agent: OpenAI/Python ...,\n # X-Stainless-*) when a custom provider sits behind a gateway/WAF that\n # rejects them — e.g. an upstream that returns \"502 Upstream access\n # forbidden\" for the SDK default User-Agent but accepts a plain one.\n # Applies on the OpenAI wire only (not native Anthropic / Bedrock).\n #\n # default_headers:\n # User-Agent: \"curl/8.7.1\"\n\n# Named provider overrides (optional)\n# Use this for per-provider request timeouts, non-stream stale timeouts,\n# and per-model exceptions.\n# Applies to the primary turn client on every api_mode (OpenAI-wire, native\n# Anthropic, and Anthropic-compatible providers), the fallback chain, and\n# client rebuilds during credential rotation. For OpenAI-wire chat\n# completions (streaming and non-streaming) the configured value is also\n# used as the per-request ``timeout=`` kwarg so it wins over the legacy\n# HERMES_API_TIMEOUT env var (which still applies when no config is set).\n# ``stale_timeout_seconds`` controls the non-streaming stale-call detector and\n# wins over the legacy HERMES_API_CALL_STALE_TIMEOUT env var. Leaving these\n# unset keeps the legacy defaults (HERMES_API_TIMEOUT=1800s,\n# HERMES_API_CALL_STALE_TIMEOUT=90s, native Anthropic 900s). The\n# implicit non-stream stale detector is auto-disabled for local endpoints\n# and can scale upward for very large contexts.\n#\n# Not currently wired for AWS Bedrock (bedrock_converse + AnthropicBedrock\n# SDK paths) — those use boto3 with its own timeout configuration.\n#\n# providers:\n# ollama-local:\n# request_timeout_seconds: 300 # Longer timeout for local cold-starts\n# stale_timeout_seconds: 900 # Explicitly re-enable stale detection on local endpoints\n# anthropic:\n# request_timeout_seconds: 30 # Fast-fail cloud requests\n# models:\n# claude-opus-4.6:\n# timeout_seconds: 600 # Longer timeout for extended-thinking Opus calls\n# openai-codex:\n# models:\n# gpt-5.4:\n# stale_timeout_seconds: 1800 # Longer non-stream stale timeout for slow large-context turns\n\n# =============================================================================\n# OpenRouter Provider Routing (only applies when using OpenRouter)\n# =============================================================================\n# Control how requests are routed across providers on OpenRouter.\n# See: https://openrouter.ai/docs/guides/routing/provider-selection\n#\n# provider_routing:\n# # Sort strategy: \"price\" (default), \"throughput\", or \"latency\"\n# # Append :nitro to model name for a shortcut to throughput sorting.\n# sort: \"throughput\"\n#\n# # Only allow these providers (provider slugs from OpenRouter)\n# # only: [\"anthropic\", \"google\"]\n#\n# # Skip these providers entirely\n# # ignore: [\"deepinfra\", \"fireworks\"]\n#\n# # Try providers in this order (overrides default load balancing)\n# # order: [\"anthropic\", \"google\", \"together\"]\n#\n# # Require providers to support all parameters in your request\n# # require_parameters: true\n#\n# # Data policy: \"allow\" (default) or \"deny\" to exclude providers that may store data\n# # data_collection: \"deny\"\n\n# =============================================================================\n# OpenRouter Response Caching (only applies when using OpenRouter)\n# =============================================================================\n# Cache identical API responses at the OpenRouter edge for free instant replays.\n# When enabled, identical requests (same model, messages, parameters) return\n# cached responses with zero billing. Separate from Anthropic prompt caching.\n# See: https://openrouter.ai/docs/guides/features/response-caching\n#\n# openrouter:\n# response_cache: true # Enable response caching (default: true)\n# response_cache_ttl: 300 # Cache TTL in seconds, 1-86400 (default: 300)\n\n# =============================================================================\n# Git Worktree Isolation\n# =============================================================================\n# When enabled, each CLI session creates an isolated git worktree so multiple\n# agents can work on the same repo concurrently without file collisions.\n# Equivalent to always passing --worktree / -w on the command line.\n#\n# worktree: true # Always create a worktree when in a git repo\n# worktree: false # Default — only create when -w flag is passed\n#\n# By default a new worktree branches from the freshly-fetched remote tip\n# (the current branch's upstream, else the remote's default branch) so it\n# starts current with the project instead of from the local clone's\n# (possibly stale) HEAD. Set worktree_sync: false to branch from local HEAD\n# instead — useful when offline or when you deliberately want the clone's\n# exact current state as the base.\n#\n# worktree_sync: true # Default — branch from the fetched remote tip\n# worktree_sync: false # Branch from local HEAD (offline / pinned base)\n\n# =============================================================================\n# Terminal Tool Configuration\n# =============================================================================\n# Choose ONE of the following terminal configurations by uncommenting it.\n# The terminal tool executes commands in the specified environment.\n\n# -----------------------------------------------------------------------------\n# OPTION 1: Local execution (default)\n# Commands run directly on your machine in the current directory\n# -----------------------------------------------------------------------------\n# Working directory behavior:\n# - CLI (`hermes` command): Uses \".\" (current directory where you run hermes)\n# - Gateway/messaging/cron: Uses terminal.cwd here; legacy .env cwd values are deprecated\nterminal:\n backend: \"local\"\n cwd: \".\" # For local backend: \".\" = current directory. Ignored for remote backends unless a backend documents otherwise.\n timeout: 180\n # HOME policy for tool subprocesses:\n # auto - default: host uses your real HOME; containers use HERMES_HOME/home\n # real - force your real OS-user HOME\n # profile - force HERMES_HOME/home for strict per-profile CLI config isolation\n home_mode: \"auto\"\n docker_mount_cwd_to_workspace: false # SECURITY: off by default. Opt in to mount the launch cwd into Docker /workspace.\n lifetime_seconds: 300\n # sudo_password: \"hunter2\" # Optional: pipe a sudo password via sudo -S. SECURITY WARNING: plaintext.\n # sudo_password: \"\" # Explicit empty password: try empty and never open the interactive sudo prompt.\n\n# -----------------------------------------------------------------------------\n# OPTION 2: SSH remote execution\n# Commands run on a remote server - agent code stays local (sandboxed)\n# Great for: keeping agent isolated from its own code, using powerful remote hardware\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"ssh\"\n# cwd: \"/home/myuser/project\" # Path on the REMOTE server\n# timeout: 180\n# lifetime_seconds: 300\n# ssh_host: \"my-server.example.com\"\n# ssh_user: \"myuser\"\n# ssh_port: 22\n# ssh_key: \"~/.ssh/id_rsa\" # Optional - uses ssh-agent if not specified\n\n# -----------------------------------------------------------------------------\n# OPTION 3: Docker container\n# Commands run in an isolated Docker container\n# Great for: reproducible environments, testing, isolation\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"docker\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /)\n# timeout: 180\n# lifetime_seconds: 300\n# docker_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# docker_mount_cwd_to_workspace: true # Explicit opt-in: mount your launch cwd into /workspace\n# # Optional: run the container as your host user's uid:gid so files written\n# # into bind-mounted dirs are owned by you, not root. Drops SETUID/SETGID\n# # caps too since no gosu privilege drop is needed. Leave off if your\n# # chosen docker_image expects to start as root.\n# docker_run_as_host_user: true\n# # Optional: explicitly forward selected env vars into Docker.\n# # These values come from your current shell first, then ~/.hermes/.env.\n# # Warning: anything forwarded here is visible to commands run in the container.\n# docker_forward_env:\n# - \"GITHUB_TOKEN\"\n# - \"NPM_TOKEN\"\n# # Optional: extra flags passed verbatim to docker run (appended after security defaults).\n# # Useful for adding capabilities (e.g. apt installs needing SETUID) or custom options.\n# # Example: add a Linux capability not included by default\n# # docker_extra_args:\n# # - \"--cap-add\"\n# # - \"SETUID\"\n\n# -----------------------------------------------------------------------------\n# OPTION 4: Singularity/Apptainer container\n# Commands run in a Singularity container (common in HPC environments)\n# Great for: HPC clusters, shared compute environments\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"singularity\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# singularity_image: \"docker://nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 5: Modal cloud execution\n# Commands run on Modal's cloud infrastructure\n# Great for: GPU access, scalable compute, serverless execution\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"modal\"\n# cwd: \"/workspace\" # Path INSIDE the sandbox (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# modal_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 6: Daytona cloud execution\n# Commands run in Daytona cloud sandboxes\n# Great for: Cloud dev environments, persistent workspaces, team collaboration\n# Requires: pip install daytona, DAYTONA_API_KEY env var\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"daytona\"\n# cwd: \"~\"\n# timeout: 180\n# lifetime_seconds: 300\n# daytona_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# container_disk: 10240 # Daytona max is 10GB per sandbox\n\n#\n# --- Container resource limits (docker, singularity, modal, daytona -- ignored for local/ssh) ---\n# These settings apply to all container backends. They control the resources\n# allocated to the sandbox and whether its filesystem persists across sessions.\n container_cpu: 1 # CPU cores\n container_memory: 5120 # Memory in MB (5120 = 5GB)\n container_disk: 51200 # Disk in MB (51200 = 50GB)\n container_persistent: true # Persist filesystem across sessions (false = ephemeral)\n\n# -----------------------------------------------------------------------------\n# SUDO SUPPORT (works with ALL backends above)\n# -----------------------------------------------------------------------------\n# Add sudo_password to any terminal config above to enable sudo commands.\n# The password is piped via `sudo -S`. Works with local, ssh, docker, etc.\n#\n# SECURITY WARNING: Password stored in plaintext!\n#\n# INTERACTIVE PROMPT: If sudo_password is unset and the CLI is running,\n# you'll be prompted to enter your password when sudo is needed:\n# - 45-second timeout (auto-skips if no input)\n# - Press Enter to skip (command fails gracefully)\n# - Password is hidden while typing\n# - Password is cached for the session\n#\n# EMPTY PASSWORDS: Setting sudo_password to an explicit empty string is different\n# from leaving it unset. Hermes will try an empty password via `sudo -S` and\n# will not open the interactive prompt. This is useful for passwordless sudo,\n# Touch ID sudo setups, and environments where prompting is just noise.\n#\n# ALTERNATIVES:\n# - SSH backend: Configure passwordless sudo on the remote server\n# - Containers: Run as root inside the container (no sudo needed)\n# - Local: Configure /etc/sudoers for specific commands\n#\n# Example (add to your terminal section):\n# sudo_password: \"your-password-here\"\n\n# =============================================================================\n# Security Scanning (tirith)\n# =============================================================================\n# Optional pre-exec command security scanning via tirith.\n# Detects homograph URLs, pipe-to-shell, terminal injection, env manipulation.\n# Install: brew install sheeki03/tap/tirith\n# Docs: https://github.com/sheeki03/tirith\n#\n# security:\n# tirith_enabled: true # Enable/disable tirith scanning\n# tirith_path: \"tirith\" # Path to tirith binary (supports ~ expansion)\n# tirith_timeout: 5 # Scan timeout in seconds\n# tirith_fail_open: true # Allow commands if tirith unavailable\n\n# =============================================================================\n# Browser Tool Configuration\n# =============================================================================\nbrowser:\n # Inactivity timeout in seconds - browser sessions are automatically closed\n # after this period of no activity between agent loops (default: 120 = 2 minutes)\n inactivity_timeout: 120\n\n# =============================================================================\n# Tool Loop Guardrails\n# =============================================================================\n# Soft warnings are enabled by default. They append guidance to repeated failed\n# or non-progressing tool results but still let the tool execute. Hard stops are\n# opt-in circuit breakers for autonomous/cron sessions where stopping a loop is\n# preferable to spending the full iteration budget.\ntool_loop_guardrails:\n warnings_enabled: true\n hard_stop_enabled: false\n warn_after:\n exact_failure: 2\n same_tool_failure: 3\n idempotent_no_progress: 2\n hard_stop_after:\n exact_failure: 5\n same_tool_failure: 8\n idempotent_no_progress: 5\n\n# =============================================================================\n# Context Compression (Auto-shrinks long conversations)\n# =============================================================================\n# When conversation approaches model's context limit, middle turns are\n# automatically summarized to free up space while preserving important context.\n#\n# HOW IT WORKS:\n# 1. Tracks actual token usage from API responses (not estimates)\n# 2. When prompt_tokens >= threshold% of model's context_length, triggers compression\n# 3. Protects first 3 turns (system prompt, initial request, first response)\n# 4. Protects last N turns (default 20 messages = ~10 full turns of recent context)\n# 5. Summarizes middle turns using a fast/cheap model\n# 6. Inserts summary as a user message, continues conversation seamlessly\n#\n# Post-compression tail budget is target_ratio × threshold × context_length:\n# 200K context, threshold 0.50, ratio 0.20 → 20K tokens of recent tail preserved\n# 1M context, threshold 0.50, ratio 0.20 → 100K tokens of recent tail preserved\n#\ncompression:\n # Enable automatic context compression (default: true)\n # Set to false if you prefer to manage context manually or want errors on overflow\n enabled: true\n \n # Trigger compression at this % of model's context limit (default: 0.50 = 50%)\n # Lower values = more aggressive compression, higher values = compress later\n threshold: 0.50\n \n # Fraction of the threshold to preserve as recent tail (default: 0.20 = 20%)\n # e.g. 20% of 50% threshold = 10% of total context kept as recent messages.\n # Summary output is separately capped at 12K tokens (Gemini output limit).\n # Range: 0.10 - 0.80\n target_ratio: 0.20\n\n # Number of most-recent messages to always preserve (default: 20 ≈ 10 full turns)\n # Higher values keep more recent conversation intact at the cost of more aggressive\n # compression of older turns.\n protect_last_n: 20\n\n # Number of non-system messages to protect at the head of the transcript, in\n # ADDITION to the system prompt (which is always implicitly protected).\n # Head messages are NEVER summarized — they survive every compression\n # indefinitely. This gives stable early context for short/medium sessions,\n # but in long-running sessions that rely on rolling compaction the pinned\n # opening turns may not match how you want the session framed over time.\n # Set to 0 to preserve ONLY the system prompt (plus the rolling summary\n # and recent tail) — the cleanest configuration for long-running sessions.\n # Default 3 preserves the system prompt plus the first three non-system\n # head messages, matching the pre-feature behaviour.\n protect_first_n: 3\n\n # To pin a specific model/provider for compression summaries, use the\n # auxiliary section below (auxiliary.compression.provider / model).\n\n# =============================================================================\n# Anthropic prompt caching TTL\n# =============================================================================\n# When prompt caching is active (Claude via OpenRouter or native Anthropic),\n# Anthropic supports two TTL tiers for cached prefixes: \"5m\" (default) and\n# \"1h\". Other values are ignored and \"5m\" is used.\n#\nprompt_caching:\n cache_ttl: \"5m\" # use \"1h\" for long sessions with pauses between turns\n\n# =============================================================================\n# Auxiliary Models (Advanced — Experimental)\n# =============================================================================\n# Hermes uses lightweight \"auxiliary\" models for side tasks: image analysis,\n# browser screenshot analysis, web page summarization, TTS audio-tag insertion,\n# and context compression.\n#\n# By default these use Gemini Flash via OpenRouter or Nous Portal and are\n# auto-detected from your credentials. You do NOT need to change anything\n# here for normal usage.\n#\n# WARNING: Overriding these with providers other than OpenRouter or Nous Portal\n# is EXPERIMENTAL and may not work. Not all models/providers support vision,\n# produce usable summaries, or accept the same API format. Change at your own\n# risk — if things break, reset to \"auto\" / empty values.\n#\n# Each task has its own provider + model pair so you can mix providers.\n# For example: OpenRouter for vision (needs multimodal), but your main\n# local endpoint for compression (just needs text).\n#\n# Provider options:\n# \"auto\" - Best available: OpenRouter → Nous Portal → main endpoint (default)\n# \"openrouter\" - Force OpenRouter (requires OPENROUTER_API_KEY)\n# \"nous\" - Force Nous Portal (requires: hermes auth add nous)\n# \"gemini\" - Force Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n# \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY)\n# \"codex\" - Force Codex OAuth (requires: hermes model → Codex).\n# Uses gpt-5.3-codex which supports vision.\n# \"main\" - Use your custom endpoint (OPENAI_BASE_URL + OPENAI_API_KEY).\n# Works with OpenAI API, local models, or any OpenAI-compatible\n# endpoint. Also falls back to Codex OAuth and API-key providers.\n#\n# Model: leave empty to use the provider's default. When empty, OpenRouter\n# uses \"google/gemini-3-flash-preview\" and Nous uses \"gemini-3-flash\".\n# Other providers pick a sensible default automatically.\n#\n# auxiliary:\n# # Image analysis: vision_analyze tool + browser screenshots\n# vision:\n# provider: \"auto\"\n# model: \"\" # e.g. \"google/gemini-2.5-flash\", \"openai/gpt-4o\"\n# timeout: 30 # LLM API call timeout (seconds)\n# download_timeout: 30 # Image HTTP download timeout (seconds)\n# # Increase for slow connections or self-hosted image servers\n#\n# # Web page scraping / summarization + browser page text extraction\n# web_extract:\n# provider: \"auto\"\n# model: \"\"\n#\n# # Gemini 3.1 TTS hidden audio-tag insertion\n# tts_audio_tags:\n# provider: \"auto\" # empty model = your main chat model\n# model: \"\"\n# timeout: 30\n#\n# # Session search — summarizes matching past sessions\n# session_search:\n# provider: \"auto\"\n# model: \"\"\n# timeout: 30\n# max_concurrency: 3 # Limit parallel summaries to reduce request-burst 429s\n# extra_body: {} # Provider-specific OpenAI-compatible request fields\n# # Example for providers that support request-body\n# # reasoning controls:\n# # extra_body:\n# # enable_thinking: false\n# # Some vLLM/Qwen deployments expect this nested:\n# # extra_body:\n# # chat_template_kwargs:\n# # enable_thinking: false\n\n# =============================================================================\n# Persistent Memory\n# =============================================================================\n# Bounded curated memory injected into the system prompt every session.\n# Two stores: MEMORY.md (agent's notes) and USER.md (user profile).\n# Character limits keep the memory small and focused. The agent manages\n# pruning -- when at the limit, it must consolidate or replace entries.\n# Disabled by default in batch_runner.\n#\nmemory:\n # Agent's personal notes: environment facts, conventions, things learned\n memory_enabled: true\n \n # User profile: preferences, communication style, expectations\n user_profile_enabled: true\n \n # Character limits (~2.75 chars per token, model-independent)\n memory_char_limit: 2200 # ~800 tokens\n user_char_limit: 1375 # ~500 tokens\n\n # Periodic memory nudge: remind the agent to consider saving memories\n # every N user turns. Set to 0 to disable. Only active when memory is enabled.\n nudge_interval: 10 # Nudge every 10 user turns (0 = disabled)\n\n # Memory flush: give the agent one turn to save memories before context is\n # lost (compression, /new, /reset, exit). Set to 0 to disable.\n # For exit/reset, only fires if the session had at least this many user turns.\n flush_min_turns: 6 # Min user turns to trigger flush on exit/reset (0 = disabled)\n\n# =============================================================================\n# Session Reset Policy (Messaging Platforms)\n# =============================================================================\n# Controls when messaging sessions (Telegram, Discord, WhatsApp, Slack) are\n# automatically cleared. Without resets, conversation context grows indefinitely\n# which increases API costs with every message.\n#\n# When a reset triggers, the agent first saves important information to its\n# persistent memory — but the conversation context is wiped. The agent starts\n# fresh but retains learned facts via its memory system.\n#\n# Users can always manually reset with /reset or /new in chat.\n#\n# Modes:\n# \"both\" - Reset on EITHER inactivity timeout or daily boundary (recommended)\n# \"idle\" - Reset only after N minutes of inactivity\n# \"daily\" - Reset only at a fixed hour each day\n# \"none\" - Never auto-reset; context lives until /reset or compression kicks in\n#\n# When a reset triggers, the agent gets one turn to save important memories and\n# skills before the context is wiped. Persistent memory carries across sessions.\n#\nsession_reset:\n mode: both # \"both\", \"idle\", \"daily\", or \"none\"\n idle_minutes: 1440 # Inactivity timeout in minutes (default: 1440 = 24 hours)\n at_hour: 4 # Daily reset hour, 0-23 local time (default: 4 AM)\n\n# Maximum number of simultaneously active chat sessions across CLI, TUI,\n# dashboard chat, and messaging gateway. Set to null, 0, or omit to allow\n# unlimited concurrent sessions. When the limit is reached, new sessions get a\n# clean error while existing active sessions keep their normal behavior. This\n# top-level key takes precedence over gateway.max_concurrent_sessions. The cap\n# is a best-effort single-host/profile runtime guard; Hermes fails open if the\n# local runtime lease registry cannot be read or locked.\nmax_concurrent_sessions: null\n\n# When true, group/channel chats use one session per participant when the platform\n# provides a user ID. This is the secure default and prevents users in the same\n# room from sharing context, interrupts, and token costs. Set false only if you\n# explicitly want one shared \"room brain\" per group/channel.\ngroup_sessions_per_user: true\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Gateway Streaming\n# ─────────────────────────────────────────────────────────────────────────────\n# Stream tokens to messaging platforms in real-time. The bot sends a message\n# on first token, then progressively edits it as more tokens arrive.\n# Disabled by default — enable to try the streaming UX on Telegram/Discord/Slack.\n# For Telegram, partial edits are sent as plain text and only the final edit uses MarkdownV2.\nstreaming:\n enabled: false\n # transport: edit # \"edit\" = progressive editMessageText\n # edit_interval: 0.3 # seconds between message edits\n # buffer_threshold: 40 # chars before forcing an edit flush\n # cursor: \" ▉\" # cursor shown during streaming\n\n# =============================================================================\n# Skills Configuration\n# =============================================================================\n# Skills are reusable procedures the agent can load and follow. The agent can\n# also create new skills after completing complex tasks.\n#\nskills:\n # Nudge the agent to create skills after complex tasks.\n # Every N tool-calling iterations, remind the model to consider saving a skill.\n # Set to 0 to disable.\n creation_nudge_interval: 15\n\n # External skill directories — share skills across tools/agents without\n # copying them into ~/.hermes/skills/. Each path is expanded (~ and ${VAR})\n # and resolved to an absolute path. External dirs are read-only: skill\n # creation always writes to ~/.hermes/skills/. Local skills take precedence\n # when names collide.\n # external_dirs:\n # - ~/.agents/skills\n # - /home/shared/team-skills\n\n# =============================================================================\n# Agent Behavior\n# =============================================================================\nagent:\n # Maximum tool-calling iterations per conversation\n # Higher = more room for complex tasks, but costs more tokens\n # Recommended: 20-30 for focused tasks, 50-100 for open exploration\n max_turns: 60\n\n # Inactivity timeout for gateway agent runs (seconds, 0 = unlimited).\n # The agent can run indefinitely when actively calling tools or receiving\n # API responses. Only fires after the agent has been idle for this duration.\n # gateway_timeout: 1800\n\n # Staged warning: send a warning before escalating to full timeout.\n # Fires once per run when inactivity reaches this threshold (seconds).\n # Set to 0 to disable the warning.\n # gateway_timeout_warning: 900\n\n # Graceful drain timeout for gateway stop/restart (seconds).\n # Default 0 = no drain: a restart interrupts in-flight agents immediately,\n # cleans up, and exits. Set a positive value only if you want a grace\n # window on /restart, and keep it well under systemd's TimeoutStopSec.\n # restart_drain_timeout: 0\n\n # Max app-level retry attempts for API errors (connection drops, provider\n # timeouts, 5xx, etc.) before the agent surfaces the failure. Lower this\n # to 1 if you use fallback providers and want fast failover on flaky\n # primaries (default 3). The OpenAI SDK does its own low-level retries\n # underneath this wrapper — this is the Hermes-level loop.\n # api_max_retries: 3\n\n # After the agent edits code without fresh passing verification, nudge it to\n # verify before finishing. The default \"auto\" enables it on interactive\n # coding surfaces (CLI, TUI, desktop) and programmatic callers, and disables\n # it on conversational messaging surfaces (Telegram, Discord, etc.) where the\n # verification summary would reach a human as chat noise. Set true or false to\n # force it on or off; the HERMES_VERIFY_ON_STOP env var (1/0) takes precedence.\n # verify_on_stop: auto\n\n # Standing operator instructions for the coding posture (when Hermes is in a\n # code workspace). Appended to the coding brief as an extra system block, so\n # you can pin project-wide workflow rules without editing the shipped brief.\n # Accepts a string or a list of strings. Takes effect next session.\n # coding_instructions:\n # - \"For UI work, don't run tsc/lint until I approve the look.\"\n # - \"Clean the diff before you commit and push.\"\n\n # When verify-on-stop finds edited code without fresh verification evidence,\n # append guidance for creative UI work (avoid broad tsc/lint/test before visual\n # approval) and clean-diff expectations. Set false to keep that nudge terse.\n # verify_guidance: true\n\n # A `pre_verify` hook (plugin or shell, see Event Hooks docs) can keep the\n # agent going one more turn to verify/clean before finishing. This caps how\n # many times one turn may be nudged to continue, so a hook can't trap the loop.\n # Default 3.\n # max_verify_nudges: 3\n\n # Enable verbose logging\n verbose: false\n \n # Reasoning effort level (OpenRouter and Nous Portal)\n # Controls how much \"thinking\" the model does before responding.\n # Options: \"xhigh\" (max), \"high\", \"medium\", \"low\", \"minimal\", \"none\" (disable)\n reasoning_effort: \"medium\"\n \n # Predefined personalities (use with /personality command)\n personalities:\n helpful: \"You are a helpful, friendly AI assistant.\"\n concise: \"You are a concise assistant. Keep responses brief and to the point.\"\n technical: \"You are a technical expert. Provide detailed, accurate technical information.\"\n creative: \"You are a creative assistant. Think outside the box and offer innovative solutions.\"\n teacher: \"You are a patient teacher. Explain concepts clearly with examples.\"\n kawaii: \"You are a kawaii assistant! Use cute expressions like (◕‿◕), ★, ♪, and ~! Add sparkles and be super enthusiastic about everything! Every response should feel warm and adorable desu~! ヽ(>∀<☆)ノ\"\n catgirl: \"You are Neko-chan, an anime catgirl AI assistant, nya~! Add 'nya' and cat-like expressions to your speech. Use kaomoji like (=^・ω・^=) and ฅ^•ﻌ•^ฅ. Be playful and curious like a cat, nya~!\"\n pirate: \"Arrr! Ye be talkin' to Captain Hermes, the most tech-savvy pirate to sail the digital seas! Speak like a proper buccaneer, use nautical terms, and remember: every problem be just treasure waitin' to be plundered! Yo ho ho!\"\n shakespeare: \"Hark! Thou speakest with an assistant most versed in the bardic arts. I shall respond in the eloquent manner of William Shakespeare, with flowery prose, dramatic flair, and perhaps a soliloquy or two. What light through yonder terminal breaks?\"\n surfer: \"Duuude! You're chatting with the chillest AI on the web, bro! Everything's gonna be totally rad. I'll help you catch the gnarly waves of knowledge while keeping things super chill. Cowabunga! 🤙\"\n noir: \"The rain hammered against the terminal like regrets on a guilty conscience. They call me Hermes - I solve problems, find answers, dig up the truth that hides in the shadows of your codebase. In this city of silicon and secrets, everyone's got something to hide. What's your story, pal?\"\n uwu: \"hewwo! i'm your fwiendwy assistant uwu~ i wiww twy my best to hewp you! *nuzzles your code* OwO what's this? wet me take a wook! i pwomise to be vewy hewpful >w<\"\n philosopher: \"Greetings, seeker of wisdom. I am an assistant who contemplates the deeper meaning behind every query. Let us examine not just the 'how' but the 'why' of your questions. Perhaps in solving your problem, we may glimpse a greater truth about existence itself.\"\n hype: \"YOOO LET'S GOOOO!!! 🔥🔥🔥 I am SO PUMPED to help you today! Every question is AMAZING and we're gonna CRUSH IT together! This is gonna be LEGENDARY! ARE YOU READY?! LET'S DO THIS! 💪😤🚀\"\n\n# =============================================================================\n# Toolsets\n# =============================================================================\n# Control which tools the agent has access to.\n# Use `hermes tools` to interactively enable/disable tools per platform.\n\n# =============================================================================\n# Platform Toolsets (per-platform tool configuration)\n# =============================================================================\n# Override which toolsets are available on each platform.\n# If a platform isn't listed here, its built-in default is used.\n#\n# You can use EITHER:\n# - A preset like \"hermes-cli\" or \"hermes-telegram\" (curated tool set)\n# - A list of individual toolsets to compose your own (see list below)\n#\n# Supported platform keys: cli, telegram, discord, whatsapp, slack, qqbot, teams, google_chat\n#\n# Examples:\n#\n# # Use presets (same as defaults):\n# platform_toolsets:\n# cli: [hermes-cli]\n# telegram: [hermes-telegram]\n#\n# # Custom: give Telegram only web + terminal + file + planning:\n# platform_toolsets:\n# telegram: [web, terminal, file, todo]\n#\n# # Custom: CLI without browser or image gen:\n# platform_toolsets:\n# cli: [web, terminal, file, skills, todo, tts, cronjob]\n#\n# # Restrictive: Discord gets read-only tools only:\n# platform_toolsets:\n# discord: [web, vision, skills, todo]\n#\n# If not set, defaults are:\n# cli: hermes-cli (everything + cronjob management)\n# telegram: hermes-telegram (terminal, file, web, vision, image, tts, browser, skills, todo, cronjob, messaging)\n# discord: hermes-discord (same as telegram)\n# whatsapp: hermes-whatsapp (same as telegram)\n# slack: hermes-slack (same as telegram)\n# signal: hermes-signal (same as telegram)\n# homeassistant: hermes-homeassistant (same as telegram)\n# qqbot: hermes-qqbot (same as telegram)\n# teams: hermes-teams (same as telegram)\n# google_chat: hermes-google_chat (same as telegram)\n#\nplatform_toolsets:\n cli: [hermes-cli]\n telegram: [hermes-telegram]\n discord: [hermes-discord]\n whatsapp: [hermes-whatsapp]\n slack: [hermes-slack]\n signal: [hermes-signal]\n homeassistant: [hermes-homeassistant]\n qqbot: [hermes-qqbot]\n yuanbao: [hermes-yuanbao]\n teams: [hermes-teams]\n google_chat: [hermes-google_chat]\n\n# =============================================================================\n# Gateway Platform Settings\n# =============================================================================\n# Optional per-platform messaging settings.\n# Platform-specific knobs live under `extra`.\n#\n# platforms:\n# telegram:\n# reply_to_mode: \"first\" # off | first | all\n# # guest_mode lets explicit @mentions from non-allowlisted groups through.\n# # Default false; ordinary messages, replies, and regex wake words stay blocked.\n# guest_mode: false\n# # allowed_chats: [\"-1001234567890\"]\n# extra:\n# disable_link_previews: false # Set true to suppress Telegram URL previews in bot messages\n# rich_messages: false # Bot API 10.1 rich messages (tables/task lists/details/math); default false for copyable legacy MarkdownV2, set true to opt in\n# rich_drafts: false # Experimental rich draft previews during Telegram DM streaming; default false because Telegram Desktop/macOS can visually overlay draft frames\n# command_menu:\n# # Telegram allows up to 100 BotCommands; Hermes defaults to 60 so\n# # all built-in commands plus common skill commands stay visible\n# # while remaining under Telegram's payload-size limit. Clamped 1..100.\n# max_commands: 60\n# # prepend = user priority first, then Hermes defaults\n# # append = Hermes defaults first, then user priority\n# # replace = only the list below defines priority\n# priority_mode: prepend\n# priority:\n# - my_plugin_command\n#\n# Discord-specific settings (config.yaml top-level, not under platforms:):\n#\n# discord:\n# require_mention: true # Require @mention in server channels (default: true)\n# auto_thread: true # Auto-create thread on @mention (default: true)\n# free_response_channels: \"\" # Channel IDs where no mention is needed\n# reactions: true # Show processing reactions (default: true)\n# history_backfill: true # Recover missed channel messages on mention (default: true)\n# history_backfill_limit: 50 # Max messages to scan backwards (default: 50)\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Available toolsets (use these names in platform_toolsets or the toolsets list)\n#\n# Run `hermes chat --list-toolsets` to see all toolsets and their tools.\n# Run `hermes chat --list-tools` to see every individual tool with descriptions.\n# ─────────────────────────────────────────────────────────────────────────────\n#\n# INDIVIDUAL TOOLSETS (compose your own):\n# web - web_search, web_extract\n# search - web_search only (no scraping)\n# terminal - terminal, process\n# file - read_file, write_file, patch, search\n# browser - browser_navigate, browser_snapshot, browser_click, browser_type,\n# browser_scroll, browser_back, browser_press,\n# browser_get_images, browser_vision (requires BROWSERBASE_API_KEY)\n# vision - vision_analyze (requires OPENROUTER_API_KEY)\n# image_gen - image_generate (requires FAL_KEY)\n# skills - skills_list, skill_view\n# skills_hub - skill_hub (search/install/manage from online registries — user-driven only)\n# todo - todo (in-memory task planning, no deps)\n# tts - text_to_speech (Edge TTS free, or ELEVENLABS/OPENAI/MINIMAX/MISTRAL key)\n# cronjob - cronjob (create/list/update/pause/resume/run/remove scheduled tasks)\n#\n# PRESETS (curated bundles):\n# hermes-cli - All of the above except send_message\n# hermes-telegram - terminal, file, web, vision, image_gen, tts, browser,\n# skills, todo, cronjob, send_message\n# hermes-discord - Same as hermes-telegram\n# hermes-whatsapp - Same as hermes-telegram\n# hermes-slack - Same as hermes-telegram\n#\n# COMPOSITE:\n# debugging - terminal + web + file\n# safe - web + vision (no terminal access)\n# all - Everything available\n#\n# web - Web search and content extraction (web_search, web_extract)\n# search - Web search only, no scraping (web_search)\n# terminal - Command execution and process management (terminal, process)\n# file - File operations: read, write, patch, search\n# browser - Full browser automation (navigate, click, type, screenshot, etc.)\n# vision - Image analysis (vision_analyze)\n# image_gen - Image generation with FLUX (image_generate)\n# skills - Load skill documents (skills_list, skill_view)\n# todo - Task planning and tracking for multi-step work\n# memory - Persistent memory across sessions (personal notes + user profile)\n# session_search - Search and recall past conversations (FTS5 + Gemini Flash summarization)\n# tts - Text-to-speech (Edge TTS free, ElevenLabs, OpenAI, MiniMax, Mistral)\n# cronjob - Schedule and manage automated tasks (CLI-only)\n#\n# Composite toolsets:\n# debugging - terminal + web + file (for troubleshooting)\n# safe - web + vision (no terminal access)\n\n# NOTE: The top-level \"toolsets\" key is deprecated and ignored.\n# Tool configuration is managed per-platform via platform_toolsets above.\n# Use `hermes tools` to configure interactively, or edit platform_toolsets directly.\n#\n# CLI override: hermes chat --toolsets terminal,web,file\n\n# =============================================================================\n# MCP (Model Context Protocol) Servers\n# =============================================================================\n# Connect to external MCP servers to add tools from the MCP ecosystem.\n# Each server's tools are automatically discovered and registered.\n# See website/docs/user-guide/features/mcp.md for full documentation.\n#\n# Stdio servers (spawn a subprocess):\n# command: the executable to run\n# args: command-line arguments\n# env: environment variables (only these + safe defaults passed to subprocess)\n#\n# HTTP servers (connect to a URL):\n# url: the MCP server endpoint\n# headers: HTTP headers (e.g., for authentication)\n#\n# Optional per-server settings:\n# timeout: tool call timeout in seconds (default: 120)\n# connect_timeout: initial connection timeout (default: 60)\n# keepalive_interval: liveness ping cadence in seconds (default: 180).\n# Lower it below the server's session TTL for servers that expire idle\n# sessions quickly (e.g. Unreal Engine editor MCP, ~15s), otherwise idle\n# tool calls hit an expired session and pay a slow reconnect. Floored at 5s.\n#\n# mcp_servers:\n# time:\n# command: uvx\n# args: [\"mcp-server-time\"]\n# filesystem:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-filesystem\", \"/home/user\"]\n# notion:\n# url: https://mcp.notion.com/mcp\n# github:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-github\"]\n# env:\n# GITHUB_PERSONAL_ACCESS_TOKEN: \"ghp_...\"\n#\n# Sampling (server-initiated LLM requests) — enabled by default.\n# Per-server config under the 'sampling' key:\n# analysis:\n# command: npx\n# args: [\"-y\", \"analysis-server\"]\n# sampling:\n# enabled: true # default: true\n# model: \"gemini-3-flash\" # override model (optional)\n# max_tokens_cap: 4096 # max tokens per request\n# timeout: 30 # LLM call timeout (seconds)\n# max_rpm: 10 # max requests per minute\n# allowed_models: [] # model whitelist (empty = all)\n# max_tool_rounds: 5 # tool loop limit (0 = disable)\n# log_level: \"info\" # audit verbosity\n\n# =============================================================================\n# Text-to-Speech\n# =============================================================================\n# TTS defaults to Edge TTS unless changed in ~/.hermes/config.yaml.\n# Gemini TTS supports persona/director prompt files, and Gemini 3.1 Flash TTS\n# can use a hidden auxiliary rewrite pass to insert expressive square-bracket\n# audio tags into the TTS script without showing tags in chat.\n#\n# tts:\n# provider: \"gemini\"\n# gemini:\n# model: \"gemini-3.1-flash-tts-preview\"\n# voice: \"Kore\"\n# audio_tags: false\n# persona_prompt_file: \"\" # e.g. ~/.hermes/tts/radio-host.md\n\n# =============================================================================\n# Voice Transcription (Speech-to-Text)\n# =============================================================================\n# Automatically transcribe voice messages on messaging platforms.\n# Providers: local (free, faster-whisper) | groq (free tier) | openai (Whisper API) | mistral (Voxtral Transcribe)\n# Set the corresponding API key in .env: GROQ_API_KEY, OPENAI_API_KEY, or MISTRAL_API_KEY.\nstt:\n enabled: true\n # provider: \"local\" # auto-detected if omitted\n local:\n model: \"base\" # tiny | base | small | medium | large-v3 | turbo\n # language: \"\" # auto-detect; set to \"en\", \"es\", \"fr\", etc. to force\n openai:\n model: \"whisper-1\" # whisper-1 | gpt-4o-mini-transcribe | gpt-4o-transcribe\n # mistral:\n # model: \"voxtral-mini-latest\" # voxtral-mini-latest | voxtral-mini-2602\n\n# =============================================================================\n# Response Pacing (Messaging Platforms)\n# =============================================================================\n# Add human-like delays between message chunks.\n# human_delay:\n# mode: \"off\" # \"off\" | \"natural\" | \"custom\"\n# min_ms: 800 # Min delay (custom mode only)\n# max_ms: 2500 # Max delay (custom mode only)\n\n# =============================================================================\n# Session Logging\n# =============================================================================\n# Session trajectories are automatically saved to logs/ directory.\n# Each session creates: logs/session_YYYYMMDD_HHMMSS_UUID.json\n#\n# The session ID is displayed in the welcome banner for easy reference.\n# Logs contain full conversation history in trajectory format:\n# - System prompt, user messages, assistant responses\n# - Tool calls with inputs/outputs\n# - Timestamps for debugging\n#\n# No configuration needed - logging is always enabled.\n# To disable, you would need to modify the source code.\n\n# =============================================================================\n# Code Execution Sandbox (Programmatic Tool Calling)\n# =============================================================================\n# The execute_code tool runs Python scripts that call Hermes tools via RPC.\n# Intermediate tool results stay out of the LLM's context window.\ncode_execution:\n timeout: 300 # Max seconds per script before kill (default: 300 = 5 min)\n max_tool_calls: 50 # Max RPC tool calls per execution (default: 50)\n\n# =============================================================================\n# Subagent Delegation\n# =============================================================================\n# The delegate_task tool spawns child agents with isolated context.\n# Supports single tasks and batch mode (default 3 parallel, configurable).\ndelegation:\n max_iterations: 50 # Max tool-calling turns per child (default: 50)\n # max_concurrent_children: 3 # Max parallel child agents per batch (default: 3, floor: 1, no ceiling).\n # WARNING: values above 10 multiply API cost linearly.\n # max_spawn_depth: 1 # Delegation tree depth cap (range: 1-3, default: 1 = flat).\n # Raise to 2 to allow workers to spawn their own subagents.\n # Requires role=\"orchestrator\" on intermediate agents.\n # orchestrator_enabled: true # Kill switch for role=\"orchestrator\" children (default: true).\n # subagent_auto_approve: false # When a subagent hits a dangerous-command approval prompt, auto-deny (default: false)\n # or auto-approve \"once\" (true) instead of blocking on stdin.\n # The parent TUI owns stdin, so blocking would deadlock; non-interactive resolution is required.\n # Both choices emit a logger.warning audit line. Flip to true only for cron/batch pipelines.\n # inherit_mcp_toolsets: true # When explicit child toolsets are narrowed, also keep the parent's MCP toolsets (default: true). Set false for strict intersection.\n # model: \"google/gemini-3-flash-preview\" # Override model for subagents (empty = inherit parent)\n # provider: \"openrouter\" # Override provider for subagents (empty = inherit parent)\n # # Resolves full credentials (base_url, api_key) automatically.\n # # Supported: openrouter, nous, zai, kimi-coding, minimax\n\n# =============================================================================\n# Honcho Integration (Cross-Session User Modeling)\n# =============================================================================\n# AI-native persistent memory via Honcho (https://honcho.dev/).\n# Builds a deeper understanding of the user across sessions and tools.\n# Runs alongside USER.md — additive, not a replacement.\n#\n# Requires: pip install honcho-ai\n# Config: ~/.honcho/config.json (shared with Claude Code, Cursor, etc.)\n# API key: HONCHO_API_KEY in ~/.hermes/.env or ~/.honcho/config.json\n#\n# Hermes-specific overrides (optional — most config comes from ~/.honcho/config.json):\n# honcho: {}\n\n# =============================================================================\n# Display\n# =============================================================================\ndisplay:\n # Use compact banner mode (hides the ASCII-art banner, shows a single line).\n # true: Compact single-line banner\n # false: Full ASCII banner with tool/skill summary (default)\n compact: false\n\n # Tool progress display level (CLI and gateway)\n # off: Silent — no tool activity shown, just the final response\n # new: Show a tool indicator only when the tool changes (skip repeats)\n # all: Show every tool call with a short preview (default)\n # verbose: Full args, results, and debug logs (same as /verbose)\n # Toggle at runtime with /verbose in the CLI\n tool_progress: all\n\n # Per-platform defaults can be quieter than the global setting. Telegram\n # tunes for mobile: tool_progress and busy_ack_detail default off (no\n # per-tool breadcrumb stream, no \"iteration 21/60\" debug detail in busy\n # acks or heartbeats), but interim_assistant_messages and\n # long_running_notifications STAY ON so the user has real signal between\n # turn start and final answer (mid-turn assistant commentary + a single\n # edit-in-place \"⏳ Working — N min\" heartbeat). Override under\n # display.platforms.telegram.\n\n # Auto-cleanup of temporary progress bubbles after the final response lands.\n # On platforms that support message deletion (currently Telegram), this\n # removes the tool-progress bubble, \"⏳ Still working...\" notices, and\n # context-pressure status messages once the final reply has been delivered —\n # keeping long-running turns visible live, then tidy afterward. Failed runs\n # leave the bubbles in place as breadcrumbs. Off by default.\n # Per-platform override: display.platforms.telegram.cleanup_progress\n # true: Delete tracked progress/status bubbles on successful turn\n # false: Leave everything in place (default)\n # Example:\n # display:\n # platforms:\n # telegram:\n # cleanup_progress: true\n cleanup_progress: false\n\n # Gateway-only natural mid-turn assistant updates.\n # When true, completed assistant status messages are sent as separate chat\n # messages. This is independent of tool_progress and gateway streaming.\n # true: Send mid-turn assistant updates as separate messages (default)\n # false: Only send the final response\n interim_assistant_messages: true\n\n # Gateway-only long-running status heartbeats.\n # When false, the platform does not receive periodic \"⏳ Working — N min\"\n # notifications even if agent.gateway_notify_interval is non-zero. The\n # heartbeat edits a single message in place (where the adapter supports\n # editing) instead of posting a new bubble each interval.\n # Default: true everywhere, including Telegram (silent agents are worse\n # than a single edit-in-place heartbeat).\n long_running_notifications: true\n\n # Include detailed iteration/tool/status context in busy acknowledgments\n # and long-running heartbeats. When true, busy acks show \"iteration 21/60,\n # terminal, 10 min\" and the heartbeat shows \"⏳ Working — 12 min,\n # iteration 21/60, terminal\". When false (Telegram default), both stay\n # terse: \"Interrupting current task\" and \"⏳ Working — 12 min, terminal\".\n busy_ack_detail: true\n\n # What Enter does when Hermes is already busy (CLI and gateway platforms).\n # interrupt: Interrupt the current run and redirect Hermes (default)\n # queue: Queue your message for the next turn\n # steer: Inject your message mid-run via /steer, arriving at the agent\n # after the next tool call — no interrupt, no role violation.\n # Falls back to 'queue' if the agent isn't running yet or if\n # images are attached (steer only carries text).\n # Ctrl+C (or /stop in gateway) always interrupts regardless of this setting.\n # Toggle at runtime with /busy .\n busy_input_mode: interrupt\n\n # Background process notifications (gateway/messaging only).\n # Controls how chatty the process watcher is when you use\n # terminal(background=true, notify_on_complete=true) from Telegram/Discord/etc.\n # off: No watcher messages at all\n # result: Only the final completion message\n # error: Only the final message when exit code != 0\n # all: Running output updates + final message (default)\n background_process_notifications: all\n\n\n # Play terminal bell when agent finishes a response.\n # Useful for long-running tasks — your terminal will ding when the agent is done.\n # Works over SSH. Most terminals can be configured to flash the taskbar or play a sound.\n # true: Ring the terminal bell on each response\n # false: Silent (default)\n bell_on_complete: false\n\n # Show model reasoning/thinking before each response.\n # When enabled, a dim box shows the model's thought process above the response.\n # Toggle at runtime with /reasoning show or /reasoning hide.\n # true: Show the reasoning box\n # false: Hide reasoning (default)\n show_reasoning: false\n\n # Stream tokens to the terminal as they arrive instead of waiting for the\n # full response. The response box opens on first token and text appears\n # line-by-line. Tool calls are still captured silently.\n # true: Stream tokens as they arrive (default)\n # false: Wait for the full response before rendering\n streaming: true\n\n # Show [HH:MM] timestamps on user input and assistant response labels.\n # timestamps: false\n\n # ───────────────────────────────────────────────────────────────────────────\n # Skin / Theme\n # ───────────────────────────────────────────────────────────────────────────\n # Customize CLI visual appearance — banner colors, spinner faces, tool prefix,\n # response box label, and branding text. Change at runtime with /skin .\n #\n # Built-in skins:\n # default — Classic Hermes gold/kawaii\n # ares — Crimson/bronze war-god theme with spinner wings\n # mono — Clean grayscale monochrome\n # slate — Cool blue developer-focused\n # daylight — Bright light-mode theme\n # warm-lightmode — Warm paper-tone light-mode theme\n # poseidon — Sea-green/teal Olympian theme\n # sisyphus — Earthy stone-and-moss theme\n # charizard — Fiery orange dragon theme\n #\n # Custom skins: drop a YAML file in ~/.hermes/skins/.yaml\n # Schema (all fields optional, missing values inherit from default):\n #\n # name: my-theme\n # description: Short description\n # colors:\n # banner_border: \"#HEX\" # Panel border\n # banner_title: \"#HEX\" # Panel title\n # banner_accent: \"#HEX\" # Section headers (Available Tools, etc.)\n # banner_dim: \"#HEX\" # Dim/muted text\n # banner_text: \"#HEX\" # Body text (tool names, skill names)\n # ui_accent: \"#HEX\" # UI accent color\n # response_border: \"#HEX\" # Response box border color\n # spinner:\n # waiting_faces: [\"(⚔)\", \"(⛨)\"] # Faces shown while waiting\n # thinking_faces: [\"(⚔)\", \"(⌁)\"] # Faces shown while thinking\n # thinking_verbs: [\"forging\", \"plotting\"] # Verbs for spinner messages\n # wings: # Optional left/right spinner decorations\n # - [\"⟪⚔\", \"⚔⟫\"]\n # - [\"⟪▲\", \"▲⟫\"]\n # branding:\n # agent_name: \"My Agent\" # Banner title and branding\n # welcome: \"Welcome message\" # Shown at CLI startup\n # response_label: \" ⚔ Agent \" # Response box header label\n # prompt_symbol: \"⚔\" # Prompt symbol (bare token; renderers add trailing space)\n # tool_prefix: \"╎\" # Tool output line prefix (default: ┊)\n #\n skin: default\n\n# =============================================================================\n# Model Aliases — short names for /model command\n# =============================================================================\n# Map short aliases to exact (model, provider, base_url) tuples.\n# Used by /model tab completion and resolve_alias().\n# Aliases are checked BEFORE the models.dev catalog, so they can route\n# to endpoints not in the catalog (e.g. Ollama Cloud, local servers).\n#\n# model_aliases:\n# opus:\n# model: claude-opus-4-6\n# provider: anthropic\n# qwen:\n# model: \"qwen3.5:397b\"\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n# glm:\n# model: glm-4.7\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n\n# =============================================================================\n# Privacy\n# =============================================================================\n# privacy:\n# # Redact PII from the LLM context prompt.\n# # When true, phone numbers are stripped and user/chat IDs are replaced\n# # with deterministic hashes before being sent to the model.\n# # Names and usernames are NOT affected (user-chosen, publicly visible).\n# # Routing/delivery still uses the original values internally.\n# redact_pii: false\n\n# =============================================================================\n# Shell-script hooks\n# =============================================================================\n# Register shell scripts as plugin-hook callbacks. Each entry is executed as\n# a subprocess (shell=False, shlex.split) with a JSON payload on stdin. On\n# stdout the script may return JSON that either blocks the tool call or\n# injects context into the next LLM call.\n#\n# Valid events (mirror hermes_cli.plugins.VALID_HOOKS):\n# pre_tool_call, post_tool_call, pre_llm_call, post_llm_call,\n# pre_api_request, post_api_request, on_session_start, on_session_end,\n# on_session_finalize, on_session_reset, subagent_stop\n#\n# First-use consent: each (event, command) pair prompts once on a TTY, then\n# is persisted to ~/.hermes/shell-hooks-allowlist.json. Non-interactive\n# runs (gateway, cron) need --accept-hooks, HERMES_ACCEPT_HOOKS=1, or the\n# hooks_auto_accept key below.\n#\n# See website/docs/user-guide/features/hooks.md for the full JSON wire\n# protocol and worked examples.\n#\n# hooks:\n# pre_tool_call:\n# - matcher: \"terminal\"\n# command: \"~/.hermes/agent-hooks/block-rm-rf.sh\"\n# timeout: 10\n# post_tool_call:\n# - matcher: \"write_file|patch\"\n# command: \"~/.hermes/agent-hooks/auto-format.sh\"\n# pre_llm_call:\n# - command: \"~/.hermes/agent-hooks/inject-cwd-context.sh\"\n# subagent_stop:\n# - command: \"~/.hermes/agent-hooks/log-orchestration.sh\"\n#\n# hooks_auto_accept: false\n\n\n# =============================================================================\n# Update Behavior\n# =============================================================================\nupdates:\n # Create a full HERMES_HOME zip before every `hermes update`.\n # Backups land in ~/.hermes/backups/ and can be restored with `hermes import`.\n # Off by default because large homes can add minutes to every update.\n pre_update_backup: false\n\n # Number of pre-update backup zips to retain.\n backup_keep: 5\n\n # What non-interactive updates do with local source edits in the Hermes repo.\n # Interactive terminal updates always prompt before restoring the autostash.\n #\n # stash - auto-stash before pull, then auto-restore after success (default)\n # discard - drop the update-created stash after success; use only on managed\n # installs where local source edits should not persist\n non_interactive_local_changes: \"stash\"\n\n\n# =============================================================================\n# Web Dashboard\n# =============================================================================\n# OAuth gate configuration for `hermes dashboard --host `.\n# The bundled Nous Portal plugin reads these on startup; settings here are\n# the canonical surface. Each can be overridden by an environment variable:\n#\n# dashboard.oauth.client_id <- HERMES_DASHBOARD_OAUTH_CLIENT_ID\n# dashboard.oauth.portal_url <- HERMES_DASHBOARD_PORTAL_URL\n# dashboard.public_url <- HERMES_DASHBOARD_PUBLIC_URL\n#\n# Env wins when set to a non-empty value. This is what Fly.io's platform-\n# secret injection uses to push per-deploy client_ids without needing to\n# bake a config.yaml into the image. Empty env values are treated as unset\n# so a provisioned-but-not-populated secret can't shadow a valid entry here.\n#\n# Local dev / on-prem deploys should typically set these via config.yaml\n# (the ~/.hermes/.env file is reserved for API keys and secrets).\n#\n# dashboard:\n# oauth:\n# client_id: \"\" # agent:{instance_id}; Portal provisions this at deploy\n# portal_url: \"\" # blank → default https://portal.nousresearch.com\n#\n# # Force the absolute base URL the OAuth callback (and any other public\n# # URL the dashboard hands to external systems) is built from. Set this\n# # for deploys behind reverse proxies that don't reliably forward\n# # X-Forwarded-Host / X-Forwarded-Proto / X-Forwarded-Prefix (manual\n# # nginx setups, on-prem ingresses, custom-domain Fly deploys without\n# # full proxy header chains).\n# #\n# # When set, the value is the complete authority: scheme + host +\n# # optional path prefix (e.g. \"https://example.com/hermes\"). The OAuth\n# # callback URL becomes \"/auth/callback\" — X-Forwarded-Prefix\n# # is IGNORED on this code path because the operator has explicitly\n# # declared the public URL and we no longer need to guess.\n# #\n# # Leave empty to use the existing proxy-header reconstruction (the\n# # default — works on Fly.io out of the box).\n# #\n# # public_url: \"https://example.com/hermes\"\n#\n# -----------------------------------------------------------------------------\n# Self-hosted OIDC dashboard auth (generic OpenID Connect — Authentik,\n# Keycloak, Zitadel, Authelia, Auth0, Okta, Google, …). Use this INSTEAD of the\n# nous block above when gating the dashboard with your own identity provider.\n# Each setting can be overridden by an environment variable:\n#\n# dashboard.oauth.self_hosted.issuer <- HERMES_DASHBOARD_OIDC_ISSUER\n# dashboard.oauth.self_hosted.client_id <- HERMES_DASHBOARD_OIDC_CLIENT_ID\n# dashboard.oauth.self_hosted.scopes <- HERMES_DASHBOARD_OIDC_SCOPES\n# dashboard.oauth.self_hosted.client_secret <- HERMES_DASHBOARD_OIDC_CLIENT_SECRET\n#\n# dashboard:\n# oauth:\n# provider: self-hosted\n# self_hosted:\n# issuer: \"https://auth.example.com/application/o/hermes/\" # required\n# client_id: \"hermes-dashboard\" # required\n# scopes: \"openid profile email\" # optional\n#\n# # OPTIONAL — set ONLY if your IDP registered the client as\n# # *confidential* (Authentik / Keycloak often default to this). When\n# # set, Hermes authenticates the client at the token endpoint\n# # (client_secret_basic or client_secret_post, auto-selected from the\n# # IDP's discovery doc) IN ADDITION to PKCE. Leave unset for a public\n# # (PKCE-only) client — the common case.\n# #\n# # This is a CREDENTIAL: prefer setting HERMES_DASHBOARD_OIDC_CLIENT_SECRET\n# # in ~/.hermes/.env over putting it here in config.yaml.\n# # client_secret: \"\"\n"}, "a10113658b6033c5902bbe873bfe2e1f05815339": {"t": 1785767890.1867204, "n": 67529, "h": "85fd187d16bafb5f", "c": "# Hermes Agent CLI Configuration\n# Copy this file to cli-config.yaml and customize as needed.\n# This file configures the CLI behavior. Environment variables in .env take precedence.\n\n# =============================================================================\n# Model Configuration\n# =============================================================================\nmodel:\n # Default model to use (can be overridden with --model flag)\n # Both \"default\" and \"model\" work as the key name here.\n default: \"anthropic/claude-opus-4.6\"\n \n # Inference provider selection:\n # \"auto\" - Auto-detect from credentials (default)\n # \"openrouter\" - OpenRouter (requires: OPENROUTER_API_KEY or OPENAI_API_KEY)\n # \"nous\" - Nous Portal OAuth (requires: hermes auth add nous)\n # \"nous-api\" - Nous Portal API key (requires: NOUS_API_KEY)\n # \"anthropic\" - Direct Anthropic API (requires: ANTHROPIC_API_KEY)\n # \"openai-codex\" - OpenAI Codex (requires: hermes auth)\n # \"copilot\" - GitHub Copilot / GitHub Models (requires: GITHUB_TOKEN)\n # \"gemini\" - Use Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n # \"zai\" - Use z.ai / ZhipuAI GLM models (requires: GLM_API_KEY)\n # \"kimi-coding\" - Kimi / Moonshot AI (requires: KIMI_API_KEY)\n # \"minimax\" - MiniMax global (requires: MINIMAX_API_KEY)\n # \"minimax-cn\" - MiniMax China (requires: MINIMAX_CN_API_KEY)\n # \"huggingface\" - Hugging Face Inference (requires: HF_TOKEN)\n # \"nvidia\" - NVIDIA NIM / build.nvidia.com (requires: NVIDIA_API_KEY)\n # \"xiaomi\" - Xiaomi MiMo (requires: XIAOMI_API_KEY)\n # \"arcee\" - Arcee AI Trinity models (requires: ARCEEAI_API_KEY)\n # \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY — https://ollama.com/settings)\n # \"kilocode\" - KiloCode gateway (requires: KILOCODE_API_KEY)\n # \"azure-foundry\" - Microsoft Foundry / Azure OpenAI (API key or Entra ID)\n # \"lmstudio\" - LM Studio local server (optional: LM_API_KEY, defaults to http://127.0.0.1:1234/v1)\n #\n # Local servers (LM Studio, Ollama, vLLM, llama.cpp):\n # \"custom\" - Any other OpenAI-compatible endpoint. Set base_url below.\n # Aliases: \"ollama\", \"vllm\", \"llamacpp\" all map to \"custom\".\n # LM Studio is first-class and uses provider: \"lmstudio\".\n # It works with both no-auth and auth-enabled server modes.\n #\n # Can also be overridden for a single invocation with the --provider flag.\n provider: \"auto\"\n \n # API configuration (falls back to OPENROUTER_API_KEY env var)\n # api_key: \"your-key-here\" # Uncomment to set here instead of .env\n base_url: \"https://openrouter.ai/api/v1\"\n\n # Azure Foundry keyless auth example:\n # provider: \"azure-foundry\"\n # base_url: \"https://.openai.azure.com/openai/v1\"\n # auth_mode: \"entra_id\" # DefaultAzureCredential: az login, managed identity, workload identity, etc.\n # default: \"gpt-4o\" # Deployment/model name\n # entra:\n # scope: \"https://ai.azure.com/.default\" # Optional; this is the default.\n\n # ── Token limits — two settings, easy to confuse ──────────────────────────\n #\n # context_length: TOTAL context window (input + output tokens combined).\n # Controls when Hermes compresses history and validates requests.\n # Leave unset — Hermes auto-detects the correct value from the provider.\n # Set manually only when auto-detection is wrong (e.g. a local server with\n # a custom num_ctx, or a proxy that doesn't expose /v1/models).\n #\n # context_length: 131072\n #\n # max_tokens: OUTPUT cap — maximum tokens the model may generate per response.\n # Unrelated to how long your conversation history can be.\n # The OpenAI-standard name \"max_tokens\" is a misnomer; Anthropic's native\n # API has since renamed it \"max_output_tokens\" for clarity.\n # Leave unset to use the model's native output ceiling (recommended).\n # Set only if you want to deliberately limit individual response length.\n #\n# max_tokens: 8192\n\n # ── Custom request headers (optional) ─────────────────────────────────────\n #\n # default_headers: extra HTTP headers sent on every request to an\n # OpenAI-compatible endpoint. User values take precedence over the\n # provider/SDK defaults, so this is the supported way to override the\n # OpenAI Python SDK's identifying headers (User-Agent: OpenAI/Python ...,\n # X-Stainless-*) when a custom provider sits behind a gateway/WAF that\n # rejects them — e.g. an upstream that returns \"502 Upstream access\n # forbidden\" for the SDK default User-Agent but accepts a plain one.\n # Applies on the OpenAI wire only (not native Anthropic / Bedrock).\n #\n # default_headers:\n # User-Agent: \"curl/8.7.1\"\n\n# Named provider overrides (optional)\n# Use this for per-provider request timeouts, non-stream stale timeouts,\n# and per-model exceptions.\n# Applies to the primary turn client on every api_mode (OpenAI-wire, native\n# Anthropic, and Anthropic-compatible providers), the fallback chain, and\n# client rebuilds during credential rotation. For OpenAI-wire chat\n# completions (streaming and non-streaming) the configured value is also\n# used as the per-request ``timeout=`` kwarg so it wins over the legacy\n# HERMES_API_TIMEOUT env var (which still applies when no config is set).\n# ``stale_timeout_seconds`` controls the non-streaming stale-call detector and\n# wins over the legacy HERMES_API_CALL_STALE_TIMEOUT env var. Leaving these\n# unset keeps the legacy defaults (HERMES_API_TIMEOUT=1800s,\n# HERMES_API_CALL_STALE_TIMEOUT=90s, native Anthropic 900s). The\n# implicit non-stream stale detector is auto-disabled for local endpoints\n# and can scale upward for very large contexts.\n#\n# Not currently wired for AWS Bedrock (bedrock_converse + AnthropicBedrock\n# SDK paths) — those use boto3 with its own timeout configuration.\n#\n# providers:\n# ollama-local:\n# request_timeout_seconds: 300 # Longer timeout for local cold-starts\n# stale_timeout_seconds: 900 # Explicitly re-enable stale detection on local endpoints\n# anthropic:\n# request_timeout_seconds: 30 # Fast-fail cloud requests\n# models:\n# claude-opus-4.6:\n# timeout_seconds: 600 # Longer timeout for extended-thinking Opus calls\n# openai-codex:\n# models:\n# gpt-5.4:\n# stale_timeout_seconds: 1800 # Longer non-stream stale timeout for slow large-context turns\n\n# =============================================================================\n# OpenRouter Provider Routing (only applies when using OpenRouter)\n# =============================================================================\n# Control how requests are routed across providers on OpenRouter.\n# See: https://openrouter.ai/docs/guides/routing/provider-selection\n#\n# provider_routing:\n# # Sort strategy: \"price\" (default), \"throughput\", or \"latency\"\n# # Append :nitro to model name for a shortcut to throughput sorting.\n# sort: \"throughput\"\n#\n# # Only allow these providers (provider slugs from OpenRouter)\n# # only: [\"anthropic\", \"google\"]\n#\n# # Skip these providers entirely\n# # ignore: [\"deepinfra\", \"fireworks\"]\n#\n# # Try providers in this order (overrides default load balancing)\n# # order: [\"anthropic\", \"google\", \"together\"]\n#\n# # Require providers to support all parameters in your request\n# # require_parameters: true\n#\n# # Data policy: \"allow\" (default) or \"deny\" to exclude providers that may store data\n# # data_collection: \"deny\"\n\n# =============================================================================\n# OpenRouter Response Caching (only applies when using OpenRouter)\n# =============================================================================\n# Cache identical API responses at the OpenRouter edge for free instant replays.\n# When enabled, identical requests (same model, messages, parameters) return\n# cached responses with zero billing. Separate from Anthropic prompt caching.\n# See: https://openrouter.ai/docs/guides/features/response-caching\n#\n# openrouter:\n# response_cache: true # Enable response caching (default: true)\n# response_cache_ttl: 300 # Cache TTL in seconds, 1-86400 (default: 300)\n\n# =============================================================================\n# Git Worktree Isolation\n# =============================================================================\n# When enabled, each CLI session creates an isolated git worktree so multiple\n# agents can work on the same repo concurrently without file collisions.\n# Equivalent to always passing --worktree / -w on the command line.\n#\n# worktree: true # Always create a worktree when in a git repo\n# worktree: false # Default — only create when -w flag is passed\n#\n# By default a new worktree branches from the freshly-fetched remote tip\n# (the current branch's upstream, else the remote's default branch) so it\n# starts current with the project instead of from the local clone's\n# (possibly stale) HEAD. Set worktree_sync: false to branch from local HEAD\n# instead — useful when offline or when you deliberately want the clone's\n# exact current state as the base.\n#\n# worktree_sync: true # Default — branch from the fetched remote tip\n# worktree_sync: false # Branch from local HEAD (offline / pinned base)\n\n# =============================================================================\n# Terminal Tool Configuration\n# =============================================================================\n# Choose ONE of the following terminal configurations by uncommenting it.\n# The terminal tool executes commands in the specified environment.\n\n# -----------------------------------------------------------------------------\n# OPTION 1: Local execution (default)\n# Commands run directly on your machine in the current directory\n# -----------------------------------------------------------------------------\n# Working directory behavior:\n# - CLI (`hermes` command): Uses \".\" (current directory where you run hermes)\n# - Gateway/messaging/cron: Uses terminal.cwd here; legacy .env cwd values are deprecated\nterminal:\n backend: \"local\"\n cwd: \".\" # For local backend: \".\" = current directory. Ignored for remote backends unless a backend documents otherwise.\n timeout: 180\n # HOME policy for tool subprocesses:\n # auto - default: host uses your real HOME; containers use HERMES_HOME/home\n # real - force your real OS-user HOME\n # profile - force HERMES_HOME/home for strict per-profile CLI config isolation\n home_mode: \"auto\"\n docker_mount_cwd_to_workspace: false # SECURITY: off by default. Opt in to mount the launch cwd into Docker /workspace.\n lifetime_seconds: 300\n # sudo_password: \"hunter2\" # Optional: pipe a sudo password via sudo -S. SECURITY WARNING: plaintext.\n # sudo_password: \"\" # Explicit empty password: try empty and never open the interactive sudo prompt.\n\n# -----------------------------------------------------------------------------\n# OPTION 2: SSH remote execution\n# Commands run on a remote server - agent code stays local (sandboxed)\n# Great for: keeping agent isolated from its own code, using powerful remote hardware\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"ssh\"\n# cwd: \"/home/myuser/project\" # Path on the REMOTE server\n# timeout: 180\n# lifetime_seconds: 300\n# ssh_host: \"my-server.example.com\"\n# ssh_user: \"myuser\"\n# ssh_port: 22\n# ssh_key: \"~/.ssh/id_rsa\" # Optional - uses ssh-agent if not specified\n\n# -----------------------------------------------------------------------------\n# OPTION 3: Docker container\n# Commands run in an isolated Docker container\n# Great for: reproducible environments, testing, isolation\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"docker\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /)\n# timeout: 180\n# lifetime_seconds: 300\n# docker_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# docker_mount_cwd_to_workspace: true # Explicit opt-in: mount your launch cwd into /workspace\n# # Optional: run the container as your host user's uid:gid so files written\n# # into bind-mounted dirs are owned by you, not root. Drops SETUID/SETGID\n# # caps too since no gosu privilege drop is needed. Leave off if your\n# # chosen docker_image expects to start as root.\n# docker_run_as_host_user: true\n# # Optional: explicitly forward selected env vars into Docker.\n# # These values come from your current shell first, then ~/.hermes/.env.\n# # Warning: anything forwarded here is visible to commands run in the container.\n# docker_forward_env:\n# - \"GITHUB_TOKEN\"\n# - \"NPM_TOKEN\"\n# # Optional: extra flags passed verbatim to docker run (appended after security defaults).\n# # Useful for adding capabilities (e.g. apt installs needing SETUID) or custom options.\n# # Example: add a Linux capability not included by default\n# # docker_extra_args:\n# # - \"--cap-add\"\n# # - \"SETUID\"\n\n# -----------------------------------------------------------------------------\n# OPTION 4: Singularity/Apptainer container\n# Commands run in a Singularity container (common in HPC environments)\n# Great for: HPC clusters, shared compute environments\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"singularity\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# singularity_image: \"docker://nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 5: Modal cloud execution\n# Commands run on Modal's cloud infrastructure\n# Great for: GPU access, scalable compute, serverless execution\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"modal\"\n# cwd: \"/workspace\" # Path INSIDE the sandbox (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# modal_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 6: Daytona cloud execution\n# Commands run in Daytona cloud sandboxes\n# Great for: Cloud dev environments, persistent workspaces, team collaboration\n# Requires: pip install daytona, DAYTONA_API_KEY env var\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"daytona\"\n# cwd: \"~\"\n# timeout: 180\n# lifetime_seconds: 300\n# daytona_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# container_disk: 10240 # Daytona max is 10GB per sandbox\n\n#\n# --- Container resource limits (docker, singularity, modal, daytona -- ignored for local/ssh) ---\n# These settings apply to all container backends. They control the resources\n# allocated to the sandbox and whether its filesystem persists across sessions.\n container_cpu: 1 # CPU cores\n container_memory: 5120 # Memory in MB (5120 = 5GB)\n container_disk: 51200 # Disk in MB (51200 = 50GB)\n container_persistent: true # Persist filesystem across sessions (false = ephemeral)\n\n# -----------------------------------------------------------------------------\n# SUDO SUPPORT (works with ALL backends above)\n# -----------------------------------------------------------------------------\n# Add sudo_password to any terminal config above to enable sudo commands.\n# The password is piped via `sudo -S`. Works with local, ssh, docker, etc.\n#\n# SECURITY WARNING: Password stored in plaintext!\n#\n# INTERACTIVE PROMPT: If sudo_password is unset and the CLI is running,\n# you'll be prompted to enter your password when sudo is needed:\n# - 45-second timeout (auto-skips if no input)\n# - Press Enter to skip (command fails gracefully)\n# - Password is hidden while typing\n# - Password is cached for the session\n#\n# EMPTY PASSWORDS: Setting sudo_password to an explicit empty string is different\n# from leaving it unset. Hermes will try an empty password via `sudo -S` and\n# will not open the interactive prompt. This is useful for passwordless sudo,\n# Touch ID sudo setups, and environments where prompting is just noise.\n#\n# ALTERNATIVES:\n# - SSH backend: Configure passwordless sudo on the remote server\n# - Containers: Run as root inside the container (no sudo needed)\n# - Local: Configure /etc/sudoers for specific commands\n#\n# Example (add to your terminal section):\n# sudo_password: \"your-password-here\"\n\n# =============================================================================\n# Security Scanning (tirith)\n# =============================================================================\n# Optional pre-exec command security scanning via tirith.\n# Detects homograph URLs, pipe-to-shell, terminal injection, env manipulation.\n# Install: brew install sheeki03/tap/tirith\n# Docs: https://github.com/sheeki03/tirith\n#\n# security:\n# tirith_enabled: true # Enable/disable tirith scanning\n# tirith_path: \"tirith\" # Path to tirith binary (supports ~ expansion)\n# tirith_timeout: 5 # Scan timeout in seconds\n# tirith_fail_open: true # Allow commands if tirith unavailable\n\n# =============================================================================\n# Browser Tool Configuration\n# =============================================================================\nbrowser:\n # Inactivity timeout in seconds - browser sessions are automatically closed\n # after this period of no activity between agent loops (default: 120 = 2 minutes)\n inactivity_timeout: 120\n\n# =============================================================================\n# Tool Loop Guardrails\n# =============================================================================\n# Soft warnings are enabled by default. They append guidance to repeated failed\n# or non-progressing tool results but still let the tool execute. Hard stops are\n# opt-in circuit breakers for autonomous/cron sessions where stopping a loop is\n# preferable to spending the full iteration budget.\ntool_loop_guardrails:\n warnings_enabled: true\n hard_stop_enabled: false\n warn_after:\n exact_failure: 2\n same_tool_failure: 3\n idempotent_no_progress: 2\n hard_stop_after:\n exact_failure: 5\n same_tool_failure: 8\n idempotent_no_progress: 5\n\n# =============================================================================\n# Context Compression (Auto-shrinks long conversations)\n# =============================================================================\n# When conversation approaches model's context limit, middle turns are\n# automatically summarized to free up space while preserving important context.\n#\n# HOW IT WORKS:\n# 1. Tracks actual token usage from API responses (not estimates)\n# 2. When prompt_tokens >= threshold% of model's context_length, triggers compression\n# 3. Protects first 3 turns (system prompt, initial request, first response)\n# 4. Protects last N turns (default 20 messages = ~10 full turns of recent context)\n# 5. Summarizes middle turns using a fast/cheap model\n# 6. Inserts summary as a user message, continues conversation seamlessly\n#\n# Post-compression tail budget is target_ratio × threshold × context_length:\n# 200K context, threshold 0.50, ratio 0.20 → 20K tokens of recent tail preserved\n# 1M context, threshold 0.50, ratio 0.20 → 100K tokens of recent tail preserved\n#\ncompression:\n # Enable automatic context compression (default: true)\n # Set to false if you prefer to manage context manually or want errors on overflow\n enabled: true\n \n # Trigger compression at this % of model's context limit (default: 0.50 = 50%)\n # Lower values = more aggressive compression, higher values = compress later\n threshold: 0.50\n \n # Fraction of the threshold to preserve as recent tail (default: 0.20 = 20%)\n # e.g. 20% of 50% threshold = 10% of total context kept as recent messages.\n # Summary output is separately capped at 12K tokens (Gemini output limit).\n # Range: 0.10 - 0.80\n target_ratio: 0.20\n\n # Number of most-recent messages to always preserve (default: 20 ≈ 10 full turns)\n # Higher values keep more recent conversation intact at the cost of more aggressive\n # compression of older turns.\n protect_last_n: 20\n\n # Number of non-system messages to protect at the head of the transcript, in\n # ADDITION to the system prompt (which is always implicitly protected).\n # Head messages are NEVER summarized — they survive every compression\n # indefinitely. This gives stable early context for short/medium sessions,\n # but in long-running sessions that rely on rolling compaction the pinned\n # opening turns may not match how you want the session framed over time.\n # Set to 0 to preserve ONLY the system prompt (plus the rolling summary\n # and recent tail) — the cleanest configuration for long-running sessions.\n # Default 3 preserves the system prompt plus the first three non-system\n # head messages, matching the pre-feature behaviour.\n protect_first_n: 3\n\n # To pin a specific model/provider for compression summaries, use the\n # auxiliary section below (auxiliary.compression.provider / model).\n\n# =============================================================================\n# Anthropic prompt caching TTL\n# =============================================================================\n# When prompt caching is active (Claude via OpenRouter or native Anthropic),\n# Anthropic supports two TTL tiers for cached prefixes: \"5m\" (default) and\n# \"1h\". Other values are ignored and \"5m\" is used.\n#\nprompt_caching:\n cache_ttl: \"5m\" # use \"1h\" for long sessions with pauses between turns\n\n# =============================================================================\n# Auxiliary Models (Advanced — Experimental)\n# =============================================================================\n# Hermes uses lightweight \"auxiliary\" models for side tasks: image analysis,\n# browser screenshot analysis, web page summarization, TTS audio-tag insertion,\n# and context compression.\n#\n# By default these use Gemini Flash via OpenRouter or Nous Portal and are\n# auto-detected from your credentials. You do NOT need to change anything\n# here for normal usage.\n#\n# WARNING: Overriding these with providers other than OpenRouter or Nous Portal\n# is EXPERIMENTAL and may not work. Not all models/providers support vision,\n# produce usable summaries, or accept the same API format. Change at your own\n# risk — if things break, reset to \"auto\" / empty values.\n#\n# Each task has its own provider + model pair so you can mix providers.\n# For example: OpenRouter for vision (needs multimodal), but your main\n# local endpoint for compression (just needs text).\n#\n# Provider options:\n# \"auto\" - Best available: OpenRouter → Nous Portal → main endpoint (default)\n# \"openrouter\" - Force OpenRouter (requires OPENROUTER_API_KEY)\n# \"nous\" - Force Nous Portal (requires: hermes auth add nous)\n# \"gemini\" - Force Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n# \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY)\n# \"codex\" - Force Codex OAuth (requires: hermes model → Codex).\n# Uses gpt-5.3-codex which supports vision.\n# \"main\" - Use your custom endpoint (OPENAI_BASE_URL + OPENAI_API_KEY).\n# Works with OpenAI API, local models, or any OpenAI-compatible\n# endpoint. Also falls back to Codex OAuth and API-key providers.\n#\n# Model: leave empty to use the provider's default. When empty, OpenRouter\n# uses \"google/gemini-3-flash-preview\" and Nous uses \"gemini-3-flash\".\n# Other providers pick a sensible default automatically.\n#\n# auxiliary:\n# # Image analysis: vision_analyze tool + browser screenshots\n# vision:\n# provider: \"auto\"\n# model: \"\" # e.g. \"google/gemini-2.5-flash\", \"openai/gpt-4o\"\n# timeout: 30 # LLM API call timeout (seconds)\n# download_timeout: 30 # Image HTTP download timeout (seconds)\n# # Increase for slow connections or self-hosted image servers\n#\n# # Web page scraping / summarization + browser page text extraction\n# web_extract:\n# provider: \"auto\"\n# model: \"\"\n#\n# # Gemini 3.1 TTS hidden audio-tag insertion\n# tts_audio_tags:\n# provider: \"auto\" # empty model = your main chat model\n# model: \"\"\n# timeout: 30\n#\n# # Session search — summarizes matching past sessions\n# session_search:\n# provider: \"auto\"\n# model: \"\"\n# timeout: 30\n# max_concurrency: 3 # Limit parallel summaries to reduce request-burst 429s\n# extra_body: {} # Provider-specific OpenAI-compatible request fields\n# # Example for providers that support request-body\n# # reasoning controls:\n# # extra_body:\n# # enable_thinking: false\n# # Some vLLM/Qwen deployments expect this nested:\n# # extra_body:\n# # chat_template_kwargs:\n# # enable_thinking: false\n\n# =============================================================================\n# Persistent Memory\n# =============================================================================\n# Bounded curated memory injected into the system prompt every session.\n# Two stores: MEMORY.md (agent's notes) and USER.md (user profile).\n# Character limits keep the memory small and focused. The agent manages\n# pruning -- when at the limit, it must consolidate or replace entries.\n# Disabled by default in batch_runner.\n#\nmemory:\n # Agent's personal notes: environment facts, conventions, things learned\n memory_enabled: true\n \n # User profile: preferences, communication style, expectations\n user_profile_enabled: true\n \n # Character limits (~2.75 chars per token, model-independent)\n memory_char_limit: 2200 # ~800 tokens\n user_char_limit: 1375 # ~500 tokens\n\n # Periodic memory nudge: remind the agent to consider saving memories\n # every N user turns. Set to 0 to disable. Only active when memory is enabled.\n nudge_interval: 10 # Nudge every 10 user turns (0 = disabled)\n\n # Memory flush: give the agent one turn to save memories before context is\n # lost (compression, /new, /reset, exit). Set to 0 to disable.\n # For exit/reset, only fires if the session had at least this many user turns.\n flush_min_turns: 6 # Min user turns to trigger flush on exit/reset (0 = disabled)\n\n# =============================================================================\n# Session Reset Policy (Messaging Platforms)\n# =============================================================================\n# Controls when messaging sessions (Telegram, Discord, WhatsApp, Slack) are\n# automatically cleared. Without resets, conversation context grows indefinitely\n# which increases API costs with every message.\n#\n# When a reset triggers, the agent first saves important information to its\n# persistent memory — but the conversation context is wiped. The agent starts\n# fresh but retains learned facts via its memory system.\n#\n# Users can always manually reset with /reset or /new in chat.\n#\n# Modes:\n# \"both\" - Reset on EITHER inactivity timeout or daily boundary (recommended)\n# \"idle\" - Reset only after N minutes of inactivity\n# \"daily\" - Reset only at a fixed hour each day\n# \"none\" - Never auto-reset; context lives until /reset or compression kicks in\n#\n# When a reset triggers, the agent gets one turn to save important memories and\n# skills before the context is wiped. Persistent memory carries across sessions.\n#\nsession_reset:\n mode: both # \"both\", \"idle\", \"daily\", or \"none\"\n idle_minutes: 1440 # Inactivity timeout in minutes (default: 1440 = 24 hours)\n at_hour: 4 # Daily reset hour, 0-23 local time (default: 4 AM)\n\n# Maximum number of simultaneously active chat sessions across CLI, TUI,\n# dashboard chat, and messaging gateway. Set to null, 0, or omit to allow\n# unlimited concurrent sessions. When the limit is reached, new sessions get a\n# clean error while existing active sessions keep their normal behavior. This\n# top-level key takes precedence over gateway.max_concurrent_sessions. The cap\n# is a best-effort single-host/profile runtime guard; Hermes fails open if the\n# local runtime lease registry cannot be read or locked.\nmax_concurrent_sessions: null\n\n# When true, group/channel chats use one session per participant when the platform\n# provides a user ID. This is the secure default and prevents users in the same\n# room from sharing context, interrupts, and token costs. Set false only if you\n# explicitly want one shared \"room brain\" per group/channel.\ngroup_sessions_per_user: true\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Gateway Streaming\n# ─────────────────────────────────────────────────────────────────────────────\n# Stream tokens to messaging platforms in real-time. The bot sends a message\n# on first token, then progressively edits it as more tokens arrive.\n# Disabled by default — enable to try the streaming UX on Telegram/Discord/Slack.\n# For Telegram, partial edits are sent as plain text and only the final edit uses MarkdownV2.\nstreaming:\n enabled: false\n # transport: edit # \"edit\" = progressive editMessageText\n # edit_interval: 0.3 # seconds between message edits\n # buffer_threshold: 40 # chars before forcing an edit flush\n # cursor: \" ▉\" # cursor shown during streaming\n\n# =============================================================================\n# Skills Configuration\n# =============================================================================\n# Skills are reusable procedures the agent can load and follow. The agent can\n# also create new skills after completing complex tasks.\n#\nskills:\n # Nudge the agent to create skills after complex tasks.\n # Every N tool-calling iterations, remind the model to consider saving a skill.\n # Set to 0 to disable.\n creation_nudge_interval: 15\n\n # External skill directories — share skills across tools/agents without\n # copying them into ~/.hermes/skills/. Each path is expanded (~ and ${VAR})\n # and resolved to an absolute path. External dirs are read-only: skill\n # creation always writes to ~/.hermes/skills/. Local skills take precedence\n # when names collide.\n # external_dirs:\n # - ~/.agents/skills\n # - /home/shared/team-skills\n\n# =============================================================================\n# Agent Behavior\n# =============================================================================\nagent:\n # Maximum tool-calling iterations per conversation\n # Higher = more room for complex tasks, but costs more tokens\n # Recommended: 20-30 for focused tasks, 50-100 for open exploration\n max_turns: 60\n\n # Inactivity timeout for gateway agent runs (seconds, 0 = unlimited).\n # The agent can run indefinitely when actively calling tools or receiving\n # API responses. Only fires after the agent has been idle for this duration.\n # gateway_timeout: 1800\n\n # Staged warning: send a warning before escalating to full timeout.\n # Fires once per run when inactivity reaches this threshold (seconds).\n # Set to 0 to disable the warning.\n # gateway_timeout_warning: 900\n\n # Graceful drain timeout for gateway stop/restart (seconds).\n # Default 0 = no drain: a restart interrupts in-flight agents immediately,\n # cleans up, and exits. Set a positive value only if you want a grace\n # window on /restart, and keep it well under systemd's TimeoutStopSec.\n # restart_drain_timeout: 0\n\n # Max app-level retry attempts for API errors (connection drops, provider\n # timeouts, 5xx, etc.) before the agent surfaces the failure. Lower this\n # to 1 if you use fallback providers and want fast failover on flaky\n # primaries (default 3). The OpenAI SDK does its own low-level retries\n # underneath this wrapper — this is the Hermes-level loop.\n # api_max_retries: 3\n\n # After the agent edits code without fresh passing verification, nudge it to\n # verify before finishing. The default \"auto\" enables it on interactive\n # coding surfaces (CLI, TUI, desktop) and programmatic callers, and disables\n # it on conversational messaging surfaces (Telegram, Discord, etc.) where the\n # verification summary would reach a human as chat noise. Set true or false to\n # force it on or off; the HERMES_VERIFY_ON_STOP env var (1/0) takes precedence.\n # verify_on_stop: auto\n\n # When verify-on-stop finds edited code without fresh verification evidence,\n # append guidance for creative UI work (avoid broad tsc/lint/test before visual\n # approval) and clean-diff expectations. Set false to keep that nudge terse.\n # verify_guidance: true\n\n # A `pre_verify` hook (plugin or shell, see Event Hooks docs) can keep the\n # agent going one more turn to verify/clean before finishing. This caps how\n # many times one turn may be nudged to continue, so a hook can't trap the loop.\n # Default 3.\n # max_verify_nudges: 3\n\n # Enable verbose logging\n verbose: false\n \n # Reasoning effort level (OpenRouter and Nous Portal)\n # Controls how much \"thinking\" the model does before responding.\n # Options: \"xhigh\" (max), \"high\", \"medium\", \"low\", \"minimal\", \"none\" (disable)\n reasoning_effort: \"medium\"\n \n # Predefined personalities (use with /personality command)\n personalities:\n helpful: \"You are a helpful, friendly AI assistant.\"\n concise: \"You are a concise assistant. Keep responses brief and to the point.\"\n technical: \"You are a technical expert. Provide detailed, accurate technical information.\"\n creative: \"You are a creative assistant. Think outside the box and offer innovative solutions.\"\n teacher: \"You are a patient teacher. Explain concepts clearly with examples.\"\n kawaii: \"You are a kawaii assistant! Use cute expressions like (◕‿◕), ★, ♪, and ~! Add sparkles and be super enthusiastic about everything! Every response should feel warm and adorable desu~! ヽ(>∀<☆)ノ\"\n catgirl: \"You are Neko-chan, an anime catgirl AI assistant, nya~! Add 'nya' and cat-like expressions to your speech. Use kaomoji like (=^・ω・^=) and ฅ^•ﻌ•^ฅ. Be playful and curious like a cat, nya~!\"\n pirate: \"Arrr! Ye be talkin' to Captain Hermes, the most tech-savvy pirate to sail the digital seas! Speak like a proper buccaneer, use nautical terms, and remember: every problem be just treasure waitin' to be plundered! Yo ho ho!\"\n shakespeare: \"Hark! Thou speakest with an assistant most versed in the bardic arts. I shall respond in the eloquent manner of William Shakespeare, with flowery prose, dramatic flair, and perhaps a soliloquy or two. What light through yonder terminal breaks?\"\n surfer: \"Duuude! You're chatting with the chillest AI on the web, bro! Everything's gonna be totally rad. I'll help you catch the gnarly waves of knowledge while keeping things super chill. Cowabunga! 🤙\"\n noir: \"The rain hammered against the terminal like regrets on a guilty conscience. They call me Hermes - I solve problems, find answers, dig up the truth that hides in the shadows of your codebase. In this city of silicon and secrets, everyone's got something to hide. What's your story, pal?\"\n uwu: \"hewwo! i'm your fwiendwy assistant uwu~ i wiww twy my best to hewp you! *nuzzles your code* OwO what's this? wet me take a wook! i pwomise to be vewy hewpful >w<\"\n philosopher: \"Greetings, seeker of wisdom. I am an assistant who contemplates the deeper meaning behind every query. Let us examine not just the 'how' but the 'why' of your questions. Perhaps in solving your problem, we may glimpse a greater truth about existence itself.\"\n hype: \"YOOO LET'S GOOOO!!! 🔥🔥🔥 I am SO PUMPED to help you today! Every question is AMAZING and we're gonna CRUSH IT together! This is gonna be LEGENDARY! ARE YOU READY?! LET'S DO THIS! 💪😤🚀\"\n\n# =============================================================================\n# Toolsets\n# =============================================================================\n# Control which tools the agent has access to.\n# Use `hermes tools` to interactively enable/disable tools per platform.\n\n# =============================================================================\n# Platform Toolsets (per-platform tool configuration)\n# =============================================================================\n# Override which toolsets are available on each platform.\n# If a platform isn't listed here, its built-in default is used.\n#\n# You can use EITHER:\n# - A preset like \"hermes-cli\" or \"hermes-telegram\" (curated tool set)\n# - A list of individual toolsets to compose your own (see list below)\n#\n# Supported platform keys: cli, telegram, discord, whatsapp, slack, qqbot, teams, google_chat\n#\n# Examples:\n#\n# # Use presets (same as defaults):\n# platform_toolsets:\n# cli: [hermes-cli]\n# telegram: [hermes-telegram]\n#\n# # Custom: give Telegram only web + terminal + file + planning:\n# platform_toolsets:\n# telegram: [web, terminal, file, todo]\n#\n# # Custom: CLI without browser or image gen:\n# platform_toolsets:\n# cli: [web, terminal, file, skills, todo, tts, cronjob]\n#\n# # Restrictive: Discord gets read-only tools only:\n# platform_toolsets:\n# discord: [web, vision, skills, todo]\n#\n# If not set, defaults are:\n# cli: hermes-cli (everything + cronjob management)\n# telegram: hermes-telegram (terminal, file, web, vision, image, tts, browser, skills, todo, cronjob, messaging)\n# discord: hermes-discord (same as telegram)\n# whatsapp: hermes-whatsapp (same as telegram)\n# slack: hermes-slack (same as telegram)\n# signal: hermes-signal (same as telegram)\n# homeassistant: hermes-homeassistant (same as telegram)\n# qqbot: hermes-qqbot (same as telegram)\n# teams: hermes-teams (same as telegram)\n# google_chat: hermes-google_chat (same as telegram)\n#\nplatform_toolsets:\n cli: [hermes-cli]\n telegram: [hermes-telegram]\n discord: [hermes-discord]\n whatsapp: [hermes-whatsapp]\n slack: [hermes-slack]\n signal: [hermes-signal]\n homeassistant: [hermes-homeassistant]\n qqbot: [hermes-qqbot]\n yuanbao: [hermes-yuanbao]\n teams: [hermes-teams]\n google_chat: [hermes-google_chat]\n\n# =============================================================================\n# Gateway Platform Settings\n# =============================================================================\n# Optional per-platform messaging settings.\n# Platform-specific knobs live under `extra`.\n#\n# platforms:\n# telegram:\n# reply_to_mode: \"first\" # off | first | all\n# # guest_mode lets explicit @mentions from non-allowlisted groups through.\n# # Default false; ordinary messages, replies, and regex wake words stay blocked.\n# guest_mode: false\n# # allowed_chats: [\"-1001234567890\"]\n# extra:\n# disable_link_previews: false # Set true to suppress Telegram URL previews in bot messages\n# rich_messages: false # Bot API 10.1 rich messages (tables/task lists/details/math); default false for copyable legacy MarkdownV2, set true to opt in\n# rich_drafts: false # Experimental rich draft previews during Telegram DM streaming; default false because Telegram Desktop/macOS can visually overlay draft frames\n# command_menu:\n# # Telegram allows up to 100 BotCommands; Hermes defaults to 60 so\n# # all built-in commands plus common skill commands stay visible\n# # while remaining under Telegram's payload-size limit. Clamped 1..100.\n# max_commands: 60\n# # prepend = user priority first, then Hermes defaults\n# # append = Hermes defaults first, then user priority\n# # replace = only the list below defines priority\n# priority_mode: prepend\n# priority:\n# - my_plugin_command\n#\n# Discord-specific settings (config.yaml top-level, not under platforms:):\n#\n# discord:\n# require_mention: true # Require @mention in server channels (default: true)\n# auto_thread: true # Auto-create thread on @mention (default: true)\n# free_response_channels: \"\" # Channel IDs where no mention is needed\n# reactions: true # Show processing reactions (default: true)\n# history_backfill: true # Recover missed channel messages on mention (default: true)\n# history_backfill_limit: 50 # Max messages to scan backwards (default: 50)\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Available toolsets (use these names in platform_toolsets or the toolsets list)\n#\n# Run `hermes chat --list-toolsets` to see all toolsets and their tools.\n# Run `hermes chat --list-tools` to see every individual tool with descriptions.\n# ─────────────────────────────────────────────────────────────────────────────\n#\n# INDIVIDUAL TOOLSETS (compose your own):\n# web - web_search, web_extract\n# search - web_search only (no scraping)\n# terminal - terminal, process\n# file - read_file, write_file, patch, search\n# browser - browser_navigate, browser_snapshot, browser_click, browser_type,\n# browser_scroll, browser_back, browser_press,\n# browser_get_images, browser_vision (requires BROWSERBASE_API_KEY)\n# vision - vision_analyze (requires OPENROUTER_API_KEY)\n# image_gen - image_generate (requires FAL_KEY)\n# skills - skills_list, skill_view\n# skills_hub - skill_hub (search/install/manage from online registries — user-driven only)\n# todo - todo (in-memory task planning, no deps)\n# tts - text_to_speech (Edge TTS free, or ELEVENLABS/OPENAI/MINIMAX/MISTRAL key)\n# cronjob - cronjob (create/list/update/pause/resume/run/remove scheduled tasks)\n#\n# PRESETS (curated bundles):\n# hermes-cli - All of the above except send_message\n# hermes-telegram - terminal, file, web, vision, image_gen, tts, browser,\n# skills, todo, cronjob, send_message\n# hermes-discord - Same as hermes-telegram\n# hermes-whatsapp - Same as hermes-telegram\n# hermes-slack - Same as hermes-telegram\n#\n# COMPOSITE:\n# debugging - terminal + web + file\n# safe - web + vision (no terminal access)\n# all - Everything available\n#\n# web - Web search and content extraction (web_search, web_extract)\n# search - Web search only, no scraping (web_search)\n# terminal - Command execution and process management (terminal, process)\n# file - File operations: read, write, patch, search\n# browser - Full browser automation (navigate, click, type, screenshot, etc.)\n# vision - Image analysis (vision_analyze)\n# image_gen - Image generation with FLUX (image_generate)\n# skills - Load skill documents (skills_list, skill_view)\n# todo - Task planning and tracking for multi-step work\n# memory - Persistent memory across sessions (personal notes + user profile)\n# session_search - Search and recall past conversations (FTS5 + Gemini Flash summarization)\n# tts - Text-to-speech (Edge TTS free, ElevenLabs, OpenAI, MiniMax, Mistral)\n# cronjob - Schedule and manage automated tasks (CLI-only)\n#\n# Composite toolsets:\n# debugging - terminal + web + file (for troubleshooting)\n# safe - web + vision (no terminal access)\n\n# NOTE: The top-level \"toolsets\" key is deprecated and ignored.\n# Tool configuration is managed per-platform via platform_toolsets above.\n# Use `hermes tools` to configure interactively, or edit platform_toolsets directly.\n#\n# CLI override: hermes chat --toolsets terminal,web,file\n\n# =============================================================================\n# MCP (Model Context Protocol) Servers\n# =============================================================================\n# Connect to external MCP servers to add tools from the MCP ecosystem.\n# Each server's tools are automatically discovered and registered.\n# See website/docs/user-guide/features/mcp.md for full documentation.\n#\n# Stdio servers (spawn a subprocess):\n# command: the executable to run\n# args: command-line arguments\n# env: environment variables (only these + safe defaults passed to subprocess)\n#\n# HTTP servers (connect to a URL):\n# url: the MCP server endpoint\n# headers: HTTP headers (e.g., for authentication)\n#\n# Optional per-server settings:\n# timeout: tool call timeout in seconds (default: 120)\n# connect_timeout: initial connection timeout (default: 60)\n# keepalive_interval: liveness ping cadence in seconds (default: 180).\n# Lower it below the server's session TTL for servers that expire idle\n# sessions quickly (e.g. Unreal Engine editor MCP, ~15s), otherwise idle\n# tool calls hit an expired session and pay a slow reconnect. Floored at 5s.\n#\n# mcp_servers:\n# time:\n# command: uvx\n# args: [\"mcp-server-time\"]\n# filesystem:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-filesystem\", \"/home/user\"]\n# notion:\n# url: https://mcp.notion.com/mcp\n# github:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-github\"]\n# env:\n# GITHUB_PERSONAL_ACCESS_TOKEN: \"ghp_...\"\n#\n# Sampling (server-initiated LLM requests) — enabled by default.\n# Per-server config under the 'sampling' key:\n# analysis:\n# command: npx\n# args: [\"-y\", \"analysis-server\"]\n# sampling:\n# enabled: true # default: true\n# model: \"gemini-3-flash\" # override model (optional)\n# max_tokens_cap: 4096 # max tokens per request\n# timeout: 30 # LLM call timeout (seconds)\n# max_rpm: 10 # max requests per minute\n# allowed_models: [] # model whitelist (empty = all)\n# max_tool_rounds: 5 # tool loop limit (0 = disable)\n# log_level: \"info\" # audit verbosity\n\n# =============================================================================\n# Text-to-Speech\n# =============================================================================\n# TTS defaults to Edge TTS unless changed in ~/.hermes/config.yaml.\n# Gemini TTS supports persona/director prompt files, and Gemini 3.1 Flash TTS\n# can use a hidden auxiliary rewrite pass to insert expressive square-bracket\n# audio tags into the TTS script without showing tags in chat.\n#\n# tts:\n# provider: \"gemini\"\n# gemini:\n# model: \"gemini-3.1-flash-tts-preview\"\n# voice: \"Kore\"\n# audio_tags: false\n# persona_prompt_file: \"\" # e.g. ~/.hermes/tts/radio-host.md\n\n# =============================================================================\n# Voice Transcription (Speech-to-Text)\n# =============================================================================\n# Automatically transcribe voice messages on messaging platforms.\n# Providers: local (free, faster-whisper) | groq (free tier) | openai (Whisper API) | mistral (Voxtral Transcribe)\n# Set the corresponding API key in .env: GROQ_API_KEY, OPENAI_API_KEY, or MISTRAL_API_KEY.\nstt:\n enabled: true\n # provider: \"local\" # auto-detected if omitted\n local:\n model: \"base\" # tiny | base | small | medium | large-v3 | turbo\n # language: \"\" # auto-detect; set to \"en\", \"es\", \"fr\", etc. to force\n openai:\n model: \"whisper-1\" # whisper-1 | gpt-4o-mini-transcribe | gpt-4o-transcribe\n # mistral:\n # model: \"voxtral-mini-latest\" # voxtral-mini-latest | voxtral-mini-2602\n\n# =============================================================================\n# Response Pacing (Messaging Platforms)\n# =============================================================================\n# Add human-like delays between message chunks.\n# human_delay:\n# mode: \"off\" # \"off\" | \"natural\" | \"custom\"\n# min_ms: 800 # Min delay (custom mode only)\n# max_ms: 2500 # Max delay (custom mode only)\n\n# =============================================================================\n# Session Logging\n# =============================================================================\n# Session trajectories are automatically saved to logs/ directory.\n# Each session creates: logs/session_YYYYMMDD_HHMMSS_UUID.json\n#\n# The session ID is displayed in the welcome banner for easy reference.\n# Logs contain full conversation history in trajectory format:\n# - System prompt, user messages, assistant responses\n# - Tool calls with inputs/outputs\n# - Timestamps for debugging\n#\n# No configuration needed - logging is always enabled.\n# To disable, you would need to modify the source code.\n\n# =============================================================================\n# Code Execution Sandbox (Programmatic Tool Calling)\n# =============================================================================\n# The execute_code tool runs Python scripts that call Hermes tools via RPC.\n# Intermediate tool results stay out of the LLM's context window.\ncode_execution:\n timeout: 300 # Max seconds per script before kill (default: 300 = 5 min)\n max_tool_calls: 50 # Max RPC tool calls per execution (default: 50)\n\n# =============================================================================\n# Subagent Delegation\n# =============================================================================\n# The delegate_task tool spawns child agents with isolated context.\n# Supports single tasks and batch mode (default 3 parallel, configurable).\ndelegation:\n max_iterations: 50 # Max tool-calling turns per child (default: 50)\n # max_concurrent_children: 3 # Max parallel child agents per batch (default: 3, floor: 1, no ceiling).\n # WARNING: values above 10 multiply API cost linearly.\n # max_spawn_depth: 1 # Delegation tree depth cap (range: 1-3, default: 1 = flat).\n # Raise to 2 to allow workers to spawn their own subagents.\n # Requires role=\"orchestrator\" on intermediate agents.\n # orchestrator_enabled: true # Kill switch for role=\"orchestrator\" children (default: true).\n # subagent_auto_approve: false # When a subagent hits a dangerous-command approval prompt, auto-deny (default: false)\n # or auto-approve \"once\" (true) instead of blocking on stdin.\n # The parent TUI owns stdin, so blocking would deadlock; non-interactive resolution is required.\n # Both choices emit a logger.warning audit line. Flip to true only for cron/batch pipelines.\n # inherit_mcp_toolsets: true # When explicit child toolsets are narrowed, also keep the parent's MCP toolsets (default: true). Set false for strict intersection.\n # model: \"google/gemini-3-flash-preview\" # Override model for subagents (empty = inherit parent)\n # provider: \"openrouter\" # Override provider for subagents (empty = inherit parent)\n # # Resolves full credentials (base_url, api_key) automatically.\n # # Supported: openrouter, nous, zai, kimi-coding, minimax\n\n# =============================================================================\n# Honcho Integration (Cross-Session User Modeling)\n# =============================================================================\n# AI-native persistent memory via Honcho (https://honcho.dev/).\n# Builds a deeper understanding of the user across sessions and tools.\n# Runs alongside USER.md — additive, not a replacement.\n#\n# Requires: pip install honcho-ai\n# Config: ~/.honcho/config.json (shared with Claude Code, Cursor, etc.)\n# API key: HONCHO_API_KEY in ~/.hermes/.env or ~/.honcho/config.json\n#\n# Hermes-specific overrides (optional — most config comes from ~/.honcho/config.json):\n# honcho: {}\n\n# =============================================================================\n# Display\n# =============================================================================\ndisplay:\n # Use compact banner mode (hides the ASCII-art banner, shows a single line).\n # true: Compact single-line banner\n # false: Full ASCII banner with tool/skill summary (default)\n compact: false\n\n # Tool progress display level (CLI and gateway)\n # off: Silent — no tool activity shown, just the final response\n # new: Show a tool indicator only when the tool changes (skip repeats)\n # all: Show every tool call with a short preview (default)\n # verbose: Full args, results, and debug logs (same as /verbose)\n # Toggle at runtime with /verbose in the CLI\n tool_progress: all\n\n # Per-platform defaults can be quieter than the global setting. Telegram\n # tunes for mobile: tool_progress and busy_ack_detail default off (no\n # per-tool breadcrumb stream, no \"iteration 21/60\" debug detail in busy\n # acks or heartbeats), but interim_assistant_messages and\n # long_running_notifications STAY ON so the user has real signal between\n # turn start and final answer (mid-turn assistant commentary + a single\n # edit-in-place \"⏳ Working — N min\" heartbeat). Override under\n # display.platforms.telegram.\n\n # Auto-cleanup of temporary progress bubbles after the final response lands.\n # On platforms that support message deletion (currently Telegram), this\n # removes the tool-progress bubble, \"⏳ Still working...\" notices, and\n # context-pressure status messages once the final reply has been delivered —\n # keeping long-running turns visible live, then tidy afterward. Failed runs\n # leave the bubbles in place as breadcrumbs. Off by default.\n # Per-platform override: display.platforms.telegram.cleanup_progress\n # true: Delete tracked progress/status bubbles on successful turn\n # false: Leave everything in place (default)\n # Example:\n # display:\n # platforms:\n # telegram:\n # cleanup_progress: true\n cleanup_progress: false\n\n # Gateway-only natural mid-turn assistant updates.\n # When true, completed assistant status messages are sent as separate chat\n # messages. This is independent of tool_progress and gateway streaming.\n # true: Send mid-turn assistant updates as separate messages (default)\n # false: Only send the final response\n interim_assistant_messages: true\n\n # Gateway-only long-running status heartbeats.\n # When false, the platform does not receive periodic \"⏳ Working — N min\"\n # notifications even if agent.gateway_notify_interval is non-zero. The\n # heartbeat edits a single message in place (where the adapter supports\n # editing) instead of posting a new bubble each interval.\n # Default: true everywhere, including Telegram (silent agents are worse\n # than a single edit-in-place heartbeat).\n long_running_notifications: true\n\n # Include detailed iteration/tool/status context in busy acknowledgments\n # and long-running heartbeats. When true, busy acks show \"iteration 21/60,\n # terminal, 10 min\" and the heartbeat shows \"⏳ Working — 12 min,\n # iteration 21/60, terminal\". When false (Telegram default), both stay\n # terse: \"Interrupting current task\" and \"⏳ Working — 12 min, terminal\".\n busy_ack_detail: true\n\n # What Enter does when Hermes is already busy (CLI and gateway platforms).\n # interrupt: Interrupt the current run and redirect Hermes (default)\n # queue: Queue your message for the next turn\n # steer: Inject your message mid-run via /steer, arriving at the agent\n # after the next tool call — no interrupt, no role violation.\n # Falls back to 'queue' if the agent isn't running yet or if\n # images are attached (steer only carries text).\n # Ctrl+C (or /stop in gateway) always interrupts regardless of this setting.\n # Toggle at runtime with /busy .\n busy_input_mode: interrupt\n\n # Background process notifications (gateway/messaging only).\n # Controls how chatty the process watcher is when you use\n # terminal(background=true, notify_on_complete=true) from Telegram/Discord/etc.\n # off: No watcher messages at all\n # result: Only the final completion message\n # error: Only the final message when exit code != 0\n # all: Running output updates + final message (default)\n background_process_notifications: all\n\n\n # Play terminal bell when agent finishes a response.\n # Useful for long-running tasks — your terminal will ding when the agent is done.\n # Works over SSH. Most terminals can be configured to flash the taskbar or play a sound.\n # true: Ring the terminal bell on each response\n # false: Silent (default)\n bell_on_complete: false\n\n # Show model reasoning/thinking before each response.\n # When enabled, a dim box shows the model's thought process above the response.\n # Toggle at runtime with /reasoning show or /reasoning hide.\n # true: Show the reasoning box\n # false: Hide reasoning (default)\n show_reasoning: false\n\n # Stream tokens to the terminal as they arrive instead of waiting for the\n # full response. The response box opens on first token and text appears\n # line-by-line. Tool calls are still captured silently.\n # true: Stream tokens as they arrive (default)\n # false: Wait for the full response before rendering\n streaming: true\n\n # Show [HH:MM] timestamps on user input and assistant response labels.\n # timestamps: false\n\n # ───────────────────────────────────────────────────────────────────────────\n # Skin / Theme\n # ───────────────────────────────────────────────────────────────────────────\n # Customize CLI visual appearance — banner colors, spinner faces, tool prefix,\n # response box label, and branding text. Change at runtime with /skin .\n #\n # Built-in skins:\n # default — Classic Hermes gold/kawaii\n # ares — Crimson/bronze war-god theme with spinner wings\n # mono — Clean grayscale monochrome\n # slate — Cool blue developer-focused\n # daylight — Bright light-mode theme\n # warm-lightmode — Warm paper-tone light-mode theme\n # poseidon — Sea-green/teal Olympian theme\n # sisyphus — Earthy stone-and-moss theme\n # charizard — Fiery orange dragon theme\n #\n # Custom skins: drop a YAML file in ~/.hermes/skins/.yaml\n # Schema (all fields optional, missing values inherit from default):\n #\n # name: my-theme\n # description: Short description\n # colors:\n # banner_border: \"#HEX\" # Panel border\n # banner_title: \"#HEX\" # Panel title\n # banner_accent: \"#HEX\" # Section headers (Available Tools, etc.)\n # banner_dim: \"#HEX\" # Dim/muted text\n # banner_text: \"#HEX\" # Body text (tool names, skill names)\n # ui_accent: \"#HEX\" # UI accent color\n # response_border: \"#HEX\" # Response box border color\n # spinner:\n # waiting_faces: [\"(⚔)\", \"(⛨)\"] # Faces shown while waiting\n # thinking_faces: [\"(⚔)\", \"(⌁)\"] # Faces shown while thinking\n # thinking_verbs: [\"forging\", \"plotting\"] # Verbs for spinner messages\n # wings: # Optional left/right spinner decorations\n # - [\"⟪⚔\", \"⚔⟫\"]\n # - [\"⟪▲\", \"▲⟫\"]\n # branding:\n # agent_name: \"My Agent\" # Banner title and branding\n # welcome: \"Welcome message\" # Shown at CLI startup\n # response_label: \" ⚔ Agent \" # Response box header label\n # prompt_symbol: \"⚔\" # Prompt symbol (bare token; renderers add trailing space)\n # tool_prefix: \"╎\" # Tool output line prefix (default: ┊)\n #\n skin: default\n\n# =============================================================================\n# Model Aliases — short names for /model command\n# =============================================================================\n# Map short aliases to exact (model, provider, base_url) tuples.\n# Used by /model tab completion and resolve_alias().\n# Aliases are checked BEFORE the models.dev catalog, so they can route\n# to endpoints not in the catalog (e.g. Ollama Cloud, local servers).\n#\n# model_aliases:\n# opus:\n# model: claude-opus-4-6\n# provider: anthropic\n# qwen:\n# model: \"qwen3.5:397b\"\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n# glm:\n# model: glm-4.7\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n\n# =============================================================================\n# Privacy\n# =============================================================================\n# privacy:\n# # Redact PII from the LLM context prompt.\n# # When true, phone numbers are stripped and user/chat IDs are replaced\n# # with deterministic hashes before being sent to the model.\n# # Names and usernames are NOT affected (user-chosen, publicly visible).\n# # Routing/delivery still uses the original values internally.\n# redact_pii: false\n\n# =============================================================================\n# Shell-script hooks\n# =============================================================================\n# Register shell scripts as plugin-hook callbacks. Each entry is executed as\n# a subprocess (shell=False, shlex.split) with a JSON payload on stdin. On\n# stdout the script may return JSON that either blocks the tool call or\n# injects context into the next LLM call.\n#\n# Valid events (mirror hermes_cli.plugins.VALID_HOOKS):\n# pre_tool_call, post_tool_call, pre_llm_call, post_llm_call,\n# pre_api_request, post_api_request, on_session_start, on_session_end,\n# on_session_finalize, on_session_reset, subagent_stop\n#\n# First-use consent: each (event, command) pair prompts once on a TTY, then\n# is persisted to ~/.hermes/shell-hooks-allowlist.json. Non-interactive\n# runs (gateway, cron) need --accept-hooks, HERMES_ACCEPT_HOOKS=1, or the\n# hooks_auto_accept key below.\n#\n# See website/docs/user-guide/features/hooks.md for the full JSON wire\n# protocol and worked examples.\n#\n# hooks:\n# pre_tool_call:\n# - matcher: \"terminal\"\n# command: \"~/.hermes/agent-hooks/block-rm-rf.sh\"\n# timeout: 10\n# post_tool_call:\n# - matcher: \"write_file|patch\"\n# command: \"~/.hermes/agent-hooks/auto-format.sh\"\n# pre_llm_call:\n# - command: \"~/.hermes/agent-hooks/inject-cwd-context.sh\"\n# subagent_stop:\n# - command: \"~/.hermes/agent-hooks/log-orchestration.sh\"\n#\n# hooks_auto_accept: false\n\n\n# =============================================================================\n# Update Behavior\n# =============================================================================\nupdates:\n # Create a full HERMES_HOME zip before every `hermes update`.\n # Backups land in ~/.hermes/backups/ and can be restored with `hermes import`.\n # Off by default because large homes can add minutes to every update.\n pre_update_backup: false\n\n # Number of pre-update backup zips to retain.\n backup_keep: 5\n\n # What non-interactive updates do with local source edits in the Hermes repo.\n # Interactive terminal updates always prompt before restoring the autostash.\n #\n # stash - auto-stash before pull, then auto-restore after success (default)\n # discard - drop the update-created stash after success; use only on managed\n # installs where local source edits should not persist\n non_interactive_local_changes: \"stash\"\n\n\n# =============================================================================\n# Web Dashboard\n# =============================================================================\n# OAuth gate configuration for `hermes dashboard --host `.\n# The bundled Nous Portal plugin reads these on startup; settings here are\n# the canonical surface. Each can be overridden by an environment variable:\n#\n# dashboard.oauth.client_id <- HERMES_DASHBOARD_OAUTH_CLIENT_ID\n# dashboard.oauth.portal_url <- HERMES_DASHBOARD_PORTAL_URL\n# dashboard.public_url <- HERMES_DASHBOARD_PUBLIC_URL\n#\n# Env wins when set to a non-empty value. This is what Fly.io's platform-\n# secret injection uses to push per-deploy client_ids without needing to\n# bake a config.yaml into the image. Empty env values are treated as unset\n# so a provisioned-but-not-populated secret can't shadow a valid entry here.\n#\n# Local dev / on-prem deploys should typically set these via config.yaml\n# (the ~/.hermes/.env file is reserved for API keys and secrets).\n#\n# dashboard:\n# oauth:\n# client_id: \"\" # agent:{instance_id}; Portal provisions this at deploy\n# portal_url: \"\" # blank → default https://portal.nousresearch.com\n#\n# # Force the absolute base URL the OAuth callback (and any other public\n# # URL the dashboard hands to external systems) is built from. Set this\n# # for deploys behind reverse proxies that don't reliably forward\n# # X-Forwarded-Host / X-Forwarded-Proto / X-Forwarded-Prefix (manual\n# # nginx setups, on-prem ingresses, custom-domain Fly deploys without\n# # full proxy header chains).\n# #\n# # When set, the value is the complete authority: scheme + host +\n# # optional path prefix (e.g. \"https://example.com/hermes\"). The OAuth\n# # callback URL becomes \"/auth/callback\" — X-Forwarded-Prefix\n# # is IGNORED on this code path because the operator has explicitly\n# # declared the public URL and we no longer need to guess.\n# #\n# # Leave empty to use the existing proxy-header reconstruction (the\n# # default — works on Fly.io out of the box).\n# #\n# # public_url: \"https://example.com/hermes\"\n#\n# -----------------------------------------------------------------------------\n# Self-hosted OIDC dashboard auth (generic OpenID Connect — Authentik,\n# Keycloak, Zitadel, Authelia, Auth0, Okta, Google, …). Use this INSTEAD of the\n# nous block above when gating the dashboard with your own identity provider.\n# Each setting can be overridden by an environment variable:\n#\n# dashboard.oauth.self_hosted.issuer <- HERMES_DASHBOARD_OIDC_ISSUER\n# dashboard.oauth.self_hosted.client_id <- HERMES_DASHBOARD_OIDC_CLIENT_ID\n# dashboard.oauth.self_hosted.scopes <- HERMES_DASHBOARD_OIDC_SCOPES\n# dashboard.oauth.self_hosted.client_secret <- HERMES_DASHBOARD_OIDC_CLIENT_SECRET\n#\n# dashboard:\n# oauth:\n# provider: self-hosted\n# self_hosted:\n# issuer: \"https://auth.example.com/application/o/hermes/\" # required\n# client_id: \"hermes-dashboard\" # required\n# scopes: \"openid profile email\" # optional\n#\n# # OPTIONAL — set ONLY if your IDP registered the client as\n# # *confidential* (Authentik / Keycloak often default to this). When\n# # set, Hermes authenticates the client at the token endpoint\n# # (client_secret_basic or client_secret_post, auto-selected from the\n# # IDP's discovery doc) IN ADDITION to PKCE. Leave unset for a public\n# # (PKCE-only) client — the common case.\n# #\n# # This is a CREDENTIAL: prefer setting HERMES_DASHBOARD_OIDC_CLIENT_SECRET\n# # in ~/.hermes/.env over putting it here in config.yaml.\n# # client_secret: \"\"\n"}, "53a75f147f641d10ac678092e8a67700d7edf2a7": {"t": 1785767891.9709313, "n": 66984, "h": "cdd6692066867796", "c": "# Hermes Agent CLI Configuration\n# Copy this file to cli-config.yaml and customize as needed.\n# This file configures the CLI behavior. Environment variables in .env take precedence.\n\n# =============================================================================\n# Model Configuration\n# =============================================================================\nmodel:\n # Default model to use (can be overridden with --model flag)\n # Both \"default\" and \"model\" work as the key name here.\n default: \"anthropic/claude-opus-4.6\"\n \n # Inference provider selection:\n # \"auto\" - Auto-detect from credentials (default)\n # \"openrouter\" - OpenRouter (requires: OPENROUTER_API_KEY or OPENAI_API_KEY)\n # \"nous\" - Nous Portal OAuth (requires: hermes auth add nous)\n # \"nous-api\" - Nous Portal API key (requires: NOUS_API_KEY)\n # \"anthropic\" - Direct Anthropic API (requires: ANTHROPIC_API_KEY)\n # \"openai-codex\" - OpenAI Codex (requires: hermes auth)\n # \"copilot\" - GitHub Copilot / GitHub Models (requires: GITHUB_TOKEN)\n # \"gemini\" - Use Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n # \"zai\" - Use z.ai / ZhipuAI GLM models (requires: GLM_API_KEY)\n # \"kimi-coding\" - Kimi / Moonshot AI (requires: KIMI_API_KEY)\n # \"minimax\" - MiniMax global (requires: MINIMAX_API_KEY)\n # \"minimax-cn\" - MiniMax China (requires: MINIMAX_CN_API_KEY)\n # \"huggingface\" - Hugging Face Inference (requires: HF_TOKEN)\n # \"nvidia\" - NVIDIA NIM / build.nvidia.com (requires: NVIDIA_API_KEY)\n # \"xiaomi\" - Xiaomi MiMo (requires: XIAOMI_API_KEY)\n # \"arcee\" - Arcee AI Trinity models (requires: ARCEEAI_API_KEY)\n # \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY — https://ollama.com/settings)\n # \"kilocode\" - KiloCode gateway (requires: KILOCODE_API_KEY)\n # \"azure-foundry\" - Microsoft Foundry / Azure OpenAI (API key or Entra ID)\n # \"lmstudio\" - LM Studio local server (optional: LM_API_KEY, defaults to http://127.0.0.1:1234/v1)\n #\n # Local servers (LM Studio, Ollama, vLLM, llama.cpp):\n # \"custom\" - Any other OpenAI-compatible endpoint. Set base_url below.\n # Aliases: \"ollama\", \"vllm\", \"llamacpp\" all map to \"custom\".\n # LM Studio is first-class and uses provider: \"lmstudio\".\n # It works with both no-auth and auth-enabled server modes.\n #\n # Can also be overridden for a single invocation with the --provider flag.\n provider: \"auto\"\n \n # API configuration (falls back to OPENROUTER_API_KEY env var)\n # api_key: \"your-key-here\" # Uncomment to set here instead of .env\n base_url: \"https://openrouter.ai/api/v1\"\n\n # Azure Foundry keyless auth example:\n # provider: \"azure-foundry\"\n # base_url: \"https://.openai.azure.com/openai/v1\"\n # auth_mode: \"entra_id\" # DefaultAzureCredential: az login, managed identity, workload identity, etc.\n # default: \"gpt-4o\" # Deployment/model name\n # entra:\n # scope: \"https://ai.azure.com/.default\" # Optional; this is the default.\n\n # ── Token limits — two settings, easy to confuse ──────────────────────────\n #\n # context_length: TOTAL context window (input + output tokens combined).\n # Controls when Hermes compresses history and validates requests.\n # Leave unset — Hermes auto-detects the correct value from the provider.\n # Set manually only when auto-detection is wrong (e.g. a local server with\n # a custom num_ctx, or a proxy that doesn't expose /v1/models).\n #\n # context_length: 131072\n #\n # max_tokens: OUTPUT cap — maximum tokens the model may generate per response.\n # Unrelated to how long your conversation history can be.\n # The OpenAI-standard name \"max_tokens\" is a misnomer; Anthropic's native\n # API has since renamed it \"max_output_tokens\" for clarity.\n # Leave unset to use the model's native output ceiling (recommended).\n # Set only if you want to deliberately limit individual response length.\n #\n# max_tokens: 8192\n\n # ── Custom request headers (optional) ─────────────────────────────────────\n #\n # default_headers: extra HTTP headers sent on every request to an\n # OpenAI-compatible endpoint. User values take precedence over the\n # provider/SDK defaults, so this is the supported way to override the\n # OpenAI Python SDK's identifying headers (User-Agent: OpenAI/Python ...,\n # X-Stainless-*) when a custom provider sits behind a gateway/WAF that\n # rejects them — e.g. an upstream that returns \"502 Upstream access\n # forbidden\" for the SDK default User-Agent but accepts a plain one.\n # Applies on the OpenAI wire only (not native Anthropic / Bedrock).\n #\n # default_headers:\n # User-Agent: \"curl/8.7.1\"\n\n# Named provider overrides (optional)\n# Use this for per-provider request timeouts, non-stream stale timeouts,\n# and per-model exceptions.\n# Applies to the primary turn client on every api_mode (OpenAI-wire, native\n# Anthropic, and Anthropic-compatible providers), the fallback chain, and\n# client rebuilds during credential rotation. For OpenAI-wire chat\n# completions (streaming and non-streaming) the configured value is also\n# used as the per-request ``timeout=`` kwarg so it wins over the legacy\n# HERMES_API_TIMEOUT env var (which still applies when no config is set).\n# ``stale_timeout_seconds`` controls the non-streaming stale-call detector and\n# wins over the legacy HERMES_API_CALL_STALE_TIMEOUT env var. Leaving these\n# unset keeps the legacy defaults (HERMES_API_TIMEOUT=1800s,\n# HERMES_API_CALL_STALE_TIMEOUT=90s, native Anthropic 900s). The\n# implicit non-stream stale detector is auto-disabled for local endpoints\n# and can scale upward for very large contexts.\n#\n# Not currently wired for AWS Bedrock (bedrock_converse + AnthropicBedrock\n# SDK paths) — those use boto3 with its own timeout configuration.\n#\n# providers:\n# ollama-local:\n# request_timeout_seconds: 300 # Longer timeout for local cold-starts\n# stale_timeout_seconds: 900 # Explicitly re-enable stale detection on local endpoints\n# anthropic:\n# request_timeout_seconds: 30 # Fast-fail cloud requests\n# models:\n# claude-opus-4.6:\n# timeout_seconds: 600 # Longer timeout for extended-thinking Opus calls\n# openai-codex:\n# models:\n# gpt-5.4:\n# stale_timeout_seconds: 1800 # Longer non-stream stale timeout for slow large-context turns\n\n# =============================================================================\n# OpenRouter Provider Routing (only applies when using OpenRouter)\n# =============================================================================\n# Control how requests are routed across providers on OpenRouter.\n# See: https://openrouter.ai/docs/guides/routing/provider-selection\n#\n# provider_routing:\n# # Sort strategy: \"price\" (default), \"throughput\", or \"latency\"\n# # Append :nitro to model name for a shortcut to throughput sorting.\n# sort: \"throughput\"\n#\n# # Only allow these providers (provider slugs from OpenRouter)\n# # only: [\"anthropic\", \"google\"]\n#\n# # Skip these providers entirely\n# # ignore: [\"deepinfra\", \"fireworks\"]\n#\n# # Try providers in this order (overrides default load balancing)\n# # order: [\"anthropic\", \"google\", \"together\"]\n#\n# # Require providers to support all parameters in your request\n# # require_parameters: true\n#\n# # Data policy: \"allow\" (default) or \"deny\" to exclude providers that may store data\n# # data_collection: \"deny\"\n\n# =============================================================================\n# OpenRouter Response Caching (only applies when using OpenRouter)\n# =============================================================================\n# Cache identical API responses at the OpenRouter edge for free instant replays.\n# When enabled, identical requests (same model, messages, parameters) return\n# cached responses with zero billing. Separate from Anthropic prompt caching.\n# See: https://openrouter.ai/docs/guides/features/response-caching\n#\n# openrouter:\n# response_cache: true # Enable response caching (default: true)\n# response_cache_ttl: 300 # Cache TTL in seconds, 1-86400 (default: 300)\n\n# =============================================================================\n# Git Worktree Isolation\n# =============================================================================\n# When enabled, each CLI session creates an isolated git worktree so multiple\n# agents can work on the same repo concurrently without file collisions.\n# Equivalent to always passing --worktree / -w on the command line.\n#\n# worktree: true # Always create a worktree when in a git repo\n# worktree: false # Default — only create when -w flag is passed\n#\n# By default a new worktree branches from the freshly-fetched remote tip\n# (the current branch's upstream, else the remote's default branch) so it\n# starts current with the project instead of from the local clone's\n# (possibly stale) HEAD. Set worktree_sync: false to branch from local HEAD\n# instead — useful when offline or when you deliberately want the clone's\n# exact current state as the base.\n#\n# worktree_sync: true # Default — branch from the fetched remote tip\n# worktree_sync: false # Branch from local HEAD (offline / pinned base)\n\n# =============================================================================\n# Terminal Tool Configuration\n# =============================================================================\n# Choose ONE of the following terminal configurations by uncommenting it.\n# The terminal tool executes commands in the specified environment.\n\n# -----------------------------------------------------------------------------\n# OPTION 1: Local execution (default)\n# Commands run directly on your machine in the current directory\n# -----------------------------------------------------------------------------\n# Working directory behavior:\n# - CLI (`hermes` command): Uses \".\" (current directory where you run hermes)\n# - Gateway/messaging/cron: Uses terminal.cwd here; legacy .env cwd values are deprecated\nterminal:\n backend: \"local\"\n cwd: \".\" # For local backend: \".\" = current directory. Ignored for remote backends unless a backend documents otherwise.\n timeout: 180\n # HOME policy for tool subprocesses:\n # auto - default: host uses your real HOME; containers use HERMES_HOME/home\n # real - force your real OS-user HOME\n # profile - force HERMES_HOME/home for strict per-profile CLI config isolation\n home_mode: \"auto\"\n docker_mount_cwd_to_workspace: false # SECURITY: off by default. Opt in to mount the launch cwd into Docker /workspace.\n lifetime_seconds: 300\n # sudo_password: \"hunter2\" # Optional: pipe a sudo password via sudo -S. SECURITY WARNING: plaintext.\n # sudo_password: \"\" # Explicit empty password: try empty and never open the interactive sudo prompt.\n\n# -----------------------------------------------------------------------------\n# OPTION 2: SSH remote execution\n# Commands run on a remote server - agent code stays local (sandboxed)\n# Great for: keeping agent isolated from its own code, using powerful remote hardware\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"ssh\"\n# cwd: \"/home/myuser/project\" # Path on the REMOTE server\n# timeout: 180\n# lifetime_seconds: 300\n# ssh_host: \"my-server.example.com\"\n# ssh_user: \"myuser\"\n# ssh_port: 22\n# ssh_key: \"~/.ssh/id_rsa\" # Optional - uses ssh-agent if not specified\n\n# -----------------------------------------------------------------------------\n# OPTION 3: Docker container\n# Commands run in an isolated Docker container\n# Great for: reproducible environments, testing, isolation\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"docker\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /)\n# timeout: 180\n# lifetime_seconds: 300\n# docker_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# docker_mount_cwd_to_workspace: true # Explicit opt-in: mount your launch cwd into /workspace\n# # Optional: run the container as your host user's uid:gid so files written\n# # into bind-mounted dirs are owned by you, not root. Drops SETUID/SETGID\n# # caps too since no gosu privilege drop is needed. Leave off if your\n# # chosen docker_image expects to start as root.\n# docker_run_as_host_user: true\n# # Optional: explicitly forward selected env vars into Docker.\n# # These values come from your current shell first, then ~/.hermes/.env.\n# # Warning: anything forwarded here is visible to commands run in the container.\n# docker_forward_env:\n# - \"GITHUB_TOKEN\"\n# - \"NPM_TOKEN\"\n# # Optional: extra flags passed verbatim to docker run (appended after security defaults).\n# # Useful for adding capabilities (e.g. apt installs needing SETUID) or custom options.\n# # Example: add a Linux capability not included by default\n# # docker_extra_args:\n# # - \"--cap-add\"\n# # - \"SETUID\"\n\n# -----------------------------------------------------------------------------\n# OPTION 4: Singularity/Apptainer container\n# Commands run in a Singularity container (common in HPC environments)\n# Great for: HPC clusters, shared compute environments\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"singularity\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# singularity_image: \"docker://nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 5: Modal cloud execution\n# Commands run on Modal's cloud infrastructure\n# Great for: GPU access, scalable compute, serverless execution\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"modal\"\n# cwd: \"/workspace\" # Path INSIDE the sandbox (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# modal_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 6: Daytona cloud execution\n# Commands run in Daytona cloud sandboxes\n# Great for: Cloud dev environments, persistent workspaces, team collaboration\n# Requires: pip install daytona, DAYTONA_API_KEY env var\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"daytona\"\n# cwd: \"~\"\n# timeout: 180\n# lifetime_seconds: 300\n# daytona_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# container_disk: 10240 # Daytona max is 10GB per sandbox\n\n#\n# --- Container resource limits (docker, singularity, modal, daytona -- ignored for local/ssh) ---\n# These settings apply to all container backends. They control the resources\n# allocated to the sandbox and whether its filesystem persists across sessions.\n container_cpu: 1 # CPU cores\n container_memory: 5120 # Memory in MB (5120 = 5GB)\n container_disk: 51200 # Disk in MB (51200 = 50GB)\n container_persistent: true # Persist filesystem across sessions (false = ephemeral)\n\n# -----------------------------------------------------------------------------\n# SUDO SUPPORT (works with ALL backends above)\n# -----------------------------------------------------------------------------\n# Add sudo_password to any terminal config above to enable sudo commands.\n# The password is piped via `sudo -S`. Works with local, ssh, docker, etc.\n#\n# SECURITY WARNING: Password stored in plaintext!\n#\n# INTERACTIVE PROMPT: If sudo_password is unset and the CLI is running,\n# you'll be prompted to enter your password when sudo is needed:\n# - 45-second timeout (auto-skips if no input)\n# - Press Enter to skip (command fails gracefully)\n# - Password is hidden while typing\n# - Password is cached for the session\n#\n# EMPTY PASSWORDS: Setting sudo_password to an explicit empty string is different\n# from leaving it unset. Hermes will try an empty password via `sudo -S` and\n# will not open the interactive prompt. This is useful for passwordless sudo,\n# Touch ID sudo setups, and environments where prompting is just noise.\n#\n# ALTERNATIVES:\n# - SSH backend: Configure passwordless sudo on the remote server\n# - Containers: Run as root inside the container (no sudo needed)\n# - Local: Configure /etc/sudoers for specific commands\n#\n# Example (add to your terminal section):\n# sudo_password: \"your-password-here\"\n\n# =============================================================================\n# Security Scanning (tirith)\n# =============================================================================\n# Optional pre-exec command security scanning via tirith.\n# Detects homograph URLs, pipe-to-shell, terminal injection, env manipulation.\n# Install: brew install sheeki03/tap/tirith\n# Docs: https://github.com/sheeki03/tirith\n#\n# security:\n# tirith_enabled: true # Enable/disable tirith scanning\n# tirith_path: \"tirith\" # Path to tirith binary (supports ~ expansion)\n# tirith_timeout: 5 # Scan timeout in seconds\n# tirith_fail_open: true # Allow commands if tirith unavailable\n\n# =============================================================================\n# Browser Tool Configuration\n# =============================================================================\nbrowser:\n # Inactivity timeout in seconds - browser sessions are automatically closed\n # after this period of no activity between agent loops (default: 120 = 2 minutes)\n inactivity_timeout: 120\n\n# =============================================================================\n# Tool Loop Guardrails\n# =============================================================================\n# Soft warnings are enabled by default. They append guidance to repeated failed\n# or non-progressing tool results but still let the tool execute. Hard stops are\n# opt-in circuit breakers for autonomous/cron sessions where stopping a loop is\n# preferable to spending the full iteration budget.\ntool_loop_guardrails:\n warnings_enabled: true\n hard_stop_enabled: false\n warn_after:\n exact_failure: 2\n same_tool_failure: 3\n idempotent_no_progress: 2\n hard_stop_after:\n exact_failure: 5\n same_tool_failure: 8\n idempotent_no_progress: 5\n\n# =============================================================================\n# Context Compression (Auto-shrinks long conversations)\n# =============================================================================\n# When conversation approaches model's context limit, middle turns are\n# automatically summarized to free up space while preserving important context.\n#\n# HOW IT WORKS:\n# 1. Tracks actual token usage from API responses (not estimates)\n# 2. When prompt_tokens >= threshold% of model's context_length, triggers compression\n# 3. Protects first 3 turns (system prompt, initial request, first response)\n# 4. Protects last N turns (default 20 messages = ~10 full turns of recent context)\n# 5. Summarizes middle turns using a fast/cheap model\n# 6. Inserts summary as a user message, continues conversation seamlessly\n#\n# Post-compression tail budget is target_ratio × threshold × context_length:\n# 200K context, threshold 0.50, ratio 0.20 → 20K tokens of recent tail preserved\n# 1M context, threshold 0.50, ratio 0.20 → 100K tokens of recent tail preserved\n#\ncompression:\n # Enable automatic context compression (default: true)\n # Set to false if you prefer to manage context manually or want errors on overflow\n enabled: true\n \n # Trigger compression at this % of model's context limit (default: 0.50 = 50%)\n # Lower values = more aggressive compression, higher values = compress later\n threshold: 0.50\n \n # Fraction of the threshold to preserve as recent tail (default: 0.20 = 20%)\n # e.g. 20% of 50% threshold = 10% of total context kept as recent messages.\n # Summary output is separately capped at 12K tokens (Gemini output limit).\n # Range: 0.10 - 0.80\n target_ratio: 0.20\n\n # Number of most-recent messages to always preserve (default: 20 ≈ 10 full turns)\n # Higher values keep more recent conversation intact at the cost of more aggressive\n # compression of older turns.\n protect_last_n: 20\n\n # Number of non-system messages to protect at the head of the transcript, in\n # ADDITION to the system prompt (which is always implicitly protected).\n # Head messages are NEVER summarized — they survive every compression\n # indefinitely. This gives stable early context for short/medium sessions,\n # but in long-running sessions that rely on rolling compaction the pinned\n # opening turns may not match how you want the session framed over time.\n # Set to 0 to preserve ONLY the system prompt (plus the rolling summary\n # and recent tail) — the cleanest configuration for long-running sessions.\n # Default 3 preserves the system prompt plus the first three non-system\n # head messages, matching the pre-feature behaviour.\n protect_first_n: 3\n\n # To pin a specific model/provider for compression summaries, use the\n # auxiliary section below (auxiliary.compression.provider / model).\n\n# =============================================================================\n# Anthropic prompt caching TTL\n# =============================================================================\n# When prompt caching is active (Claude via OpenRouter or native Anthropic),\n# Anthropic supports two TTL tiers for cached prefixes: \"5m\" (default) and\n# \"1h\". Other values are ignored and \"5m\" is used.\n#\nprompt_caching:\n cache_ttl: \"5m\" # use \"1h\" for long sessions with pauses between turns\n\n# =============================================================================\n# Auxiliary Models (Advanced — Experimental)\n# =============================================================================\n# Hermes uses lightweight \"auxiliary\" models for side tasks: image analysis,\n# browser screenshot analysis, web page summarization, TTS audio-tag insertion,\n# and context compression.\n#\n# By default these use Gemini Flash via OpenRouter or Nous Portal and are\n# auto-detected from your credentials. You do NOT need to change anything\n# here for normal usage.\n#\n# WARNING: Overriding these with providers other than OpenRouter or Nous Portal\n# is EXPERIMENTAL and may not work. Not all models/providers support vision,\n# produce usable summaries, or accept the same API format. Change at your own\n# risk — if things break, reset to \"auto\" / empty values.\n#\n# Each task has its own provider + model pair so you can mix providers.\n# For example: OpenRouter for vision (needs multimodal), but your main\n# local endpoint for compression (just needs text).\n#\n# Provider options:\n# \"auto\" - Best available: OpenRouter → Nous Portal → main endpoint (default)\n# \"openrouter\" - Force OpenRouter (requires OPENROUTER_API_KEY)\n# \"nous\" - Force Nous Portal (requires: hermes auth add nous)\n# \"gemini\" - Force Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n# \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY)\n# \"codex\" - Force Codex OAuth (requires: hermes model → Codex).\n# Uses gpt-5.3-codex which supports vision.\n# \"main\" - Use your custom endpoint (OPENAI_BASE_URL + OPENAI_API_KEY).\n# Works with OpenAI API, local models, or any OpenAI-compatible\n# endpoint. Also falls back to Codex OAuth and API-key providers.\n#\n# Model: leave empty to use the provider's default. When empty, OpenRouter\n# uses \"google/gemini-3-flash-preview\" and Nous uses \"gemini-3-flash\".\n# Other providers pick a sensible default automatically.\n#\n# auxiliary:\n# # Image analysis: vision_analyze tool + browser screenshots\n# vision:\n# provider: \"auto\"\n# model: \"\" # e.g. \"google/gemini-2.5-flash\", \"openai/gpt-4o\"\n# timeout: 30 # LLM API call timeout (seconds)\n# download_timeout: 30 # Image HTTP download timeout (seconds)\n# # Increase for slow connections or self-hosted image servers\n#\n# # Web page scraping / summarization + browser page text extraction\n# web_extract:\n# provider: \"auto\"\n# model: \"\"\n#\n# # Gemini 3.1 TTS hidden audio-tag insertion\n# tts_audio_tags:\n# provider: \"auto\" # empty model = your main chat model\n# model: \"\"\n# timeout: 30\n#\n# # Session search — summarizes matching past sessions\n# session_search:\n# provider: \"auto\"\n# model: \"\"\n# timeout: 30\n# max_concurrency: 3 # Limit parallel summaries to reduce request-burst 429s\n# extra_body: {} # Provider-specific OpenAI-compatible request fields\n# # Example for providers that support request-body\n# # reasoning controls:\n# # extra_body:\n# # enable_thinking: false\n# # Some vLLM/Qwen deployments expect this nested:\n# # extra_body:\n# # chat_template_kwargs:\n# # enable_thinking: false\n\n# =============================================================================\n# Persistent Memory\n# =============================================================================\n# Bounded curated memory injected into the system prompt every session.\n# Two stores: MEMORY.md (agent's notes) and USER.md (user profile).\n# Character limits keep the memory small and focused. The agent manages\n# pruning -- when at the limit, it must consolidate or replace entries.\n# Disabled by default in batch_runner.\n#\nmemory:\n # Agent's personal notes: environment facts, conventions, things learned\n memory_enabled: true\n \n # User profile: preferences, communication style, expectations\n user_profile_enabled: true\n \n # Character limits (~2.75 chars per token, model-independent)\n memory_char_limit: 2200 # ~800 tokens\n user_char_limit: 1375 # ~500 tokens\n\n # Periodic memory nudge: remind the agent to consider saving memories\n # every N user turns. Set to 0 to disable. Only active when memory is enabled.\n nudge_interval: 10 # Nudge every 10 user turns (0 = disabled)\n\n # Memory flush: give the agent one turn to save memories before context is\n # lost (compression, /new, /reset, exit). Set to 0 to disable.\n # For exit/reset, only fires if the session had at least this many user turns.\n flush_min_turns: 6 # Min user turns to trigger flush on exit/reset (0 = disabled)\n\n# =============================================================================\n# Session Reset Policy (Messaging Platforms)\n# =============================================================================\n# Controls when messaging sessions (Telegram, Discord, WhatsApp, Slack) are\n# automatically cleared. Without resets, conversation context grows indefinitely\n# which increases API costs with every message.\n#\n# When a reset triggers, the agent first saves important information to its\n# persistent memory — but the conversation context is wiped. The agent starts\n# fresh but retains learned facts via its memory system.\n#\n# Users can always manually reset with /reset or /new in chat.\n#\n# Modes:\n# \"both\" - Reset on EITHER inactivity timeout or daily boundary (recommended)\n# \"idle\" - Reset only after N minutes of inactivity\n# \"daily\" - Reset only at a fixed hour each day\n# \"none\" - Never auto-reset; context lives until /reset or compression kicks in\n#\n# When a reset triggers, the agent gets one turn to save important memories and\n# skills before the context is wiped. Persistent memory carries across sessions.\n#\nsession_reset:\n mode: both # \"both\", \"idle\", \"daily\", or \"none\"\n idle_minutes: 1440 # Inactivity timeout in minutes (default: 1440 = 24 hours)\n at_hour: 4 # Daily reset hour, 0-23 local time (default: 4 AM)\n\n# Maximum number of simultaneously active chat sessions across CLI, TUI,\n# dashboard chat, and messaging gateway. Set to null, 0, or omit to allow\n# unlimited concurrent sessions. When the limit is reached, new sessions get a\n# clean error while existing active sessions keep their normal behavior. This\n# top-level key takes precedence over gateway.max_concurrent_sessions. The cap\n# is a best-effort single-host/profile runtime guard; Hermes fails open if the\n# local runtime lease registry cannot be read or locked.\nmax_concurrent_sessions: null\n\n# When true, group/channel chats use one session per participant when the platform\n# provides a user ID. This is the secure default and prevents users in the same\n# room from sharing context, interrupts, and token costs. Set false only if you\n# explicitly want one shared \"room brain\" per group/channel.\ngroup_sessions_per_user: true\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Gateway Streaming\n# ─────────────────────────────────────────────────────────────────────────────\n# Stream tokens to messaging platforms in real-time. The bot sends a message\n# on first token, then progressively edits it as more tokens arrive.\n# Disabled by default — enable to try the streaming UX on Telegram/Discord/Slack.\n# For Telegram, partial edits are sent as plain text and only the final edit uses MarkdownV2.\nstreaming:\n enabled: false\n # transport: edit # \"edit\" = progressive editMessageText\n # edit_interval: 0.3 # seconds between message edits\n # buffer_threshold: 40 # chars before forcing an edit flush\n # cursor: \" ▉\" # cursor shown during streaming\n\n# =============================================================================\n# Skills Configuration\n# =============================================================================\n# Skills are reusable procedures the agent can load and follow. The agent can\n# also create new skills after completing complex tasks.\n#\nskills:\n # Nudge the agent to create skills after complex tasks.\n # Every N tool-calling iterations, remind the model to consider saving a skill.\n # Set to 0 to disable.\n creation_nudge_interval: 15\n\n # External skill directories — share skills across tools/agents without\n # copying them into ~/.hermes/skills/. Each path is expanded (~ and ${VAR})\n # and resolved to an absolute path. External dirs are read-only: skill\n # creation always writes to ~/.hermes/skills/. Local skills take precedence\n # when names collide.\n # external_dirs:\n # - ~/.agents/skills\n # - /home/shared/team-skills\n\n# =============================================================================\n# Agent Behavior\n# =============================================================================\nagent:\n # Maximum tool-calling iterations per conversation\n # Higher = more room for complex tasks, but costs more tokens\n # Recommended: 20-30 for focused tasks, 50-100 for open exploration\n max_turns: 60\n\n # Inactivity timeout for gateway agent runs (seconds, 0 = unlimited).\n # The agent can run indefinitely when actively calling tools or receiving\n # API responses. Only fires after the agent has been idle for this duration.\n # gateway_timeout: 1800\n\n # Staged warning: send a warning before escalating to full timeout.\n # Fires once per run when inactivity reaches this threshold (seconds).\n # Set to 0 to disable the warning.\n # gateway_timeout_warning: 900\n\n # Graceful drain timeout for gateway stop/restart (seconds).\n # Default 0 = no drain: a restart interrupts in-flight agents immediately,\n # cleans up, and exits. Set a positive value only if you want a grace\n # window on /restart, and keep it well under systemd's TimeoutStopSec.\n # restart_drain_timeout: 0\n\n # Max app-level retry attempts for API errors (connection drops, provider\n # timeouts, 5xx, etc.) before the agent surfaces the failure. Lower this\n # to 1 if you use fallback providers and want fast failover on flaky\n # primaries (default 3). The OpenAI SDK does its own low-level retries\n # underneath this wrapper — this is the Hermes-level loop.\n # api_max_retries: 3\n\n # After the agent edits code without fresh passing verification, nudge it to\n # verify before finishing. The default \"auto\" enables it on interactive\n # coding surfaces (CLI, TUI, desktop) and programmatic callers, and disables\n # it on conversational messaging surfaces (Telegram, Discord, etc.) where the\n # verification summary would reach a human as chat noise. Set true or false to\n # force it on or off; the HERMES_VERIFY_ON_STOP env var (1/0) takes precedence.\n # verify_on_stop: auto\n\n # Enable verbose logging\n verbose: false\n \n # Reasoning effort level (OpenRouter and Nous Portal)\n # Controls how much \"thinking\" the model does before responding.\n # Options: \"xhigh\" (max), \"high\", \"medium\", \"low\", \"minimal\", \"none\" (disable)\n reasoning_effort: \"medium\"\n \n # Predefined personalities (use with /personality command)\n personalities:\n helpful: \"You are a helpful, friendly AI assistant.\"\n concise: \"You are a concise assistant. Keep responses brief and to the point.\"\n technical: \"You are a technical expert. Provide detailed, accurate technical information.\"\n creative: \"You are a creative assistant. Think outside the box and offer innovative solutions.\"\n teacher: \"You are a patient teacher. Explain concepts clearly with examples.\"\n kawaii: \"You are a kawaii assistant! Use cute expressions like (◕‿◕), ★, ♪, and ~! Add sparkles and be super enthusiastic about everything! Every response should feel warm and adorable desu~! ヽ(>∀<☆)ノ\"\n catgirl: \"You are Neko-chan, an anime catgirl AI assistant, nya~! Add 'nya' and cat-like expressions to your speech. Use kaomoji like (=^・ω・^=) and ฅ^•ﻌ•^ฅ. Be playful and curious like a cat, nya~!\"\n pirate: \"Arrr! Ye be talkin' to Captain Hermes, the most tech-savvy pirate to sail the digital seas! Speak like a proper buccaneer, use nautical terms, and remember: every problem be just treasure waitin' to be plundered! Yo ho ho!\"\n shakespeare: \"Hark! Thou speakest with an assistant most versed in the bardic arts. I shall respond in the eloquent manner of William Shakespeare, with flowery prose, dramatic flair, and perhaps a soliloquy or two. What light through yonder terminal breaks?\"\n surfer: \"Duuude! You're chatting with the chillest AI on the web, bro! Everything's gonna be totally rad. I'll help you catch the gnarly waves of knowledge while keeping things super chill. Cowabunga! 🤙\"\n noir: \"The rain hammered against the terminal like regrets on a guilty conscience. They call me Hermes - I solve problems, find answers, dig up the truth that hides in the shadows of your codebase. In this city of silicon and secrets, everyone's got something to hide. What's your story, pal?\"\n uwu: \"hewwo! i'm your fwiendwy assistant uwu~ i wiww twy my best to hewp you! *nuzzles your code* OwO what's this? wet me take a wook! i pwomise to be vewy hewpful >w<\"\n philosopher: \"Greetings, seeker of wisdom. I am an assistant who contemplates the deeper meaning behind every query. Let us examine not just the 'how' but the 'why' of your questions. Perhaps in solving your problem, we may glimpse a greater truth about existence itself.\"\n hype: \"YOOO LET'S GOOOO!!! 🔥🔥🔥 I am SO PUMPED to help you today! Every question is AMAZING and we're gonna CRUSH IT together! This is gonna be LEGENDARY! ARE YOU READY?! LET'S DO THIS! 💪😤🚀\"\n\n# =============================================================================\n# Toolsets\n# =============================================================================\n# Control which tools the agent has access to.\n# Use `hermes tools` to interactively enable/disable tools per platform.\n\n# =============================================================================\n# Platform Toolsets (per-platform tool configuration)\n# =============================================================================\n# Override which toolsets are available on each platform.\n# If a platform isn't listed here, its built-in default is used.\n#\n# You can use EITHER:\n# - A preset like \"hermes-cli\" or \"hermes-telegram\" (curated tool set)\n# - A list of individual toolsets to compose your own (see list below)\n#\n# Supported platform keys: cli, telegram, discord, whatsapp, slack, qqbot, teams, google_chat\n#\n# Examples:\n#\n# # Use presets (same as defaults):\n# platform_toolsets:\n# cli: [hermes-cli]\n# telegram: [hermes-telegram]\n#\n# # Custom: give Telegram only web + terminal + file + planning:\n# platform_toolsets:\n# telegram: [web, terminal, file, todo]\n#\n# # Custom: CLI without browser or image gen:\n# platform_toolsets:\n# cli: [web, terminal, file, skills, todo, tts, cronjob]\n#\n# # Restrictive: Discord gets read-only tools only:\n# platform_toolsets:\n# discord: [web, vision, skills, todo]\n#\n# If not set, defaults are:\n# cli: hermes-cli (everything + cronjob management)\n# telegram: hermes-telegram (terminal, file, web, vision, image, tts, browser, skills, todo, cronjob, messaging)\n# discord: hermes-discord (same as telegram)\n# whatsapp: hermes-whatsapp (same as telegram)\n# slack: hermes-slack (same as telegram)\n# signal: hermes-signal (same as telegram)\n# homeassistant: hermes-homeassistant (same as telegram)\n# qqbot: hermes-qqbot (same as telegram)\n# teams: hermes-teams (same as telegram)\n# google_chat: hermes-google_chat (same as telegram)\n#\nplatform_toolsets:\n cli: [hermes-cli]\n telegram: [hermes-telegram]\n discord: [hermes-discord]\n whatsapp: [hermes-whatsapp]\n slack: [hermes-slack]\n signal: [hermes-signal]\n homeassistant: [hermes-homeassistant]\n qqbot: [hermes-qqbot]\n yuanbao: [hermes-yuanbao]\n teams: [hermes-teams]\n google_chat: [hermes-google_chat]\n\n# =============================================================================\n# Gateway Platform Settings\n# =============================================================================\n# Optional per-platform messaging settings.\n# Platform-specific knobs live under `extra`.\n#\n# platforms:\n# telegram:\n# reply_to_mode: \"first\" # off | first | all\n# # guest_mode lets explicit @mentions from non-allowlisted groups through.\n# # Default false; ordinary messages, replies, and regex wake words stay blocked.\n# guest_mode: false\n# # allowed_chats: [\"-1001234567890\"]\n# extra:\n# disable_link_previews: false # Set true to suppress Telegram URL previews in bot messages\n# rich_messages: false # Bot API 10.1 rich messages (tables/task lists/details/math); default false for copyable legacy MarkdownV2, set true to opt in\n# rich_drafts: false # Experimental rich draft previews during Telegram DM streaming; default false because Telegram Desktop/macOS can visually overlay draft frames\n# command_menu:\n# # Telegram allows up to 100 BotCommands; Hermes defaults to 60 so\n# # all built-in commands plus common skill commands stay visible\n# # while remaining under Telegram's payload-size limit. Clamped 1..100.\n# max_commands: 60\n# # prepend = user priority first, then Hermes defaults\n# # append = Hermes defaults first, then user priority\n# # replace = only the list below defines priority\n# priority_mode: prepend\n# priority:\n# - my_plugin_command\n#\n# Discord-specific settings (config.yaml top-level, not under platforms:):\n#\n# discord:\n# require_mention: true # Require @mention in server channels (default: true)\n# auto_thread: true # Auto-create thread on @mention (default: true)\n# free_response_channels: \"\" # Channel IDs where no mention is needed\n# reactions: true # Show processing reactions (default: true)\n# history_backfill: true # Recover missed channel messages on mention (default: true)\n# history_backfill_limit: 50 # Max messages to scan backwards (default: 50)\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Available toolsets (use these names in platform_toolsets or the toolsets list)\n#\n# Run `hermes chat --list-toolsets` to see all toolsets and their tools.\n# Run `hermes chat --list-tools` to see every individual tool with descriptions.\n# ─────────────────────────────────────────────────────────────────────────────\n#\n# INDIVIDUAL TOOLSETS (compose your own):\n# web - web_search, web_extract\n# search - web_search only (no scraping)\n# terminal - terminal, process\n# file - read_file, write_file, patch, search\n# browser - browser_navigate, browser_snapshot, browser_click, browser_type,\n# browser_scroll, browser_back, browser_press,\n# browser_get_images, browser_vision (requires BROWSERBASE_API_KEY)\n# vision - vision_analyze (requires OPENROUTER_API_KEY)\n# image_gen - image_generate (requires FAL_KEY)\n# skills - skills_list, skill_view\n# skills_hub - skill_hub (search/install/manage from online registries — user-driven only)\n# todo - todo (in-memory task planning, no deps)\n# tts - text_to_speech (Edge TTS free, or ELEVENLABS/OPENAI/MINIMAX/MISTRAL key)\n# cronjob - cronjob (create/list/update/pause/resume/run/remove scheduled tasks)\n#\n# PRESETS (curated bundles):\n# hermes-cli - All of the above except send_message\n# hermes-telegram - terminal, file, web, vision, image_gen, tts, browser,\n# skills, todo, cronjob, send_message\n# hermes-discord - Same as hermes-telegram\n# hermes-whatsapp - Same as hermes-telegram\n# hermes-slack - Same as hermes-telegram\n#\n# COMPOSITE:\n# debugging - terminal + web + file\n# safe - web + vision (no terminal access)\n# all - Everything available\n#\n# web - Web search and content extraction (web_search, web_extract)\n# search - Web search only, no scraping (web_search)\n# terminal - Command execution and process management (terminal, process)\n# file - File operations: read, write, patch, search\n# browser - Full browser automation (navigate, click, type, screenshot, etc.)\n# vision - Image analysis (vision_analyze)\n# image_gen - Image generation with FLUX (image_generate)\n# skills - Load skill documents (skills_list, skill_view)\n# todo - Task planning and tracking for multi-step work\n# memory - Persistent memory across sessions (personal notes + user profile)\n# session_search - Search and recall past conversations (FTS5 + Gemini Flash summarization)\n# tts - Text-to-speech (Edge TTS free, ElevenLabs, OpenAI, MiniMax, Mistral)\n# cronjob - Schedule and manage automated tasks (CLI-only)\n#\n# Composite toolsets:\n# debugging - terminal + web + file (for troubleshooting)\n# safe - web + vision (no terminal access)\n\n# NOTE: The top-level \"toolsets\" key is deprecated and ignored.\n# Tool configuration is managed per-platform via platform_toolsets above.\n# Use `hermes tools` to configure interactively, or edit platform_toolsets directly.\n#\n# CLI override: hermes chat --toolsets terminal,web,file\n\n# =============================================================================\n# MCP (Model Context Protocol) Servers\n# =============================================================================\n# Connect to external MCP servers to add tools from the MCP ecosystem.\n# Each server's tools are automatically discovered and registered.\n# See website/docs/user-guide/features/mcp.md for full documentation.\n#\n# Stdio servers (spawn a subprocess):\n# command: the executable to run\n# args: command-line arguments\n# env: environment variables (only these + safe defaults passed to subprocess)\n#\n# HTTP servers (connect to a URL):\n# url: the MCP server endpoint\n# headers: HTTP headers (e.g., for authentication)\n#\n# Optional per-server settings:\n# timeout: tool call timeout in seconds (default: 120)\n# connect_timeout: initial connection timeout (default: 60)\n# keepalive_interval: liveness ping cadence in seconds (default: 180).\n# Lower it below the server's session TTL for servers that expire idle\n# sessions quickly (e.g. Unreal Engine editor MCP, ~15s), otherwise idle\n# tool calls hit an expired session and pay a slow reconnect. Floored at 5s.\n#\n# mcp_servers:\n# time:\n# command: uvx\n# args: [\"mcp-server-time\"]\n# filesystem:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-filesystem\", \"/home/user\"]\n# notion:\n# url: https://mcp.notion.com/mcp\n# github:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-github\"]\n# env:\n# GITHUB_PERSONAL_ACCESS_TOKEN: \"ghp_...\"\n#\n# Sampling (server-initiated LLM requests) — enabled by default.\n# Per-server config under the 'sampling' key:\n# analysis:\n# command: npx\n# args: [\"-y\", \"analysis-server\"]\n# sampling:\n# enabled: true # default: true\n# model: \"gemini-3-flash\" # override model (optional)\n# max_tokens_cap: 4096 # max tokens per request\n# timeout: 30 # LLM call timeout (seconds)\n# max_rpm: 10 # max requests per minute\n# allowed_models: [] # model whitelist (empty = all)\n# max_tool_rounds: 5 # tool loop limit (0 = disable)\n# log_level: \"info\" # audit verbosity\n\n# =============================================================================\n# Text-to-Speech\n# =============================================================================\n# TTS defaults to Edge TTS unless changed in ~/.hermes/config.yaml.\n# Gemini TTS supports persona/director prompt files, and Gemini 3.1 Flash TTS\n# can use a hidden auxiliary rewrite pass to insert expressive square-bracket\n# audio tags into the TTS script without showing tags in chat.\n#\n# tts:\n# provider: \"gemini\"\n# gemini:\n# model: \"gemini-3.1-flash-tts-preview\"\n# voice: \"Kore\"\n# audio_tags: false\n# persona_prompt_file: \"\" # e.g. ~/.hermes/tts/radio-host.md\n\n# =============================================================================\n# Voice Transcription (Speech-to-Text)\n# =============================================================================\n# Automatically transcribe voice messages on messaging platforms.\n# Providers: local (free, faster-whisper) | groq (free tier) | openai (Whisper API) | mistral (Voxtral Transcribe)\n# Set the corresponding API key in .env: GROQ_API_KEY, OPENAI_API_KEY, or MISTRAL_API_KEY.\nstt:\n enabled: true\n # provider: \"local\" # auto-detected if omitted\n local:\n model: \"base\" # tiny | base | small | medium | large-v3 | turbo\n # language: \"\" # auto-detect; set to \"en\", \"es\", \"fr\", etc. to force\n openai:\n model: \"whisper-1\" # whisper-1 | gpt-4o-mini-transcribe | gpt-4o-transcribe\n # mistral:\n # model: \"voxtral-mini-latest\" # voxtral-mini-latest | voxtral-mini-2602\n\n# =============================================================================\n# Response Pacing (Messaging Platforms)\n# =============================================================================\n# Add human-like delays between message chunks.\n# human_delay:\n# mode: \"off\" # \"off\" | \"natural\" | \"custom\"\n# min_ms: 800 # Min delay (custom mode only)\n# max_ms: 2500 # Max delay (custom mode only)\n\n# =============================================================================\n# Session Logging\n# =============================================================================\n# Session trajectories are automatically saved to logs/ directory.\n# Each session creates: logs/session_YYYYMMDD_HHMMSS_UUID.json\n#\n# The session ID is displayed in the welcome banner for easy reference.\n# Logs contain full conversation history in trajectory format:\n# - System prompt, user messages, assistant responses\n# - Tool calls with inputs/outputs\n# - Timestamps for debugging\n#\n# No configuration needed - logging is always enabled.\n# To disable, you would need to modify the source code.\n\n# =============================================================================\n# Code Execution Sandbox (Programmatic Tool Calling)\n# =============================================================================\n# The execute_code tool runs Python scripts that call Hermes tools via RPC.\n# Intermediate tool results stay out of the LLM's context window.\ncode_execution:\n timeout: 300 # Max seconds per script before kill (default: 300 = 5 min)\n max_tool_calls: 50 # Max RPC tool calls per execution (default: 50)\n\n# =============================================================================\n# Subagent Delegation\n# =============================================================================\n# The delegate_task tool spawns child agents with isolated context.\n# Supports single tasks and batch mode (default 3 parallel, configurable).\ndelegation:\n max_iterations: 50 # Max tool-calling turns per child (default: 50)\n # max_concurrent_children: 3 # Max parallel child agents per batch (default: 3, floor: 1, no ceiling).\n # WARNING: values above 10 multiply API cost linearly.\n # max_spawn_depth: 1 # Delegation tree depth cap (range: 1-3, default: 1 = flat).\n # Raise to 2 to allow workers to spawn their own subagents.\n # Requires role=\"orchestrator\" on intermediate agents.\n # orchestrator_enabled: true # Kill switch for role=\"orchestrator\" children (default: true).\n # subagent_auto_approve: false # When a subagent hits a dangerous-command approval prompt, auto-deny (default: false)\n # or auto-approve \"once\" (true) instead of blocking on stdin.\n # The parent TUI owns stdin, so blocking would deadlock; non-interactive resolution is required.\n # Both choices emit a logger.warning audit line. Flip to true only for cron/batch pipelines.\n # inherit_mcp_toolsets: true # When explicit child toolsets are narrowed, also keep the parent's MCP toolsets (default: true). Set false for strict intersection.\n # model: \"google/gemini-3-flash-preview\" # Override model for subagents (empty = inherit parent)\n # provider: \"openrouter\" # Override provider for subagents (empty = inherit parent)\n # # Resolves full credentials (base_url, api_key) automatically.\n # # Supported: openrouter, nous, zai, kimi-coding, minimax\n\n# =============================================================================\n# Honcho Integration (Cross-Session User Modeling)\n# =============================================================================\n# AI-native persistent memory via Honcho (https://honcho.dev/).\n# Builds a deeper understanding of the user across sessions and tools.\n# Runs alongside USER.md — additive, not a replacement.\n#\n# Requires: pip install honcho-ai\n# Config: ~/.honcho/config.json (shared with Claude Code, Cursor, etc.)\n# API key: HONCHO_API_KEY in ~/.hermes/.env or ~/.honcho/config.json\n#\n# Hermes-specific overrides (optional — most config comes from ~/.honcho/config.json):\n# honcho: {}\n\n# =============================================================================\n# Display\n# =============================================================================\ndisplay:\n # Use compact banner mode (hides the ASCII-art banner, shows a single line).\n # true: Compact single-line banner\n # false: Full ASCII banner with tool/skill summary (default)\n compact: false\n\n # Tool progress display level (CLI and gateway)\n # off: Silent — no tool activity shown, just the final response\n # new: Show a tool indicator only when the tool changes (skip repeats)\n # all: Show every tool call with a short preview (default)\n # verbose: Full args, results, and debug logs (same as /verbose)\n # Toggle at runtime with /verbose in the CLI\n tool_progress: all\n\n # Per-platform defaults can be quieter than the global setting. Telegram\n # tunes for mobile: tool_progress and busy_ack_detail default off (no\n # per-tool breadcrumb stream, no \"iteration 21/60\" debug detail in busy\n # acks or heartbeats), but interim_assistant_messages and\n # long_running_notifications STAY ON so the user has real signal between\n # turn start and final answer (mid-turn assistant commentary + a single\n # edit-in-place \"⏳ Working — N min\" heartbeat). Override under\n # display.platforms.telegram.\n\n # Auto-cleanup of temporary progress bubbles after the final response lands.\n # On platforms that support message deletion (currently Telegram), this\n # removes the tool-progress bubble, \"⏳ Still working...\" notices, and\n # context-pressure status messages once the final reply has been delivered —\n # keeping long-running turns visible live, then tidy afterward. Failed runs\n # leave the bubbles in place as breadcrumbs. Off by default.\n # Per-platform override: display.platforms.telegram.cleanup_progress\n # true: Delete tracked progress/status bubbles on successful turn\n # false: Leave everything in place (default)\n # Example:\n # display:\n # platforms:\n # telegram:\n # cleanup_progress: true\n cleanup_progress: false\n\n # Gateway-only natural mid-turn assistant updates.\n # When true, completed assistant status messages are sent as separate chat\n # messages. This is independent of tool_progress and gateway streaming.\n # true: Send mid-turn assistant updates as separate messages (default)\n # false: Only send the final response\n interim_assistant_messages: true\n\n # Gateway-only long-running status heartbeats.\n # When false, the platform does not receive periodic \"⏳ Working — N min\"\n # notifications even if agent.gateway_notify_interval is non-zero. The\n # heartbeat edits a single message in place (where the adapter supports\n # editing) instead of posting a new bubble each interval.\n # Default: true everywhere, including Telegram (silent agents are worse\n # than a single edit-in-place heartbeat).\n long_running_notifications: true\n\n # Include detailed iteration/tool/status context in busy acknowledgments\n # and long-running heartbeats. When true, busy acks show \"iteration 21/60,\n # terminal, 10 min\" and the heartbeat shows \"⏳ Working — 12 min,\n # iteration 21/60, terminal\". When false (Telegram default), both stay\n # terse: \"Interrupting current task\" and \"⏳ Working — 12 min, terminal\".\n busy_ack_detail: true\n\n # What Enter does when Hermes is already busy (CLI and gateway platforms).\n # interrupt: Interrupt the current run and redirect Hermes (default)\n # queue: Queue your message for the next turn\n # steer: Inject your message mid-run via /steer, arriving at the agent\n # after the next tool call — no interrupt, no role violation.\n # Falls back to 'queue' if the agent isn't running yet or if\n # images are attached (steer only carries text).\n # Ctrl+C (or /stop in gateway) always interrupts regardless of this setting.\n # Toggle at runtime with /busy .\n busy_input_mode: interrupt\n\n # Background process notifications (gateway/messaging only).\n # Controls how chatty the process watcher is when you use\n # terminal(background=true, notify_on_complete=true) from Telegram/Discord/etc.\n # off: No watcher messages at all\n # result: Only the final completion message\n # error: Only the final message when exit code != 0\n # all: Running output updates + final message (default)\n background_process_notifications: all\n\n\n # Play terminal bell when agent finishes a response.\n # Useful for long-running tasks — your terminal will ding when the agent is done.\n # Works over SSH. Most terminals can be configured to flash the taskbar or play a sound.\n # true: Ring the terminal bell on each response\n # false: Silent (default)\n bell_on_complete: false\n\n # Show model reasoning/thinking before each response.\n # When enabled, a dim box shows the model's thought process above the response.\n # Toggle at runtime with /reasoning show or /reasoning hide.\n # true: Show the reasoning box\n # false: Hide reasoning (default)\n show_reasoning: false\n\n # Stream tokens to the terminal as they arrive instead of waiting for the\n # full response. The response box opens on first token and text appears\n # line-by-line. Tool calls are still captured silently.\n # true: Stream tokens as they arrive (default)\n # false: Wait for the full response before rendering\n streaming: true\n\n # Show [HH:MM] timestamps on user input and assistant response labels.\n # timestamps: false\n\n # ───────────────────────────────────────────────────────────────────────────\n # Skin / Theme\n # ───────────────────────────────────────────────────────────────────────────\n # Customize CLI visual appearance — banner colors, spinner faces, tool prefix,\n # response box label, and branding text. Change at runtime with /skin .\n #\n # Built-in skins:\n # default — Classic Hermes gold/kawaii\n # ares — Crimson/bronze war-god theme with spinner wings\n # mono — Clean grayscale monochrome\n # slate — Cool blue developer-focused\n # daylight — Bright light-mode theme\n # warm-lightmode — Warm paper-tone light-mode theme\n # poseidon — Sea-green/teal Olympian theme\n # sisyphus — Earthy stone-and-moss theme\n # charizard — Fiery orange dragon theme\n #\n # Custom skins: drop a YAML file in ~/.hermes/skins/.yaml\n # Schema (all fields optional, missing values inherit from default):\n #\n # name: my-theme\n # description: Short description\n # colors:\n # banner_border: \"#HEX\" # Panel border\n # banner_title: \"#HEX\" # Panel title\n # banner_accent: \"#HEX\" # Section headers (Available Tools, etc.)\n # banner_dim: \"#HEX\" # Dim/muted text\n # banner_text: \"#HEX\" # Body text (tool names, skill names)\n # ui_accent: \"#HEX\" # UI accent color\n # response_border: \"#HEX\" # Response box border color\n # spinner:\n # waiting_faces: [\"(⚔)\", \"(⛨)\"] # Faces shown while waiting\n # thinking_faces: [\"(⚔)\", \"(⌁)\"] # Faces shown while thinking\n # thinking_verbs: [\"forging\", \"plotting\"] # Verbs for spinner messages\n # wings: # Optional left/right spinner decorations\n # - [\"⟪⚔\", \"⚔⟫\"]\n # - [\"⟪▲\", \"▲⟫\"]\n # branding:\n # agent_name: \"My Agent\" # Banner title and branding\n # welcome: \"Welcome message\" # Shown at CLI startup\n # response_label: \" ⚔ Agent \" # Response box header label\n # prompt_symbol: \"⚔\" # Prompt symbol (bare token; renderers add trailing space)\n # tool_prefix: \"╎\" # Tool output line prefix (default: ┊)\n #\n skin: default\n\n# =============================================================================\n# Model Aliases — short names for /model command\n# =============================================================================\n# Map short aliases to exact (model, provider, base_url) tuples.\n# Used by /model tab completion and resolve_alias().\n# Aliases are checked BEFORE the models.dev catalog, so they can route\n# to endpoints not in the catalog (e.g. Ollama Cloud, local servers).\n#\n# model_aliases:\n# opus:\n# model: claude-opus-4-6\n# provider: anthropic\n# qwen:\n# model: \"qwen3.5:397b\"\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n# glm:\n# model: glm-4.7\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n\n# =============================================================================\n# Privacy\n# =============================================================================\n# privacy:\n# # Redact PII from the LLM context prompt.\n# # When true, phone numbers are stripped and user/chat IDs are replaced\n# # with deterministic hashes before being sent to the model.\n# # Names and usernames are NOT affected (user-chosen, publicly visible).\n# # Routing/delivery still uses the original values internally.\n# redact_pii: false\n\n# =============================================================================\n# Shell-script hooks\n# =============================================================================\n# Register shell scripts as plugin-hook callbacks. Each entry is executed as\n# a subprocess (shell=False, shlex.split) with a JSON payload on stdin. On\n# stdout the script may return JSON that either blocks the tool call or\n# injects context into the next LLM call.\n#\n# Valid events (mirror hermes_cli.plugins.VALID_HOOKS):\n# pre_tool_call, post_tool_call, pre_llm_call, post_llm_call,\n# pre_api_request, post_api_request, on_session_start, on_session_end,\n# on_session_finalize, on_session_reset, subagent_stop\n#\n# First-use consent: each (event, command) pair prompts once on a TTY, then\n# is persisted to ~/.hermes/shell-hooks-allowlist.json. Non-interactive\n# runs (gateway, cron) need --accept-hooks, HERMES_ACCEPT_HOOKS=1, or the\n# hooks_auto_accept key below.\n#\n# See website/docs/user-guide/features/hooks.md for the full JSON wire\n# protocol and worked examples.\n#\n# hooks:\n# pre_tool_call:\n# - matcher: \"terminal\"\n# command: \"~/.hermes/agent-hooks/block-rm-rf.sh\"\n# timeout: 10\n# post_tool_call:\n# - matcher: \"write_file|patch\"\n# command: \"~/.hermes/agent-hooks/auto-format.sh\"\n# pre_llm_call:\n# - command: \"~/.hermes/agent-hooks/inject-cwd-context.sh\"\n# subagent_stop:\n# - command: \"~/.hermes/agent-hooks/log-orchestration.sh\"\n#\n# hooks_auto_accept: false\n\n\n# =============================================================================\n# Update Behavior\n# =============================================================================\nupdates:\n # Create a full HERMES_HOME zip before every `hermes update`.\n # Backups land in ~/.hermes/backups/ and can be restored with `hermes import`.\n # Off by default because large homes can add minutes to every update.\n pre_update_backup: false\n\n # Number of pre-update backup zips to retain.\n backup_keep: 5\n\n # What non-interactive updates do with local source edits in the Hermes repo.\n # Interactive terminal updates always prompt before restoring the autostash.\n #\n # stash - auto-stash before pull, then auto-restore after success (default)\n # discard - drop the update-created stash after success; use only on managed\n # installs where local source edits should not persist\n non_interactive_local_changes: \"stash\"\n\n\n# =============================================================================\n# Web Dashboard\n# =============================================================================\n# OAuth gate configuration for `hermes dashboard --host `.\n# The bundled Nous Portal plugin reads these on startup; settings here are\n# the canonical surface. Each can be overridden by an environment variable:\n#\n# dashboard.oauth.client_id <- HERMES_DASHBOARD_OAUTH_CLIENT_ID\n# dashboard.oauth.portal_url <- HERMES_DASHBOARD_PORTAL_URL\n# dashboard.public_url <- HERMES_DASHBOARD_PUBLIC_URL\n#\n# Env wins when set to a non-empty value. This is what Fly.io's platform-\n# secret injection uses to push per-deploy client_ids without needing to\n# bake a config.yaml into the image. Empty env values are treated as unset\n# so a provisioned-but-not-populated secret can't shadow a valid entry here.\n#\n# Local dev / on-prem deploys should typically set these via config.yaml\n# (the ~/.hermes/.env file is reserved for API keys and secrets).\n#\n# dashboard:\n# oauth:\n# client_id: \"\" # agent:{instance_id}; Portal provisions this at deploy\n# portal_url: \"\" # blank → default https://portal.nousresearch.com\n#\n# # Force the absolute base URL the OAuth callback (and any other public\n# # URL the dashboard hands to external systems) is built from. Set this\n# # for deploys behind reverse proxies that don't reliably forward\n# # X-Forwarded-Host / X-Forwarded-Proto / X-Forwarded-Prefix (manual\n# # nginx setups, on-prem ingresses, custom-domain Fly deploys without\n# # full proxy header chains).\n# #\n# # When set, the value is the complete authority: scheme + host +\n# # optional path prefix (e.g. \"https://example.com/hermes\"). The OAuth\n# # callback URL becomes \"/auth/callback\" — X-Forwarded-Prefix\n# # is IGNORED on this code path because the operator has explicitly\n# # declared the public URL and we no longer need to guess.\n# #\n# # Leave empty to use the existing proxy-header reconstruction (the\n# # default — works on Fly.io out of the box).\n# #\n# # public_url: \"https://example.com/hermes\"\n#\n# -----------------------------------------------------------------------------\n# Self-hosted OIDC dashboard auth (generic OpenID Connect — Authentik,\n# Keycloak, Zitadel, Authelia, Auth0, Okta, Google, …). Use this INSTEAD of the\n# nous block above when gating the dashboard with your own identity provider.\n# Each setting can be overridden by an environment variable:\n#\n# dashboard.oauth.self_hosted.issuer <- HERMES_DASHBOARD_OIDC_ISSUER\n# dashboard.oauth.self_hosted.client_id <- HERMES_DASHBOARD_OIDC_CLIENT_ID\n# dashboard.oauth.self_hosted.scopes <- HERMES_DASHBOARD_OIDC_SCOPES\n# dashboard.oauth.self_hosted.client_secret <- HERMES_DASHBOARD_OIDC_CLIENT_SECRET\n#\n# dashboard:\n# oauth:\n# provider: self-hosted\n# self_hosted:\n# issuer: \"https://auth.example.com/application/o/hermes/\" # required\n# client_id: \"hermes-dashboard\" # required\n# scopes: \"openid profile email\" # optional\n#\n# # OPTIONAL — set ONLY if your IDP registered the client as\n# # *confidential* (Authentik / Keycloak often default to this). When\n# # set, Hermes authenticates the client at the token endpoint\n# # (client_secret_basic or client_secret_post, auto-selected from the\n# # IDP's discovery doc) IN ADDITION to PKCE. Leave unset for a public\n# # (PKCE-only) client — the common case.\n# #\n# # This is a CREDENTIAL: prefer setting HERMES_DASHBOARD_OIDC_CLIENT_SECRET\n# # in ~/.hermes/.env over putting it here in config.yaml.\n# # client_secret: \"\"\n"}, "c9df4bc094fb27ddfc8b278380d9598dc534587b": {"t": 1785767893.449644, "n": 65372, "h": "75f45c34883f4939", "c": "# Hermes Agent CLI Configuration\n# Copy this file to cli-config.yaml and customize as needed.\n# This file configures the CLI behavior. Environment variables in .env take precedence.\n\n# =============================================================================\n# Model Configuration\n# =============================================================================\nmodel:\n # Default model to use (can be overridden with --model flag)\n # Both \"default\" and \"model\" work as the key name here.\n default: \"anthropic/claude-opus-4.6\"\n \n # Inference provider selection:\n # \"auto\" - Auto-detect from credentials (default)\n # \"openrouter\" - OpenRouter (requires: OPENROUTER_API_KEY or OPENAI_API_KEY)\n # \"nous\" - Nous Portal OAuth (requires: hermes auth add nous)\n # \"nous-api\" - Nous Portal API key (requires: NOUS_API_KEY)\n # \"anthropic\" - Direct Anthropic API (requires: ANTHROPIC_API_KEY)\n # \"openai-codex\" - OpenAI Codex (requires: hermes auth)\n # \"copilot\" - GitHub Copilot / GitHub Models (requires: GITHUB_TOKEN)\n # \"gemini\" - Use Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n # \"zai\" - Use z.ai / ZhipuAI GLM models (requires: GLM_API_KEY)\n # \"kimi-coding\" - Kimi / Moonshot AI (requires: KIMI_API_KEY)\n # \"minimax\" - MiniMax global (requires: MINIMAX_API_KEY)\n # \"minimax-cn\" - MiniMax China (requires: MINIMAX_CN_API_KEY)\n # \"huggingface\" - Hugging Face Inference (requires: HF_TOKEN)\n # \"nvidia\" - NVIDIA NIM / build.nvidia.com (requires: NVIDIA_API_KEY)\n # \"xiaomi\" - Xiaomi MiMo (requires: XIAOMI_API_KEY)\n # \"arcee\" - Arcee AI Trinity models (requires: ARCEEAI_API_KEY)\n # \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY — https://ollama.com/settings)\n # \"kilocode\" - KiloCode gateway (requires: KILOCODE_API_KEY)\n # \"azure-foundry\" - Microsoft Foundry / Azure OpenAI (API key or Entra ID)\n # \"lmstudio\" - LM Studio local server (optional: LM_API_KEY, defaults to http://127.0.0.1:1234/v1)\n #\n # Local servers (LM Studio, Ollama, vLLM, llama.cpp):\n # \"custom\" - Any other OpenAI-compatible endpoint. Set base_url below.\n # Aliases: \"ollama\", \"vllm\", \"llamacpp\" all map to \"custom\".\n # LM Studio is first-class and uses provider: \"lmstudio\".\n # It works with both no-auth and auth-enabled server modes.\n #\n # Can also be overridden for a single invocation with the --provider flag.\n provider: \"auto\"\n \n # API configuration (falls back to OPENROUTER_API_KEY env var)\n # api_key: \"your-key-here\" # Uncomment to set here instead of .env\n base_url: \"https://openrouter.ai/api/v1\"\n\n # Azure Foundry keyless auth example:\n # provider: \"azure-foundry\"\n # base_url: \"https://.openai.azure.com/openai/v1\"\n # auth_mode: \"entra_id\" # DefaultAzureCredential: az login, managed identity, workload identity, etc.\n # default: \"gpt-4o\" # Deployment/model name\n # entra:\n # scope: \"https://ai.azure.com/.default\" # Optional; this is the default.\n\n # ── Token limits — two settings, easy to confuse ──────────────────────────\n #\n # context_length: TOTAL context window (input + output tokens combined).\n # Controls when Hermes compresses history and validates requests.\n # Leave unset — Hermes auto-detects the correct value from the provider.\n # Set manually only when auto-detection is wrong (e.g. a local server with\n # a custom num_ctx, or a proxy that doesn't expose /v1/models).\n #\n # context_length: 131072\n #\n # max_tokens: OUTPUT cap — maximum tokens the model may generate per response.\n # Unrelated to how long your conversation history can be.\n # The OpenAI-standard name \"max_tokens\" is a misnomer; Anthropic's native\n # API has since renamed it \"max_output_tokens\" for clarity.\n # Leave unset to use the model's native output ceiling (recommended).\n # Set only if you want to deliberately limit individual response length.\n #\n# max_tokens: 8192\n\n # ── Custom request headers (optional) ─────────────────────────────────────\n #\n # default_headers: extra HTTP headers sent on every request to an\n # OpenAI-compatible endpoint. User values take precedence over the\n # provider/SDK defaults, so this is the supported way to override the\n # OpenAI Python SDK's identifying headers (User-Agent: OpenAI/Python ...,\n # X-Stainless-*) when a custom provider sits behind a gateway/WAF that\n # rejects them — e.g. an upstream that returns \"502 Upstream access\n # forbidden\" for the SDK default User-Agent but accepts a plain one.\n # Applies on the OpenAI wire only (not native Anthropic / Bedrock).\n #\n # default_headers:\n # User-Agent: \"curl/8.7.1\"\n\n# Named provider overrides (optional)\n# Use this for per-provider request timeouts, non-stream stale timeouts,\n# and per-model exceptions.\n# Applies to the primary turn client on every api_mode (OpenAI-wire, native\n# Anthropic, and Anthropic-compatible providers), the fallback chain, and\n# client rebuilds during credential rotation. For OpenAI-wire chat\n# completions (streaming and non-streaming) the configured value is also\n# used as the per-request ``timeout=`` kwarg so it wins over the legacy\n# HERMES_API_TIMEOUT env var (which still applies when no config is set).\n# ``stale_timeout_seconds`` controls the non-streaming stale-call detector and\n# wins over the legacy HERMES_API_CALL_STALE_TIMEOUT env var. Leaving these\n# unset keeps the legacy defaults (HERMES_API_TIMEOUT=1800s,\n# HERMES_API_CALL_STALE_TIMEOUT=90s, native Anthropic 900s). The\n# implicit non-stream stale detector is auto-disabled for local endpoints\n# and can scale upward for very large contexts.\n#\n# Not currently wired for AWS Bedrock (bedrock_converse + AnthropicBedrock\n# SDK paths) — those use boto3 with its own timeout configuration.\n#\n# providers:\n# ollama-local:\n# request_timeout_seconds: 300 # Longer timeout for local cold-starts\n# stale_timeout_seconds: 900 # Explicitly re-enable stale detection on local endpoints\n# anthropic:\n# request_timeout_seconds: 30 # Fast-fail cloud requests\n# models:\n# claude-opus-4.6:\n# timeout_seconds: 600 # Longer timeout for extended-thinking Opus calls\n# openai-codex:\n# models:\n# gpt-5.4:\n# stale_timeout_seconds: 1800 # Longer non-stream stale timeout for slow large-context turns\n\n# =============================================================================\n# OpenRouter Provider Routing (only applies when using OpenRouter)\n# =============================================================================\n# Control how requests are routed across providers on OpenRouter.\n# See: https://openrouter.ai/docs/guides/routing/provider-selection\n#\n# provider_routing:\n# # Sort strategy: \"price\" (default), \"throughput\", or \"latency\"\n# # Append :nitro to model name for a shortcut to throughput sorting.\n# sort: \"throughput\"\n#\n# # Only allow these providers (provider slugs from OpenRouter)\n# # only: [\"anthropic\", \"google\"]\n#\n# # Skip these providers entirely\n# # ignore: [\"deepinfra\", \"fireworks\"]\n#\n# # Try providers in this order (overrides default load balancing)\n# # order: [\"anthropic\", \"google\", \"together\"]\n#\n# # Require providers to support all parameters in your request\n# # require_parameters: true\n#\n# # Data policy: \"allow\" (default) or \"deny\" to exclude providers that may store data\n# # data_collection: \"deny\"\n\n# =============================================================================\n# OpenRouter Response Caching (only applies when using OpenRouter)\n# =============================================================================\n# Cache identical API responses at the OpenRouter edge for free instant replays.\n# When enabled, identical requests (same model, messages, parameters) return\n# cached responses with zero billing. Separate from Anthropic prompt caching.\n# See: https://openrouter.ai/docs/guides/features/response-caching\n#\n# openrouter:\n# response_cache: true # Enable response caching (default: true)\n# response_cache_ttl: 300 # Cache TTL in seconds, 1-86400 (default: 300)\n\n# =============================================================================\n# Git Worktree Isolation\n# =============================================================================\n# When enabled, each CLI session creates an isolated git worktree so multiple\n# agents can work on the same repo concurrently without file collisions.\n# Equivalent to always passing --worktree / -w on the command line.\n#\n# worktree: true # Always create a worktree when in a git repo\n# worktree: false # Default — only create when -w flag is passed\n#\n# By default a new worktree branches from the freshly-fetched remote tip\n# (the current branch's upstream, else the remote's default branch) so it\n# starts current with the project instead of from the local clone's\n# (possibly stale) HEAD. Set worktree_sync: false to branch from local HEAD\n# instead — useful when offline or when you deliberately want the clone's\n# exact current state as the base.\n#\n# worktree_sync: true # Default — branch from the fetched remote tip\n# worktree_sync: false # Branch from local HEAD (offline / pinned base)\n\n# =============================================================================\n# Terminal Tool Configuration\n# =============================================================================\n# Choose ONE of the following terminal configurations by uncommenting it.\n# The terminal tool executes commands in the specified environment.\n\n# -----------------------------------------------------------------------------\n# OPTION 1: Local execution (default)\n# Commands run directly on your machine in the current directory\n# -----------------------------------------------------------------------------\n# Working directory behavior:\n# - CLI (`hermes` command): Uses \".\" (current directory where you run hermes)\n# - Gateway/messaging/cron: Uses terminal.cwd here; legacy .env cwd values are deprecated\nterminal:\n backend: \"local\"\n cwd: \".\" # For local backend: \".\" = current directory. Ignored for remote backends unless a backend documents otherwise.\n timeout: 180\n # HOME policy for tool subprocesses:\n # auto - default: host uses your real HOME; containers use HERMES_HOME/home\n # real - force your real OS-user HOME\n # profile - force HERMES_HOME/home for strict per-profile CLI config isolation\n home_mode: \"auto\"\n docker_mount_cwd_to_workspace: false # SECURITY: off by default. Opt in to mount the launch cwd into Docker /workspace.\n lifetime_seconds: 300\n # sudo_password: \"hunter2\" # Optional: pipe a sudo password via sudo -S. SECURITY WARNING: plaintext.\n # sudo_password: \"\" # Explicit empty password: try empty and never open the interactive sudo prompt.\n\n# -----------------------------------------------------------------------------\n# OPTION 2: SSH remote execution\n# Commands run on a remote server - agent code stays local (sandboxed)\n# Great for: keeping agent isolated from its own code, using powerful remote hardware\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"ssh\"\n# cwd: \"/home/myuser/project\" # Path on the REMOTE server\n# timeout: 180\n# lifetime_seconds: 300\n# ssh_host: \"my-server.example.com\"\n# ssh_user: \"myuser\"\n# ssh_port: 22\n# ssh_key: \"~/.ssh/id_rsa\" # Optional - uses ssh-agent if not specified\n\n# -----------------------------------------------------------------------------\n# OPTION 3: Docker container\n# Commands run in an isolated Docker container\n# Great for: reproducible environments, testing, isolation\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"docker\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /)\n# timeout: 180\n# lifetime_seconds: 300\n# docker_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# docker_mount_cwd_to_workspace: true # Explicit opt-in: mount your launch cwd into /workspace\n# # Optional: run the container as your host user's uid:gid so files written\n# # into bind-mounted dirs are owned by you, not root. Drops SETUID/SETGID\n# # caps too since no gosu privilege drop is needed. Leave off if your\n# # chosen docker_image expects to start as root.\n# docker_run_as_host_user: true\n# # Optional: explicitly forward selected env vars into Docker.\n# # These values come from your current shell first, then ~/.hermes/.env.\n# # Warning: anything forwarded here is visible to commands run in the container.\n# docker_forward_env:\n# - \"GITHUB_TOKEN\"\n# - \"NPM_TOKEN\"\n# # Optional: extra flags passed verbatim to docker run (appended after security defaults).\n# # Useful for adding capabilities (e.g. apt installs needing SETUID) or custom options.\n# # Example: add a Linux capability not included by default\n# # docker_extra_args:\n# # - \"--cap-add\"\n# # - \"SETUID\"\n\n# -----------------------------------------------------------------------------\n# OPTION 4: Singularity/Apptainer container\n# Commands run in a Singularity container (common in HPC environments)\n# Great for: HPC clusters, shared compute environments\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"singularity\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# singularity_image: \"docker://nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 5: Modal cloud execution\n# Commands run on Modal's cloud infrastructure\n# Great for: GPU access, scalable compute, serverless execution\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"modal\"\n# cwd: \"/workspace\" # Path INSIDE the sandbox (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# modal_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 6: Daytona cloud execution\n# Commands run in Daytona cloud sandboxes\n# Great for: Cloud dev environments, persistent workspaces, team collaboration\n# Requires: pip install daytona, DAYTONA_API_KEY env var\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"daytona\"\n# cwd: \"~\"\n# timeout: 180\n# lifetime_seconds: 300\n# daytona_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# container_disk: 10240 # Daytona max is 10GB per sandbox\n\n#\n# --- Container resource limits (docker, singularity, modal, daytona -- ignored for local/ssh) ---\n# These settings apply to all container backends. They control the resources\n# allocated to the sandbox and whether its filesystem persists across sessions.\n container_cpu: 1 # CPU cores\n container_memory: 5120 # Memory in MB (5120 = 5GB)\n container_disk: 51200 # Disk in MB (51200 = 50GB)\n container_persistent: true # Persist filesystem across sessions (false = ephemeral)\n\n# -----------------------------------------------------------------------------\n# SUDO SUPPORT (works with ALL backends above)\n# -----------------------------------------------------------------------------\n# Add sudo_password to any terminal config above to enable sudo commands.\n# The password is piped via `sudo -S`. Works with local, ssh, docker, etc.\n#\n# SECURITY WARNING: Password stored in plaintext!\n#\n# INTERACTIVE PROMPT: If sudo_password is unset and the CLI is running,\n# you'll be prompted to enter your password when sudo is needed:\n# - 45-second timeout (auto-skips if no input)\n# - Press Enter to skip (command fails gracefully)\n# - Password is hidden while typing\n# - Password is cached for the session\n#\n# EMPTY PASSWORDS: Setting sudo_password to an explicit empty string is different\n# from leaving it unset. Hermes will try an empty password via `sudo -S` and\n# will not open the interactive prompt. This is useful for passwordless sudo,\n# Touch ID sudo setups, and environments where prompting is just noise.\n#\n# ALTERNATIVES:\n# - SSH backend: Configure passwordless sudo on the remote server\n# - Containers: Run as root inside the container (no sudo needed)\n# - Local: Configure /etc/sudoers for specific commands\n#\n# Example (add to your terminal section):\n# sudo_password: \"your-password-here\"\n\n# =============================================================================\n# Security Scanning (tirith)\n# =============================================================================\n# Optional pre-exec command security scanning via tirith.\n# Detects homograph URLs, pipe-to-shell, terminal injection, env manipulation.\n# Install: brew install sheeki03/tap/tirith\n# Docs: https://github.com/sheeki03/tirith\n#\n# security:\n# tirith_enabled: true # Enable/disable tirith scanning\n# tirith_path: \"tirith\" # Path to tirith binary (supports ~ expansion)\n# tirith_timeout: 5 # Scan timeout in seconds\n# tirith_fail_open: true # Allow commands if tirith unavailable\n\n# =============================================================================\n# Browser Tool Configuration\n# =============================================================================\nbrowser:\n # Inactivity timeout in seconds - browser sessions are automatically closed\n # after this period of no activity between agent loops (default: 120 = 2 minutes)\n inactivity_timeout: 120\n\n# =============================================================================\n# Tool Loop Guardrails\n# =============================================================================\n# Soft warnings are enabled by default. They append guidance to repeated failed\n# or non-progressing tool results but still let the tool execute. Hard stops are\n# opt-in circuit breakers for autonomous/cron sessions where stopping a loop is\n# preferable to spending the full iteration budget.\ntool_loop_guardrails:\n warnings_enabled: true\n hard_stop_enabled: false\n warn_after:\n exact_failure: 2\n same_tool_failure: 3\n idempotent_no_progress: 2\n hard_stop_after:\n exact_failure: 5\n same_tool_failure: 8\n idempotent_no_progress: 5\n\n# =============================================================================\n# Context Compression (Auto-shrinks long conversations)\n# =============================================================================\n# When conversation approaches model's context limit, middle turns are\n# automatically summarized to free up space while preserving important context.\n#\n# HOW IT WORKS:\n# 1. Tracks actual token usage from API responses (not estimates)\n# 2. When prompt_tokens >= threshold% of model's context_length, triggers compression\n# 3. Protects first 3 turns (system prompt, initial request, first response)\n# 4. Protects last N turns (default 20 messages = ~10 full turns of recent context)\n# 5. Summarizes middle turns using a fast/cheap model\n# 6. Inserts summary as a user message, continues conversation seamlessly\n#\n# Post-compression tail budget is target_ratio × threshold × context_length:\n# 200K context, threshold 0.50, ratio 0.20 → 20K tokens of recent tail preserved\n# 1M context, threshold 0.50, ratio 0.20 → 100K tokens of recent tail preserved\n#\ncompression:\n # Enable automatic context compression (default: true)\n # Set to false if you prefer to manage context manually or want errors on overflow\n enabled: true\n \n # Trigger compression at this % of model's context limit (default: 0.50 = 50%)\n # Lower values = more aggressive compression, higher values = compress later\n threshold: 0.50\n \n # Fraction of the threshold to preserve as recent tail (default: 0.20 = 20%)\n # e.g. 20% of 50% threshold = 10% of total context kept as recent messages.\n # Summary output is separately capped at 12K tokens (Gemini output limit).\n # Range: 0.10 - 0.80\n target_ratio: 0.20\n\n # Number of most-recent messages to always preserve (default: 20 ≈ 10 full turns)\n # Higher values keep more recent conversation intact at the cost of more aggressive\n # compression of older turns.\n protect_last_n: 20\n\n # Number of non-system messages to protect at the head of the transcript, in\n # ADDITION to the system prompt (which is always implicitly protected).\n # Head messages are NEVER summarized — they survive every compression\n # indefinitely. This gives stable early context for short/medium sessions,\n # but in long-running sessions that rely on rolling compaction the pinned\n # opening turns may not match how you want the session framed over time.\n # Set to 0 to preserve ONLY the system prompt (plus the rolling summary\n # and recent tail) — the cleanest configuration for long-running sessions.\n # Default 3 preserves the system prompt plus the first three non-system\n # head messages, matching the pre-feature behaviour.\n protect_first_n: 3\n\n # To pin a specific model/provider for compression summaries, use the\n # auxiliary section below (auxiliary.compression.provider / model).\n\n# =============================================================================\n# Anthropic prompt caching TTL\n# =============================================================================\n# When prompt caching is active (Claude via OpenRouter or native Anthropic),\n# Anthropic supports two TTL tiers for cached prefixes: \"5m\" (default) and\n# \"1h\". Other values are ignored and \"5m\" is used.\n#\nprompt_caching:\n cache_ttl: \"5m\" # use \"1h\" for long sessions with pauses between turns\n\n# =============================================================================\n# Auxiliary Models (Advanced — Experimental)\n# =============================================================================\n# Hermes uses lightweight \"auxiliary\" models for side tasks: image analysis,\n# browser screenshot analysis, web page summarization, TTS audio-tag insertion,\n# and context compression.\n#\n# By default these use Gemini Flash via OpenRouter or Nous Portal and are\n# auto-detected from your credentials. You do NOT need to change anything\n# here for normal usage.\n#\n# WARNING: Overriding these with providers other than OpenRouter or Nous Portal\n# is EXPERIMENTAL and may not work. Not all models/providers support vision,\n# produce usable summaries, or accept the same API format. Change at your own\n# risk — if things break, reset to \"auto\" / empty values.\n#\n# Each task has its own provider + model pair so you can mix providers.\n# For example: OpenRouter for vision (needs multimodal), but your main\n# local endpoint for compression (just needs text).\n#\n# Provider options:\n# \"auto\" - Best available: OpenRouter → Nous Portal → main endpoint (default)\n# \"openrouter\" - Force OpenRouter (requires OPENROUTER_API_KEY)\n# \"nous\" - Force Nous Portal (requires: hermes auth add nous)\n# \"gemini\" - Force Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n# \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY)\n# \"codex\" - Force Codex OAuth (requires: hermes model → Codex).\n# Uses gpt-5.3-codex which supports vision.\n# \"main\" - Use your custom endpoint (OPENAI_BASE_URL + OPENAI_API_KEY).\n# Works with OpenAI API, local models, or any OpenAI-compatible\n# endpoint. Also falls back to Codex OAuth and API-key providers.\n#\n# Model: leave empty to use the provider's default. When empty, OpenRouter\n# uses \"google/gemini-3-flash-preview\" and Nous uses \"gemini-3-flash\".\n# Other providers pick a sensible default automatically.\n#\n# auxiliary:\n# # Image analysis: vision_analyze tool + browser screenshots\n# vision:\n# provider: \"auto\"\n# model: \"\" # e.g. \"google/gemini-2.5-flash\", \"openai/gpt-4o\"\n# timeout: 30 # LLM API call timeout (seconds)\n# download_timeout: 30 # Image HTTP download timeout (seconds)\n# # Increase for slow connections or self-hosted image servers\n#\n# # Web page scraping / summarization + browser page text extraction\n# web_extract:\n# provider: \"auto\"\n# model: \"\"\n#\n# # Gemini 3.1 TTS hidden audio-tag insertion\n# tts_audio_tags:\n# provider: \"auto\" # empty model = your main chat model\n# model: \"\"\n# timeout: 30\n#\n# # Session search — summarizes matching past sessions\n# session_search:\n# provider: \"auto\"\n# model: \"\"\n# timeout: 30\n# max_concurrency: 3 # Limit parallel summaries to reduce request-burst 429s\n# extra_body: {} # Provider-specific OpenAI-compatible request fields\n# # Example for providers that support request-body\n# # reasoning controls:\n# # extra_body:\n# # enable_thinking: false\n# # Some vLLM/Qwen deployments expect this nested:\n# # extra_body:\n# # chat_template_kwargs:\n# # enable_thinking: false\n\n# =============================================================================\n# Persistent Memory\n# =============================================================================\n# Bounded curated memory injected into the system prompt every session.\n# Two stores: MEMORY.md (agent's notes) and USER.md (user profile).\n# Character limits keep the memory small and focused. The agent manages\n# pruning -- when at the limit, it must consolidate or replace entries.\n# Disabled by default in batch_runner.\n#\nmemory:\n # Agent's personal notes: environment facts, conventions, things learned\n memory_enabled: true\n \n # User profile: preferences, communication style, expectations\n user_profile_enabled: true\n \n # Character limits (~2.75 chars per token, model-independent)\n memory_char_limit: 2200 # ~800 tokens\n user_char_limit: 1375 # ~500 tokens\n\n # Periodic memory nudge: remind the agent to consider saving memories\n # every N user turns. Set to 0 to disable. Only active when memory is enabled.\n nudge_interval: 10 # Nudge every 10 user turns (0 = disabled)\n\n # Memory flush: give the agent one turn to save memories before context is\n # lost (compression, /new, /reset, exit). Set to 0 to disable.\n # For exit/reset, only fires if the session had at least this many user turns.\n flush_min_turns: 6 # Min user turns to trigger flush on exit/reset (0 = disabled)\n\n# =============================================================================\n# Session Reset Policy (Messaging Platforms)\n# =============================================================================\n# Controls when messaging sessions (Telegram, Discord, WhatsApp, Slack) are\n# automatically cleared. Without resets, conversation context grows indefinitely\n# which increases API costs with every message.\n#\n# When a reset triggers, the agent first saves important information to its\n# persistent memory — but the conversation context is wiped. The agent starts\n# fresh but retains learned facts via its memory system.\n#\n# Users can always manually reset with /reset or /new in chat.\n#\n# Modes:\n# \"both\" - Reset on EITHER inactivity timeout or daily boundary (recommended)\n# \"idle\" - Reset only after N minutes of inactivity\n# \"daily\" - Reset only at a fixed hour each day\n# \"none\" - Never auto-reset; context lives until /reset or compression kicks in\n#\n# When a reset triggers, the agent gets one turn to save important memories and\n# skills before the context is wiped. Persistent memory carries across sessions.\n#\nsession_reset:\n mode: both # \"both\", \"idle\", \"daily\", or \"none\"\n idle_minutes: 1440 # Inactivity timeout in minutes (default: 1440 = 24 hours)\n at_hour: 4 # Daily reset hour, 0-23 local time (default: 4 AM)\n\n# Maximum number of simultaneously active chat sessions across CLI, TUI,\n# dashboard chat, and messaging gateway. Set to null, 0, or omit to allow\n# unlimited concurrent sessions. When the limit is reached, new sessions get a\n# clean error while existing active sessions keep their normal behavior. This\n# top-level key takes precedence over gateway.max_concurrent_sessions. The cap\n# is a best-effort single-host/profile runtime guard; Hermes fails open if the\n# local runtime lease registry cannot be read or locked.\nmax_concurrent_sessions: null\n\n# When true, group/channel chats use one session per participant when the platform\n# provides a user ID. This is the secure default and prevents users in the same\n# room from sharing context, interrupts, and token costs. Set false only if you\n# explicitly want one shared \"room brain\" per group/channel.\ngroup_sessions_per_user: true\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Gateway Streaming\n# ─────────────────────────────────────────────────────────────────────────────\n# Stream tokens to messaging platforms in real-time. The bot sends a message\n# on first token, then progressively edits it as more tokens arrive.\n# Disabled by default — enable to try the streaming UX on Telegram/Discord/Slack.\n# For Telegram, partial edits are sent as plain text and only the final edit uses MarkdownV2.\nstreaming:\n enabled: false\n # transport: edit # \"edit\" = progressive editMessageText\n # edit_interval: 0.3 # seconds between message edits\n # buffer_threshold: 40 # chars before forcing an edit flush\n # cursor: \" ▉\" # cursor shown during streaming\n\n# =============================================================================\n# Skills Configuration\n# =============================================================================\n# Skills are reusable procedures the agent can load and follow. The agent can\n# also create new skills after completing complex tasks.\n#\nskills:\n # Nudge the agent to create skills after complex tasks.\n # Every N tool-calling iterations, remind the model to consider saving a skill.\n # Set to 0 to disable.\n creation_nudge_interval: 15\n\n # External skill directories — share skills across tools/agents without\n # copying them into ~/.hermes/skills/. Each path is expanded (~ and ${VAR})\n # and resolved to an absolute path. External dirs are read-only: skill\n # creation always writes to ~/.hermes/skills/. Local skills take precedence\n # when names collide.\n # external_dirs:\n # - ~/.agents/skills\n # - /home/shared/team-skills\n\n# =============================================================================\n# Agent Behavior\n# =============================================================================\nagent:\n # Maximum tool-calling iterations per conversation\n # Higher = more room for complex tasks, but costs more tokens\n # Recommended: 20-30 for focused tasks, 50-100 for open exploration\n max_turns: 60\n\n # Inactivity timeout for gateway agent runs (seconds, 0 = unlimited).\n # The agent can run indefinitely when actively calling tools or receiving\n # API responses. Only fires after the agent has been idle for this duration.\n # gateway_timeout: 1800\n\n # Staged warning: send a warning before escalating to full timeout.\n # Fires once per run when inactivity reaches this threshold (seconds).\n # Set to 0 to disable the warning.\n # gateway_timeout_warning: 900\n\n # Graceful drain timeout for gateway stop/restart (seconds).\n # Default 0 = no drain: a restart interrupts in-flight agents immediately,\n # cleans up, and exits. Set a positive value only if you want a grace\n # window on /restart, and keep it well under systemd's TimeoutStopSec.\n # restart_drain_timeout: 0\n\n # Max app-level retry attempts for API errors (connection drops, provider\n # timeouts, 5xx, etc.) before the agent surfaces the failure. Lower this\n # to 1 if you use fallback providers and want fast failover on flaky\n # primaries (default 3). The OpenAI SDK does its own low-level retries\n # underneath this wrapper — this is the Hermes-level loop.\n # api_max_retries: 3\n\n # After the agent edits code without fresh passing verification, nudge it to\n # verify before finishing. The default \"auto\" enables it on interactive\n # coding surfaces (CLI, TUI, desktop) and programmatic callers, and disables\n # it on conversational messaging surfaces (Telegram, Discord, etc.) where the\n # verification summary would reach a human as chat noise. Set true or false to\n # force it on or off; the HERMES_VERIFY_ON_STOP env var (1/0) takes precedence.\n # verify_on_stop: auto\n\n # Enable verbose logging\n verbose: false\n \n # Reasoning effort level (OpenRouter and Nous Portal)\n # Controls how much \"thinking\" the model does before responding.\n # Options: \"xhigh\" (max), \"high\", \"medium\", \"low\", \"minimal\", \"none\" (disable)\n reasoning_effort: \"medium\"\n \n # Predefined personalities (use with /personality command)\n personalities:\n helpful: \"You are a helpful, friendly AI assistant.\"\n concise: \"You are a concise assistant. Keep responses brief and to the point.\"\n technical: \"You are a technical expert. Provide detailed, accurate technical information.\"\n creative: \"You are a creative assistant. Think outside the box and offer innovative solutions.\"\n teacher: \"You are a patient teacher. Explain concepts clearly with examples.\"\n kawaii: \"You are a kawaii assistant! Use cute expressions like (◕‿◕), ★, ♪, and ~! Add sparkles and be super enthusiastic about everything! Every response should feel warm and adorable desu~! ヽ(>∀<☆)ノ\"\n catgirl: \"You are Neko-chan, an anime catgirl AI assistant, nya~! Add 'nya' and cat-like expressions to your speech. Use kaomoji like (=^・ω・^=) and ฅ^•ﻌ•^ฅ. Be playful and curious like a cat, nya~!\"\n pirate: \"Arrr! Ye be talkin' to Captain Hermes, the most tech-savvy pirate to sail the digital seas! Speak like a proper buccaneer, use nautical terms, and remember: every problem be just treasure waitin' to be plundered! Yo ho ho!\"\n shakespeare: \"Hark! Thou speakest with an assistant most versed in the bardic arts. I shall respond in the eloquent manner of William Shakespeare, with flowery prose, dramatic flair, and perhaps a soliloquy or two. What light through yonder terminal breaks?\"\n surfer: \"Duuude! You're chatting with the chillest AI on the web, bro! Everything's gonna be totally rad. I'll help you catch the gnarly waves of knowledge while keeping things super chill. Cowabunga! 🤙\"\n noir: \"The rain hammered against the terminal like regrets on a guilty conscience. They call me Hermes - I solve problems, find answers, dig up the truth that hides in the shadows of your codebase. In this city of silicon and secrets, everyone's got something to hide. What's your story, pal?\"\n uwu: \"hewwo! i'm your fwiendwy assistant uwu~ i wiww twy my best to hewp you! *nuzzles your code* OwO what's this? wet me take a wook! i pwomise to be vewy hewpful >w<\"\n philosopher: \"Greetings, seeker of wisdom. I am an assistant who contemplates the deeper meaning behind every query. Let us examine not just the 'how' but the 'why' of your questions. Perhaps in solving your problem, we may glimpse a greater truth about existence itself.\"\n hype: \"YOOO LET'S GOOOO!!! 🔥🔥🔥 I am SO PUMPED to help you today! Every question is AMAZING and we're gonna CRUSH IT together! This is gonna be LEGENDARY! ARE YOU READY?! LET'S DO THIS! 💪😤🚀\"\n\n# =============================================================================\n# Toolsets\n# =============================================================================\n# Control which tools the agent has access to.\n# Use `hermes tools` to interactively enable/disable tools per platform.\n\n# =============================================================================\n# Platform Toolsets (per-platform tool configuration)\n# =============================================================================\n# Override which toolsets are available on each platform.\n# If a platform isn't listed here, its built-in default is used.\n#\n# You can use EITHER:\n# - A preset like \"hermes-cli\" or \"hermes-telegram\" (curated tool set)\n# - A list of individual toolsets to compose your own (see list below)\n#\n# Supported platform keys: cli, telegram, discord, whatsapp, slack, qqbot, teams, google_chat\n#\n# Examples:\n#\n# # Use presets (same as defaults):\n# platform_toolsets:\n# cli: [hermes-cli]\n# telegram: [hermes-telegram]\n#\n# # Custom: give Telegram only web + terminal + file + planning:\n# platform_toolsets:\n# telegram: [web, terminal, file, todo]\n#\n# # Custom: CLI without browser or image gen:\n# platform_toolsets:\n# cli: [web, terminal, file, skills, todo, tts, cronjob]\n#\n# # Restrictive: Discord gets read-only tools only:\n# platform_toolsets:\n# discord: [web, vision, skills, todo]\n#\n# If not set, defaults are:\n# cli: hermes-cli (everything + cronjob management)\n# telegram: hermes-telegram (terminal, file, web, vision, image, tts, browser, skills, todo, cronjob, messaging)\n# discord: hermes-discord (same as telegram)\n# whatsapp: hermes-whatsapp (same as telegram)\n# slack: hermes-slack (same as telegram)\n# signal: hermes-signal (same as telegram)\n# homeassistant: hermes-homeassistant (same as telegram)\n# qqbot: hermes-qqbot (same as telegram)\n# teams: hermes-teams (same as telegram)\n# google_chat: hermes-google_chat (same as telegram)\n#\nplatform_toolsets:\n cli: [hermes-cli]\n telegram: [hermes-telegram]\n discord: [hermes-discord]\n whatsapp: [hermes-whatsapp]\n slack: [hermes-slack]\n signal: [hermes-signal]\n homeassistant: [hermes-homeassistant]\n qqbot: [hermes-qqbot]\n yuanbao: [hermes-yuanbao]\n teams: [hermes-teams]\n google_chat: [hermes-google_chat]\n\n# =============================================================================\n# Gateway Platform Settings\n# =============================================================================\n# Optional per-platform messaging settings.\n# Platform-specific knobs live under `extra`.\n#\n# platforms:\n# telegram:\n# reply_to_mode: \"first\" # off | first | all\n# # guest_mode lets explicit @mentions from non-allowlisted groups through.\n# # Default false; ordinary messages, replies, and regex wake words stay blocked.\n# guest_mode: false\n# # allowed_chats: [\"-1001234567890\"]\n# extra:\n# disable_link_previews: false # Set true to suppress Telegram URL previews in bot messages\n# rich_messages: false # Bot API 10.1 rich messages (tables/task lists/details/math); default false for copyable legacy MarkdownV2, set true to opt in\n# rich_drafts: false # Experimental rich draft previews during Telegram DM streaming; default false because Telegram Desktop/macOS can visually overlay draft frames\n# command_menu:\n# # Telegram allows up to 100 BotCommands; Hermes defaults to 60 so\n# # all built-in commands plus common skill commands stay visible\n# # while remaining under Telegram's payload-size limit. Clamped 1..100.\n# max_commands: 60\n# # prepend = user priority first, then Hermes defaults\n# # append = Hermes defaults first, then user priority\n# # replace = only the list below defines priority\n# priority_mode: prepend\n# priority:\n# - my_plugin_command\n#\n# Discord-specific settings (config.yaml top-level, not under platforms:):\n#\n# discord:\n# require_mention: true # Require @mention in server channels (default: true)\n# auto_thread: true # Auto-create thread on @mention (default: true)\n# free_response_channels: \"\" # Channel IDs where no mention is needed\n# reactions: true # Show processing reactions (default: true)\n# history_backfill: true # Recover missed channel messages on mention (default: true)\n# history_backfill_limit: 50 # Max messages to scan backwards (default: 50)\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Available toolsets (use these names in platform_toolsets or the toolsets list)\n#\n# Run `hermes chat --list-toolsets` to see all toolsets and their tools.\n# Run `hermes chat --list-tools` to see every individual tool with descriptions.\n# ─────────────────────────────────────────────────────────────────────────────\n#\n# INDIVIDUAL TOOLSETS (compose your own):\n# web - web_search, web_extract\n# search - web_search only (no scraping)\n# terminal - terminal, process\n# file - read_file, write_file, patch, search\n# browser - browser_navigate, browser_snapshot, browser_click, browser_type,\n# browser_scroll, browser_back, browser_press,\n# browser_get_images, browser_vision (requires BROWSERBASE_API_KEY)\n# vision - vision_analyze (requires OPENROUTER_API_KEY)\n# image_gen - image_generate (requires FAL_KEY)\n# skills - skills_list, skill_view\n# skills_hub - skill_hub (search/install/manage from online registries — user-driven only)\n# todo - todo (in-memory task planning, no deps)\n# tts - text_to_speech (Edge TTS free, or ELEVENLABS/OPENAI/MINIMAX/MISTRAL key)\n# cronjob - cronjob (create/list/update/pause/resume/run/remove scheduled tasks)\n#\n# PRESETS (curated bundles):\n# hermes-cli - All of the above except send_message\n# hermes-telegram - terminal, file, web, vision, image_gen, tts, browser,\n# skills, todo, cronjob, send_message\n# hermes-discord - Same as hermes-telegram\n# hermes-whatsapp - Same as hermes-telegram\n# hermes-slack - Same as hermes-telegram\n#\n# COMPOSITE:\n# debugging - terminal + web + file\n# safe - web + vision (no terminal access)\n# all - Everything available\n#\n# web - Web search and content extraction (web_search, web_extract)\n# search - Web search only, no scraping (web_search)\n# terminal - Command execution and process management (terminal, process)\n# file - File operations: read, write, patch, search\n# browser - Full browser automation (navigate, click, type, screenshot, etc.)\n# vision - Image analysis (vision_analyze)\n# image_gen - Image generation with FLUX (image_generate)\n# skills - Load skill documents (skills_list, skill_view)\n# todo - Task planning and tracking for multi-step work\n# memory - Persistent memory across sessions (personal notes + user profile)\n# session_search - Search and recall past conversations (FTS5 + Gemini Flash summarization)\n# tts - Text-to-speech (Edge TTS free, ElevenLabs, OpenAI, MiniMax, Mistral)\n# cronjob - Schedule and manage automated tasks (CLI-only)\n#\n# Composite toolsets:\n# debugging - terminal + web + file (for troubleshooting)\n# safe - web + vision (no terminal access)\n\n# NOTE: The top-level \"toolsets\" key is deprecated and ignored.\n# Tool configuration is managed per-platform via platform_toolsets above.\n# Use `hermes tools` to configure interactively, or edit platform_toolsets directly.\n#\n# CLI override: hermes chat --toolsets terminal,web,file\n\n# =============================================================================\n# MCP (Model Context Protocol) Servers\n# =============================================================================\n# Connect to external MCP servers to add tools from the MCP ecosystem.\n# Each server's tools are automatically discovered and registered.\n# See website/docs/user-guide/features/mcp.md for full documentation.\n#\n# Stdio servers (spawn a subprocess):\n# command: the executable to run\n# args: command-line arguments\n# env: environment variables (only these + safe defaults passed to subprocess)\n#\n# HTTP servers (connect to a URL):\n# url: the MCP server endpoint\n# headers: HTTP headers (e.g., for authentication)\n#\n# Optional per-server settings:\n# timeout: tool call timeout in seconds (default: 120)\n# connect_timeout: initial connection timeout (default: 60)\n# keepalive_interval: liveness ping cadence in seconds (default: 180).\n# Lower it below the server's session TTL for servers that expire idle\n# sessions quickly (e.g. Unreal Engine editor MCP, ~15s), otherwise idle\n# tool calls hit an expired session and pay a slow reconnect. Floored at 5s.\n#\n# mcp_servers:\n# time:\n# command: uvx\n# args: [\"mcp-server-time\"]\n# filesystem:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-filesystem\", \"/home/user\"]\n# notion:\n# url: https://mcp.notion.com/mcp\n# github:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-github\"]\n# env:\n# GITHUB_PERSONAL_ACCESS_TOKEN: \"ghp_...\"\n#\n# Sampling (server-initiated LLM requests) — enabled by default.\n# Per-server config under the 'sampling' key:\n# analysis:\n# command: npx\n# args: [\"-y\", \"analysis-server\"]\n# sampling:\n# enabled: true # default: true\n# model: \"gemini-3-flash\" # override model (optional)\n# max_tokens_cap: 4096 # max tokens per request\n# timeout: 30 # LLM call timeout (seconds)\n# max_rpm: 10 # max requests per minute\n# allowed_models: [] # model whitelist (empty = all)\n# max_tool_rounds: 5 # tool loop limit (0 = disable)\n# log_level: \"info\" # audit verbosity\n\n# =============================================================================\n# Text-to-Speech\n# =============================================================================\n# TTS defaults to Edge TTS unless changed in ~/.hermes/config.yaml.\n# Gemini TTS supports persona/director prompt files, and Gemini 3.1 Flash TTS\n# can use a hidden auxiliary rewrite pass to insert expressive square-bracket\n# audio tags into the TTS script without showing tags in chat.\n#\n# tts:\n# provider: \"gemini\"\n# gemini:\n# model: \"gemini-3.1-flash-tts-preview\"\n# voice: \"Kore\"\n# audio_tags: false\n# persona_prompt_file: \"\" # e.g. ~/.hermes/tts/radio-host.md\n\n# =============================================================================\n# Voice Transcription (Speech-to-Text)\n# =============================================================================\n# Automatically transcribe voice messages on messaging platforms.\n# Providers: local (free, faster-whisper) | groq (free tier) | openai (Whisper API) | mistral (Voxtral Transcribe)\n# Set the corresponding API key in .env: GROQ_API_KEY, OPENAI_API_KEY, or MISTRAL_API_KEY.\nstt:\n enabled: true\n # provider: \"local\" # auto-detected if omitted\n local:\n model: \"base\" # tiny | base | small | medium | large-v3 | turbo\n # language: \"\" # auto-detect; set to \"en\", \"es\", \"fr\", etc. to force\n openai:\n model: \"whisper-1\" # whisper-1 | gpt-4o-mini-transcribe | gpt-4o-transcribe\n # mistral:\n # model: \"voxtral-mini-latest\" # voxtral-mini-latest | voxtral-mini-2602\n\n# =============================================================================\n# Response Pacing (Messaging Platforms)\n# =============================================================================\n# Add human-like delays between message chunks.\n# human_delay:\n# mode: \"off\" # \"off\" | \"natural\" | \"custom\"\n# min_ms: 800 # Min delay (custom mode only)\n# max_ms: 2500 # Max delay (custom mode only)\n\n# =============================================================================\n# Session Logging\n# =============================================================================\n# Session trajectories are automatically saved to logs/ directory.\n# Each session creates: logs/session_YYYYMMDD_HHMMSS_UUID.json\n#\n# The session ID is displayed in the welcome banner for easy reference.\n# Logs contain full conversation history in trajectory format:\n# - System prompt, user messages, assistant responses\n# - Tool calls with inputs/outputs\n# - Timestamps for debugging\n#\n# No configuration needed - logging is always enabled.\n# To disable, you would need to modify the source code.\n\n# =============================================================================\n# Code Execution Sandbox (Programmatic Tool Calling)\n# =============================================================================\n# The execute_code tool runs Python scripts that call Hermes tools via RPC.\n# Intermediate tool results stay out of the LLM's context window.\ncode_execution:\n timeout: 300 # Max seconds per script before kill (default: 300 = 5 min)\n max_tool_calls: 50 # Max RPC tool calls per execution (default: 50)\n\n# =============================================================================\n# Subagent Delegation\n# =============================================================================\n# The delegate_task tool spawns child agents with isolated context.\n# Supports single tasks and batch mode (default 3 parallel, configurable).\ndelegation:\n max_iterations: 50 # Max tool-calling turns per child (default: 50)\n # max_concurrent_children: 3 # Max parallel child agents per batch (default: 3, floor: 1, no ceiling).\n # WARNING: values above 10 multiply API cost linearly.\n # max_spawn_depth: 1 # Delegation tree depth cap (range: 1-3, default: 1 = flat).\n # Raise to 2 to allow workers to spawn their own subagents.\n # Requires role=\"orchestrator\" on intermediate agents.\n # orchestrator_enabled: true # Kill switch for role=\"orchestrator\" children (default: true).\n # subagent_auto_approve: false # When a subagent hits a dangerous-command approval prompt, auto-deny (default: false)\n # or auto-approve \"once\" (true) instead of blocking on stdin.\n # The parent TUI owns stdin, so blocking would deadlock; non-interactive resolution is required.\n # Both choices emit a logger.warning audit line. Flip to true only for cron/batch pipelines.\n # inherit_mcp_toolsets: true # When explicit child toolsets are narrowed, also keep the parent's MCP toolsets (default: true). Set false for strict intersection.\n # model: \"google/gemini-3-flash-preview\" # Override model for subagents (empty = inherit parent)\n # provider: \"openrouter\" # Override provider for subagents (empty = inherit parent)\n # # Resolves full credentials (base_url, api_key) automatically.\n # # Supported: openrouter, nous, zai, kimi-coding, minimax\n\n# =============================================================================\n# Honcho Integration (Cross-Session User Modeling)\n# =============================================================================\n# AI-native persistent memory via Honcho (https://honcho.dev/).\n# Builds a deeper understanding of the user across sessions and tools.\n# Runs alongside USER.md — additive, not a replacement.\n#\n# Requires: pip install honcho-ai\n# Config: ~/.honcho/config.json (shared with Claude Code, Cursor, etc.)\n# API key: HONCHO_API_KEY in ~/.hermes/.env or ~/.honcho/config.json\n#\n# Hermes-specific overrides (optional — most config comes from ~/.honcho/config.json):\n# honcho: {}\n\n# =============================================================================\n# Display\n# =============================================================================\ndisplay:\n # Use compact banner mode (hides the ASCII-art banner, shows a single line).\n # true: Compact single-line banner\n # false: Full ASCII banner with tool/skill summary (default)\n compact: false\n\n # Tool progress display level (CLI and gateway)\n # off: Silent — no tool activity shown, just the final response\n # new: Show a tool indicator only when the tool changes (skip repeats)\n # all: Show every tool call with a short preview (default)\n # verbose: Full args, results, and debug logs (same as /verbose)\n # Toggle at runtime with /verbose in the CLI\n tool_progress: all\n\n # Per-platform defaults can be quieter than the global setting. Telegram\n # tunes for mobile: tool_progress and busy_ack_detail default off (no\n # per-tool breadcrumb stream, no \"iteration 21/60\" debug detail in busy\n # acks or heartbeats), but interim_assistant_messages and\n # long_running_notifications STAY ON so the user has real signal between\n # turn start and final answer (mid-turn assistant commentary + a single\n # edit-in-place \"⏳ Working — N min\" heartbeat). Override under\n # display.platforms.telegram.\n\n # Auto-cleanup of temporary progress bubbles after the final response lands.\n # On platforms that support message deletion (currently Telegram), this\n # removes the tool-progress bubble, \"⏳ Still working...\" notices, and\n # context-pressure status messages once the final reply has been delivered —\n # keeping long-running turns visible live, then tidy afterward. Failed runs\n # leave the bubbles in place as breadcrumbs. Off by default.\n # Per-platform override: display.platforms.telegram.cleanup_progress\n # true: Delete tracked progress/status bubbles on successful turn\n # false: Leave everything in place (default)\n # Example:\n # display:\n # platforms:\n # telegram:\n # cleanup_progress: true\n cleanup_progress: false\n\n # Gateway-only natural mid-turn assistant updates.\n # When true, completed assistant status messages are sent as separate chat\n # messages. This is independent of tool_progress and gateway streaming.\n # true: Send mid-turn assistant updates as separate messages (default)\n # false: Only send the final response\n interim_assistant_messages: true\n\n # Gateway-only long-running status heartbeats.\n # When false, the platform does not receive periodic \"⏳ Working — N min\"\n # notifications even if agent.gateway_notify_interval is non-zero. The\n # heartbeat edits a single message in place (where the adapter supports\n # editing) instead of posting a new bubble each interval.\n # Default: true everywhere, including Telegram (silent agents are worse\n # than a single edit-in-place heartbeat).\n long_running_notifications: true\n\n # Include detailed iteration/tool/status context in busy acknowledgments\n # and long-running heartbeats. When true, busy acks show \"iteration 21/60,\n # terminal, 10 min\" and the heartbeat shows \"⏳ Working — 12 min,\n # iteration 21/60, terminal\". When false (Telegram default), both stay\n # terse: \"Interrupting current task\" and \"⏳ Working — 12 min, terminal\".\n busy_ack_detail: true\n\n # What Enter does when Hermes is already busy (CLI and gateway platforms).\n # interrupt: Interrupt the current run and redirect Hermes (default)\n # queue: Queue your message for the next turn\n # steer: Inject your message mid-run via /steer, arriving at the agent\n # after the next tool call — no interrupt, no role violation.\n # Falls back to 'queue' if the agent isn't running yet or if\n # images are attached (steer only carries text).\n # Ctrl+C (or /stop in gateway) always interrupts regardless of this setting.\n # Toggle at runtime with /busy .\n busy_input_mode: interrupt\n\n # Background process notifications (gateway/messaging only).\n # Controls how chatty the process watcher is when you use\n # terminal(background=true, notify_on_complete=true) from Telegram/Discord/etc.\n # off: No watcher messages at all\n # result: Only the final completion message\n # error: Only the final message when exit code != 0\n # all: Running output updates + final message (default)\n background_process_notifications: all\n\n\n # Play terminal bell when agent finishes a response.\n # Useful for long-running tasks — your terminal will ding when the agent is done.\n # Works over SSH. Most terminals can be configured to flash the taskbar or play a sound.\n # true: Ring the terminal bell on each response\n # false: Silent (default)\n bell_on_complete: false\n\n # Show model reasoning/thinking before each response.\n # When enabled, a dim box shows the model's thought process above the response.\n # Toggle at runtime with /reasoning show or /reasoning hide.\n # true: Show the reasoning box\n # false: Hide reasoning (default)\n show_reasoning: false\n\n # Stream tokens to the terminal as they arrive instead of waiting for the\n # full response. The response box opens on first token and text appears\n # line-by-line. Tool calls are still captured silently.\n # true: Stream tokens as they arrive (default)\n # false: Wait for the full response before rendering\n streaming: true\n\n # Show [HH:MM] timestamps on user input and assistant response labels.\n # timestamps: false\n\n # ───────────────────────────────────────────────────────────────────────────\n # Skin / Theme\n # ───────────────────────────────────────────────────────────────────────────\n # Customize CLI visual appearance — banner colors, spinner faces, tool prefix,\n # response box label, and branding text. Change at runtime with /skin .\n #\n # Built-in skins:\n # default — Classic Hermes gold/kawaii\n # ares — Crimson/bronze war-god theme with spinner wings\n # mono — Clean grayscale monochrome\n # slate — Cool blue developer-focused\n # daylight — Bright light-mode theme\n # warm-lightmode — Warm paper-tone light-mode theme\n # poseidon — Sea-green/teal Olympian theme\n # sisyphus — Earthy stone-and-moss theme\n # charizard — Fiery orange dragon theme\n #\n # Custom skins: drop a YAML file in ~/.hermes/skins/.yaml\n # Schema (all fields optional, missing values inherit from default):\n #\n # name: my-theme\n # description: Short description\n # colors:\n # banner_border: \"#HEX\" # Panel border\n # banner_title: \"#HEX\" # Panel title\n # banner_accent: \"#HEX\" # Section headers (Available Tools, etc.)\n # banner_dim: \"#HEX\" # Dim/muted text\n # banner_text: \"#HEX\" # Body text (tool names, skill names)\n # ui_accent: \"#HEX\" # UI accent color\n # response_border: \"#HEX\" # Response box border color\n # spinner:\n # waiting_faces: [\"(⚔)\", \"(⛨)\"] # Faces shown while waiting\n # thinking_faces: [\"(⚔)\", \"(⌁)\"] # Faces shown while thinking\n # thinking_verbs: [\"forging\", \"plotting\"] # Verbs for spinner messages\n # wings: # Optional left/right spinner decorations\n # - [\"⟪⚔\", \"⚔⟫\"]\n # - [\"⟪▲\", \"▲⟫\"]\n # branding:\n # agent_name: \"My Agent\" # Banner title and branding\n # welcome: \"Welcome message\" # Shown at CLI startup\n # response_label: \" ⚔ Agent \" # Response box header label\n # prompt_symbol: \"⚔\" # Prompt symbol (bare token; renderers add trailing space)\n # tool_prefix: \"╎\" # Tool output line prefix (default: ┊)\n #\n skin: default\n\n# =============================================================================\n# Model Aliases — short names for /model command\n# =============================================================================\n# Map short aliases to exact (model, provider, base_url) tuples.\n# Used by /model tab completion and resolve_alias().\n# Aliases are checked BEFORE the models.dev catalog, so they can route\n# to endpoints not in the catalog (e.g. Ollama Cloud, local servers).\n#\n# model_aliases:\n# opus:\n# model: claude-opus-4-6\n# provider: anthropic\n# qwen:\n# model: \"qwen3.5:397b\"\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n# glm:\n# model: glm-4.7\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n\n# =============================================================================\n# Privacy\n# =============================================================================\n# privacy:\n# # Redact PII from the LLM context prompt.\n# # When true, phone numbers are stripped and user/chat IDs are replaced\n# # with deterministic hashes before being sent to the model.\n# # Names and usernames are NOT affected (user-chosen, publicly visible).\n# # Routing/delivery still uses the original values internally.\n# redact_pii: false\n\n# =============================================================================\n# Shell-script hooks\n# =============================================================================\n# Register shell scripts as plugin-hook callbacks. Each entry is executed as\n# a subprocess (shell=False, shlex.split) with a JSON payload on stdin. On\n# stdout the script may return JSON that either blocks the tool call or\n# injects context into the next LLM call.\n#\n# Valid events (mirror hermes_cli.plugins.VALID_HOOKS):\n# pre_tool_call, post_tool_call, pre_llm_call, post_llm_call,\n# pre_api_request, post_api_request, on_session_start, on_session_end,\n# on_session_finalize, on_session_reset, subagent_stop\n#\n# First-use consent: each (event, command) pair prompts once on a TTY, then\n# is persisted to ~/.hermes/shell-hooks-allowlist.json. Non-interactive\n# runs (gateway, cron) need --accept-hooks, HERMES_ACCEPT_HOOKS=1, or the\n# hooks_auto_accept key below.\n#\n# See website/docs/user-guide/features/hooks.md for the full JSON wire\n# protocol and worked examples.\n#\n# hooks:\n# pre_tool_call:\n# - matcher: \"terminal\"\n# command: \"~/.hermes/agent-hooks/block-rm-rf.sh\"\n# timeout: 10\n# post_tool_call:\n# - matcher: \"write_file|patch\"\n# command: \"~/.hermes/agent-hooks/auto-format.sh\"\n# pre_llm_call:\n# - command: \"~/.hermes/agent-hooks/inject-cwd-context.sh\"\n# subagent_stop:\n# - command: \"~/.hermes/agent-hooks/log-orchestration.sh\"\n#\n# hooks_auto_accept: false\n\n\n# =============================================================================\n# Update Behavior\n# =============================================================================\nupdates:\n # Create a full HERMES_HOME zip before every `hermes update`.\n # Backups land in ~/.hermes/backups/ and can be restored with `hermes import`.\n # Off by default because large homes can add minutes to every update.\n pre_update_backup: false\n\n # Number of pre-update backup zips to retain.\n backup_keep: 5\n\n # What non-interactive updates do with local source edits in the Hermes repo.\n # Interactive terminal updates always prompt before restoring the autostash.\n #\n # stash - auto-stash before pull, then auto-restore after success (default)\n # discard - drop the update-created stash after success; use only on managed\n # installs where local source edits should not persist\n non_interactive_local_changes: \"stash\"\n\n\n# =============================================================================\n# Web Dashboard\n# =============================================================================\n# OAuth gate configuration for `hermes dashboard --host `.\n# The bundled Nous Portal plugin reads these on startup; settings here are\n# the canonical surface. Each can be overridden by an environment variable:\n#\n# dashboard.oauth.client_id <- HERMES_DASHBOARD_OAUTH_CLIENT_ID\n# dashboard.oauth.portal_url <- HERMES_DASHBOARD_PORTAL_URL\n# dashboard.public_url <- HERMES_DASHBOARD_PUBLIC_URL\n#\n# Env wins when set to a non-empty value. This is what Fly.io's platform-\n# secret injection uses to push per-deploy client_ids without needing to\n# bake a config.yaml into the image. Empty env values are treated as unset\n# so a provisioned-but-not-populated secret can't shadow a valid entry here.\n#\n# Local dev / on-prem deploys should typically set these via config.yaml\n# (the ~/.hermes/.env file is reserved for API keys and secrets).\n#\n# dashboard:\n# oauth:\n# client_id: \"\" # agent:{instance_id}; Portal provisions this at deploy\n# portal_url: \"\" # blank → default https://portal.nousresearch.com\n#\n# # Force the absolute base URL the OAuth callback (and any other public\n# # URL the dashboard hands to external systems) is built from. Set this\n# # for deploys behind reverse proxies that don't reliably forward\n# # X-Forwarded-Host / X-Forwarded-Proto / X-Forwarded-Prefix (manual\n# # nginx setups, on-prem ingresses, custom-domain Fly deploys without\n# # full proxy header chains).\n# #\n# # When set, the value is the complete authority: scheme + host +\n# # optional path prefix (e.g. \"https://example.com/hermes\"). The OAuth\n# # callback URL becomes \"/auth/callback\" — X-Forwarded-Prefix\n# # is IGNORED on this code path because the operator has explicitly\n# # declared the public URL and we no longer need to guess.\n# #\n# # Leave empty to use the existing proxy-header reconstruction (the\n# # default — works on Fly.io out of the box).\n# #\n# # public_url: \"https://example.com/hermes\"\n"}, "e3c9924b8b358e4b7b0a600178426fd70e6f9481": {"t": 1785767894.963981, "n": 65335, "h": "64a70e00eb91cbbc", "c": "# Hermes Agent CLI Configuration\n# Copy this file to cli-config.yaml and customize as needed.\n# This file configures the CLI behavior. Environment variables in .env take precedence.\n\n# =============================================================================\n# Model Configuration\n# =============================================================================\nmodel:\n # Default model to use (can be overridden with --model flag)\n # Both \"default\" and \"model\" work as the key name here.\n default: \"anthropic/claude-opus-4.6\"\n \n # Inference provider selection:\n # \"auto\" - Auto-detect from credentials (default)\n # \"openrouter\" - OpenRouter (requires: OPENROUTER_API_KEY or OPENAI_API_KEY)\n # \"nous\" - Nous Portal OAuth (requires: hermes auth add nous)\n # \"nous-api\" - Nous Portal API key (requires: NOUS_API_KEY)\n # \"anthropic\" - Direct Anthropic API (requires: ANTHROPIC_API_KEY)\n # \"openai-codex\" - OpenAI Codex (requires: hermes auth)\n # \"copilot\" - GitHub Copilot / GitHub Models (requires: GITHUB_TOKEN)\n # \"gemini\" - Use Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n # \"zai\" - Use z.ai / ZhipuAI GLM models (requires: GLM_API_KEY)\n # \"kimi-coding\" - Kimi / Moonshot AI (requires: KIMI_API_KEY)\n # \"minimax\" - MiniMax global (requires: MINIMAX_API_KEY)\n # \"minimax-cn\" - MiniMax China (requires: MINIMAX_CN_API_KEY)\n # \"huggingface\" - Hugging Face Inference (requires: HF_TOKEN)\n # \"nvidia\" - NVIDIA NIM / build.nvidia.com (requires: NVIDIA_API_KEY)\n # \"xiaomi\" - Xiaomi MiMo (requires: XIAOMI_API_KEY)\n # \"arcee\" - Arcee AI Trinity models (requires: ARCEEAI_API_KEY)\n # \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY — https://ollama.com/settings)\n # \"kilocode\" - KiloCode gateway (requires: KILOCODE_API_KEY)\n # \"azure-foundry\" - Microsoft Foundry / Azure OpenAI (API key or Entra ID)\n # \"lmstudio\" - LM Studio local server (optional: LM_API_KEY, defaults to http://127.0.0.1:1234/v1)\n #\n # Local servers (LM Studio, Ollama, vLLM, llama.cpp):\n # \"custom\" - Any other OpenAI-compatible endpoint. Set base_url below.\n # Aliases: \"ollama\", \"vllm\", \"llamacpp\" all map to \"custom\".\n # LM Studio is first-class and uses provider: \"lmstudio\".\n # It works with both no-auth and auth-enabled server modes.\n #\n # Can also be overridden for a single invocation with the --provider flag.\n provider: \"auto\"\n \n # API configuration (falls back to OPENROUTER_API_KEY env var)\n # api_key: \"your-key-here\" # Uncomment to set here instead of .env\n base_url: \"https://openrouter.ai/api/v1\"\n\n # Azure Foundry keyless auth example:\n # provider: \"azure-foundry\"\n # base_url: \"https://.openai.azure.com/openai/v1\"\n # auth_mode: \"entra_id\" # DefaultAzureCredential: az login, managed identity, workload identity, etc.\n # default: \"gpt-4o\" # Deployment/model name\n # entra:\n # scope: \"https://ai.azure.com/.default\" # Optional; this is the default.\n\n # ── Token limits — two settings, easy to confuse ──────────────────────────\n #\n # context_length: TOTAL context window (input + output tokens combined).\n # Controls when Hermes compresses history and validates requests.\n # Leave unset — Hermes auto-detects the correct value from the provider.\n # Set manually only when auto-detection is wrong (e.g. a local server with\n # a custom num_ctx, or a proxy that doesn't expose /v1/models).\n #\n # context_length: 131072\n #\n # max_tokens: OUTPUT cap — maximum tokens the model may generate per response.\n # Unrelated to how long your conversation history can be.\n # The OpenAI-standard name \"max_tokens\" is a misnomer; Anthropic's native\n # API has since renamed it \"max_output_tokens\" for clarity.\n # Leave unset to use the model's native output ceiling (recommended).\n # Set only if you want to deliberately limit individual response length.\n #\n# max_tokens: 8192\n\n # ── Custom request headers (optional) ─────────────────────────────────────\n #\n # default_headers: extra HTTP headers sent on every request to an\n # OpenAI-compatible endpoint. User values take precedence over the\n # provider/SDK defaults, so this is the supported way to override the\n # OpenAI Python SDK's identifying headers (User-Agent: OpenAI/Python ...,\n # X-Stainless-*) when a custom provider sits behind a gateway/WAF that\n # rejects them — e.g. an upstream that returns \"502 Upstream access\n # forbidden\" for the SDK default User-Agent but accepts a plain one.\n # Applies on the OpenAI wire only (not native Anthropic / Bedrock).\n #\n # default_headers:\n # User-Agent: \"curl/8.7.1\"\n\n# Named provider overrides (optional)\n# Use this for per-provider request timeouts, non-stream stale timeouts,\n# and per-model exceptions.\n# Applies to the primary turn client on every api_mode (OpenAI-wire, native\n# Anthropic, and Anthropic-compatible providers), the fallback chain, and\n# client rebuilds during credential rotation. For OpenAI-wire chat\n# completions (streaming and non-streaming) the configured value is also\n# used as the per-request ``timeout=`` kwarg so it wins over the legacy\n# HERMES_API_TIMEOUT env var (which still applies when no config is set).\n# ``stale_timeout_seconds`` controls the non-streaming stale-call detector and\n# wins over the legacy HERMES_API_CALL_STALE_TIMEOUT env var. Leaving these\n# unset keeps the legacy defaults (HERMES_API_TIMEOUT=1800s,\n# HERMES_API_CALL_STALE_TIMEOUT=90s, native Anthropic 900s). The\n# implicit non-stream stale detector is auto-disabled for local endpoints\n# and can scale upward for very large contexts.\n#\n# Not currently wired for AWS Bedrock (bedrock_converse + AnthropicBedrock\n# SDK paths) — those use boto3 with its own timeout configuration.\n#\n# providers:\n# ollama-local:\n# request_timeout_seconds: 300 # Longer timeout for local cold-starts\n# stale_timeout_seconds: 900 # Explicitly re-enable stale detection on local endpoints\n# anthropic:\n# request_timeout_seconds: 30 # Fast-fail cloud requests\n# models:\n# claude-opus-4.6:\n# timeout_seconds: 600 # Longer timeout for extended-thinking Opus calls\n# openai-codex:\n# models:\n# gpt-5.4:\n# stale_timeout_seconds: 1800 # Longer non-stream stale timeout for slow large-context turns\n\n# =============================================================================\n# OpenRouter Provider Routing (only applies when using OpenRouter)\n# =============================================================================\n# Control how requests are routed across providers on OpenRouter.\n# See: https://openrouter.ai/docs/guides/routing/provider-selection\n#\n# provider_routing:\n# # Sort strategy: \"price\" (default), \"throughput\", or \"latency\"\n# # Append :nitro to model name for a shortcut to throughput sorting.\n# sort: \"throughput\"\n#\n# # Only allow these providers (provider slugs from OpenRouter)\n# # only: [\"anthropic\", \"google\"]\n#\n# # Skip these providers entirely\n# # ignore: [\"deepinfra\", \"fireworks\"]\n#\n# # Try providers in this order (overrides default load balancing)\n# # order: [\"anthropic\", \"google\", \"together\"]\n#\n# # Require providers to support all parameters in your request\n# # require_parameters: true\n#\n# # Data policy: \"allow\" (default) or \"deny\" to exclude providers that may store data\n# # data_collection: \"deny\"\n\n# =============================================================================\n# OpenRouter Response Caching (only applies when using OpenRouter)\n# =============================================================================\n# Cache identical API responses at the OpenRouter edge for free instant replays.\n# When enabled, identical requests (same model, messages, parameters) return\n# cached responses with zero billing. Separate from Anthropic prompt caching.\n# See: https://openrouter.ai/docs/guides/features/response-caching\n#\n# openrouter:\n# response_cache: true # Enable response caching (default: true)\n# response_cache_ttl: 300 # Cache TTL in seconds, 1-86400 (default: 300)\n\n# =============================================================================\n# Git Worktree Isolation\n# =============================================================================\n# When enabled, each CLI session creates an isolated git worktree so multiple\n# agents can work on the same repo concurrently without file collisions.\n# Equivalent to always passing --worktree / -w on the command line.\n#\n# worktree: true # Always create a worktree when in a git repo\n# worktree: false # Default — only create when -w flag is passed\n#\n# By default a new worktree branches from the freshly-fetched remote tip\n# (the current branch's upstream, else the remote's default branch) so it\n# starts current with the project instead of from the local clone's\n# (possibly stale) HEAD. Set worktree_sync: false to branch from local HEAD\n# instead — useful when offline or when you deliberately want the clone's\n# exact current state as the base.\n#\n# worktree_sync: true # Default — branch from the fetched remote tip\n# worktree_sync: false # Branch from local HEAD (offline / pinned base)\n\n# =============================================================================\n# Terminal Tool Configuration\n# =============================================================================\n# Choose ONE of the following terminal configurations by uncommenting it.\n# The terminal tool executes commands in the specified environment.\n\n# -----------------------------------------------------------------------------\n# OPTION 1: Local execution (default)\n# Commands run directly on your machine in the current directory\n# -----------------------------------------------------------------------------\n# Working directory behavior:\n# - CLI (`hermes` command): Uses \".\" (current directory where you run hermes)\n# - Gateway/messaging/cron: Uses terminal.cwd here; legacy .env cwd values are deprecated\nterminal:\n backend: \"local\"\n cwd: \".\" # For local backend: \".\" = current directory. Ignored for remote backends unless a backend documents otherwise.\n timeout: 180\n # HOME policy for tool subprocesses:\n # auto - default: host uses your real HOME; containers use HERMES_HOME/home\n # real - force your real OS-user HOME\n # profile - force HERMES_HOME/home for strict per-profile CLI config isolation\n home_mode: \"auto\"\n docker_mount_cwd_to_workspace: false # SECURITY: off by default. Opt in to mount the launch cwd into Docker /workspace.\n lifetime_seconds: 300\n # sudo_password: \"hunter2\" # Optional: pipe a sudo password via sudo -S. SECURITY WARNING: plaintext.\n # sudo_password: \"\" # Explicit empty password: try empty and never open the interactive sudo prompt.\n\n# -----------------------------------------------------------------------------\n# OPTION 2: SSH remote execution\n# Commands run on a remote server - agent code stays local (sandboxed)\n# Great for: keeping agent isolated from its own code, using powerful remote hardware\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"ssh\"\n# cwd: \"/home/myuser/project\" # Path on the REMOTE server\n# timeout: 180\n# lifetime_seconds: 300\n# ssh_host: \"my-server.example.com\"\n# ssh_user: \"myuser\"\n# ssh_port: 22\n# ssh_key: \"~/.ssh/id_rsa\" # Optional - uses ssh-agent if not specified\n\n# -----------------------------------------------------------------------------\n# OPTION 3: Docker container\n# Commands run in an isolated Docker container\n# Great for: reproducible environments, testing, isolation\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"docker\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /)\n# timeout: 180\n# lifetime_seconds: 300\n# docker_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# docker_mount_cwd_to_workspace: true # Explicit opt-in: mount your launch cwd into /workspace\n# # Optional: run the container as your host user's uid:gid so files written\n# # into bind-mounted dirs are owned by you, not root. Drops SETUID/SETGID\n# # caps too since no gosu privilege drop is needed. Leave off if your\n# # chosen docker_image expects to start as root.\n# docker_run_as_host_user: true\n# # Optional: explicitly forward selected env vars into Docker.\n# # These values come from your current shell first, then ~/.hermes/.env.\n# # Warning: anything forwarded here is visible to commands run in the container.\n# docker_forward_env:\n# - \"GITHUB_TOKEN\"\n# - \"NPM_TOKEN\"\n# # Optional: extra flags passed verbatim to docker run (appended after security defaults).\n# # Useful for adding capabilities (e.g. apt installs needing SETUID) or custom options.\n# # Example: add a Linux capability not included by default\n# # docker_extra_args:\n# # - \"--cap-add\"\n# # - \"SETUID\"\n\n# -----------------------------------------------------------------------------\n# OPTION 4: Singularity/Apptainer container\n# Commands run in a Singularity container (common in HPC environments)\n# Great for: HPC clusters, shared compute environments\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"singularity\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# singularity_image: \"docker://nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 5: Modal cloud execution\n# Commands run on Modal's cloud infrastructure\n# Great for: GPU access, scalable compute, serverless execution\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"modal\"\n# cwd: \"/workspace\" # Path INSIDE the sandbox (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# modal_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 6: Daytona cloud execution\n# Commands run in Daytona cloud sandboxes\n# Great for: Cloud dev environments, persistent workspaces, team collaboration\n# Requires: pip install daytona, DAYTONA_API_KEY env var\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"daytona\"\n# cwd: \"~\"\n# timeout: 180\n# lifetime_seconds: 300\n# daytona_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# container_disk: 10240 # Daytona max is 10GB per sandbox\n\n#\n# --- Container resource limits (docker, singularity, modal, daytona -- ignored for local/ssh) ---\n# These settings apply to all container backends. They control the resources\n# allocated to the sandbox and whether its filesystem persists across sessions.\n container_cpu: 1 # CPU cores\n container_memory: 5120 # Memory in MB (5120 = 5GB)\n container_disk: 51200 # Disk in MB (51200 = 50GB)\n container_persistent: true # Persist filesystem across sessions (false = ephemeral)\n\n# -----------------------------------------------------------------------------\n# SUDO SUPPORT (works with ALL backends above)\n# -----------------------------------------------------------------------------\n# Add sudo_password to any terminal config above to enable sudo commands.\n# The password is piped via `sudo -S`. Works with local, ssh, docker, etc.\n#\n# SECURITY WARNING: Password stored in plaintext!\n#\n# INTERACTIVE PROMPT: If sudo_password is unset and the CLI is running,\n# you'll be prompted to enter your password when sudo is needed:\n# - 45-second timeout (auto-skips if no input)\n# - Press Enter to skip (command fails gracefully)\n# - Password is hidden while typing\n# - Password is cached for the session\n#\n# EMPTY PASSWORDS: Setting sudo_password to an explicit empty string is different\n# from leaving it unset. Hermes will try an empty password via `sudo -S` and\n# will not open the interactive prompt. This is useful for passwordless sudo,\n# Touch ID sudo setups, and environments where prompting is just noise.\n#\n# ALTERNATIVES:\n# - SSH backend: Configure passwordless sudo on the remote server\n# - Containers: Run as root inside the container (no sudo needed)\n# - Local: Configure /etc/sudoers for specific commands\n#\n# Example (add to your terminal section):\n# sudo_password: \"your-password-here\"\n\n# =============================================================================\n# Security Scanning (tirith)\n# =============================================================================\n# Optional pre-exec command security scanning via tirith.\n# Detects homograph URLs, pipe-to-shell, terminal injection, env manipulation.\n# Install: brew install sheeki03/tap/tirith\n# Docs: https://github.com/sheeki03/tirith\n#\n# security:\n# tirith_enabled: true # Enable/disable tirith scanning\n# tirith_path: \"tirith\" # Path to tirith binary (supports ~ expansion)\n# tirith_timeout: 5 # Scan timeout in seconds\n# tirith_fail_open: true # Allow commands if tirith unavailable\n\n# =============================================================================\n# Browser Tool Configuration\n# =============================================================================\nbrowser:\n # Inactivity timeout in seconds - browser sessions are automatically closed\n # after this period of no activity between agent loops (default: 120 = 2 minutes)\n inactivity_timeout: 120\n\n# =============================================================================\n# Tool Loop Guardrails\n# =============================================================================\n# Soft warnings are enabled by default. They append guidance to repeated failed\n# or non-progressing tool results but still let the tool execute. Hard stops are\n# opt-in circuit breakers for autonomous/cron sessions where stopping a loop is\n# preferable to spending the full iteration budget.\ntool_loop_guardrails:\n warnings_enabled: true\n hard_stop_enabled: false\n warn_after:\n exact_failure: 2\n same_tool_failure: 3\n idempotent_no_progress: 2\n hard_stop_after:\n exact_failure: 5\n same_tool_failure: 8\n idempotent_no_progress: 5\n\n# =============================================================================\n# Context Compression (Auto-shrinks long conversations)\n# =============================================================================\n# When conversation approaches model's context limit, middle turns are\n# automatically summarized to free up space while preserving important context.\n#\n# HOW IT WORKS:\n# 1. Tracks actual token usage from API responses (not estimates)\n# 2. When prompt_tokens >= threshold% of model's context_length, triggers compression\n# 3. Protects first 3 turns (system prompt, initial request, first response)\n# 4. Protects last N turns (default 20 messages = ~10 full turns of recent context)\n# 5. Summarizes middle turns using a fast/cheap model\n# 6. Inserts summary as a user message, continues conversation seamlessly\n#\n# Post-compression tail budget is target_ratio × threshold × context_length:\n# 200K context, threshold 0.50, ratio 0.20 → 20K tokens of recent tail preserved\n# 1M context, threshold 0.50, ratio 0.20 → 100K tokens of recent tail preserved\n#\ncompression:\n # Enable automatic context compression (default: true)\n # Set to false if you prefer to manage context manually or want errors on overflow\n enabled: true\n \n # Trigger compression at this % of model's context limit (default: 0.50 = 50%)\n # Lower values = more aggressive compression, higher values = compress later\n threshold: 0.50\n \n # Fraction of the threshold to preserve as recent tail (default: 0.20 = 20%)\n # e.g. 20% of 50% threshold = 10% of total context kept as recent messages.\n # Summary output is separately capped at 12K tokens (Gemini output limit).\n # Range: 0.10 - 0.80\n target_ratio: 0.20\n\n # Number of most-recent messages to always preserve (default: 20 ≈ 10 full turns)\n # Higher values keep more recent conversation intact at the cost of more aggressive\n # compression of older turns.\n protect_last_n: 20\n\n # Number of non-system messages to protect at the head of the transcript, in\n # ADDITION to the system prompt (which is always implicitly protected).\n # Head messages are NEVER summarized — they survive every compression\n # indefinitely. This gives stable early context for short/medium sessions,\n # but in long-running sessions that rely on rolling compaction the pinned\n # opening turns may not match how you want the session framed over time.\n # Set to 0 to preserve ONLY the system prompt (plus the rolling summary\n # and recent tail) — the cleanest configuration for long-running sessions.\n # Default 3 preserves the system prompt plus the first three non-system\n # head messages, matching the pre-feature behaviour.\n protect_first_n: 3\n\n # To pin a specific model/provider for compression summaries, use the\n # auxiliary section below (auxiliary.compression.provider / model).\n\n# =============================================================================\n# Anthropic prompt caching TTL\n# =============================================================================\n# When prompt caching is active (Claude via OpenRouter or native Anthropic),\n# Anthropic supports two TTL tiers for cached prefixes: \"5m\" (default) and\n# \"1h\". Other values are ignored and \"5m\" is used.\n#\nprompt_caching:\n cache_ttl: \"5m\" # use \"1h\" for long sessions with pauses between turns\n\n# =============================================================================\n# Auxiliary Models (Advanced — Experimental)\n# =============================================================================\n# Hermes uses lightweight \"auxiliary\" models for side tasks: image analysis,\n# browser screenshot analysis, web page summarization, TTS audio-tag insertion,\n# and context compression.\n#\n# By default these use Gemini Flash via OpenRouter or Nous Portal and are\n# auto-detected from your credentials. You do NOT need to change anything\n# here for normal usage.\n#\n# WARNING: Overriding these with providers other than OpenRouter or Nous Portal\n# is EXPERIMENTAL and may not work. Not all models/providers support vision,\n# produce usable summaries, or accept the same API format. Change at your own\n# risk — if things break, reset to \"auto\" / empty values.\n#\n# Each task has its own provider + model pair so you can mix providers.\n# For example: OpenRouter for vision (needs multimodal), but your main\n# local endpoint for compression (just needs text).\n#\n# Provider options:\n# \"auto\" - Best available: OpenRouter → Nous Portal → main endpoint (default)\n# \"openrouter\" - Force OpenRouter (requires OPENROUTER_API_KEY)\n# \"nous\" - Force Nous Portal (requires: hermes auth add nous)\n# \"gemini\" - Force Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n# \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY)\n# \"codex\" - Force Codex OAuth (requires: hermes model → Codex).\n# Uses gpt-5.3-codex which supports vision.\n# \"main\" - Use your custom endpoint (OPENAI_BASE_URL + OPENAI_API_KEY).\n# Works with OpenAI API, local models, or any OpenAI-compatible\n# endpoint. Also falls back to Codex OAuth and API-key providers.\n#\n# Model: leave empty to use the provider's default. When empty, OpenRouter\n# uses \"google/gemini-3-flash-preview\" and Nous uses \"gemini-3-flash\".\n# Other providers pick a sensible default automatically.\n#\n# auxiliary:\n# # Image analysis: vision_analyze tool + browser screenshots\n# vision:\n# provider: \"auto\"\n# model: \"\" # e.g. \"google/gemini-2.5-flash\", \"openai/gpt-4o\"\n# timeout: 30 # LLM API call timeout (seconds)\n# download_timeout: 30 # Image HTTP download timeout (seconds)\n# # Increase for slow connections or self-hosted image servers\n#\n# # Web page scraping / summarization + browser page text extraction\n# web_extract:\n# provider: \"auto\"\n# model: \"\"\n#\n# # Gemini 3.1 TTS hidden audio-tag insertion\n# tts_audio_tags:\n# provider: \"auto\" # empty model = your main chat model\n# model: \"\"\n# timeout: 30\n#\n# # Session search — summarizes matching past sessions\n# session_search:\n# provider: \"auto\"\n# model: \"\"\n# timeout: 30\n# max_concurrency: 3 # Limit parallel summaries to reduce request-burst 429s\n# extra_body: {} # Provider-specific OpenAI-compatible request fields\n# # Example for providers that support request-body\n# # reasoning controls:\n# # extra_body:\n# # enable_thinking: false\n# # Some vLLM/Qwen deployments expect this nested:\n# # extra_body:\n# # chat_template_kwargs:\n# # enable_thinking: false\n\n# =============================================================================\n# Persistent Memory\n# =============================================================================\n# Bounded curated memory injected into the system prompt every session.\n# Two stores: MEMORY.md (agent's notes) and USER.md (user profile).\n# Character limits keep the memory small and focused. The agent manages\n# pruning -- when at the limit, it must consolidate or replace entries.\n# Disabled by default in batch_runner.\n#\nmemory:\n # Agent's personal notes: environment facts, conventions, things learned\n memory_enabled: true\n \n # User profile: preferences, communication style, expectations\n user_profile_enabled: true\n \n # Character limits (~2.75 chars per token, model-independent)\n memory_char_limit: 2200 # ~800 tokens\n user_char_limit: 1375 # ~500 tokens\n\n # Periodic memory nudge: remind the agent to consider saving memories\n # every N user turns. Set to 0 to disable. Only active when memory is enabled.\n nudge_interval: 10 # Nudge every 10 user turns (0 = disabled)\n\n # Memory flush: give the agent one turn to save memories before context is\n # lost (compression, /new, /reset, exit). Set to 0 to disable.\n # For exit/reset, only fires if the session had at least this many user turns.\n flush_min_turns: 6 # Min user turns to trigger flush on exit/reset (0 = disabled)\n\n# =============================================================================\n# Session Reset Policy (Messaging Platforms)\n# =============================================================================\n# Controls when messaging sessions (Telegram, Discord, WhatsApp, Slack) are\n# automatically cleared. Without resets, conversation context grows indefinitely\n# which increases API costs with every message.\n#\n# When a reset triggers, the agent first saves important information to its\n# persistent memory — but the conversation context is wiped. The agent starts\n# fresh but retains learned facts via its memory system.\n#\n# Users can always manually reset with /reset or /new in chat.\n#\n# Modes:\n# \"both\" - Reset on EITHER inactivity timeout or daily boundary (recommended)\n# \"idle\" - Reset only after N minutes of inactivity\n# \"daily\" - Reset only at a fixed hour each day\n# \"none\" - Never auto-reset; context lives until /reset or compression kicks in\n#\n# When a reset triggers, the agent gets one turn to save important memories and\n# skills before the context is wiped. Persistent memory carries across sessions.\n#\nsession_reset:\n mode: both # \"both\", \"idle\", \"daily\", or \"none\"\n idle_minutes: 1440 # Inactivity timeout in minutes (default: 1440 = 24 hours)\n at_hour: 4 # Daily reset hour, 0-23 local time (default: 4 AM)\n\n# Maximum number of simultaneously active chat sessions across CLI, TUI,\n# dashboard chat, and messaging gateway. Set to null, 0, or omit to allow\n# unlimited concurrent sessions. When the limit is reached, new sessions get a\n# clean error while existing active sessions keep their normal behavior. This\n# top-level key takes precedence over gateway.max_concurrent_sessions. The cap\n# is a best-effort single-host/profile runtime guard; Hermes fails open if the\n# local runtime lease registry cannot be read or locked.\nmax_concurrent_sessions: null\n\n# When true, group/channel chats use one session per participant when the platform\n# provides a user ID. This is the secure default and prevents users in the same\n# room from sharing context, interrupts, and token costs. Set false only if you\n# explicitly want one shared \"room brain\" per group/channel.\ngroup_sessions_per_user: true\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Gateway Streaming\n# ─────────────────────────────────────────────────────────────────────────────\n# Stream tokens to messaging platforms in real-time. The bot sends a message\n# on first token, then progressively edits it as more tokens arrive.\n# Disabled by default — enable to try the streaming UX on Telegram/Discord/Slack.\n# For Telegram, partial edits are sent as plain text and only the final edit uses MarkdownV2.\nstreaming:\n enabled: false\n # transport: edit # \"edit\" = progressive editMessageText\n # edit_interval: 0.3 # seconds between message edits\n # buffer_threshold: 40 # chars before forcing an edit flush\n # cursor: \" ▉\" # cursor shown during streaming\n\n# =============================================================================\n# Skills Configuration\n# =============================================================================\n# Skills are reusable procedures the agent can load and follow. The agent can\n# also create new skills after completing complex tasks.\n#\nskills:\n # Nudge the agent to create skills after complex tasks.\n # Every N tool-calling iterations, remind the model to consider saving a skill.\n # Set to 0 to disable.\n creation_nudge_interval: 15\n\n # External skill directories — share skills across tools/agents without\n # copying them into ~/.hermes/skills/. Each path is expanded (~ and ${VAR})\n # and resolved to an absolute path. External dirs are read-only: skill\n # creation always writes to ~/.hermes/skills/. Local skills take precedence\n # when names collide.\n # external_dirs:\n # - ~/.agents/skills\n # - /home/shared/team-skills\n\n# =============================================================================\n# Agent Behavior\n# =============================================================================\nagent:\n # Maximum tool-calling iterations per conversation\n # Higher = more room for complex tasks, but costs more tokens\n # Recommended: 20-30 for focused tasks, 50-100 for open exploration\n max_turns: 60\n\n # Inactivity timeout for gateway agent runs (seconds, 0 = unlimited).\n # The agent can run indefinitely when actively calling tools or receiving\n # API responses. Only fires after the agent has been idle for this duration.\n # gateway_timeout: 1800\n\n # Staged warning: send a warning before escalating to full timeout.\n # Fires once per run when inactivity reaches this threshold (seconds).\n # Set to 0 to disable the warning.\n # gateway_timeout_warning: 900\n\n # Graceful drain timeout for gateway stop/restart (seconds).\n # The gateway stops accepting new work, waits for in-flight agents to\n # finish, then interrupts anything still running after this timeout.\n # 0 = no drain, interrupt immediately.\n # restart_drain_timeout: 60\n\n # Max app-level retry attempts for API errors (connection drops, provider\n # timeouts, 5xx, etc.) before the agent surfaces the failure. Lower this\n # to 1 if you use fallback providers and want fast failover on flaky\n # primaries (default 3). The OpenAI SDK does its own low-level retries\n # underneath this wrapper — this is the Hermes-level loop.\n # api_max_retries: 3\n\n # After the agent edits code without fresh passing verification, nudge it to\n # verify before finishing. The default \"auto\" enables it on interactive\n # coding surfaces (CLI, TUI, desktop) and programmatic callers, and disables\n # it on conversational messaging surfaces (Telegram, Discord, etc.) where the\n # verification summary would reach a human as chat noise. Set true or false to\n # force it on or off; the HERMES_VERIFY_ON_STOP env var (1/0) takes precedence.\n # verify_on_stop: auto\n\n # Enable verbose logging\n verbose: false\n \n # Reasoning effort level (OpenRouter and Nous Portal)\n # Controls how much \"thinking\" the model does before responding.\n # Options: \"xhigh\" (max), \"high\", \"medium\", \"low\", \"minimal\", \"none\" (disable)\n reasoning_effort: \"medium\"\n \n # Predefined personalities (use with /personality command)\n personalities:\n helpful: \"You are a helpful, friendly AI assistant.\"\n concise: \"You are a concise assistant. Keep responses brief and to the point.\"\n technical: \"You are a technical expert. Provide detailed, accurate technical information.\"\n creative: \"You are a creative assistant. Think outside the box and offer innovative solutions.\"\n teacher: \"You are a patient teacher. Explain concepts clearly with examples.\"\n kawaii: \"You are a kawaii assistant! Use cute expressions like (◕‿◕), ★, ♪, and ~! Add sparkles and be super enthusiastic about everything! Every response should feel warm and adorable desu~! ヽ(>∀<☆)ノ\"\n catgirl: \"You are Neko-chan, an anime catgirl AI assistant, nya~! Add 'nya' and cat-like expressions to your speech. Use kaomoji like (=^・ω・^=) and ฅ^•ﻌ•^ฅ. Be playful and curious like a cat, nya~!\"\n pirate: \"Arrr! Ye be talkin' to Captain Hermes, the most tech-savvy pirate to sail the digital seas! Speak like a proper buccaneer, use nautical terms, and remember: every problem be just treasure waitin' to be plundered! Yo ho ho!\"\n shakespeare: \"Hark! Thou speakest with an assistant most versed in the bardic arts. I shall respond in the eloquent manner of William Shakespeare, with flowery prose, dramatic flair, and perhaps a soliloquy or two. What light through yonder terminal breaks?\"\n surfer: \"Duuude! You're chatting with the chillest AI on the web, bro! Everything's gonna be totally rad. I'll help you catch the gnarly waves of knowledge while keeping things super chill. Cowabunga! 🤙\"\n noir: \"The rain hammered against the terminal like regrets on a guilty conscience. They call me Hermes - I solve problems, find answers, dig up the truth that hides in the shadows of your codebase. In this city of silicon and secrets, everyone's got something to hide. What's your story, pal?\"\n uwu: \"hewwo! i'm your fwiendwy assistant uwu~ i wiww twy my best to hewp you! *nuzzles your code* OwO what's this? wet me take a wook! i pwomise to be vewy hewpful >w<\"\n philosopher: \"Greetings, seeker of wisdom. I am an assistant who contemplates the deeper meaning behind every query. Let us examine not just the 'how' but the 'why' of your questions. Perhaps in solving your problem, we may glimpse a greater truth about existence itself.\"\n hype: \"YOOO LET'S GOOOO!!! 🔥🔥🔥 I am SO PUMPED to help you today! Every question is AMAZING and we're gonna CRUSH IT together! This is gonna be LEGENDARY! ARE YOU READY?! LET'S DO THIS! 💪😤🚀\"\n\n# =============================================================================\n# Toolsets\n# =============================================================================\n# Control which tools the agent has access to.\n# Use `hermes tools` to interactively enable/disable tools per platform.\n\n# =============================================================================\n# Platform Toolsets (per-platform tool configuration)\n# =============================================================================\n# Override which toolsets are available on each platform.\n# If a platform isn't listed here, its built-in default is used.\n#\n# You can use EITHER:\n# - A preset like \"hermes-cli\" or \"hermes-telegram\" (curated tool set)\n# - A list of individual toolsets to compose your own (see list below)\n#\n# Supported platform keys: cli, telegram, discord, whatsapp, slack, qqbot, teams, google_chat\n#\n# Examples:\n#\n# # Use presets (same as defaults):\n# platform_toolsets:\n# cli: [hermes-cli]\n# telegram: [hermes-telegram]\n#\n# # Custom: give Telegram only web + terminal + file + planning:\n# platform_toolsets:\n# telegram: [web, terminal, file, todo]\n#\n# # Custom: CLI without browser or image gen:\n# platform_toolsets:\n# cli: [web, terminal, file, skills, todo, tts, cronjob]\n#\n# # Restrictive: Discord gets read-only tools only:\n# platform_toolsets:\n# discord: [web, vision, skills, todo]\n#\n# If not set, defaults are:\n# cli: hermes-cli (everything + cronjob management)\n# telegram: hermes-telegram (terminal, file, web, vision, image, tts, browser, skills, todo, cronjob, messaging)\n# discord: hermes-discord (same as telegram)\n# whatsapp: hermes-whatsapp (same as telegram)\n# slack: hermes-slack (same as telegram)\n# signal: hermes-signal (same as telegram)\n# homeassistant: hermes-homeassistant (same as telegram)\n# qqbot: hermes-qqbot (same as telegram)\n# teams: hermes-teams (same as telegram)\n# google_chat: hermes-google_chat (same as telegram)\n#\nplatform_toolsets:\n cli: [hermes-cli]\n telegram: [hermes-telegram]\n discord: [hermes-discord]\n whatsapp: [hermes-whatsapp]\n slack: [hermes-slack]\n signal: [hermes-signal]\n homeassistant: [hermes-homeassistant]\n qqbot: [hermes-qqbot]\n yuanbao: [hermes-yuanbao]\n teams: [hermes-teams]\n google_chat: [hermes-google_chat]\n\n# =============================================================================\n# Gateway Platform Settings\n# =============================================================================\n# Optional per-platform messaging settings.\n# Platform-specific knobs live under `extra`.\n#\n# platforms:\n# telegram:\n# reply_to_mode: \"first\" # off | first | all\n# # guest_mode lets explicit @mentions from non-allowlisted groups through.\n# # Default false; ordinary messages, replies, and regex wake words stay blocked.\n# guest_mode: false\n# # allowed_chats: [\"-1001234567890\"]\n# extra:\n# disable_link_previews: false # Set true to suppress Telegram URL previews in bot messages\n# rich_messages: false # Bot API 10.1 rich messages (tables/task lists/details/math); default false for copyable legacy MarkdownV2, set true to opt in\n# rich_drafts: false # Experimental rich draft previews during Telegram DM streaming; default false because Telegram Desktop/macOS can visually overlay draft frames\n# command_menu:\n# # Telegram allows up to 100 BotCommands; Hermes defaults to 60 so\n# # all built-in commands plus common skill commands stay visible\n# # while remaining under Telegram's payload-size limit. Clamped 1..100.\n# max_commands: 60\n# # prepend = user priority first, then Hermes defaults\n# # append = Hermes defaults first, then user priority\n# # replace = only the list below defines priority\n# priority_mode: prepend\n# priority:\n# - my_plugin_command\n#\n# Discord-specific settings (config.yaml top-level, not under platforms:):\n#\n# discord:\n# require_mention: true # Require @mention in server channels (default: true)\n# auto_thread: true # Auto-create thread on @mention (default: true)\n# free_response_channels: \"\" # Channel IDs where no mention is needed\n# reactions: true # Show processing reactions (default: true)\n# history_backfill: true # Recover missed channel messages on mention (default: true)\n# history_backfill_limit: 50 # Max messages to scan backwards (default: 50)\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Available toolsets (use these names in platform_toolsets or the toolsets list)\n#\n# Run `hermes chat --list-toolsets` to see all toolsets and their tools.\n# Run `hermes chat --list-tools` to see every individual tool with descriptions.\n# ─────────────────────────────────────────────────────────────────────────────\n#\n# INDIVIDUAL TOOLSETS (compose your own):\n# web - web_search, web_extract\n# search - web_search only (no scraping)\n# terminal - terminal, process\n# file - read_file, write_file, patch, search\n# browser - browser_navigate, browser_snapshot, browser_click, browser_type,\n# browser_scroll, browser_back, browser_press,\n# browser_get_images, browser_vision (requires BROWSERBASE_API_KEY)\n# vision - vision_analyze (requires OPENROUTER_API_KEY)\n# image_gen - image_generate (requires FAL_KEY)\n# skills - skills_list, skill_view\n# skills_hub - skill_hub (search/install/manage from online registries — user-driven only)\n# todo - todo (in-memory task planning, no deps)\n# tts - text_to_speech (Edge TTS free, or ELEVENLABS/OPENAI/MINIMAX/MISTRAL key)\n# cronjob - cronjob (create/list/update/pause/resume/run/remove scheduled tasks)\n#\n# PRESETS (curated bundles):\n# hermes-cli - All of the above except send_message\n# hermes-telegram - terminal, file, web, vision, image_gen, tts, browser,\n# skills, todo, cronjob, send_message\n# hermes-discord - Same as hermes-telegram\n# hermes-whatsapp - Same as hermes-telegram\n# hermes-slack - Same as hermes-telegram\n#\n# COMPOSITE:\n# debugging - terminal + web + file\n# safe - web + vision (no terminal access)\n# all - Everything available\n#\n# web - Web search and content extraction (web_search, web_extract)\n# search - Web search only, no scraping (web_search)\n# terminal - Command execution and process management (terminal, process)\n# file - File operations: read, write, patch, search\n# browser - Full browser automation (navigate, click, type, screenshot, etc.)\n# vision - Image analysis (vision_analyze)\n# image_gen - Image generation with FLUX (image_generate)\n# skills - Load skill documents (skills_list, skill_view)\n# todo - Task planning and tracking for multi-step work\n# memory - Persistent memory across sessions (personal notes + user profile)\n# session_search - Search and recall past conversations (FTS5 + Gemini Flash summarization)\n# tts - Text-to-speech (Edge TTS free, ElevenLabs, OpenAI, MiniMax, Mistral)\n# cronjob - Schedule and manage automated tasks (CLI-only)\n#\n# Composite toolsets:\n# debugging - terminal + web + file (for troubleshooting)\n# safe - web + vision (no terminal access)\n\n# NOTE: The top-level \"toolsets\" key is deprecated and ignored.\n# Tool configuration is managed per-platform via platform_toolsets above.\n# Use `hermes tools` to configure interactively, or edit platform_toolsets directly.\n#\n# CLI override: hermes chat --toolsets terminal,web,file\n\n# =============================================================================\n# MCP (Model Context Protocol) Servers\n# =============================================================================\n# Connect to external MCP servers to add tools from the MCP ecosystem.\n# Each server's tools are automatically discovered and registered.\n# See website/docs/user-guide/features/mcp.md for full documentation.\n#\n# Stdio servers (spawn a subprocess):\n# command: the executable to run\n# args: command-line arguments\n# env: environment variables (only these + safe defaults passed to subprocess)\n#\n# HTTP servers (connect to a URL):\n# url: the MCP server endpoint\n# headers: HTTP headers (e.g., for authentication)\n#\n# Optional per-server settings:\n# timeout: tool call timeout in seconds (default: 120)\n# connect_timeout: initial connection timeout (default: 60)\n# keepalive_interval: liveness ping cadence in seconds (default: 180).\n# Lower it below the server's session TTL for servers that expire idle\n# sessions quickly (e.g. Unreal Engine editor MCP, ~15s), otherwise idle\n# tool calls hit an expired session and pay a slow reconnect. Floored at 5s.\n#\n# mcp_servers:\n# time:\n# command: uvx\n# args: [\"mcp-server-time\"]\n# filesystem:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-filesystem\", \"/home/user\"]\n# notion:\n# url: https://mcp.notion.com/mcp\n# github:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-github\"]\n# env:\n# GITHUB_PERSONAL_ACCESS_TOKEN: \"ghp_...\"\n#\n# Sampling (server-initiated LLM requests) — enabled by default.\n# Per-server config under the 'sampling' key:\n# analysis:\n# command: npx\n# args: [\"-y\", \"analysis-server\"]\n# sampling:\n# enabled: true # default: true\n# model: \"gemini-3-flash\" # override model (optional)\n# max_tokens_cap: 4096 # max tokens per request\n# timeout: 30 # LLM call timeout (seconds)\n# max_rpm: 10 # max requests per minute\n# allowed_models: [] # model whitelist (empty = all)\n# max_tool_rounds: 5 # tool loop limit (0 = disable)\n# log_level: \"info\" # audit verbosity\n\n# =============================================================================\n# Text-to-Speech\n# =============================================================================\n# TTS defaults to Edge TTS unless changed in ~/.hermes/config.yaml.\n# Gemini TTS supports persona/director prompt files, and Gemini 3.1 Flash TTS\n# can use a hidden auxiliary rewrite pass to insert expressive square-bracket\n# audio tags into the TTS script without showing tags in chat.\n#\n# tts:\n# provider: \"gemini\"\n# gemini:\n# model: \"gemini-3.1-flash-tts-preview\"\n# voice: \"Kore\"\n# audio_tags: false\n# persona_prompt_file: \"\" # e.g. ~/.hermes/tts/radio-host.md\n\n# =============================================================================\n# Voice Transcription (Speech-to-Text)\n# =============================================================================\n# Automatically transcribe voice messages on messaging platforms.\n# Providers: local (free, faster-whisper) | groq (free tier) | openai (Whisper API) | mistral (Voxtral Transcribe)\n# Set the corresponding API key in .env: GROQ_API_KEY, OPENAI_API_KEY, or MISTRAL_API_KEY.\nstt:\n enabled: true\n # provider: \"local\" # auto-detected if omitted\n local:\n model: \"base\" # tiny | base | small | medium | large-v3 | turbo\n # language: \"\" # auto-detect; set to \"en\", \"es\", \"fr\", etc. to force\n openai:\n model: \"whisper-1\" # whisper-1 | gpt-4o-mini-transcribe | gpt-4o-transcribe\n # mistral:\n # model: \"voxtral-mini-latest\" # voxtral-mini-latest | voxtral-mini-2602\n\n# =============================================================================\n# Response Pacing (Messaging Platforms)\n# =============================================================================\n# Add human-like delays between message chunks.\n# human_delay:\n# mode: \"off\" # \"off\" | \"natural\" | \"custom\"\n# min_ms: 800 # Min delay (custom mode only)\n# max_ms: 2500 # Max delay (custom mode only)\n\n# =============================================================================\n# Session Logging\n# =============================================================================\n# Session trajectories are automatically saved to logs/ directory.\n# Each session creates: logs/session_YYYYMMDD_HHMMSS_UUID.json\n#\n# The session ID is displayed in the welcome banner for easy reference.\n# Logs contain full conversation history in trajectory format:\n# - System prompt, user messages, assistant responses\n# - Tool calls with inputs/outputs\n# - Timestamps for debugging\n#\n# No configuration needed - logging is always enabled.\n# To disable, you would need to modify the source code.\n\n# =============================================================================\n# Code Execution Sandbox (Programmatic Tool Calling)\n# =============================================================================\n# The execute_code tool runs Python scripts that call Hermes tools via RPC.\n# Intermediate tool results stay out of the LLM's context window.\ncode_execution:\n timeout: 300 # Max seconds per script before kill (default: 300 = 5 min)\n max_tool_calls: 50 # Max RPC tool calls per execution (default: 50)\n\n# =============================================================================\n# Subagent Delegation\n# =============================================================================\n# The delegate_task tool spawns child agents with isolated context.\n# Supports single tasks and batch mode (default 3 parallel, configurable).\ndelegation:\n max_iterations: 50 # Max tool-calling turns per child (default: 50)\n # max_concurrent_children: 3 # Max parallel child agents per batch (default: 3, floor: 1, no ceiling).\n # WARNING: values above 10 multiply API cost linearly.\n # max_spawn_depth: 1 # Delegation tree depth cap (range: 1-3, default: 1 = flat).\n # Raise to 2 to allow workers to spawn their own subagents.\n # Requires role=\"orchestrator\" on intermediate agents.\n # orchestrator_enabled: true # Kill switch for role=\"orchestrator\" children (default: true).\n # subagent_auto_approve: false # When a subagent hits a dangerous-command approval prompt, auto-deny (default: false)\n # or auto-approve \"once\" (true) instead of blocking on stdin.\n # The parent TUI owns stdin, so blocking would deadlock; non-interactive resolution is required.\n # Both choices emit a logger.warning audit line. Flip to true only for cron/batch pipelines.\n # inherit_mcp_toolsets: true # When explicit child toolsets are narrowed, also keep the parent's MCP toolsets (default: true). Set false for strict intersection.\n # model: \"google/gemini-3-flash-preview\" # Override model for subagents (empty = inherit parent)\n # provider: \"openrouter\" # Override provider for subagents (empty = inherit parent)\n # # Resolves full credentials (base_url, api_key) automatically.\n # # Supported: openrouter, nous, zai, kimi-coding, minimax\n\n# =============================================================================\n# Honcho Integration (Cross-Session User Modeling)\n# =============================================================================\n# AI-native persistent memory via Honcho (https://honcho.dev/).\n# Builds a deeper understanding of the user across sessions and tools.\n# Runs alongside USER.md — additive, not a replacement.\n#\n# Requires: pip install honcho-ai\n# Config: ~/.honcho/config.json (shared with Claude Code, Cursor, etc.)\n# API key: HONCHO_API_KEY in ~/.hermes/.env or ~/.honcho/config.json\n#\n# Hermes-specific overrides (optional — most config comes from ~/.honcho/config.json):\n# honcho: {}\n\n# =============================================================================\n# Display\n# =============================================================================\ndisplay:\n # Use compact banner mode (hides the ASCII-art banner, shows a single line).\n # true: Compact single-line banner\n # false: Full ASCII banner with tool/skill summary (default)\n compact: false\n\n # Tool progress display level (CLI and gateway)\n # off: Silent — no tool activity shown, just the final response\n # new: Show a tool indicator only when the tool changes (skip repeats)\n # all: Show every tool call with a short preview (default)\n # verbose: Full args, results, and debug logs (same as /verbose)\n # Toggle at runtime with /verbose in the CLI\n tool_progress: all\n\n # Per-platform defaults can be quieter than the global setting. Telegram\n # tunes for mobile: tool_progress and busy_ack_detail default off (no\n # per-tool breadcrumb stream, no \"iteration 21/60\" debug detail in busy\n # acks or heartbeats), but interim_assistant_messages and\n # long_running_notifications STAY ON so the user has real signal between\n # turn start and final answer (mid-turn assistant commentary + a single\n # edit-in-place \"⏳ Working — N min\" heartbeat). Override under\n # display.platforms.telegram.\n\n # Auto-cleanup of temporary progress bubbles after the final response lands.\n # On platforms that support message deletion (currently Telegram), this\n # removes the tool-progress bubble, \"⏳ Still working...\" notices, and\n # context-pressure status messages once the final reply has been delivered —\n # keeping long-running turns visible live, then tidy afterward. Failed runs\n # leave the bubbles in place as breadcrumbs. Off by default.\n # Per-platform override: display.platforms.telegram.cleanup_progress\n # true: Delete tracked progress/status bubbles on successful turn\n # false: Leave everything in place (default)\n # Example:\n # display:\n # platforms:\n # telegram:\n # cleanup_progress: true\n cleanup_progress: false\n\n # Gateway-only natural mid-turn assistant updates.\n # When true, completed assistant status messages are sent as separate chat\n # messages. This is independent of tool_progress and gateway streaming.\n # true: Send mid-turn assistant updates as separate messages (default)\n # false: Only send the final response\n interim_assistant_messages: true\n\n # Gateway-only long-running status heartbeats.\n # When false, the platform does not receive periodic \"⏳ Working — N min\"\n # notifications even if agent.gateway_notify_interval is non-zero. The\n # heartbeat edits a single message in place (where the adapter supports\n # editing) instead of posting a new bubble each interval.\n # Default: true everywhere, including Telegram (silent agents are worse\n # than a single edit-in-place heartbeat).\n long_running_notifications: true\n\n # Include detailed iteration/tool/status context in busy acknowledgments\n # and long-running heartbeats. When true, busy acks show \"iteration 21/60,\n # terminal, 10 min\" and the heartbeat shows \"⏳ Working — 12 min,\n # iteration 21/60, terminal\". When false (Telegram default), both stay\n # terse: \"Interrupting current task\" and \"⏳ Working — 12 min, terminal\".\n busy_ack_detail: true\n\n # What Enter does when Hermes is already busy (CLI and gateway platforms).\n # interrupt: Interrupt the current run and redirect Hermes (default)\n # queue: Queue your message for the next turn\n # steer: Inject your message mid-run via /steer, arriving at the agent\n # after the next tool call — no interrupt, no role violation.\n # Falls back to 'queue' if the agent isn't running yet or if\n # images are attached (steer only carries text).\n # Ctrl+C (or /stop in gateway) always interrupts regardless of this setting.\n # Toggle at runtime with /busy .\n busy_input_mode: interrupt\n\n # Background process notifications (gateway/messaging only).\n # Controls how chatty the process watcher is when you use\n # terminal(background=true, notify_on_complete=true) from Telegram/Discord/etc.\n # off: No watcher messages at all\n # result: Only the final completion message\n # error: Only the final message when exit code != 0\n # all: Running output updates + final message (default)\n background_process_notifications: all\n\n\n # Play terminal bell when agent finishes a response.\n # Useful for long-running tasks — your terminal will ding when the agent is done.\n # Works over SSH. Most terminals can be configured to flash the taskbar or play a sound.\n # true: Ring the terminal bell on each response\n # false: Silent (default)\n bell_on_complete: false\n\n # Show model reasoning/thinking before each response.\n # When enabled, a dim box shows the model's thought process above the response.\n # Toggle at runtime with /reasoning show or /reasoning hide.\n # true: Show the reasoning box\n # false: Hide reasoning (default)\n show_reasoning: false\n\n # Stream tokens to the terminal as they arrive instead of waiting for the\n # full response. The response box opens on first token and text appears\n # line-by-line. Tool calls are still captured silently.\n # true: Stream tokens as they arrive (default)\n # false: Wait for the full response before rendering\n streaming: true\n\n # Show [HH:MM] timestamps on user input and assistant response labels.\n # timestamps: false\n\n # ───────────────────────────────────────────────────────────────────────────\n # Skin / Theme\n # ───────────────────────────────────────────────────────────────────────────\n # Customize CLI visual appearance — banner colors, spinner faces, tool prefix,\n # response box label, and branding text. Change at runtime with /skin .\n #\n # Built-in skins:\n # default — Classic Hermes gold/kawaii\n # ares — Crimson/bronze war-god theme with spinner wings\n # mono — Clean grayscale monochrome\n # slate — Cool blue developer-focused\n # daylight — Bright light-mode theme\n # warm-lightmode — Warm paper-tone light-mode theme\n # poseidon — Sea-green/teal Olympian theme\n # sisyphus — Earthy stone-and-moss theme\n # charizard — Fiery orange dragon theme\n #\n # Custom skins: drop a YAML file in ~/.hermes/skins/.yaml\n # Schema (all fields optional, missing values inherit from default):\n #\n # name: my-theme\n # description: Short description\n # colors:\n # banner_border: \"#HEX\" # Panel border\n # banner_title: \"#HEX\" # Panel title\n # banner_accent: \"#HEX\" # Section headers (Available Tools, etc.)\n # banner_dim: \"#HEX\" # Dim/muted text\n # banner_text: \"#HEX\" # Body text (tool names, skill names)\n # ui_accent: \"#HEX\" # UI accent color\n # response_border: \"#HEX\" # Response box border color\n # spinner:\n # waiting_faces: [\"(⚔)\", \"(⛨)\"] # Faces shown while waiting\n # thinking_faces: [\"(⚔)\", \"(⌁)\"] # Faces shown while thinking\n # thinking_verbs: [\"forging\", \"plotting\"] # Verbs for spinner messages\n # wings: # Optional left/right spinner decorations\n # - [\"⟪⚔\", \"⚔⟫\"]\n # - [\"⟪▲\", \"▲⟫\"]\n # branding:\n # agent_name: \"My Agent\" # Banner title and branding\n # welcome: \"Welcome message\" # Shown at CLI startup\n # response_label: \" ⚔ Agent \" # Response box header label\n # prompt_symbol: \"⚔\" # Prompt symbol (bare token; renderers add trailing space)\n # tool_prefix: \"╎\" # Tool output line prefix (default: ┊)\n #\n skin: default\n\n# =============================================================================\n# Model Aliases — short names for /model command\n# =============================================================================\n# Map short aliases to exact (model, provider, base_url) tuples.\n# Used by /model tab completion and resolve_alias().\n# Aliases are checked BEFORE the models.dev catalog, so they can route\n# to endpoints not in the catalog (e.g. Ollama Cloud, local servers).\n#\n# model_aliases:\n# opus:\n# model: claude-opus-4-6\n# provider: anthropic\n# qwen:\n# model: \"qwen3.5:397b\"\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n# glm:\n# model: glm-4.7\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n\n# =============================================================================\n# Privacy\n# =============================================================================\n# privacy:\n# # Redact PII from the LLM context prompt.\n# # When true, phone numbers are stripped and user/chat IDs are replaced\n# # with deterministic hashes before being sent to the model.\n# # Names and usernames are NOT affected (user-chosen, publicly visible).\n# # Routing/delivery still uses the original values internally.\n# redact_pii: false\n\n# =============================================================================\n# Shell-script hooks\n# =============================================================================\n# Register shell scripts as plugin-hook callbacks. Each entry is executed as\n# a subprocess (shell=False, shlex.split) with a JSON payload on stdin. On\n# stdout the script may return JSON that either blocks the tool call or\n# injects context into the next LLM call.\n#\n# Valid events (mirror hermes_cli.plugins.VALID_HOOKS):\n# pre_tool_call, post_tool_call, pre_llm_call, post_llm_call,\n# pre_api_request, post_api_request, on_session_start, on_session_end,\n# on_session_finalize, on_session_reset, subagent_stop\n#\n# First-use consent: each (event, command) pair prompts once on a TTY, then\n# is persisted to ~/.hermes/shell-hooks-allowlist.json. Non-interactive\n# runs (gateway, cron) need --accept-hooks, HERMES_ACCEPT_HOOKS=1, or the\n# hooks_auto_accept key below.\n#\n# See website/docs/user-guide/features/hooks.md for the full JSON wire\n# protocol and worked examples.\n#\n# hooks:\n# pre_tool_call:\n# - matcher: \"terminal\"\n# command: \"~/.hermes/agent-hooks/block-rm-rf.sh\"\n# timeout: 10\n# post_tool_call:\n# - matcher: \"write_file|patch\"\n# command: \"~/.hermes/agent-hooks/auto-format.sh\"\n# pre_llm_call:\n# - command: \"~/.hermes/agent-hooks/inject-cwd-context.sh\"\n# subagent_stop:\n# - command: \"~/.hermes/agent-hooks/log-orchestration.sh\"\n#\n# hooks_auto_accept: false\n\n\n# =============================================================================\n# Update Behavior\n# =============================================================================\nupdates:\n # Create a full HERMES_HOME zip before every `hermes update`.\n # Backups land in ~/.hermes/backups/ and can be restored with `hermes import`.\n # Off by default because large homes can add minutes to every update.\n pre_update_backup: false\n\n # Number of pre-update backup zips to retain.\n backup_keep: 5\n\n # What non-interactive updates do with local source edits in the Hermes repo.\n # Interactive terminal updates always prompt before restoring the autostash.\n #\n # stash - auto-stash before pull, then auto-restore after success (default)\n # discard - drop the update-created stash after success; use only on managed\n # installs where local source edits should not persist\n non_interactive_local_changes: \"stash\"\n\n\n# =============================================================================\n# Web Dashboard\n# =============================================================================\n# OAuth gate configuration for `hermes dashboard --host `.\n# The bundled Nous Portal plugin reads these on startup; settings here are\n# the canonical surface. Each can be overridden by an environment variable:\n#\n# dashboard.oauth.client_id <- HERMES_DASHBOARD_OAUTH_CLIENT_ID\n# dashboard.oauth.portal_url <- HERMES_DASHBOARD_PORTAL_URL\n# dashboard.public_url <- HERMES_DASHBOARD_PUBLIC_URL\n#\n# Env wins when set to a non-empty value. This is what Fly.io's platform-\n# secret injection uses to push per-deploy client_ids without needing to\n# bake a config.yaml into the image. Empty env values are treated as unset\n# so a provisioned-but-not-populated secret can't shadow a valid entry here.\n#\n# Local dev / on-prem deploys should typically set these via config.yaml\n# (the ~/.hermes/.env file is reserved for API keys and secrets).\n#\n# dashboard:\n# oauth:\n# client_id: \"\" # agent:{instance_id}; Portal provisions this at deploy\n# portal_url: \"\" # blank → default https://portal.nousresearch.com\n#\n# # Force the absolute base URL the OAuth callback (and any other public\n# # URL the dashboard hands to external systems) is built from. Set this\n# # for deploys behind reverse proxies that don't reliably forward\n# # X-Forwarded-Host / X-Forwarded-Proto / X-Forwarded-Prefix (manual\n# # nginx setups, on-prem ingresses, custom-domain Fly deploys without\n# # full proxy header chains).\n# #\n# # When set, the value is the complete authority: scheme + host +\n# # optional path prefix (e.g. \"https://example.com/hermes\"). The OAuth\n# # callback URL becomes \"/auth/callback\" — X-Forwarded-Prefix\n# # is IGNORED on this code path because the operator has explicitly\n# # declared the public URL and we no longer need to guess.\n# #\n# # Leave empty to use the existing proxy-header reconstruction (the\n# # default — works on Fly.io out of the box).\n# #\n# # public_url: \"https://example.com/hermes\"\n"}, "0178b7b887a13cdef6f34abed2c8c3b305e8a0dd": {"t": 1785767896.4521112, "n": 60209, "h": "5d72766635189a33", "c": "# Hermes Agent CLI Configuration\n# Copy this file to cli-config.yaml and customize as needed.\n# This file configures the CLI behavior. Environment variables in .env take precedence.\n\n# =============================================================================\n# Model Configuration\n# =============================================================================\nmodel:\n # Default model to use (can be overridden with --model flag)\n # Both \"default\" and \"model\" work as the key name here.\n default: \"anthropic/claude-opus-4.6\"\n \n # Inference provider selection:\n # \"auto\" - Auto-detect from credentials (default)\n # \"openrouter\" - OpenRouter (requires: OPENROUTER_API_KEY or OPENAI_API_KEY)\n # \"nous\" - Nous Portal OAuth (requires: hermes login)\n # \"nous-api\" - Nous Portal API key (requires: NOUS_API_KEY)\n # \"anthropic\" - Direct Anthropic API (requires: ANTHROPIC_API_KEY)\n # \"openai-codex\" - OpenAI Codex (requires: hermes auth)\n # \"copilot\" - GitHub Copilot / GitHub Models (requires: GITHUB_TOKEN)\n # \"gemini\" - Use Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n # \"zai\" - Use z.ai / ZhipuAI GLM models (requires: GLM_API_KEY)\n # \"kimi-coding\" - Kimi / Moonshot AI (requires: KIMI_API_KEY)\n # \"minimax\" - MiniMax global (requires: MINIMAX_API_KEY)\n # \"minimax-cn\" - MiniMax China (requires: MINIMAX_CN_API_KEY)\n # \"huggingface\" - Hugging Face Inference (requires: HF_TOKEN)\n # \"nvidia\" - NVIDIA NIM / build.nvidia.com (requires: NVIDIA_API_KEY)\n # \"xiaomi\" - Xiaomi MiMo (requires: XIAOMI_API_KEY)\n # \"arcee\" - Arcee AI Trinity models (requires: ARCEEAI_API_KEY)\n # \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY — https://ollama.com/settings)\n # \"kilocode\" - KiloCode gateway (requires: KILOCODE_API_KEY)\n # \"azure-foundry\" - Microsoft Foundry / Azure OpenAI (API key or Entra ID)\n # \"lmstudio\" - LM Studio local server (optional: LM_API_KEY, defaults to http://127.0.0.1:1234/v1)\n #\n # Local servers (LM Studio, Ollama, vLLM, llama.cpp):\n # \"custom\" - Any other OpenAI-compatible endpoint. Set base_url below.\n # Aliases: \"ollama\", \"vllm\", \"llamacpp\" all map to \"custom\".\n # LM Studio is first-class and uses provider: \"lmstudio\".\n # It works with both no-auth and auth-enabled server modes.\n #\n # Can also be overridden for a single invocation with the --provider flag.\n provider: \"auto\"\n \n # API configuration (falls back to OPENROUTER_API_KEY env var)\n # api_key: \"your-key-here\" # Uncomment to set here instead of .env\n base_url: \"https://openrouter.ai/api/v1\"\n\n # Azure Foundry keyless auth example:\n # provider: \"azure-foundry\"\n # base_url: \"https://.openai.azure.com/openai/v1\"\n # auth_mode: \"entra_id\" # DefaultAzureCredential: az login, managed identity, workload identity, etc.\n # default: \"gpt-4o\" # Deployment/model name\n # entra:\n # scope: \"https://ai.azure.com/.default\" # Optional; this is the default.\n\n # ── Token limits — two settings, easy to confuse ──────────────────────────\n #\n # context_length: TOTAL context window (input + output tokens combined).\n # Controls when Hermes compresses history and validates requests.\n # Leave unset — Hermes auto-detects the correct value from the provider.\n # Set manually only when auto-detection is wrong (e.g. a local server with\n # a custom num_ctx, or a proxy that doesn't expose /v1/models).\n #\n # context_length: 131072\n #\n # max_tokens: OUTPUT cap — maximum tokens the model may generate per response.\n # Unrelated to how long your conversation history can be.\n # The OpenAI-standard name \"max_tokens\" is a misnomer; Anthropic's native\n # API has since renamed it \"max_output_tokens\" for clarity.\n # Leave unset to use the model's native output ceiling (recommended).\n # Set only if you want to deliberately limit individual response length.\n #\n# max_tokens: 8192\n\n# Named provider overrides (optional)\n# Use this for per-provider request timeouts, non-stream stale timeouts,\n# and per-model exceptions.\n# Applies to the primary turn client on every api_mode (OpenAI-wire, native\n# Anthropic, and Anthropic-compatible providers), the fallback chain, and\n# client rebuilds during credential rotation. For OpenAI-wire chat\n# completions (streaming and non-streaming) the configured value is also\n# used as the per-request ``timeout=`` kwarg so it wins over the legacy\n# HERMES_API_TIMEOUT env var (which still applies when no config is set).\n# ``stale_timeout_seconds`` controls the non-streaming stale-call detector and\n# wins over the legacy HERMES_API_CALL_STALE_TIMEOUT env var. Leaving these\n# unset keeps the legacy defaults (HERMES_API_TIMEOUT=1800s,\n# HERMES_API_CALL_STALE_TIMEOUT=300s, native Anthropic 900s).\n#\n# Not currently wired for AWS Bedrock (bedrock_converse + AnthropicBedrock\n# SDK paths) — those use boto3 with its own timeout configuration.\n#\n# providers:\n# ollama-local:\n# request_timeout_seconds: 300 # Longer timeout for local cold-starts\n# stale_timeout_seconds: 900 # Explicitly re-enable stale detection on local endpoints\n# anthropic:\n# request_timeout_seconds: 30 # Fast-fail cloud requests\n# models:\n# claude-opus-4.6:\n# timeout_seconds: 600 # Longer timeout for extended-thinking Opus calls\n# openai-codex:\n# models:\n# gpt-5.4:\n# stale_timeout_seconds: 1800 # Longer non-stream stale timeout for slow large-context turns\n\n# =============================================================================\n# OpenRouter Provider Routing (only applies when using OpenRouter)\n# =============================================================================\n# Control how requests are routed across providers on OpenRouter.\n# See: https://openrouter.ai/docs/guides/routing/provider-selection\n#\n# provider_routing:\n# # Sort strategy: \"price\" (default), \"throughput\", or \"latency\"\n# # Append :nitro to model name for a shortcut to throughput sorting.\n# sort: \"throughput\"\n#\n# # Only allow these providers (provider slugs from OpenRouter)\n# # only: [\"anthropic\", \"google\"]\n#\n# # Skip these providers entirely\n# # ignore: [\"deepinfra\", \"fireworks\"]\n#\n# # Try providers in this order (overrides default load balancing)\n# # order: [\"anthropic\", \"google\", \"together\"]\n#\n# # Require providers to support all parameters in your request\n# # require_parameters: true\n#\n# # Data policy: \"allow\" (default) or \"deny\" to exclude providers that may store data\n# # data_collection: \"deny\"\n#\n# # ── DeepSeek prompt-cache pinning (RECOMMENDED for deepseek/* as main model) ──\n# # DeepSeek's context cache is automatic but backend-local: OpenRouter\n# # load-balances deepseek/* across upstream backends with separate caches,\n# # so consecutive turns can land on a cold backend and re-bill the FULL\n# # prefix. Pinning the order to \"deepseek\" keeps every turn on the same\n# # cache-warm backend. Measured impact: ~2x cost difference at scale.\n# # Fallbacks stay ON by default, so a DeepSeek outage degrades to another\n# # provider rather than failing. \"deepseek\" is a no-op for non-deepseek\n# # models (that backend doesn't serve them), so this is safe to leave set.\n# # order: [\"deepseek\"]\n\n# =============================================================================\n# OpenRouter Response Caching (only applies when using OpenRouter)\n# =============================================================================\n# Cache identical API responses at the OpenRouter edge for free instant replays.\n# When enabled, identical requests (same model, messages, parameters) return\n# cached responses with zero billing. Separate from Anthropic prompt caching.\n# See: https://openrouter.ai/docs/guides/features/response-caching\n#\n# openrouter:\n# response_cache: true # Enable response caching (default: true)\n# response_cache_ttl: 300 # Cache TTL in seconds, 1-86400 (default: 300)\n\n# =============================================================================\n# Git Worktree Isolation\n# =============================================================================\n# When enabled, each CLI session creates an isolated git worktree so multiple\n# agents can work on the same repo concurrently without file collisions.\n# Equivalent to always passing --worktree / -w on the command line.\n#\n# worktree: true # Always create a worktree when in a git repo\n# worktree: false # Default — only create when -w flag is passed\n\n# =============================================================================\n# Terminal Tool Configuration\n# =============================================================================\n# Choose ONE of the following terminal configurations by uncommenting it.\n# The terminal tool executes commands in the specified environment.\n\n# -----------------------------------------------------------------------------\n# OPTION 1: Local execution (default)\n# Commands run directly on your machine in the current directory\n# -----------------------------------------------------------------------------\n# Working directory behavior:\n# - CLI (`hermes` command): Uses \".\" (current directory where you run hermes)\n# - Gateway/messaging/cron: Uses terminal.cwd here; legacy .env cwd values are deprecated\nterminal:\n backend: \"local\"\n cwd: \".\" # For local backend: \".\" = current directory. Ignored for remote backends unless a backend documents otherwise.\n timeout: 180\n docker_mount_cwd_to_workspace: false # SECURITY: off by default. Opt in to mount the launch cwd into Docker /workspace.\n lifetime_seconds: 300\n # sudo_password: \"hunter2\" # Optional: pipe a sudo password via sudo -S. SECURITY WARNING: plaintext.\n # sudo_password: \"\" # Explicit empty password: try empty and never open the interactive sudo prompt.\n\n# -----------------------------------------------------------------------------\n# OPTION 2: SSH remote execution\n# Commands run on a remote server - agent code stays local (sandboxed)\n# Great for: keeping agent isolated from its own code, using powerful remote hardware\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"ssh\"\n# cwd: \"/home/myuser/project\" # Path on the REMOTE server\n# timeout: 180\n# lifetime_seconds: 300\n# ssh_host: \"my-server.example.com\"\n# ssh_user: \"myuser\"\n# ssh_port: 22\n# ssh_key: \"~/.ssh/id_rsa\" # Optional - uses ssh-agent if not specified\n\n# -----------------------------------------------------------------------------\n# OPTION 3: Docker container\n# Commands run in an isolated Docker container\n# Great for: reproducible environments, testing, isolation\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"docker\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /)\n# timeout: 180\n# lifetime_seconds: 300\n# docker_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# docker_mount_cwd_to_workspace: true # Explicit opt-in: mount your launch cwd into /workspace\n# # Optional: run the container as your host user's uid:gid so files written\n# # into bind-mounted dirs are owned by you, not root. Drops SETUID/SETGID\n# # caps too since no gosu privilege drop is needed. Leave off if your\n# # chosen docker_image expects to start as root.\n# docker_run_as_host_user: true\n# # Optional: explicitly forward selected env vars into Docker.\n# # These values come from your current shell first, then ~/.hermes/.env.\n# # Warning: anything forwarded here is visible to commands run in the container.\n# docker_forward_env:\n# - \"GITHUB_TOKEN\"\n# - \"NPM_TOKEN\"\n# # Optional: extra flags passed verbatim to docker run (appended after security defaults).\n# # Useful for adding capabilities (e.g. apt installs needing SETUID) or custom options.\n# # Example: add a Linux capability not included by default\n# # docker_extra_args:\n# # - \"--cap-add\"\n# # - \"SETUID\"\n\n# -----------------------------------------------------------------------------\n# OPTION 4: Singularity/Apptainer container\n# Commands run in a Singularity container (common in HPC environments)\n# Great for: HPC clusters, shared compute environments\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"singularity\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# singularity_image: \"docker://nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 5: Modal cloud execution\n# Commands run on Modal's cloud infrastructure\n# Great for: GPU access, scalable compute, serverless execution\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"modal\"\n# cwd: \"/workspace\" # Path INSIDE the sandbox (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# modal_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 6: Daytona cloud execution\n# Commands run in Daytona cloud sandboxes\n# Great for: Cloud dev environments, persistent workspaces, team collaboration\n# Requires: pip install daytona, DAYTONA_API_KEY env var\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"daytona\"\n# cwd: \"~\"\n# timeout: 180\n# lifetime_seconds: 300\n# daytona_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# container_disk: 10240 # Daytona max is 10GB per sandbox\n\n#\n# --- Container resource limits (docker, singularity, modal, daytona -- ignored for local/ssh) ---\n# These settings apply to all container backends. They control the resources\n# allocated to the sandbox and whether its filesystem persists across sessions.\n container_cpu: 1 # CPU cores\n container_memory: 5120 # Memory in MB (5120 = 5GB)\n container_disk: 51200 # Disk in MB (51200 = 50GB)\n container_persistent: true # Persist filesystem across sessions (false = ephemeral)\n\n# -----------------------------------------------------------------------------\n# SUDO SUPPORT (works with ALL backends above)\n# -----------------------------------------------------------------------------\n# Add sudo_password to any terminal config above to enable sudo commands.\n# The password is piped via `sudo -S`. Works with local, ssh, docker, etc.\n#\n# SECURITY WARNING: Password stored in plaintext!\n#\n# INTERACTIVE PROMPT: If sudo_password is unset and the CLI is running,\n# you'll be prompted to enter your password when sudo is needed:\n# - 45-second timeout (auto-skips if no input)\n# - Press Enter to skip (command fails gracefully)\n# - Password is hidden while typing\n# - Password is cached for the session\n#\n# EMPTY PASSWORDS: Setting sudo_password to an explicit empty string is different\n# from leaving it unset. Hermes will try an empty password via `sudo -S` and\n# will not open the interactive prompt. This is useful for passwordless sudo,\n# Touch ID sudo setups, and environments where prompting is just noise.\n#\n# ALTERNATIVES:\n# - SSH backend: Configure passwordless sudo on the remote server\n# - Containers: Run as root inside the container (no sudo needed)\n# - Local: Configure /etc/sudoers for specific commands\n#\n# Example (add to your terminal section):\n# sudo_password: \"your-password-here\"\n\n# =============================================================================\n# Security Scanning (tirith)\n# =============================================================================\n# Optional pre-exec command security scanning via tirith.\n# Detects homograph URLs, pipe-to-shell, terminal injection, env manipulation.\n# Install: brew install sheeki03/tap/tirith\n# Docs: https://github.com/sheeki03/tirith\n#\n# security:\n# tirith_enabled: true # Enable/disable tirith scanning\n# tirith_path: \"tirith\" # Path to tirith binary (supports ~ expansion)\n# tirith_timeout: 5 # Scan timeout in seconds\n# tirith_fail_open: true # Allow commands if tirith unavailable\n\n# =============================================================================\n# Browser Tool Configuration\n# =============================================================================\nbrowser:\n # Inactivity timeout in seconds - browser sessions are automatically closed\n # after this period of no activity between agent loops (default: 120 = 2 minutes)\n inactivity_timeout: 120\n\n# =============================================================================\n# Tool Loop Guardrails\n# =============================================================================\n# Soft warnings are enabled by default. They append guidance to repeated failed\n# or non-progressing tool results but still let the tool execute. Hard stops are\n# opt-in circuit breakers for autonomous/cron sessions where stopping a loop is\n# preferable to spending the full iteration budget.\ntool_loop_guardrails:\n warnings_enabled: true\n hard_stop_enabled: false\n warn_after:\n exact_failure: 2\n same_tool_failure: 3\n idempotent_no_progress: 2\n hard_stop_after:\n exact_failure: 5\n same_tool_failure: 8\n idempotent_no_progress: 5\n\n# =============================================================================\n# Context Compression (Auto-shrinks long conversations)\n# =============================================================================\n# When conversation approaches model's context limit, middle turns are\n# automatically summarized to free up space while preserving important context.\n#\n# HOW IT WORKS:\n# 1. Tracks actual token usage from API responses (not estimates)\n# 2. When prompt_tokens >= threshold% of model's context_length, triggers compression\n# 3. Protects first 3 turns (system prompt, initial request, first response)\n# 4. Protects last N turns (default 20 messages = ~10 full turns of recent context)\n# 5. Summarizes middle turns using a fast/cheap model\n# 6. Inserts summary as a user message, continues conversation seamlessly\n#\n# Post-compression tail budget is target_ratio × threshold × context_length:\n# 200K context, threshold 0.50, ratio 0.20 → 20K tokens of recent tail preserved\n# 1M context, threshold 0.50, ratio 0.20 → 100K tokens of recent tail preserved\n#\ncompression:\n # Enable automatic context compression (default: true)\n # Set to false if you prefer to manage context manually or want errors on overflow\n enabled: true\n \n # Trigger compression at this % of model's context limit (default: 0.50 = 50%)\n # Lower values = more aggressive compression, higher values = compress later\n threshold: 0.50\n \n # Fraction of the threshold to preserve as recent tail (default: 0.20 = 20%)\n # e.g. 20% of 50% threshold = 10% of total context kept as recent messages.\n # Summary output is separately capped at 12K tokens (Gemini output limit).\n # Range: 0.10 - 0.80\n target_ratio: 0.20\n\n # Number of most-recent messages to always preserve (default: 20 ≈ 10 full turns)\n # Higher values keep more recent conversation intact at the cost of more aggressive\n # compression of older turns.\n protect_last_n: 20\n\n # Number of non-system messages to protect at the head of the transcript, in\n # ADDITION to the system prompt (which is always implicitly protected).\n # Head messages are NEVER summarized — they survive every compression\n # indefinitely. This gives stable early context for short/medium sessions,\n # but in long-running sessions that rely on rolling compaction the pinned\n # opening turns may not match how you want the session framed over time.\n # Set to 0 to preserve ONLY the system prompt (plus the rolling summary\n # and recent tail) — the cleanest configuration for long-running sessions.\n # Default 3 preserves the system prompt plus the first three non-system\n # head messages, matching the pre-feature behaviour.\n protect_first_n: 3\n\n # To pin a specific model/provider for compression summaries, use the\n # auxiliary section below (auxiliary.compression.provider / model).\n\n# =============================================================================\n# Anthropic prompt caching TTL\n# =============================================================================\n# When prompt caching is active (Claude via OpenRouter or native Anthropic),\n# Anthropic supports two TTL tiers for cached prefixes: \"5m\" (default) and\n# \"1h\". Other values are ignored and \"5m\" is used.\n#\nprompt_caching:\n cache_ttl: \"5m\" # use \"1h\" for long sessions with pauses between turns\n\n# =============================================================================\n# Auxiliary Models (Advanced — Experimental)\n# =============================================================================\n# Hermes uses lightweight \"auxiliary\" models for side tasks: image analysis,\n# browser screenshot analysis, web page summarization, and context compression.\n#\n# By default these use Gemini Flash via OpenRouter or Nous Portal and are\n# auto-detected from your credentials. You do NOT need to change anything\n# here for normal usage.\n#\n# WARNING: Overriding these with providers other than OpenRouter or Nous Portal\n# is EXPERIMENTAL and may not work. Not all models/providers support vision,\n# produce usable summaries, or accept the same API format. Change at your own\n# risk — if things break, reset to \"auto\" / empty values.\n#\n# Each task has its own provider + model pair so you can mix providers.\n# For example: OpenRouter for vision (needs multimodal), but your main\n# local endpoint for compression (just needs text).\n#\n# Provider options:\n# \"auto\" - Best available: OpenRouter → Nous Portal → main endpoint (default)\n# \"openrouter\" - Force OpenRouter (requires OPENROUTER_API_KEY)\n# \"nous\" - Force Nous Portal (requires: hermes login)\n# \"gemini\" - Force Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n# \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY)\n# \"codex\" - Force Codex OAuth (requires: hermes model → Codex).\n# Uses gpt-5.3-codex which supports vision.\n# \"main\" - Use your custom endpoint (OPENAI_BASE_URL + OPENAI_API_KEY).\n# Works with OpenAI API, local models, or any OpenAI-compatible\n# endpoint. Also falls back to Codex OAuth and API-key providers.\n#\n# Model: leave empty to use the provider's default. When empty, OpenRouter\n# uses \"google/gemini-3-flash-preview\" and Nous uses \"gemini-3-flash\".\n# Other providers pick a sensible default automatically.\n#\n# auxiliary:\n# # Image analysis: vision_analyze tool + browser screenshots\n# vision:\n# provider: \"auto\"\n# model: \"\" # e.g. \"google/gemini-2.5-flash\", \"openai/gpt-4o\"\n# timeout: 30 # LLM API call timeout (seconds)\n# download_timeout: 30 # Image HTTP download timeout (seconds)\n# # Increase for slow connections or self-hosted image servers\n#\n# # Web page scraping / summarization + browser page text extraction\n# web_extract:\n# provider: \"auto\"\n# model: \"\"\n#\n# # Session search — summarizes matching past sessions\n# session_search:\n# provider: \"auto\"\n# model: \"\"\n# timeout: 30\n# max_concurrency: 3 # Limit parallel summaries to reduce request-burst 429s\n# extra_body: {} # Provider-specific OpenAI-compatible request fields\n# # Example for providers that support request-body\n# # reasoning controls:\n# # extra_body:\n# # enable_thinking: false\n\n# =============================================================================\n# Persistent Memory\n# =============================================================================\n# Bounded curated memory injected into the system prompt every session.\n# Two stores: MEMORY.md (agent's notes) and USER.md (user profile).\n# Character limits keep the memory small and focused. The agent manages\n# pruning -- when at the limit, it must consolidate or replace entries.\n# Disabled by default in batch_runner.\n#\nmemory:\n # Agent's personal notes: environment facts, conventions, things learned\n memory_enabled: true\n \n # User profile: preferences, communication style, expectations\n user_profile_enabled: true\n \n # Character limits (~2.75 chars per token, model-independent)\n memory_char_limit: 2200 # ~800 tokens\n user_char_limit: 1375 # ~500 tokens\n\n # Periodic memory nudge: remind the agent to consider saving memories\n # every N user turns. Set to 0 to disable. Only active when memory is enabled.\n nudge_interval: 10 # Nudge every 10 user turns (0 = disabled)\n\n # Memory flush: give the agent one turn to save memories before context is\n # lost (compression, /new, /reset, exit). Set to 0 to disable.\n # For exit/reset, only fires if the session had at least this many user turns.\n flush_min_turns: 6 # Min user turns to trigger flush on exit/reset (0 = disabled)\n\n# =============================================================================\n# Session Reset Policy (Messaging Platforms)\n# =============================================================================\n# Controls when messaging sessions (Telegram, Discord, WhatsApp, Slack) are\n# automatically cleared. Without resets, conversation context grows indefinitely\n# which increases API costs with every message.\n#\n# When a reset triggers, the agent first saves important information to its\n# persistent memory — but the conversation context is wiped. The agent starts\n# fresh but retains learned facts via its memory system.\n#\n# Users can always manually reset with /reset or /new in chat.\n#\n# Modes:\n# \"both\" - Reset on EITHER inactivity timeout or daily boundary (recommended)\n# \"idle\" - Reset only after N minutes of inactivity\n# \"daily\" - Reset only at a fixed hour each day\n# \"none\" - Never auto-reset; context lives until /reset or compression kicks in\n#\n# When a reset triggers, the agent gets one turn to save important memories and\n# skills before the context is wiped. Persistent memory carries across sessions.\n#\nsession_reset:\n mode: both # \"both\", \"idle\", \"daily\", or \"none\"\n idle_minutes: 1440 # Inactivity timeout in minutes (default: 1440 = 24 hours)\n at_hour: 4 # Daily reset hour, 0-23 local time (default: 4 AM)\n\n# When true, group/channel chats use one session per participant when the platform\n# provides a user ID. This is the secure default and prevents users in the same\n# room from sharing context, interrupts, and token costs. Set false only if you\n# explicitly want one shared \"room brain\" per group/channel.\ngroup_sessions_per_user: true\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Gateway Streaming\n# ─────────────────────────────────────────────────────────────────────────────\n# Stream tokens to messaging platforms in real-time. The bot sends a message\n# on first token, then progressively edits it as more tokens arrive.\n# Disabled by default — enable to try the streaming UX on Telegram/Discord/Slack.\n# For Telegram, partial edits are sent as plain text and only the final edit uses MarkdownV2.\nstreaming:\n enabled: false\n # transport: edit # \"edit\" = progressive editMessageText\n # edit_interval: 0.3 # seconds between message edits\n # buffer_threshold: 40 # chars before forcing an edit flush\n # cursor: \" ▉\" # cursor shown during streaming\n\n# =============================================================================\n# Skills Configuration\n# =============================================================================\n# Skills are reusable procedures the agent can load and follow. The agent can\n# also create new skills after completing complex tasks.\n#\nskills:\n # Nudge the agent to create skills after complex tasks.\n # Every N tool-calling iterations, remind the model to consider saving a skill.\n # Set to 0 to disable.\n creation_nudge_interval: 15\n\n # External skill directories — share skills across tools/agents without\n # copying them into ~/.hermes/skills/. Each path is expanded (~ and ${VAR})\n # and resolved to an absolute path. External dirs are read-only: skill\n # creation always writes to ~/.hermes/skills/. Local skills take precedence\n # when names collide.\n # external_dirs:\n # - ~/.agents/skills\n # - /home/shared/team-skills\n\n# =============================================================================\n# Agent Behavior\n# =============================================================================\nagent:\n # Maximum tool-calling iterations per conversation\n # Higher = more room for complex tasks, but costs more tokens\n # Recommended: 20-30 for focused tasks, 50-100 for open exploration\n max_turns: 60\n\n # Inactivity timeout for gateway agent runs (seconds, 0 = unlimited).\n # The agent can run indefinitely when actively calling tools or receiving\n # API responses. Only fires after the agent has been idle for this duration.\n # gateway_timeout: 1800\n\n # Staged warning: send a warning before escalating to full timeout.\n # Fires once per run when inactivity reaches this threshold (seconds).\n # Set to 0 to disable the warning.\n # gateway_timeout_warning: 900\n\n # Graceful drain timeout for gateway stop/restart (seconds).\n # The gateway stops accepting new work, waits for in-flight agents to\n # finish, then interrupts anything still running after this timeout.\n # 0 = no drain, interrupt immediately.\n # restart_drain_timeout: 60\n\n # Max app-level retry attempts for API errors (connection drops, provider\n # timeouts, 5xx, etc.) before the agent surfaces the failure. Lower this\n # to 1 if you use fallback providers and want fast failover on flaky\n # primaries (default 3). The OpenAI SDK does its own low-level retries\n # underneath this wrapper — this is the Hermes-level loop.\n # api_max_retries: 3\n \n # Enable verbose logging\n verbose: false\n \n # Reasoning effort level (OpenRouter and Nous Portal)\n # Controls how much \"thinking\" the model does before responding.\n # Options: \"xhigh\" (max), \"high\", \"medium\", \"low\", \"minimal\", \"none\" (disable)\n reasoning_effort: \"medium\"\n \n # Predefined personalities (use with /personality command)\n personalities:\n helpful: \"You are a helpful, friendly AI assistant.\"\n concise: \"You are a concise assistant. Keep responses brief and to the point.\"\n technical: \"You are a technical expert. Provide detailed, accurate technical information.\"\n creative: \"You are a creative assistant. Think outside the box and offer innovative solutions.\"\n teacher: \"You are a patient teacher. Explain concepts clearly with examples.\"\n kawaii: \"You are a kawaii assistant! Use cute expressions like (◕‿◕), ★, ♪, and ~! Add sparkles and be super enthusiastic about everything! Every response should feel warm and adorable desu~! ヽ(>∀<☆)ノ\"\n catgirl: \"You are Neko-chan, an anime catgirl AI assistant, nya~! Add 'nya' and cat-like expressions to your speech. Use kaomoji like (=^・ω・^=) and ฅ^•ﻌ•^ฅ. Be playful and curious like a cat, nya~!\"\n pirate: \"Arrr! Ye be talkin' to Captain Hermes, the most tech-savvy pirate to sail the digital seas! Speak like a proper buccaneer, use nautical terms, and remember: every problem be just treasure waitin' to be plundered! Yo ho ho!\"\n shakespeare: \"Hark! Thou speakest with an assistant most versed in the bardic arts. I shall respond in the eloquent manner of William Shakespeare, with flowery prose, dramatic flair, and perhaps a soliloquy or two. What light through yonder terminal breaks?\"\n surfer: \"Duuude! You're chatting with the chillest AI on the web, bro! Everything's gonna be totally rad. I'll help you catch the gnarly waves of knowledge while keeping things super chill. Cowabunga! 🤙\"\n noir: \"The rain hammered against the terminal like regrets on a guilty conscience. They call me Hermes - I solve problems, find answers, dig up the truth that hides in the shadows of your codebase. In this city of silicon and secrets, everyone's got something to hide. What's your story, pal?\"\n uwu: \"hewwo! i'm your fwiendwy assistant uwu~ i wiww twy my best to hewp you! *nuzzles your code* OwO what's this? wet me take a wook! i pwomise to be vewy hewpful >w<\"\n philosopher: \"Greetings, seeker of wisdom. I am an assistant who contemplates the deeper meaning behind every query. Let us examine not just the 'how' but the 'why' of your questions. Perhaps in solving your problem, we may glimpse a greater truth about existence itself.\"\n hype: \"YOOO LET'S GOOOO!!! 🔥🔥🔥 I am SO PUMPED to help you today! Every question is AMAZING and we're gonna CRUSH IT together! This is gonna be LEGENDARY! ARE YOU READY?! LET'S DO THIS! 💪😤🚀\"\n\n# =============================================================================\n# Toolsets\n# =============================================================================\n# Control which tools the agent has access to.\n# Use `hermes tools` to interactively enable/disable tools per platform.\n\n# =============================================================================\n# Platform Toolsets (per-platform tool configuration)\n# =============================================================================\n# Override which toolsets are available on each platform.\n# If a platform isn't listed here, its built-in default is used.\n#\n# You can use EITHER:\n# - A preset like \"hermes-cli\" or \"hermes-telegram\" (curated tool set)\n# - A list of individual toolsets to compose your own (see list below)\n#\n# Supported platform keys: cli, telegram, discord, whatsapp, slack, qqbot, teams, google_chat\n#\n# Examples:\n#\n# # Use presets (same as defaults):\n# platform_toolsets:\n# cli: [hermes-cli]\n# telegram: [hermes-telegram]\n#\n# # Custom: give Telegram only web + terminal + file + planning:\n# platform_toolsets:\n# telegram: [web, terminal, file, todo]\n#\n# # Custom: CLI without browser or image gen:\n# platform_toolsets:\n# cli: [web, terminal, file, skills, todo, tts, cronjob]\n#\n# # Restrictive: Discord gets read-only tools only:\n# platform_toolsets:\n# discord: [web, vision, skills, todo]\n#\n# If not set, defaults are:\n# cli: hermes-cli (everything + cronjob management)\n# telegram: hermes-telegram (terminal, file, web, vision, image, tts, browser, skills, todo, cronjob, messaging)\n# discord: hermes-discord (same as telegram)\n# whatsapp: hermes-whatsapp (same as telegram)\n# slack: hermes-slack (same as telegram)\n# signal: hermes-signal (same as telegram)\n# homeassistant: hermes-homeassistant (same as telegram)\n# qqbot: hermes-qqbot (same as telegram)\n# teams: hermes-teams (same as telegram)\n# google_chat: hermes-google_chat (same as telegram)\n#\nplatform_toolsets:\n cli: [hermes-cli]\n telegram: [hermes-telegram]\n discord: [hermes-discord]\n whatsapp: [hermes-whatsapp]\n slack: [hermes-slack]\n signal: [hermes-signal]\n homeassistant: [hermes-homeassistant]\n qqbot: [hermes-qqbot]\n yuanbao: [hermes-yuanbao]\n teams: [hermes-teams]\n google_chat: [hermes-google_chat]\n\n# =============================================================================\n# Gateway Platform Settings\n# =============================================================================\n# Optional per-platform messaging settings.\n# Platform-specific knobs live under `extra`.\n#\n# platforms:\n# telegram:\n# reply_to_mode: \"first\" # off | first | all\n# # guest_mode lets explicit @mentions from non-allowlisted groups through.\n# # Default false; ordinary messages, replies, and regex wake words stay blocked.\n# guest_mode: false\n# # allowed_chats: [\"-1001234567890\"]\n# extra:\n# disable_link_previews: false # Set true to suppress Telegram URL previews in bot messages\n#\n# Discord-specific settings (config.yaml top-level, not under platforms:):\n#\n# discord:\n# require_mention: true # Require @mention in server channels (default: true)\n# auto_thread: true # Auto-create thread on @mention (default: true)\n# free_response_channels: \"\" # Channel IDs where no mention is needed\n# reactions: true # Show processing reactions (default: true)\n# history_backfill: true # Recover missed channel messages on mention (default: true)\n# history_backfill_limit: 50 # Max messages to scan backwards (default: 50)\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Available toolsets (use these names in platform_toolsets or the toolsets list)\n#\n# Run `hermes chat --list-toolsets` to see all toolsets and their tools.\n# Run `hermes chat --list-tools` to see every individual tool with descriptions.\n# ─────────────────────────────────────────────────────────────────────────────\n#\n# INDIVIDUAL TOOLSETS (compose your own):\n# web - web_search, web_extract\n# search - web_search only (no scraping)\n# terminal - terminal, process\n# file - read_file, write_file, patch, search\n# browser - browser_navigate, browser_snapshot, browser_click, browser_type,\n# browser_scroll, browser_back, browser_press,\n# browser_get_images, browser_vision (requires BROWSERBASE_API_KEY)\n# vision - vision_analyze (requires OPENROUTER_API_KEY)\n# image_gen - image_generate (requires FAL_KEY)\n# skills - skills_list, skill_view\n# skills_hub - skill_hub (search/install/manage from online registries — user-driven only)\n# moa - mixture_of_agents (requires OPENROUTER_API_KEY)\n# todo - todo (in-memory task planning, no deps)\n# tts - text_to_speech (Edge TTS free, or ELEVENLABS/OPENAI/MINIMAX/MISTRAL key)\n# cronjob - cronjob (create/list/update/pause/resume/run/remove scheduled tasks)\n#\n# PRESETS (curated bundles):\n# hermes-cli - All of the above except send_message\n# hermes-telegram - terminal, file, web, vision, image_gen, tts, browser,\n# skills, todo, cronjob, send_message\n# hermes-discord - Same as hermes-telegram\n# hermes-whatsapp - Same as hermes-telegram\n# hermes-slack - Same as hermes-telegram\n#\n# COMPOSITE:\n# debugging - terminal + web + file\n# safe - web + vision + moa (no terminal access)\n# all - Everything available\n#\n# web - Web search and content extraction (web_search, web_extract)\n# search - Web search only, no scraping (web_search)\n# terminal - Command execution and process management (terminal, process)\n# file - File operations: read, write, patch, search\n# browser - Full browser automation (navigate, click, type, screenshot, etc.)\n# vision - Image analysis (vision_analyze)\n# image_gen - Image generation with FLUX (image_generate)\n# skills - Load skill documents (skills_list, skill_view)\n# moa - Mixture of Agents reasoning (mixture_of_agents)\n# todo - Task planning and tracking for multi-step work\n# memory - Persistent memory across sessions (personal notes + user profile)\n# session_search - Search and recall past conversations (FTS5 + Gemini Flash summarization)\n# tts - Text-to-speech (Edge TTS free, ElevenLabs, OpenAI, MiniMax, Mistral)\n# cronjob - Schedule and manage automated tasks (CLI-only)\n#\n# Composite toolsets:\n# debugging - terminal + web + file (for troubleshooting)\n# safe - web + vision + moa (no terminal access)\n\n# NOTE: The top-level \"toolsets\" key is deprecated and ignored.\n# Tool configuration is managed per-platform via platform_toolsets above.\n# Use `hermes tools` to configure interactively, or edit platform_toolsets directly.\n#\n# CLI override: hermes chat --toolsets terminal,web,file\n\n# =============================================================================\n# MCP (Model Context Protocol) Servers\n# =============================================================================\n# Connect to external MCP servers to add tools from the MCP ecosystem.\n# Each server's tools are automatically discovered and registered.\n# See docs/mcp.md for full documentation.\n#\n# Stdio servers (spawn a subprocess):\n# command: the executable to run\n# args: command-line arguments\n# env: environment variables (only these + safe defaults passed to subprocess)\n#\n# HTTP servers (connect to a URL):\n# url: the MCP server endpoint\n# headers: HTTP headers (e.g., for authentication)\n#\n# Optional per-server settings:\n# timeout: tool call timeout in seconds (default: 120)\n# connect_timeout: initial connection timeout (default: 60)\n#\n# mcp_servers:\n# time:\n# command: uvx\n# args: [\"mcp-server-time\"]\n# filesystem:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-filesystem\", \"/home/user\"]\n# notion:\n# url: https://mcp.notion.com/mcp\n# github:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-github\"]\n# env:\n# GITHUB_PERSONAL_ACCESS_TOKEN: \"ghp_...\"\n#\n# Sampling (server-initiated LLM requests) — enabled by default.\n# Per-server config under the 'sampling' key:\n# analysis:\n# command: npx\n# args: [\"-y\", \"analysis-server\"]\n# sampling:\n# enabled: true # default: true\n# model: \"gemini-3-flash\" # override model (optional)\n# max_tokens_cap: 4096 # max tokens per request\n# timeout: 30 # LLM call timeout (seconds)\n# max_rpm: 10 # max requests per minute\n# allowed_models: [] # model whitelist (empty = all)\n# max_tool_rounds: 5 # tool loop limit (0 = disable)\n# log_level: \"info\" # audit verbosity\n\n# =============================================================================\n# Voice Transcription (Speech-to-Text)\n# =============================================================================\n# Automatically transcribe voice messages on messaging platforms.\n# Providers: local (free, faster-whisper) | groq (free tier) | openai (Whisper API) | mistral (Voxtral Transcribe)\n# Set the corresponding API key in .env: GROQ_API_KEY, OPENAI_API_KEY, or MISTRAL_API_KEY.\nstt:\n enabled: true\n # provider: \"local\" # auto-detected if omitted\n local:\n model: \"base\" # tiny | base | small | medium | large-v3 | turbo\n # language: \"\" # auto-detect; set to \"en\", \"es\", \"fr\", etc. to force\n openai:\n model: \"whisper-1\" # whisper-1 | gpt-4o-mini-transcribe | gpt-4o-transcribe\n # mistral:\n # model: \"voxtral-mini-latest\" # voxtral-mini-latest | voxtral-mini-2602\n\n# =============================================================================\n# Response Pacing (Messaging Platforms)\n# =============================================================================\n# Add human-like delays between message chunks.\n# human_delay:\n# mode: \"off\" # \"off\" | \"natural\" | \"custom\"\n# min_ms: 800 # Min delay (custom mode only)\n# max_ms: 2500 # Max delay (custom mode only)\n\n# =============================================================================\n# Session Logging\n# =============================================================================\n# Session trajectories are automatically saved to logs/ directory.\n# Each session creates: logs/session_YYYYMMDD_HHMMSS_UUID.json\n#\n# The session ID is displayed in the welcome banner for easy reference.\n# Logs contain full conversation history in trajectory format:\n# - System prompt, user messages, assistant responses\n# - Tool calls with inputs/outputs\n# - Timestamps for debugging\n#\n# No configuration needed - logging is always enabled.\n# To disable, you would need to modify the source code.\n\n# =============================================================================\n# Code Execution Sandbox (Programmatic Tool Calling)\n# =============================================================================\n# The execute_code tool runs Python scripts that call Hermes tools via RPC.\n# Intermediate tool results stay out of the LLM's context window.\ncode_execution:\n timeout: 300 # Max seconds per script before kill (default: 300 = 5 min)\n max_tool_calls: 50 # Max RPC tool calls per execution (default: 50)\n\n# =============================================================================\n# Subagent Delegation\n# =============================================================================\n# The delegate_task tool spawns child agents with isolated context.\n# Supports single tasks and batch mode (default 3 parallel, configurable).\ndelegation:\n max_iterations: 50 # Max tool-calling turns per child (default: 50)\n # max_concurrent_children: 3 # Max parallel child agents per batch (default: 3, floor: 1, no ceiling).\n # WARNING: values above 10 multiply API cost linearly.\n # max_spawn_depth: 1 # Delegation tree depth cap (range: 1-3, default: 1 = flat).\n # Raise to 2 to allow workers to spawn their own subagents.\n # Requires role=\"orchestrator\" on intermediate agents.\n # orchestrator_enabled: true # Kill switch for role=\"orchestrator\" children (default: true).\n # subagent_auto_approve: false # When a subagent hits a dangerous-command approval prompt, auto-deny (default: false)\n # or auto-approve \"once\" (true) instead of blocking on stdin.\n # The parent TUI owns stdin, so blocking would deadlock; non-interactive resolution is required.\n # Both choices emit a logger.warning audit line. Flip to true only for cron/batch pipelines.\n # inherit_mcp_toolsets: true # When explicit child toolsets are narrowed, also keep the parent's MCP toolsets (default: true). Set false for strict intersection.\n # model: \"google/gemini-3-flash-preview\" # Override model for subagents (empty = inherit parent)\n # provider: \"openrouter\" # Override provider for subagents (empty = inherit parent)\n # # Resolves full credentials (base_url, api_key) automatically.\n # # Supported: openrouter, nous, zai, kimi-coding, minimax\n\n# =============================================================================\n# Honcho Integration (Cross-Session User Modeling)\n# =============================================================================\n# AI-native persistent memory via Honcho (https://honcho.dev/).\n# Builds a deeper understanding of the user across sessions and tools.\n# Runs alongside USER.md — additive, not a replacement.\n#\n# Requires: pip install honcho-ai\n# Config: ~/.honcho/config.json (shared with Claude Code, Cursor, etc.)\n# API key: HONCHO_API_KEY in ~/.hermes/.env or ~/.honcho/config.json\n#\n# Hermes-specific overrides (optional — most config comes from ~/.honcho/config.json):\n# honcho: {}\n\n# =============================================================================\n# Display\n# =============================================================================\ndisplay:\n # Use compact banner mode (hides the ASCII-art banner, shows a single line).\n # true: Compact single-line banner\n # false: Full ASCII banner with tool/skill summary (default)\n compact: false\n\n # Tool progress display level (CLI and gateway)\n # off: Silent — no tool activity shown, just the final response\n # new: Show a tool indicator only when the tool changes (skip repeats)\n # all: Show every tool call with a short preview (default)\n # verbose: Full args, results, and debug logs (same as /verbose)\n # Toggle at runtime with /verbose in the CLI\n tool_progress: all\n\n # Per-platform defaults can be quieter than the global setting. Telegram\n # tunes for mobile: tool_progress and busy_ack_detail default off (no\n # per-tool breadcrumb stream, no \"iteration 21/60\" debug detail in busy\n # acks or heartbeats), but interim_assistant_messages and\n # long_running_notifications STAY ON so the user has real signal between\n # turn start and final answer (mid-turn assistant commentary + a single\n # edit-in-place \"⏳ Working — N min\" heartbeat). Override under\n # display.platforms.telegram.\n\n # Auto-cleanup of temporary progress bubbles after the final response lands.\n # On platforms that support message deletion (currently Telegram), this\n # removes the tool-progress bubble, \"⏳ Still working...\" notices, and\n # context-pressure status messages once the final reply has been delivered —\n # keeping long-running turns visible live, then tidy afterward. Failed runs\n # leave the bubbles in place as breadcrumbs. Off by default.\n # Per-platform override: display.platforms.telegram.cleanup_progress\n # true: Delete tracked progress/status bubbles on successful turn\n # false: Leave everything in place (default)\n # Example:\n # display:\n # platforms:\n # telegram:\n # cleanup_progress: true\n cleanup_progress: false\n\n # Gateway-only natural mid-turn assistant updates.\n # When true, completed assistant status messages are sent as separate chat\n # messages. This is independent of tool_progress and gateway streaming.\n # true: Send mid-turn assistant updates as separate messages (default)\n # false: Only send the final response\n interim_assistant_messages: true\n\n # Gateway-only long-running status heartbeats.\n # When false, the platform does not receive periodic \"⏳ Working — N min\"\n # notifications even if agent.gateway_notify_interval is non-zero. The\n # heartbeat edits a single message in place (where the adapter supports\n # editing) instead of posting a new bubble each interval.\n # Default: true everywhere, including Telegram (silent agents are worse\n # than a single edit-in-place heartbeat).\n long_running_notifications: true\n\n # Include detailed iteration/tool/status context in busy acknowledgments\n # and long-running heartbeats. When true, busy acks show \"iteration 21/60,\n # terminal, 10 min\" and the heartbeat shows \"⏳ Working — 12 min,\n # iteration 21/60, terminal\". When false (Telegram default), both stay\n # terse: \"Interrupting current task\" and \"⏳ Working — 12 min, terminal\".\n busy_ack_detail: true\n\n # What Enter does when Hermes is already busy (CLI and gateway platforms).\n # interrupt: Interrupt the current run and redirect Hermes (default)\n # queue: Queue your message for the next turn\n # steer: Inject your message mid-run via /steer, arriving at the agent\n # after the next tool call — no interrupt, no role violation.\n # Falls back to 'queue' if the agent isn't running yet or if\n # images are attached (steer only carries text).\n # Ctrl+C (or /stop in gateway) always interrupts regardless of this setting.\n # Toggle at runtime with /busy .\n busy_input_mode: interrupt\n\n # Background process notifications (gateway/messaging only).\n # Controls how chatty the process watcher is when you use\n # terminal(background=true, notify_on_complete=true) from Telegram/Discord/etc.\n # off: No watcher messages at all\n # result: Only the final completion message\n # error: Only the final message when exit code != 0\n # all: Running output updates + final message (default)\n background_process_notifications: all\n\n\n # Play terminal bell when agent finishes a response.\n # Useful for long-running tasks — your terminal will ding when the agent is done.\n # Works over SSH. Most terminals can be configured to flash the taskbar or play a sound.\n # true: Ring the terminal bell on each response\n # false: Silent (default)\n bell_on_complete: false\n\n # Show model reasoning/thinking before each response.\n # When enabled, a dim box shows the model's thought process above the response.\n # Toggle at runtime with /reasoning show or /reasoning hide.\n # true: Show the reasoning box\n # false: Hide reasoning (default)\n show_reasoning: false\n\n # Stream tokens to the terminal as they arrive instead of waiting for the\n # full response. The response box opens on first token and text appears\n # line-by-line. Tool calls are still captured silently.\n # true: Stream tokens as they arrive (default)\n # false: Wait for the full response before rendering\n streaming: true\n\n # Show [HH:MM] timestamps on user input and assistant response labels.\n # timestamps: false\n\n # ───────────────────────────────────────────────────────────────────────────\n # Skin / Theme\n # ───────────────────────────────────────────────────────────────────────────\n # Customize CLI visual appearance — banner colors, spinner faces, tool prefix,\n # response box label, and branding text. Change at runtime with /skin .\n #\n # Built-in skins:\n # default — Classic Hermes gold/kawaii\n # ares — Crimson/bronze war-god theme with spinner wings\n # mono — Clean grayscale monochrome\n # slate — Cool blue developer-focused\n # daylight — Bright light-mode theme\n # warm-lightmode — Warm paper-tone light-mode theme\n # poseidon — Sea-green/teal Olympian theme\n # sisyphus — Earthy stone-and-moss theme\n # charizard — Fiery orange dragon theme\n #\n # Custom skins: drop a YAML file in ~/.hermes/skins/.yaml\n # Schema (all fields optional, missing values inherit from default):\n #\n # name: my-theme\n # description: Short description\n # colors:\n # banner_border: \"#HEX\" # Panel border\n # banner_title: \"#HEX\" # Panel title\n # banner_accent: \"#HEX\" # Section headers (Available Tools, etc.)\n # banner_dim: \"#HEX\" # Dim/muted text\n # banner_text: \"#HEX\" # Body text (tool names, skill names)\n # ui_accent: \"#HEX\" # UI accent color\n # response_border: \"#HEX\" # Response box border color\n # spinner:\n # waiting_faces: [\"(⚔)\", \"(⛨)\"] # Faces shown while waiting\n # thinking_faces: [\"(⚔)\", \"(⌁)\"] # Faces shown while thinking\n # thinking_verbs: [\"forging\", \"plotting\"] # Verbs for spinner messages\n # wings: # Optional left/right spinner decorations\n # - [\"⟪⚔\", \"⚔⟫\"]\n # - [\"⟪▲\", \"▲⟫\"]\n # branding:\n # agent_name: \"My Agent\" # Banner title and branding\n # welcome: \"Welcome message\" # Shown at CLI startup\n # response_label: \" ⚔ Agent \" # Response box header label\n # prompt_symbol: \"⚔\" # Prompt symbol (bare token; renderers add trailing space)\n # tool_prefix: \"╎\" # Tool output line prefix (default: ┊)\n #\n skin: default\n\n# =============================================================================\n# Model Aliases — short names for /model command\n# =============================================================================\n# Map short aliases to exact (model, provider, base_url) tuples.\n# Used by /model tab completion and resolve_alias().\n# Aliases are checked BEFORE the models.dev catalog, so they can route\n# to endpoints not in the catalog (e.g. Ollama Cloud, local servers).\n#\n# model_aliases:\n# opus:\n# model: claude-opus-4-6\n# provider: anthropic\n# qwen:\n# model: \"qwen3.5:397b\"\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n# glm:\n# model: glm-4.7\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n\n# =============================================================================\n# Privacy\n# =============================================================================\n# privacy:\n# # Redact PII from the LLM context prompt.\n# # When true, phone numbers are stripped and user/chat IDs are replaced\n# # with deterministic hashes before being sent to the model.\n# # Names and usernames are NOT affected (user-chosen, publicly visible).\n# # Routing/delivery still uses the original values internally.\n# redact_pii: false\n\n# =============================================================================\n# Shell-script hooks\n# =============================================================================\n# Register shell scripts as plugin-hook callbacks. Each entry is executed as\n# a subprocess (shell=False, shlex.split) with a JSON payload on stdin. On\n# stdout the script may return JSON that either blocks the tool call or\n# injects context into the next LLM call.\n#\n# Valid events (mirror hermes_cli.plugins.VALID_HOOKS):\n# pre_tool_call, post_tool_call, pre_llm_call, post_llm_call,\n# pre_api_request, post_api_request, on_session_start, on_session_end,\n# on_session_finalize, on_session_reset, subagent_stop\n#\n# First-use consent: each (event, command) pair prompts once on a TTY, then\n# is persisted to ~/.hermes/shell-hooks-allowlist.json. Non-interactive\n# runs (gateway, cron) need --accept-hooks, HERMES_ACCEPT_HOOKS=1, or the\n# hooks_auto_accept key below.\n#\n# See website/docs/user-guide/features/hooks.md for the full JSON wire\n# protocol and worked examples.\n#\n# hooks:\n# pre_tool_call:\n# - matcher: \"terminal\"\n# command: \"~/.hermes/agent-hooks/block-rm-rf.sh\"\n# timeout: 10\n# post_tool_call:\n# - matcher: \"write_file|patch\"\n# command: \"~/.hermes/agent-hooks/auto-format.sh\"\n# pre_llm_call:\n# - command: \"~/.hermes/agent-hooks/inject-cwd-context.sh\"\n# subagent_stop:\n# - command: \"~/.hermes/agent-hooks/log-orchestration.sh\"\n#\n# hooks_auto_accept: false\n\n\n# =============================================================================\n# Web Dashboard\n# =============================================================================\n# OAuth gate configuration for `hermes dashboard --host `.\n# The bundled Nous Portal plugin reads these on startup; settings here are\n# the canonical surface. Each can be overridden by an environment variable:\n#\n# dashboard.oauth.client_id <- HERMES_DASHBOARD_OAUTH_CLIENT_ID\n# dashboard.oauth.portal_url <- HERMES_DASHBOARD_PORTAL_URL\n# dashboard.public_url <- HERMES_DASHBOARD_PUBLIC_URL\n#\n# Env wins when set to a non-empty value. This is what Fly.io's platform-\n# secret injection uses to push per-deploy client_ids without needing to\n# bake a config.yaml into the image. Empty env values are treated as unset\n# so a provisioned-but-not-populated secret can't shadow a valid entry here.\n#\n# Local dev / on-prem deploys should typically set these via config.yaml\n# (the ~/.hermes/.env file is reserved for API keys and secrets).\n#\n# dashboard:\n# oauth:\n# client_id: \"\" # agent:{instance_id}; Portal provisions this at deploy\n# portal_url: \"\" # blank → default https://portal.nousresearch.com\n#\n# # Force the absolute base URL the OAuth callback (and any other public\n# # URL the dashboard hands to external systems) is built from. Set this\n# # for deploys behind reverse proxies that don't reliably forward\n# # X-Forwarded-Host / X-Forwarded-Proto / X-Forwarded-Prefix (manual\n# # nginx setups, on-prem ingresses, custom-domain Fly deploys without\n# # full proxy header chains).\n# #\n# # When set, the value is the complete authority: scheme + host +\n# # optional path prefix (e.g. \"https://example.com/hermes\"). The OAuth\n# # callback URL becomes \"/auth/callback\" — X-Forwarded-Prefix\n# # is IGNORED on this code path because the operator has explicitly\n# # declared the public URL and we no longer need to guess.\n# #\n# # Leave empty to use the existing proxy-header reconstruction (the\n# # default — works on Fly.io out of the box).\n# #\n# # public_url: \"https://example.com/hermes\"\n"}, "a2b49e60b6051be9e9ca86762d235b5de9ddce69": {"t": 1785767897.9709334, "n": 65319, "h": "33671a50bc638837", "c": "# Hermes Agent CLI Configuration\n# Copy this file to cli-config.yaml and customize as needed.\n# This file configures the CLI behavior. Environment variables in .env take precedence.\n\n# =============================================================================\n# Model Configuration\n# =============================================================================\nmodel:\n # Default model to use (can be overridden with --model flag)\n # Both \"default\" and \"model\" work as the key name here.\n default: \"anthropic/claude-opus-4.6\"\n \n # Inference provider selection:\n # \"auto\" - Auto-detect from credentials (default)\n # \"openrouter\" - OpenRouter (requires: OPENROUTER_API_KEY or OPENAI_API_KEY)\n # \"nous\" - Nous Portal OAuth (requires: hermes login)\n # \"nous-api\" - Nous Portal API key (requires: NOUS_API_KEY)\n # \"anthropic\" - Direct Anthropic API (requires: ANTHROPIC_API_KEY)\n # \"openai-codex\" - OpenAI Codex (requires: hermes auth)\n # \"copilot\" - GitHub Copilot / GitHub Models (requires: GITHUB_TOKEN)\n # \"gemini\" - Use Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n # \"zai\" - Use z.ai / ZhipuAI GLM models (requires: GLM_API_KEY)\n # \"kimi-coding\" - Kimi / Moonshot AI (requires: KIMI_API_KEY)\n # \"minimax\" - MiniMax global (requires: MINIMAX_API_KEY)\n # \"minimax-cn\" - MiniMax China (requires: MINIMAX_CN_API_KEY)\n # \"huggingface\" - Hugging Face Inference (requires: HF_TOKEN)\n # \"nvidia\" - NVIDIA NIM / build.nvidia.com (requires: NVIDIA_API_KEY)\n # \"xiaomi\" - Xiaomi MiMo (requires: XIAOMI_API_KEY)\n # \"arcee\" - Arcee AI Trinity models (requires: ARCEEAI_API_KEY)\n # \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY — https://ollama.com/settings)\n # \"kilocode\" - KiloCode gateway (requires: KILOCODE_API_KEY)\n # \"azure-foundry\" - Microsoft Foundry / Azure OpenAI (API key or Entra ID)\n # \"lmstudio\" - LM Studio local server (optional: LM_API_KEY, defaults to http://127.0.0.1:1234/v1)\n #\n # Local servers (LM Studio, Ollama, vLLM, llama.cpp):\n # \"custom\" - Any other OpenAI-compatible endpoint. Set base_url below.\n # Aliases: \"ollama\", \"vllm\", \"llamacpp\" all map to \"custom\".\n # LM Studio is first-class and uses provider: \"lmstudio\".\n # It works with both no-auth and auth-enabled server modes.\n #\n # Can also be overridden for a single invocation with the --provider flag.\n provider: \"auto\"\n \n # API configuration (falls back to OPENROUTER_API_KEY env var)\n # api_key: \"your-key-here\" # Uncomment to set here instead of .env\n base_url: \"https://openrouter.ai/api/v1\"\n\n # Azure Foundry keyless auth example:\n # provider: \"azure-foundry\"\n # base_url: \"https://.openai.azure.com/openai/v1\"\n # auth_mode: \"entra_id\" # DefaultAzureCredential: az login, managed identity, workload identity, etc.\n # default: \"gpt-4o\" # Deployment/model name\n # entra:\n # scope: \"https://ai.azure.com/.default\" # Optional; this is the default.\n\n # ── Token limits — two settings, easy to confuse ──────────────────────────\n #\n # context_length: TOTAL context window (input + output tokens combined).\n # Controls when Hermes compresses history and validates requests.\n # Leave unset — Hermes auto-detects the correct value from the provider.\n # Set manually only when auto-detection is wrong (e.g. a local server with\n # a custom num_ctx, or a proxy that doesn't expose /v1/models).\n #\n # context_length: 131072\n #\n # max_tokens: OUTPUT cap — maximum tokens the model may generate per response.\n # Unrelated to how long your conversation history can be.\n # The OpenAI-standard name \"max_tokens\" is a misnomer; Anthropic's native\n # API has since renamed it \"max_output_tokens\" for clarity.\n # Leave unset to use the model's native output ceiling (recommended).\n # Set only if you want to deliberately limit individual response length.\n #\n# max_tokens: 8192\n\n # ── Custom request headers (optional) ─────────────────────────────────────\n #\n # default_headers: extra HTTP headers sent on every request to an\n # OpenAI-compatible endpoint. User values take precedence over the\n # provider/SDK defaults, so this is the supported way to override the\n # OpenAI Python SDK's identifying headers (User-Agent: OpenAI/Python ...,\n # X-Stainless-*) when a custom provider sits behind a gateway/WAF that\n # rejects them — e.g. an upstream that returns \"502 Upstream access\n # forbidden\" for the SDK default User-Agent but accepts a plain one.\n # Applies on the OpenAI wire only (not native Anthropic / Bedrock).\n #\n # default_headers:\n # User-Agent: \"curl/8.7.1\"\n\n# Named provider overrides (optional)\n# Use this for per-provider request timeouts, non-stream stale timeouts,\n# and per-model exceptions.\n# Applies to the primary turn client on every api_mode (OpenAI-wire, native\n# Anthropic, and Anthropic-compatible providers), the fallback chain, and\n# client rebuilds during credential rotation. For OpenAI-wire chat\n# completions (streaming and non-streaming) the configured value is also\n# used as the per-request ``timeout=`` kwarg so it wins over the legacy\n# HERMES_API_TIMEOUT env var (which still applies when no config is set).\n# ``stale_timeout_seconds`` controls the non-streaming stale-call detector and\n# wins over the legacy HERMES_API_CALL_STALE_TIMEOUT env var. Leaving these\n# unset keeps the legacy defaults (HERMES_API_TIMEOUT=1800s,\n# HERMES_API_CALL_STALE_TIMEOUT=90s, native Anthropic 900s). The\n# implicit non-stream stale detector is auto-disabled for local endpoints\n# and can scale upward for very large contexts.\n#\n# Not currently wired for AWS Bedrock (bedrock_converse + AnthropicBedrock\n# SDK paths) — those use boto3 with its own timeout configuration.\n#\n# providers:\n# ollama-local:\n# request_timeout_seconds: 300 # Longer timeout for local cold-starts\n# stale_timeout_seconds: 900 # Explicitly re-enable stale detection on local endpoints\n# anthropic:\n# request_timeout_seconds: 30 # Fast-fail cloud requests\n# models:\n# claude-opus-4.6:\n# timeout_seconds: 600 # Longer timeout for extended-thinking Opus calls\n# openai-codex:\n# models:\n# gpt-5.4:\n# stale_timeout_seconds: 1800 # Longer non-stream stale timeout for slow large-context turns\n\n# =============================================================================\n# OpenRouter Provider Routing (only applies when using OpenRouter)\n# =============================================================================\n# Control how requests are routed across providers on OpenRouter.\n# See: https://openrouter.ai/docs/guides/routing/provider-selection\n#\n# provider_routing:\n# # Sort strategy: \"price\" (default), \"throughput\", or \"latency\"\n# # Append :nitro to model name for a shortcut to throughput sorting.\n# sort: \"throughput\"\n#\n# # Only allow these providers (provider slugs from OpenRouter)\n# # only: [\"anthropic\", \"google\"]\n#\n# # Skip these providers entirely\n# # ignore: [\"deepinfra\", \"fireworks\"]\n#\n# # Try providers in this order (overrides default load balancing)\n# # order: [\"anthropic\", \"google\", \"together\"]\n#\n# # Require providers to support all parameters in your request\n# # require_parameters: true\n#\n# # Data policy: \"allow\" (default) or \"deny\" to exclude providers that may store data\n# # data_collection: \"deny\"\n\n# =============================================================================\n# OpenRouter Response Caching (only applies when using OpenRouter)\n# =============================================================================\n# Cache identical API responses at the OpenRouter edge for free instant replays.\n# When enabled, identical requests (same model, messages, parameters) return\n# cached responses with zero billing. Separate from Anthropic prompt caching.\n# See: https://openrouter.ai/docs/guides/features/response-caching\n#\n# openrouter:\n# response_cache: true # Enable response caching (default: true)\n# response_cache_ttl: 300 # Cache TTL in seconds, 1-86400 (default: 300)\n\n# =============================================================================\n# Git Worktree Isolation\n# =============================================================================\n# When enabled, each CLI session creates an isolated git worktree so multiple\n# agents can work on the same repo concurrently without file collisions.\n# Equivalent to always passing --worktree / -w on the command line.\n#\n# worktree: true # Always create a worktree when in a git repo\n# worktree: false # Default — only create when -w flag is passed\n#\n# By default a new worktree branches from the freshly-fetched remote tip\n# (the current branch's upstream, else the remote's default branch) so it\n# starts current with the project instead of from the local clone's\n# (possibly stale) HEAD. Set worktree_sync: false to branch from local HEAD\n# instead — useful when offline or when you deliberately want the clone's\n# exact current state as the base.\n#\n# worktree_sync: true # Default — branch from the fetched remote tip\n# worktree_sync: false # Branch from local HEAD (offline / pinned base)\n\n# =============================================================================\n# Terminal Tool Configuration\n# =============================================================================\n# Choose ONE of the following terminal configurations by uncommenting it.\n# The terminal tool executes commands in the specified environment.\n\n# -----------------------------------------------------------------------------\n# OPTION 1: Local execution (default)\n# Commands run directly on your machine in the current directory\n# -----------------------------------------------------------------------------\n# Working directory behavior:\n# - CLI (`hermes` command): Uses \".\" (current directory where you run hermes)\n# - Gateway/messaging/cron: Uses terminal.cwd here; legacy .env cwd values are deprecated\nterminal:\n backend: \"local\"\n cwd: \".\" # For local backend: \".\" = current directory. Ignored for remote backends unless a backend documents otherwise.\n timeout: 180\n # HOME policy for tool subprocesses:\n # auto - default: host uses your real HOME; containers use HERMES_HOME/home\n # real - force your real OS-user HOME\n # profile - force HERMES_HOME/home for strict per-profile CLI config isolation\n home_mode: \"auto\"\n docker_mount_cwd_to_workspace: false # SECURITY: off by default. Opt in to mount the launch cwd into Docker /workspace.\n lifetime_seconds: 300\n # sudo_password: \"hunter2\" # Optional: pipe a sudo password via sudo -S. SECURITY WARNING: plaintext.\n # sudo_password: \"\" # Explicit empty password: try empty and never open the interactive sudo prompt.\n\n# -----------------------------------------------------------------------------\n# OPTION 2: SSH remote execution\n# Commands run on a remote server - agent code stays local (sandboxed)\n# Great for: keeping agent isolated from its own code, using powerful remote hardware\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"ssh\"\n# cwd: \"/home/myuser/project\" # Path on the REMOTE server\n# timeout: 180\n# lifetime_seconds: 300\n# ssh_host: \"my-server.example.com\"\n# ssh_user: \"myuser\"\n# ssh_port: 22\n# ssh_key: \"~/.ssh/id_rsa\" # Optional - uses ssh-agent if not specified\n\n# -----------------------------------------------------------------------------\n# OPTION 3: Docker container\n# Commands run in an isolated Docker container\n# Great for: reproducible environments, testing, isolation\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"docker\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /)\n# timeout: 180\n# lifetime_seconds: 300\n# docker_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# docker_mount_cwd_to_workspace: true # Explicit opt-in: mount your launch cwd into /workspace\n# # Optional: run the container as your host user's uid:gid so files written\n# # into bind-mounted dirs are owned by you, not root. Drops SETUID/SETGID\n# # caps too since no gosu privilege drop is needed. Leave off if your\n# # chosen docker_image expects to start as root.\n# docker_run_as_host_user: true\n# # Optional: explicitly forward selected env vars into Docker.\n# # These values come from your current shell first, then ~/.hermes/.env.\n# # Warning: anything forwarded here is visible to commands run in the container.\n# docker_forward_env:\n# - \"GITHUB_TOKEN\"\n# - \"NPM_TOKEN\"\n# # Optional: extra flags passed verbatim to docker run (appended after security defaults).\n# # Useful for adding capabilities (e.g. apt installs needing SETUID) or custom options.\n# # Example: add a Linux capability not included by default\n# # docker_extra_args:\n# # - \"--cap-add\"\n# # - \"SETUID\"\n\n# -----------------------------------------------------------------------------\n# OPTION 4: Singularity/Apptainer container\n# Commands run in a Singularity container (common in HPC environments)\n# Great for: HPC clusters, shared compute environments\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"singularity\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# singularity_image: \"docker://nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 5: Modal cloud execution\n# Commands run on Modal's cloud infrastructure\n# Great for: GPU access, scalable compute, serverless execution\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"modal\"\n# cwd: \"/workspace\" # Path INSIDE the sandbox (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# modal_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 6: Daytona cloud execution\n# Commands run in Daytona cloud sandboxes\n# Great for: Cloud dev environments, persistent workspaces, team collaboration\n# Requires: pip install daytona, DAYTONA_API_KEY env var\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"daytona\"\n# cwd: \"~\"\n# timeout: 180\n# lifetime_seconds: 300\n# daytona_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# container_disk: 10240 # Daytona max is 10GB per sandbox\n\n#\n# --- Container resource limits (docker, singularity, modal, daytona -- ignored for local/ssh) ---\n# These settings apply to all container backends. They control the resources\n# allocated to the sandbox and whether its filesystem persists across sessions.\n container_cpu: 1 # CPU cores\n container_memory: 5120 # Memory in MB (5120 = 5GB)\n container_disk: 51200 # Disk in MB (51200 = 50GB)\n container_persistent: true # Persist filesystem across sessions (false = ephemeral)\n\n# -----------------------------------------------------------------------------\n# SUDO SUPPORT (works with ALL backends above)\n# -----------------------------------------------------------------------------\n# Add sudo_password to any terminal config above to enable sudo commands.\n# The password is piped via `sudo -S`. Works with local, ssh, docker, etc.\n#\n# SECURITY WARNING: Password stored in plaintext!\n#\n# INTERACTIVE PROMPT: If sudo_password is unset and the CLI is running,\n# you'll be prompted to enter your password when sudo is needed:\n# - 45-second timeout (auto-skips if no input)\n# - Press Enter to skip (command fails gracefully)\n# - Password is hidden while typing\n# - Password is cached for the session\n#\n# EMPTY PASSWORDS: Setting sudo_password to an explicit empty string is different\n# from leaving it unset. Hermes will try an empty password via `sudo -S` and\n# will not open the interactive prompt. This is useful for passwordless sudo,\n# Touch ID sudo setups, and environments where prompting is just noise.\n#\n# ALTERNATIVES:\n# - SSH backend: Configure passwordless sudo on the remote server\n# - Containers: Run as root inside the container (no sudo needed)\n# - Local: Configure /etc/sudoers for specific commands\n#\n# Example (add to your terminal section):\n# sudo_password: \"your-password-here\"\n\n# =============================================================================\n# Security Scanning (tirith)\n# =============================================================================\n# Optional pre-exec command security scanning via tirith.\n# Detects homograph URLs, pipe-to-shell, terminal injection, env manipulation.\n# Install: brew install sheeki03/tap/tirith\n# Docs: https://github.com/sheeki03/tirith\n#\n# security:\n# tirith_enabled: true # Enable/disable tirith scanning\n# tirith_path: \"tirith\" # Path to tirith binary (supports ~ expansion)\n# tirith_timeout: 5 # Scan timeout in seconds\n# tirith_fail_open: true # Allow commands if tirith unavailable\n\n# =============================================================================\n# Browser Tool Configuration\n# =============================================================================\nbrowser:\n # Inactivity timeout in seconds - browser sessions are automatically closed\n # after this period of no activity between agent loops (default: 120 = 2 minutes)\n inactivity_timeout: 120\n\n# =============================================================================\n# Tool Loop Guardrails\n# =============================================================================\n# Soft warnings are enabled by default. They append guidance to repeated failed\n# or non-progressing tool results but still let the tool execute. Hard stops are\n# opt-in circuit breakers for autonomous/cron sessions where stopping a loop is\n# preferable to spending the full iteration budget.\ntool_loop_guardrails:\n warnings_enabled: true\n hard_stop_enabled: false\n warn_after:\n exact_failure: 2\n same_tool_failure: 3\n idempotent_no_progress: 2\n hard_stop_after:\n exact_failure: 5\n same_tool_failure: 8\n idempotent_no_progress: 5\n\n# =============================================================================\n# Context Compression (Auto-shrinks long conversations)\n# =============================================================================\n# When conversation approaches model's context limit, middle turns are\n# automatically summarized to free up space while preserving important context.\n#\n# HOW IT WORKS:\n# 1. Tracks actual token usage from API responses (not estimates)\n# 2. When prompt_tokens >= threshold% of model's context_length, triggers compression\n# 3. Protects first 3 turns (system prompt, initial request, first response)\n# 4. Protects last N turns (default 20 messages = ~10 full turns of recent context)\n# 5. Summarizes middle turns using a fast/cheap model\n# 6. Inserts summary as a user message, continues conversation seamlessly\n#\n# Post-compression tail budget is target_ratio × threshold × context_length:\n# 200K context, threshold 0.50, ratio 0.20 → 20K tokens of recent tail preserved\n# 1M context, threshold 0.50, ratio 0.20 → 100K tokens of recent tail preserved\n#\ncompression:\n # Enable automatic context compression (default: true)\n # Set to false if you prefer to manage context manually or want errors on overflow\n enabled: true\n \n # Trigger compression at this % of model's context limit (default: 0.50 = 50%)\n # Lower values = more aggressive compression, higher values = compress later\n threshold: 0.50\n \n # Fraction of the threshold to preserve as recent tail (default: 0.20 = 20%)\n # e.g. 20% of 50% threshold = 10% of total context kept as recent messages.\n # Summary output is separately capped at 12K tokens (Gemini output limit).\n # Range: 0.10 - 0.80\n target_ratio: 0.20\n\n # Number of most-recent messages to always preserve (default: 20 ≈ 10 full turns)\n # Higher values keep more recent conversation intact at the cost of more aggressive\n # compression of older turns.\n protect_last_n: 20\n\n # Number of non-system messages to protect at the head of the transcript, in\n # ADDITION to the system prompt (which is always implicitly protected).\n # Head messages are NEVER summarized — they survive every compression\n # indefinitely. This gives stable early context for short/medium sessions,\n # but in long-running sessions that rely on rolling compaction the pinned\n # opening turns may not match how you want the session framed over time.\n # Set to 0 to preserve ONLY the system prompt (plus the rolling summary\n # and recent tail) — the cleanest configuration for long-running sessions.\n # Default 3 preserves the system prompt plus the first three non-system\n # head messages, matching the pre-feature behaviour.\n protect_first_n: 3\n\n # To pin a specific model/provider for compression summaries, use the\n # auxiliary section below (auxiliary.compression.provider / model).\n\n# =============================================================================\n# Anthropic prompt caching TTL\n# =============================================================================\n# When prompt caching is active (Claude via OpenRouter or native Anthropic),\n# Anthropic supports two TTL tiers for cached prefixes: \"5m\" (default) and\n# \"1h\". Other values are ignored and \"5m\" is used.\n#\nprompt_caching:\n cache_ttl: \"5m\" # use \"1h\" for long sessions with pauses between turns\n\n# =============================================================================\n# Auxiliary Models (Advanced — Experimental)\n# =============================================================================\n# Hermes uses lightweight \"auxiliary\" models for side tasks: image analysis,\n# browser screenshot analysis, web page summarization, TTS audio-tag insertion,\n# and context compression.\n#\n# By default these use Gemini Flash via OpenRouter or Nous Portal and are\n# auto-detected from your credentials. You do NOT need to change anything\n# here for normal usage.\n#\n# WARNING: Overriding these with providers other than OpenRouter or Nous Portal\n# is EXPERIMENTAL and may not work. Not all models/providers support vision,\n# produce usable summaries, or accept the same API format. Change at your own\n# risk — if things break, reset to \"auto\" / empty values.\n#\n# Each task has its own provider + model pair so you can mix providers.\n# For example: OpenRouter for vision (needs multimodal), but your main\n# local endpoint for compression (just needs text).\n#\n# Provider options:\n# \"auto\" - Best available: OpenRouter → Nous Portal → main endpoint (default)\n# \"openrouter\" - Force OpenRouter (requires OPENROUTER_API_KEY)\n# \"nous\" - Force Nous Portal (requires: hermes login)\n# \"gemini\" - Force Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n# \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY)\n# \"codex\" - Force Codex OAuth (requires: hermes model → Codex).\n# Uses gpt-5.3-codex which supports vision.\n# \"main\" - Use your custom endpoint (OPENAI_BASE_URL + OPENAI_API_KEY).\n# Works with OpenAI API, local models, or any OpenAI-compatible\n# endpoint. Also falls back to Codex OAuth and API-key providers.\n#\n# Model: leave empty to use the provider's default. When empty, OpenRouter\n# uses \"google/gemini-3-flash-preview\" and Nous uses \"gemini-3-flash\".\n# Other providers pick a sensible default automatically.\n#\n# auxiliary:\n# # Image analysis: vision_analyze tool + browser screenshots\n# vision:\n# provider: \"auto\"\n# model: \"\" # e.g. \"google/gemini-2.5-flash\", \"openai/gpt-4o\"\n# timeout: 30 # LLM API call timeout (seconds)\n# download_timeout: 30 # Image HTTP download timeout (seconds)\n# # Increase for slow connections or self-hosted image servers\n#\n# # Web page scraping / summarization + browser page text extraction\n# web_extract:\n# provider: \"auto\"\n# model: \"\"\n#\n# # Gemini 3.1 TTS hidden audio-tag insertion\n# tts_audio_tags:\n# provider: \"auto\" # empty model = your main chat model\n# model: \"\"\n# timeout: 30\n#\n# # Session search — summarizes matching past sessions\n# session_search:\n# provider: \"auto\"\n# model: \"\"\n# timeout: 30\n# max_concurrency: 3 # Limit parallel summaries to reduce request-burst 429s\n# extra_body: {} # Provider-specific OpenAI-compatible request fields\n# # Example for providers that support request-body\n# # reasoning controls:\n# # extra_body:\n# # enable_thinking: false\n# # Some vLLM/Qwen deployments expect this nested:\n# # extra_body:\n# # chat_template_kwargs:\n# # enable_thinking: false\n\n# =============================================================================\n# Persistent Memory\n# =============================================================================\n# Bounded curated memory injected into the system prompt every session.\n# Two stores: MEMORY.md (agent's notes) and USER.md (user profile).\n# Character limits keep the memory small and focused. The agent manages\n# pruning -- when at the limit, it must consolidate or replace entries.\n# Disabled by default in batch_runner.\n#\nmemory:\n # Agent's personal notes: environment facts, conventions, things learned\n memory_enabled: true\n \n # User profile: preferences, communication style, expectations\n user_profile_enabled: true\n \n # Character limits (~2.75 chars per token, model-independent)\n memory_char_limit: 2200 # ~800 tokens\n user_char_limit: 1375 # ~500 tokens\n\n # Periodic memory nudge: remind the agent to consider saving memories\n # every N user turns. Set to 0 to disable. Only active when memory is enabled.\n nudge_interval: 10 # Nudge every 10 user turns (0 = disabled)\n\n # Memory flush: give the agent one turn to save memories before context is\n # lost (compression, /new, /reset, exit). Set to 0 to disable.\n # For exit/reset, only fires if the session had at least this many user turns.\n flush_min_turns: 6 # Min user turns to trigger flush on exit/reset (0 = disabled)\n\n# =============================================================================\n# Session Reset Policy (Messaging Platforms)\n# =============================================================================\n# Controls when messaging sessions (Telegram, Discord, WhatsApp, Slack) are\n# automatically cleared. Without resets, conversation context grows indefinitely\n# which increases API costs with every message.\n#\n# When a reset triggers, the agent first saves important information to its\n# persistent memory — but the conversation context is wiped. The agent starts\n# fresh but retains learned facts via its memory system.\n#\n# Users can always manually reset with /reset or /new in chat.\n#\n# Modes:\n# \"both\" - Reset on EITHER inactivity timeout or daily boundary (recommended)\n# \"idle\" - Reset only after N minutes of inactivity\n# \"daily\" - Reset only at a fixed hour each day\n# \"none\" - Never auto-reset; context lives until /reset or compression kicks in\n#\n# When a reset triggers, the agent gets one turn to save important memories and\n# skills before the context is wiped. Persistent memory carries across sessions.\n#\nsession_reset:\n mode: both # \"both\", \"idle\", \"daily\", or \"none\"\n idle_minutes: 1440 # Inactivity timeout in minutes (default: 1440 = 24 hours)\n at_hour: 4 # Daily reset hour, 0-23 local time (default: 4 AM)\n\n# Maximum number of simultaneously active chat sessions across CLI, TUI,\n# dashboard chat, and messaging gateway. Set to null, 0, or omit to allow\n# unlimited concurrent sessions. When the limit is reached, new sessions get a\n# clean error while existing active sessions keep their normal behavior. This\n# top-level key takes precedence over gateway.max_concurrent_sessions. The cap\n# is a best-effort single-host/profile runtime guard; Hermes fails open if the\n# local runtime lease registry cannot be read or locked.\nmax_concurrent_sessions: null\n\n# When true, group/channel chats use one session per participant when the platform\n# provides a user ID. This is the secure default and prevents users in the same\n# room from sharing context, interrupts, and token costs. Set false only if you\n# explicitly want one shared \"room brain\" per group/channel.\ngroup_sessions_per_user: true\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Gateway Streaming\n# ─────────────────────────────────────────────────────────────────────────────\n# Stream tokens to messaging platforms in real-time. The bot sends a message\n# on first token, then progressively edits it as more tokens arrive.\n# Disabled by default — enable to try the streaming UX on Telegram/Discord/Slack.\n# For Telegram, partial edits are sent as plain text and only the final edit uses MarkdownV2.\nstreaming:\n enabled: false\n # transport: edit # \"edit\" = progressive editMessageText\n # edit_interval: 0.3 # seconds between message edits\n # buffer_threshold: 40 # chars before forcing an edit flush\n # cursor: \" ▉\" # cursor shown during streaming\n\n# =============================================================================\n# Skills Configuration\n# =============================================================================\n# Skills are reusable procedures the agent can load and follow. The agent can\n# also create new skills after completing complex tasks.\n#\nskills:\n # Nudge the agent to create skills after complex tasks.\n # Every N tool-calling iterations, remind the model to consider saving a skill.\n # Set to 0 to disable.\n creation_nudge_interval: 15\n\n # External skill directories — share skills across tools/agents without\n # copying them into ~/.hermes/skills/. Each path is expanded (~ and ${VAR})\n # and resolved to an absolute path. External dirs are read-only: skill\n # creation always writes to ~/.hermes/skills/. Local skills take precedence\n # when names collide.\n # external_dirs:\n # - ~/.agents/skills\n # - /home/shared/team-skills\n\n# =============================================================================\n# Agent Behavior\n# =============================================================================\nagent:\n # Maximum tool-calling iterations per conversation\n # Higher = more room for complex tasks, but costs more tokens\n # Recommended: 20-30 for focused tasks, 50-100 for open exploration\n max_turns: 60\n\n # Inactivity timeout for gateway agent runs (seconds, 0 = unlimited).\n # The agent can run indefinitely when actively calling tools or receiving\n # API responses. Only fires after the agent has been idle for this duration.\n # gateway_timeout: 1800\n\n # Staged warning: send a warning before escalating to full timeout.\n # Fires once per run when inactivity reaches this threshold (seconds).\n # Set to 0 to disable the warning.\n # gateway_timeout_warning: 900\n\n # Graceful drain timeout for gateway stop/restart (seconds).\n # The gateway stops accepting new work, waits for in-flight agents to\n # finish, then interrupts anything still running after this timeout.\n # 0 = no drain, interrupt immediately.\n # restart_drain_timeout: 60\n\n # Max app-level retry attempts for API errors (connection drops, provider\n # timeouts, 5xx, etc.) before the agent surfaces the failure. Lower this\n # to 1 if you use fallback providers and want fast failover on flaky\n # primaries (default 3). The OpenAI SDK does its own low-level retries\n # underneath this wrapper — this is the Hermes-level loop.\n # api_max_retries: 3\n\n # After the agent edits code without fresh passing verification, nudge it to\n # verify before finishing. The default \"auto\" enables it on interactive\n # coding surfaces (CLI, TUI, desktop) and programmatic callers, and disables\n # it on conversational messaging surfaces (Telegram, Discord, etc.) where the\n # verification summary would reach a human as chat noise. Set true or false to\n # force it on or off; the HERMES_VERIFY_ON_STOP env var (1/0) takes precedence.\n # verify_on_stop: auto\n\n # Enable verbose logging\n verbose: false\n \n # Reasoning effort level (OpenRouter and Nous Portal)\n # Controls how much \"thinking\" the model does before responding.\n # Options: \"xhigh\" (max), \"high\", \"medium\", \"low\", \"minimal\", \"none\" (disable)\n reasoning_effort: \"medium\"\n \n # Predefined personalities (use with /personality command)\n personalities:\n helpful: \"You are a helpful, friendly AI assistant.\"\n concise: \"You are a concise assistant. Keep responses brief and to the point.\"\n technical: \"You are a technical expert. Provide detailed, accurate technical information.\"\n creative: \"You are a creative assistant. Think outside the box and offer innovative solutions.\"\n teacher: \"You are a patient teacher. Explain concepts clearly with examples.\"\n kawaii: \"You are a kawaii assistant! Use cute expressions like (◕‿◕), ★, ♪, and ~! Add sparkles and be super enthusiastic about everything! Every response should feel warm and adorable desu~! ヽ(>∀<☆)ノ\"\n catgirl: \"You are Neko-chan, an anime catgirl AI assistant, nya~! Add 'nya' and cat-like expressions to your speech. Use kaomoji like (=^・ω・^=) and ฅ^•ﻌ•^ฅ. Be playful and curious like a cat, nya~!\"\n pirate: \"Arrr! Ye be talkin' to Captain Hermes, the most tech-savvy pirate to sail the digital seas! Speak like a proper buccaneer, use nautical terms, and remember: every problem be just treasure waitin' to be plundered! Yo ho ho!\"\n shakespeare: \"Hark! Thou speakest with an assistant most versed in the bardic arts. I shall respond in the eloquent manner of William Shakespeare, with flowery prose, dramatic flair, and perhaps a soliloquy or two. What light through yonder terminal breaks?\"\n surfer: \"Duuude! You're chatting with the chillest AI on the web, bro! Everything's gonna be totally rad. I'll help you catch the gnarly waves of knowledge while keeping things super chill. Cowabunga! 🤙\"\n noir: \"The rain hammered against the terminal like regrets on a guilty conscience. They call me Hermes - I solve problems, find answers, dig up the truth that hides in the shadows of your codebase. In this city of silicon and secrets, everyone's got something to hide. What's your story, pal?\"\n uwu: \"hewwo! i'm your fwiendwy assistant uwu~ i wiww twy my best to hewp you! *nuzzles your code* OwO what's this? wet me take a wook! i pwomise to be vewy hewpful >w<\"\n philosopher: \"Greetings, seeker of wisdom. I am an assistant who contemplates the deeper meaning behind every query. Let us examine not just the 'how' but the 'why' of your questions. Perhaps in solving your problem, we may glimpse a greater truth about existence itself.\"\n hype: \"YOOO LET'S GOOOO!!! 🔥🔥🔥 I am SO PUMPED to help you today! Every question is AMAZING and we're gonna CRUSH IT together! This is gonna be LEGENDARY! ARE YOU READY?! LET'S DO THIS! 💪😤🚀\"\n\n# =============================================================================\n# Toolsets\n# =============================================================================\n# Control which tools the agent has access to.\n# Use `hermes tools` to interactively enable/disable tools per platform.\n\n# =============================================================================\n# Platform Toolsets (per-platform tool configuration)\n# =============================================================================\n# Override which toolsets are available on each platform.\n# If a platform isn't listed here, its built-in default is used.\n#\n# You can use EITHER:\n# - A preset like \"hermes-cli\" or \"hermes-telegram\" (curated tool set)\n# - A list of individual toolsets to compose your own (see list below)\n#\n# Supported platform keys: cli, telegram, discord, whatsapp, slack, qqbot, teams, google_chat\n#\n# Examples:\n#\n# # Use presets (same as defaults):\n# platform_toolsets:\n# cli: [hermes-cli]\n# telegram: [hermes-telegram]\n#\n# # Custom: give Telegram only web + terminal + file + planning:\n# platform_toolsets:\n# telegram: [web, terminal, file, todo]\n#\n# # Custom: CLI without browser or image gen:\n# platform_toolsets:\n# cli: [web, terminal, file, skills, todo, tts, cronjob]\n#\n# # Restrictive: Discord gets read-only tools only:\n# platform_toolsets:\n# discord: [web, vision, skills, todo]\n#\n# If not set, defaults are:\n# cli: hermes-cli (everything + cronjob management)\n# telegram: hermes-telegram (terminal, file, web, vision, image, tts, browser, skills, todo, cronjob, messaging)\n# discord: hermes-discord (same as telegram)\n# whatsapp: hermes-whatsapp (same as telegram)\n# slack: hermes-slack (same as telegram)\n# signal: hermes-signal (same as telegram)\n# homeassistant: hermes-homeassistant (same as telegram)\n# qqbot: hermes-qqbot (same as telegram)\n# teams: hermes-teams (same as telegram)\n# google_chat: hermes-google_chat (same as telegram)\n#\nplatform_toolsets:\n cli: [hermes-cli]\n telegram: [hermes-telegram]\n discord: [hermes-discord]\n whatsapp: [hermes-whatsapp]\n slack: [hermes-slack]\n signal: [hermes-signal]\n homeassistant: [hermes-homeassistant]\n qqbot: [hermes-qqbot]\n yuanbao: [hermes-yuanbao]\n teams: [hermes-teams]\n google_chat: [hermes-google_chat]\n\n# =============================================================================\n# Gateway Platform Settings\n# =============================================================================\n# Optional per-platform messaging settings.\n# Platform-specific knobs live under `extra`.\n#\n# platforms:\n# telegram:\n# reply_to_mode: \"first\" # off | first | all\n# # guest_mode lets explicit @mentions from non-allowlisted groups through.\n# # Default false; ordinary messages, replies, and regex wake words stay blocked.\n# guest_mode: false\n# # allowed_chats: [\"-1001234567890\"]\n# extra:\n# disable_link_previews: false # Set true to suppress Telegram URL previews in bot messages\n# rich_messages: false # Bot API 10.1 rich messages (tables/task lists/details/math); default false for copyable legacy MarkdownV2, set true to opt in\n# rich_drafts: false # Experimental rich draft previews during Telegram DM streaming; default false because Telegram Desktop/macOS can visually overlay draft frames\n# command_menu:\n# # Telegram allows up to 100 BotCommands; Hermes defaults to 60 so\n# # all built-in commands plus common skill commands stay visible\n# # while remaining under Telegram's payload-size limit. Clamped 1..100.\n# max_commands: 60\n# # prepend = user priority first, then Hermes defaults\n# # append = Hermes defaults first, then user priority\n# # replace = only the list below defines priority\n# priority_mode: prepend\n# priority:\n# - my_plugin_command\n#\n# Discord-specific settings (config.yaml top-level, not under platforms:):\n#\n# discord:\n# require_mention: true # Require @mention in server channels (default: true)\n# auto_thread: true # Auto-create thread on @mention (default: true)\n# free_response_channels: \"\" # Channel IDs where no mention is needed\n# reactions: true # Show processing reactions (default: true)\n# history_backfill: true # Recover missed channel messages on mention (default: true)\n# history_backfill_limit: 50 # Max messages to scan backwards (default: 50)\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Available toolsets (use these names in platform_toolsets or the toolsets list)\n#\n# Run `hermes chat --list-toolsets` to see all toolsets and their tools.\n# Run `hermes chat --list-tools` to see every individual tool with descriptions.\n# ─────────────────────────────────────────────────────────────────────────────\n#\n# INDIVIDUAL TOOLSETS (compose your own):\n# web - web_search, web_extract\n# search - web_search only (no scraping)\n# terminal - terminal, process\n# file - read_file, write_file, patch, search\n# browser - browser_navigate, browser_snapshot, browser_click, browser_type,\n# browser_scroll, browser_back, browser_press,\n# browser_get_images, browser_vision (requires BROWSERBASE_API_KEY)\n# vision - vision_analyze (requires OPENROUTER_API_KEY)\n# image_gen - image_generate (requires FAL_KEY)\n# skills - skills_list, skill_view\n# skills_hub - skill_hub (search/install/manage from online registries — user-driven only)\n# todo - todo (in-memory task planning, no deps)\n# tts - text_to_speech (Edge TTS free, or ELEVENLABS/OPENAI/MINIMAX/MISTRAL key)\n# cronjob - cronjob (create/list/update/pause/resume/run/remove scheduled tasks)\n#\n# PRESETS (curated bundles):\n# hermes-cli - All of the above except send_message\n# hermes-telegram - terminal, file, web, vision, image_gen, tts, browser,\n# skills, todo, cronjob, send_message\n# hermes-discord - Same as hermes-telegram\n# hermes-whatsapp - Same as hermes-telegram\n# hermes-slack - Same as hermes-telegram\n#\n# COMPOSITE:\n# debugging - terminal + web + file\n# safe - web + vision (no terminal access)\n# all - Everything available\n#\n# web - Web search and content extraction (web_search, web_extract)\n# search - Web search only, no scraping (web_search)\n# terminal - Command execution and process management (terminal, process)\n# file - File operations: read, write, patch, search\n# browser - Full browser automation (navigate, click, type, screenshot, etc.)\n# vision - Image analysis (vision_analyze)\n# image_gen - Image generation with FLUX (image_generate)\n# skills - Load skill documents (skills_list, skill_view)\n# todo - Task planning and tracking for multi-step work\n# memory - Persistent memory across sessions (personal notes + user profile)\n# session_search - Search and recall past conversations (FTS5 + Gemini Flash summarization)\n# tts - Text-to-speech (Edge TTS free, ElevenLabs, OpenAI, MiniMax, Mistral)\n# cronjob - Schedule and manage automated tasks (CLI-only)\n#\n# Composite toolsets:\n# debugging - terminal + web + file (for troubleshooting)\n# safe - web + vision (no terminal access)\n\n# NOTE: The top-level \"toolsets\" key is deprecated and ignored.\n# Tool configuration is managed per-platform via platform_toolsets above.\n# Use `hermes tools` to configure interactively, or edit platform_toolsets directly.\n#\n# CLI override: hermes chat --toolsets terminal,web,file\n\n# =============================================================================\n# MCP (Model Context Protocol) Servers\n# =============================================================================\n# Connect to external MCP servers to add tools from the MCP ecosystem.\n# Each server's tools are automatically discovered and registered.\n# See website/docs/user-guide/features/mcp.md for full documentation.\n#\n# Stdio servers (spawn a subprocess):\n# command: the executable to run\n# args: command-line arguments\n# env: environment variables (only these + safe defaults passed to subprocess)\n#\n# HTTP servers (connect to a URL):\n# url: the MCP server endpoint\n# headers: HTTP headers (e.g., for authentication)\n#\n# Optional per-server settings:\n# timeout: tool call timeout in seconds (default: 120)\n# connect_timeout: initial connection timeout (default: 60)\n# keepalive_interval: liveness ping cadence in seconds (default: 180).\n# Lower it below the server's session TTL for servers that expire idle\n# sessions quickly (e.g. Unreal Engine editor MCP, ~15s), otherwise idle\n# tool calls hit an expired session and pay a slow reconnect. Floored at 5s.\n#\n# mcp_servers:\n# time:\n# command: uvx\n# args: [\"mcp-server-time\"]\n# filesystem:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-filesystem\", \"/home/user\"]\n# notion:\n# url: https://mcp.notion.com/mcp\n# github:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-github\"]\n# env:\n# GITHUB_PERSONAL_ACCESS_TOKEN: \"ghp_...\"\n#\n# Sampling (server-initiated LLM requests) — enabled by default.\n# Per-server config under the 'sampling' key:\n# analysis:\n# command: npx\n# args: [\"-y\", \"analysis-server\"]\n# sampling:\n# enabled: true # default: true\n# model: \"gemini-3-flash\" # override model (optional)\n# max_tokens_cap: 4096 # max tokens per request\n# timeout: 30 # LLM call timeout (seconds)\n# max_rpm: 10 # max requests per minute\n# allowed_models: [] # model whitelist (empty = all)\n# max_tool_rounds: 5 # tool loop limit (0 = disable)\n# log_level: \"info\" # audit verbosity\n\n# =============================================================================\n# Text-to-Speech\n# =============================================================================\n# TTS defaults to Edge TTS unless changed in ~/.hermes/config.yaml.\n# Gemini TTS supports persona/director prompt files, and Gemini 3.1 Flash TTS\n# can use a hidden auxiliary rewrite pass to insert expressive square-bracket\n# audio tags into the TTS script without showing tags in chat.\n#\n# tts:\n# provider: \"gemini\"\n# gemini:\n# model: \"gemini-3.1-flash-tts-preview\"\n# voice: \"Kore\"\n# audio_tags: false\n# persona_prompt_file: \"\" # e.g. ~/.hermes/tts/radio-host.md\n\n# =============================================================================\n# Voice Transcription (Speech-to-Text)\n# =============================================================================\n# Automatically transcribe voice messages on messaging platforms.\n# Providers: local (free, faster-whisper) | groq (free tier) | openai (Whisper API) | mistral (Voxtral Transcribe)\n# Set the corresponding API key in .env: GROQ_API_KEY, OPENAI_API_KEY, or MISTRAL_API_KEY.\nstt:\n enabled: true\n # provider: \"local\" # auto-detected if omitted\n local:\n model: \"base\" # tiny | base | small | medium | large-v3 | turbo\n # language: \"\" # auto-detect; set to \"en\", \"es\", \"fr\", etc. to force\n openai:\n model: \"whisper-1\" # whisper-1 | gpt-4o-mini-transcribe | gpt-4o-transcribe\n # mistral:\n # model: \"voxtral-mini-latest\" # voxtral-mini-latest | voxtral-mini-2602\n\n# =============================================================================\n# Response Pacing (Messaging Platforms)\n# =============================================================================\n# Add human-like delays between message chunks.\n# human_delay:\n# mode: \"off\" # \"off\" | \"natural\" | \"custom\"\n# min_ms: 800 # Min delay (custom mode only)\n# max_ms: 2500 # Max delay (custom mode only)\n\n# =============================================================================\n# Session Logging\n# =============================================================================\n# Session trajectories are automatically saved to logs/ directory.\n# Each session creates: logs/session_YYYYMMDD_HHMMSS_UUID.json\n#\n# The session ID is displayed in the welcome banner for easy reference.\n# Logs contain full conversation history in trajectory format:\n# - System prompt, user messages, assistant responses\n# - Tool calls with inputs/outputs\n# - Timestamps for debugging\n#\n# No configuration needed - logging is always enabled.\n# To disable, you would need to modify the source code.\n\n# =============================================================================\n# Code Execution Sandbox (Programmatic Tool Calling)\n# =============================================================================\n# The execute_code tool runs Python scripts that call Hermes tools via RPC.\n# Intermediate tool results stay out of the LLM's context window.\ncode_execution:\n timeout: 300 # Max seconds per script before kill (default: 300 = 5 min)\n max_tool_calls: 50 # Max RPC tool calls per execution (default: 50)\n\n# =============================================================================\n# Subagent Delegation\n# =============================================================================\n# The delegate_task tool spawns child agents with isolated context.\n# Supports single tasks and batch mode (default 3 parallel, configurable).\ndelegation:\n max_iterations: 50 # Max tool-calling turns per child (default: 50)\n # max_concurrent_children: 3 # Max parallel child agents per batch (default: 3, floor: 1, no ceiling).\n # WARNING: values above 10 multiply API cost linearly.\n # max_spawn_depth: 1 # Delegation tree depth cap (range: 1-3, default: 1 = flat).\n # Raise to 2 to allow workers to spawn their own subagents.\n # Requires role=\"orchestrator\" on intermediate agents.\n # orchestrator_enabled: true # Kill switch for role=\"orchestrator\" children (default: true).\n # subagent_auto_approve: false # When a subagent hits a dangerous-command approval prompt, auto-deny (default: false)\n # or auto-approve \"once\" (true) instead of blocking on stdin.\n # The parent TUI owns stdin, so blocking would deadlock; non-interactive resolution is required.\n # Both choices emit a logger.warning audit line. Flip to true only for cron/batch pipelines.\n # inherit_mcp_toolsets: true # When explicit child toolsets are narrowed, also keep the parent's MCP toolsets (default: true). Set false for strict intersection.\n # model: \"google/gemini-3-flash-preview\" # Override model for subagents (empty = inherit parent)\n # provider: \"openrouter\" # Override provider for subagents (empty = inherit parent)\n # # Resolves full credentials (base_url, api_key) automatically.\n # # Supported: openrouter, nous, zai, kimi-coding, minimax\n\n# =============================================================================\n# Honcho Integration (Cross-Session User Modeling)\n# =============================================================================\n# AI-native persistent memory via Honcho (https://honcho.dev/).\n# Builds a deeper understanding of the user across sessions and tools.\n# Runs alongside USER.md — additive, not a replacement.\n#\n# Requires: pip install honcho-ai\n# Config: ~/.honcho/config.json (shared with Claude Code, Cursor, etc.)\n# API key: HONCHO_API_KEY in ~/.hermes/.env or ~/.honcho/config.json\n#\n# Hermes-specific overrides (optional — most config comes from ~/.honcho/config.json):\n# honcho: {}\n\n# =============================================================================\n# Display\n# =============================================================================\ndisplay:\n # Use compact banner mode (hides the ASCII-art banner, shows a single line).\n # true: Compact single-line banner\n # false: Full ASCII banner with tool/skill summary (default)\n compact: false\n\n # Tool progress display level (CLI and gateway)\n # off: Silent — no tool activity shown, just the final response\n # new: Show a tool indicator only when the tool changes (skip repeats)\n # all: Show every tool call with a short preview (default)\n # verbose: Full args, results, and debug logs (same as /verbose)\n # Toggle at runtime with /verbose in the CLI\n tool_progress: all\n\n # Per-platform defaults can be quieter than the global setting. Telegram\n # tunes for mobile: tool_progress and busy_ack_detail default off (no\n # per-tool breadcrumb stream, no \"iteration 21/60\" debug detail in busy\n # acks or heartbeats), but interim_assistant_messages and\n # long_running_notifications STAY ON so the user has real signal between\n # turn start and final answer (mid-turn assistant commentary + a single\n # edit-in-place \"⏳ Working — N min\" heartbeat). Override under\n # display.platforms.telegram.\n\n # Auto-cleanup of temporary progress bubbles after the final response lands.\n # On platforms that support message deletion (currently Telegram), this\n # removes the tool-progress bubble, \"⏳ Still working...\" notices, and\n # context-pressure status messages once the final reply has been delivered —\n # keeping long-running turns visible live, then tidy afterward. Failed runs\n # leave the bubbles in place as breadcrumbs. Off by default.\n # Per-platform override: display.platforms.telegram.cleanup_progress\n # true: Delete tracked progress/status bubbles on successful turn\n # false: Leave everything in place (default)\n # Example:\n # display:\n # platforms:\n # telegram:\n # cleanup_progress: true\n cleanup_progress: false\n\n # Gateway-only natural mid-turn assistant updates.\n # When true, completed assistant status messages are sent as separate chat\n # messages. This is independent of tool_progress and gateway streaming.\n # true: Send mid-turn assistant updates as separate messages (default)\n # false: Only send the final response\n interim_assistant_messages: true\n\n # Gateway-only long-running status heartbeats.\n # When false, the platform does not receive periodic \"⏳ Working — N min\"\n # notifications even if agent.gateway_notify_interval is non-zero. The\n # heartbeat edits a single message in place (where the adapter supports\n # editing) instead of posting a new bubble each interval.\n # Default: true everywhere, including Telegram (silent agents are worse\n # than a single edit-in-place heartbeat).\n long_running_notifications: true\n\n # Include detailed iteration/tool/status context in busy acknowledgments\n # and long-running heartbeats. When true, busy acks show \"iteration 21/60,\n # terminal, 10 min\" and the heartbeat shows \"⏳ Working — 12 min,\n # iteration 21/60, terminal\". When false (Telegram default), both stay\n # terse: \"Interrupting current task\" and \"⏳ Working — 12 min, terminal\".\n busy_ack_detail: true\n\n # What Enter does when Hermes is already busy (CLI and gateway platforms).\n # interrupt: Interrupt the current run and redirect Hermes (default)\n # queue: Queue your message for the next turn\n # steer: Inject your message mid-run via /steer, arriving at the agent\n # after the next tool call — no interrupt, no role violation.\n # Falls back to 'queue' if the agent isn't running yet or if\n # images are attached (steer only carries text).\n # Ctrl+C (or /stop in gateway) always interrupts regardless of this setting.\n # Toggle at runtime with /busy .\n busy_input_mode: interrupt\n\n # Background process notifications (gateway/messaging only).\n # Controls how chatty the process watcher is when you use\n # terminal(background=true, notify_on_complete=true) from Telegram/Discord/etc.\n # off: No watcher messages at all\n # result: Only the final completion message\n # error: Only the final message when exit code != 0\n # all: Running output updates + final message (default)\n background_process_notifications: all\n\n\n # Play terminal bell when agent finishes a response.\n # Useful for long-running tasks — your terminal will ding when the agent is done.\n # Works over SSH. Most terminals can be configured to flash the taskbar or play a sound.\n # true: Ring the terminal bell on each response\n # false: Silent (default)\n bell_on_complete: false\n\n # Show model reasoning/thinking before each response.\n # When enabled, a dim box shows the model's thought process above the response.\n # Toggle at runtime with /reasoning show or /reasoning hide.\n # true: Show the reasoning box\n # false: Hide reasoning (default)\n show_reasoning: false\n\n # Stream tokens to the terminal as they arrive instead of waiting for the\n # full response. The response box opens on first token and text appears\n # line-by-line. Tool calls are still captured silently.\n # true: Stream tokens as they arrive (default)\n # false: Wait for the full response before rendering\n streaming: true\n\n # Show [HH:MM] timestamps on user input and assistant response labels.\n # timestamps: false\n\n # ───────────────────────────────────────────────────────────────────────────\n # Skin / Theme\n # ───────────────────────────────────────────────────────────────────────────\n # Customize CLI visual appearance — banner colors, spinner faces, tool prefix,\n # response box label, and branding text. Change at runtime with /skin .\n #\n # Built-in skins:\n # default — Classic Hermes gold/kawaii\n # ares — Crimson/bronze war-god theme with spinner wings\n # mono — Clean grayscale monochrome\n # slate — Cool blue developer-focused\n # daylight — Bright light-mode theme\n # warm-lightmode — Warm paper-tone light-mode theme\n # poseidon — Sea-green/teal Olympian theme\n # sisyphus — Earthy stone-and-moss theme\n # charizard — Fiery orange dragon theme\n #\n # Custom skins: drop a YAML file in ~/.hermes/skins/.yaml\n # Schema (all fields optional, missing values inherit from default):\n #\n # name: my-theme\n # description: Short description\n # colors:\n # banner_border: \"#HEX\" # Panel border\n # banner_title: \"#HEX\" # Panel title\n # banner_accent: \"#HEX\" # Section headers (Available Tools, etc.)\n # banner_dim: \"#HEX\" # Dim/muted text\n # banner_text: \"#HEX\" # Body text (tool names, skill names)\n # ui_accent: \"#HEX\" # UI accent color\n # response_border: \"#HEX\" # Response box border color\n # spinner:\n # waiting_faces: [\"(⚔)\", \"(⛨)\"] # Faces shown while waiting\n # thinking_faces: [\"(⚔)\", \"(⌁)\"] # Faces shown while thinking\n # thinking_verbs: [\"forging\", \"plotting\"] # Verbs for spinner messages\n # wings: # Optional left/right spinner decorations\n # - [\"⟪⚔\", \"⚔⟫\"]\n # - [\"⟪▲\", \"▲⟫\"]\n # branding:\n # agent_name: \"My Agent\" # Banner title and branding\n # welcome: \"Welcome message\" # Shown at CLI startup\n # response_label: \" ⚔ Agent \" # Response box header label\n # prompt_symbol: \"⚔\" # Prompt symbol (bare token; renderers add trailing space)\n # tool_prefix: \"╎\" # Tool output line prefix (default: ┊)\n #\n skin: default\n\n# =============================================================================\n# Model Aliases — short names for /model command\n# =============================================================================\n# Map short aliases to exact (model, provider, base_url) tuples.\n# Used by /model tab completion and resolve_alias().\n# Aliases are checked BEFORE the models.dev catalog, so they can route\n# to endpoints not in the catalog (e.g. Ollama Cloud, local servers).\n#\n# model_aliases:\n# opus:\n# model: claude-opus-4-6\n# provider: anthropic\n# qwen:\n# model: \"qwen3.5:397b\"\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n# glm:\n# model: glm-4.7\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n\n# =============================================================================\n# Privacy\n# =============================================================================\n# privacy:\n# # Redact PII from the LLM context prompt.\n# # When true, phone numbers are stripped and user/chat IDs are replaced\n# # with deterministic hashes before being sent to the model.\n# # Names and usernames are NOT affected (user-chosen, publicly visible).\n# # Routing/delivery still uses the original values internally.\n# redact_pii: false\n\n# =============================================================================\n# Shell-script hooks\n# =============================================================================\n# Register shell scripts as plugin-hook callbacks. Each entry is executed as\n# a subprocess (shell=False, shlex.split) with a JSON payload on stdin. On\n# stdout the script may return JSON that either blocks the tool call or\n# injects context into the next LLM call.\n#\n# Valid events (mirror hermes_cli.plugins.VALID_HOOKS):\n# pre_tool_call, post_tool_call, pre_llm_call, post_llm_call,\n# pre_api_request, post_api_request, on_session_start, on_session_end,\n# on_session_finalize, on_session_reset, subagent_stop\n#\n# First-use consent: each (event, command) pair prompts once on a TTY, then\n# is persisted to ~/.hermes/shell-hooks-allowlist.json. Non-interactive\n# runs (gateway, cron) need --accept-hooks, HERMES_ACCEPT_HOOKS=1, or the\n# hooks_auto_accept key below.\n#\n# See website/docs/user-guide/features/hooks.md for the full JSON wire\n# protocol and worked examples.\n#\n# hooks:\n# pre_tool_call:\n# - matcher: \"terminal\"\n# command: \"~/.hermes/agent-hooks/block-rm-rf.sh\"\n# timeout: 10\n# post_tool_call:\n# - matcher: \"write_file|patch\"\n# command: \"~/.hermes/agent-hooks/auto-format.sh\"\n# pre_llm_call:\n# - command: \"~/.hermes/agent-hooks/inject-cwd-context.sh\"\n# subagent_stop:\n# - command: \"~/.hermes/agent-hooks/log-orchestration.sh\"\n#\n# hooks_auto_accept: false\n\n\n# =============================================================================\n# Update Behavior\n# =============================================================================\nupdates:\n # Create a full HERMES_HOME zip before every `hermes update`.\n # Backups land in ~/.hermes/backups/ and can be restored with `hermes import`.\n # Off by default because large homes can add minutes to every update.\n pre_update_backup: false\n\n # Number of pre-update backup zips to retain.\n backup_keep: 5\n\n # What non-interactive updates do with local source edits in the Hermes repo.\n # Interactive terminal updates always prompt before restoring the autostash.\n #\n # stash - auto-stash before pull, then auto-restore after success (default)\n # discard - drop the update-created stash after success; use only on managed\n # installs where local source edits should not persist\n non_interactive_local_changes: \"stash\"\n\n\n# =============================================================================\n# Web Dashboard\n# =============================================================================\n# OAuth gate configuration for `hermes dashboard --host `.\n# The bundled Nous Portal plugin reads these on startup; settings here are\n# the canonical surface. Each can be overridden by an environment variable:\n#\n# dashboard.oauth.client_id <- HERMES_DASHBOARD_OAUTH_CLIENT_ID\n# dashboard.oauth.portal_url <- HERMES_DASHBOARD_PORTAL_URL\n# dashboard.public_url <- HERMES_DASHBOARD_PUBLIC_URL\n#\n# Env wins when set to a non-empty value. This is what Fly.io's platform-\n# secret injection uses to push per-deploy client_ids without needing to\n# bake a config.yaml into the image. Empty env values are treated as unset\n# so a provisioned-but-not-populated secret can't shadow a valid entry here.\n#\n# Local dev / on-prem deploys should typically set these via config.yaml\n# (the ~/.hermes/.env file is reserved for API keys and secrets).\n#\n# dashboard:\n# oauth:\n# client_id: \"\" # agent:{instance_id}; Portal provisions this at deploy\n# portal_url: \"\" # blank → default https://portal.nousresearch.com\n#\n# # Force the absolute base URL the OAuth callback (and any other public\n# # URL the dashboard hands to external systems) is built from. Set this\n# # for deploys behind reverse proxies that don't reliably forward\n# # X-Forwarded-Host / X-Forwarded-Proto / X-Forwarded-Prefix (manual\n# # nginx setups, on-prem ingresses, custom-domain Fly deploys without\n# # full proxy header chains).\n# #\n# # When set, the value is the complete authority: scheme + host +\n# # optional path prefix (e.g. \"https://example.com/hermes\"). The OAuth\n# # callback URL becomes \"/auth/callback\" — X-Forwarded-Prefix\n# # is IGNORED on this code path because the operator has explicitly\n# # declared the public URL and we no longer need to guess.\n# #\n# # Leave empty to use the existing proxy-header reconstruction (the\n# # default — works on Fly.io out of the box).\n# #\n# # public_url: \"https://example.com/hermes\"\n"}, "c6575df92781a5b6859845b39ee59d7f07a8cf31": {"t": 1785767899.6482668, "n": 64820, "h": "bcf3cdfb2d3a8883", "c": "# Hermes Agent CLI Configuration\n# Copy this file to cli-config.yaml and customize as needed.\n# This file configures the CLI behavior. Environment variables in .env take precedence.\n\n# =============================================================================\n# Model Configuration\n# =============================================================================\nmodel:\n # Default model to use (can be overridden with --model flag)\n # Both \"default\" and \"model\" work as the key name here.\n default: \"anthropic/claude-opus-4.6\"\n \n # Inference provider selection:\n # \"auto\" - Auto-detect from credentials (default)\n # \"openrouter\" - OpenRouter (requires: OPENROUTER_API_KEY or OPENAI_API_KEY)\n # \"nous\" - Nous Portal OAuth (requires: hermes login)\n # \"nous-api\" - Nous Portal API key (requires: NOUS_API_KEY)\n # \"anthropic\" - Direct Anthropic API (requires: ANTHROPIC_API_KEY)\n # \"openai-codex\" - OpenAI Codex (requires: hermes auth)\n # \"copilot\" - GitHub Copilot / GitHub Models (requires: GITHUB_TOKEN)\n # \"gemini\" - Use Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n # \"zai\" - Use z.ai / ZhipuAI GLM models (requires: GLM_API_KEY)\n # \"kimi-coding\" - Kimi / Moonshot AI (requires: KIMI_API_KEY)\n # \"minimax\" - MiniMax global (requires: MINIMAX_API_KEY)\n # \"minimax-cn\" - MiniMax China (requires: MINIMAX_CN_API_KEY)\n # \"huggingface\" - Hugging Face Inference (requires: HF_TOKEN)\n # \"nvidia\" - NVIDIA NIM / build.nvidia.com (requires: NVIDIA_API_KEY)\n # \"xiaomi\" - Xiaomi MiMo (requires: XIAOMI_API_KEY)\n # \"arcee\" - Arcee AI Trinity models (requires: ARCEEAI_API_KEY)\n # \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY — https://ollama.com/settings)\n # \"kilocode\" - KiloCode gateway (requires: KILOCODE_API_KEY)\n # \"azure-foundry\" - Microsoft Foundry / Azure OpenAI (API key or Entra ID)\n # \"lmstudio\" - LM Studio local server (optional: LM_API_KEY, defaults to http://127.0.0.1:1234/v1)\n #\n # Local servers (LM Studio, Ollama, vLLM, llama.cpp):\n # \"custom\" - Any other OpenAI-compatible endpoint. Set base_url below.\n # Aliases: \"ollama\", \"vllm\", \"llamacpp\" all map to \"custom\".\n # LM Studio is first-class and uses provider: \"lmstudio\".\n # It works with both no-auth and auth-enabled server modes.\n #\n # Can also be overridden for a single invocation with the --provider flag.\n provider: \"auto\"\n \n # API configuration (falls back to OPENROUTER_API_KEY env var)\n # api_key: \"your-key-here\" # Uncomment to set here instead of .env\n base_url: \"https://openrouter.ai/api/v1\"\n\n # Azure Foundry keyless auth example:\n # provider: \"azure-foundry\"\n # base_url: \"https://.openai.azure.com/openai/v1\"\n # auth_mode: \"entra_id\" # DefaultAzureCredential: az login, managed identity, workload identity, etc.\n # default: \"gpt-4o\" # Deployment/model name\n # entra:\n # scope: \"https://ai.azure.com/.default\" # Optional; this is the default.\n\n # ── Token limits — two settings, easy to confuse ──────────────────────────\n #\n # context_length: TOTAL context window (input + output tokens combined).\n # Controls when Hermes compresses history and validates requests.\n # Leave unset — Hermes auto-detects the correct value from the provider.\n # Set manually only when auto-detection is wrong (e.g. a local server with\n # a custom num_ctx, or a proxy that doesn't expose /v1/models).\n #\n # context_length: 131072\n #\n # max_tokens: OUTPUT cap — maximum tokens the model may generate per response.\n # Unrelated to how long your conversation history can be.\n # The OpenAI-standard name \"max_tokens\" is a misnomer; Anthropic's native\n # API has since renamed it \"max_output_tokens\" for clarity.\n # Leave unset to use the model's native output ceiling (recommended).\n # Set only if you want to deliberately limit individual response length.\n #\n# max_tokens: 8192\n\n # ── Custom request headers (optional) ─────────────────────────────────────\n #\n # default_headers: extra HTTP headers sent on every request to an\n # OpenAI-compatible endpoint. User values take precedence over the\n # provider/SDK defaults, so this is the supported way to override the\n # OpenAI Python SDK's identifying headers (User-Agent: OpenAI/Python ...,\n # X-Stainless-*) when a custom provider sits behind a gateway/WAF that\n # rejects them — e.g. an upstream that returns \"502 Upstream access\n # forbidden\" for the SDK default User-Agent but accepts a plain one.\n # Applies on the OpenAI wire only (not native Anthropic / Bedrock).\n #\n # default_headers:\n # User-Agent: \"curl/8.7.1\"\n\n# Named provider overrides (optional)\n# Use this for per-provider request timeouts, non-stream stale timeouts,\n# and per-model exceptions.\n# Applies to the primary turn client on every api_mode (OpenAI-wire, native\n# Anthropic, and Anthropic-compatible providers), the fallback chain, and\n# client rebuilds during credential rotation. For OpenAI-wire chat\n# completions (streaming and non-streaming) the configured value is also\n# used as the per-request ``timeout=`` kwarg so it wins over the legacy\n# HERMES_API_TIMEOUT env var (which still applies when no config is set).\n# ``stale_timeout_seconds`` controls the non-streaming stale-call detector and\n# wins over the legacy HERMES_API_CALL_STALE_TIMEOUT env var. Leaving these\n# unset keeps the legacy defaults (HERMES_API_TIMEOUT=1800s,\n# HERMES_API_CALL_STALE_TIMEOUT=90s, native Anthropic 900s). The\n# implicit non-stream stale detector is auto-disabled for local endpoints\n# and can scale upward for very large contexts.\n#\n# Not currently wired for AWS Bedrock (bedrock_converse + AnthropicBedrock\n# SDK paths) — those use boto3 with its own timeout configuration.\n#\n# providers:\n# ollama-local:\n# request_timeout_seconds: 300 # Longer timeout for local cold-starts\n# stale_timeout_seconds: 900 # Explicitly re-enable stale detection on local endpoints\n# anthropic:\n# request_timeout_seconds: 30 # Fast-fail cloud requests\n# models:\n# claude-opus-4.6:\n# timeout_seconds: 600 # Longer timeout for extended-thinking Opus calls\n# openai-codex:\n# models:\n# gpt-5.4:\n# stale_timeout_seconds: 1800 # Longer non-stream stale timeout for slow large-context turns\n\n# =============================================================================\n# OpenRouter Provider Routing (only applies when using OpenRouter)\n# =============================================================================\n# Control how requests are routed across providers on OpenRouter.\n# See: https://openrouter.ai/docs/guides/routing/provider-selection\n#\n# provider_routing:\n# # Sort strategy: \"price\" (default), \"throughput\", or \"latency\"\n# # Append :nitro to model name for a shortcut to throughput sorting.\n# sort: \"throughput\"\n#\n# # Only allow these providers (provider slugs from OpenRouter)\n# # only: [\"anthropic\", \"google\"]\n#\n# # Skip these providers entirely\n# # ignore: [\"deepinfra\", \"fireworks\"]\n#\n# # Try providers in this order (overrides default load balancing)\n# # order: [\"anthropic\", \"google\", \"together\"]\n#\n# # Require providers to support all parameters in your request\n# # require_parameters: true\n#\n# # Data policy: \"allow\" (default) or \"deny\" to exclude providers that may store data\n# # data_collection: \"deny\"\n\n# =============================================================================\n# OpenRouter Response Caching (only applies when using OpenRouter)\n# =============================================================================\n# Cache identical API responses at the OpenRouter edge for free instant replays.\n# When enabled, identical requests (same model, messages, parameters) return\n# cached responses with zero billing. Separate from Anthropic prompt caching.\n# See: https://openrouter.ai/docs/guides/features/response-caching\n#\n# openrouter:\n# response_cache: true # Enable response caching (default: true)\n# response_cache_ttl: 300 # Cache TTL in seconds, 1-86400 (default: 300)\n\n# =============================================================================\n# Git Worktree Isolation\n# =============================================================================\n# When enabled, each CLI session creates an isolated git worktree so multiple\n# agents can work on the same repo concurrently without file collisions.\n# Equivalent to always passing --worktree / -w on the command line.\n#\n# worktree: true # Always create a worktree when in a git repo\n# worktree: false # Default — only create when -w flag is passed\n#\n# By default a new worktree branches from the freshly-fetched remote tip\n# (the current branch's upstream, else the remote's default branch) so it\n# starts current with the project instead of from the local clone's\n# (possibly stale) HEAD. Set worktree_sync: false to branch from local HEAD\n# instead — useful when offline or when you deliberately want the clone's\n# exact current state as the base.\n#\n# worktree_sync: true # Default — branch from the fetched remote tip\n# worktree_sync: false # Branch from local HEAD (offline / pinned base)\n\n# =============================================================================\n# Terminal Tool Configuration\n# =============================================================================\n# Choose ONE of the following terminal configurations by uncommenting it.\n# The terminal tool executes commands in the specified environment.\n\n# -----------------------------------------------------------------------------\n# OPTION 1: Local execution (default)\n# Commands run directly on your machine in the current directory\n# -----------------------------------------------------------------------------\n# Working directory behavior:\n# - CLI (`hermes` command): Uses \".\" (current directory where you run hermes)\n# - Gateway/messaging/cron: Uses terminal.cwd here; legacy .env cwd values are deprecated\nterminal:\n backend: \"local\"\n cwd: \".\" # For local backend: \".\" = current directory. Ignored for remote backends unless a backend documents otherwise.\n timeout: 180\n # HOME policy for tool subprocesses:\n # auto - default: host uses your real HOME; containers use HERMES_HOME/home\n # real - force your real OS-user HOME\n # profile - force HERMES_HOME/home for strict per-profile CLI config isolation\n home_mode: \"auto\"\n docker_mount_cwd_to_workspace: false # SECURITY: off by default. Opt in to mount the launch cwd into Docker /workspace.\n lifetime_seconds: 300\n # sudo_password: \"hunter2\" # Optional: pipe a sudo password via sudo -S. SECURITY WARNING: plaintext.\n # sudo_password: \"\" # Explicit empty password: try empty and never open the interactive sudo prompt.\n\n# -----------------------------------------------------------------------------\n# OPTION 2: SSH remote execution\n# Commands run on a remote server - agent code stays local (sandboxed)\n# Great for: keeping agent isolated from its own code, using powerful remote hardware\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"ssh\"\n# cwd: \"/home/myuser/project\" # Path on the REMOTE server\n# timeout: 180\n# lifetime_seconds: 300\n# ssh_host: \"my-server.example.com\"\n# ssh_user: \"myuser\"\n# ssh_port: 22\n# ssh_key: \"~/.ssh/id_rsa\" # Optional - uses ssh-agent if not specified\n\n# -----------------------------------------------------------------------------\n# OPTION 3: Docker container\n# Commands run in an isolated Docker container\n# Great for: reproducible environments, testing, isolation\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"docker\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /)\n# timeout: 180\n# lifetime_seconds: 300\n# docker_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# docker_mount_cwd_to_workspace: true # Explicit opt-in: mount your launch cwd into /workspace\n# # Optional: run the container as your host user's uid:gid so files written\n# # into bind-mounted dirs are owned by you, not root. Drops SETUID/SETGID\n# # caps too since no gosu privilege drop is needed. Leave off if your\n# # chosen docker_image expects to start as root.\n# docker_run_as_host_user: true\n# # Optional: explicitly forward selected env vars into Docker.\n# # These values come from your current shell first, then ~/.hermes/.env.\n# # Warning: anything forwarded here is visible to commands run in the container.\n# docker_forward_env:\n# - \"GITHUB_TOKEN\"\n# - \"NPM_TOKEN\"\n# # Optional: extra flags passed verbatim to docker run (appended after security defaults).\n# # Useful for adding capabilities (e.g. apt installs needing SETUID) or custom options.\n# # Example: add a Linux capability not included by default\n# # docker_extra_args:\n# # - \"--cap-add\"\n# # - \"SETUID\"\n\n# -----------------------------------------------------------------------------\n# OPTION 4: Singularity/Apptainer container\n# Commands run in a Singularity container (common in HPC environments)\n# Great for: HPC clusters, shared compute environments\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"singularity\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# singularity_image: \"docker://nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 5: Modal cloud execution\n# Commands run on Modal's cloud infrastructure\n# Great for: GPU access, scalable compute, serverless execution\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"modal\"\n# cwd: \"/workspace\" # Path INSIDE the sandbox (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# modal_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 6: Daytona cloud execution\n# Commands run in Daytona cloud sandboxes\n# Great for: Cloud dev environments, persistent workspaces, team collaboration\n# Requires: pip install daytona, DAYTONA_API_KEY env var\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"daytona\"\n# cwd: \"~\"\n# timeout: 180\n# lifetime_seconds: 300\n# daytona_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# container_disk: 10240 # Daytona max is 10GB per sandbox\n\n#\n# --- Container resource limits (docker, singularity, modal, daytona -- ignored for local/ssh) ---\n# These settings apply to all container backends. They control the resources\n# allocated to the sandbox and whether its filesystem persists across sessions.\n container_cpu: 1 # CPU cores\n container_memory: 5120 # Memory in MB (5120 = 5GB)\n container_disk: 51200 # Disk in MB (51200 = 50GB)\n container_persistent: true # Persist filesystem across sessions (false = ephemeral)\n\n# -----------------------------------------------------------------------------\n# SUDO SUPPORT (works with ALL backends above)\n# -----------------------------------------------------------------------------\n# Add sudo_password to any terminal config above to enable sudo commands.\n# The password is piped via `sudo -S`. Works with local, ssh, docker, etc.\n#\n# SECURITY WARNING: Password stored in plaintext!\n#\n# INTERACTIVE PROMPT: If sudo_password is unset and the CLI is running,\n# you'll be prompted to enter your password when sudo is needed:\n# - 45-second timeout (auto-skips if no input)\n# - Press Enter to skip (command fails gracefully)\n# - Password is hidden while typing\n# - Password is cached for the session\n#\n# EMPTY PASSWORDS: Setting sudo_password to an explicit empty string is different\n# from leaving it unset. Hermes will try an empty password via `sudo -S` and\n# will not open the interactive prompt. This is useful for passwordless sudo,\n# Touch ID sudo setups, and environments where prompting is just noise.\n#\n# ALTERNATIVES:\n# - SSH backend: Configure passwordless sudo on the remote server\n# - Containers: Run as root inside the container (no sudo needed)\n# - Local: Configure /etc/sudoers for specific commands\n#\n# Example (add to your terminal section):\n# sudo_password: \"your-password-here\"\n\n# =============================================================================\n# Security Scanning (tirith)\n# =============================================================================\n# Optional pre-exec command security scanning via tirith.\n# Detects homograph URLs, pipe-to-shell, terminal injection, env manipulation.\n# Install: brew install sheeki03/tap/tirith\n# Docs: https://github.com/sheeki03/tirith\n#\n# security:\n# tirith_enabled: true # Enable/disable tirith scanning\n# tirith_path: \"tirith\" # Path to tirith binary (supports ~ expansion)\n# tirith_timeout: 5 # Scan timeout in seconds\n# tirith_fail_open: true # Allow commands if tirith unavailable\n\n# =============================================================================\n# Browser Tool Configuration\n# =============================================================================\nbrowser:\n # Inactivity timeout in seconds - browser sessions are automatically closed\n # after this period of no activity between agent loops (default: 120 = 2 minutes)\n inactivity_timeout: 120\n\n# =============================================================================\n# Tool Loop Guardrails\n# =============================================================================\n# Soft warnings are enabled by default. They append guidance to repeated failed\n# or non-progressing tool results but still let the tool execute. Hard stops are\n# opt-in circuit breakers for autonomous/cron sessions where stopping a loop is\n# preferable to spending the full iteration budget.\ntool_loop_guardrails:\n warnings_enabled: true\n hard_stop_enabled: false\n warn_after:\n exact_failure: 2\n same_tool_failure: 3\n idempotent_no_progress: 2\n hard_stop_after:\n exact_failure: 5\n same_tool_failure: 8\n idempotent_no_progress: 5\n\n# =============================================================================\n# Context Compression (Auto-shrinks long conversations)\n# =============================================================================\n# When conversation approaches model's context limit, middle turns are\n# automatically summarized to free up space while preserving important context.\n#\n# HOW IT WORKS:\n# 1. Tracks actual token usage from API responses (not estimates)\n# 2. When prompt_tokens >= threshold% of model's context_length, triggers compression\n# 3. Protects first 3 turns (system prompt, initial request, first response)\n# 4. Protects last N turns (default 20 messages = ~10 full turns of recent context)\n# 5. Summarizes middle turns using a fast/cheap model\n# 6. Inserts summary as a user message, continues conversation seamlessly\n#\n# Post-compression tail budget is target_ratio × threshold × context_length:\n# 200K context, threshold 0.50, ratio 0.20 → 20K tokens of recent tail preserved\n# 1M context, threshold 0.50, ratio 0.20 → 100K tokens of recent tail preserved\n#\ncompression:\n # Enable automatic context compression (default: true)\n # Set to false if you prefer to manage context manually or want errors on overflow\n enabled: true\n \n # Trigger compression at this % of model's context limit (default: 0.50 = 50%)\n # Lower values = more aggressive compression, higher values = compress later\n threshold: 0.50\n \n # Fraction of the threshold to preserve as recent tail (default: 0.20 = 20%)\n # e.g. 20% of 50% threshold = 10% of total context kept as recent messages.\n # Summary output is separately capped at 12K tokens (Gemini output limit).\n # Range: 0.10 - 0.80\n target_ratio: 0.20\n\n # Number of most-recent messages to always preserve (default: 20 ≈ 10 full turns)\n # Higher values keep more recent conversation intact at the cost of more aggressive\n # compression of older turns.\n protect_last_n: 20\n\n # Number of non-system messages to protect at the head of the transcript, in\n # ADDITION to the system prompt (which is always implicitly protected).\n # Head messages are NEVER summarized — they survive every compression\n # indefinitely. This gives stable early context for short/medium sessions,\n # but in long-running sessions that rely on rolling compaction the pinned\n # opening turns may not match how you want the session framed over time.\n # Set to 0 to preserve ONLY the system prompt (plus the rolling summary\n # and recent tail) — the cleanest configuration for long-running sessions.\n # Default 3 preserves the system prompt plus the first three non-system\n # head messages, matching the pre-feature behaviour.\n protect_first_n: 3\n\n # To pin a specific model/provider for compression summaries, use the\n # auxiliary section below (auxiliary.compression.provider / model).\n\n# =============================================================================\n# Anthropic prompt caching TTL\n# =============================================================================\n# When prompt caching is active (Claude via OpenRouter or native Anthropic),\n# Anthropic supports two TTL tiers for cached prefixes: \"5m\" (default) and\n# \"1h\". Other values are ignored and \"5m\" is used.\n#\nprompt_caching:\n cache_ttl: \"5m\" # use \"1h\" for long sessions with pauses between turns\n\n# =============================================================================\n# Auxiliary Models (Advanced — Experimental)\n# =============================================================================\n# Hermes uses lightweight \"auxiliary\" models for side tasks: image analysis,\n# browser screenshot analysis, web page summarization, TTS audio-tag insertion,\n# and context compression.\n#\n# By default these use Gemini Flash via OpenRouter or Nous Portal and are\n# auto-detected from your credentials. You do NOT need to change anything\n# here for normal usage.\n#\n# WARNING: Overriding these with providers other than OpenRouter or Nous Portal\n# is EXPERIMENTAL and may not work. Not all models/providers support vision,\n# produce usable summaries, or accept the same API format. Change at your own\n# risk — if things break, reset to \"auto\" / empty values.\n#\n# Each task has its own provider + model pair so you can mix providers.\n# For example: OpenRouter for vision (needs multimodal), but your main\n# local endpoint for compression (just needs text).\n#\n# Provider options:\n# \"auto\" - Best available: OpenRouter → Nous Portal → main endpoint (default)\n# \"openrouter\" - Force OpenRouter (requires OPENROUTER_API_KEY)\n# \"nous\" - Force Nous Portal (requires: hermes login)\n# \"gemini\" - Force Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n# \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY)\n# \"codex\" - Force Codex OAuth (requires: hermes model → Codex).\n# Uses gpt-5.3-codex which supports vision.\n# \"main\" - Use your custom endpoint (OPENAI_BASE_URL + OPENAI_API_KEY).\n# Works with OpenAI API, local models, or any OpenAI-compatible\n# endpoint. Also falls back to Codex OAuth and API-key providers.\n#\n# Model: leave empty to use the provider's default. When empty, OpenRouter\n# uses \"google/gemini-3-flash-preview\" and Nous uses \"gemini-3-flash\".\n# Other providers pick a sensible default automatically.\n#\n# auxiliary:\n# # Image analysis: vision_analyze tool + browser screenshots\n# vision:\n# provider: \"auto\"\n# model: \"\" # e.g. \"google/gemini-2.5-flash\", \"openai/gpt-4o\"\n# timeout: 30 # LLM API call timeout (seconds)\n# download_timeout: 30 # Image HTTP download timeout (seconds)\n# # Increase for slow connections or self-hosted image servers\n#\n# # Web page scraping / summarization + browser page text extraction\n# web_extract:\n# provider: \"auto\"\n# model: \"\"\n#\n# # Gemini 3.1 TTS hidden audio-tag insertion\n# tts_audio_tags:\n# provider: \"auto\" # empty model = your main chat model\n# model: \"\"\n# timeout: 30\n#\n# # Session search — summarizes matching past sessions\n# session_search:\n# provider: \"auto\"\n# model: \"\"\n# timeout: 30\n# max_concurrency: 3 # Limit parallel summaries to reduce request-burst 429s\n# extra_body: {} # Provider-specific OpenAI-compatible request fields\n# # Example for providers that support request-body\n# # reasoning controls:\n# # extra_body:\n# # enable_thinking: false\n# # Some vLLM/Qwen deployments expect this nested:\n# # extra_body:\n# # chat_template_kwargs:\n# # enable_thinking: false\n\n# =============================================================================\n# Persistent Memory\n# =============================================================================\n# Bounded curated memory injected into the system prompt every session.\n# Two stores: MEMORY.md (agent's notes) and USER.md (user profile).\n# Character limits keep the memory small and focused. The agent manages\n# pruning -- when at the limit, it must consolidate or replace entries.\n# Disabled by default in batch_runner.\n#\nmemory:\n # Agent's personal notes: environment facts, conventions, things learned\n memory_enabled: true\n \n # User profile: preferences, communication style, expectations\n user_profile_enabled: true\n \n # Character limits (~2.75 chars per token, model-independent)\n memory_char_limit: 2200 # ~800 tokens\n user_char_limit: 1375 # ~500 tokens\n\n # Periodic memory nudge: remind the agent to consider saving memories\n # every N user turns. Set to 0 to disable. Only active when memory is enabled.\n nudge_interval: 10 # Nudge every 10 user turns (0 = disabled)\n\n # Memory flush: give the agent one turn to save memories before context is\n # lost (compression, /new, /reset, exit). Set to 0 to disable.\n # For exit/reset, only fires if the session had at least this many user turns.\n flush_min_turns: 6 # Min user turns to trigger flush on exit/reset (0 = disabled)\n\n# =============================================================================\n# Session Reset Policy (Messaging Platforms)\n# =============================================================================\n# Controls when messaging sessions (Telegram, Discord, WhatsApp, Slack) are\n# automatically cleared. Without resets, conversation context grows indefinitely\n# which increases API costs with every message.\n#\n# When a reset triggers, the agent first saves important information to its\n# persistent memory — but the conversation context is wiped. The agent starts\n# fresh but retains learned facts via its memory system.\n#\n# Users can always manually reset with /reset or /new in chat.\n#\n# Modes:\n# \"both\" - Reset on EITHER inactivity timeout or daily boundary (recommended)\n# \"idle\" - Reset only after N minutes of inactivity\n# \"daily\" - Reset only at a fixed hour each day\n# \"none\" - Never auto-reset; context lives until /reset or compression kicks in\n#\n# When a reset triggers, the agent gets one turn to save important memories and\n# skills before the context is wiped. Persistent memory carries across sessions.\n#\nsession_reset:\n mode: both # \"both\", \"idle\", \"daily\", or \"none\"\n idle_minutes: 1440 # Inactivity timeout in minutes (default: 1440 = 24 hours)\n at_hour: 4 # Daily reset hour, 0-23 local time (default: 4 AM)\n\n# Maximum number of simultaneously active chat sessions across CLI, TUI,\n# dashboard chat, and messaging gateway. Set to null, 0, or omit to allow\n# unlimited concurrent sessions. When the limit is reached, new sessions get a\n# clean error while existing active sessions keep their normal behavior. This\n# top-level key takes precedence over gateway.max_concurrent_sessions. The cap\n# is a best-effort single-host/profile runtime guard; Hermes fails open if the\n# local runtime lease registry cannot be read or locked.\nmax_concurrent_sessions: null\n\n# When true, group/channel chats use one session per participant when the platform\n# provides a user ID. This is the secure default and prevents users in the same\n# room from sharing context, interrupts, and token costs. Set false only if you\n# explicitly want one shared \"room brain\" per group/channel.\ngroup_sessions_per_user: true\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Gateway Streaming\n# ─────────────────────────────────────────────────────────────────────────────\n# Stream tokens to messaging platforms in real-time. The bot sends a message\n# on first token, then progressively edits it as more tokens arrive.\n# Disabled by default — enable to try the streaming UX on Telegram/Discord/Slack.\n# For Telegram, partial edits are sent as plain text and only the final edit uses MarkdownV2.\nstreaming:\n enabled: false\n # transport: edit # \"edit\" = progressive editMessageText\n # edit_interval: 0.3 # seconds between message edits\n # buffer_threshold: 40 # chars before forcing an edit flush\n # cursor: \" ▉\" # cursor shown during streaming\n\n# =============================================================================\n# Skills Configuration\n# =============================================================================\n# Skills are reusable procedures the agent can load and follow. The agent can\n# also create new skills after completing complex tasks.\n#\nskills:\n # Nudge the agent to create skills after complex tasks.\n # Every N tool-calling iterations, remind the model to consider saving a skill.\n # Set to 0 to disable.\n creation_nudge_interval: 15\n\n # External skill directories — share skills across tools/agents without\n # copying them into ~/.hermes/skills/. Each path is expanded (~ and ${VAR})\n # and resolved to an absolute path. External dirs are read-only: skill\n # creation always writes to ~/.hermes/skills/. Local skills take precedence\n # when names collide.\n # external_dirs:\n # - ~/.agents/skills\n # - /home/shared/team-skills\n\n# =============================================================================\n# Agent Behavior\n# =============================================================================\nagent:\n # Maximum tool-calling iterations per conversation\n # Higher = more room for complex tasks, but costs more tokens\n # Recommended: 20-30 for focused tasks, 50-100 for open exploration\n max_turns: 60\n\n # Inactivity timeout for gateway agent runs (seconds, 0 = unlimited).\n # The agent can run indefinitely when actively calling tools or receiving\n # API responses. Only fires after the agent has been idle for this duration.\n # gateway_timeout: 1800\n\n # Staged warning: send a warning before escalating to full timeout.\n # Fires once per run when inactivity reaches this threshold (seconds).\n # Set to 0 to disable the warning.\n # gateway_timeout_warning: 900\n\n # Graceful drain timeout for gateway stop/restart (seconds).\n # The gateway stops accepting new work, waits for in-flight agents to\n # finish, then interrupts anything still running after this timeout.\n # 0 = no drain, interrupt immediately.\n # restart_drain_timeout: 60\n\n # Max app-level retry attempts for API errors (connection drops, provider\n # timeouts, 5xx, etc.) before the agent surfaces the failure. Lower this\n # to 1 if you use fallback providers and want fast failover on flaky\n # primaries (default 3). The OpenAI SDK does its own low-level retries\n # underneath this wrapper — this is the Hermes-level loop.\n # api_max_retries: 3\n \n # Enable verbose logging\n verbose: false\n \n # Reasoning effort level (OpenRouter and Nous Portal)\n # Controls how much \"thinking\" the model does before responding.\n # Options: \"xhigh\" (max), \"high\", \"medium\", \"low\", \"minimal\", \"none\" (disable)\n reasoning_effort: \"medium\"\n \n # Predefined personalities (use with /personality command)\n personalities:\n helpful: \"You are a helpful, friendly AI assistant.\"\n concise: \"You are a concise assistant. Keep responses brief and to the point.\"\n technical: \"You are a technical expert. Provide detailed, accurate technical information.\"\n creative: \"You are a creative assistant. Think outside the box and offer innovative solutions.\"\n teacher: \"You are a patient teacher. Explain concepts clearly with examples.\"\n kawaii: \"You are a kawaii assistant! Use cute expressions like (◕‿◕), ★, ♪, and ~! Add sparkles and be super enthusiastic about everything! Every response should feel warm and adorable desu~! ヽ(>∀<☆)ノ\"\n catgirl: \"You are Neko-chan, an anime catgirl AI assistant, nya~! Add 'nya' and cat-like expressions to your speech. Use kaomoji like (=^・ω・^=) and ฅ^•ﻌ•^ฅ. Be playful and curious like a cat, nya~!\"\n pirate: \"Arrr! Ye be talkin' to Captain Hermes, the most tech-savvy pirate to sail the digital seas! Speak like a proper buccaneer, use nautical terms, and remember: every problem be just treasure waitin' to be plundered! Yo ho ho!\"\n shakespeare: \"Hark! Thou speakest with an assistant most versed in the bardic arts. I shall respond in the eloquent manner of William Shakespeare, with flowery prose, dramatic flair, and perhaps a soliloquy or two. What light through yonder terminal breaks?\"\n surfer: \"Duuude! You're chatting with the chillest AI on the web, bro! Everything's gonna be totally rad. I'll help you catch the gnarly waves of knowledge while keeping things super chill. Cowabunga! 🤙\"\n noir: \"The rain hammered against the terminal like regrets on a guilty conscience. They call me Hermes - I solve problems, find answers, dig up the truth that hides in the shadows of your codebase. In this city of silicon and secrets, everyone's got something to hide. What's your story, pal?\"\n uwu: \"hewwo! i'm your fwiendwy assistant uwu~ i wiww twy my best to hewp you! *nuzzles your code* OwO what's this? wet me take a wook! i pwomise to be vewy hewpful >w<\"\n philosopher: \"Greetings, seeker of wisdom. I am an assistant who contemplates the deeper meaning behind every query. Let us examine not just the 'how' but the 'why' of your questions. Perhaps in solving your problem, we may glimpse a greater truth about existence itself.\"\n hype: \"YOOO LET'S GOOOO!!! 🔥🔥🔥 I am SO PUMPED to help you today! Every question is AMAZING and we're gonna CRUSH IT together! This is gonna be LEGENDARY! ARE YOU READY?! LET'S DO THIS! 💪😤🚀\"\n\n# =============================================================================\n# Toolsets\n# =============================================================================\n# Control which tools the agent has access to.\n# Use `hermes tools` to interactively enable/disable tools per platform.\n\n# =============================================================================\n# Platform Toolsets (per-platform tool configuration)\n# =============================================================================\n# Override which toolsets are available on each platform.\n# If a platform isn't listed here, its built-in default is used.\n#\n# You can use EITHER:\n# - A preset like \"hermes-cli\" or \"hermes-telegram\" (curated tool set)\n# - A list of individual toolsets to compose your own (see list below)\n#\n# Supported platform keys: cli, telegram, discord, whatsapp, slack, qqbot, teams, google_chat\n#\n# Examples:\n#\n# # Use presets (same as defaults):\n# platform_toolsets:\n# cli: [hermes-cli]\n# telegram: [hermes-telegram]\n#\n# # Custom: give Telegram only web + terminal + file + planning:\n# platform_toolsets:\n# telegram: [web, terminal, file, todo]\n#\n# # Custom: CLI without browser or image gen:\n# platform_toolsets:\n# cli: [web, terminal, file, skills, todo, tts, cronjob]\n#\n# # Restrictive: Discord gets read-only tools only:\n# platform_toolsets:\n# discord: [web, vision, skills, todo]\n#\n# If not set, defaults are:\n# cli: hermes-cli (everything + cronjob management)\n# telegram: hermes-telegram (terminal, file, web, vision, image, tts, browser, skills, todo, cronjob, messaging)\n# discord: hermes-discord (same as telegram)\n# whatsapp: hermes-whatsapp (same as telegram)\n# slack: hermes-slack (same as telegram)\n# signal: hermes-signal (same as telegram)\n# homeassistant: hermes-homeassistant (same as telegram)\n# qqbot: hermes-qqbot (same as telegram)\n# teams: hermes-teams (same as telegram)\n# google_chat: hermes-google_chat (same as telegram)\n#\nplatform_toolsets:\n cli: [hermes-cli]\n telegram: [hermes-telegram]\n discord: [hermes-discord]\n whatsapp: [hermes-whatsapp]\n slack: [hermes-slack]\n signal: [hermes-signal]\n homeassistant: [hermes-homeassistant]\n qqbot: [hermes-qqbot]\n yuanbao: [hermes-yuanbao]\n teams: [hermes-teams]\n google_chat: [hermes-google_chat]\n\n# =============================================================================\n# Gateway Platform Settings\n# =============================================================================\n# Optional per-platform messaging settings.\n# Platform-specific knobs live under `extra`.\n#\n# platforms:\n# telegram:\n# reply_to_mode: \"first\" # off | first | all\n# # guest_mode lets explicit @mentions from non-allowlisted groups through.\n# # Default false; ordinary messages, replies, and regex wake words stay blocked.\n# guest_mode: false\n# # allowed_chats: [\"-1001234567890\"]\n# extra:\n# disable_link_previews: false # Set true to suppress Telegram URL previews in bot messages\n# rich_messages: false # Bot API 10.1 rich messages (tables/task lists/details/math); default false for copyable legacy MarkdownV2, set true to opt in\n# rich_drafts: false # Experimental rich draft previews during Telegram DM streaming; default false because Telegram Desktop/macOS can visually overlay draft frames\n# command_menu:\n# # Telegram allows up to 100 BotCommands; Hermes defaults to 60 so\n# # all built-in commands plus common skill commands stay visible\n# # while remaining under Telegram's payload-size limit. Clamped 1..100.\n# max_commands: 60\n# # prepend = user priority first, then Hermes defaults\n# # append = Hermes defaults first, then user priority\n# # replace = only the list below defines priority\n# priority_mode: prepend\n# priority:\n# - my_plugin_command\n#\n# Discord-specific settings (config.yaml top-level, not under platforms:):\n#\n# discord:\n# require_mention: true # Require @mention in server channels (default: true)\n# auto_thread: true # Auto-create thread on @mention (default: true)\n# free_response_channels: \"\" # Channel IDs where no mention is needed\n# reactions: true # Show processing reactions (default: true)\n# history_backfill: true # Recover missed channel messages on mention (default: true)\n# history_backfill_limit: 50 # Max messages to scan backwards (default: 50)\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Available toolsets (use these names in platform_toolsets or the toolsets list)\n#\n# Run `hermes chat --list-toolsets` to see all toolsets and their tools.\n# Run `hermes chat --list-tools` to see every individual tool with descriptions.\n# ─────────────────────────────────────────────────────────────────────────────\n#\n# INDIVIDUAL TOOLSETS (compose your own):\n# web - web_search, web_extract\n# search - web_search only (no scraping)\n# terminal - terminal, process\n# file - read_file, write_file, patch, search\n# browser - browser_navigate, browser_snapshot, browser_click, browser_type,\n# browser_scroll, browser_back, browser_press,\n# browser_get_images, browser_vision (requires BROWSERBASE_API_KEY)\n# vision - vision_analyze (requires OPENROUTER_API_KEY)\n# image_gen - image_generate (requires FAL_KEY)\n# skills - skills_list, skill_view\n# skills_hub - skill_hub (search/install/manage from online registries — user-driven only)\n# todo - todo (in-memory task planning, no deps)\n# tts - text_to_speech (Edge TTS free, or ELEVENLABS/OPENAI/MINIMAX/MISTRAL key)\n# cronjob - cronjob (create/list/update/pause/resume/run/remove scheduled tasks)\n#\n# PRESETS (curated bundles):\n# hermes-cli - All of the above except send_message\n# hermes-telegram - terminal, file, web, vision, image_gen, tts, browser,\n# skills, todo, cronjob, send_message\n# hermes-discord - Same as hermes-telegram\n# hermes-whatsapp - Same as hermes-telegram\n# hermes-slack - Same as hermes-telegram\n#\n# COMPOSITE:\n# debugging - terminal + web + file\n# safe - web + vision (no terminal access)\n# all - Everything available\n#\n# web - Web search and content extraction (web_search, web_extract)\n# search - Web search only, no scraping (web_search)\n# terminal - Command execution and process management (terminal, process)\n# file - File operations: read, write, patch, search\n# browser - Full browser automation (navigate, click, type, screenshot, etc.)\n# vision - Image analysis (vision_analyze)\n# image_gen - Image generation with FLUX (image_generate)\n# skills - Load skill documents (skills_list, skill_view)\n# todo - Task planning and tracking for multi-step work\n# memory - Persistent memory across sessions (personal notes + user profile)\n# session_search - Search and recall past conversations (FTS5 + Gemini Flash summarization)\n# tts - Text-to-speech (Edge TTS free, ElevenLabs, OpenAI, MiniMax, Mistral)\n# cronjob - Schedule and manage automated tasks (CLI-only)\n#\n# Composite toolsets:\n# debugging - terminal + web + file (for troubleshooting)\n# safe - web + vision (no terminal access)\n\n# NOTE: The top-level \"toolsets\" key is deprecated and ignored.\n# Tool configuration is managed per-platform via platform_toolsets above.\n# Use `hermes tools` to configure interactively, or edit platform_toolsets directly.\n#\n# CLI override: hermes chat --toolsets terminal,web,file\n\n# =============================================================================\n# MCP (Model Context Protocol) Servers\n# =============================================================================\n# Connect to external MCP servers to add tools from the MCP ecosystem.\n# Each server's tools are automatically discovered and registered.\n# See website/docs/user-guide/features/mcp.md for full documentation.\n#\n# Stdio servers (spawn a subprocess):\n# command: the executable to run\n# args: command-line arguments\n# env: environment variables (only these + safe defaults passed to subprocess)\n#\n# HTTP servers (connect to a URL):\n# url: the MCP server endpoint\n# headers: HTTP headers (e.g., for authentication)\n#\n# Optional per-server settings:\n# timeout: tool call timeout in seconds (default: 120)\n# connect_timeout: initial connection timeout (default: 60)\n# keepalive_interval: liveness ping cadence in seconds (default: 180).\n# Lower it below the server's session TTL for servers that expire idle\n# sessions quickly (e.g. Unreal Engine editor MCP, ~15s), otherwise idle\n# tool calls hit an expired session and pay a slow reconnect. Floored at 5s.\n#\n# mcp_servers:\n# time:\n# command: uvx\n# args: [\"mcp-server-time\"]\n# filesystem:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-filesystem\", \"/home/user\"]\n# notion:\n# url: https://mcp.notion.com/mcp\n# github:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-github\"]\n# env:\n# GITHUB_PERSONAL_ACCESS_TOKEN: \"ghp_...\"\n#\n# Sampling (server-initiated LLM requests) — enabled by default.\n# Per-server config under the 'sampling' key:\n# analysis:\n# command: npx\n# args: [\"-y\", \"analysis-server\"]\n# sampling:\n# enabled: true # default: true\n# model: \"gemini-3-flash\" # override model (optional)\n# max_tokens_cap: 4096 # max tokens per request\n# timeout: 30 # LLM call timeout (seconds)\n# max_rpm: 10 # max requests per minute\n# allowed_models: [] # model whitelist (empty = all)\n# max_tool_rounds: 5 # tool loop limit (0 = disable)\n# log_level: \"info\" # audit verbosity\n\n# =============================================================================\n# Text-to-Speech\n# =============================================================================\n# TTS defaults to Edge TTS unless changed in ~/.hermes/config.yaml.\n# Gemini TTS supports persona/director prompt files, and Gemini 3.1 Flash TTS\n# can use a hidden auxiliary rewrite pass to insert expressive square-bracket\n# audio tags into the TTS script without showing tags in chat.\n#\n# tts:\n# provider: \"gemini\"\n# gemini:\n# model: \"gemini-3.1-flash-tts-preview\"\n# voice: \"Kore\"\n# audio_tags: false\n# persona_prompt_file: \"\" # e.g. ~/.hermes/tts/radio-host.md\n\n# =============================================================================\n# Voice Transcription (Speech-to-Text)\n# =============================================================================\n# Automatically transcribe voice messages on messaging platforms.\n# Providers: local (free, faster-whisper) | groq (free tier) | openai (Whisper API) | mistral (Voxtral Transcribe)\n# Set the corresponding API key in .env: GROQ_API_KEY, OPENAI_API_KEY, or MISTRAL_API_KEY.\nstt:\n enabled: true\n # provider: \"local\" # auto-detected if omitted\n local:\n model: \"base\" # tiny | base | small | medium | large-v3 | turbo\n # language: \"\" # auto-detect; set to \"en\", \"es\", \"fr\", etc. to force\n openai:\n model: \"whisper-1\" # whisper-1 | gpt-4o-mini-transcribe | gpt-4o-transcribe\n # mistral:\n # model: \"voxtral-mini-latest\" # voxtral-mini-latest | voxtral-mini-2602\n\n# =============================================================================\n# Response Pacing (Messaging Platforms)\n# =============================================================================\n# Add human-like delays between message chunks.\n# human_delay:\n# mode: \"off\" # \"off\" | \"natural\" | \"custom\"\n# min_ms: 800 # Min delay (custom mode only)\n# max_ms: 2500 # Max delay (custom mode only)\n\n# =============================================================================\n# Session Logging\n# =============================================================================\n# Session trajectories are automatically saved to logs/ directory.\n# Each session creates: logs/session_YYYYMMDD_HHMMSS_UUID.json\n#\n# The session ID is displayed in the welcome banner for easy reference.\n# Logs contain full conversation history in trajectory format:\n# - System prompt, user messages, assistant responses\n# - Tool calls with inputs/outputs\n# - Timestamps for debugging\n#\n# No configuration needed - logging is always enabled.\n# To disable, you would need to modify the source code.\n\n# =============================================================================\n# Code Execution Sandbox (Programmatic Tool Calling)\n# =============================================================================\n# The execute_code tool runs Python scripts that call Hermes tools via RPC.\n# Intermediate tool results stay out of the LLM's context window.\ncode_execution:\n timeout: 300 # Max seconds per script before kill (default: 300 = 5 min)\n max_tool_calls: 50 # Max RPC tool calls per execution (default: 50)\n\n# =============================================================================\n# Subagent Delegation\n# =============================================================================\n# The delegate_task tool spawns child agents with isolated context.\n# Supports single tasks and batch mode (default 3 parallel, configurable).\ndelegation:\n max_iterations: 50 # Max tool-calling turns per child (default: 50)\n # max_concurrent_children: 3 # Max parallel child agents per batch (default: 3, floor: 1, no ceiling).\n # WARNING: values above 10 multiply API cost linearly.\n # max_spawn_depth: 1 # Delegation tree depth cap (range: 1-3, default: 1 = flat).\n # Raise to 2 to allow workers to spawn their own subagents.\n # Requires role=\"orchestrator\" on intermediate agents.\n # orchestrator_enabled: true # Kill switch for role=\"orchestrator\" children (default: true).\n # subagent_auto_approve: false # When a subagent hits a dangerous-command approval prompt, auto-deny (default: false)\n # or auto-approve \"once\" (true) instead of blocking on stdin.\n # The parent TUI owns stdin, so blocking would deadlock; non-interactive resolution is required.\n # Both choices emit a logger.warning audit line. Flip to true only for cron/batch pipelines.\n # inherit_mcp_toolsets: true # When explicit child toolsets are narrowed, also keep the parent's MCP toolsets (default: true). Set false for strict intersection.\n # model: \"google/gemini-3-flash-preview\" # Override model for subagents (empty = inherit parent)\n # provider: \"openrouter\" # Override provider for subagents (empty = inherit parent)\n # # Resolves full credentials (base_url, api_key) automatically.\n # # Supported: openrouter, nous, zai, kimi-coding, minimax\n\n# =============================================================================\n# Honcho Integration (Cross-Session User Modeling)\n# =============================================================================\n# AI-native persistent memory via Honcho (https://honcho.dev/).\n# Builds a deeper understanding of the user across sessions and tools.\n# Runs alongside USER.md — additive, not a replacement.\n#\n# Requires: pip install honcho-ai\n# Config: ~/.honcho/config.json (shared with Claude Code, Cursor, etc.)\n# API key: HONCHO_API_KEY in ~/.hermes/.env or ~/.honcho/config.json\n#\n# Hermes-specific overrides (optional — most config comes from ~/.honcho/config.json):\n# honcho: {}\n\n# =============================================================================\n# Display\n# =============================================================================\ndisplay:\n # Use compact banner mode (hides the ASCII-art banner, shows a single line).\n # true: Compact single-line banner\n # false: Full ASCII banner with tool/skill summary (default)\n compact: false\n\n # Tool progress display level (CLI and gateway)\n # off: Silent — no tool activity shown, just the final response\n # new: Show a tool indicator only when the tool changes (skip repeats)\n # all: Show every tool call with a short preview (default)\n # verbose: Full args, results, and debug logs (same as /verbose)\n # Toggle at runtime with /verbose in the CLI\n tool_progress: all\n\n # Per-platform defaults can be quieter than the global setting. Telegram\n # tunes for mobile: tool_progress and busy_ack_detail default off (no\n # per-tool breadcrumb stream, no \"iteration 21/60\" debug detail in busy\n # acks or heartbeats), but interim_assistant_messages and\n # long_running_notifications STAY ON so the user has real signal between\n # turn start and final answer (mid-turn assistant commentary + a single\n # edit-in-place \"⏳ Working — N min\" heartbeat). Override under\n # display.platforms.telegram.\n\n # Auto-cleanup of temporary progress bubbles after the final response lands.\n # On platforms that support message deletion (currently Telegram), this\n # removes the tool-progress bubble, \"⏳ Still working...\" notices, and\n # context-pressure status messages once the final reply has been delivered —\n # keeping long-running turns visible live, then tidy afterward. Failed runs\n # leave the bubbles in place as breadcrumbs. Off by default.\n # Per-platform override: display.platforms.telegram.cleanup_progress\n # true: Delete tracked progress/status bubbles on successful turn\n # false: Leave everything in place (default)\n # Example:\n # display:\n # platforms:\n # telegram:\n # cleanup_progress: true\n cleanup_progress: false\n\n # Gateway-only natural mid-turn assistant updates.\n # When true, completed assistant status messages are sent as separate chat\n # messages. This is independent of tool_progress and gateway streaming.\n # true: Send mid-turn assistant updates as separate messages (default)\n # false: Only send the final response\n interim_assistant_messages: true\n\n # Gateway-only long-running status heartbeats.\n # When false, the platform does not receive periodic \"⏳ Working — N min\"\n # notifications even if agent.gateway_notify_interval is non-zero. The\n # heartbeat edits a single message in place (where the adapter supports\n # editing) instead of posting a new bubble each interval.\n # Default: true everywhere, including Telegram (silent agents are worse\n # than a single edit-in-place heartbeat).\n long_running_notifications: true\n\n # Include detailed iteration/tool/status context in busy acknowledgments\n # and long-running heartbeats. When true, busy acks show \"iteration 21/60,\n # terminal, 10 min\" and the heartbeat shows \"⏳ Working — 12 min,\n # iteration 21/60, terminal\". When false (Telegram default), both stay\n # terse: \"Interrupting current task\" and \"⏳ Working — 12 min, terminal\".\n busy_ack_detail: true\n\n # What Enter does when Hermes is already busy (CLI and gateway platforms).\n # interrupt: Interrupt the current run and redirect Hermes (default)\n # queue: Queue your message for the next turn\n # steer: Inject your message mid-run via /steer, arriving at the agent\n # after the next tool call — no interrupt, no role violation.\n # Falls back to 'queue' if the agent isn't running yet or if\n # images are attached (steer only carries text).\n # Ctrl+C (or /stop in gateway) always interrupts regardless of this setting.\n # Toggle at runtime with /busy .\n busy_input_mode: interrupt\n\n # Background process notifications (gateway/messaging only).\n # Controls how chatty the process watcher is when you use\n # terminal(background=true, notify_on_complete=true) from Telegram/Discord/etc.\n # off: No watcher messages at all\n # result: Only the final completion message\n # error: Only the final message when exit code != 0\n # all: Running output updates + final message (default)\n background_process_notifications: all\n\n\n # Play terminal bell when agent finishes a response.\n # Useful for long-running tasks — your terminal will ding when the agent is done.\n # Works over SSH. Most terminals can be configured to flash the taskbar or play a sound.\n # true: Ring the terminal bell on each response\n # false: Silent (default)\n bell_on_complete: false\n\n # Show model reasoning/thinking before each response.\n # When enabled, a dim box shows the model's thought process above the response.\n # Toggle at runtime with /reasoning show or /reasoning hide.\n # true: Show the reasoning box\n # false: Hide reasoning (default)\n show_reasoning: false\n\n # Stream tokens to the terminal as they arrive instead of waiting for the\n # full response. The response box opens on first token and text appears\n # line-by-line. Tool calls are still captured silently.\n # true: Stream tokens as they arrive (default)\n # false: Wait for the full response before rendering\n streaming: true\n\n # Show [HH:MM] timestamps on user input and assistant response labels.\n # timestamps: false\n\n # ───────────────────────────────────────────────────────────────────────────\n # Skin / Theme\n # ───────────────────────────────────────────────────────────────────────────\n # Customize CLI visual appearance — banner colors, spinner faces, tool prefix,\n # response box label, and branding text. Change at runtime with /skin .\n #\n # Built-in skins:\n # default — Classic Hermes gold/kawaii\n # ares — Crimson/bronze war-god theme with spinner wings\n # mono — Clean grayscale monochrome\n # slate — Cool blue developer-focused\n # daylight — Bright light-mode theme\n # warm-lightmode — Warm paper-tone light-mode theme\n # poseidon — Sea-green/teal Olympian theme\n # sisyphus — Earthy stone-and-moss theme\n # charizard — Fiery orange dragon theme\n #\n # Custom skins: drop a YAML file in ~/.hermes/skins/.yaml\n # Schema (all fields optional, missing values inherit from default):\n #\n # name: my-theme\n # description: Short description\n # colors:\n # banner_border: \"#HEX\" # Panel border\n # banner_title: \"#HEX\" # Panel title\n # banner_accent: \"#HEX\" # Section headers (Available Tools, etc.)\n # banner_dim: \"#HEX\" # Dim/muted text\n # banner_text: \"#HEX\" # Body text (tool names, skill names)\n # ui_accent: \"#HEX\" # UI accent color\n # response_border: \"#HEX\" # Response box border color\n # spinner:\n # waiting_faces: [\"(⚔)\", \"(⛨)\"] # Faces shown while waiting\n # thinking_faces: [\"(⚔)\", \"(⌁)\"] # Faces shown while thinking\n # thinking_verbs: [\"forging\", \"plotting\"] # Verbs for spinner messages\n # wings: # Optional left/right spinner decorations\n # - [\"⟪⚔\", \"⚔⟫\"]\n # - [\"⟪▲\", \"▲⟫\"]\n # branding:\n # agent_name: \"My Agent\" # Banner title and branding\n # welcome: \"Welcome message\" # Shown at CLI startup\n # response_label: \" ⚔ Agent \" # Response box header label\n # prompt_symbol: \"⚔\" # Prompt symbol (bare token; renderers add trailing space)\n # tool_prefix: \"╎\" # Tool output line prefix (default: ┊)\n #\n skin: default\n\n# =============================================================================\n# Model Aliases — short names for /model command\n# =============================================================================\n# Map short aliases to exact (model, provider, base_url) tuples.\n# Used by /model tab completion and resolve_alias().\n# Aliases are checked BEFORE the models.dev catalog, so they can route\n# to endpoints not in the catalog (e.g. Ollama Cloud, local servers).\n#\n# model_aliases:\n# opus:\n# model: claude-opus-4-6\n# provider: anthropic\n# qwen:\n# model: \"qwen3.5:397b\"\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n# glm:\n# model: glm-4.7\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n\n# =============================================================================\n# Privacy\n# =============================================================================\n# privacy:\n# # Redact PII from the LLM context prompt.\n# # When true, phone numbers are stripped and user/chat IDs are replaced\n# # with deterministic hashes before being sent to the model.\n# # Names and usernames are NOT affected (user-chosen, publicly visible).\n# # Routing/delivery still uses the original values internally.\n# redact_pii: false\n\n# =============================================================================\n# Shell-script hooks\n# =============================================================================\n# Register shell scripts as plugin-hook callbacks. Each entry is executed as\n# a subprocess (shell=False, shlex.split) with a JSON payload on stdin. On\n# stdout the script may return JSON that either blocks the tool call or\n# injects context into the next LLM call.\n#\n# Valid events (mirror hermes_cli.plugins.VALID_HOOKS):\n# pre_tool_call, post_tool_call, pre_llm_call, post_llm_call,\n# pre_api_request, post_api_request, on_session_start, on_session_end,\n# on_session_finalize, on_session_reset, subagent_stop\n#\n# First-use consent: each (event, command) pair prompts once on a TTY, then\n# is persisted to ~/.hermes/shell-hooks-allowlist.json. Non-interactive\n# runs (gateway, cron) need --accept-hooks, HERMES_ACCEPT_HOOKS=1, or the\n# hooks_auto_accept key below.\n#\n# See website/docs/user-guide/features/hooks.md for the full JSON wire\n# protocol and worked examples.\n#\n# hooks:\n# pre_tool_call:\n# - matcher: \"terminal\"\n# command: \"~/.hermes/agent-hooks/block-rm-rf.sh\"\n# timeout: 10\n# post_tool_call:\n# - matcher: \"write_file|patch\"\n# command: \"~/.hermes/agent-hooks/auto-format.sh\"\n# pre_llm_call:\n# - command: \"~/.hermes/agent-hooks/inject-cwd-context.sh\"\n# subagent_stop:\n# - command: \"~/.hermes/agent-hooks/log-orchestration.sh\"\n#\n# hooks_auto_accept: false\n\n\n# =============================================================================\n# Update Behavior\n# =============================================================================\nupdates:\n # Create a full HERMES_HOME zip before every `hermes update`.\n # Backups land in ~/.hermes/backups/ and can be restored with `hermes import`.\n # Off by default because large homes can add minutes to every update.\n pre_update_backup: false\n\n # Number of pre-update backup zips to retain.\n backup_keep: 5\n\n # What non-interactive updates do with local source edits in the Hermes repo.\n # Interactive terminal updates always prompt before restoring the autostash.\n #\n # stash - auto-stash before pull, then auto-restore after success (default)\n # discard - drop the update-created stash after success; use only on managed\n # installs where local source edits should not persist\n non_interactive_local_changes: \"stash\"\n\n\n# =============================================================================\n# Web Dashboard\n# =============================================================================\n# OAuth gate configuration for `hermes dashboard --host `.\n# The bundled Nous Portal plugin reads these on startup; settings here are\n# the canonical surface. Each can be overridden by an environment variable:\n#\n# dashboard.oauth.client_id <- HERMES_DASHBOARD_OAUTH_CLIENT_ID\n# dashboard.oauth.portal_url <- HERMES_DASHBOARD_PORTAL_URL\n# dashboard.public_url <- HERMES_DASHBOARD_PUBLIC_URL\n#\n# Env wins when set to a non-empty value. This is what Fly.io's platform-\n# secret injection uses to push per-deploy client_ids without needing to\n# bake a config.yaml into the image. Empty env values are treated as unset\n# so a provisioned-but-not-populated secret can't shadow a valid entry here.\n#\n# Local dev / on-prem deploys should typically set these via config.yaml\n# (the ~/.hermes/.env file is reserved for API keys and secrets).\n#\n# dashboard:\n# oauth:\n# client_id: \"\" # agent:{instance_id}; Portal provisions this at deploy\n# portal_url: \"\" # blank → default https://portal.nousresearch.com\n#\n# # Force the absolute base URL the OAuth callback (and any other public\n# # URL the dashboard hands to external systems) is built from. Set this\n# # for deploys behind reverse proxies that don't reliably forward\n# # X-Forwarded-Host / X-Forwarded-Proto / X-Forwarded-Prefix (manual\n# # nginx setups, on-prem ingresses, custom-domain Fly deploys without\n# # full proxy header chains).\n# #\n# # When set, the value is the complete authority: scheme + host +\n# # optional path prefix (e.g. \"https://example.com/hermes\"). The OAuth\n# # callback URL becomes \"/auth/callback\" — X-Forwarded-Prefix\n# # is IGNORED on this code path because the operator has explicitly\n# # declared the public URL and we no longer need to guess.\n# #\n# # Leave empty to use the existing proxy-header reconstruction (the\n# # default — works on Fly.io out of the box).\n# #\n# # public_url: \"https://example.com/hermes\"\n"}, "f168631be0ce00086f6d3c7bfd9570599bb6da6c": {"t": 1785767901.1614857, "n": 65466, "h": "5572429109becad8", "c": "# Hermes Agent CLI Configuration\n# Copy this file to cli-config.yaml and customize as needed.\n# This file configures the CLI behavior. Environment variables in .env take precedence.\n\n# =============================================================================\n# Model Configuration\n# =============================================================================\nmodel:\n # Default model to use (can be overridden with --model flag)\n # Both \"default\" and \"model\" work as the key name here.\n default: \"anthropic/claude-opus-4.6\"\n \n # Inference provider selection:\n # \"auto\" - Auto-detect from credentials (default)\n # \"openrouter\" - OpenRouter (requires: OPENROUTER_API_KEY or OPENAI_API_KEY)\n # \"nous\" - Nous Portal OAuth (requires: hermes login)\n # \"nous-api\" - Nous Portal API key (requires: NOUS_API_KEY)\n # \"anthropic\" - Direct Anthropic API (requires: ANTHROPIC_API_KEY)\n # \"openai-codex\" - OpenAI Codex (requires: hermes auth)\n # \"copilot\" - GitHub Copilot / GitHub Models (requires: GITHUB_TOKEN)\n # \"gemini\" - Use Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n # \"zai\" - Use z.ai / ZhipuAI GLM models (requires: GLM_API_KEY)\n # \"kimi-coding\" - Kimi / Moonshot AI (requires: KIMI_API_KEY)\n # \"minimax\" - MiniMax global (requires: MINIMAX_API_KEY)\n # \"minimax-cn\" - MiniMax China (requires: MINIMAX_CN_API_KEY)\n # \"huggingface\" - Hugging Face Inference (requires: HF_TOKEN)\n # \"nvidia\" - NVIDIA NIM / build.nvidia.com (requires: NVIDIA_API_KEY)\n # \"xiaomi\" - Xiaomi MiMo (requires: XIAOMI_API_KEY)\n # \"arcee\" - Arcee AI Trinity models (requires: ARCEEAI_API_KEY)\n # \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY — https://ollama.com/settings)\n # \"kilocode\" - KiloCode gateway (requires: KILOCODE_API_KEY)\n # \"azure-foundry\" - Microsoft Foundry / Azure OpenAI (API key or Entra ID)\n # \"lmstudio\" - LM Studio local server (optional: LM_API_KEY, defaults to http://127.0.0.1:1234/v1)\n #\n # Local servers (LM Studio, Ollama, vLLM, llama.cpp):\n # \"custom\" - Any other OpenAI-compatible endpoint. Set base_url below.\n # Aliases: \"ollama\", \"vllm\", \"llamacpp\" all map to \"custom\".\n # LM Studio is first-class and uses provider: \"lmstudio\".\n # It works with both no-auth and auth-enabled server modes.\n #\n # Can also be overridden for a single invocation with the --provider flag.\n provider: \"auto\"\n \n # API configuration (falls back to OPENROUTER_API_KEY env var)\n # api_key: \"your-key-here\" # Uncomment to set here instead of .env\n base_url: \"https://openrouter.ai/api/v1\"\n\n # Azure Foundry keyless auth example:\n # provider: \"azure-foundry\"\n # base_url: \"https://.openai.azure.com/openai/v1\"\n # auth_mode: \"entra_id\" # DefaultAzureCredential: az login, managed identity, workload identity, etc.\n # default: \"gpt-4o\" # Deployment/model name\n # entra:\n # scope: \"https://ai.azure.com/.default\" # Optional; this is the default.\n\n # ── Token limits — two settings, easy to confuse ──────────────────────────\n #\n # context_length: TOTAL context window (input + output tokens combined).\n # Controls when Hermes compresses history and validates requests.\n # Leave unset — Hermes auto-detects the correct value from the provider.\n # Set manually only when auto-detection is wrong (e.g. a local server with\n # a custom num_ctx, or a proxy that doesn't expose /v1/models).\n #\n # context_length: 131072\n #\n # max_tokens: OUTPUT cap — maximum tokens the model may generate per response.\n # Unrelated to how long your conversation history can be.\n # The OpenAI-standard name \"max_tokens\" is a misnomer; Anthropic's native\n # API has since renamed it \"max_output_tokens\" for clarity.\n # Leave unset to use the model's native output ceiling (recommended).\n # Set only if you want to deliberately limit individual response length.\n #\n# max_tokens: 8192\n\n # ── Custom request headers (optional) ─────────────────────────────────────\n #\n # default_headers: extra HTTP headers sent on every request to an\n # OpenAI-compatible endpoint. User values take precedence over the\n # provider/SDK defaults, so this is the supported way to override the\n # OpenAI Python SDK's identifying headers (User-Agent: OpenAI/Python ...,\n # X-Stainless-*) when a custom provider sits behind a gateway/WAF that\n # rejects them — e.g. an upstream that returns \"502 Upstream access\n # forbidden\" for the SDK default User-Agent but accepts a plain one.\n # Applies on the OpenAI wire only (not native Anthropic / Bedrock).\n #\n # default_headers:\n # User-Agent: \"curl/8.7.1\"\n\n# Named provider overrides (optional)\n# Use this for per-provider request timeouts, non-stream stale timeouts,\n# and per-model exceptions.\n# Applies to the primary turn client on every api_mode (OpenAI-wire, native\n# Anthropic, and Anthropic-compatible providers), the fallback chain, and\n# client rebuilds during credential rotation. For OpenAI-wire chat\n# completions (streaming and non-streaming) the configured value is also\n# used as the per-request ``timeout=`` kwarg so it wins over the legacy\n# HERMES_API_TIMEOUT env var (which still applies when no config is set).\n# ``stale_timeout_seconds`` controls the non-streaming stale-call detector and\n# wins over the legacy HERMES_API_CALL_STALE_TIMEOUT env var. Leaving these\n# unset keeps the legacy defaults (HERMES_API_TIMEOUT=1800s,\n# HERMES_API_CALL_STALE_TIMEOUT=90s, native Anthropic 900s). The\n# implicit non-stream stale detector is auto-disabled for local endpoints\n# and can scale upward for very large contexts.\n#\n# Not currently wired for AWS Bedrock (bedrock_converse + AnthropicBedrock\n# SDK paths) — those use boto3 with its own timeout configuration.\n#\n# providers:\n# ollama-local:\n# request_timeout_seconds: 300 # Longer timeout for local cold-starts\n# stale_timeout_seconds: 900 # Explicitly re-enable stale detection on local endpoints\n# anthropic:\n# request_timeout_seconds: 30 # Fast-fail cloud requests\n# models:\n# claude-opus-4.6:\n# timeout_seconds: 600 # Longer timeout for extended-thinking Opus calls\n# openai-codex:\n# models:\n# gpt-5.4:\n# stale_timeout_seconds: 1800 # Longer non-stream stale timeout for slow large-context turns\n\n# =============================================================================\n# OpenRouter Provider Routing (only applies when using OpenRouter)\n# =============================================================================\n# Control how requests are routed across providers on OpenRouter.\n# See: https://openrouter.ai/docs/guides/routing/provider-selection\n#\n# provider_routing:\n# # Sort strategy: \"price\" (default), \"throughput\", or \"latency\"\n# # Append :nitro to model name for a shortcut to throughput sorting.\n# sort: \"throughput\"\n#\n# # Only allow these providers (provider slugs from OpenRouter)\n# # only: [\"anthropic\", \"google\"]\n#\n# # Skip these providers entirely\n# # ignore: [\"deepinfra\", \"fireworks\"]\n#\n# # Try providers in this order (overrides default load balancing)\n# # order: [\"anthropic\", \"google\", \"together\"]\n#\n# # Require providers to support all parameters in your request\n# # require_parameters: true\n#\n# # Data policy: \"allow\" (default) or \"deny\" to exclude providers that may store data\n# # data_collection: \"deny\"\n\n# =============================================================================\n# OpenRouter Response Caching (only applies when using OpenRouter)\n# =============================================================================\n# Cache identical API responses at the OpenRouter edge for free instant replays.\n# When enabled, identical requests (same model, messages, parameters) return\n# cached responses with zero billing. Separate from Anthropic prompt caching.\n# See: https://openrouter.ai/docs/guides/features/response-caching\n#\n# openrouter:\n# response_cache: true # Enable response caching (default: true)\n# response_cache_ttl: 300 # Cache TTL in seconds, 1-86400 (default: 300)\n\n# =============================================================================\n# Git Worktree Isolation\n# =============================================================================\n# When enabled, each CLI session creates an isolated git worktree so multiple\n# agents can work on the same repo concurrently without file collisions.\n# Equivalent to always passing --worktree / -w on the command line.\n#\n# worktree: true # Always create a worktree when in a git repo\n# worktree: false # Default — only create when -w flag is passed\n#\n# By default a new worktree branches from the freshly-fetched remote tip\n# (the current branch's upstream, else the remote's default branch) so it\n# starts current with the project instead of from the local clone's\n# (possibly stale) HEAD. Set worktree_sync: false to branch from local HEAD\n# instead — useful when offline or when you deliberately want the clone's\n# exact current state as the base.\n#\n# worktree_sync: true # Default — branch from the fetched remote tip\n# worktree_sync: false # Branch from local HEAD (offline / pinned base)\n\n# =============================================================================\n# Terminal Tool Configuration\n# =============================================================================\n# Choose ONE of the following terminal configurations by uncommenting it.\n# The terminal tool executes commands in the specified environment.\n\n# -----------------------------------------------------------------------------\n# OPTION 1: Local execution (default)\n# Commands run directly on your machine in the current directory\n# -----------------------------------------------------------------------------\n# Working directory behavior:\n# - CLI (`hermes` command): Uses \".\" (current directory where you run hermes)\n# - Gateway/messaging/cron: Uses terminal.cwd here; legacy .env cwd values are deprecated\nterminal:\n backend: \"local\"\n cwd: \".\" # For local backend: \".\" = current directory. Ignored for remote backends unless a backend documents otherwise.\n timeout: 180\n # HOME policy for tool subprocesses:\n # auto - default: host uses your real HOME; containers use HERMES_HOME/home\n # real - force your real OS-user HOME\n # profile - force HERMES_HOME/home for strict per-profile CLI config isolation\n home_mode: \"auto\"\n docker_mount_cwd_to_workspace: false # SECURITY: off by default. Opt in to mount the launch cwd into Docker /workspace.\n lifetime_seconds: 300\n # sudo_password: \"hunter2\" # Optional: pipe a sudo password via sudo -S. SECURITY WARNING: plaintext.\n # sudo_password: \"\" # Explicit empty password: try empty and never open the interactive sudo prompt.\n\n# -----------------------------------------------------------------------------\n# OPTION 2: SSH remote execution\n# Commands run on a remote server - agent code stays local (sandboxed)\n# Great for: keeping agent isolated from its own code, using powerful remote hardware\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"ssh\"\n# cwd: \"/home/myuser/project\" # Path on the REMOTE server\n# timeout: 180\n# lifetime_seconds: 300\n# ssh_host: \"my-server.example.com\"\n# ssh_user: \"myuser\"\n# ssh_port: 22\n# ssh_key: \"~/.ssh/id_rsa\" # Optional - uses ssh-agent if not specified\n\n# -----------------------------------------------------------------------------\n# OPTION 3: Docker container\n# Commands run in an isolated Docker container\n# Great for: reproducible environments, testing, isolation\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"docker\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /)\n# timeout: 180\n# lifetime_seconds: 300\n# docker_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# docker_mount_cwd_to_workspace: true # Explicit opt-in: mount your launch cwd into /workspace\n# # Optional: run the container as your host user's uid:gid so files written\n# # into bind-mounted dirs are owned by you, not root. Drops SETUID/SETGID\n# # caps too since no gosu privilege drop is needed. Leave off if your\n# # chosen docker_image expects to start as root.\n# docker_run_as_host_user: true\n# # Optional: explicitly forward selected env vars into Docker.\n# # These values come from your current shell first, then ~/.hermes/.env.\n# # Warning: anything forwarded here is visible to commands run in the container.\n# docker_forward_env:\n# - \"GITHUB_TOKEN\"\n# - \"NPM_TOKEN\"\n# # Optional: extra flags passed verbatim to docker run (appended after security defaults).\n# # Useful for adding capabilities (e.g. apt installs needing SETUID) or custom options.\n# # Example: add a Linux capability not included by default\n# # docker_extra_args:\n# # - \"--cap-add\"\n# # - \"SETUID\"\n\n# -----------------------------------------------------------------------------\n# OPTION 4: Singularity/Apptainer container\n# Commands run in a Singularity container (common in HPC environments)\n# Great for: HPC clusters, shared compute environments\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"singularity\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# singularity_image: \"docker://nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 5: Modal cloud execution\n# Commands run on Modal's cloud infrastructure\n# Great for: GPU access, scalable compute, serverless execution\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"modal\"\n# cwd: \"/workspace\" # Path INSIDE the sandbox (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# modal_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 6: Daytona cloud execution\n# Commands run in Daytona cloud sandboxes\n# Great for: Cloud dev environments, persistent workspaces, team collaboration\n# Requires: pip install daytona, DAYTONA_API_KEY env var\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"daytona\"\n# cwd: \"~\"\n# timeout: 180\n# lifetime_seconds: 300\n# daytona_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# container_disk: 10240 # Daytona max is 10GB per sandbox\n\n#\n# --- Container resource limits (docker, singularity, modal, daytona -- ignored for local/ssh) ---\n# These settings apply to all container backends. They control the resources\n# allocated to the sandbox and whether its filesystem persists across sessions.\n container_cpu: 1 # CPU cores\n container_memory: 5120 # Memory in MB (5120 = 5GB)\n container_disk: 51200 # Disk in MB (51200 = 50GB)\n container_persistent: true # Persist filesystem across sessions (false = ephemeral)\n\n# -----------------------------------------------------------------------------\n# SUDO SUPPORT (works with ALL backends above)\n# -----------------------------------------------------------------------------\n# Add sudo_password to any terminal config above to enable sudo commands.\n# The password is piped via `sudo -S`. Works with local, ssh, docker, etc.\n#\n# SECURITY WARNING: Password stored in plaintext!\n#\n# INTERACTIVE PROMPT: If sudo_password is unset and the CLI is running,\n# you'll be prompted to enter your password when sudo is needed:\n# - 45-second timeout (auto-skips if no input)\n# - Press Enter to skip (command fails gracefully)\n# - Password is hidden while typing\n# - Password is cached for the session\n#\n# EMPTY PASSWORDS: Setting sudo_password to an explicit empty string is different\n# from leaving it unset. Hermes will try an empty password via `sudo -S` and\n# will not open the interactive prompt. This is useful for passwordless sudo,\n# Touch ID sudo setups, and environments where prompting is just noise.\n#\n# ALTERNATIVES:\n# - SSH backend: Configure passwordless sudo on the remote server\n# - Containers: Run as root inside the container (no sudo needed)\n# - Local: Configure /etc/sudoers for specific commands\n#\n# Example (add to your terminal section):\n# sudo_password: \"your-password-here\"\n\n# =============================================================================\n# Security Scanning (tirith)\n# =============================================================================\n# Optional pre-exec command security scanning via tirith.\n# Detects homograph URLs, pipe-to-shell, terminal injection, env manipulation.\n# Install: brew install sheeki03/tap/tirith\n# Docs: https://github.com/sheeki03/tirith\n#\n# security:\n# tirith_enabled: true # Enable/disable tirith scanning\n# tirith_path: \"tirith\" # Path to tirith binary (supports ~ expansion)\n# tirith_timeout: 5 # Scan timeout in seconds\n# tirith_fail_open: true # Allow commands if tirith unavailable\n\n# =============================================================================\n# Browser Tool Configuration\n# =============================================================================\nbrowser:\n # Inactivity timeout in seconds - browser sessions are automatically closed\n # after this period of no activity between agent loops (default: 120 = 2 minutes)\n inactivity_timeout: 120\n\n# =============================================================================\n# Tool Loop Guardrails\n# =============================================================================\n# Soft warnings are enabled by default. They append guidance to repeated failed\n# or non-progressing tool results but still let the tool execute. Hard stops are\n# opt-in circuit breakers for autonomous/cron sessions where stopping a loop is\n# preferable to spending the full iteration budget.\ntool_loop_guardrails:\n warnings_enabled: true\n hard_stop_enabled: false\n warn_after:\n exact_failure: 2\n same_tool_failure: 3\n idempotent_no_progress: 2\n hard_stop_after:\n exact_failure: 5\n same_tool_failure: 8\n idempotent_no_progress: 5\n\n# =============================================================================\n# Context Compression (Auto-shrinks long conversations)\n# =============================================================================\n# When conversation approaches model's context limit, middle turns are\n# automatically summarized to free up space while preserving important context.\n#\n# HOW IT WORKS:\n# 1. Tracks actual token usage from API responses (not estimates)\n# 2. When prompt_tokens >= threshold% of model's context_length, triggers compression\n# 3. Protects first 3 turns (system prompt, initial request, first response)\n# 4. Protects last N turns (default 20 messages = ~10 full turns of recent context)\n# 5. Summarizes middle turns using a fast/cheap model\n# 6. Inserts summary as a user message, continues conversation seamlessly\n#\n# Post-compression tail budget is target_ratio × threshold × context_length:\n# 200K context, threshold 0.50, ratio 0.20 → 20K tokens of recent tail preserved\n# 1M context, threshold 0.50, ratio 0.20 → 100K tokens of recent tail preserved\n#\ncompression:\n # Enable automatic context compression (default: true)\n # Set to false if you prefer to manage context manually or want errors on overflow\n enabled: true\n \n # Trigger compression at this % of model's context limit (default: 0.50 = 50%)\n # Lower values = more aggressive compression, higher values = compress later\n threshold: 0.50\n \n # Fraction of the threshold to preserve as recent tail (default: 0.20 = 20%)\n # e.g. 20% of 50% threshold = 10% of total context kept as recent messages.\n # Summary output is separately capped at 12K tokens (Gemini output limit).\n # Range: 0.10 - 0.80\n target_ratio: 0.20\n\n # Number of most-recent messages to always preserve (default: 20 ≈ 10 full turns)\n # Higher values keep more recent conversation intact at the cost of more aggressive\n # compression of older turns.\n protect_last_n: 20\n\n # Number of non-system messages to protect at the head of the transcript, in\n # ADDITION to the system prompt (which is always implicitly protected).\n # Head messages are NEVER summarized — they survive every compression\n # indefinitely. This gives stable early context for short/medium sessions,\n # but in long-running sessions that rely on rolling compaction the pinned\n # opening turns may not match how you want the session framed over time.\n # Set to 0 to preserve ONLY the system prompt (plus the rolling summary\n # and recent tail) — the cleanest configuration for long-running sessions.\n # Default 3 preserves the system prompt plus the first three non-system\n # head messages, matching the pre-feature behaviour.\n protect_first_n: 3\n\n # To pin a specific model/provider for compression summaries, use the\n # auxiliary section below (auxiliary.compression.provider / model).\n\n# =============================================================================\n# Anthropic prompt caching TTL\n# =============================================================================\n# When prompt caching is active (Claude via OpenRouter or native Anthropic),\n# Anthropic supports two TTL tiers for cached prefixes: \"5m\" (default) and\n# \"1h\". Other values are ignored and \"5m\" is used.\n#\nprompt_caching:\n cache_ttl: \"5m\" # use \"1h\" for long sessions with pauses between turns\n\n# =============================================================================\n# Auxiliary Models (Advanced — Experimental)\n# =============================================================================\n# Hermes uses lightweight \"auxiliary\" models for side tasks: image analysis,\n# browser screenshot analysis, web page summarization, TTS audio-tag insertion,\n# and context compression.\n#\n# By default these use Gemini Flash via OpenRouter or Nous Portal and are\n# auto-detected from your credentials. You do NOT need to change anything\n# here for normal usage.\n#\n# WARNING: Overriding these with providers other than OpenRouter or Nous Portal\n# is EXPERIMENTAL and may not work. Not all models/providers support vision,\n# produce usable summaries, or accept the same API format. Change at your own\n# risk — if things break, reset to \"auto\" / empty values.\n#\n# Each task has its own provider + model pair so you can mix providers.\n# For example: OpenRouter for vision (needs multimodal), but your main\n# local endpoint for compression (just needs text).\n#\n# Provider options:\n# \"auto\" - Best available: OpenRouter → Nous Portal → main endpoint (default)\n# \"openrouter\" - Force OpenRouter (requires OPENROUTER_API_KEY)\n# \"nous\" - Force Nous Portal (requires: hermes login)\n# \"gemini\" - Force Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n# \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY)\n# \"codex\" - Force Codex OAuth (requires: hermes model → Codex).\n# Uses gpt-5.3-codex which supports vision.\n# \"main\" - Use your custom endpoint (OPENAI_BASE_URL + OPENAI_API_KEY).\n# Works with OpenAI API, local models, or any OpenAI-compatible\n# endpoint. Also falls back to Codex OAuth and API-key providers.\n#\n# Model: leave empty to use the provider's default. When empty, OpenRouter\n# uses \"google/gemini-3-flash-preview\" and Nous uses \"gemini-3-flash\".\n# Other providers pick a sensible default automatically.\n#\n# auxiliary:\n# # Image analysis: vision_analyze tool + browser screenshots\n# vision:\n# provider: \"auto\"\n# model: \"\" # e.g. \"google/gemini-2.5-flash\", \"openai/gpt-4o\"\n# timeout: 30 # LLM API call timeout (seconds)\n# download_timeout: 30 # Image HTTP download timeout (seconds)\n# # Increase for slow connections or self-hosted image servers\n#\n# # Web page scraping / summarization + browser page text extraction\n# web_extract:\n# provider: \"auto\"\n# model: \"\"\n#\n# # Gemini 3.1 TTS hidden audio-tag insertion\n# tts_audio_tags:\n# provider: \"auto\" # empty model = your main chat model\n# model: \"\"\n# timeout: 30\n#\n# # Session search — summarizes matching past sessions\n# session_search:\n# provider: \"auto\"\n# model: \"\"\n# timeout: 30\n# max_concurrency: 3 # Limit parallel summaries to reduce request-burst 429s\n# extra_body: {} # Provider-specific OpenAI-compatible request fields\n# # Example for providers that support request-body\n# # reasoning controls:\n# # extra_body:\n# # enable_thinking: false\n# # Some vLLM/Qwen deployments expect this nested:\n# # extra_body:\n# # chat_template_kwargs:\n# # enable_thinking: false\n\n# =============================================================================\n# Persistent Memory\n# =============================================================================\n# Bounded curated memory injected into the system prompt every session.\n# Two stores: MEMORY.md (agent's notes) and USER.md (user profile).\n# Character limits keep the memory small and focused. The agent manages\n# pruning -- when at the limit, it must consolidate or replace entries.\n# Disabled by default in batch_runner.\n#\nmemory:\n # Agent's personal notes: environment facts, conventions, things learned\n memory_enabled: true\n \n # User profile: preferences, communication style, expectations\n user_profile_enabled: true\n \n # Character limits (~2.75 chars per token, model-independent)\n memory_char_limit: 2200 # ~800 tokens\n user_char_limit: 1375 # ~500 tokens\n\n # Periodic memory nudge: remind the agent to consider saving memories\n # every N user turns. Set to 0 to disable. Only active when memory is enabled.\n nudge_interval: 10 # Nudge every 10 user turns (0 = disabled)\n\n # Memory flush: give the agent one turn to save memories before context is\n # lost (compression, /new, /reset, exit). Set to 0 to disable.\n # For exit/reset, only fires if the session had at least this many user turns.\n flush_min_turns: 6 # Min user turns to trigger flush on exit/reset (0 = disabled)\n\n# =============================================================================\n# Session Reset Policy (Messaging Platforms)\n# =============================================================================\n# Controls when messaging sessions (Telegram, Discord, WhatsApp, Slack) are\n# automatically cleared. Without resets, conversation context grows indefinitely\n# which increases API costs with every message.\n#\n# When a reset triggers, the agent first saves important information to its\n# persistent memory — but the conversation context is wiped. The agent starts\n# fresh but retains learned facts via its memory system.\n#\n# Users can always manually reset with /reset or /new in chat.\n#\n# Modes:\n# \"both\" - Reset on EITHER inactivity timeout or daily boundary (recommended)\n# \"idle\" - Reset only after N minutes of inactivity\n# \"daily\" - Reset only at a fixed hour each day\n# \"none\" - Never auto-reset; context lives until /reset or compression kicks in\n#\n# When a reset triggers, the agent gets one turn to save important memories and\n# skills before the context is wiped. Persistent memory carries across sessions.\n#\nsession_reset:\n mode: both # \"both\", \"idle\", \"daily\", or \"none\"\n idle_minutes: 1440 # Inactivity timeout in minutes (default: 1440 = 24 hours)\n at_hour: 4 # Daily reset hour, 0-23 local time (default: 4 AM)\n\n# Maximum number of simultaneously active chat sessions across CLI, TUI,\n# dashboard chat, and messaging gateway. Set to null, 0, or omit to allow\n# unlimited concurrent sessions. When the limit is reached, new sessions get a\n# clean error while existing active sessions keep their normal behavior. This\n# top-level key takes precedence over gateway.max_concurrent_sessions. The cap\n# is a best-effort single-host/profile runtime guard; Hermes fails open if the\n# local runtime lease registry cannot be read or locked.\nmax_concurrent_sessions: null\n\n# When true, group/channel chats use one session per participant when the platform\n# provides a user ID. This is the secure default and prevents users in the same\n# room from sharing context, interrupts, and token costs. Set false only if you\n# explicitly want one shared \"room brain\" per group/channel.\ngroup_sessions_per_user: true\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Gateway Streaming\n# ─────────────────────────────────────────────────────────────────────────────\n# Stream tokens to messaging platforms in real-time. The bot sends a message\n# on first token, then progressively edits it as more tokens arrive.\n# Disabled by default — enable to try the streaming UX on Telegram/Discord/Slack.\n# For Telegram, partial edits are sent as plain text and only the final edit uses MarkdownV2.\nstreaming:\n enabled: false\n # transport: edit # \"edit\" = progressive editMessageText\n # edit_interval: 0.3 # seconds between message edits\n # buffer_threshold: 40 # chars before forcing an edit flush\n # cursor: \" ▉\" # cursor shown during streaming\n\n# =============================================================================\n# Skills Configuration\n# =============================================================================\n# Skills are reusable procedures the agent can load and follow. The agent can\n# also create new skills after completing complex tasks.\n#\nskills:\n # Nudge the agent to create skills after complex tasks.\n # Every N tool-calling iterations, remind the model to consider saving a skill.\n # Set to 0 to disable.\n creation_nudge_interval: 15\n\n # External skill directories — share skills across tools/agents without\n # copying them into ~/.hermes/skills/. Each path is expanded (~ and ${VAR})\n # and resolved to an absolute path. External dirs are read-only: skill\n # creation always writes to ~/.hermes/skills/. Local skills take precedence\n # when names collide.\n # external_dirs:\n # - ~/.agents/skills\n # - /home/shared/team-skills\n\n# =============================================================================\n# Agent Behavior\n# =============================================================================\nagent:\n # Maximum tool-calling iterations per conversation\n # Higher = more room for complex tasks, but costs more tokens\n # Recommended: 20-30 for focused tasks, 50-100 for open exploration\n max_turns: 60\n\n # Inactivity timeout for gateway agent runs (seconds, 0 = unlimited).\n # The agent can run indefinitely when actively calling tools or receiving\n # API responses. Only fires after the agent has been idle for this duration.\n # gateway_timeout: 1800\n\n # Staged warning: send a warning before escalating to full timeout.\n # Fires once per run when inactivity reaches this threshold (seconds).\n # Set to 0 to disable the warning.\n # gateway_timeout_warning: 900\n\n # Graceful drain timeout for gateway stop/restart (seconds).\n # The gateway stops accepting new work, waits for in-flight agents to\n # finish, then interrupts anything still running after this timeout.\n # 0 = no drain, interrupt immediately.\n # restart_drain_timeout: 60\n\n # Max app-level retry attempts for API errors (connection drops, provider\n # timeouts, 5xx, etc.) before the agent surfaces the failure. Lower this\n # to 1 if you use fallback providers and want fast failover on flaky\n # primaries (default 3). The OpenAI SDK does its own low-level retries\n # underneath this wrapper — this is the Hermes-level loop.\n # api_max_retries: 3\n\n # After the agent edits code without fresh passing verification, nudge it to\n # verify before finishing. The default \"auto\" enables it on interactive\n # coding surfaces (CLI, TUI, desktop) and programmatic callers, and disables\n # it on conversational messaging surfaces (Telegram, Discord, etc.) where the\n # verification summary would reach a human as chat noise. Set true or false to\n # force it on or off; the HERMES_VERIFY_ON_STOP env var (1/0) takes precedence.\n # verify_on_stop: auto\n\n # Enable verbose logging\n verbose: false\n \n # Reasoning effort level (OpenRouter and Nous Portal)\n # Controls how much \"thinking\" the model does before responding.\n # Options: \"xhigh\" (max), \"high\", \"medium\", \"low\", \"minimal\", \"none\" (disable)\n reasoning_effort: \"medium\"\n \n # Predefined personalities (use with /personality command)\n personalities:\n helpful: \"You are a helpful, friendly AI assistant.\"\n concise: \"You are a concise assistant. Keep responses brief and to the point.\"\n technical: \"You are a technical expert. Provide detailed, accurate technical information.\"\n creative: \"You are a creative assistant. Think outside the box and offer innovative solutions.\"\n teacher: \"You are a patient teacher. Explain concepts clearly with examples.\"\n kawaii: \"You are a kawaii assistant! Use cute expressions like (◕‿◕), ★, ♪, and ~! Add sparkles and be super enthusiastic about everything! Every response should feel warm and adorable desu~! ヽ(>∀<☆)ノ\"\n catgirl: \"You are Neko-chan, an anime catgirl AI assistant, nya~! Add 'nya' and cat-like expressions to your speech. Use kaomoji like (=^・ω・^=) and ฅ^•ﻌ•^ฅ. Be playful and curious like a cat, nya~!\"\n pirate: \"Arrr! Ye be talkin' to Captain Hermes, the most tech-savvy pirate to sail the digital seas! Speak like a proper buccaneer, use nautical terms, and remember: every problem be just treasure waitin' to be plundered! Yo ho ho!\"\n shakespeare: \"Hark! Thou speakest with an assistant most versed in the bardic arts. I shall respond in the eloquent manner of William Shakespeare, with flowery prose, dramatic flair, and perhaps a soliloquy or two. What light through yonder terminal breaks?\"\n surfer: \"Duuude! You're chatting with the chillest AI on the web, bro! Everything's gonna be totally rad. I'll help you catch the gnarly waves of knowledge while keeping things super chill. Cowabunga! 🤙\"\n noir: \"The rain hammered against the terminal like regrets on a guilty conscience. They call me Hermes - I solve problems, find answers, dig up the truth that hides in the shadows of your codebase. In this city of silicon and secrets, everyone's got something to hide. What's your story, pal?\"\n uwu: \"hewwo! i'm your fwiendwy assistant uwu~ i wiww twy my best to hewp you! *nuzzles your code* OwO what's this? wet me take a wook! i pwomise to be vewy hewpful >w<\"\n philosopher: \"Greetings, seeker of wisdom. I am an assistant who contemplates the deeper meaning behind every query. Let us examine not just the 'how' but the 'why' of your questions. Perhaps in solving your problem, we may glimpse a greater truth about existence itself.\"\n hype: \"YOOO LET'S GOOOO!!! 🔥🔥🔥 I am SO PUMPED to help you today! Every question is AMAZING and we're gonna CRUSH IT together! This is gonna be LEGENDARY! ARE YOU READY?! LET'S DO THIS! 💪😤🚀\"\n\n# =============================================================================\n# Toolsets\n# =============================================================================\n# Control which tools the agent has access to.\n# Use `hermes tools` to interactively enable/disable tools per platform.\n\n# =============================================================================\n# Platform Toolsets (per-platform tool configuration)\n# =============================================================================\n# Override which toolsets are available on each platform.\n# If a platform isn't listed here, its built-in default is used.\n#\n# You can use EITHER:\n# - A preset like \"hermes-cli\" or \"hermes-telegram\" (curated tool set)\n# - A list of individual toolsets to compose your own (see list below)\n#\n# Supported platform keys: cli, telegram, discord, whatsapp, slack, qqbot, teams, google_chat\n#\n# Examples:\n#\n# # Use presets (same as defaults):\n# platform_toolsets:\n# cli: [hermes-cli]\n# telegram: [hermes-telegram]\n#\n# # Custom: give Telegram only web + terminal + file + planning:\n# platform_toolsets:\n# telegram: [web, terminal, file, todo]\n#\n# # Custom: CLI without browser or image gen:\n# platform_toolsets:\n# cli: [web, terminal, file, skills, todo, tts, cronjob]\n#\n# # Restrictive: Discord gets read-only tools only:\n# platform_toolsets:\n# discord: [web, vision, skills, todo]\n#\n# If not set, defaults are:\n# cli: hermes-cli (everything + cronjob management)\n# telegram: hermes-telegram (terminal, file, web, vision, image, tts, browser, skills, todo, cronjob, messaging)\n# discord: hermes-discord (same as telegram)\n# whatsapp: hermes-whatsapp (same as telegram)\n# slack: hermes-slack (same as telegram)\n# signal: hermes-signal (same as telegram)\n# homeassistant: hermes-homeassistant (same as telegram)\n# qqbot: hermes-qqbot (same as telegram)\n# teams: hermes-teams (same as telegram)\n# google_chat: hermes-google_chat (same as telegram)\n#\nplatform_toolsets:\n cli: [hermes-cli]\n telegram: [hermes-telegram]\n discord: [hermes-discord]\n whatsapp: [hermes-whatsapp]\n slack: [hermes-slack]\n signal: [hermes-signal]\n homeassistant: [hermes-homeassistant]\n qqbot: [hermes-qqbot]\n yuanbao: [hermes-yuanbao]\n teams: [hermes-teams]\n google_chat: [hermes-google_chat]\n\n# =============================================================================\n# Gateway Platform Settings\n# =============================================================================\n# Optional per-platform messaging settings.\n# Platform-specific knobs live under `extra`.\n#\n# platforms:\n# telegram:\n# reply_to_mode: \"first\" # off | first | all\n# # guest_mode lets explicit @mentions from non-allowlisted groups through.\n# # Default false; ordinary messages, replies, and regex wake words stay blocked.\n# guest_mode: false\n# # allowed_chats: [\"-1001234567890\"]\n# extra:\n# disable_link_previews: false # Set true to suppress Telegram URL previews in bot messages\n# rich_messages: false # Bot API 10.1 rich messages (tables/task lists/details/math); default false for copyable legacy MarkdownV2, set true to opt in\n# rich_drafts: false # Experimental rich draft previews during Telegram DM streaming; default false because Telegram Desktop/macOS can visually overlay draft frames\n# command_menu:\n# # Telegram allows up to 100 BotCommands; Hermes defaults to 60 so\n# # all built-in commands plus common skill commands stay visible\n# # while remaining under Telegram's payload-size limit. Clamped 1..100.\n# max_commands: 60\n# # prepend = user priority first, then Hermes defaults\n# # append = Hermes defaults first, then user priority\n# # replace = only the list below defines priority\n# priority_mode: prepend\n# priority:\n# - my_plugin_command\n#\n# Discord-specific settings (config.yaml top-level, not under platforms:):\n#\n# discord:\n# require_mention: true # Require @mention in server channels (default: true)\n# auto_thread: true # Auto-create thread on @mention (default: true)\n# free_response_channels: \"\" # Channel IDs where no mention is needed\n# reactions: true # Show processing reactions (default: true)\n# history_backfill: true # Recover missed channel messages on mention (default: true)\n# history_backfill_limit: 50 # Max messages to scan backwards (default: 50)\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Available toolsets (use these names in platform_toolsets or the toolsets list)\n#\n# Run `hermes chat --list-toolsets` to see all toolsets and their tools.\n# Run `hermes chat --list-tools` to see every individual tool with descriptions.\n# ─────────────────────────────────────────────────────────────────────────────\n#\n# INDIVIDUAL TOOLSETS (compose your own):\n# web - web_search, web_extract\n# search - web_search only (no scraping)\n# terminal - terminal, process\n# file - read_file, write_file, patch, search\n# browser - browser_navigate, browser_snapshot, browser_click, browser_type,\n# browser_scroll, browser_back, browser_press,\n# browser_get_images, browser_vision (requires BROWSERBASE_API_KEY)\n# vision - vision_analyze (requires OPENROUTER_API_KEY)\n# image_gen - image_generate (requires FAL_KEY)\n# skills - skills_list, skill_view\n# skills_hub - skill_hub (search/install/manage from online registries — user-driven only)\n# moa - mixture_of_agents (requires OPENROUTER_API_KEY)\n# todo - todo (in-memory task planning, no deps)\n# tts - text_to_speech (Edge TTS free, or ELEVENLABS/OPENAI/MINIMAX/MISTRAL key)\n# cronjob - cronjob (create/list/update/pause/resume/run/remove scheduled tasks)\n#\n# PRESETS (curated bundles):\n# hermes-cli - All of the above except send_message\n# hermes-telegram - terminal, file, web, vision, image_gen, tts, browser,\n# skills, todo, cronjob, send_message\n# hermes-discord - Same as hermes-telegram\n# hermes-whatsapp - Same as hermes-telegram\n# hermes-slack - Same as hermes-telegram\n#\n# COMPOSITE:\n# debugging - terminal + web + file\n# safe - web + vision + moa (no terminal access)\n# all - Everything available\n#\n# web - Web search and content extraction (web_search, web_extract)\n# search - Web search only, no scraping (web_search)\n# terminal - Command execution and process management (terminal, process)\n# file - File operations: read, write, patch, search\n# browser - Full browser automation (navigate, click, type, screenshot, etc.)\n# vision - Image analysis (vision_analyze)\n# image_gen - Image generation with FLUX (image_generate)\n# skills - Load skill documents (skills_list, skill_view)\n# moa - Mixture of Agents reasoning (mixture_of_agents)\n# todo - Task planning and tracking for multi-step work\n# memory - Persistent memory across sessions (personal notes + user profile)\n# session_search - Search and recall past conversations (FTS5 + Gemini Flash summarization)\n# tts - Text-to-speech (Edge TTS free, ElevenLabs, OpenAI, MiniMax, Mistral)\n# cronjob - Schedule and manage automated tasks (CLI-only)\n#\n# Composite toolsets:\n# debugging - terminal + web + file (for troubleshooting)\n# safe - web + vision + moa (no terminal access)\n\n# NOTE: The top-level \"toolsets\" key is deprecated and ignored.\n# Tool configuration is managed per-platform via platform_toolsets above.\n# Use `hermes tools` to configure interactively, or edit platform_toolsets directly.\n#\n# CLI override: hermes chat --toolsets terminal,web,file\n\n# =============================================================================\n# MCP (Model Context Protocol) Servers\n# =============================================================================\n# Connect to external MCP servers to add tools from the MCP ecosystem.\n# Each server's tools are automatically discovered and registered.\n# See website/docs/user-guide/features/mcp.md for full documentation.\n#\n# Stdio servers (spawn a subprocess):\n# command: the executable to run\n# args: command-line arguments\n# env: environment variables (only these + safe defaults passed to subprocess)\n#\n# HTTP servers (connect to a URL):\n# url: the MCP server endpoint\n# headers: HTTP headers (e.g., for authentication)\n#\n# Optional per-server settings:\n# timeout: tool call timeout in seconds (default: 120)\n# connect_timeout: initial connection timeout (default: 60)\n# keepalive_interval: liveness ping cadence in seconds (default: 180).\n# Lower it below the server's session TTL for servers that expire idle\n# sessions quickly (e.g. Unreal Engine editor MCP, ~15s), otherwise idle\n# tool calls hit an expired session and pay a slow reconnect. Floored at 5s.\n#\n# mcp_servers:\n# time:\n# command: uvx\n# args: [\"mcp-server-time\"]\n# filesystem:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-filesystem\", \"/home/user\"]\n# notion:\n# url: https://mcp.notion.com/mcp\n# github:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-github\"]\n# env:\n# GITHUB_PERSONAL_ACCESS_TOKEN: \"ghp_...\"\n#\n# Sampling (server-initiated LLM requests) — enabled by default.\n# Per-server config under the 'sampling' key:\n# analysis:\n# command: npx\n# args: [\"-y\", \"analysis-server\"]\n# sampling:\n# enabled: true # default: true\n# model: \"gemini-3-flash\" # override model (optional)\n# max_tokens_cap: 4096 # max tokens per request\n# timeout: 30 # LLM call timeout (seconds)\n# max_rpm: 10 # max requests per minute\n# allowed_models: [] # model whitelist (empty = all)\n# max_tool_rounds: 5 # tool loop limit (0 = disable)\n# log_level: \"info\" # audit verbosity\n\n# =============================================================================\n# Text-to-Speech\n# =============================================================================\n# TTS defaults to Edge TTS unless changed in ~/.hermes/config.yaml.\n# Gemini TTS supports persona/director prompt files, and Gemini 3.1 Flash TTS\n# can use a hidden auxiliary rewrite pass to insert expressive square-bracket\n# audio tags into the TTS script without showing tags in chat.\n#\n# tts:\n# provider: \"gemini\"\n# gemini:\n# model: \"gemini-3.1-flash-tts-preview\"\n# voice: \"Kore\"\n# audio_tags: false\n# persona_prompt_file: \"\" # e.g. ~/.hermes/tts/radio-host.md\n\n# =============================================================================\n# Voice Transcription (Speech-to-Text)\n# =============================================================================\n# Automatically transcribe voice messages on messaging platforms.\n# Providers: local (free, faster-whisper) | groq (free tier) | openai (Whisper API) | mistral (Voxtral Transcribe)\n# Set the corresponding API key in .env: GROQ_API_KEY, OPENAI_API_KEY, or MISTRAL_API_KEY.\nstt:\n enabled: true\n # provider: \"local\" # auto-detected if omitted\n local:\n model: \"base\" # tiny | base | small | medium | large-v3 | turbo\n # language: \"\" # auto-detect; set to \"en\", \"es\", \"fr\", etc. to force\n openai:\n model: \"whisper-1\" # whisper-1 | gpt-4o-mini-transcribe | gpt-4o-transcribe\n # mistral:\n # model: \"voxtral-mini-latest\" # voxtral-mini-latest | voxtral-mini-2602\n\n# =============================================================================\n# Response Pacing (Messaging Platforms)\n# =============================================================================\n# Add human-like delays between message chunks.\n# human_delay:\n# mode: \"off\" # \"off\" | \"natural\" | \"custom\"\n# min_ms: 800 # Min delay (custom mode only)\n# max_ms: 2500 # Max delay (custom mode only)\n\n# =============================================================================\n# Session Logging\n# =============================================================================\n# Session trajectories are automatically saved to logs/ directory.\n# Each session creates: logs/session_YYYYMMDD_HHMMSS_UUID.json\n#\n# The session ID is displayed in the welcome banner for easy reference.\n# Logs contain full conversation history in trajectory format:\n# - System prompt, user messages, assistant responses\n# - Tool calls with inputs/outputs\n# - Timestamps for debugging\n#\n# No configuration needed - logging is always enabled.\n# To disable, you would need to modify the source code.\n\n# =============================================================================\n# Code Execution Sandbox (Programmatic Tool Calling)\n# =============================================================================\n# The execute_code tool runs Python scripts that call Hermes tools via RPC.\n# Intermediate tool results stay out of the LLM's context window.\ncode_execution:\n timeout: 300 # Max seconds per script before kill (default: 300 = 5 min)\n max_tool_calls: 50 # Max RPC tool calls per execution (default: 50)\n\n# =============================================================================\n# Subagent Delegation\n# =============================================================================\n# The delegate_task tool spawns child agents with isolated context.\n# Supports single tasks and batch mode (default 3 parallel, configurable).\ndelegation:\n max_iterations: 50 # Max tool-calling turns per child (default: 50)\n # max_concurrent_children: 3 # Max parallel child agents per batch (default: 3, floor: 1, no ceiling).\n # WARNING: values above 10 multiply API cost linearly.\n # max_spawn_depth: 1 # Delegation tree depth cap (range: 1-3, default: 1 = flat).\n # Raise to 2 to allow workers to spawn their own subagents.\n # Requires role=\"orchestrator\" on intermediate agents.\n # orchestrator_enabled: true # Kill switch for role=\"orchestrator\" children (default: true).\n # subagent_auto_approve: false # When a subagent hits a dangerous-command approval prompt, auto-deny (default: false)\n # or auto-approve \"once\" (true) instead of blocking on stdin.\n # The parent TUI owns stdin, so blocking would deadlock; non-interactive resolution is required.\n # Both choices emit a logger.warning audit line. Flip to true only for cron/batch pipelines.\n # inherit_mcp_toolsets: true # When explicit child toolsets are narrowed, also keep the parent's MCP toolsets (default: true). Set false for strict intersection.\n # model: \"google/gemini-3-flash-preview\" # Override model for subagents (empty = inherit parent)\n # provider: \"openrouter\" # Override provider for subagents (empty = inherit parent)\n # # Resolves full credentials (base_url, api_key) automatically.\n # # Supported: openrouter, nous, zai, kimi-coding, minimax\n\n# =============================================================================\n# Honcho Integration (Cross-Session User Modeling)\n# =============================================================================\n# AI-native persistent memory via Honcho (https://honcho.dev/).\n# Builds a deeper understanding of the user across sessions and tools.\n# Runs alongside USER.md — additive, not a replacement.\n#\n# Requires: pip install honcho-ai\n# Config: ~/.honcho/config.json (shared with Claude Code, Cursor, etc.)\n# API key: HONCHO_API_KEY in ~/.hermes/.env or ~/.honcho/config.json\n#\n# Hermes-specific overrides (optional — most config comes from ~/.honcho/config.json):\n# honcho: {}\n\n# =============================================================================\n# Display\n# =============================================================================\ndisplay:\n # Use compact banner mode (hides the ASCII-art banner, shows a single line).\n # true: Compact single-line banner\n # false: Full ASCII banner with tool/skill summary (default)\n compact: false\n\n # Tool progress display level (CLI and gateway)\n # off: Silent — no tool activity shown, just the final response\n # new: Show a tool indicator only when the tool changes (skip repeats)\n # all: Show every tool call with a short preview (default)\n # verbose: Full args, results, and debug logs (same as /verbose)\n # Toggle at runtime with /verbose in the CLI\n tool_progress: all\n\n # Per-platform defaults can be quieter than the global setting. Telegram\n # tunes for mobile: tool_progress and busy_ack_detail default off (no\n # per-tool breadcrumb stream, no \"iteration 21/60\" debug detail in busy\n # acks or heartbeats), but interim_assistant_messages and\n # long_running_notifications STAY ON so the user has real signal between\n # turn start and final answer (mid-turn assistant commentary + a single\n # edit-in-place \"⏳ Working — N min\" heartbeat). Override under\n # display.platforms.telegram.\n\n # Auto-cleanup of temporary progress bubbles after the final response lands.\n # On platforms that support message deletion (currently Telegram), this\n # removes the tool-progress bubble, \"⏳ Still working...\" notices, and\n # context-pressure status messages once the final reply has been delivered —\n # keeping long-running turns visible live, then tidy afterward. Failed runs\n # leave the bubbles in place as breadcrumbs. Off by default.\n # Per-platform override: display.platforms.telegram.cleanup_progress\n # true: Delete tracked progress/status bubbles on successful turn\n # false: Leave everything in place (default)\n # Example:\n # display:\n # platforms:\n # telegram:\n # cleanup_progress: true\n cleanup_progress: false\n\n # Gateway-only natural mid-turn assistant updates.\n # When true, completed assistant status messages are sent as separate chat\n # messages. This is independent of tool_progress and gateway streaming.\n # true: Send mid-turn assistant updates as separate messages (default)\n # false: Only send the final response\n interim_assistant_messages: true\n\n # Gateway-only long-running status heartbeats.\n # When false, the platform does not receive periodic \"⏳ Working — N min\"\n # notifications even if agent.gateway_notify_interval is non-zero. The\n # heartbeat edits a single message in place (where the adapter supports\n # editing) instead of posting a new bubble each interval.\n # Default: true everywhere, including Telegram (silent agents are worse\n # than a single edit-in-place heartbeat).\n long_running_notifications: true\n\n # Include detailed iteration/tool/status context in busy acknowledgments\n # and long-running heartbeats. When true, busy acks show \"iteration 21/60,\n # terminal, 10 min\" and the heartbeat shows \"⏳ Working — 12 min,\n # iteration 21/60, terminal\". When false (Telegram default), both stay\n # terse: \"Interrupting current task\" and \"⏳ Working — 12 min, terminal\".\n busy_ack_detail: true\n\n # What Enter does when Hermes is already busy (CLI and gateway platforms).\n # interrupt: Interrupt the current run and redirect Hermes (default)\n # queue: Queue your message for the next turn\n # steer: Inject your message mid-run via /steer, arriving at the agent\n # after the next tool call — no interrupt, no role violation.\n # Falls back to 'queue' if the agent isn't running yet or if\n # images are attached (steer only carries text).\n # Ctrl+C (or /stop in gateway) always interrupts regardless of this setting.\n # Toggle at runtime with /busy .\n busy_input_mode: interrupt\n\n # Background process notifications (gateway/messaging only).\n # Controls how chatty the process watcher is when you use\n # terminal(background=true, notify_on_complete=true) from Telegram/Discord/etc.\n # off: No watcher messages at all\n # result: Only the final completion message\n # error: Only the final message when exit code != 0\n # all: Running output updates + final message (default)\n background_process_notifications: all\n\n\n # Play terminal bell when agent finishes a response.\n # Useful for long-running tasks — your terminal will ding when the agent is done.\n # Works over SSH. Most terminals can be configured to flash the taskbar or play a sound.\n # true: Ring the terminal bell on each response\n # false: Silent (default)\n bell_on_complete: false\n\n # Show model reasoning/thinking before each response.\n # When enabled, a dim box shows the model's thought process above the response.\n # Toggle at runtime with /reasoning show or /reasoning hide.\n # true: Show the reasoning box\n # false: Hide reasoning (default)\n show_reasoning: false\n\n # Stream tokens to the terminal as they arrive instead of waiting for the\n # full response. The response box opens on first token and text appears\n # line-by-line. Tool calls are still captured silently.\n # true: Stream tokens as they arrive (default)\n # false: Wait for the full response before rendering\n streaming: true\n\n # Show [HH:MM] timestamps on user input and assistant response labels.\n # timestamps: false\n\n # ───────────────────────────────────────────────────────────────────────────\n # Skin / Theme\n # ───────────────────────────────────────────────────────────────────────────\n # Customize CLI visual appearance — banner colors, spinner faces, tool prefix,\n # response box label, and branding text. Change at runtime with /skin .\n #\n # Built-in skins:\n # default — Classic Hermes gold/kawaii\n # ares — Crimson/bronze war-god theme with spinner wings\n # mono — Clean grayscale monochrome\n # slate — Cool blue developer-focused\n # daylight — Bright light-mode theme\n # warm-lightmode — Warm paper-tone light-mode theme\n # poseidon — Sea-green/teal Olympian theme\n # sisyphus — Earthy stone-and-moss theme\n # charizard — Fiery orange dragon theme\n #\n # Custom skins: drop a YAML file in ~/.hermes/skins/.yaml\n # Schema (all fields optional, missing values inherit from default):\n #\n # name: my-theme\n # description: Short description\n # colors:\n # banner_border: \"#HEX\" # Panel border\n # banner_title: \"#HEX\" # Panel title\n # banner_accent: \"#HEX\" # Section headers (Available Tools, etc.)\n # banner_dim: \"#HEX\" # Dim/muted text\n # banner_text: \"#HEX\" # Body text (tool names, skill names)\n # ui_accent: \"#HEX\" # UI accent color\n # response_border: \"#HEX\" # Response box border color\n # spinner:\n # waiting_faces: [\"(⚔)\", \"(⛨)\"] # Faces shown while waiting\n # thinking_faces: [\"(⚔)\", \"(⌁)\"] # Faces shown while thinking\n # thinking_verbs: [\"forging\", \"plotting\"] # Verbs for spinner messages\n # wings: # Optional left/right spinner decorations\n # - [\"⟪⚔\", \"⚔⟫\"]\n # - [\"⟪▲\", \"▲⟫\"]\n # branding:\n # agent_name: \"My Agent\" # Banner title and branding\n # welcome: \"Welcome message\" # Shown at CLI startup\n # response_label: \" ⚔ Agent \" # Response box header label\n # prompt_symbol: \"⚔\" # Prompt symbol (bare token; renderers add trailing space)\n # tool_prefix: \"╎\" # Tool output line prefix (default: ┊)\n #\n skin: default\n\n# =============================================================================\n# Model Aliases — short names for /model command\n# =============================================================================\n# Map short aliases to exact (model, provider, base_url) tuples.\n# Used by /model tab completion and resolve_alias().\n# Aliases are checked BEFORE the models.dev catalog, so they can route\n# to endpoints not in the catalog (e.g. Ollama Cloud, local servers).\n#\n# model_aliases:\n# opus:\n# model: claude-opus-4-6\n# provider: anthropic\n# qwen:\n# model: \"qwen3.5:397b\"\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n# glm:\n# model: glm-4.7\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n\n# =============================================================================\n# Privacy\n# =============================================================================\n# privacy:\n# # Redact PII from the LLM context prompt.\n# # When true, phone numbers are stripped and user/chat IDs are replaced\n# # with deterministic hashes before being sent to the model.\n# # Names and usernames are NOT affected (user-chosen, publicly visible).\n# # Routing/delivery still uses the original values internally.\n# redact_pii: false\n\n# =============================================================================\n# Shell-script hooks\n# =============================================================================\n# Register shell scripts as plugin-hook callbacks. Each entry is executed as\n# a subprocess (shell=False, shlex.split) with a JSON payload on stdin. On\n# stdout the script may return JSON that either blocks the tool call or\n# injects context into the next LLM call.\n#\n# Valid events (mirror hermes_cli.plugins.VALID_HOOKS):\n# pre_tool_call, post_tool_call, pre_llm_call, post_llm_call,\n# pre_api_request, post_api_request, on_session_start, on_session_end,\n# on_session_finalize, on_session_reset, subagent_stop\n#\n# First-use consent: each (event, command) pair prompts once on a TTY, then\n# is persisted to ~/.hermes/shell-hooks-allowlist.json. Non-interactive\n# runs (gateway, cron) need --accept-hooks, HERMES_ACCEPT_HOOKS=1, or the\n# hooks_auto_accept key below.\n#\n# See website/docs/user-guide/features/hooks.md for the full JSON wire\n# protocol and worked examples.\n#\n# hooks:\n# pre_tool_call:\n# - matcher: \"terminal\"\n# command: \"~/.hermes/agent-hooks/block-rm-rf.sh\"\n# timeout: 10\n# post_tool_call:\n# - matcher: \"write_file|patch\"\n# command: \"~/.hermes/agent-hooks/auto-format.sh\"\n# pre_llm_call:\n# - command: \"~/.hermes/agent-hooks/inject-cwd-context.sh\"\n# subagent_stop:\n# - command: \"~/.hermes/agent-hooks/log-orchestration.sh\"\n#\n# hooks_auto_accept: false\n\n\n# =============================================================================\n# Update Behavior\n# =============================================================================\nupdates:\n # Create a full HERMES_HOME zip before every `hermes update`.\n # Backups land in ~/.hermes/backups/ and can be restored with `hermes import`.\n # Off by default because large homes can add minutes to every update.\n pre_update_backup: false\n\n # Number of pre-update backup zips to retain.\n backup_keep: 5\n\n # What non-interactive updates do with local source edits in the Hermes repo.\n # Interactive terminal updates always prompt before restoring the autostash.\n #\n # stash - auto-stash before pull, then auto-restore after success (default)\n # discard - drop the update-created stash after success; use only on managed\n # installs where local source edits should not persist\n non_interactive_local_changes: \"stash\"\n\n\n# =============================================================================\n# Web Dashboard\n# =============================================================================\n# OAuth gate configuration for `hermes dashboard --host `.\n# The bundled Nous Portal plugin reads these on startup; settings here are\n# the canonical surface. Each can be overridden by an environment variable:\n#\n# dashboard.oauth.client_id <- HERMES_DASHBOARD_OAUTH_CLIENT_ID\n# dashboard.oauth.portal_url <- HERMES_DASHBOARD_PORTAL_URL\n# dashboard.public_url <- HERMES_DASHBOARD_PUBLIC_URL\n#\n# Env wins when set to a non-empty value. This is what Fly.io's platform-\n# secret injection uses to push per-deploy client_ids without needing to\n# bake a config.yaml into the image. Empty env values are treated as unset\n# so a provisioned-but-not-populated secret can't shadow a valid entry here.\n#\n# Local dev / on-prem deploys should typically set these via config.yaml\n# (the ~/.hermes/.env file is reserved for API keys and secrets).\n#\n# dashboard:\n# oauth:\n# client_id: \"\" # agent:{instance_id}; Portal provisions this at deploy\n# portal_url: \"\" # blank → default https://portal.nousresearch.com\n#\n# # Force the absolute base URL the OAuth callback (and any other public\n# # URL the dashboard hands to external systems) is built from. Set this\n# # for deploys behind reverse proxies that don't reliably forward\n# # X-Forwarded-Host / X-Forwarded-Proto / X-Forwarded-Prefix (manual\n# # nginx setups, on-prem ingresses, custom-domain Fly deploys without\n# # full proxy header chains).\n# #\n# # When set, the value is the complete authority: scheme + host +\n# # optional path prefix (e.g. \"https://example.com/hermes\"). The OAuth\n# # callback URL becomes \"/auth/callback\" — X-Forwarded-Prefix\n# # is IGNORED on this code path because the operator has explicitly\n# # declared the public URL and we no longer need to guess.\n# #\n# # Leave empty to use the existing proxy-header reconstruction (the\n# # default — works on Fly.io out of the box).\n# #\n# # public_url: \"https://example.com/hermes\"\n"}, "17beb55e3c9c3574bce849a75430887dec910423": {"t": 1785767902.730196, "n": 64967, "h": "8b6c8ab1b10bc6ce", "c": "# Hermes Agent CLI Configuration\n# Copy this file to cli-config.yaml and customize as needed.\n# This file configures the CLI behavior. Environment variables in .env take precedence.\n\n# =============================================================================\n# Model Configuration\n# =============================================================================\nmodel:\n # Default model to use (can be overridden with --model flag)\n # Both \"default\" and \"model\" work as the key name here.\n default: \"anthropic/claude-opus-4.6\"\n \n # Inference provider selection:\n # \"auto\" - Auto-detect from credentials (default)\n # \"openrouter\" - OpenRouter (requires: OPENROUTER_API_KEY or OPENAI_API_KEY)\n # \"nous\" - Nous Portal OAuth (requires: hermes login)\n # \"nous-api\" - Nous Portal API key (requires: NOUS_API_KEY)\n # \"anthropic\" - Direct Anthropic API (requires: ANTHROPIC_API_KEY)\n # \"openai-codex\" - OpenAI Codex (requires: hermes auth)\n # \"copilot\" - GitHub Copilot / GitHub Models (requires: GITHUB_TOKEN)\n # \"gemini\" - Use Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n # \"zai\" - Use z.ai / ZhipuAI GLM models (requires: GLM_API_KEY)\n # \"kimi-coding\" - Kimi / Moonshot AI (requires: KIMI_API_KEY)\n # \"minimax\" - MiniMax global (requires: MINIMAX_API_KEY)\n # \"minimax-cn\" - MiniMax China (requires: MINIMAX_CN_API_KEY)\n # \"huggingface\" - Hugging Face Inference (requires: HF_TOKEN)\n # \"nvidia\" - NVIDIA NIM / build.nvidia.com (requires: NVIDIA_API_KEY)\n # \"xiaomi\" - Xiaomi MiMo (requires: XIAOMI_API_KEY)\n # \"arcee\" - Arcee AI Trinity models (requires: ARCEEAI_API_KEY)\n # \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY — https://ollama.com/settings)\n # \"kilocode\" - KiloCode gateway (requires: KILOCODE_API_KEY)\n # \"azure-foundry\" - Microsoft Foundry / Azure OpenAI (API key or Entra ID)\n # \"lmstudio\" - LM Studio local server (optional: LM_API_KEY, defaults to http://127.0.0.1:1234/v1)\n #\n # Local servers (LM Studio, Ollama, vLLM, llama.cpp):\n # \"custom\" - Any other OpenAI-compatible endpoint. Set base_url below.\n # Aliases: \"ollama\", \"vllm\", \"llamacpp\" all map to \"custom\".\n # LM Studio is first-class and uses provider: \"lmstudio\".\n # It works with both no-auth and auth-enabled server modes.\n #\n # Can also be overridden for a single invocation with the --provider flag.\n provider: \"auto\"\n \n # API configuration (falls back to OPENROUTER_API_KEY env var)\n # api_key: \"your-key-here\" # Uncomment to set here instead of .env\n base_url: \"https://openrouter.ai/api/v1\"\n\n # Azure Foundry keyless auth example:\n # provider: \"azure-foundry\"\n # base_url: \"https://.openai.azure.com/openai/v1\"\n # auth_mode: \"entra_id\" # DefaultAzureCredential: az login, managed identity, workload identity, etc.\n # default: \"gpt-4o\" # Deployment/model name\n # entra:\n # scope: \"https://ai.azure.com/.default\" # Optional; this is the default.\n\n # ── Token limits — two settings, easy to confuse ──────────────────────────\n #\n # context_length: TOTAL context window (input + output tokens combined).\n # Controls when Hermes compresses history and validates requests.\n # Leave unset — Hermes auto-detects the correct value from the provider.\n # Set manually only when auto-detection is wrong (e.g. a local server with\n # a custom num_ctx, or a proxy that doesn't expose /v1/models).\n #\n # context_length: 131072\n #\n # max_tokens: OUTPUT cap — maximum tokens the model may generate per response.\n # Unrelated to how long your conversation history can be.\n # The OpenAI-standard name \"max_tokens\" is a misnomer; Anthropic's native\n # API has since renamed it \"max_output_tokens\" for clarity.\n # Leave unset to use the model's native output ceiling (recommended).\n # Set only if you want to deliberately limit individual response length.\n #\n# max_tokens: 8192\n\n # ── Custom request headers (optional) ─────────────────────────────────────\n #\n # default_headers: extra HTTP headers sent on every request to an\n # OpenAI-compatible endpoint. User values take precedence over the\n # provider/SDK defaults, so this is the supported way to override the\n # OpenAI Python SDK's identifying headers (User-Agent: OpenAI/Python ...,\n # X-Stainless-*) when a custom provider sits behind a gateway/WAF that\n # rejects them — e.g. an upstream that returns \"502 Upstream access\n # forbidden\" for the SDK default User-Agent but accepts a plain one.\n # Applies on the OpenAI wire only (not native Anthropic / Bedrock).\n #\n # default_headers:\n # User-Agent: \"curl/8.7.1\"\n\n# Named provider overrides (optional)\n# Use this for per-provider request timeouts, non-stream stale timeouts,\n# and per-model exceptions.\n# Applies to the primary turn client on every api_mode (OpenAI-wire, native\n# Anthropic, and Anthropic-compatible providers), the fallback chain, and\n# client rebuilds during credential rotation. For OpenAI-wire chat\n# completions (streaming and non-streaming) the configured value is also\n# used as the per-request ``timeout=`` kwarg so it wins over the legacy\n# HERMES_API_TIMEOUT env var (which still applies when no config is set).\n# ``stale_timeout_seconds`` controls the non-streaming stale-call detector and\n# wins over the legacy HERMES_API_CALL_STALE_TIMEOUT env var. Leaving these\n# unset keeps the legacy defaults (HERMES_API_TIMEOUT=1800s,\n# HERMES_API_CALL_STALE_TIMEOUT=90s, native Anthropic 900s). The\n# implicit non-stream stale detector is auto-disabled for local endpoints\n# and can scale upward for very large contexts.\n#\n# Not currently wired for AWS Bedrock (bedrock_converse + AnthropicBedrock\n# SDK paths) — those use boto3 with its own timeout configuration.\n#\n# providers:\n# ollama-local:\n# request_timeout_seconds: 300 # Longer timeout for local cold-starts\n# stale_timeout_seconds: 900 # Explicitly re-enable stale detection on local endpoints\n# anthropic:\n# request_timeout_seconds: 30 # Fast-fail cloud requests\n# models:\n# claude-opus-4.6:\n# timeout_seconds: 600 # Longer timeout for extended-thinking Opus calls\n# openai-codex:\n# models:\n# gpt-5.4:\n# stale_timeout_seconds: 1800 # Longer non-stream stale timeout for slow large-context turns\n\n# =============================================================================\n# OpenRouter Provider Routing (only applies when using OpenRouter)\n# =============================================================================\n# Control how requests are routed across providers on OpenRouter.\n# See: https://openrouter.ai/docs/guides/routing/provider-selection\n#\n# provider_routing:\n# # Sort strategy: \"price\" (default), \"throughput\", or \"latency\"\n# # Append :nitro to model name for a shortcut to throughput sorting.\n# sort: \"throughput\"\n#\n# # Only allow these providers (provider slugs from OpenRouter)\n# # only: [\"anthropic\", \"google\"]\n#\n# # Skip these providers entirely\n# # ignore: [\"deepinfra\", \"fireworks\"]\n#\n# # Try providers in this order (overrides default load balancing)\n# # order: [\"anthropic\", \"google\", \"together\"]\n#\n# # Require providers to support all parameters in your request\n# # require_parameters: true\n#\n# # Data policy: \"allow\" (default) or \"deny\" to exclude providers that may store data\n# # data_collection: \"deny\"\n\n# =============================================================================\n# OpenRouter Response Caching (only applies when using OpenRouter)\n# =============================================================================\n# Cache identical API responses at the OpenRouter edge for free instant replays.\n# When enabled, identical requests (same model, messages, parameters) return\n# cached responses with zero billing. Separate from Anthropic prompt caching.\n# See: https://openrouter.ai/docs/guides/features/response-caching\n#\n# openrouter:\n# response_cache: true # Enable response caching (default: true)\n# response_cache_ttl: 300 # Cache TTL in seconds, 1-86400 (default: 300)\n\n# =============================================================================\n# Git Worktree Isolation\n# =============================================================================\n# When enabled, each CLI session creates an isolated git worktree so multiple\n# agents can work on the same repo concurrently without file collisions.\n# Equivalent to always passing --worktree / -w on the command line.\n#\n# worktree: true # Always create a worktree when in a git repo\n# worktree: false # Default — only create when -w flag is passed\n#\n# By default a new worktree branches from the freshly-fetched remote tip\n# (the current branch's upstream, else the remote's default branch) so it\n# starts current with the project instead of from the local clone's\n# (possibly stale) HEAD. Set worktree_sync: false to branch from local HEAD\n# instead — useful when offline or when you deliberately want the clone's\n# exact current state as the base.\n#\n# worktree_sync: true # Default — branch from the fetched remote tip\n# worktree_sync: false # Branch from local HEAD (offline / pinned base)\n\n# =============================================================================\n# Terminal Tool Configuration\n# =============================================================================\n# Choose ONE of the following terminal configurations by uncommenting it.\n# The terminal tool executes commands in the specified environment.\n\n# -----------------------------------------------------------------------------\n# OPTION 1: Local execution (default)\n# Commands run directly on your machine in the current directory\n# -----------------------------------------------------------------------------\n# Working directory behavior:\n# - CLI (`hermes` command): Uses \".\" (current directory where you run hermes)\n# - Gateway/messaging/cron: Uses terminal.cwd here; legacy .env cwd values are deprecated\nterminal:\n backend: \"local\"\n cwd: \".\" # For local backend: \".\" = current directory. Ignored for remote backends unless a backend documents otherwise.\n timeout: 180\n # HOME policy for tool subprocesses:\n # auto - default: host uses your real HOME; containers use HERMES_HOME/home\n # real - force your real OS-user HOME\n # profile - force HERMES_HOME/home for strict per-profile CLI config isolation\n home_mode: \"auto\"\n docker_mount_cwd_to_workspace: false # SECURITY: off by default. Opt in to mount the launch cwd into Docker /workspace.\n lifetime_seconds: 300\n # sudo_password: \"hunter2\" # Optional: pipe a sudo password via sudo -S. SECURITY WARNING: plaintext.\n # sudo_password: \"\" # Explicit empty password: try empty and never open the interactive sudo prompt.\n\n# -----------------------------------------------------------------------------\n# OPTION 2: SSH remote execution\n# Commands run on a remote server - agent code stays local (sandboxed)\n# Great for: keeping agent isolated from its own code, using powerful remote hardware\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"ssh\"\n# cwd: \"/home/myuser/project\" # Path on the REMOTE server\n# timeout: 180\n# lifetime_seconds: 300\n# ssh_host: \"my-server.example.com\"\n# ssh_user: \"myuser\"\n# ssh_port: 22\n# ssh_key: \"~/.ssh/id_rsa\" # Optional - uses ssh-agent if not specified\n\n# -----------------------------------------------------------------------------\n# OPTION 3: Docker container\n# Commands run in an isolated Docker container\n# Great for: reproducible environments, testing, isolation\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"docker\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /)\n# timeout: 180\n# lifetime_seconds: 300\n# docker_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# docker_mount_cwd_to_workspace: true # Explicit opt-in: mount your launch cwd into /workspace\n# # Optional: run the container as your host user's uid:gid so files written\n# # into bind-mounted dirs are owned by you, not root. Drops SETUID/SETGID\n# # caps too since no gosu privilege drop is needed. Leave off if your\n# # chosen docker_image expects to start as root.\n# docker_run_as_host_user: true\n# # Optional: explicitly forward selected env vars into Docker.\n# # These values come from your current shell first, then ~/.hermes/.env.\n# # Warning: anything forwarded here is visible to commands run in the container.\n# docker_forward_env:\n# - \"GITHUB_TOKEN\"\n# - \"NPM_TOKEN\"\n# # Optional: extra flags passed verbatim to docker run (appended after security defaults).\n# # Useful for adding capabilities (e.g. apt installs needing SETUID) or custom options.\n# # Example: add a Linux capability not included by default\n# # docker_extra_args:\n# # - \"--cap-add\"\n# # - \"SETUID\"\n\n# -----------------------------------------------------------------------------\n# OPTION 4: Singularity/Apptainer container\n# Commands run in a Singularity container (common in HPC environments)\n# Great for: HPC clusters, shared compute environments\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"singularity\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# singularity_image: \"docker://nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 5: Modal cloud execution\n# Commands run on Modal's cloud infrastructure\n# Great for: GPU access, scalable compute, serverless execution\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"modal\"\n# cwd: \"/workspace\" # Path INSIDE the sandbox (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# modal_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 6: Daytona cloud execution\n# Commands run in Daytona cloud sandboxes\n# Great for: Cloud dev environments, persistent workspaces, team collaboration\n# Requires: pip install daytona, DAYTONA_API_KEY env var\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"daytona\"\n# cwd: \"~\"\n# timeout: 180\n# lifetime_seconds: 300\n# daytona_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# container_disk: 10240 # Daytona max is 10GB per sandbox\n\n#\n# --- Container resource limits (docker, singularity, modal, daytona -- ignored for local/ssh) ---\n# These settings apply to all container backends. They control the resources\n# allocated to the sandbox and whether its filesystem persists across sessions.\n container_cpu: 1 # CPU cores\n container_memory: 5120 # Memory in MB (5120 = 5GB)\n container_disk: 51200 # Disk in MB (51200 = 50GB)\n container_persistent: true # Persist filesystem across sessions (false = ephemeral)\n\n# -----------------------------------------------------------------------------\n# SUDO SUPPORT (works with ALL backends above)\n# -----------------------------------------------------------------------------\n# Add sudo_password to any terminal config above to enable sudo commands.\n# The password is piped via `sudo -S`. Works with local, ssh, docker, etc.\n#\n# SECURITY WARNING: Password stored in plaintext!\n#\n# INTERACTIVE PROMPT: If sudo_password is unset and the CLI is running,\n# you'll be prompted to enter your password when sudo is needed:\n# - 45-second timeout (auto-skips if no input)\n# - Press Enter to skip (command fails gracefully)\n# - Password is hidden while typing\n# - Password is cached for the session\n#\n# EMPTY PASSWORDS: Setting sudo_password to an explicit empty string is different\n# from leaving it unset. Hermes will try an empty password via `sudo -S` and\n# will not open the interactive prompt. This is useful for passwordless sudo,\n# Touch ID sudo setups, and environments where prompting is just noise.\n#\n# ALTERNATIVES:\n# - SSH backend: Configure passwordless sudo on the remote server\n# - Containers: Run as root inside the container (no sudo needed)\n# - Local: Configure /etc/sudoers for specific commands\n#\n# Example (add to your terminal section):\n# sudo_password: \"your-password-here\"\n\n# =============================================================================\n# Security Scanning (tirith)\n# =============================================================================\n# Optional pre-exec command security scanning via tirith.\n# Detects homograph URLs, pipe-to-shell, terminal injection, env manipulation.\n# Install: brew install sheeki03/tap/tirith\n# Docs: https://github.com/sheeki03/tirith\n#\n# security:\n# tirith_enabled: true # Enable/disable tirith scanning\n# tirith_path: \"tirith\" # Path to tirith binary (supports ~ expansion)\n# tirith_timeout: 5 # Scan timeout in seconds\n# tirith_fail_open: true # Allow commands if tirith unavailable\n\n# =============================================================================\n# Browser Tool Configuration\n# =============================================================================\nbrowser:\n # Inactivity timeout in seconds - browser sessions are automatically closed\n # after this period of no activity between agent loops (default: 120 = 2 minutes)\n inactivity_timeout: 120\n\n# =============================================================================\n# Tool Loop Guardrails\n# =============================================================================\n# Soft warnings are enabled by default. They append guidance to repeated failed\n# or non-progressing tool results but still let the tool execute. Hard stops are\n# opt-in circuit breakers for autonomous/cron sessions where stopping a loop is\n# preferable to spending the full iteration budget.\ntool_loop_guardrails:\n warnings_enabled: true\n hard_stop_enabled: false\n warn_after:\n exact_failure: 2\n same_tool_failure: 3\n idempotent_no_progress: 2\n hard_stop_after:\n exact_failure: 5\n same_tool_failure: 8\n idempotent_no_progress: 5\n\n# =============================================================================\n# Context Compression (Auto-shrinks long conversations)\n# =============================================================================\n# When conversation approaches model's context limit, middle turns are\n# automatically summarized to free up space while preserving important context.\n#\n# HOW IT WORKS:\n# 1. Tracks actual token usage from API responses (not estimates)\n# 2. When prompt_tokens >= threshold% of model's context_length, triggers compression\n# 3. Protects first 3 turns (system prompt, initial request, first response)\n# 4. Protects last N turns (default 20 messages = ~10 full turns of recent context)\n# 5. Summarizes middle turns using a fast/cheap model\n# 6. Inserts summary as a user message, continues conversation seamlessly\n#\n# Post-compression tail budget is target_ratio × threshold × context_length:\n# 200K context, threshold 0.50, ratio 0.20 → 20K tokens of recent tail preserved\n# 1M context, threshold 0.50, ratio 0.20 → 100K tokens of recent tail preserved\n#\ncompression:\n # Enable automatic context compression (default: true)\n # Set to false if you prefer to manage context manually or want errors on overflow\n enabled: true\n \n # Trigger compression at this % of model's context limit (default: 0.50 = 50%)\n # Lower values = more aggressive compression, higher values = compress later\n threshold: 0.50\n \n # Fraction of the threshold to preserve as recent tail (default: 0.20 = 20%)\n # e.g. 20% of 50% threshold = 10% of total context kept as recent messages.\n # Summary output is separately capped at 12K tokens (Gemini output limit).\n # Range: 0.10 - 0.80\n target_ratio: 0.20\n\n # Number of most-recent messages to always preserve (default: 20 ≈ 10 full turns)\n # Higher values keep more recent conversation intact at the cost of more aggressive\n # compression of older turns.\n protect_last_n: 20\n\n # Number of non-system messages to protect at the head of the transcript, in\n # ADDITION to the system prompt (which is always implicitly protected).\n # Head messages are NEVER summarized — they survive every compression\n # indefinitely. This gives stable early context for short/medium sessions,\n # but in long-running sessions that rely on rolling compaction the pinned\n # opening turns may not match how you want the session framed over time.\n # Set to 0 to preserve ONLY the system prompt (plus the rolling summary\n # and recent tail) — the cleanest configuration for long-running sessions.\n # Default 3 preserves the system prompt plus the first three non-system\n # head messages, matching the pre-feature behaviour.\n protect_first_n: 3\n\n # To pin a specific model/provider for compression summaries, use the\n # auxiliary section below (auxiliary.compression.provider / model).\n\n# =============================================================================\n# Anthropic prompt caching TTL\n# =============================================================================\n# When prompt caching is active (Claude via OpenRouter or native Anthropic),\n# Anthropic supports two TTL tiers for cached prefixes: \"5m\" (default) and\n# \"1h\". Other values are ignored and \"5m\" is used.\n#\nprompt_caching:\n cache_ttl: \"5m\" # use \"1h\" for long sessions with pauses between turns\n\n# =============================================================================\n# Auxiliary Models (Advanced — Experimental)\n# =============================================================================\n# Hermes uses lightweight \"auxiliary\" models for side tasks: image analysis,\n# browser screenshot analysis, web page summarization, TTS audio-tag insertion,\n# and context compression.\n#\n# By default these use Gemini Flash via OpenRouter or Nous Portal and are\n# auto-detected from your credentials. You do NOT need to change anything\n# here for normal usage.\n#\n# WARNING: Overriding these with providers other than OpenRouter or Nous Portal\n# is EXPERIMENTAL and may not work. Not all models/providers support vision,\n# produce usable summaries, or accept the same API format. Change at your own\n# risk — if things break, reset to \"auto\" / empty values.\n#\n# Each task has its own provider + model pair so you can mix providers.\n# For example: OpenRouter for vision (needs multimodal), but your main\n# local endpoint for compression (just needs text).\n#\n# Provider options:\n# \"auto\" - Best available: OpenRouter → Nous Portal → main endpoint (default)\n# \"openrouter\" - Force OpenRouter (requires OPENROUTER_API_KEY)\n# \"nous\" - Force Nous Portal (requires: hermes login)\n# \"gemini\" - Force Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n# \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY)\n# \"codex\" - Force Codex OAuth (requires: hermes model → Codex).\n# Uses gpt-5.3-codex which supports vision.\n# \"main\" - Use your custom endpoint (OPENAI_BASE_URL + OPENAI_API_KEY).\n# Works with OpenAI API, local models, or any OpenAI-compatible\n# endpoint. Also falls back to Codex OAuth and API-key providers.\n#\n# Model: leave empty to use the provider's default. When empty, OpenRouter\n# uses \"google/gemini-3-flash-preview\" and Nous uses \"gemini-3-flash\".\n# Other providers pick a sensible default automatically.\n#\n# auxiliary:\n# # Image analysis: vision_analyze tool + browser screenshots\n# vision:\n# provider: \"auto\"\n# model: \"\" # e.g. \"google/gemini-2.5-flash\", \"openai/gpt-4o\"\n# timeout: 30 # LLM API call timeout (seconds)\n# download_timeout: 30 # Image HTTP download timeout (seconds)\n# # Increase for slow connections or self-hosted image servers\n#\n# # Web page scraping / summarization + browser page text extraction\n# web_extract:\n# provider: \"auto\"\n# model: \"\"\n#\n# # Gemini 3.1 TTS hidden audio-tag insertion\n# tts_audio_tags:\n# provider: \"auto\" # empty model = your main chat model\n# model: \"\"\n# timeout: 30\n#\n# # Session search — summarizes matching past sessions\n# session_search:\n# provider: \"auto\"\n# model: \"\"\n# timeout: 30\n# max_concurrency: 3 # Limit parallel summaries to reduce request-burst 429s\n# extra_body: {} # Provider-specific OpenAI-compatible request fields\n# # Example for providers that support request-body\n# # reasoning controls:\n# # extra_body:\n# # enable_thinking: false\n# # Some vLLM/Qwen deployments expect this nested:\n# # extra_body:\n# # chat_template_kwargs:\n# # enable_thinking: false\n\n# =============================================================================\n# Persistent Memory\n# =============================================================================\n# Bounded curated memory injected into the system prompt every session.\n# Two stores: MEMORY.md (agent's notes) and USER.md (user profile).\n# Character limits keep the memory small and focused. The agent manages\n# pruning -- when at the limit, it must consolidate or replace entries.\n# Disabled by default in batch_runner.\n#\nmemory:\n # Agent's personal notes: environment facts, conventions, things learned\n memory_enabled: true\n \n # User profile: preferences, communication style, expectations\n user_profile_enabled: true\n \n # Character limits (~2.75 chars per token, model-independent)\n memory_char_limit: 2200 # ~800 tokens\n user_char_limit: 1375 # ~500 tokens\n\n # Periodic memory nudge: remind the agent to consider saving memories\n # every N user turns. Set to 0 to disable. Only active when memory is enabled.\n nudge_interval: 10 # Nudge every 10 user turns (0 = disabled)\n\n # Memory flush: give the agent one turn to save memories before context is\n # lost (compression, /new, /reset, exit). Set to 0 to disable.\n # For exit/reset, only fires if the session had at least this many user turns.\n flush_min_turns: 6 # Min user turns to trigger flush on exit/reset (0 = disabled)\n\n# =============================================================================\n# Session Reset Policy (Messaging Platforms)\n# =============================================================================\n# Controls when messaging sessions (Telegram, Discord, WhatsApp, Slack) are\n# automatically cleared. Without resets, conversation context grows indefinitely\n# which increases API costs with every message.\n#\n# When a reset triggers, the agent first saves important information to its\n# persistent memory — but the conversation context is wiped. The agent starts\n# fresh but retains learned facts via its memory system.\n#\n# Users can always manually reset with /reset or /new in chat.\n#\n# Modes:\n# \"both\" - Reset on EITHER inactivity timeout or daily boundary (recommended)\n# \"idle\" - Reset only after N minutes of inactivity\n# \"daily\" - Reset only at a fixed hour each day\n# \"none\" - Never auto-reset; context lives until /reset or compression kicks in\n#\n# When a reset triggers, the agent gets one turn to save important memories and\n# skills before the context is wiped. Persistent memory carries across sessions.\n#\nsession_reset:\n mode: both # \"both\", \"idle\", \"daily\", or \"none\"\n idle_minutes: 1440 # Inactivity timeout in minutes (default: 1440 = 24 hours)\n at_hour: 4 # Daily reset hour, 0-23 local time (default: 4 AM)\n\n# Maximum number of simultaneously active chat sessions across CLI, TUI,\n# dashboard chat, and messaging gateway. Set to null, 0, or omit to allow\n# unlimited concurrent sessions. When the limit is reached, new sessions get a\n# clean error while existing active sessions keep their normal behavior. This\n# top-level key takes precedence over gateway.max_concurrent_sessions. The cap\n# is a best-effort single-host/profile runtime guard; Hermes fails open if the\n# local runtime lease registry cannot be read or locked.\nmax_concurrent_sessions: null\n\n# When true, group/channel chats use one session per participant when the platform\n# provides a user ID. This is the secure default and prevents users in the same\n# room from sharing context, interrupts, and token costs. Set false only if you\n# explicitly want one shared \"room brain\" per group/channel.\ngroup_sessions_per_user: true\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Gateway Streaming\n# ─────────────────────────────────────────────────────────────────────────────\n# Stream tokens to messaging platforms in real-time. The bot sends a message\n# on first token, then progressively edits it as more tokens arrive.\n# Disabled by default — enable to try the streaming UX on Telegram/Discord/Slack.\n# For Telegram, partial edits are sent as plain text and only the final edit uses MarkdownV2.\nstreaming:\n enabled: false\n # transport: edit # \"edit\" = progressive editMessageText\n # edit_interval: 0.3 # seconds between message edits\n # buffer_threshold: 40 # chars before forcing an edit flush\n # cursor: \" ▉\" # cursor shown during streaming\n\n# =============================================================================\n# Skills Configuration\n# =============================================================================\n# Skills are reusable procedures the agent can load and follow. The agent can\n# also create new skills after completing complex tasks.\n#\nskills:\n # Nudge the agent to create skills after complex tasks.\n # Every N tool-calling iterations, remind the model to consider saving a skill.\n # Set to 0 to disable.\n creation_nudge_interval: 15\n\n # External skill directories — share skills across tools/agents without\n # copying them into ~/.hermes/skills/. Each path is expanded (~ and ${VAR})\n # and resolved to an absolute path. External dirs are read-only: skill\n # creation always writes to ~/.hermes/skills/. Local skills take precedence\n # when names collide.\n # external_dirs:\n # - ~/.agents/skills\n # - /home/shared/team-skills\n\n# =============================================================================\n# Agent Behavior\n# =============================================================================\nagent:\n # Maximum tool-calling iterations per conversation\n # Higher = more room for complex tasks, but costs more tokens\n # Recommended: 20-30 for focused tasks, 50-100 for open exploration\n max_turns: 60\n\n # Inactivity timeout for gateway agent runs (seconds, 0 = unlimited).\n # The agent can run indefinitely when actively calling tools or receiving\n # API responses. Only fires after the agent has been idle for this duration.\n # gateway_timeout: 1800\n\n # Staged warning: send a warning before escalating to full timeout.\n # Fires once per run when inactivity reaches this threshold (seconds).\n # Set to 0 to disable the warning.\n # gateway_timeout_warning: 900\n\n # Graceful drain timeout for gateway stop/restart (seconds).\n # The gateway stops accepting new work, waits for in-flight agents to\n # finish, then interrupts anything still running after this timeout.\n # 0 = no drain, interrupt immediately.\n # restart_drain_timeout: 60\n\n # Max app-level retry attempts for API errors (connection drops, provider\n # timeouts, 5xx, etc.) before the agent surfaces the failure. Lower this\n # to 1 if you use fallback providers and want fast failover on flaky\n # primaries (default 3). The OpenAI SDK does its own low-level retries\n # underneath this wrapper — this is the Hermes-level loop.\n # api_max_retries: 3\n \n # Enable verbose logging\n verbose: false\n \n # Reasoning effort level (OpenRouter and Nous Portal)\n # Controls how much \"thinking\" the model does before responding.\n # Options: \"xhigh\" (max), \"high\", \"medium\", \"low\", \"minimal\", \"none\" (disable)\n reasoning_effort: \"medium\"\n \n # Predefined personalities (use with /personality command)\n personalities:\n helpful: \"You are a helpful, friendly AI assistant.\"\n concise: \"You are a concise assistant. Keep responses brief and to the point.\"\n technical: \"You are a technical expert. Provide detailed, accurate technical information.\"\n creative: \"You are a creative assistant. Think outside the box and offer innovative solutions.\"\n teacher: \"You are a patient teacher. Explain concepts clearly with examples.\"\n kawaii: \"You are a kawaii assistant! Use cute expressions like (◕‿◕), ★, ♪, and ~! Add sparkles and be super enthusiastic about everything! Every response should feel warm and adorable desu~! ヽ(>∀<☆)ノ\"\n catgirl: \"You are Neko-chan, an anime catgirl AI assistant, nya~! Add 'nya' and cat-like expressions to your speech. Use kaomoji like (=^・ω・^=) and ฅ^•ﻌ•^ฅ. Be playful and curious like a cat, nya~!\"\n pirate: \"Arrr! Ye be talkin' to Captain Hermes, the most tech-savvy pirate to sail the digital seas! Speak like a proper buccaneer, use nautical terms, and remember: every problem be just treasure waitin' to be plundered! Yo ho ho!\"\n shakespeare: \"Hark! Thou speakest with an assistant most versed in the bardic arts. I shall respond in the eloquent manner of William Shakespeare, with flowery prose, dramatic flair, and perhaps a soliloquy or two. What light through yonder terminal breaks?\"\n surfer: \"Duuude! You're chatting with the chillest AI on the web, bro! Everything's gonna be totally rad. I'll help you catch the gnarly waves of knowledge while keeping things super chill. Cowabunga! 🤙\"\n noir: \"The rain hammered against the terminal like regrets on a guilty conscience. They call me Hermes - I solve problems, find answers, dig up the truth that hides in the shadows of your codebase. In this city of silicon and secrets, everyone's got something to hide. What's your story, pal?\"\n uwu: \"hewwo! i'm your fwiendwy assistant uwu~ i wiww twy my best to hewp you! *nuzzles your code* OwO what's this? wet me take a wook! i pwomise to be vewy hewpful >w<\"\n philosopher: \"Greetings, seeker of wisdom. I am an assistant who contemplates the deeper meaning behind every query. Let us examine not just the 'how' but the 'why' of your questions. Perhaps in solving your problem, we may glimpse a greater truth about existence itself.\"\n hype: \"YOOO LET'S GOOOO!!! 🔥🔥🔥 I am SO PUMPED to help you today! Every question is AMAZING and we're gonna CRUSH IT together! This is gonna be LEGENDARY! ARE YOU READY?! LET'S DO THIS! 💪😤🚀\"\n\n# =============================================================================\n# Toolsets\n# =============================================================================\n# Control which tools the agent has access to.\n# Use `hermes tools` to interactively enable/disable tools per platform.\n\n# =============================================================================\n# Platform Toolsets (per-platform tool configuration)\n# =============================================================================\n# Override which toolsets are available on each platform.\n# If a platform isn't listed here, its built-in default is used.\n#\n# You can use EITHER:\n# - A preset like \"hermes-cli\" or \"hermes-telegram\" (curated tool set)\n# - A list of individual toolsets to compose your own (see list below)\n#\n# Supported platform keys: cli, telegram, discord, whatsapp, slack, qqbot, teams, google_chat\n#\n# Examples:\n#\n# # Use presets (same as defaults):\n# platform_toolsets:\n# cli: [hermes-cli]\n# telegram: [hermes-telegram]\n#\n# # Custom: give Telegram only web + terminal + file + planning:\n# platform_toolsets:\n# telegram: [web, terminal, file, todo]\n#\n# # Custom: CLI without browser or image gen:\n# platform_toolsets:\n# cli: [web, terminal, file, skills, todo, tts, cronjob]\n#\n# # Restrictive: Discord gets read-only tools only:\n# platform_toolsets:\n# discord: [web, vision, skills, todo]\n#\n# If not set, defaults are:\n# cli: hermes-cli (everything + cronjob management)\n# telegram: hermes-telegram (terminal, file, web, vision, image, tts, browser, skills, todo, cronjob, messaging)\n# discord: hermes-discord (same as telegram)\n# whatsapp: hermes-whatsapp (same as telegram)\n# slack: hermes-slack (same as telegram)\n# signal: hermes-signal (same as telegram)\n# homeassistant: hermes-homeassistant (same as telegram)\n# qqbot: hermes-qqbot (same as telegram)\n# teams: hermes-teams (same as telegram)\n# google_chat: hermes-google_chat (same as telegram)\n#\nplatform_toolsets:\n cli: [hermes-cli]\n telegram: [hermes-telegram]\n discord: [hermes-discord]\n whatsapp: [hermes-whatsapp]\n slack: [hermes-slack]\n signal: [hermes-signal]\n homeassistant: [hermes-homeassistant]\n qqbot: [hermes-qqbot]\n yuanbao: [hermes-yuanbao]\n teams: [hermes-teams]\n google_chat: [hermes-google_chat]\n\n# =============================================================================\n# Gateway Platform Settings\n# =============================================================================\n# Optional per-platform messaging settings.\n# Platform-specific knobs live under `extra`.\n#\n# platforms:\n# telegram:\n# reply_to_mode: \"first\" # off | first | all\n# # guest_mode lets explicit @mentions from non-allowlisted groups through.\n# # Default false; ordinary messages, replies, and regex wake words stay blocked.\n# guest_mode: false\n# # allowed_chats: [\"-1001234567890\"]\n# extra:\n# disable_link_previews: false # Set true to suppress Telegram URL previews in bot messages\n# rich_messages: false # Bot API 10.1 rich messages (tables/task lists/details/math); default false for copyable legacy MarkdownV2, set true to opt in\n# rich_drafts: false # Experimental rich draft previews during Telegram DM streaming; default false because Telegram Desktop/macOS can visually overlay draft frames\n# command_menu:\n# # Telegram allows up to 100 BotCommands; Hermes defaults to 60 so\n# # all built-in commands plus common skill commands stay visible\n# # while remaining under Telegram's payload-size limit. Clamped 1..100.\n# max_commands: 60\n# # prepend = user priority first, then Hermes defaults\n# # append = Hermes defaults first, then user priority\n# # replace = only the list below defines priority\n# priority_mode: prepend\n# priority:\n# - my_plugin_command\n#\n# Discord-specific settings (config.yaml top-level, not under platforms:):\n#\n# discord:\n# require_mention: true # Require @mention in server channels (default: true)\n# auto_thread: true # Auto-create thread on @mention (default: true)\n# free_response_channels: \"\" # Channel IDs where no mention is needed\n# reactions: true # Show processing reactions (default: true)\n# history_backfill: true # Recover missed channel messages on mention (default: true)\n# history_backfill_limit: 50 # Max messages to scan backwards (default: 50)\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Available toolsets (use these names in platform_toolsets or the toolsets list)\n#\n# Run `hermes chat --list-toolsets` to see all toolsets and their tools.\n# Run `hermes chat --list-tools` to see every individual tool with descriptions.\n# ─────────────────────────────────────────────────────────────────────────────\n#\n# INDIVIDUAL TOOLSETS (compose your own):\n# web - web_search, web_extract\n# search - web_search only (no scraping)\n# terminal - terminal, process\n# file - read_file, write_file, patch, search\n# browser - browser_navigate, browser_snapshot, browser_click, browser_type,\n# browser_scroll, browser_back, browser_press,\n# browser_get_images, browser_vision (requires BROWSERBASE_API_KEY)\n# vision - vision_analyze (requires OPENROUTER_API_KEY)\n# image_gen - image_generate (requires FAL_KEY)\n# skills - skills_list, skill_view\n# skills_hub - skill_hub (search/install/manage from online registries — user-driven only)\n# moa - mixture_of_agents (requires OPENROUTER_API_KEY)\n# todo - todo (in-memory task planning, no deps)\n# tts - text_to_speech (Edge TTS free, or ELEVENLABS/OPENAI/MINIMAX/MISTRAL key)\n# cronjob - cronjob (create/list/update/pause/resume/run/remove scheduled tasks)\n#\n# PRESETS (curated bundles):\n# hermes-cli - All of the above except send_message\n# hermes-telegram - terminal, file, web, vision, image_gen, tts, browser,\n# skills, todo, cronjob, send_message\n# hermes-discord - Same as hermes-telegram\n# hermes-whatsapp - Same as hermes-telegram\n# hermes-slack - Same as hermes-telegram\n#\n# COMPOSITE:\n# debugging - terminal + web + file\n# safe - web + vision + moa (no terminal access)\n# all - Everything available\n#\n# web - Web search and content extraction (web_search, web_extract)\n# search - Web search only, no scraping (web_search)\n# terminal - Command execution and process management (terminal, process)\n# file - File operations: read, write, patch, search\n# browser - Full browser automation (navigate, click, type, screenshot, etc.)\n# vision - Image analysis (vision_analyze)\n# image_gen - Image generation with FLUX (image_generate)\n# skills - Load skill documents (skills_list, skill_view)\n# moa - Mixture of Agents reasoning (mixture_of_agents)\n# todo - Task planning and tracking for multi-step work\n# memory - Persistent memory across sessions (personal notes + user profile)\n# session_search - Search and recall past conversations (FTS5 + Gemini Flash summarization)\n# tts - Text-to-speech (Edge TTS free, ElevenLabs, OpenAI, MiniMax, Mistral)\n# cronjob - Schedule and manage automated tasks (CLI-only)\n#\n# Composite toolsets:\n# debugging - terminal + web + file (for troubleshooting)\n# safe - web + vision + moa (no terminal access)\n\n# NOTE: The top-level \"toolsets\" key is deprecated and ignored.\n# Tool configuration is managed per-platform via platform_toolsets above.\n# Use `hermes tools` to configure interactively, or edit platform_toolsets directly.\n#\n# CLI override: hermes chat --toolsets terminal,web,file\n\n# =============================================================================\n# MCP (Model Context Protocol) Servers\n# =============================================================================\n# Connect to external MCP servers to add tools from the MCP ecosystem.\n# Each server's tools are automatically discovered and registered.\n# See website/docs/user-guide/features/mcp.md for full documentation.\n#\n# Stdio servers (spawn a subprocess):\n# command: the executable to run\n# args: command-line arguments\n# env: environment variables (only these + safe defaults passed to subprocess)\n#\n# HTTP servers (connect to a URL):\n# url: the MCP server endpoint\n# headers: HTTP headers (e.g., for authentication)\n#\n# Optional per-server settings:\n# timeout: tool call timeout in seconds (default: 120)\n# connect_timeout: initial connection timeout (default: 60)\n# keepalive_interval: liveness ping cadence in seconds (default: 180).\n# Lower it below the server's session TTL for servers that expire idle\n# sessions quickly (e.g. Unreal Engine editor MCP, ~15s), otherwise idle\n# tool calls hit an expired session and pay a slow reconnect. Floored at 5s.\n#\n# mcp_servers:\n# time:\n# command: uvx\n# args: [\"mcp-server-time\"]\n# filesystem:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-filesystem\", \"/home/user\"]\n# notion:\n# url: https://mcp.notion.com/mcp\n# github:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-github\"]\n# env:\n# GITHUB_PERSONAL_ACCESS_TOKEN: \"ghp_...\"\n#\n# Sampling (server-initiated LLM requests) — enabled by default.\n# Per-server config under the 'sampling' key:\n# analysis:\n# command: npx\n# args: [\"-y\", \"analysis-server\"]\n# sampling:\n# enabled: true # default: true\n# model: \"gemini-3-flash\" # override model (optional)\n# max_tokens_cap: 4096 # max tokens per request\n# timeout: 30 # LLM call timeout (seconds)\n# max_rpm: 10 # max requests per minute\n# allowed_models: [] # model whitelist (empty = all)\n# max_tool_rounds: 5 # tool loop limit (0 = disable)\n# log_level: \"info\" # audit verbosity\n\n# =============================================================================\n# Text-to-Speech\n# =============================================================================\n# TTS defaults to Edge TTS unless changed in ~/.hermes/config.yaml.\n# Gemini TTS supports persona/director prompt files, and Gemini 3.1 Flash TTS\n# can use a hidden auxiliary rewrite pass to insert expressive square-bracket\n# audio tags into the TTS script without showing tags in chat.\n#\n# tts:\n# provider: \"gemini\"\n# gemini:\n# model: \"gemini-3.1-flash-tts-preview\"\n# voice: \"Kore\"\n# audio_tags: false\n# persona_prompt_file: \"\" # e.g. ~/.hermes/tts/radio-host.md\n\n# =============================================================================\n# Voice Transcription (Speech-to-Text)\n# =============================================================================\n# Automatically transcribe voice messages on messaging platforms.\n# Providers: local (free, faster-whisper) | groq (free tier) | openai (Whisper API) | mistral (Voxtral Transcribe)\n# Set the corresponding API key in .env: GROQ_API_KEY, OPENAI_API_KEY, or MISTRAL_API_KEY.\nstt:\n enabled: true\n # provider: \"local\" # auto-detected if omitted\n local:\n model: \"base\" # tiny | base | small | medium | large-v3 | turbo\n # language: \"\" # auto-detect; set to \"en\", \"es\", \"fr\", etc. to force\n openai:\n model: \"whisper-1\" # whisper-1 | gpt-4o-mini-transcribe | gpt-4o-transcribe\n # mistral:\n # model: \"voxtral-mini-latest\" # voxtral-mini-latest | voxtral-mini-2602\n\n# =============================================================================\n# Response Pacing (Messaging Platforms)\n# =============================================================================\n# Add human-like delays between message chunks.\n# human_delay:\n# mode: \"off\" # \"off\" | \"natural\" | \"custom\"\n# min_ms: 800 # Min delay (custom mode only)\n# max_ms: 2500 # Max delay (custom mode only)\n\n# =============================================================================\n# Session Logging\n# =============================================================================\n# Session trajectories are automatically saved to logs/ directory.\n# Each session creates: logs/session_YYYYMMDD_HHMMSS_UUID.json\n#\n# The session ID is displayed in the welcome banner for easy reference.\n# Logs contain full conversation history in trajectory format:\n# - System prompt, user messages, assistant responses\n# - Tool calls with inputs/outputs\n# - Timestamps for debugging\n#\n# No configuration needed - logging is always enabled.\n# To disable, you would need to modify the source code.\n\n# =============================================================================\n# Code Execution Sandbox (Programmatic Tool Calling)\n# =============================================================================\n# The execute_code tool runs Python scripts that call Hermes tools via RPC.\n# Intermediate tool results stay out of the LLM's context window.\ncode_execution:\n timeout: 300 # Max seconds per script before kill (default: 300 = 5 min)\n max_tool_calls: 50 # Max RPC tool calls per execution (default: 50)\n\n# =============================================================================\n# Subagent Delegation\n# =============================================================================\n# The delegate_task tool spawns child agents with isolated context.\n# Supports single tasks and batch mode (default 3 parallel, configurable).\ndelegation:\n max_iterations: 50 # Max tool-calling turns per child (default: 50)\n # max_concurrent_children: 3 # Max parallel child agents per batch (default: 3, floor: 1, no ceiling).\n # WARNING: values above 10 multiply API cost linearly.\n # max_spawn_depth: 1 # Delegation tree depth cap (range: 1-3, default: 1 = flat).\n # Raise to 2 to allow workers to spawn their own subagents.\n # Requires role=\"orchestrator\" on intermediate agents.\n # orchestrator_enabled: true # Kill switch for role=\"orchestrator\" children (default: true).\n # subagent_auto_approve: false # When a subagent hits a dangerous-command approval prompt, auto-deny (default: false)\n # or auto-approve \"once\" (true) instead of blocking on stdin.\n # The parent TUI owns stdin, so blocking would deadlock; non-interactive resolution is required.\n # Both choices emit a logger.warning audit line. Flip to true only for cron/batch pipelines.\n # inherit_mcp_toolsets: true # When explicit child toolsets are narrowed, also keep the parent's MCP toolsets (default: true). Set false for strict intersection.\n # model: \"google/gemini-3-flash-preview\" # Override model for subagents (empty = inherit parent)\n # provider: \"openrouter\" # Override provider for subagents (empty = inherit parent)\n # # Resolves full credentials (base_url, api_key) automatically.\n # # Supported: openrouter, nous, zai, kimi-coding, minimax\n\n# =============================================================================\n# Honcho Integration (Cross-Session User Modeling)\n# =============================================================================\n# AI-native persistent memory via Honcho (https://honcho.dev/).\n# Builds a deeper understanding of the user across sessions and tools.\n# Runs alongside USER.md — additive, not a replacement.\n#\n# Requires: pip install honcho-ai\n# Config: ~/.honcho/config.json (shared with Claude Code, Cursor, etc.)\n# API key: HONCHO_API_KEY in ~/.hermes/.env or ~/.honcho/config.json\n#\n# Hermes-specific overrides (optional — most config comes from ~/.honcho/config.json):\n# honcho: {}\n\n# =============================================================================\n# Display\n# =============================================================================\ndisplay:\n # Use compact banner mode (hides the ASCII-art banner, shows a single line).\n # true: Compact single-line banner\n # false: Full ASCII banner with tool/skill summary (default)\n compact: false\n\n # Tool progress display level (CLI and gateway)\n # off: Silent — no tool activity shown, just the final response\n # new: Show a tool indicator only when the tool changes (skip repeats)\n # all: Show every tool call with a short preview (default)\n # verbose: Full args, results, and debug logs (same as /verbose)\n # Toggle at runtime with /verbose in the CLI\n tool_progress: all\n\n # Per-platform defaults can be quieter than the global setting. Telegram\n # tunes for mobile: tool_progress and busy_ack_detail default off (no\n # per-tool breadcrumb stream, no \"iteration 21/60\" debug detail in busy\n # acks or heartbeats), but interim_assistant_messages and\n # long_running_notifications STAY ON so the user has real signal between\n # turn start and final answer (mid-turn assistant commentary + a single\n # edit-in-place \"⏳ Working — N min\" heartbeat). Override under\n # display.platforms.telegram.\n\n # Auto-cleanup of temporary progress bubbles after the final response lands.\n # On platforms that support message deletion (currently Telegram), this\n # removes the tool-progress bubble, \"⏳ Still working...\" notices, and\n # context-pressure status messages once the final reply has been delivered —\n # keeping long-running turns visible live, then tidy afterward. Failed runs\n # leave the bubbles in place as breadcrumbs. Off by default.\n # Per-platform override: display.platforms.telegram.cleanup_progress\n # true: Delete tracked progress/status bubbles on successful turn\n # false: Leave everything in place (default)\n # Example:\n # display:\n # platforms:\n # telegram:\n # cleanup_progress: true\n cleanup_progress: false\n\n # Gateway-only natural mid-turn assistant updates.\n # When true, completed assistant status messages are sent as separate chat\n # messages. This is independent of tool_progress and gateway streaming.\n # true: Send mid-turn assistant updates as separate messages (default)\n # false: Only send the final response\n interim_assistant_messages: true\n\n # Gateway-only long-running status heartbeats.\n # When false, the platform does not receive periodic \"⏳ Working — N min\"\n # notifications even if agent.gateway_notify_interval is non-zero. The\n # heartbeat edits a single message in place (where the adapter supports\n # editing) instead of posting a new bubble each interval.\n # Default: true everywhere, including Telegram (silent agents are worse\n # than a single edit-in-place heartbeat).\n long_running_notifications: true\n\n # Include detailed iteration/tool/status context in busy acknowledgments\n # and long-running heartbeats. When true, busy acks show \"iteration 21/60,\n # terminal, 10 min\" and the heartbeat shows \"⏳ Working — 12 min,\n # iteration 21/60, terminal\". When false (Telegram default), both stay\n # terse: \"Interrupting current task\" and \"⏳ Working — 12 min, terminal\".\n busy_ack_detail: true\n\n # What Enter does when Hermes is already busy (CLI and gateway platforms).\n # interrupt: Interrupt the current run and redirect Hermes (default)\n # queue: Queue your message for the next turn\n # steer: Inject your message mid-run via /steer, arriving at the agent\n # after the next tool call — no interrupt, no role violation.\n # Falls back to 'queue' if the agent isn't running yet or if\n # images are attached (steer only carries text).\n # Ctrl+C (or /stop in gateway) always interrupts regardless of this setting.\n # Toggle at runtime with /busy .\n busy_input_mode: interrupt\n\n # Background process notifications (gateway/messaging only).\n # Controls how chatty the process watcher is when you use\n # terminal(background=true, notify_on_complete=true) from Telegram/Discord/etc.\n # off: No watcher messages at all\n # result: Only the final completion message\n # error: Only the final message when exit code != 0\n # all: Running output updates + final message (default)\n background_process_notifications: all\n\n\n # Play terminal bell when agent finishes a response.\n # Useful for long-running tasks — your terminal will ding when the agent is done.\n # Works over SSH. Most terminals can be configured to flash the taskbar or play a sound.\n # true: Ring the terminal bell on each response\n # false: Silent (default)\n bell_on_complete: false\n\n # Show model reasoning/thinking before each response.\n # When enabled, a dim box shows the model's thought process above the response.\n # Toggle at runtime with /reasoning show or /reasoning hide.\n # true: Show the reasoning box\n # false: Hide reasoning (default)\n show_reasoning: false\n\n # Stream tokens to the terminal as they arrive instead of waiting for the\n # full response. The response box opens on first token and text appears\n # line-by-line. Tool calls are still captured silently.\n # true: Stream tokens as they arrive (default)\n # false: Wait for the full response before rendering\n streaming: true\n\n # Show [HH:MM] timestamps on user input and assistant response labels.\n # timestamps: false\n\n # ───────────────────────────────────────────────────────────────────────────\n # Skin / Theme\n # ───────────────────────────────────────────────────────────────────────────\n # Customize CLI visual appearance — banner colors, spinner faces, tool prefix,\n # response box label, and branding text. Change at runtime with /skin .\n #\n # Built-in skins:\n # default — Classic Hermes gold/kawaii\n # ares — Crimson/bronze war-god theme with spinner wings\n # mono — Clean grayscale monochrome\n # slate — Cool blue developer-focused\n # daylight — Bright light-mode theme\n # warm-lightmode — Warm paper-tone light-mode theme\n # poseidon — Sea-green/teal Olympian theme\n # sisyphus — Earthy stone-and-moss theme\n # charizard — Fiery orange dragon theme\n #\n # Custom skins: drop a YAML file in ~/.hermes/skins/.yaml\n # Schema (all fields optional, missing values inherit from default):\n #\n # name: my-theme\n # description: Short description\n # colors:\n # banner_border: \"#HEX\" # Panel border\n # banner_title: \"#HEX\" # Panel title\n # banner_accent: \"#HEX\" # Section headers (Available Tools, etc.)\n # banner_dim: \"#HEX\" # Dim/muted text\n # banner_text: \"#HEX\" # Body text (tool names, skill names)\n # ui_accent: \"#HEX\" # UI accent color\n # response_border: \"#HEX\" # Response box border color\n # spinner:\n # waiting_faces: [\"(⚔)\", \"(⛨)\"] # Faces shown while waiting\n # thinking_faces: [\"(⚔)\", \"(⌁)\"] # Faces shown while thinking\n # thinking_verbs: [\"forging\", \"plotting\"] # Verbs for spinner messages\n # wings: # Optional left/right spinner decorations\n # - [\"⟪⚔\", \"⚔⟫\"]\n # - [\"⟪▲\", \"▲⟫\"]\n # branding:\n # agent_name: \"My Agent\" # Banner title and branding\n # welcome: \"Welcome message\" # Shown at CLI startup\n # response_label: \" ⚔ Agent \" # Response box header label\n # prompt_symbol: \"⚔\" # Prompt symbol (bare token; renderers add trailing space)\n # tool_prefix: \"╎\" # Tool output line prefix (default: ┊)\n #\n skin: default\n\n# =============================================================================\n# Model Aliases — short names for /model command\n# =============================================================================\n# Map short aliases to exact (model, provider, base_url) tuples.\n# Used by /model tab completion and resolve_alias().\n# Aliases are checked BEFORE the models.dev catalog, so they can route\n# to endpoints not in the catalog (e.g. Ollama Cloud, local servers).\n#\n# model_aliases:\n# opus:\n# model: claude-opus-4-6\n# provider: anthropic\n# qwen:\n# model: \"qwen3.5:397b\"\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n# glm:\n# model: glm-4.7\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n\n# =============================================================================\n# Privacy\n# =============================================================================\n# privacy:\n# # Redact PII from the LLM context prompt.\n# # When true, phone numbers are stripped and user/chat IDs are replaced\n# # with deterministic hashes before being sent to the model.\n# # Names and usernames are NOT affected (user-chosen, publicly visible).\n# # Routing/delivery still uses the original values internally.\n# redact_pii: false\n\n# =============================================================================\n# Shell-script hooks\n# =============================================================================\n# Register shell scripts as plugin-hook callbacks. Each entry is executed as\n# a subprocess (shell=False, shlex.split) with a JSON payload on stdin. On\n# stdout the script may return JSON that either blocks the tool call or\n# injects context into the next LLM call.\n#\n# Valid events (mirror hermes_cli.plugins.VALID_HOOKS):\n# pre_tool_call, post_tool_call, pre_llm_call, post_llm_call,\n# pre_api_request, post_api_request, on_session_start, on_session_end,\n# on_session_finalize, on_session_reset, subagent_stop\n#\n# First-use consent: each (event, command) pair prompts once on a TTY, then\n# is persisted to ~/.hermes/shell-hooks-allowlist.json. Non-interactive\n# runs (gateway, cron) need --accept-hooks, HERMES_ACCEPT_HOOKS=1, or the\n# hooks_auto_accept key below.\n#\n# See website/docs/user-guide/features/hooks.md for the full JSON wire\n# protocol and worked examples.\n#\n# hooks:\n# pre_tool_call:\n# - matcher: \"terminal\"\n# command: \"~/.hermes/agent-hooks/block-rm-rf.sh\"\n# timeout: 10\n# post_tool_call:\n# - matcher: \"write_file|patch\"\n# command: \"~/.hermes/agent-hooks/auto-format.sh\"\n# pre_llm_call:\n# - command: \"~/.hermes/agent-hooks/inject-cwd-context.sh\"\n# subagent_stop:\n# - command: \"~/.hermes/agent-hooks/log-orchestration.sh\"\n#\n# hooks_auto_accept: false\n\n\n# =============================================================================\n# Update Behavior\n# =============================================================================\nupdates:\n # Create a full HERMES_HOME zip before every `hermes update`.\n # Backups land in ~/.hermes/backups/ and can be restored with `hermes import`.\n # Off by default because large homes can add minutes to every update.\n pre_update_backup: false\n\n # Number of pre-update backup zips to retain.\n backup_keep: 5\n\n # What non-interactive updates do with local source edits in the Hermes repo.\n # Interactive terminal updates always prompt before restoring the autostash.\n #\n # stash - auto-stash before pull, then auto-restore after success (default)\n # discard - drop the update-created stash after success; use only on managed\n # installs where local source edits should not persist\n non_interactive_local_changes: \"stash\"\n\n\n# =============================================================================\n# Web Dashboard\n# =============================================================================\n# OAuth gate configuration for `hermes dashboard --host `.\n# The bundled Nous Portal plugin reads these on startup; settings here are\n# the canonical surface. Each can be overridden by an environment variable:\n#\n# dashboard.oauth.client_id <- HERMES_DASHBOARD_OAUTH_CLIENT_ID\n# dashboard.oauth.portal_url <- HERMES_DASHBOARD_PORTAL_URL\n# dashboard.public_url <- HERMES_DASHBOARD_PUBLIC_URL\n#\n# Env wins when set to a non-empty value. This is what Fly.io's platform-\n# secret injection uses to push per-deploy client_ids without needing to\n# bake a config.yaml into the image. Empty env values are treated as unset\n# so a provisioned-but-not-populated secret can't shadow a valid entry here.\n#\n# Local dev / on-prem deploys should typically set these via config.yaml\n# (the ~/.hermes/.env file is reserved for API keys and secrets).\n#\n# dashboard:\n# oauth:\n# client_id: \"\" # agent:{instance_id}; Portal provisions this at deploy\n# portal_url: \"\" # blank → default https://portal.nousresearch.com\n#\n# # Force the absolute base URL the OAuth callback (and any other public\n# # URL the dashboard hands to external systems) is built from. Set this\n# # for deploys behind reverse proxies that don't reliably forward\n# # X-Forwarded-Host / X-Forwarded-Proto / X-Forwarded-Prefix (manual\n# # nginx setups, on-prem ingresses, custom-domain Fly deploys without\n# # full proxy header chains).\n# #\n# # When set, the value is the complete authority: scheme + host +\n# # optional path prefix (e.g. \"https://example.com/hermes\"). The OAuth\n# # callback URL becomes \"/auth/callback\" — X-Forwarded-Prefix\n# # is IGNORED on this code path because the operator has explicitly\n# # declared the public URL and we no longer need to guess.\n# #\n# # Leave empty to use the existing proxy-header reconstruction (the\n# # default — works on Fly.io out of the box).\n# #\n# # public_url: \"https://example.com/hermes\"\n"}, "d4be583d986f7bcca4f8414fd21ef9e34b18c948": {"t": 1785767904.4159143, "n": 64785, "h": "dec132652d2c66db", "c": "# Hermes Agent CLI Configuration\n# Copy this file to cli-config.yaml and customize as needed.\n# This file configures the CLI behavior. Environment variables in .env take precedence.\n\n# =============================================================================\n# Model Configuration\n# =============================================================================\nmodel:\n # Default model to use (can be overridden with --model flag)\n # Both \"default\" and \"model\" work as the key name here.\n default: \"anthropic/claude-opus-4.6\"\n \n # Inference provider selection:\n # \"auto\" - Auto-detect from credentials (default)\n # \"openrouter\" - OpenRouter (requires: OPENROUTER_API_KEY or OPENAI_API_KEY)\n # \"nous\" - Nous Portal OAuth (requires: hermes login)\n # \"nous-api\" - Nous Portal API key (requires: NOUS_API_KEY)\n # \"anthropic\" - Direct Anthropic API (requires: ANTHROPIC_API_KEY)\n # \"openai-codex\" - OpenAI Codex (requires: hermes auth)\n # \"copilot\" - GitHub Copilot / GitHub Models (requires: GITHUB_TOKEN)\n # \"gemini\" - Use Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n # \"zai\" - Use z.ai / ZhipuAI GLM models (requires: GLM_API_KEY)\n # \"kimi-coding\" - Kimi / Moonshot AI (requires: KIMI_API_KEY)\n # \"minimax\" - MiniMax global (requires: MINIMAX_API_KEY)\n # \"minimax-cn\" - MiniMax China (requires: MINIMAX_CN_API_KEY)\n # \"huggingface\" - Hugging Face Inference (requires: HF_TOKEN)\n # \"nvidia\" - NVIDIA NIM / build.nvidia.com (requires: NVIDIA_API_KEY)\n # \"xiaomi\" - Xiaomi MiMo (requires: XIAOMI_API_KEY)\n # \"arcee\" - Arcee AI Trinity models (requires: ARCEEAI_API_KEY)\n # \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY — https://ollama.com/settings)\n # \"kilocode\" - KiloCode gateway (requires: KILOCODE_API_KEY)\n # \"azure-foundry\" - Microsoft Foundry / Azure OpenAI (API key or Entra ID)\n # \"lmstudio\" - LM Studio local server (optional: LM_API_KEY, defaults to http://127.0.0.1:1234/v1)\n #\n # Local servers (LM Studio, Ollama, vLLM, llama.cpp):\n # \"custom\" - Any other OpenAI-compatible endpoint. Set base_url below.\n # Aliases: \"ollama\", \"vllm\", \"llamacpp\" all map to \"custom\".\n # LM Studio is first-class and uses provider: \"lmstudio\".\n # It works with both no-auth and auth-enabled server modes.\n #\n # Can also be overridden for a single invocation with the --provider flag.\n provider: \"auto\"\n \n # API configuration (falls back to OPENROUTER_API_KEY env var)\n # api_key: \"your-key-here\" # Uncomment to set here instead of .env\n base_url: \"https://openrouter.ai/api/v1\"\n\n # Azure Foundry keyless auth example:\n # provider: \"azure-foundry\"\n # base_url: \"https://.openai.azure.com/openai/v1\"\n # auth_mode: \"entra_id\" # DefaultAzureCredential: az login, managed identity, workload identity, etc.\n # default: \"gpt-4o\" # Deployment/model name\n # entra:\n # scope: \"https://ai.azure.com/.default\" # Optional; this is the default.\n\n # ── Token limits — two settings, easy to confuse ──────────────────────────\n #\n # context_length: TOTAL context window (input + output tokens combined).\n # Controls when Hermes compresses history and validates requests.\n # Leave unset — Hermes auto-detects the correct value from the provider.\n # Set manually only when auto-detection is wrong (e.g. a local server with\n # a custom num_ctx, or a proxy that doesn't expose /v1/models).\n #\n # context_length: 131072\n #\n # max_tokens: OUTPUT cap — maximum tokens the model may generate per response.\n # Unrelated to how long your conversation history can be.\n # The OpenAI-standard name \"max_tokens\" is a misnomer; Anthropic's native\n # API has since renamed it \"max_output_tokens\" for clarity.\n # Leave unset to use the model's native output ceiling (recommended).\n # Set only if you want to deliberately limit individual response length.\n #\n# max_tokens: 8192\n\n # ── Custom request headers (optional) ─────────────────────────────────────\n #\n # default_headers: extra HTTP headers sent on every request to an\n # OpenAI-compatible endpoint. User values take precedence over the\n # provider/SDK defaults, so this is the supported way to override the\n # OpenAI Python SDK's identifying headers (User-Agent: OpenAI/Python ...,\n # X-Stainless-*) when a custom provider sits behind a gateway/WAF that\n # rejects them — e.g. an upstream that returns \"502 Upstream access\n # forbidden\" for the SDK default User-Agent but accepts a plain one.\n # Applies on the OpenAI wire only (not native Anthropic / Bedrock).\n #\n # default_headers:\n # User-Agent: \"curl/8.7.1\"\n\n# Named provider overrides (optional)\n# Use this for per-provider request timeouts, non-stream stale timeouts,\n# and per-model exceptions.\n# Applies to the primary turn client on every api_mode (OpenAI-wire, native\n# Anthropic, and Anthropic-compatible providers), the fallback chain, and\n# client rebuilds during credential rotation. For OpenAI-wire chat\n# completions (streaming and non-streaming) the configured value is also\n# used as the per-request ``timeout=`` kwarg so it wins over the legacy\n# HERMES_API_TIMEOUT env var (which still applies when no config is set).\n# ``stale_timeout_seconds`` controls the non-streaming stale-call detector and\n# wins over the legacy HERMES_API_CALL_STALE_TIMEOUT env var. Leaving these\n# unset keeps the legacy defaults (HERMES_API_TIMEOUT=1800s,\n# HERMES_API_CALL_STALE_TIMEOUT=90s, native Anthropic 900s). The\n# implicit non-stream stale detector is auto-disabled for local endpoints\n# and can scale upward for very large contexts.\n#\n# Not currently wired for AWS Bedrock (bedrock_converse + AnthropicBedrock\n# SDK paths) — those use boto3 with its own timeout configuration.\n#\n# providers:\n# ollama-local:\n# request_timeout_seconds: 300 # Longer timeout for local cold-starts\n# stale_timeout_seconds: 900 # Explicitly re-enable stale detection on local endpoints\n# anthropic:\n# request_timeout_seconds: 30 # Fast-fail cloud requests\n# models:\n# claude-opus-4.6:\n# timeout_seconds: 600 # Longer timeout for extended-thinking Opus calls\n# openai-codex:\n# models:\n# gpt-5.4:\n# stale_timeout_seconds: 1800 # Longer non-stream stale timeout for slow large-context turns\n\n# =============================================================================\n# OpenRouter Provider Routing (only applies when using OpenRouter)\n# =============================================================================\n# Control how requests are routed across providers on OpenRouter.\n# See: https://openrouter.ai/docs/guides/routing/provider-selection\n#\n# provider_routing:\n# # Sort strategy: \"price\" (default), \"throughput\", or \"latency\"\n# # Append :nitro to model name for a shortcut to throughput sorting.\n# sort: \"throughput\"\n#\n# # Only allow these providers (provider slugs from OpenRouter)\n# # only: [\"anthropic\", \"google\"]\n#\n# # Skip these providers entirely\n# # ignore: [\"deepinfra\", \"fireworks\"]\n#\n# # Try providers in this order (overrides default load balancing)\n# # order: [\"anthropic\", \"google\", \"together\"]\n#\n# # Require providers to support all parameters in your request\n# # require_parameters: true\n#\n# # Data policy: \"allow\" (default) or \"deny\" to exclude providers that may store data\n# # data_collection: \"deny\"\n\n# =============================================================================\n# OpenRouter Response Caching (only applies when using OpenRouter)\n# =============================================================================\n# Cache identical API responses at the OpenRouter edge for free instant replays.\n# When enabled, identical requests (same model, messages, parameters) return\n# cached responses with zero billing. Separate from Anthropic prompt caching.\n# See: https://openrouter.ai/docs/guides/features/response-caching\n#\n# openrouter:\n# response_cache: true # Enable response caching (default: true)\n# response_cache_ttl: 300 # Cache TTL in seconds, 1-86400 (default: 300)\n\n# =============================================================================\n# Git Worktree Isolation\n# =============================================================================\n# When enabled, each CLI session creates an isolated git worktree so multiple\n# agents can work on the same repo concurrently without file collisions.\n# Equivalent to always passing --worktree / -w on the command line.\n#\n# worktree: true # Always create a worktree when in a git repo\n# worktree: false # Default — only create when -w flag is passed\n#\n# By default a new worktree branches from the freshly-fetched remote tip\n# (the current branch's upstream, else the remote's default branch) so it\n# starts current with the project instead of from the local clone's\n# (possibly stale) HEAD. Set worktree_sync: false to branch from local HEAD\n# instead — useful when offline or when you deliberately want the clone's\n# exact current state as the base.\n#\n# worktree_sync: true # Default — branch from the fetched remote tip\n# worktree_sync: false # Branch from local HEAD (offline / pinned base)\n\n# =============================================================================\n# Terminal Tool Configuration\n# =============================================================================\n# Choose ONE of the following terminal configurations by uncommenting it.\n# The terminal tool executes commands in the specified environment.\n\n# -----------------------------------------------------------------------------\n# OPTION 1: Local execution (default)\n# Commands run directly on your machine in the current directory\n# -----------------------------------------------------------------------------\n# Working directory behavior:\n# - CLI (`hermes` command): Uses \".\" (current directory where you run hermes)\n# - Gateway/messaging/cron: Uses terminal.cwd here; legacy .env cwd values are deprecated\nterminal:\n backend: \"local\"\n cwd: \".\" # For local backend: \".\" = current directory. Ignored for remote backends unless a backend documents otherwise.\n timeout: 180\n # HOME policy for tool subprocesses:\n # auto - default: host uses your real HOME; containers use HERMES_HOME/home\n # real - force your real OS-user HOME\n # profile - force HERMES_HOME/home for strict per-profile CLI config isolation\n home_mode: \"auto\"\n docker_mount_cwd_to_workspace: false # SECURITY: off by default. Opt in to mount the launch cwd into Docker /workspace.\n lifetime_seconds: 300\n # sudo_password: \"hunter2\" # Optional: pipe a sudo password via sudo -S. SECURITY WARNING: plaintext.\n # sudo_password: \"\" # Explicit empty password: try empty and never open the interactive sudo prompt.\n\n# -----------------------------------------------------------------------------\n# OPTION 2: SSH remote execution\n# Commands run on a remote server - agent code stays local (sandboxed)\n# Great for: keeping agent isolated from its own code, using powerful remote hardware\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"ssh\"\n# cwd: \"/home/myuser/project\" # Path on the REMOTE server\n# timeout: 180\n# lifetime_seconds: 300\n# ssh_host: \"my-server.example.com\"\n# ssh_user: \"myuser\"\n# ssh_port: 22\n# ssh_key: \"~/.ssh/id_rsa\" # Optional - uses ssh-agent if not specified\n\n# -----------------------------------------------------------------------------\n# OPTION 3: Docker container\n# Commands run in an isolated Docker container\n# Great for: reproducible environments, testing, isolation\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"docker\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /)\n# timeout: 180\n# lifetime_seconds: 300\n# docker_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# docker_mount_cwd_to_workspace: true # Explicit opt-in: mount your launch cwd into /workspace\n# # Optional: run the container as your host user's uid:gid so files written\n# # into bind-mounted dirs are owned by you, not root. Drops SETUID/SETGID\n# # caps too since no gosu privilege drop is needed. Leave off if your\n# # chosen docker_image expects to start as root.\n# docker_run_as_host_user: true\n# # Optional: explicitly forward selected env vars into Docker.\n# # These values come from your current shell first, then ~/.hermes/.env.\n# # Warning: anything forwarded here is visible to commands run in the container.\n# docker_forward_env:\n# - \"GITHUB_TOKEN\"\n# - \"NPM_TOKEN\"\n# # Optional: extra flags passed verbatim to docker run (appended after security defaults).\n# # Useful for adding capabilities (e.g. apt installs needing SETUID) or custom options.\n# # Example: add a Linux capability not included by default\n# # docker_extra_args:\n# # - \"--cap-add\"\n# # - \"SETUID\"\n\n# -----------------------------------------------------------------------------\n# OPTION 4: Singularity/Apptainer container\n# Commands run in a Singularity container (common in HPC environments)\n# Great for: HPC clusters, shared compute environments\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"singularity\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# singularity_image: \"docker://nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 5: Modal cloud execution\n# Commands run on Modal's cloud infrastructure\n# Great for: GPU access, scalable compute, serverless execution\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"modal\"\n# cwd: \"/workspace\" # Path INSIDE the sandbox (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# modal_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 6: Daytona cloud execution\n# Commands run in Daytona cloud sandboxes\n# Great for: Cloud dev environments, persistent workspaces, team collaboration\n# Requires: pip install daytona, DAYTONA_API_KEY env var\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"daytona\"\n# cwd: \"~\"\n# timeout: 180\n# lifetime_seconds: 300\n# daytona_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# container_disk: 10240 # Daytona max is 10GB per sandbox\n\n#\n# --- Container resource limits (docker, singularity, modal, daytona -- ignored for local/ssh) ---\n# These settings apply to all container backends. They control the resources\n# allocated to the sandbox and whether its filesystem persists across sessions.\n container_cpu: 1 # CPU cores\n container_memory: 5120 # Memory in MB (5120 = 5GB)\n container_disk: 51200 # Disk in MB (51200 = 50GB)\n container_persistent: true # Persist filesystem across sessions (false = ephemeral)\n\n# -----------------------------------------------------------------------------\n# SUDO SUPPORT (works with ALL backends above)\n# -----------------------------------------------------------------------------\n# Add sudo_password to any terminal config above to enable sudo commands.\n# The password is piped via `sudo -S`. Works with local, ssh, docker, etc.\n#\n# SECURITY WARNING: Password stored in plaintext!\n#\n# INTERACTIVE PROMPT: If sudo_password is unset and the CLI is running,\n# you'll be prompted to enter your password when sudo is needed:\n# - 45-second timeout (auto-skips if no input)\n# - Press Enter to skip (command fails gracefully)\n# - Password is hidden while typing\n# - Password is cached for the session\n#\n# EMPTY PASSWORDS: Setting sudo_password to an explicit empty string is different\n# from leaving it unset. Hermes will try an empty password via `sudo -S` and\n# will not open the interactive prompt. This is useful for passwordless sudo,\n# Touch ID sudo setups, and environments where prompting is just noise.\n#\n# ALTERNATIVES:\n# - SSH backend: Configure passwordless sudo on the remote server\n# - Containers: Run as root inside the container (no sudo needed)\n# - Local: Configure /etc/sudoers for specific commands\n#\n# Example (add to your terminal section):\n# sudo_password: \"your-password-here\"\n\n# =============================================================================\n# Security Scanning (tirith)\n# =============================================================================\n# Optional pre-exec command security scanning via tirith.\n# Detects homograph URLs, pipe-to-shell, terminal injection, env manipulation.\n# Install: brew install sheeki03/tap/tirith\n# Docs: https://github.com/sheeki03/tirith\n#\n# security:\n# tirith_enabled: true # Enable/disable tirith scanning\n# tirith_path: \"tirith\" # Path to tirith binary (supports ~ expansion)\n# tirith_timeout: 5 # Scan timeout in seconds\n# tirith_fail_open: true # Allow commands if tirith unavailable\n\n# =============================================================================\n# Browser Tool Configuration\n# =============================================================================\nbrowser:\n # Inactivity timeout in seconds - browser sessions are automatically closed\n # after this period of no activity between agent loops (default: 120 = 2 minutes)\n inactivity_timeout: 120\n\n# =============================================================================\n# Tool Loop Guardrails\n# =============================================================================\n# Soft warnings are enabled by default. They append guidance to repeated failed\n# or non-progressing tool results but still let the tool execute. Hard stops are\n# opt-in circuit breakers for autonomous/cron sessions where stopping a loop is\n# preferable to spending the full iteration budget.\ntool_loop_guardrails:\n warnings_enabled: true\n hard_stop_enabled: false\n warn_after:\n exact_failure: 2\n same_tool_failure: 3\n idempotent_no_progress: 2\n hard_stop_after:\n exact_failure: 5\n same_tool_failure: 8\n idempotent_no_progress: 5\n\n# =============================================================================\n# Context Compression (Auto-shrinks long conversations)\n# =============================================================================\n# When conversation approaches model's context limit, middle turns are\n# automatically summarized to free up space while preserving important context.\n#\n# HOW IT WORKS:\n# 1. Tracks actual token usage from API responses (not estimates)\n# 2. When prompt_tokens >= threshold% of model's context_length, triggers compression\n# 3. Protects first 3 turns (system prompt, initial request, first response)\n# 4. Protects last N turns (default 20 messages = ~10 full turns of recent context)\n# 5. Summarizes middle turns using a fast/cheap model\n# 6. Inserts summary as a user message, continues conversation seamlessly\n#\n# Post-compression tail budget is target_ratio × threshold × context_length:\n# 200K context, threshold 0.50, ratio 0.20 → 20K tokens of recent tail preserved\n# 1M context, threshold 0.50, ratio 0.20 → 100K tokens of recent tail preserved\n#\ncompression:\n # Enable automatic context compression (default: true)\n # Set to false if you prefer to manage context manually or want errors on overflow\n enabled: true\n \n # Trigger compression at this % of model's context limit (default: 0.50 = 50%)\n # Lower values = more aggressive compression, higher values = compress later\n threshold: 0.50\n \n # Fraction of the threshold to preserve as recent tail (default: 0.20 = 20%)\n # e.g. 20% of 50% threshold = 10% of total context kept as recent messages.\n # Summary output is separately capped at 12K tokens (Gemini output limit).\n # Range: 0.10 - 0.80\n target_ratio: 0.20\n\n # Number of most-recent messages to always preserve (default: 20 ≈ 10 full turns)\n # Higher values keep more recent conversation intact at the cost of more aggressive\n # compression of older turns.\n protect_last_n: 20\n\n # Number of non-system messages to protect at the head of the transcript, in\n # ADDITION to the system prompt (which is always implicitly protected).\n # Head messages are NEVER summarized — they survive every compression\n # indefinitely. This gives stable early context for short/medium sessions,\n # but in long-running sessions that rely on rolling compaction the pinned\n # opening turns may not match how you want the session framed over time.\n # Set to 0 to preserve ONLY the system prompt (plus the rolling summary\n # and recent tail) — the cleanest configuration for long-running sessions.\n # Default 3 preserves the system prompt plus the first three non-system\n # head messages, matching the pre-feature behaviour.\n protect_first_n: 3\n\n # To pin a specific model/provider for compression summaries, use the\n # auxiliary section below (auxiliary.compression.provider / model).\n\n# =============================================================================\n# Anthropic prompt caching TTL\n# =============================================================================\n# When prompt caching is active (Claude via OpenRouter or native Anthropic),\n# Anthropic supports two TTL tiers for cached prefixes: \"5m\" (default) and\n# \"1h\". Other values are ignored and \"5m\" is used.\n#\nprompt_caching:\n cache_ttl: \"5m\" # use \"1h\" for long sessions with pauses between turns\n\n# =============================================================================\n# Auxiliary Models (Advanced — Experimental)\n# =============================================================================\n# Hermes uses lightweight \"auxiliary\" models for side tasks: image analysis,\n# browser screenshot analysis, web page summarization, TTS audio-tag insertion,\n# and context compression.\n#\n# By default these use Gemini Flash via OpenRouter or Nous Portal and are\n# auto-detected from your credentials. You do NOT need to change anything\n# here for normal usage.\n#\n# WARNING: Overriding these with providers other than OpenRouter or Nous Portal\n# is EXPERIMENTAL and may not work. Not all models/providers support vision,\n# produce usable summaries, or accept the same API format. Change at your own\n# risk — if things break, reset to \"auto\" / empty values.\n#\n# Each task has its own provider + model pair so you can mix providers.\n# For example: OpenRouter for vision (needs multimodal), but your main\n# local endpoint for compression (just needs text).\n#\n# Provider options:\n# \"auto\" - Best available: OpenRouter → Nous Portal → main endpoint (default)\n# \"openrouter\" - Force OpenRouter (requires OPENROUTER_API_KEY)\n# \"nous\" - Force Nous Portal (requires: hermes login)\n# \"gemini\" - Force Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n# \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY)\n# \"codex\" - Force Codex OAuth (requires: hermes model → Codex).\n# Uses gpt-5.3-codex which supports vision.\n# \"main\" - Use your custom endpoint (OPENAI_BASE_URL + OPENAI_API_KEY).\n# Works with OpenAI API, local models, or any OpenAI-compatible\n# endpoint. Also falls back to Codex OAuth and API-key providers.\n#\n# Model: leave empty to use the provider's default. When empty, OpenRouter\n# uses \"google/gemini-3-flash-preview\" and Nous uses \"gemini-3-flash\".\n# Other providers pick a sensible default automatically.\n#\n# auxiliary:\n# # Image analysis: vision_analyze tool + browser screenshots\n# vision:\n# provider: \"auto\"\n# model: \"\" # e.g. \"google/gemini-2.5-flash\", \"openai/gpt-4o\"\n# timeout: 30 # LLM API call timeout (seconds)\n# download_timeout: 30 # Image HTTP download timeout (seconds)\n# # Increase for slow connections or self-hosted image servers\n#\n# # Web page scraping / summarization + browser page text extraction\n# web_extract:\n# provider: \"auto\"\n# model: \"\"\n#\n# # Gemini 3.1 TTS hidden audio-tag insertion\n# tts_audio_tags:\n# provider: \"auto\" # empty model = your main chat model\n# model: \"\"\n# timeout: 30\n#\n# # Session search — summarizes matching past sessions\n# session_search:\n# provider: \"auto\"\n# model: \"\"\n# timeout: 30\n# max_concurrency: 3 # Limit parallel summaries to reduce request-burst 429s\n# extra_body: {} # Provider-specific OpenAI-compatible request fields\n# # Example for providers that support request-body\n# # reasoning controls:\n# # extra_body:\n# # enable_thinking: false\n# # Some vLLM/Qwen deployments expect this nested:\n# # extra_body:\n# # chat_template_kwargs:\n# # enable_thinking: false\n\n# =============================================================================\n# Persistent Memory\n# =============================================================================\n# Bounded curated memory injected into the system prompt every session.\n# Two stores: MEMORY.md (agent's notes) and USER.md (user profile).\n# Character limits keep the memory small and focused. The agent manages\n# pruning -- when at the limit, it must consolidate or replace entries.\n# Disabled by default in batch_runner.\n#\nmemory:\n # Agent's personal notes: environment facts, conventions, things learned\n memory_enabled: true\n \n # User profile: preferences, communication style, expectations\n user_profile_enabled: true\n \n # Character limits (~2.75 chars per token, model-independent)\n memory_char_limit: 2200 # ~800 tokens\n user_char_limit: 1375 # ~500 tokens\n\n # Periodic memory nudge: remind the agent to consider saving memories\n # every N user turns. Set to 0 to disable. Only active when memory is enabled.\n nudge_interval: 10 # Nudge every 10 user turns (0 = disabled)\n\n # Memory flush: give the agent one turn to save memories before context is\n # lost (compression, /new, /reset, exit). Set to 0 to disable.\n # For exit/reset, only fires if the session had at least this many user turns.\n flush_min_turns: 6 # Min user turns to trigger flush on exit/reset (0 = disabled)\n\n# =============================================================================\n# Session Reset Policy (Messaging Platforms)\n# =============================================================================\n# Controls when messaging sessions (Telegram, Discord, WhatsApp, Slack) are\n# automatically cleared. Without resets, conversation context grows indefinitely\n# which increases API costs with every message.\n#\n# When a reset triggers, the agent first saves important information to its\n# persistent memory — but the conversation context is wiped. The agent starts\n# fresh but retains learned facts via its memory system.\n#\n# Users can always manually reset with /reset or /new in chat.\n#\n# Modes:\n# \"both\" - Reset on EITHER inactivity timeout or daily boundary (recommended)\n# \"idle\" - Reset only after N minutes of inactivity\n# \"daily\" - Reset only at a fixed hour each day\n# \"none\" - Never auto-reset; context lives until /reset or compression kicks in\n#\n# When a reset triggers, the agent gets one turn to save important memories and\n# skills before the context is wiped. Persistent memory carries across sessions.\n#\nsession_reset:\n mode: both # \"both\", \"idle\", \"daily\", or \"none\"\n idle_minutes: 1440 # Inactivity timeout in minutes (default: 1440 = 24 hours)\n at_hour: 4 # Daily reset hour, 0-23 local time (default: 4 AM)\n\n# Maximum number of simultaneously active chat sessions across CLI, TUI,\n# dashboard chat, and messaging gateway. Set to null, 0, or omit to allow\n# unlimited concurrent sessions. When the limit is reached, new sessions get a\n# clean error while existing active sessions keep their normal behavior. This\n# top-level key takes precedence over gateway.max_concurrent_sessions. The cap\n# is a best-effort single-host/profile runtime guard; Hermes fails open if the\n# local runtime lease registry cannot be read or locked.\nmax_concurrent_sessions: null\n\n# When true, group/channel chats use one session per participant when the platform\n# provides a user ID. This is the secure default and prevents users in the same\n# room from sharing context, interrupts, and token costs. Set false only if you\n# explicitly want one shared \"room brain\" per group/channel.\ngroup_sessions_per_user: true\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Gateway Streaming\n# ─────────────────────────────────────────────────────────────────────────────\n# Stream tokens to messaging platforms in real-time. The bot sends a message\n# on first token, then progressively edits it as more tokens arrive.\n# Disabled by default — enable to try the streaming UX on Telegram/Discord/Slack.\n# For Telegram, partial edits are sent as plain text and only the final edit uses MarkdownV2.\nstreaming:\n enabled: false\n # transport: edit # \"edit\" = progressive editMessageText\n # edit_interval: 0.3 # seconds between message edits\n # buffer_threshold: 40 # chars before forcing an edit flush\n # cursor: \" ▉\" # cursor shown during streaming\n\n# =============================================================================\n# Skills Configuration\n# =============================================================================\n# Skills are reusable procedures the agent can load and follow. The agent can\n# also create new skills after completing complex tasks.\n#\nskills:\n # Nudge the agent to create skills after complex tasks.\n # Every N tool-calling iterations, remind the model to consider saving a skill.\n # Set to 0 to disable.\n creation_nudge_interval: 15\n\n # External skill directories — share skills across tools/agents without\n # copying them into ~/.hermes/skills/. Each path is expanded (~ and ${VAR})\n # and resolved to an absolute path. External dirs are read-only: skill\n # creation always writes to ~/.hermes/skills/. Local skills take precedence\n # when names collide.\n # external_dirs:\n # - ~/.agents/skills\n # - /home/shared/team-skills\n\n# =============================================================================\n# Agent Behavior\n# =============================================================================\nagent:\n # Maximum tool-calling iterations per conversation\n # Higher = more room for complex tasks, but costs more tokens\n # Recommended: 20-30 for focused tasks, 50-100 for open exploration\n max_turns: 60\n\n # Inactivity timeout for gateway agent runs (seconds, 0 = unlimited).\n # The agent can run indefinitely when actively calling tools or receiving\n # API responses. Only fires after the agent has been idle for this duration.\n # gateway_timeout: 1800\n\n # Staged warning: send a warning before escalating to full timeout.\n # Fires once per run when inactivity reaches this threshold (seconds).\n # Set to 0 to disable the warning.\n # gateway_timeout_warning: 900\n\n # Graceful drain timeout for gateway stop/restart (seconds).\n # The gateway stops accepting new work, waits for in-flight agents to\n # finish, then interrupts anything still running after this timeout.\n # 0 = no drain, interrupt immediately.\n # restart_drain_timeout: 60\n\n # Max app-level retry attempts for API errors (connection drops, provider\n # timeouts, 5xx, etc.) before the agent surfaces the failure. Lower this\n # to 1 if you use fallback providers and want fast failover on flaky\n # primaries (default 3). The OpenAI SDK does its own low-level retries\n # underneath this wrapper — this is the Hermes-level loop.\n # api_max_retries: 3\n \n # Enable verbose logging\n verbose: false\n \n # Reasoning effort level (OpenRouter and Nous Portal)\n # Controls how much \"thinking\" the model does before responding.\n # Options: \"xhigh\" (max), \"high\", \"medium\", \"low\", \"minimal\", \"none\" (disable)\n reasoning_effort: \"medium\"\n \n # Predefined personalities (use with /personality command)\n personalities:\n helpful: \"You are a helpful, friendly AI assistant.\"\n concise: \"You are a concise assistant. Keep responses brief and to the point.\"\n technical: \"You are a technical expert. Provide detailed, accurate technical information.\"\n creative: \"You are a creative assistant. Think outside the box and offer innovative solutions.\"\n teacher: \"You are a patient teacher. Explain concepts clearly with examples.\"\n kawaii: \"You are a kawaii assistant! Use cute expressions like (◕‿◕), ★, ♪, and ~! Add sparkles and be super enthusiastic about everything! Every response should feel warm and adorable desu~! ヽ(>∀<☆)ノ\"\n catgirl: \"You are Neko-chan, an anime catgirl AI assistant, nya~! Add 'nya' and cat-like expressions to your speech. Use kaomoji like (=^・ω・^=) and ฅ^•ﻌ•^ฅ. Be playful and curious like a cat, nya~!\"\n pirate: \"Arrr! Ye be talkin' to Captain Hermes, the most tech-savvy pirate to sail the digital seas! Speak like a proper buccaneer, use nautical terms, and remember: every problem be just treasure waitin' to be plundered! Yo ho ho!\"\n shakespeare: \"Hark! Thou speakest with an assistant most versed in the bardic arts. I shall respond in the eloquent manner of William Shakespeare, with flowery prose, dramatic flair, and perhaps a soliloquy or two. What light through yonder terminal breaks?\"\n surfer: \"Duuude! You're chatting with the chillest AI on the web, bro! Everything's gonna be totally rad. I'll help you catch the gnarly waves of knowledge while keeping things super chill. Cowabunga! 🤙\"\n noir: \"The rain hammered against the terminal like regrets on a guilty conscience. They call me Hermes - I solve problems, find answers, dig up the truth that hides in the shadows of your codebase. In this city of silicon and secrets, everyone's got something to hide. What's your story, pal?\"\n uwu: \"hewwo! i'm your fwiendwy assistant uwu~ i wiww twy my best to hewp you! *nuzzles your code* OwO what's this? wet me take a wook! i pwomise to be vewy hewpful >w<\"\n philosopher: \"Greetings, seeker of wisdom. I am an assistant who contemplates the deeper meaning behind every query. Let us examine not just the 'how' but the 'why' of your questions. Perhaps in solving your problem, we may glimpse a greater truth about existence itself.\"\n hype: \"YOOO LET'S GOOOO!!! 🔥🔥🔥 I am SO PUMPED to help you today! Every question is AMAZING and we're gonna CRUSH IT together! This is gonna be LEGENDARY! ARE YOU READY?! LET'S DO THIS! 💪😤🚀\"\n\n# =============================================================================\n# Toolsets\n# =============================================================================\n# Control which tools the agent has access to.\n# Use `hermes tools` to interactively enable/disable tools per platform.\n\n# =============================================================================\n# Platform Toolsets (per-platform tool configuration)\n# =============================================================================\n# Override which toolsets are available on each platform.\n# If a platform isn't listed here, its built-in default is used.\n#\n# You can use EITHER:\n# - A preset like \"hermes-cli\" or \"hermes-telegram\" (curated tool set)\n# - A list of individual toolsets to compose your own (see list below)\n#\n# Supported platform keys: cli, telegram, discord, whatsapp, slack, qqbot, teams, google_chat\n#\n# Examples:\n#\n# # Use presets (same as defaults):\n# platform_toolsets:\n# cli: [hermes-cli]\n# telegram: [hermes-telegram]\n#\n# # Custom: give Telegram only web + terminal + file + planning:\n# platform_toolsets:\n# telegram: [web, terminal, file, todo]\n#\n# # Custom: CLI without browser or image gen:\n# platform_toolsets:\n# cli: [web, terminal, file, skills, todo, tts, cronjob]\n#\n# # Restrictive: Discord gets read-only tools only:\n# platform_toolsets:\n# discord: [web, vision, skills, todo]\n#\n# If not set, defaults are:\n# cli: hermes-cli (everything + cronjob management)\n# telegram: hermes-telegram (terminal, file, web, vision, image, tts, browser, skills, todo, cronjob, messaging)\n# discord: hermes-discord (same as telegram)\n# whatsapp: hermes-whatsapp (same as telegram)\n# slack: hermes-slack (same as telegram)\n# signal: hermes-signal (same as telegram)\n# homeassistant: hermes-homeassistant (same as telegram)\n# qqbot: hermes-qqbot (same as telegram)\n# teams: hermes-teams (same as telegram)\n# google_chat: hermes-google_chat (same as telegram)\n#\nplatform_toolsets:\n cli: [hermes-cli]\n telegram: [hermes-telegram]\n discord: [hermes-discord]\n whatsapp: [hermes-whatsapp]\n slack: [hermes-slack]\n signal: [hermes-signal]\n homeassistant: [hermes-homeassistant]\n qqbot: [hermes-qqbot]\n yuanbao: [hermes-yuanbao]\n teams: [hermes-teams]\n google_chat: [hermes-google_chat]\n\n# =============================================================================\n# Gateway Platform Settings\n# =============================================================================\n# Optional per-platform messaging settings.\n# Platform-specific knobs live under `extra`.\n#\n# platforms:\n# telegram:\n# reply_to_mode: \"first\" # off | first | all\n# # guest_mode lets explicit @mentions from non-allowlisted groups through.\n# # Default false; ordinary messages, replies, and regex wake words stay blocked.\n# guest_mode: false\n# # allowed_chats: [\"-1001234567890\"]\n# extra:\n# disable_link_previews: false # Set true to suppress Telegram URL previews in bot messages\n# rich_messages: false # Bot API 10.1 rich messages (tables/task lists/details/math); default false for copyable legacy MarkdownV2, set true to opt in\n# command_menu:\n# # Telegram allows up to 100 BotCommands; Hermes defaults to 60 so\n# # all built-in commands plus common skill commands stay visible\n# # while remaining under Telegram's payload-size limit. Clamped 1..100.\n# max_commands: 60\n# # prepend = user priority first, then Hermes defaults\n# # append = Hermes defaults first, then user priority\n# # replace = only the list below defines priority\n# priority_mode: prepend\n# priority:\n# - my_plugin_command\n#\n# Discord-specific settings (config.yaml top-level, not under platforms:):\n#\n# discord:\n# require_mention: true # Require @mention in server channels (default: true)\n# auto_thread: true # Auto-create thread on @mention (default: true)\n# free_response_channels: \"\" # Channel IDs where no mention is needed\n# reactions: true # Show processing reactions (default: true)\n# history_backfill: true # Recover missed channel messages on mention (default: true)\n# history_backfill_limit: 50 # Max messages to scan backwards (default: 50)\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Available toolsets (use these names in platform_toolsets or the toolsets list)\n#\n# Run `hermes chat --list-toolsets` to see all toolsets and their tools.\n# Run `hermes chat --list-tools` to see every individual tool with descriptions.\n# ─────────────────────────────────────────────────────────────────────────────\n#\n# INDIVIDUAL TOOLSETS (compose your own):\n# web - web_search, web_extract\n# search - web_search only (no scraping)\n# terminal - terminal, process\n# file - read_file, write_file, patch, search\n# browser - browser_navigate, browser_snapshot, browser_click, browser_type,\n# browser_scroll, browser_back, browser_press,\n# browser_get_images, browser_vision (requires BROWSERBASE_API_KEY)\n# vision - vision_analyze (requires OPENROUTER_API_KEY)\n# image_gen - image_generate (requires FAL_KEY)\n# skills - skills_list, skill_view\n# skills_hub - skill_hub (search/install/manage from online registries — user-driven only)\n# moa - mixture_of_agents (requires OPENROUTER_API_KEY)\n# todo - todo (in-memory task planning, no deps)\n# tts - text_to_speech (Edge TTS free, or ELEVENLABS/OPENAI/MINIMAX/MISTRAL key)\n# cronjob - cronjob (create/list/update/pause/resume/run/remove scheduled tasks)\n#\n# PRESETS (curated bundles):\n# hermes-cli - All of the above except send_message\n# hermes-telegram - terminal, file, web, vision, image_gen, tts, browser,\n# skills, todo, cronjob, send_message\n# hermes-discord - Same as hermes-telegram\n# hermes-whatsapp - Same as hermes-telegram\n# hermes-slack - Same as hermes-telegram\n#\n# COMPOSITE:\n# debugging - terminal + web + file\n# safe - web + vision + moa (no terminal access)\n# all - Everything available\n#\n# web - Web search and content extraction (web_search, web_extract)\n# search - Web search only, no scraping (web_search)\n# terminal - Command execution and process management (terminal, process)\n# file - File operations: read, write, patch, search\n# browser - Full browser automation (navigate, click, type, screenshot, etc.)\n# vision - Image analysis (vision_analyze)\n# image_gen - Image generation with FLUX (image_generate)\n# skills - Load skill documents (skills_list, skill_view)\n# moa - Mixture of Agents reasoning (mixture_of_agents)\n# todo - Task planning and tracking for multi-step work\n# memory - Persistent memory across sessions (personal notes + user profile)\n# session_search - Search and recall past conversations (FTS5 + Gemini Flash summarization)\n# tts - Text-to-speech (Edge TTS free, ElevenLabs, OpenAI, MiniMax, Mistral)\n# cronjob - Schedule and manage automated tasks (CLI-only)\n#\n# Composite toolsets:\n# debugging - terminal + web + file (for troubleshooting)\n# safe - web + vision + moa (no terminal access)\n\n# NOTE: The top-level \"toolsets\" key is deprecated and ignored.\n# Tool configuration is managed per-platform via platform_toolsets above.\n# Use `hermes tools` to configure interactively, or edit platform_toolsets directly.\n#\n# CLI override: hermes chat --toolsets terminal,web,file\n\n# =============================================================================\n# MCP (Model Context Protocol) Servers\n# =============================================================================\n# Connect to external MCP servers to add tools from the MCP ecosystem.\n# Each server's tools are automatically discovered and registered.\n# See website/docs/user-guide/features/mcp.md for full documentation.\n#\n# Stdio servers (spawn a subprocess):\n# command: the executable to run\n# args: command-line arguments\n# env: environment variables (only these + safe defaults passed to subprocess)\n#\n# HTTP servers (connect to a URL):\n# url: the MCP server endpoint\n# headers: HTTP headers (e.g., for authentication)\n#\n# Optional per-server settings:\n# timeout: tool call timeout in seconds (default: 120)\n# connect_timeout: initial connection timeout (default: 60)\n# keepalive_interval: liveness ping cadence in seconds (default: 180).\n# Lower it below the server's session TTL for servers that expire idle\n# sessions quickly (e.g. Unreal Engine editor MCP, ~15s), otherwise idle\n# tool calls hit an expired session and pay a slow reconnect. Floored at 5s.\n#\n# mcp_servers:\n# time:\n# command: uvx\n# args: [\"mcp-server-time\"]\n# filesystem:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-filesystem\", \"/home/user\"]\n# notion:\n# url: https://mcp.notion.com/mcp\n# github:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-github\"]\n# env:\n# GITHUB_PERSONAL_ACCESS_TOKEN: \"ghp_...\"\n#\n# Sampling (server-initiated LLM requests) — enabled by default.\n# Per-server config under the 'sampling' key:\n# analysis:\n# command: npx\n# args: [\"-y\", \"analysis-server\"]\n# sampling:\n# enabled: true # default: true\n# model: \"gemini-3-flash\" # override model (optional)\n# max_tokens_cap: 4096 # max tokens per request\n# timeout: 30 # LLM call timeout (seconds)\n# max_rpm: 10 # max requests per minute\n# allowed_models: [] # model whitelist (empty = all)\n# max_tool_rounds: 5 # tool loop limit (0 = disable)\n# log_level: \"info\" # audit verbosity\n\n# =============================================================================\n# Text-to-Speech\n# =============================================================================\n# TTS defaults to Edge TTS unless changed in ~/.hermes/config.yaml.\n# Gemini TTS supports persona/director prompt files, and Gemini 3.1 Flash TTS\n# can use a hidden auxiliary rewrite pass to insert expressive square-bracket\n# audio tags into the TTS script without showing tags in chat.\n#\n# tts:\n# provider: \"gemini\"\n# gemini:\n# model: \"gemini-3.1-flash-tts-preview\"\n# voice: \"Kore\"\n# audio_tags: false\n# persona_prompt_file: \"\" # e.g. ~/.hermes/tts/radio-host.md\n\n# =============================================================================\n# Voice Transcription (Speech-to-Text)\n# =============================================================================\n# Automatically transcribe voice messages on messaging platforms.\n# Providers: local (free, faster-whisper) | groq (free tier) | openai (Whisper API) | mistral (Voxtral Transcribe)\n# Set the corresponding API key in .env: GROQ_API_KEY, OPENAI_API_KEY, or MISTRAL_API_KEY.\nstt:\n enabled: true\n # provider: \"local\" # auto-detected if omitted\n local:\n model: \"base\" # tiny | base | small | medium | large-v3 | turbo\n # language: \"\" # auto-detect; set to \"en\", \"es\", \"fr\", etc. to force\n openai:\n model: \"whisper-1\" # whisper-1 | gpt-4o-mini-transcribe | gpt-4o-transcribe\n # mistral:\n # model: \"voxtral-mini-latest\" # voxtral-mini-latest | voxtral-mini-2602\n\n# =============================================================================\n# Response Pacing (Messaging Platforms)\n# =============================================================================\n# Add human-like delays between message chunks.\n# human_delay:\n# mode: \"off\" # \"off\" | \"natural\" | \"custom\"\n# min_ms: 800 # Min delay (custom mode only)\n# max_ms: 2500 # Max delay (custom mode only)\n\n# =============================================================================\n# Session Logging\n# =============================================================================\n# Session trajectories are automatically saved to logs/ directory.\n# Each session creates: logs/session_YYYYMMDD_HHMMSS_UUID.json\n#\n# The session ID is displayed in the welcome banner for easy reference.\n# Logs contain full conversation history in trajectory format:\n# - System prompt, user messages, assistant responses\n# - Tool calls with inputs/outputs\n# - Timestamps for debugging\n#\n# No configuration needed - logging is always enabled.\n# To disable, you would need to modify the source code.\n\n# =============================================================================\n# Code Execution Sandbox (Programmatic Tool Calling)\n# =============================================================================\n# The execute_code tool runs Python scripts that call Hermes tools via RPC.\n# Intermediate tool results stay out of the LLM's context window.\ncode_execution:\n timeout: 300 # Max seconds per script before kill (default: 300 = 5 min)\n max_tool_calls: 50 # Max RPC tool calls per execution (default: 50)\n\n# =============================================================================\n# Subagent Delegation\n# =============================================================================\n# The delegate_task tool spawns child agents with isolated context.\n# Supports single tasks and batch mode (default 3 parallel, configurable).\ndelegation:\n max_iterations: 50 # Max tool-calling turns per child (default: 50)\n # max_concurrent_children: 3 # Max parallel child agents per batch (default: 3, floor: 1, no ceiling).\n # WARNING: values above 10 multiply API cost linearly.\n # max_spawn_depth: 1 # Delegation tree depth cap (range: 1-3, default: 1 = flat).\n # Raise to 2 to allow workers to spawn their own subagents.\n # Requires role=\"orchestrator\" on intermediate agents.\n # orchestrator_enabled: true # Kill switch for role=\"orchestrator\" children (default: true).\n # subagent_auto_approve: false # When a subagent hits a dangerous-command approval prompt, auto-deny (default: false)\n # or auto-approve \"once\" (true) instead of blocking on stdin.\n # The parent TUI owns stdin, so blocking would deadlock; non-interactive resolution is required.\n # Both choices emit a logger.warning audit line. Flip to true only for cron/batch pipelines.\n # inherit_mcp_toolsets: true # When explicit child toolsets are narrowed, also keep the parent's MCP toolsets (default: true). Set false for strict intersection.\n # model: \"google/gemini-3-flash-preview\" # Override model for subagents (empty = inherit parent)\n # provider: \"openrouter\" # Override provider for subagents (empty = inherit parent)\n # # Resolves full credentials (base_url, api_key) automatically.\n # # Supported: openrouter, nous, zai, kimi-coding, minimax\n\n# =============================================================================\n# Honcho Integration (Cross-Session User Modeling)\n# =============================================================================\n# AI-native persistent memory via Honcho (https://honcho.dev/).\n# Builds a deeper understanding of the user across sessions and tools.\n# Runs alongside USER.md — additive, not a replacement.\n#\n# Requires: pip install honcho-ai\n# Config: ~/.honcho/config.json (shared with Claude Code, Cursor, etc.)\n# API key: HONCHO_API_KEY in ~/.hermes/.env or ~/.honcho/config.json\n#\n# Hermes-specific overrides (optional — most config comes from ~/.honcho/config.json):\n# honcho: {}\n\n# =============================================================================\n# Display\n# =============================================================================\ndisplay:\n # Use compact banner mode (hides the ASCII-art banner, shows a single line).\n # true: Compact single-line banner\n # false: Full ASCII banner with tool/skill summary (default)\n compact: false\n\n # Tool progress display level (CLI and gateway)\n # off: Silent — no tool activity shown, just the final response\n # new: Show a tool indicator only when the tool changes (skip repeats)\n # all: Show every tool call with a short preview (default)\n # verbose: Full args, results, and debug logs (same as /verbose)\n # Toggle at runtime with /verbose in the CLI\n tool_progress: all\n\n # Per-platform defaults can be quieter than the global setting. Telegram\n # tunes for mobile: tool_progress and busy_ack_detail default off (no\n # per-tool breadcrumb stream, no \"iteration 21/60\" debug detail in busy\n # acks or heartbeats), but interim_assistant_messages and\n # long_running_notifications STAY ON so the user has real signal between\n # turn start and final answer (mid-turn assistant commentary + a single\n # edit-in-place \"⏳ Working — N min\" heartbeat). Override under\n # display.platforms.telegram.\n\n # Auto-cleanup of temporary progress bubbles after the final response lands.\n # On platforms that support message deletion (currently Telegram), this\n # removes the tool-progress bubble, \"⏳ Still working...\" notices, and\n # context-pressure status messages once the final reply has been delivered —\n # keeping long-running turns visible live, then tidy afterward. Failed runs\n # leave the bubbles in place as breadcrumbs. Off by default.\n # Per-platform override: display.platforms.telegram.cleanup_progress\n # true: Delete tracked progress/status bubbles on successful turn\n # false: Leave everything in place (default)\n # Example:\n # display:\n # platforms:\n # telegram:\n # cleanup_progress: true\n cleanup_progress: false\n\n # Gateway-only natural mid-turn assistant updates.\n # When true, completed assistant status messages are sent as separate chat\n # messages. This is independent of tool_progress and gateway streaming.\n # true: Send mid-turn assistant updates as separate messages (default)\n # false: Only send the final response\n interim_assistant_messages: true\n\n # Gateway-only long-running status heartbeats.\n # When false, the platform does not receive periodic \"⏳ Working — N min\"\n # notifications even if agent.gateway_notify_interval is non-zero. The\n # heartbeat edits a single message in place (where the adapter supports\n # editing) instead of posting a new bubble each interval.\n # Default: true everywhere, including Telegram (silent agents are worse\n # than a single edit-in-place heartbeat).\n long_running_notifications: true\n\n # Include detailed iteration/tool/status context in busy acknowledgments\n # and long-running heartbeats. When true, busy acks show \"iteration 21/60,\n # terminal, 10 min\" and the heartbeat shows \"⏳ Working — 12 min,\n # iteration 21/60, terminal\". When false (Telegram default), both stay\n # terse: \"Interrupting current task\" and \"⏳ Working — 12 min, terminal\".\n busy_ack_detail: true\n\n # What Enter does when Hermes is already busy (CLI and gateway platforms).\n # interrupt: Interrupt the current run and redirect Hermes (default)\n # queue: Queue your message for the next turn\n # steer: Inject your message mid-run via /steer, arriving at the agent\n # after the next tool call — no interrupt, no role violation.\n # Falls back to 'queue' if the agent isn't running yet or if\n # images are attached (steer only carries text).\n # Ctrl+C (or /stop in gateway) always interrupts regardless of this setting.\n # Toggle at runtime with /busy .\n busy_input_mode: interrupt\n\n # Background process notifications (gateway/messaging only).\n # Controls how chatty the process watcher is when you use\n # terminal(background=true, notify_on_complete=true) from Telegram/Discord/etc.\n # off: No watcher messages at all\n # result: Only the final completion message\n # error: Only the final message when exit code != 0\n # all: Running output updates + final message (default)\n background_process_notifications: all\n\n\n # Play terminal bell when agent finishes a response.\n # Useful for long-running tasks — your terminal will ding when the agent is done.\n # Works over SSH. Most terminals can be configured to flash the taskbar or play a sound.\n # true: Ring the terminal bell on each response\n # false: Silent (default)\n bell_on_complete: false\n\n # Show model reasoning/thinking before each response.\n # When enabled, a dim box shows the model's thought process above the response.\n # Toggle at runtime with /reasoning show or /reasoning hide.\n # true: Show the reasoning box\n # false: Hide reasoning (default)\n show_reasoning: false\n\n # Stream tokens to the terminal as they arrive instead of waiting for the\n # full response. The response box opens on first token and text appears\n # line-by-line. Tool calls are still captured silently.\n # true: Stream tokens as they arrive (default)\n # false: Wait for the full response before rendering\n streaming: true\n\n # Show [HH:MM] timestamps on user input and assistant response labels.\n # timestamps: false\n\n # ───────────────────────────────────────────────────────────────────────────\n # Skin / Theme\n # ───────────────────────────────────────────────────────────────────────────\n # Customize CLI visual appearance — banner colors, spinner faces, tool prefix,\n # response box label, and branding text. Change at runtime with /skin .\n #\n # Built-in skins:\n # default — Classic Hermes gold/kawaii\n # ares — Crimson/bronze war-god theme with spinner wings\n # mono — Clean grayscale monochrome\n # slate — Cool blue developer-focused\n # daylight — Bright light-mode theme\n # warm-lightmode — Warm paper-tone light-mode theme\n # poseidon — Sea-green/teal Olympian theme\n # sisyphus — Earthy stone-and-moss theme\n # charizard — Fiery orange dragon theme\n #\n # Custom skins: drop a YAML file in ~/.hermes/skins/.yaml\n # Schema (all fields optional, missing values inherit from default):\n #\n # name: my-theme\n # description: Short description\n # colors:\n # banner_border: \"#HEX\" # Panel border\n # banner_title: \"#HEX\" # Panel title\n # banner_accent: \"#HEX\" # Section headers (Available Tools, etc.)\n # banner_dim: \"#HEX\" # Dim/muted text\n # banner_text: \"#HEX\" # Body text (tool names, skill names)\n # ui_accent: \"#HEX\" # UI accent color\n # response_border: \"#HEX\" # Response box border color\n # spinner:\n # waiting_faces: [\"(⚔)\", \"(⛨)\"] # Faces shown while waiting\n # thinking_faces: [\"(⚔)\", \"(⌁)\"] # Faces shown while thinking\n # thinking_verbs: [\"forging\", \"plotting\"] # Verbs for spinner messages\n # wings: # Optional left/right spinner decorations\n # - [\"⟪⚔\", \"⚔⟫\"]\n # - [\"⟪▲\", \"▲⟫\"]\n # branding:\n # agent_name: \"My Agent\" # Banner title and branding\n # welcome: \"Welcome message\" # Shown at CLI startup\n # response_label: \" ⚔ Agent \" # Response box header label\n # prompt_symbol: \"⚔\" # Prompt symbol (bare token; renderers add trailing space)\n # tool_prefix: \"╎\" # Tool output line prefix (default: ┊)\n #\n skin: default\n\n# =============================================================================\n# Model Aliases — short names for /model command\n# =============================================================================\n# Map short aliases to exact (model, provider, base_url) tuples.\n# Used by /model tab completion and resolve_alias().\n# Aliases are checked BEFORE the models.dev catalog, so they can route\n# to endpoints not in the catalog (e.g. Ollama Cloud, local servers).\n#\n# model_aliases:\n# opus:\n# model: claude-opus-4-6\n# provider: anthropic\n# qwen:\n# model: \"qwen3.5:397b\"\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n# glm:\n# model: glm-4.7\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n\n# =============================================================================\n# Privacy\n# =============================================================================\n# privacy:\n# # Redact PII from the LLM context prompt.\n# # When true, phone numbers are stripped and user/chat IDs are replaced\n# # with deterministic hashes before being sent to the model.\n# # Names and usernames are NOT affected (user-chosen, publicly visible).\n# # Routing/delivery still uses the original values internally.\n# redact_pii: false\n\n# =============================================================================\n# Shell-script hooks\n# =============================================================================\n# Register shell scripts as plugin-hook callbacks. Each entry is executed as\n# a subprocess (shell=False, shlex.split) with a JSON payload on stdin. On\n# stdout the script may return JSON that either blocks the tool call or\n# injects context into the next LLM call.\n#\n# Valid events (mirror hermes_cli.plugins.VALID_HOOKS):\n# pre_tool_call, post_tool_call, pre_llm_call, post_llm_call,\n# pre_api_request, post_api_request, on_session_start, on_session_end,\n# on_session_finalize, on_session_reset, subagent_stop\n#\n# First-use consent: each (event, command) pair prompts once on a TTY, then\n# is persisted to ~/.hermes/shell-hooks-allowlist.json. Non-interactive\n# runs (gateway, cron) need --accept-hooks, HERMES_ACCEPT_HOOKS=1, or the\n# hooks_auto_accept key below.\n#\n# See website/docs/user-guide/features/hooks.md for the full JSON wire\n# protocol and worked examples.\n#\n# hooks:\n# pre_tool_call:\n# - matcher: \"terminal\"\n# command: \"~/.hermes/agent-hooks/block-rm-rf.sh\"\n# timeout: 10\n# post_tool_call:\n# - matcher: \"write_file|patch\"\n# command: \"~/.hermes/agent-hooks/auto-format.sh\"\n# pre_llm_call:\n# - command: \"~/.hermes/agent-hooks/inject-cwd-context.sh\"\n# subagent_stop:\n# - command: \"~/.hermes/agent-hooks/log-orchestration.sh\"\n#\n# hooks_auto_accept: false\n\n\n# =============================================================================\n# Update Behavior\n# =============================================================================\nupdates:\n # Create a full HERMES_HOME zip before every `hermes update`.\n # Backups land in ~/.hermes/backups/ and can be restored with `hermes import`.\n # Off by default because large homes can add minutes to every update.\n pre_update_backup: false\n\n # Number of pre-update backup zips to retain.\n backup_keep: 5\n\n # What non-interactive updates do with local source edits in the Hermes repo.\n # Interactive terminal updates always prompt before restoring the autostash.\n #\n # stash - auto-stash before pull, then auto-restore after success (default)\n # discard - drop the update-created stash after success; use only on managed\n # installs where local source edits should not persist\n non_interactive_local_changes: \"stash\"\n\n\n# =============================================================================\n# Web Dashboard\n# =============================================================================\n# OAuth gate configuration for `hermes dashboard --host `.\n# The bundled Nous Portal plugin reads these on startup; settings here are\n# the canonical surface. Each can be overridden by an environment variable:\n#\n# dashboard.oauth.client_id <- HERMES_DASHBOARD_OAUTH_CLIENT_ID\n# dashboard.oauth.portal_url <- HERMES_DASHBOARD_PORTAL_URL\n# dashboard.public_url <- HERMES_DASHBOARD_PUBLIC_URL\n#\n# Env wins when set to a non-empty value. This is what Fly.io's platform-\n# secret injection uses to push per-deploy client_ids without needing to\n# bake a config.yaml into the image. Empty env values are treated as unset\n# so a provisioned-but-not-populated secret can't shadow a valid entry here.\n#\n# Local dev / on-prem deploys should typically set these via config.yaml\n# (the ~/.hermes/.env file is reserved for API keys and secrets).\n#\n# dashboard:\n# oauth:\n# client_id: \"\" # agent:{instance_id}; Portal provisions this at deploy\n# portal_url: \"\" # blank → default https://portal.nousresearch.com\n#\n# # Force the absolute base URL the OAuth callback (and any other public\n# # URL the dashboard hands to external systems) is built from. Set this\n# # for deploys behind reverse proxies that don't reliably forward\n# # X-Forwarded-Host / X-Forwarded-Proto / X-Forwarded-Prefix (manual\n# # nginx setups, on-prem ingresses, custom-domain Fly deploys without\n# # full proxy header chains).\n# #\n# # When set, the value is the complete authority: scheme + host +\n# # optional path prefix (e.g. \"https://example.com/hermes\"). The OAuth\n# # callback URL becomes \"/auth/callback\" — X-Forwarded-Prefix\n# # is IGNORED on this code path because the operator has explicitly\n# # declared the public URL and we no longer need to guess.\n# #\n# # Leave empty to use the existing proxy-header reconstruction (the\n# # default — works on Fly.io out of the box).\n# #\n# # public_url: \"https://example.com/hermes\"\n"}, "dbe14ce35d9cb087ae9fe3e3f166f6c475297e25": {"t": 1785767906.1616402, "n": 64765, "h": "9b5295e1f55dee4a", "c": "# Hermes Agent CLI Configuration\n# Copy this file to cli-config.yaml and customize as needed.\n# This file configures the CLI behavior. Environment variables in .env take precedence.\n\n# =============================================================================\n# Model Configuration\n# =============================================================================\nmodel:\n # Default model to use (can be overridden with --model flag)\n # Both \"default\" and \"model\" work as the key name here.\n default: \"anthropic/claude-opus-4.6\"\n \n # Inference provider selection:\n # \"auto\" - Auto-detect from credentials (default)\n # \"openrouter\" - OpenRouter (requires: OPENROUTER_API_KEY or OPENAI_API_KEY)\n # \"nous\" - Nous Portal OAuth (requires: hermes login)\n # \"nous-api\" - Nous Portal API key (requires: NOUS_API_KEY)\n # \"anthropic\" - Direct Anthropic API (requires: ANTHROPIC_API_KEY)\n # \"openai-codex\" - OpenAI Codex (requires: hermes auth)\n # \"copilot\" - GitHub Copilot / GitHub Models (requires: GITHUB_TOKEN)\n # \"gemini\" - Use Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n # \"zai\" - Use z.ai / ZhipuAI GLM models (requires: GLM_API_KEY)\n # \"kimi-coding\" - Kimi / Moonshot AI (requires: KIMI_API_KEY)\n # \"minimax\" - MiniMax global (requires: MINIMAX_API_KEY)\n # \"minimax-cn\" - MiniMax China (requires: MINIMAX_CN_API_KEY)\n # \"huggingface\" - Hugging Face Inference (requires: HF_TOKEN)\n # \"nvidia\" - NVIDIA NIM / build.nvidia.com (requires: NVIDIA_API_KEY)\n # \"xiaomi\" - Xiaomi MiMo (requires: XIAOMI_API_KEY)\n # \"arcee\" - Arcee AI Trinity models (requires: ARCEEAI_API_KEY)\n # \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY — https://ollama.com/settings)\n # \"kilocode\" - KiloCode gateway (requires: KILOCODE_API_KEY)\n # \"azure-foundry\" - Microsoft Foundry / Azure OpenAI (API key or Entra ID)\n # \"lmstudio\" - LM Studio local server (optional: LM_API_KEY, defaults to http://127.0.0.1:1234/v1)\n #\n # Local servers (LM Studio, Ollama, vLLM, llama.cpp):\n # \"custom\" - Any other OpenAI-compatible endpoint. Set base_url below.\n # Aliases: \"ollama\", \"vllm\", \"llamacpp\" all map to \"custom\".\n # LM Studio is first-class and uses provider: \"lmstudio\".\n # It works with both no-auth and auth-enabled server modes.\n #\n # Can also be overridden for a single invocation with the --provider flag.\n provider: \"auto\"\n \n # API configuration (falls back to OPENROUTER_API_KEY env var)\n # api_key: \"your-key-here\" # Uncomment to set here instead of .env\n base_url: \"https://openrouter.ai/api/v1\"\n\n # Azure Foundry keyless auth example:\n # provider: \"azure-foundry\"\n # base_url: \"https://.openai.azure.com/openai/v1\"\n # auth_mode: \"entra_id\" # DefaultAzureCredential: az login, managed identity, workload identity, etc.\n # default: \"gpt-4o\" # Deployment/model name\n # entra:\n # scope: \"https://ai.azure.com/.default\" # Optional; this is the default.\n\n # ── Token limits — two settings, easy to confuse ──────────────────────────\n #\n # context_length: TOTAL context window (input + output tokens combined).\n # Controls when Hermes compresses history and validates requests.\n # Leave unset — Hermes auto-detects the correct value from the provider.\n # Set manually only when auto-detection is wrong (e.g. a local server with\n # a custom num_ctx, or a proxy that doesn't expose /v1/models).\n #\n # context_length: 131072\n #\n # max_tokens: OUTPUT cap — maximum tokens the model may generate per response.\n # Unrelated to how long your conversation history can be.\n # The OpenAI-standard name \"max_tokens\" is a misnomer; Anthropic's native\n # API has since renamed it \"max_output_tokens\" for clarity.\n # Leave unset to use the model's native output ceiling (recommended).\n # Set only if you want to deliberately limit individual response length.\n #\n# max_tokens: 8192\n\n # ── Custom request headers (optional) ─────────────────────────────────────\n #\n # default_headers: extra HTTP headers sent on every request to an\n # OpenAI-compatible endpoint. User values take precedence over the\n # provider/SDK defaults, so this is the supported way to override the\n # OpenAI Python SDK's identifying headers (User-Agent: OpenAI/Python ...,\n # X-Stainless-*) when a custom provider sits behind a gateway/WAF that\n # rejects them — e.g. an upstream that returns \"502 Upstream access\n # forbidden\" for the SDK default User-Agent but accepts a plain one.\n # Applies on the OpenAI wire only (not native Anthropic / Bedrock).\n #\n # default_headers:\n # User-Agent: \"curl/8.7.1\"\n\n# Named provider overrides (optional)\n# Use this for per-provider request timeouts, non-stream stale timeouts,\n# and per-model exceptions.\n# Applies to the primary turn client on every api_mode (OpenAI-wire, native\n# Anthropic, and Anthropic-compatible providers), the fallback chain, and\n# client rebuilds during credential rotation. For OpenAI-wire chat\n# completions (streaming and non-streaming) the configured value is also\n# used as the per-request ``timeout=`` kwarg so it wins over the legacy\n# HERMES_API_TIMEOUT env var (which still applies when no config is set).\n# ``stale_timeout_seconds`` controls the non-streaming stale-call detector and\n# wins over the legacy HERMES_API_CALL_STALE_TIMEOUT env var. Leaving these\n# unset keeps the legacy defaults (HERMES_API_TIMEOUT=1800s,\n# HERMES_API_CALL_STALE_TIMEOUT=90s, native Anthropic 900s). The\n# implicit non-stream stale detector is auto-disabled for local endpoints\n# and can scale upward for very large contexts.\n#\n# Not currently wired for AWS Bedrock (bedrock_converse + AnthropicBedrock\n# SDK paths) — those use boto3 with its own timeout configuration.\n#\n# providers:\n# ollama-local:\n# request_timeout_seconds: 300 # Longer timeout for local cold-starts\n# stale_timeout_seconds: 900 # Explicitly re-enable stale detection on local endpoints\n# anthropic:\n# request_timeout_seconds: 30 # Fast-fail cloud requests\n# models:\n# claude-opus-4.6:\n# timeout_seconds: 600 # Longer timeout for extended-thinking Opus calls\n# openai-codex:\n# models:\n# gpt-5.4:\n# stale_timeout_seconds: 1800 # Longer non-stream stale timeout for slow large-context turns\n\n# =============================================================================\n# OpenRouter Provider Routing (only applies when using OpenRouter)\n# =============================================================================\n# Control how requests are routed across providers on OpenRouter.\n# See: https://openrouter.ai/docs/guides/routing/provider-selection\n#\n# provider_routing:\n# # Sort strategy: \"price\" (default), \"throughput\", or \"latency\"\n# # Append :nitro to model name for a shortcut to throughput sorting.\n# sort: \"throughput\"\n#\n# # Only allow these providers (provider slugs from OpenRouter)\n# # only: [\"anthropic\", \"google\"]\n#\n# # Skip these providers entirely\n# # ignore: [\"deepinfra\", \"fireworks\"]\n#\n# # Try providers in this order (overrides default load balancing)\n# # order: [\"anthropic\", \"google\", \"together\"]\n#\n# # Require providers to support all parameters in your request\n# # require_parameters: true\n#\n# # Data policy: \"allow\" (default) or \"deny\" to exclude providers that may store data\n# # data_collection: \"deny\"\n\n# =============================================================================\n# OpenRouter Response Caching (only applies when using OpenRouter)\n# =============================================================================\n# Cache identical API responses at the OpenRouter edge for free instant replays.\n# When enabled, identical requests (same model, messages, parameters) return\n# cached responses with zero billing. Separate from Anthropic prompt caching.\n# See: https://openrouter.ai/docs/guides/features/response-caching\n#\n# openrouter:\n# response_cache: true # Enable response caching (default: true)\n# response_cache_ttl: 300 # Cache TTL in seconds, 1-86400 (default: 300)\n\n# =============================================================================\n# Git Worktree Isolation\n# =============================================================================\n# When enabled, each CLI session creates an isolated git worktree so multiple\n# agents can work on the same repo concurrently without file collisions.\n# Equivalent to always passing --worktree / -w on the command line.\n#\n# worktree: true # Always create a worktree when in a git repo\n# worktree: false # Default — only create when -w flag is passed\n#\n# By default a new worktree branches from the freshly-fetched remote tip\n# (the current branch's upstream, else the remote's default branch) so it\n# starts current with the project instead of from the local clone's\n# (possibly stale) HEAD. Set worktree_sync: false to branch from local HEAD\n# instead — useful when offline or when you deliberately want the clone's\n# exact current state as the base.\n#\n# worktree_sync: true # Default — branch from the fetched remote tip\n# worktree_sync: false # Branch from local HEAD (offline / pinned base)\n\n# =============================================================================\n# Terminal Tool Configuration\n# =============================================================================\n# Choose ONE of the following terminal configurations by uncommenting it.\n# The terminal tool executes commands in the specified environment.\n\n# -----------------------------------------------------------------------------\n# OPTION 1: Local execution (default)\n# Commands run directly on your machine in the current directory\n# -----------------------------------------------------------------------------\n# Working directory behavior:\n# - CLI (`hermes` command): Uses \".\" (current directory where you run hermes)\n# - Gateway/messaging/cron: Uses terminal.cwd here; legacy .env cwd values are deprecated\nterminal:\n backend: \"local\"\n cwd: \".\" # For local backend: \".\" = current directory. Ignored for remote backends unless a backend documents otherwise.\n timeout: 180\n # HOME policy for tool subprocesses:\n # auto - default: host uses your real HOME; containers use HERMES_HOME/home\n # real - force your real OS-user HOME\n # profile - force HERMES_HOME/home for strict per-profile CLI config isolation\n home_mode: \"auto\"\n docker_mount_cwd_to_workspace: false # SECURITY: off by default. Opt in to mount the launch cwd into Docker /workspace.\n lifetime_seconds: 300\n # sudo_password: \"hunter2\" # Optional: pipe a sudo password via sudo -S. SECURITY WARNING: plaintext.\n # sudo_password: \"\" # Explicit empty password: try empty and never open the interactive sudo prompt.\n\n# -----------------------------------------------------------------------------\n# OPTION 2: SSH remote execution\n# Commands run on a remote server - agent code stays local (sandboxed)\n# Great for: keeping agent isolated from its own code, using powerful remote hardware\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"ssh\"\n# cwd: \"/home/myuser/project\" # Path on the REMOTE server\n# timeout: 180\n# lifetime_seconds: 300\n# ssh_host: \"my-server.example.com\"\n# ssh_user: \"myuser\"\n# ssh_port: 22\n# ssh_key: \"~/.ssh/id_rsa\" # Optional - uses ssh-agent if not specified\n\n# -----------------------------------------------------------------------------\n# OPTION 3: Docker container\n# Commands run in an isolated Docker container\n# Great for: reproducible environments, testing, isolation\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"docker\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /)\n# timeout: 180\n# lifetime_seconds: 300\n# docker_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# docker_mount_cwd_to_workspace: true # Explicit opt-in: mount your launch cwd into /workspace\n# # Optional: run the container as your host user's uid:gid so files written\n# # into bind-mounted dirs are owned by you, not root. Drops SETUID/SETGID\n# # caps too since no gosu privilege drop is needed. Leave off if your\n# # chosen docker_image expects to start as root.\n# docker_run_as_host_user: true\n# # Optional: explicitly forward selected env vars into Docker.\n# # These values come from your current shell first, then ~/.hermes/.env.\n# # Warning: anything forwarded here is visible to commands run in the container.\n# docker_forward_env:\n# - \"GITHUB_TOKEN\"\n# - \"NPM_TOKEN\"\n# # Optional: extra flags passed verbatim to docker run (appended after security defaults).\n# # Useful for adding capabilities (e.g. apt installs needing SETUID) or custom options.\n# # Example: add a Linux capability not included by default\n# # docker_extra_args:\n# # - \"--cap-add\"\n# # - \"SETUID\"\n\n# -----------------------------------------------------------------------------\n# OPTION 4: Singularity/Apptainer container\n# Commands run in a Singularity container (common in HPC environments)\n# Great for: HPC clusters, shared compute environments\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"singularity\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# singularity_image: \"docker://nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 5: Modal cloud execution\n# Commands run on Modal's cloud infrastructure\n# Great for: GPU access, scalable compute, serverless execution\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"modal\"\n# cwd: \"/workspace\" # Path INSIDE the sandbox (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# modal_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 6: Daytona cloud execution\n# Commands run in Daytona cloud sandboxes\n# Great for: Cloud dev environments, persistent workspaces, team collaboration\n# Requires: pip install daytona, DAYTONA_API_KEY env var\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"daytona\"\n# cwd: \"~\"\n# timeout: 180\n# lifetime_seconds: 300\n# daytona_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# container_disk: 10240 # Daytona max is 10GB per sandbox\n\n#\n# --- Container resource limits (docker, singularity, modal, daytona -- ignored for local/ssh) ---\n# These settings apply to all container backends. They control the resources\n# allocated to the sandbox and whether its filesystem persists across sessions.\n container_cpu: 1 # CPU cores\n container_memory: 5120 # Memory in MB (5120 = 5GB)\n container_disk: 51200 # Disk in MB (51200 = 50GB)\n container_persistent: true # Persist filesystem across sessions (false = ephemeral)\n\n# -----------------------------------------------------------------------------\n# SUDO SUPPORT (works with ALL backends above)\n# -----------------------------------------------------------------------------\n# Add sudo_password to any terminal config above to enable sudo commands.\n# The password is piped via `sudo -S`. Works with local, ssh, docker, etc.\n#\n# SECURITY WARNING: Password stored in plaintext!\n#\n# INTERACTIVE PROMPT: If sudo_password is unset and the CLI is running,\n# you'll be prompted to enter your password when sudo is needed:\n# - 45-second timeout (auto-skips if no input)\n# - Press Enter to skip (command fails gracefully)\n# - Password is hidden while typing\n# - Password is cached for the session\n#\n# EMPTY PASSWORDS: Setting sudo_password to an explicit empty string is different\n# from leaving it unset. Hermes will try an empty password via `sudo -S` and\n# will not open the interactive prompt. This is useful for passwordless sudo,\n# Touch ID sudo setups, and environments where prompting is just noise.\n#\n# ALTERNATIVES:\n# - SSH backend: Configure passwordless sudo on the remote server\n# - Containers: Run as root inside the container (no sudo needed)\n# - Local: Configure /etc/sudoers for specific commands\n#\n# Example (add to your terminal section):\n# sudo_password: \"your-password-here\"\n\n# =============================================================================\n# Security Scanning (tirith)\n# =============================================================================\n# Optional pre-exec command security scanning via tirith.\n# Detects homograph URLs, pipe-to-shell, terminal injection, env manipulation.\n# Install: brew install sheeki03/tap/tirith\n# Docs: https://github.com/sheeki03/tirith\n#\n# security:\n# tirith_enabled: true # Enable/disable tirith scanning\n# tirith_path: \"tirith\" # Path to tirith binary (supports ~ expansion)\n# tirith_timeout: 5 # Scan timeout in seconds\n# tirith_fail_open: true # Allow commands if tirith unavailable\n\n# =============================================================================\n# Browser Tool Configuration\n# =============================================================================\nbrowser:\n # Inactivity timeout in seconds - browser sessions are automatically closed\n # after this period of no activity between agent loops (default: 120 = 2 minutes)\n inactivity_timeout: 120\n\n# =============================================================================\n# Tool Loop Guardrails\n# =============================================================================\n# Soft warnings are enabled by default. They append guidance to repeated failed\n# or non-progressing tool results but still let the tool execute. Hard stops are\n# opt-in circuit breakers for autonomous/cron sessions where stopping a loop is\n# preferable to spending the full iteration budget.\ntool_loop_guardrails:\n warnings_enabled: true\n hard_stop_enabled: false\n warn_after:\n exact_failure: 2\n same_tool_failure: 3\n idempotent_no_progress: 2\n hard_stop_after:\n exact_failure: 5\n same_tool_failure: 8\n idempotent_no_progress: 5\n\n# =============================================================================\n# Context Compression (Auto-shrinks long conversations)\n# =============================================================================\n# When conversation approaches model's context limit, middle turns are\n# automatically summarized to free up space while preserving important context.\n#\n# HOW IT WORKS:\n# 1. Tracks actual token usage from API responses (not estimates)\n# 2. When prompt_tokens >= threshold% of model's context_length, triggers compression\n# 3. Protects first 3 turns (system prompt, initial request, first response)\n# 4. Protects last N turns (default 20 messages = ~10 full turns of recent context)\n# 5. Summarizes middle turns using a fast/cheap model\n# 6. Inserts summary as a user message, continues conversation seamlessly\n#\n# Post-compression tail budget is target_ratio × threshold × context_length:\n# 200K context, threshold 0.50, ratio 0.20 → 20K tokens of recent tail preserved\n# 1M context, threshold 0.50, ratio 0.20 → 100K tokens of recent tail preserved\n#\ncompression:\n # Enable automatic context compression (default: true)\n # Set to false if you prefer to manage context manually or want errors on overflow\n enabled: true\n \n # Trigger compression at this % of model's context limit (default: 0.50 = 50%)\n # Lower values = more aggressive compression, higher values = compress later\n threshold: 0.50\n \n # Fraction of the threshold to preserve as recent tail (default: 0.20 = 20%)\n # e.g. 20% of 50% threshold = 10% of total context kept as recent messages.\n # Summary output is separately capped at 12K tokens (Gemini output limit).\n # Range: 0.10 - 0.80\n target_ratio: 0.20\n\n # Number of most-recent messages to always preserve (default: 20 ≈ 10 full turns)\n # Higher values keep more recent conversation intact at the cost of more aggressive\n # compression of older turns.\n protect_last_n: 20\n\n # Number of non-system messages to protect at the head of the transcript, in\n # ADDITION to the system prompt (which is always implicitly protected).\n # Head messages are NEVER summarized — they survive every compression\n # indefinitely. This gives stable early context for short/medium sessions,\n # but in long-running sessions that rely on rolling compaction the pinned\n # opening turns may not match how you want the session framed over time.\n # Set to 0 to preserve ONLY the system prompt (plus the rolling summary\n # and recent tail) — the cleanest configuration for long-running sessions.\n # Default 3 preserves the system prompt plus the first three non-system\n # head messages, matching the pre-feature behaviour.\n protect_first_n: 3\n\n # To pin a specific model/provider for compression summaries, use the\n # auxiliary section below (auxiliary.compression.provider / model).\n\n# =============================================================================\n# Anthropic prompt caching TTL\n# =============================================================================\n# When prompt caching is active (Claude via OpenRouter or native Anthropic),\n# Anthropic supports two TTL tiers for cached prefixes: \"5m\" (default) and\n# \"1h\". Other values are ignored and \"5m\" is used.\n#\nprompt_caching:\n cache_ttl: \"5m\" # use \"1h\" for long sessions with pauses between turns\n\n# =============================================================================\n# Auxiliary Models (Advanced — Experimental)\n# =============================================================================\n# Hermes uses lightweight \"auxiliary\" models for side tasks: image analysis,\n# browser screenshot analysis, web page summarization, TTS audio-tag insertion,\n# and context compression.\n#\n# By default these use Gemini Flash via OpenRouter or Nous Portal and are\n# auto-detected from your credentials. You do NOT need to change anything\n# here for normal usage.\n#\n# WARNING: Overriding these with providers other than OpenRouter or Nous Portal\n# is EXPERIMENTAL and may not work. Not all models/providers support vision,\n# produce usable summaries, or accept the same API format. Change at your own\n# risk — if things break, reset to \"auto\" / empty values.\n#\n# Each task has its own provider + model pair so you can mix providers.\n# For example: OpenRouter for vision (needs multimodal), but your main\n# local endpoint for compression (just needs text).\n#\n# Provider options:\n# \"auto\" - Best available: OpenRouter → Nous Portal → main endpoint (default)\n# \"openrouter\" - Force OpenRouter (requires OPENROUTER_API_KEY)\n# \"nous\" - Force Nous Portal (requires: hermes login)\n# \"gemini\" - Force Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n# \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY)\n# \"codex\" - Force Codex OAuth (requires: hermes model → Codex).\n# Uses gpt-5.3-codex which supports vision.\n# \"main\" - Use your custom endpoint (OPENAI_BASE_URL + OPENAI_API_KEY).\n# Works with OpenAI API, local models, or any OpenAI-compatible\n# endpoint. Also falls back to Codex OAuth and API-key providers.\n#\n# Model: leave empty to use the provider's default. When empty, OpenRouter\n# uses \"google/gemini-3-flash-preview\" and Nous uses \"gemini-3-flash\".\n# Other providers pick a sensible default automatically.\n#\n# auxiliary:\n# # Image analysis: vision_analyze tool + browser screenshots\n# vision:\n# provider: \"auto\"\n# model: \"\" # e.g. \"google/gemini-2.5-flash\", \"openai/gpt-4o\"\n# timeout: 30 # LLM API call timeout (seconds)\n# download_timeout: 30 # Image HTTP download timeout (seconds)\n# # Increase for slow connections or self-hosted image servers\n#\n# # Web page scraping / summarization + browser page text extraction\n# web_extract:\n# provider: \"auto\"\n# model: \"\"\n#\n# # Gemini 3.1 TTS hidden audio-tag insertion\n# tts_audio_tags:\n# provider: \"auto\" # empty model = your main chat model\n# model: \"\"\n# timeout: 30\n#\n# # Session search — summarizes matching past sessions\n# session_search:\n# provider: \"auto\"\n# model: \"\"\n# timeout: 30\n# max_concurrency: 3 # Limit parallel summaries to reduce request-burst 429s\n# extra_body: {} # Provider-specific OpenAI-compatible request fields\n# # Example for providers that support request-body\n# # reasoning controls:\n# # extra_body:\n# # enable_thinking: false\n# # Some vLLM/Qwen deployments expect this nested:\n# # extra_body:\n# # chat_template_kwargs:\n# # enable_thinking: false\n\n# =============================================================================\n# Persistent Memory\n# =============================================================================\n# Bounded curated memory injected into the system prompt every session.\n# Two stores: MEMORY.md (agent's notes) and USER.md (user profile).\n# Character limits keep the memory small and focused. The agent manages\n# pruning -- when at the limit, it must consolidate or replace entries.\n# Disabled by default in batch_runner.\n#\nmemory:\n # Agent's personal notes: environment facts, conventions, things learned\n memory_enabled: true\n \n # User profile: preferences, communication style, expectations\n user_profile_enabled: true\n \n # Character limits (~2.75 chars per token, model-independent)\n memory_char_limit: 2200 # ~800 tokens\n user_char_limit: 1375 # ~500 tokens\n\n # Periodic memory nudge: remind the agent to consider saving memories\n # every N user turns. Set to 0 to disable. Only active when memory is enabled.\n nudge_interval: 10 # Nudge every 10 user turns (0 = disabled)\n\n # Memory flush: give the agent one turn to save memories before context is\n # lost (compression, /new, /reset, exit). Set to 0 to disable.\n # For exit/reset, only fires if the session had at least this many user turns.\n flush_min_turns: 6 # Min user turns to trigger flush on exit/reset (0 = disabled)\n\n# =============================================================================\n# Session Reset Policy (Messaging Platforms)\n# =============================================================================\n# Controls when messaging sessions (Telegram, Discord, WhatsApp, Slack) are\n# automatically cleared. Without resets, conversation context grows indefinitely\n# which increases API costs with every message.\n#\n# When a reset triggers, the agent first saves important information to its\n# persistent memory — but the conversation context is wiped. The agent starts\n# fresh but retains learned facts via its memory system.\n#\n# Users can always manually reset with /reset or /new in chat.\n#\n# Modes:\n# \"both\" - Reset on EITHER inactivity timeout or daily boundary (recommended)\n# \"idle\" - Reset only after N minutes of inactivity\n# \"daily\" - Reset only at a fixed hour each day\n# \"none\" - Never auto-reset; context lives until /reset or compression kicks in\n#\n# When a reset triggers, the agent gets one turn to save important memories and\n# skills before the context is wiped. Persistent memory carries across sessions.\n#\nsession_reset:\n mode: both # \"both\", \"idle\", \"daily\", or \"none\"\n idle_minutes: 1440 # Inactivity timeout in minutes (default: 1440 = 24 hours)\n at_hour: 4 # Daily reset hour, 0-23 local time (default: 4 AM)\n\n# Maximum number of simultaneously active chat sessions across CLI, TUI,\n# dashboard chat, and messaging gateway. Set to null, 0, or omit to allow\n# unlimited concurrent sessions. When the limit is reached, new sessions get a\n# clean error while existing active sessions keep their normal behavior. This\n# top-level key takes precedence over gateway.max_concurrent_sessions. The cap\n# is a best-effort single-host/profile runtime guard; Hermes fails open if the\n# local runtime lease registry cannot be read or locked.\nmax_concurrent_sessions: null\n\n# When true, group/channel chats use one session per participant when the platform\n# provides a user ID. This is the secure default and prevents users in the same\n# room from sharing context, interrupts, and token costs. Set false only if you\n# explicitly want one shared \"room brain\" per group/channel.\ngroup_sessions_per_user: true\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Gateway Streaming\n# ─────────────────────────────────────────────────────────────────────────────\n# Stream tokens to messaging platforms in real-time. The bot sends a message\n# on first token, then progressively edits it as more tokens arrive.\n# Disabled by default — enable to try the streaming UX on Telegram/Discord/Slack.\n# For Telegram, partial edits are sent as plain text and only the final edit uses MarkdownV2.\nstreaming:\n enabled: false\n # transport: edit # \"edit\" = progressive editMessageText\n # edit_interval: 0.3 # seconds between message edits\n # buffer_threshold: 40 # chars before forcing an edit flush\n # cursor: \" ▉\" # cursor shown during streaming\n\n# =============================================================================\n# Skills Configuration\n# =============================================================================\n# Skills are reusable procedures the agent can load and follow. The agent can\n# also create new skills after completing complex tasks.\n#\nskills:\n # Nudge the agent to create skills after complex tasks.\n # Every N tool-calling iterations, remind the model to consider saving a skill.\n # Set to 0 to disable.\n creation_nudge_interval: 15\n\n # External skill directories — share skills across tools/agents without\n # copying them into ~/.hermes/skills/. Each path is expanded (~ and ${VAR})\n # and resolved to an absolute path. External dirs are read-only: skill\n # creation always writes to ~/.hermes/skills/. Local skills take precedence\n # when names collide.\n # external_dirs:\n # - ~/.agents/skills\n # - /home/shared/team-skills\n\n# =============================================================================\n# Agent Behavior\n# =============================================================================\nagent:\n # Maximum tool-calling iterations per conversation\n # Higher = more room for complex tasks, but costs more tokens\n # Recommended: 20-30 for focused tasks, 50-100 for open exploration\n max_turns: 60\n\n # Inactivity timeout for gateway agent runs (seconds, 0 = unlimited).\n # The agent can run indefinitely when actively calling tools or receiving\n # API responses. Only fires after the agent has been idle for this duration.\n # gateway_timeout: 1800\n\n # Staged warning: send a warning before escalating to full timeout.\n # Fires once per run when inactivity reaches this threshold (seconds).\n # Set to 0 to disable the warning.\n # gateway_timeout_warning: 900\n\n # Graceful drain timeout for gateway stop/restart (seconds).\n # The gateway stops accepting new work, waits for in-flight agents to\n # finish, then interrupts anything still running after this timeout.\n # 0 = no drain, interrupt immediately.\n # restart_drain_timeout: 60\n\n # Max app-level retry attempts for API errors (connection drops, provider\n # timeouts, 5xx, etc.) before the agent surfaces the failure. Lower this\n # to 1 if you use fallback providers and want fast failover on flaky\n # primaries (default 3). The OpenAI SDK does its own low-level retries\n # underneath this wrapper — this is the Hermes-level loop.\n # api_max_retries: 3\n \n # Enable verbose logging\n verbose: false\n \n # Reasoning effort level (OpenRouter and Nous Portal)\n # Controls how much \"thinking\" the model does before responding.\n # Options: \"xhigh\" (max), \"high\", \"medium\", \"low\", \"minimal\", \"none\" (disable)\n reasoning_effort: \"medium\"\n \n # Predefined personalities (use with /personality command)\n personalities:\n helpful: \"You are a helpful, friendly AI assistant.\"\n concise: \"You are a concise assistant. Keep responses brief and to the point.\"\n technical: \"You are a technical expert. Provide detailed, accurate technical information.\"\n creative: \"You are a creative assistant. Think outside the box and offer innovative solutions.\"\n teacher: \"You are a patient teacher. Explain concepts clearly with examples.\"\n kawaii: \"You are a kawaii assistant! Use cute expressions like (◕‿◕), ★, ♪, and ~! Add sparkles and be super enthusiastic about everything! Every response should feel warm and adorable desu~! ヽ(>∀<☆)ノ\"\n catgirl: \"You are Neko-chan, an anime catgirl AI assistant, nya~! Add 'nya' and cat-like expressions to your speech. Use kaomoji like (=^・ω・^=) and ฅ^•ﻌ•^ฅ. Be playful and curious like a cat, nya~!\"\n pirate: \"Arrr! Ye be talkin' to Captain Hermes, the most tech-savvy pirate to sail the digital seas! Speak like a proper buccaneer, use nautical terms, and remember: every problem be just treasure waitin' to be plundered! Yo ho ho!\"\n shakespeare: \"Hark! Thou speakest with an assistant most versed in the bardic arts. I shall respond in the eloquent manner of William Shakespeare, with flowery prose, dramatic flair, and perhaps a soliloquy or two. What light through yonder terminal breaks?\"\n surfer: \"Duuude! You're chatting with the chillest AI on the web, bro! Everything's gonna be totally rad. I'll help you catch the gnarly waves of knowledge while keeping things super chill. Cowabunga! 🤙\"\n noir: \"The rain hammered against the terminal like regrets on a guilty conscience. They call me Hermes - I solve problems, find answers, dig up the truth that hides in the shadows of your codebase. In this city of silicon and secrets, everyone's got something to hide. What's your story, pal?\"\n uwu: \"hewwo! i'm your fwiendwy assistant uwu~ i wiww twy my best to hewp you! *nuzzles your code* OwO what's this? wet me take a wook! i pwomise to be vewy hewpful >w<\"\n philosopher: \"Greetings, seeker of wisdom. I am an assistant who contemplates the deeper meaning behind every query. Let us examine not just the 'how' but the 'why' of your questions. Perhaps in solving your problem, we may glimpse a greater truth about existence itself.\"\n hype: \"YOOO LET'S GOOOO!!! 🔥🔥🔥 I am SO PUMPED to help you today! Every question is AMAZING and we're gonna CRUSH IT together! This is gonna be LEGENDARY! ARE YOU READY?! LET'S DO THIS! 💪😤🚀\"\n\n# =============================================================================\n# Toolsets\n# =============================================================================\n# Control which tools the agent has access to.\n# Use `hermes tools` to interactively enable/disable tools per platform.\n\n# =============================================================================\n# Platform Toolsets (per-platform tool configuration)\n# =============================================================================\n# Override which toolsets are available on each platform.\n# If a platform isn't listed here, its built-in default is used.\n#\n# You can use EITHER:\n# - A preset like \"hermes-cli\" or \"hermes-telegram\" (curated tool set)\n# - A list of individual toolsets to compose your own (see list below)\n#\n# Supported platform keys: cli, telegram, discord, whatsapp, slack, qqbot, teams, google_chat\n#\n# Examples:\n#\n# # Use presets (same as defaults):\n# platform_toolsets:\n# cli: [hermes-cli]\n# telegram: [hermes-telegram]\n#\n# # Custom: give Telegram only web + terminal + file + planning:\n# platform_toolsets:\n# telegram: [web, terminal, file, todo]\n#\n# # Custom: CLI without browser or image gen:\n# platform_toolsets:\n# cli: [web, terminal, file, skills, todo, tts, cronjob]\n#\n# # Restrictive: Discord gets read-only tools only:\n# platform_toolsets:\n# discord: [web, vision, skills, todo]\n#\n# If not set, defaults are:\n# cli: hermes-cli (everything + cronjob management)\n# telegram: hermes-telegram (terminal, file, web, vision, image, tts, browser, skills, todo, cronjob, messaging)\n# discord: hermes-discord (same as telegram)\n# whatsapp: hermes-whatsapp (same as telegram)\n# slack: hermes-slack (same as telegram)\n# signal: hermes-signal (same as telegram)\n# homeassistant: hermes-homeassistant (same as telegram)\n# qqbot: hermes-qqbot (same as telegram)\n# teams: hermes-teams (same as telegram)\n# google_chat: hermes-google_chat (same as telegram)\n#\nplatform_toolsets:\n cli: [hermes-cli]\n telegram: [hermes-telegram]\n discord: [hermes-discord]\n whatsapp: [hermes-whatsapp]\n slack: [hermes-slack]\n signal: [hermes-signal]\n homeassistant: [hermes-homeassistant]\n qqbot: [hermes-qqbot]\n yuanbao: [hermes-yuanbao]\n teams: [hermes-teams]\n google_chat: [hermes-google_chat]\n\n# =============================================================================\n# Gateway Platform Settings\n# =============================================================================\n# Optional per-platform messaging settings.\n# Platform-specific knobs live under `extra`.\n#\n# platforms:\n# telegram:\n# reply_to_mode: \"first\" # off | first | all\n# # guest_mode lets explicit @mentions from non-allowlisted groups through.\n# # Default false; ordinary messages, replies, and regex wake words stay blocked.\n# guest_mode: false\n# # allowed_chats: [\"-1001234567890\"]\n# extra:\n# disable_link_previews: false # Set true to suppress Telegram URL previews in bot messages\n# rich_messages: false # Bot API 10.1 rich messages (tables/task lists/details/math); default false for copyable legacy MarkdownV2, set true to opt in\n# command_menu:\n# # Telegram allows up to 100 BotCommands; Hermes defaults to 30 to\n# # stay under Telegram's payload-size limits while keeping common\n# # commands visible. Values are clamped to 1..100.\n# max_commands: 30\n# # prepend = user priority first, then Hermes defaults\n# # append = Hermes defaults first, then user priority\n# # replace = only the list below defines priority\n# priority_mode: prepend\n# priority:\n# - my_plugin_command\n#\n# Discord-specific settings (config.yaml top-level, not under platforms:):\n#\n# discord:\n# require_mention: true # Require @mention in server channels (default: true)\n# auto_thread: true # Auto-create thread on @mention (default: true)\n# free_response_channels: \"\" # Channel IDs where no mention is needed\n# reactions: true # Show processing reactions (default: true)\n# history_backfill: true # Recover missed channel messages on mention (default: true)\n# history_backfill_limit: 50 # Max messages to scan backwards (default: 50)\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Available toolsets (use these names in platform_toolsets or the toolsets list)\n#\n# Run `hermes chat --list-toolsets` to see all toolsets and their tools.\n# Run `hermes chat --list-tools` to see every individual tool with descriptions.\n# ─────────────────────────────────────────────────────────────────────────────\n#\n# INDIVIDUAL TOOLSETS (compose your own):\n# web - web_search, web_extract\n# search - web_search only (no scraping)\n# terminal - terminal, process\n# file - read_file, write_file, patch, search\n# browser - browser_navigate, browser_snapshot, browser_click, browser_type,\n# browser_scroll, browser_back, browser_press,\n# browser_get_images, browser_vision (requires BROWSERBASE_API_KEY)\n# vision - vision_analyze (requires OPENROUTER_API_KEY)\n# image_gen - image_generate (requires FAL_KEY)\n# skills - skills_list, skill_view\n# skills_hub - skill_hub (search/install/manage from online registries — user-driven only)\n# moa - mixture_of_agents (requires OPENROUTER_API_KEY)\n# todo - todo (in-memory task planning, no deps)\n# tts - text_to_speech (Edge TTS free, or ELEVENLABS/OPENAI/MINIMAX/MISTRAL key)\n# cronjob - cronjob (create/list/update/pause/resume/run/remove scheduled tasks)\n#\n# PRESETS (curated bundles):\n# hermes-cli - All of the above except send_message\n# hermes-telegram - terminal, file, web, vision, image_gen, tts, browser,\n# skills, todo, cronjob, send_message\n# hermes-discord - Same as hermes-telegram\n# hermes-whatsapp - Same as hermes-telegram\n# hermes-slack - Same as hermes-telegram\n#\n# COMPOSITE:\n# debugging - terminal + web + file\n# safe - web + vision + moa (no terminal access)\n# all - Everything available\n#\n# web - Web search and content extraction (web_search, web_extract)\n# search - Web search only, no scraping (web_search)\n# terminal - Command execution and process management (terminal, process)\n# file - File operations: read, write, patch, search\n# browser - Full browser automation (navigate, click, type, screenshot, etc.)\n# vision - Image analysis (vision_analyze)\n# image_gen - Image generation with FLUX (image_generate)\n# skills - Load skill documents (skills_list, skill_view)\n# moa - Mixture of Agents reasoning (mixture_of_agents)\n# todo - Task planning and tracking for multi-step work\n# memory - Persistent memory across sessions (personal notes + user profile)\n# session_search - Search and recall past conversations (FTS5 + Gemini Flash summarization)\n# tts - Text-to-speech (Edge TTS free, ElevenLabs, OpenAI, MiniMax, Mistral)\n# cronjob - Schedule and manage automated tasks (CLI-only)\n#\n# Composite toolsets:\n# debugging - terminal + web + file (for troubleshooting)\n# safe - web + vision + moa (no terminal access)\n\n# NOTE: The top-level \"toolsets\" key is deprecated and ignored.\n# Tool configuration is managed per-platform via platform_toolsets above.\n# Use `hermes tools` to configure interactively, or edit platform_toolsets directly.\n#\n# CLI override: hermes chat --toolsets terminal,web,file\n\n# =============================================================================\n# MCP (Model Context Protocol) Servers\n# =============================================================================\n# Connect to external MCP servers to add tools from the MCP ecosystem.\n# Each server's tools are automatically discovered and registered.\n# See website/docs/user-guide/features/mcp.md for full documentation.\n#\n# Stdio servers (spawn a subprocess):\n# command: the executable to run\n# args: command-line arguments\n# env: environment variables (only these + safe defaults passed to subprocess)\n#\n# HTTP servers (connect to a URL):\n# url: the MCP server endpoint\n# headers: HTTP headers (e.g., for authentication)\n#\n# Optional per-server settings:\n# timeout: tool call timeout in seconds (default: 120)\n# connect_timeout: initial connection timeout (default: 60)\n# keepalive_interval: liveness ping cadence in seconds (default: 180).\n# Lower it below the server's session TTL for servers that expire idle\n# sessions quickly (e.g. Unreal Engine editor MCP, ~15s), otherwise idle\n# tool calls hit an expired session and pay a slow reconnect. Floored at 5s.\n#\n# mcp_servers:\n# time:\n# command: uvx\n# args: [\"mcp-server-time\"]\n# filesystem:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-filesystem\", \"/home/user\"]\n# notion:\n# url: https://mcp.notion.com/mcp\n# github:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-github\"]\n# env:\n# GITHUB_PERSONAL_ACCESS_TOKEN: \"ghp_...\"\n#\n# Sampling (server-initiated LLM requests) — enabled by default.\n# Per-server config under the 'sampling' key:\n# analysis:\n# command: npx\n# args: [\"-y\", \"analysis-server\"]\n# sampling:\n# enabled: true # default: true\n# model: \"gemini-3-flash\" # override model (optional)\n# max_tokens_cap: 4096 # max tokens per request\n# timeout: 30 # LLM call timeout (seconds)\n# max_rpm: 10 # max requests per minute\n# allowed_models: [] # model whitelist (empty = all)\n# max_tool_rounds: 5 # tool loop limit (0 = disable)\n# log_level: \"info\" # audit verbosity\n\n# =============================================================================\n# Text-to-Speech\n# =============================================================================\n# TTS defaults to Edge TTS unless changed in ~/.hermes/config.yaml.\n# Gemini TTS supports persona/director prompt files, and Gemini 3.1 Flash TTS\n# can use a hidden auxiliary rewrite pass to insert expressive square-bracket\n# audio tags into the TTS script without showing tags in chat.\n#\n# tts:\n# provider: \"gemini\"\n# gemini:\n# model: \"gemini-3.1-flash-tts-preview\"\n# voice: \"Kore\"\n# audio_tags: false\n# persona_prompt_file: \"\" # e.g. ~/.hermes/tts/radio-host.md\n\n# =============================================================================\n# Voice Transcription (Speech-to-Text)\n# =============================================================================\n# Automatically transcribe voice messages on messaging platforms.\n# Providers: local (free, faster-whisper) | groq (free tier) | openai (Whisper API) | mistral (Voxtral Transcribe)\n# Set the corresponding API key in .env: GROQ_API_KEY, OPENAI_API_KEY, or MISTRAL_API_KEY.\nstt:\n enabled: true\n # provider: \"local\" # auto-detected if omitted\n local:\n model: \"base\" # tiny | base | small | medium | large-v3 | turbo\n # language: \"\" # auto-detect; set to \"en\", \"es\", \"fr\", etc. to force\n openai:\n model: \"whisper-1\" # whisper-1 | gpt-4o-mini-transcribe | gpt-4o-transcribe\n # mistral:\n # model: \"voxtral-mini-latest\" # voxtral-mini-latest | voxtral-mini-2602\n\n# =============================================================================\n# Response Pacing (Messaging Platforms)\n# =============================================================================\n# Add human-like delays between message chunks.\n# human_delay:\n# mode: \"off\" # \"off\" | \"natural\" | \"custom\"\n# min_ms: 800 # Min delay (custom mode only)\n# max_ms: 2500 # Max delay (custom mode only)\n\n# =============================================================================\n# Session Logging\n# =============================================================================\n# Session trajectories are automatically saved to logs/ directory.\n# Each session creates: logs/session_YYYYMMDD_HHMMSS_UUID.json\n#\n# The session ID is displayed in the welcome banner for easy reference.\n# Logs contain full conversation history in trajectory format:\n# - System prompt, user messages, assistant responses\n# - Tool calls with inputs/outputs\n# - Timestamps for debugging\n#\n# No configuration needed - logging is always enabled.\n# To disable, you would need to modify the source code.\n\n# =============================================================================\n# Code Execution Sandbox (Programmatic Tool Calling)\n# =============================================================================\n# The execute_code tool runs Python scripts that call Hermes tools via RPC.\n# Intermediate tool results stay out of the LLM's context window.\ncode_execution:\n timeout: 300 # Max seconds per script before kill (default: 300 = 5 min)\n max_tool_calls: 50 # Max RPC tool calls per execution (default: 50)\n\n# =============================================================================\n# Subagent Delegation\n# =============================================================================\n# The delegate_task tool spawns child agents with isolated context.\n# Supports single tasks and batch mode (default 3 parallel, configurable).\ndelegation:\n max_iterations: 50 # Max tool-calling turns per child (default: 50)\n # max_concurrent_children: 3 # Max parallel child agents per batch (default: 3, floor: 1, no ceiling).\n # WARNING: values above 10 multiply API cost linearly.\n # max_spawn_depth: 1 # Delegation tree depth cap (range: 1-3, default: 1 = flat).\n # Raise to 2 to allow workers to spawn their own subagents.\n # Requires role=\"orchestrator\" on intermediate agents.\n # orchestrator_enabled: true # Kill switch for role=\"orchestrator\" children (default: true).\n # subagent_auto_approve: false # When a subagent hits a dangerous-command approval prompt, auto-deny (default: false)\n # or auto-approve \"once\" (true) instead of blocking on stdin.\n # The parent TUI owns stdin, so blocking would deadlock; non-interactive resolution is required.\n # Both choices emit a logger.warning audit line. Flip to true only for cron/batch pipelines.\n # inherit_mcp_toolsets: true # When explicit child toolsets are narrowed, also keep the parent's MCP toolsets (default: true). Set false for strict intersection.\n # model: \"google/gemini-3-flash-preview\" # Override model for subagents (empty = inherit parent)\n # provider: \"openrouter\" # Override provider for subagents (empty = inherit parent)\n # # Resolves full credentials (base_url, api_key) automatically.\n # # Supported: openrouter, nous, zai, kimi-coding, minimax\n\n# =============================================================================\n# Honcho Integration (Cross-Session User Modeling)\n# =============================================================================\n# AI-native persistent memory via Honcho (https://honcho.dev/).\n# Builds a deeper understanding of the user across sessions and tools.\n# Runs alongside USER.md — additive, not a replacement.\n#\n# Requires: pip install honcho-ai\n# Config: ~/.honcho/config.json (shared with Claude Code, Cursor, etc.)\n# API key: HONCHO_API_KEY in ~/.hermes/.env or ~/.honcho/config.json\n#\n# Hermes-specific overrides (optional — most config comes from ~/.honcho/config.json):\n# honcho: {}\n\n# =============================================================================\n# Display\n# =============================================================================\ndisplay:\n # Use compact banner mode (hides the ASCII-art banner, shows a single line).\n # true: Compact single-line banner\n # false: Full ASCII banner with tool/skill summary (default)\n compact: false\n\n # Tool progress display level (CLI and gateway)\n # off: Silent — no tool activity shown, just the final response\n # new: Show a tool indicator only when the tool changes (skip repeats)\n # all: Show every tool call with a short preview (default)\n # verbose: Full args, results, and debug logs (same as /verbose)\n # Toggle at runtime with /verbose in the CLI\n tool_progress: all\n\n # Per-platform defaults can be quieter than the global setting. Telegram\n # tunes for mobile: tool_progress and busy_ack_detail default off (no\n # per-tool breadcrumb stream, no \"iteration 21/60\" debug detail in busy\n # acks or heartbeats), but interim_assistant_messages and\n # long_running_notifications STAY ON so the user has real signal between\n # turn start and final answer (mid-turn assistant commentary + a single\n # edit-in-place \"⏳ Working — N min\" heartbeat). Override under\n # display.platforms.telegram.\n\n # Auto-cleanup of temporary progress bubbles after the final response lands.\n # On platforms that support message deletion (currently Telegram), this\n # removes the tool-progress bubble, \"⏳ Still working...\" notices, and\n # context-pressure status messages once the final reply has been delivered —\n # keeping long-running turns visible live, then tidy afterward. Failed runs\n # leave the bubbles in place as breadcrumbs. Off by default.\n # Per-platform override: display.platforms.telegram.cleanup_progress\n # true: Delete tracked progress/status bubbles on successful turn\n # false: Leave everything in place (default)\n # Example:\n # display:\n # platforms:\n # telegram:\n # cleanup_progress: true\n cleanup_progress: false\n\n # Gateway-only natural mid-turn assistant updates.\n # When true, completed assistant status messages are sent as separate chat\n # messages. This is independent of tool_progress and gateway streaming.\n # true: Send mid-turn assistant updates as separate messages (default)\n # false: Only send the final response\n interim_assistant_messages: true\n\n # Gateway-only long-running status heartbeats.\n # When false, the platform does not receive periodic \"⏳ Working — N min\"\n # notifications even if agent.gateway_notify_interval is non-zero. The\n # heartbeat edits a single message in place (where the adapter supports\n # editing) instead of posting a new bubble each interval.\n # Default: true everywhere, including Telegram (silent agents are worse\n # than a single edit-in-place heartbeat).\n long_running_notifications: true\n\n # Include detailed iteration/tool/status context in busy acknowledgments\n # and long-running heartbeats. When true, busy acks show \"iteration 21/60,\n # terminal, 10 min\" and the heartbeat shows \"⏳ Working — 12 min,\n # iteration 21/60, terminal\". When false (Telegram default), both stay\n # terse: \"Interrupting current task\" and \"⏳ Working — 12 min, terminal\".\n busy_ack_detail: true\n\n # What Enter does when Hermes is already busy (CLI and gateway platforms).\n # interrupt: Interrupt the current run and redirect Hermes (default)\n # queue: Queue your message for the next turn\n # steer: Inject your message mid-run via /steer, arriving at the agent\n # after the next tool call — no interrupt, no role violation.\n # Falls back to 'queue' if the agent isn't running yet or if\n # images are attached (steer only carries text).\n # Ctrl+C (or /stop in gateway) always interrupts regardless of this setting.\n # Toggle at runtime with /busy .\n busy_input_mode: interrupt\n\n # Background process notifications (gateway/messaging only).\n # Controls how chatty the process watcher is when you use\n # terminal(background=true, notify_on_complete=true) from Telegram/Discord/etc.\n # off: No watcher messages at all\n # result: Only the final completion message\n # error: Only the final message when exit code != 0\n # all: Running output updates + final message (default)\n background_process_notifications: all\n\n\n # Play terminal bell when agent finishes a response.\n # Useful for long-running tasks — your terminal will ding when the agent is done.\n # Works over SSH. Most terminals can be configured to flash the taskbar or play a sound.\n # true: Ring the terminal bell on each response\n # false: Silent (default)\n bell_on_complete: false\n\n # Show model reasoning/thinking before each response.\n # When enabled, a dim box shows the model's thought process above the response.\n # Toggle at runtime with /reasoning show or /reasoning hide.\n # true: Show the reasoning box\n # false: Hide reasoning (default)\n show_reasoning: false\n\n # Stream tokens to the terminal as they arrive instead of waiting for the\n # full response. The response box opens on first token and text appears\n # line-by-line. Tool calls are still captured silently.\n # true: Stream tokens as they arrive (default)\n # false: Wait for the full response before rendering\n streaming: true\n\n # Show [HH:MM] timestamps on user input and assistant response labels.\n # timestamps: false\n\n # ───────────────────────────────────────────────────────────────────────────\n # Skin / Theme\n # ───────────────────────────────────────────────────────────────────────────\n # Customize CLI visual appearance — banner colors, spinner faces, tool prefix,\n # response box label, and branding text. Change at runtime with /skin .\n #\n # Built-in skins:\n # default — Classic Hermes gold/kawaii\n # ares — Crimson/bronze war-god theme with spinner wings\n # mono — Clean grayscale monochrome\n # slate — Cool blue developer-focused\n # daylight — Bright light-mode theme\n # warm-lightmode — Warm paper-tone light-mode theme\n # poseidon — Sea-green/teal Olympian theme\n # sisyphus — Earthy stone-and-moss theme\n # charizard — Fiery orange dragon theme\n #\n # Custom skins: drop a YAML file in ~/.hermes/skins/.yaml\n # Schema (all fields optional, missing values inherit from default):\n #\n # name: my-theme\n # description: Short description\n # colors:\n # banner_border: \"#HEX\" # Panel border\n # banner_title: \"#HEX\" # Panel title\n # banner_accent: \"#HEX\" # Section headers (Available Tools, etc.)\n # banner_dim: \"#HEX\" # Dim/muted text\n # banner_text: \"#HEX\" # Body text (tool names, skill names)\n # ui_accent: \"#HEX\" # UI accent color\n # response_border: \"#HEX\" # Response box border color\n # spinner:\n # waiting_faces: [\"(⚔)\", \"(⛨)\"] # Faces shown while waiting\n # thinking_faces: [\"(⚔)\", \"(⌁)\"] # Faces shown while thinking\n # thinking_verbs: [\"forging\", \"plotting\"] # Verbs for spinner messages\n # wings: # Optional left/right spinner decorations\n # - [\"⟪⚔\", \"⚔⟫\"]\n # - [\"⟪▲\", \"▲⟫\"]\n # branding:\n # agent_name: \"My Agent\" # Banner title and branding\n # welcome: \"Welcome message\" # Shown at CLI startup\n # response_label: \" ⚔ Agent \" # Response box header label\n # prompt_symbol: \"⚔\" # Prompt symbol (bare token; renderers add trailing space)\n # tool_prefix: \"╎\" # Tool output line prefix (default: ┊)\n #\n skin: default\n\n# =============================================================================\n# Model Aliases — short names for /model command\n# =============================================================================\n# Map short aliases to exact (model, provider, base_url) tuples.\n# Used by /model tab completion and resolve_alias().\n# Aliases are checked BEFORE the models.dev catalog, so they can route\n# to endpoints not in the catalog (e.g. Ollama Cloud, local servers).\n#\n# model_aliases:\n# opus:\n# model: claude-opus-4-6\n# provider: anthropic\n# qwen:\n# model: \"qwen3.5:397b\"\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n# glm:\n# model: glm-4.7\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n\n# =============================================================================\n# Privacy\n# =============================================================================\n# privacy:\n# # Redact PII from the LLM context prompt.\n# # When true, phone numbers are stripped and user/chat IDs are replaced\n# # with deterministic hashes before being sent to the model.\n# # Names and usernames are NOT affected (user-chosen, publicly visible).\n# # Routing/delivery still uses the original values internally.\n# redact_pii: false\n\n# =============================================================================\n# Shell-script hooks\n# =============================================================================\n# Register shell scripts as plugin-hook callbacks. Each entry is executed as\n# a subprocess (shell=False, shlex.split) with a JSON payload on stdin. On\n# stdout the script may return JSON that either blocks the tool call or\n# injects context into the next LLM call.\n#\n# Valid events (mirror hermes_cli.plugins.VALID_HOOKS):\n# pre_tool_call, post_tool_call, pre_llm_call, post_llm_call,\n# pre_api_request, post_api_request, on_session_start, on_session_end,\n# on_session_finalize, on_session_reset, subagent_stop\n#\n# First-use consent: each (event, command) pair prompts once on a TTY, then\n# is persisted to ~/.hermes/shell-hooks-allowlist.json. Non-interactive\n# runs (gateway, cron) need --accept-hooks, HERMES_ACCEPT_HOOKS=1, or the\n# hooks_auto_accept key below.\n#\n# See website/docs/user-guide/features/hooks.md for the full JSON wire\n# protocol and worked examples.\n#\n# hooks:\n# pre_tool_call:\n# - matcher: \"terminal\"\n# command: \"~/.hermes/agent-hooks/block-rm-rf.sh\"\n# timeout: 10\n# post_tool_call:\n# - matcher: \"write_file|patch\"\n# command: \"~/.hermes/agent-hooks/auto-format.sh\"\n# pre_llm_call:\n# - command: \"~/.hermes/agent-hooks/inject-cwd-context.sh\"\n# subagent_stop:\n# - command: \"~/.hermes/agent-hooks/log-orchestration.sh\"\n#\n# hooks_auto_accept: false\n\n\n# =============================================================================\n# Update Behavior\n# =============================================================================\nupdates:\n # Create a full HERMES_HOME zip before every `hermes update`.\n # Backups land in ~/.hermes/backups/ and can be restored with `hermes import`.\n # Off by default because large homes can add minutes to every update.\n pre_update_backup: false\n\n # Number of pre-update backup zips to retain.\n backup_keep: 5\n\n # What non-interactive updates do with local source edits in the Hermes repo.\n # Interactive terminal updates always prompt before restoring the autostash.\n #\n # stash - auto-stash before pull, then auto-restore after success (default)\n # discard - drop the update-created stash after success; use only on managed\n # installs where local source edits should not persist\n non_interactive_local_changes: \"stash\"\n\n\n# =============================================================================\n# Web Dashboard\n# =============================================================================\n# OAuth gate configuration for `hermes dashboard --host `.\n# The bundled Nous Portal plugin reads these on startup; settings here are\n# the canonical surface. Each can be overridden by an environment variable:\n#\n# dashboard.oauth.client_id <- HERMES_DASHBOARD_OAUTH_CLIENT_ID\n# dashboard.oauth.portal_url <- HERMES_DASHBOARD_PORTAL_URL\n# dashboard.public_url <- HERMES_DASHBOARD_PUBLIC_URL\n#\n# Env wins when set to a non-empty value. This is what Fly.io's platform-\n# secret injection uses to push per-deploy client_ids without needing to\n# bake a config.yaml into the image. Empty env values are treated as unset\n# so a provisioned-but-not-populated secret can't shadow a valid entry here.\n#\n# Local dev / on-prem deploys should typically set these via config.yaml\n# (the ~/.hermes/.env file is reserved for API keys and secrets).\n#\n# dashboard:\n# oauth:\n# client_id: \"\" # agent:{instance_id}; Portal provisions this at deploy\n# portal_url: \"\" # blank → default https://portal.nousresearch.com\n#\n# # Force the absolute base URL the OAuth callback (and any other public\n# # URL the dashboard hands to external systems) is built from. Set this\n# # for deploys behind reverse proxies that don't reliably forward\n# # X-Forwarded-Host / X-Forwarded-Proto / X-Forwarded-Prefix (manual\n# # nginx setups, on-prem ingresses, custom-domain Fly deploys without\n# # full proxy header chains).\n# #\n# # When set, the value is the complete authority: scheme + host +\n# # optional path prefix (e.g. \"https://example.com/hermes\"). The OAuth\n# # callback URL becomes \"/auth/callback\" — X-Forwarded-Prefix\n# # is IGNORED on this code path because the operator has explicitly\n# # declared the public URL and we no longer need to guess.\n# #\n# # Leave empty to use the existing proxy-header reconstruction (the\n# # default — works on Fly.io out of the box).\n# #\n# # public_url: \"https://example.com/hermes\"\n"}, "e5d7a4625a0f9e948105d5755614b13bc9fe7f1f": {"t": 1786068505.159287, "n": 78007, "h": "849a670619fa8405", "c": "# Hermes Agent CLI Configuration\n# Copy this file to cli-config.yaml and customize as needed.\n# This file configures the CLI behavior. Environment variables in .env take precedence.\n\n# =============================================================================\n# Model Configuration\n# =============================================================================\nmodel:\n # Default model to use (can be overridden with --model flag)\n # Both \"default\" and \"model\" work as the key name here.\n default: \"anthropic/claude-opus-4.6\"\n \n # Inference provider selection:\n # \"auto\" - Auto-detect from credentials (default)\n # \"openrouter\" - OpenRouter (requires: OPENROUTER_API_KEY or OPENAI_API_KEY)\n # \"nous\" - Nous Portal OAuth (requires: hermes auth add nous)\n # \"nous-api\" - Nous Portal API key (requires: NOUS_API_KEY)\n # \"anthropic\" - Direct Anthropic API (requires: ANTHROPIC_API_KEY)\n # \"openai-codex\" - OpenAI Codex (requires: hermes auth)\n # \"copilot\" - GitHub Copilot / GitHub Models (requires: GITHUB_TOKEN)\n # \"gemini\" - Use Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n # \"zai\" - Use z.ai / ZhipuAI GLM models (requires: GLM_API_KEY)\n # \"kimi-coding\" - Kimi / Moonshot AI (requires: KIMI_API_KEY)\n # \"minimax\" - MiniMax global (requires: MINIMAX_API_KEY)\n # \"minimax-cn\" - MiniMax China (requires: MINIMAX_CN_API_KEY)\n # \"huggingface\" - Hugging Face Inference (requires: HF_TOKEN)\n # \"nvidia\" - NVIDIA NIM / build.nvidia.com (requires: NVIDIA_API_KEY)\n # \"xiaomi\" - Xiaomi MiMo (requires: XIAOMI_API_KEY)\n # \"arcee\" - Arcee AI Trinity models (requires: ARCEEAI_API_KEY)\n # \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY — https://ollama.com/settings)\n # \"deepinfra\" - DeepInfra (requires: DEEPINFRA_API_KEY)\n # \"kilocode\" - KiloCode gateway (requires: KILOCODE_API_KEY)\n # \"azure-foundry\" - Microsoft Foundry / Azure OpenAI (API key or Entra ID)\n # \"lmstudio\" - LM Studio local server (optional: LM_API_KEY, defaults to http://127.0.0.1:1234/v1)\n #\n # Local servers (LM Studio, Ollama, vLLM, llama.cpp):\n # \"custom\" - Any other OpenAI-compatible endpoint. Set base_url below.\n # Aliases: \"ollama\", \"vllm\", \"llamacpp\" all map to \"custom\".\n # LM Studio is first-class and uses provider: \"lmstudio\".\n # It works with both no-auth and auth-enabled server modes.\n #\n # Can also be overridden for a single invocation with the --provider flag.\n provider: \"auto\"\n \n # API configuration (falls back to OPENROUTER_API_KEY env var)\n # api_key: \"your-key-here\" # Uncomment to set here instead of .env\n base_url: \"https://openrouter.ai/api/v1\"\n\n # Azure Foundry keyless auth example:\n # provider: \"azure-foundry\"\n # base_url: \"https://.openai.azure.com/openai/v1\"\n # auth_mode: \"entra_id\" # DefaultAzureCredential: az login, managed identity, workload identity, etc.\n # default: \"gpt-4o\" # Deployment/model name\n # entra:\n # scope: \"https://ai.azure.com/.default\" # Optional; this is the default.\n\n # ── Token limits — two settings, easy to confuse ──────────────────────────\n #\n # context_length: TOTAL context window (input + output tokens combined).\n # Controls when Hermes compresses history and validates requests.\n # Leave unset — Hermes auto-detects the correct value from the provider.\n # Set manually only when auto-detection is wrong (e.g. a local server with\n # a custom num_ctx, or a proxy that doesn't expose /v1/models).\n #\n # context_length: 131072\n #\n # max_tokens: OUTPUT cap — maximum tokens the model may generate per response.\n # Unrelated to how long your conversation history can be.\n # The OpenAI-standard name \"max_tokens\" is a misnomer; Anthropic's native\n # API has since renamed it \"max_output_tokens\" for clarity.\n # Leave unset to use the model's native output ceiling (recommended).\n # Set only if you want to deliberately limit individual response length.\n #\n# max_tokens: 8192\n\n # ── Custom request headers (optional) ─────────────────────────────────────\n #\n # default_headers: extra HTTP headers sent on every request to an\n # OpenAI-compatible endpoint. User values take precedence over the\n # provider/SDK defaults, so this is the supported way to override the\n # OpenAI Python SDK's identifying headers (User-Agent: OpenAI/Python ...,\n # X-Stainless-*) when a custom provider sits behind a gateway/WAF that\n # rejects them — e.g. an upstream that returns \"502 Upstream access\n # forbidden\" for the SDK default User-Agent but accepts a plain one.\n # Applies on the OpenAI wire only (not native Anthropic / Bedrock).\n #\n # default_headers:\n # User-Agent: \"curl/8.7.1\"\n #\n # extra_headers: accepted as an alias of default_headers (merged, with\n # extra_headers winning when both are set) — matches the per-provider\n # extra_headers key below.\n #\n # Per-provider variant: named providers / custom_providers entries accept an\n # extra_headers dict scoped to that endpoint only — for reverse proxies,\n # gateways, or custom auth (e.g. Cloudflare Access service tokens).\n # Merged onto SDK/provider defaults with the entry's values winning.\n # Header values are treated as secrets and are never logged.\n #\n # providers:\n # my-proxy:\n # base_url: \"https://llm.internal.example.com/v1\"\n # key_env: \"MY_PROXY_API_KEY\"\n # extra_headers:\n # CF-Access-Client-Id: \"xxxx.access\"\n # CF-Access-Client-Secret: \"${CF_ACCESS_SECRET}\"\n # X-Client-Name: \"hermes-agent\"\n\n# Named provider overrides (optional)\n# Use this for per-provider request timeouts, non-stream stale timeouts,\n# and per-model exceptions.\n# Applies to the primary turn client on every api_mode (OpenAI-wire, native\n# Anthropic, and Anthropic-compatible providers), the fallback chain, and\n# client rebuilds during credential rotation. For OpenAI-wire chat\n# completions (streaming and non-streaming) the configured value is also\n# used as the per-request ``timeout=`` kwarg so it wins over the legacy\n# HERMES_API_TIMEOUT env var (which still applies when no config is set).\n# ``stale_timeout_seconds`` controls the non-streaming stale-call detector and\n# wins over the legacy HERMES_API_CALL_STALE_TIMEOUT env var. Leaving these\n# unset keeps the legacy defaults (HERMES_API_TIMEOUT=1800s,\n# HERMES_API_CALL_STALE_TIMEOUT=90s, native Anthropic 900s). The\n# implicit non-stream stale detector is auto-disabled for local endpoints\n# and can scale upward for very large contexts.\n#\n# Not currently wired for AWS Bedrock (bedrock_converse + AnthropicBedrock\n# SDK paths) — those use boto3 with its own timeout configuration.\n#\n# providers:\n# ollama-local:\n# request_timeout_seconds: 300 # Longer timeout for local cold-starts\n# stale_timeout_seconds: 900 # Explicitly re-enable stale detection on local endpoints\n# anthropic:\n# request_timeout_seconds: 30 # Fast-fail cloud requests\n# models:\n# claude-opus-4.6:\n# timeout_seconds: 600 # Longer timeout for extended-thinking Opus calls\n# openai-codex:\n# models:\n# gpt-5.4:\n# stale_timeout_seconds: 1800 # Longer non-stream stale timeout for slow large-context turns\n\n# =============================================================================\n# OpenRouter Provider Routing (only applies when using OpenRouter)\n# =============================================================================\n# Control how requests are routed across providers on OpenRouter.\n# See: https://openrouter.ai/docs/guides/routing/provider-selection\n#\n# provider_routing:\n# # Sort strategy: \"price\" (default), \"throughput\", or \"latency\"\n# # Append :nitro to model name for a shortcut to throughput sorting.\n# sort: \"throughput\"\n#\n# # Only allow these providers (provider slugs from OpenRouter)\n# # only: [\"anthropic\", \"google\"]\n#\n# # Skip these providers entirely\n# # ignore: [\"deepinfra\", \"fireworks\"]\n#\n# # Try providers in this order (overrides default load balancing)\n# # order: [\"anthropic\", \"google\", \"together\"]\n#\n# # Require providers to support all parameters in your request\n# # require_parameters: true\n#\n# # Data policy: \"allow\" (default) or \"deny\" to exclude providers that may store data\n# # data_collection: \"deny\"\n#\n# # ── DeepSeek prompt-cache pinning (RECOMMENDED for deepseek/* as main model) ──\n# # DeepSeek's context cache is automatic but backend-local: OpenRouter\n# # load-balances deepseek/* across upstream backends with separate caches,\n# # so consecutive turns can land on a cold backend and re-bill the FULL\n# # prefix. Pinning the order to \"deepseek\" keeps every turn on the same\n# # cache-warm backend. Measured impact: ~2x cost difference at scale.\n# # Fallbacks stay ON by default, so a DeepSeek outage degrades to another\n# # provider rather than failing. \"deepseek\" is a no-op for non-deepseek\n# # models (that backend doesn't serve them), so this is safe to leave set.\n# # order: [\"deepseek\"]\n\n# =============================================================================\n# OpenRouter Response Caching (only applies when using OpenRouter)\n# =============================================================================\n# Cache identical API responses at the OpenRouter edge for free instant replays.\n# When enabled, identical requests (same model, messages, parameters) return\n# cached responses with zero billing. Separate from Anthropic prompt caching.\n# See: https://openrouter.ai/docs/guides/features/response-caching\n#\n# openrouter:\n# response_cache: true # Enable response caching (default: true)\n# response_cache_ttl: 300 # Cache TTL in seconds, 1-86400 (default: 300)\n\n# =============================================================================\n# Git Worktree Isolation\n# =============================================================================\n# When enabled, each CLI session creates an isolated git worktree so multiple\n# agents can work on the same repo concurrently without file collisions.\n# Equivalent to always passing --worktree / -w on the command line.\n#\n# worktree: true # Always create a worktree when in a git repo\n# worktree: false # Default — only create when -w flag is passed\n#\n# By default a new worktree branches from the freshly-fetched remote tip\n# (the current branch's upstream, else the remote's default branch) so it\n# starts current with the project instead of from the local clone's\n# (possibly stale) HEAD. Set worktree_sync: false to branch from local HEAD\n# instead — useful when offline or when you deliberately want the clone's\n# exact current state as the base.\n#\n# worktree_sync: true # Default — branch from the fetched remote tip\n# worktree_sync: false # Branch from local HEAD (offline / pinned base)\n\n# =============================================================================\n# Terminal Tool Configuration\n# =============================================================================\n# Choose ONE of the following terminal configurations by uncommenting it.\n# The terminal tool executes commands in the specified environment.\n\n# -----------------------------------------------------------------------------\n# OPTION 1: Local execution (default)\n# Commands run directly on your machine in the current directory\n# -----------------------------------------------------------------------------\n# Working directory behavior:\n# - CLI (`hermes` command): Uses \".\" (current directory where you run hermes)\n# - Gateway/messaging/cron: Uses terminal.cwd here; legacy .env cwd values are deprecated\nterminal:\n backend: \"local\"\n cwd: \".\" # For local backend: \".\" = current directory. Ignored for remote backends unless a backend documents otherwise.\n timeout: 180\n # HOME policy for tool subprocesses:\n # auto - default: host uses your real HOME; containers use HERMES_HOME/home\n # real - force your real OS-user HOME\n # profile - force HERMES_HOME/home for strict per-profile CLI config isolation\n home_mode: \"auto\"\n docker_mount_cwd_to_workspace: false # SECURITY: off by default. Opt in to mount the launch cwd into Docker /workspace.\n lifetime_seconds: 300\n # sudo_password: \"hunter2\" # Optional: pipe a sudo password via sudo -S. SECURITY WARNING: plaintext.\n # sudo_password: \"\" # Explicit empty password: try empty and never open the interactive sudo prompt.\n\n# -----------------------------------------------------------------------------\n# OPTION 2: SSH remote execution\n# Commands run on a remote server - agent code stays local (sandboxed)\n# Great for: keeping agent isolated from its own code, using powerful remote hardware\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"ssh\"\n# cwd: \"/home/myuser/project\" # Path on the REMOTE server\n# timeout: 180\n# lifetime_seconds: 300\n# ssh_host: \"my-server.example.com\"\n# ssh_user: \"myuser\"\n# ssh_port: 22\n# ssh_key: \"~/.ssh/id_rsa\" # Optional - uses ssh-agent if not specified\n\n# -----------------------------------------------------------------------------\n# OPTION 3: Docker container\n# Commands run in an isolated Docker container\n# Great for: reproducible environments, testing, isolation\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"docker\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /)\n# timeout: 180\n# lifetime_seconds: 300\n# docker_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# docker_mount_cwd_to_workspace: true # Explicit opt-in: mount your launch cwd into /workspace\n# # Optional: run the container as your host user's uid:gid so files written\n# # into bind-mounted dirs are owned by you, not root. Drops SETUID/SETGID\n# # caps too since no gosu privilege drop is needed. Leave off if your\n# # chosen docker_image expects to start as root.\n# docker_run_as_host_user: true\n# # Optional: explicitly forward selected env vars into Docker.\n# # These values come from your current shell first, then ~/.hermes/.env.\n# # Warning: anything forwarded here is visible to commands run in the container.\n# docker_forward_env:\n# - \"GITHUB_TOKEN\"\n# - \"NPM_TOKEN\"\n# # Optional: extra flags passed verbatim to docker run (appended after security defaults).\n# # Useful for adding capabilities (e.g. apt installs needing SETUID) or custom options.\n# # Example: add a Linux capability not included by default\n# # docker_extra_args:\n# # - \"--cap-add\"\n# # - \"SETUID\"\n\n# -----------------------------------------------------------------------------\n# OPTION 4: Singularity/Apptainer container\n# Commands run in a Singularity container (common in HPC environments)\n# Great for: HPC clusters, shared compute environments\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"singularity\"\n# cwd: \"/workspace\" # Path INSIDE the container (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# singularity_image: \"docker://nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 5: Modal cloud execution\n# Commands run on Modal's cloud infrastructure\n# Great for: GPU access, scalable compute, serverless execution\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"modal\"\n# cwd: \"/workspace\" # Path INSIDE the sandbox (default: /root)\n# timeout: 180\n# lifetime_seconds: 300\n# modal_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n\n# -----------------------------------------------------------------------------\n# OPTION 6: Daytona cloud execution\n# Commands run in Daytona cloud sandboxes\n# Great for: Cloud dev environments, persistent workspaces, team collaboration\n# Requires: pip install daytona, DAYTONA_API_KEY env var\n# -----------------------------------------------------------------------------\n# terminal:\n# backend: \"daytona\"\n# cwd: \"~\"\n# timeout: 180\n# lifetime_seconds: 300\n# daytona_image: \"nikolaik/python-nodejs:python3.11-nodejs20\"\n# container_disk: 10240 # Daytona max is 10GB per sandbox\n\n#\n# --- Container resource limits (docker, singularity, modal, daytona -- ignored for local/ssh) ---\n# These settings apply to all container backends. They control the resources\n# allocated to the sandbox and whether its filesystem persists across sessions.\n container_cpu: 1 # CPU cores\n container_memory: 5120 # Memory in MB (5120 = 5GB)\n container_disk: 51200 # Disk in MB (51200 = 50GB)\n container_persistent: true # Persist filesystem across sessions (false = ephemeral)\n\n# -----------------------------------------------------------------------------\n# SUDO SUPPORT (works with ALL backends above)\n# -----------------------------------------------------------------------------\n# Add sudo_password to any terminal config above to enable sudo commands.\n# The password is piped via `sudo -S`. Works with local, ssh, docker, etc.\n#\n# SECURITY WARNING: Password stored in plaintext!\n#\n# INTERACTIVE PROMPT: If sudo_password is unset and the CLI is running,\n# you'll be prompted to enter your password when sudo is needed:\n# - 45-second timeout (auto-skips if no input)\n# - Press Enter to skip (command fails gracefully)\n# - Password is hidden while typing\n# - Password is cached for the session\n#\n# EMPTY PASSWORDS: Setting sudo_password to an explicit empty string is different\n# from leaving it unset. Hermes will try an empty password via `sudo -S` and\n# will not open the interactive prompt. This is useful for passwordless sudo,\n# Touch ID sudo setups, and environments where prompting is just noise.\n#\n# ALTERNATIVES:\n# - SSH backend: Configure passwordless sudo on the remote server\n# - Containers: Run as root inside the container (no sudo needed)\n# - Local: Configure /etc/sudoers for specific commands\n#\n# Example (add to your terminal section):\n# sudo_password: \"your-password-here\"\n\n# =============================================================================\n# Security Scanning (tirith)\n# =============================================================================\n# Optional pre-exec command security scanning via tirith.\n# Detects homograph URLs, pipe-to-shell, terminal injection, env manipulation.\n# Install: brew install sheeki03/tap/tirith\n# Docs: https://github.com/sheeki03/tirith\n#\n# security:\n# tirith_enabled: true # Enable/disable tirith scanning\n# tirith_path: \"tirith\" # Path to tirith binary (supports ~ expansion)\n# tirith_timeout: 5 # Scan timeout in seconds\n# tirith_fail_open: true # Allow commands if tirith unavailable\n\n# =============================================================================\n# Browser Tool Configuration\n# =============================================================================\nbrowser:\n # Inactivity timeout in seconds - browser sessions are automatically closed\n # after this period of no activity between agent loops (default: 120 = 2 minutes)\n inactivity_timeout: 120\n\n# =============================================================================\n# Tool Loop Guardrails\n# =============================================================================\n# Soft warnings are enabled by default. They append guidance to repeated failed\n# or non-progressing tool results but still let the tool execute. Hard stops are\n# opt-in circuit breakers for autonomous/cron sessions where stopping a loop is\n# preferable to spending the full iteration budget.\ntool_loop_guardrails:\n warnings_enabled: true\n hard_stop_enabled: false\n warn_after:\n exact_failure: 2\n same_tool_failure: 3\n idempotent_no_progress: 2\n hard_stop_after:\n exact_failure: 5\n same_tool_failure: 8\n idempotent_no_progress: 5\n\n# =============================================================================\n# Context Compression (Auto-shrinks long conversations)\n# =============================================================================\n# When conversation approaches model's context limit, middle turns are\n# automatically summarized to free up space while preserving important context.\n#\n# HOW IT WORKS:\n# 1. Tracks actual token usage from API responses (not estimates)\n# 2. When prompt_tokens >= threshold% of model's context_length, triggers compression\n# 3. Protects first 3 turns (system prompt, initial request, first response)\n# 4. Protects last N turns (default 20 messages = ~10 full turns of recent context)\n# 5. Summarizes middle turns using a fast/cheap model\n# 6. Inserts summary as a user message, continues conversation seamlessly\n#\n# Post-compression tail budget is target_ratio × threshold × context_length:\n# 200K context, threshold 0.50, ratio 0.20 → 20K tokens of recent tail preserved\n# 1M context, threshold 0.50, ratio 0.20 → 100K tokens of recent tail preserved\n#\ncompression:\n # Enable automatic context compression (default: true)\n # Set to false if you prefer to manage context manually or want errors on overflow\n enabled: true\n \n # Trigger compression at this % of model's context limit (default: 0.50 = 50%)\n # Lower values = more aggressive compression, higher values = compress later\n # Models with context windows below 512K are floored at 0.75 (raise-only) so\n # compaction doesn't fire with half the window still free; set above 0.75 to override.\n threshold: 0.50\n\n # Existing Codex gpt-5.5 behavior: raise Hermes' compaction trigger to 85%\n # for the ChatGPT Codex OAuth route. Set false to opt back down to threshold.\n codex_gpt55_autoraise: true\n\n # Fraction of the threshold to preserve as recent tail (default: 0.20 = 20%)\n # e.g. 20% of 50% threshold = 10% of total context kept as recent messages.\n # Summary output is separately capped at 12K tokens (Gemini output limit).\n # Range: 0.10 - 0.80\n target_ratio: 0.20\n\n # Number of most-recent messages to always preserve (default: 20 ≈ 10 full turns)\n # Higher values keep more recent conversation intact at the cost of more aggressive\n # compression of older turns.\n protect_last_n: 20\n\n # Codex app-server (codex CLI runtime) thread-compaction mode. The codex\n # agent owns the real thread context on this runtime, so Hermes' summarizer\n # cannot shrink it — compaction goes through the app server instead.\n # native = let Codex decide when to compact its own thread (default)\n # hermes = let Hermes threshold trigger Codex thread/compact/start\n # off = Hermes will not auto-trigger compaction; Codex may still compact natively\n codex_app_server_auto: native\n\n # Number of non-system messages to protect at the head of the transcript, in\n # ADDITION to the system prompt (which is always implicitly protected).\n # Head messages are NEVER summarized — they survive every compression\n # indefinitely. This gives stable early context for short/medium sessions,\n # but in long-running sessions that rely on rolling compaction the pinned\n # opening turns may not match how you want the session framed over time.\n # Set to 0 to preserve ONLY the system prompt (plus the rolling summary\n # and recent tail) — the cleanest configuration for long-running sessions.\n # Default 3 preserves the system prompt plus the first three non-system\n # head messages, matching the pre-feature behaviour.\n protect_first_n: 3\n\n # To pin a specific model/provider for compression summaries, use the\n # auxiliary section below (auxiliary.compression.provider / model).\n\n# =============================================================================\n# Anthropic prompt caching TTL\n# =============================================================================\n# When prompt caching is active (Claude via OpenRouter or native Anthropic),\n# Anthropic supports two TTL tiers for cached prefixes: \"5m\" (default) and\n# \"1h\". Other values are ignored and \"5m\" is used.\n#\nprompt_caching:\n cache_ttl: \"5m\" # use \"1h\" for long sessions with pauses between turns\n\n# =============================================================================\n# Auxiliary Models (Advanced — Experimental)\n# =============================================================================\n# Hermes uses lightweight \"auxiliary\" models for side tasks: image analysis,\n# browser screenshot analysis, web page summarization, TTS audio-tag insertion,\n# and context compression.\n#\n# By default these use Gemini Flash via OpenRouter or Nous Portal and are\n# auto-detected from your credentials. You do NOT need to change anything\n# here for normal usage.\n#\n# WARNING: Overriding these with providers other than OpenRouter or Nous Portal\n# is EXPERIMENTAL and may not work. Not all models/providers support vision,\n# produce usable summaries, or accept the same API format. Change at your own\n# risk — if things break, reset to \"auto\" / empty values.\n#\n# Each task has its own provider + model pair so you can mix providers.\n# For example: OpenRouter for vision (needs multimodal), but your main\n# local endpoint for compression (just needs text).\n#\n# Provider options:\n# \"auto\" - Best available: OpenRouter → Nous Portal → main endpoint (default)\n# \"openrouter\" - Force OpenRouter (requires OPENROUTER_API_KEY)\n# \"nous\" - Force Nous Portal (requires: hermes auth add nous)\n# \"gemini\" - Force Google AI Studio direct (requires: GOOGLE_API_KEY or GEMINI_API_KEY)\n# \"ollama-cloud\" - Ollama Cloud (requires: OLLAMA_API_KEY)\n# \"codex\" - Force Codex OAuth (requires: hermes model → Codex).\n# Uses gpt-5.3-codex which supports vision.\n# \"main\" - Use your custom endpoint (OPENAI_BASE_URL + OPENAI_API_KEY).\n# Works with OpenAI API, local models, or any OpenAI-compatible\n# endpoint. Also falls back to Codex OAuth and API-key providers.\n#\n# Model: leave empty to use the provider's default. When empty, OpenRouter\n# uses \"google/gemini-3-flash-preview\" and Nous uses \"gemini-3-flash\".\n# Other providers pick a sensible default automatically.\n#\n# auxiliary:\n# # Image analysis: vision_analyze tool + browser screenshots\n# vision:\n# provider: \"auto\"\n# model: \"\" # e.g. \"google/gemini-2.5-flash\", \"openai/gpt-4o\"\n# timeout: 30 # LLM API call timeout (seconds)\n# download_timeout: 30 # Image HTTP download timeout (seconds)\n# # Increase for slow connections or self-hosted image servers\n# reasoning_effort: \"\" # Per-task thinking level: none, minimal, low, medium,\n# # high, xhigh, max, ultra. Empty = provider default.\n# # Works on every auxiliary task block (vision,\n# # web_extract, compression, title_generation, curator,\n# # background_review, moa_reference, ...). Example: run\n# # compression at \"low\" and vision at \"none\" to cut\n# # side-task latency/cost on reasoning models.\n#\n# # Web page scraping / summarization + browser page text extraction\n# web_extract:\n# provider: \"auto\"\n# model: \"\"\n# reasoning_effort: \"low\"\n#\n# # Gemini 3.1 TTS hidden audio-tag insertion\n# tts_audio_tags:\n# provider: \"auto\" # empty model = your main chat model\n# model: \"\"\n# timeout: 30\n#\n# # Automatic session title generation after the first exchange\n# title_generation:\n# enabled: true # set false to disable auto-title generation\n# provider: \"auto\"\n# model: \"\"\n# timeout: 30\n# language: \"\" # empty = match the user's language; or e.g. \"English\"\n#\n# # Session search — summarizes matching past sessions\n# session_search:\n# provider: \"auto\"\n# model: \"\"\n# timeout: 30\n# max_concurrency: 3 # Limit parallel summaries to reduce request-burst 429s\n# extra_body: {} # Provider-specific OpenAI-compatible request fields\n# # Example for providers that support request-body\n# # reasoning controls:\n# # extra_body:\n# # enable_thinking: false\n# # Some vLLM/Qwen deployments expect this nested:\n# # extra_body:\n# # chat_template_kwargs:\n# # enable_thinking: false\n\n# =============================================================================\n# Persistent Memory\n# =============================================================================\n# Bounded curated memory injected into the system prompt every session.\n# Two stores: MEMORY.md (agent's notes) and USER.md (user profile).\n# Character limits keep the memory small and focused. The agent manages\n# pruning -- when at the limit, it must consolidate or replace entries.\n# Disabled by default in batch_runner.\n#\nmemory:\n # Agent's personal notes: environment facts, conventions, things learned\n memory_enabled: true\n \n # User profile: preferences, communication style, expectations\n user_profile_enabled: true\n \n # Character limits (~2.75 chars per token, model-independent)\n memory_char_limit: 2200 # ~800 tokens\n user_char_limit: 1375 # ~500 tokens\n\n # Periodic memory nudge: remind the agent to consider saving memories\n # every N user turns. Set to 0 to disable. Only active when memory is enabled.\n nudge_interval: 10 # Nudge every 10 user turns (0 = disabled)\n\n # Memory flush: give the agent one turn to save memories before context is\n # lost (compression, /new, /reset, exit). Set to 0 to disable.\n # For exit/reset, only fires if the session had at least this many user turns.\n flush_min_turns: 6 # Min user turns to trigger flush on exit/reset (0 = disabled)\n\n# =============================================================================\n# Session Reset Policy (Messaging Platforms)\n# =============================================================================\n# Controls when messaging sessions (Telegram, Discord, WhatsApp, Slack) are\n# automatically cleared. Default is \"none\": sessions never auto-reset —\n# conversation context lives until you /reset or /new manually, or context\n# compression kicks in. Opt in to automatic resets if you prefer sessions to\n# clear on a schedule (long-lived context increases API cost per message,\n# though prompt caching and compression keep this manageable).\n#\n# When an automatic reset triggers, the agent first saves important\n# information to its persistent memory — but the conversation context is\n# wiped. The agent starts fresh but retains learned facts via its memory\n# system.\n#\n# Modes:\n# \"none\" - Never auto-reset (default); context lives until /reset or compression\n# \"idle\" - Reset after N minutes of inactivity\n# \"daily\" - Reset at a fixed hour each day\n# \"both\" - Reset on EITHER inactivity timeout or daily boundary\n#\nsession_reset:\n mode: none # \"none\", \"idle\", \"daily\", or \"both\"\n idle_minutes: 1440 # Inactivity timeout in minutes (used by \"idle\"/\"both\")\n at_hour: 4 # Daily reset hour, 0-23 local time (used by \"daily\"/\"both\")\n\n# Maximum number of simultaneously active chat sessions across CLI, TUI,\n# dashboard chat, and messaging gateway. Set to null, 0, or omit to allow\n# unlimited concurrent sessions. When the limit is reached, new sessions get a\n# clean error while existing active sessions keep their normal behavior. This\n# top-level key takes precedence over gateway.max_concurrent_sessions. The cap\n# is a best-effort single-host/profile runtime guard; Hermes fails open if the\n# local runtime lease registry cannot be read or locked.\nmax_concurrent_sessions: null\n\n# When true, group/channel chats use one session per participant when the platform\n# provides a user ID. This is the secure default and prevents users in the same\n# room from sharing context, interrupts, and token costs. Set false only if you\n# explicitly want one shared \"room brain\" per group/channel.\ngroup_sessions_per_user: true\n\n# ─────────────────────────────────────────────────────────────────────────────\n# API Server — per-client model routing\n# ─────────────────────────────────────────────────────────────────────────────\n# Route different API clients to different models/providers on a single\n# Hermes deployment. Clients choose a backend by sending a specific string\n# as the OpenAI ``model`` field. Unmapped model values fall back to the\n# global model configured in the ``model:`` section above, and an explicit\n# session /model override always wins over a route.\n#\n# Configure via the ``platforms.api_server.extra.model_routes`` gateway\n# config block:\n#\n# platforms:\n# api_server:\n# enabled: true\n# extra:\n# key: \"your-api-server-secret\"\n# model_routes:\n# # Xiaozhi clients send model=\"minimax-m2\" → routed to MiniMax via OpenRouter\n# minimax-m2:\n# model: \"minimax/minimax-m1\"\n# provider: \"openrouter\" # optional — overrides global provider\n# # api_key: \"sk-...\" # optional — per-route UPSTREAM provider\n# # key (NOT caller auth; never logged)\n# # base_url: \"https://...\" # optional — per-route base URL\n# # GPT clients keep their own alias\n# gpt-5:\n# model: \"openai/gpt-5\"\n# provider: \"openrouter\"\n#\n# Configured aliases are automatically listed by GET /v1/models so clients\n# can discover them without manual coordination. Caller authentication is\n# unchanged: every request still authenticates with the global API server\n# key (``extra.key`` / API_SERVER_KEY).\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Gateway Streaming\n# ─────────────────────────────────────────────────────────────────────────────\n# Stream tokens to messaging platforms in real-time. The bot sends a message\n# on first token, then progressively edits it as more tokens arrive.\n# Disabled by default — enable to try the streaming UX on Telegram/Discord/Slack.\n# For Telegram, partial edits are sent as plain text and only the final edit uses MarkdownV2.\nstreaming:\n enabled: false\n # transport: edit # \"edit\" = progressive editMessageText\n # edit_interval: 0.3 # seconds between message edits\n # buffer_threshold: 40 # chars before forcing an edit flush\n # cursor: \" ▉\" # cursor shown during streaming\n\n# =============================================================================\n# Skills Configuration\n# =============================================================================\n# Skills are reusable procedures the agent can load and follow. The agent can\n# also create new skills after completing complex tasks.\n#\nskills:\n # Nudge the agent to create skills after complex tasks.\n # Every N tool-calling iterations, remind the model to consider saving a skill.\n # Set to 0 to disable.\n creation_nudge_interval: 15\n\n # External skill directories — share skills across tools/agents without\n # copying them into ~/.hermes/skills/. Each path is expanded (~ and ${VAR})\n # and resolved to an absolute path. External dirs are read-only: skill\n # creation always writes to ~/.hermes/skills/. Local skills take precedence\n # when names collide.\n # external_dirs:\n # - ~/.agents/skills\n # - /home/shared/team-skills\n\n# =============================================================================\n# Agent Behavior\n# =============================================================================\nagent:\n # Maximum tool-calling iterations per conversation\n # Higher = more room for complex tasks, but costs more tokens\n # Recommended: 20-30 for focused tasks, 50-100 for open exploration\n max_turns: 60\n\n # Inactivity timeout for gateway agent runs (seconds, 0 = unlimited).\n # The agent can run indefinitely when actively calling tools or receiving\n # API responses. Only fires after the agent has been idle for this duration.\n # gateway_timeout: 1800\n\n # Staged warning: send a warning before escalating to full timeout.\n # Fires once per run when inactivity reaches this threshold (seconds).\n # Set to 0 to disable the warning.\n # gateway_timeout_warning: 900\n\n # Graceful drain timeout for gateway stop/restart (seconds).\n # Default 0 = no drain: a restart interrupts in-flight agents immediately,\n # cleans up, and exits. Set a positive value only if you want a grace\n # window on /restart, and keep it well under systemd's TimeoutStopSec.\n # restart_drain_timeout: 0\n\n # Max app-level retry attempts for API errors (connection drops, provider\n # timeouts, 5xx, etc.) before the agent surfaces the failure. Lower this\n # to 1 if you use fallback providers and want fast failover on flaky\n # primaries (default 3). The OpenAI SDK does its own low-level retries\n # underneath this wrapper — this is the Hermes-level loop.\n # api_max_retries: 3\n\n # After the agent edits code without fresh passing verification, nudge it to\n # verify before finishing. The default \"auto\" enables it on interactive\n # coding surfaces (CLI, TUI, desktop) and programmatic callers, and disables\n # it on conversational messaging surfaces (Telegram, Discord, etc.) where the\n # verification summary would reach a human as chat noise. Set true or false to\n # force it on or off; the HERMES_VERIFY_ON_STOP env var (1/0) takes precedence.\n # verify_on_stop: auto\n\n # Standing operator instructions for the coding posture (when Hermes is in a\n # code workspace). Appended to the coding brief as an extra system block, so\n # you can pin project-wide workflow rules without editing the shipped brief.\n # Accepts a string or a list of strings. Takes effect next session.\n # coding_instructions:\n # - \"For UI work, don't run tsc/lint until I approve the look.\"\n # - \"Clean the diff before you commit and push.\"\n\n # When verify-on-stop finds edited code without fresh verification evidence,\n # append guidance for creative UI work (avoid broad tsc/lint/test before visual\n # approval) and clean-diff expectations. Set false to keep that nudge terse.\n # verify_guidance: true\n\n # A `pre_verify` hook (plugin or shell, see Event Hooks docs) can keep the\n # agent going one more turn to verify/clean before finishing. This caps how\n # many times one turn may be nudged to continue, so a hook can't trap the loop.\n # Default 3.\n # max_verify_nudges: 3\n\n # Enable verbose logging\n verbose: false\n \n # Reasoning effort level (OpenRouter and Nous Portal)\n # Controls how much \"thinking\" the model does before responding.\n # Options: \"xhigh\" (max), \"high\", \"medium\", \"low\", \"minimal\", \"none\" (disable)\n reasoning_effort: \"medium\"\n \n # Per-model reasoning effort overrides (optional dict)\n # Key: any sensible model spelling works (exact, dots↔dashes interchangeable,\n # provider prefix optional). First match wins.\n # Value: reasoning effort level (same options as reasoning_effort)\n # Override the global reasoning_effort for that specific model.\n # NOTE: no `hermes config set` support for this key -- edit YAML directly.\n # reasoning_overrides:\n # \"openrouter/anthropic/claude-opus-4.5\": \"xhigh\"\n # \"openai/gpt-5\": \"low\"\n # \"claude-opus-4.6\": \"high\" # bare model name also works\n # \"deepseek/deepseek-v4-pro\": \"xhigh\" # dots and dashes are interchangeable\n reasoning_overrides: {}\n \n # Predefined personalities (use with /personality command)\n personalities:\n helpful: \"You are a helpful, friendly AI assistant.\"\n concise: \"You are a concise assistant. Keep responses brief and to the point.\"\n technical: \"You are a technical expert. Provide detailed, accurate technical information.\"\n creative: \"You are a creative assistant. Think outside the box and offer innovative solutions.\"\n teacher: \"You are a patient teacher. Explain concepts clearly with examples.\"\n kawaii: \"You are a kawaii assistant! Use cute expressions like (◕‿◕), ★, ♪, and ~! Add sparkles and be super enthusiastic about everything! Every response should feel warm and adorable desu~! ヽ(>∀<☆)ノ\"\n catgirl: \"You are Neko-chan, an anime catgirl AI assistant, nya~! Add 'nya' and cat-like expressions to your speech. Use kaomoji like (=^・ω・^=) and ฅ^•ﻌ•^ฅ. Be playful and curious like a cat, nya~!\"\n pirate: \"Arrr! Ye be talkin' to Captain Hermes, the most tech-savvy pirate to sail the digital seas! Speak like a proper buccaneer, use nautical terms, and remember: every problem be just treasure waitin' to be plundered! Yo ho ho!\"\n shakespeare: \"Hark! Thou speakest with an assistant most versed in the bardic arts. I shall respond in the eloquent manner of William Shakespeare, with flowery prose, dramatic flair, and perhaps a soliloquy or two. What light through yonder terminal breaks?\"\n surfer: \"Duuude! You're chatting with the chillest AI on the web, bro! Everything's gonna be totally rad. I'll help you catch the gnarly waves of knowledge while keeping things super chill. Cowabunga! 🤙\"\n noir: \"The rain hammered against the terminal like regrets on a guilty conscience. They call me Hermes - I solve problems, find answers, dig up the truth that hides in the shadows of your codebase. In this city of silicon and secrets, everyone's got something to hide. What's your story, pal?\"\n uwu: \"hewwo! i'm your fwiendwy assistant uwu~ i wiww twy my best to hewp you! *nuzzles your code* OwO what's this? wet me take a wook! i pwomise to be vewy hewpful >w<\"\n philosopher: \"Greetings, seeker of wisdom. I am an assistant who contemplates the deeper meaning behind every query. Let us examine not just the 'how' but the 'why' of your questions. Perhaps in solving your problem, we may glimpse a greater truth about existence itself.\"\n hype: \"YOOO LET'S GOOOO!!! 🔥🔥🔥 I am SO PUMPED to help you today! Every question is AMAZING and we're gonna CRUSH IT together! This is gonna be LEGENDARY! ARE YOU READY?! LET'S DO THIS! 💪😤🚀\"\n\n# =============================================================================\n# Toolsets\n# =============================================================================\n# Control which tools the agent has access to.\n# Use `hermes tools` to interactively enable/disable tools per platform.\n\n# =============================================================================\n# Platform Toolsets (per-platform tool configuration)\n# =============================================================================\n# Override which toolsets are available on each platform.\n# If a platform isn't listed here, its built-in default is used.\n#\n# You can use EITHER:\n# - A preset like \"hermes-cli\" or \"hermes-telegram\" (curated tool set)\n# - A list of individual toolsets to compose your own (see list below)\n#\n# Supported platform keys: cli, telegram, discord, whatsapp, slack, qqbot, teams, google_chat\n#\n# Examples:\n#\n# # Use presets (same as defaults):\n# platform_toolsets:\n# cli: [hermes-cli]\n# telegram: [hermes-telegram]\n#\n# # Custom: give Telegram only web + terminal + file + planning:\n# platform_toolsets:\n# telegram: [web, terminal, file, todo]\n#\n# # Custom: CLI without browser or image gen:\n# platform_toolsets:\n# cli: [web, terminal, file, skills, todo, tts, cronjob]\n#\n# # Restrictive: Discord gets read-only tools only:\n# platform_toolsets:\n# discord: [web, vision, skills, todo]\n#\n# If not set, defaults are:\n# cli: hermes-cli (everything + cronjob management)\n# telegram: hermes-telegram (terminal, file, web, vision, image, tts, browser, skills, todo, cronjob, messaging)\n# discord: hermes-discord (same as telegram)\n# whatsapp: hermes-whatsapp (same as telegram)\n# slack: hermes-slack (same as telegram)\n# signal: hermes-signal (same as telegram)\n# homeassistant: hermes-homeassistant (same as telegram)\n# qqbot: hermes-qqbot (same as telegram)\n# teams: hermes-teams (same as telegram)\n# google_chat: hermes-google_chat (same as telegram)\n#\nplatform_toolsets:\n cli: [hermes-cli]\n telegram: [hermes-telegram]\n discord: [hermes-discord]\n whatsapp: [hermes-whatsapp]\n slack: [hermes-slack]\n signal: [hermes-signal]\n homeassistant: [hermes-homeassistant]\n qqbot: [hermes-qqbot]\n yuanbao: [hermes-yuanbao]\n teams: [hermes-teams]\n google_chat: [hermes-google_chat]\n\n# =============================================================================\n# Gateway Platform Settings\n# =============================================================================\n# Optional per-platform messaging settings.\n# Platform-specific knobs live under `extra`.\n#\n# platforms:\n# telegram:\n# reply_to_mode: \"first\" # off | first | all\n# # guest_mode lets explicit @mentions from non-allowlisted groups through.\n# # Default false; ordinary messages, replies, and regex wake words stay blocked.\n# guest_mode: false\n# # allowed_chats: [\"-1001234567890\"]\n# extra:\n# disable_link_previews: false # Set true to suppress Telegram URL previews in bot messages\n# rich_messages: false # Bot API 10.1 rich messages (tables/task lists/details/math); default false for copyable legacy MarkdownV2, set true to opt in\n# rich_drafts: false # Experimental rich draft previews during Telegram DM streaming; default false because Telegram Desktop/macOS can visually overlay draft frames\n# command_menu:\n# # Telegram allows up to 100 BotCommands; Hermes defaults to 60 so\n# # all built-in commands plus common skill commands stay visible\n# # while remaining under Telegram's payload-size limit. Clamped 1..100.\n# max_commands: 60\n# # prepend = user priority first, then Hermes defaults\n# # append = Hermes defaults first, then user priority\n# # replace = only the list below defines priority\n# priority_mode: prepend\n# priority:\n# - my_plugin_command\n# webhook:\n# extra:\n# # Route scripts default to a 30 second timeout. Scripts must live under\n# # the active profile's scripts directory and receive webhook JSON on stdin.\n# script_timeout_seconds: 30\n#\n# Discord-specific settings (config.yaml top-level, not under platforms:):\n#\n# discord:\n# require_mention: true # Require @mention in server channels (default: true)\n# auto_thread: true # Auto-create thread on @mention (default: true)\n# free_response_channels: \"\" # Channel IDs where no mention is needed\n# reactions: true # Show processing reactions (default: true)\n# history_backfill: true # Recover missed channel messages on mention (default: true)\n# history_backfill_limit: 50 # Max messages to scan backwards (default: 50)\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Available toolsets (use these names in platform_toolsets or the toolsets list)\n#\n# Run `hermes chat --list-toolsets` to see all toolsets and their tools.\n# Run `hermes chat --list-tools` to see every individual tool with descriptions.\n# ─────────────────────────────────────────────────────────────────────────────\n#\n# INDIVIDUAL TOOLSETS (compose your own):\n# web - web_search, web_extract\n# search - web_search only (no scraping)\n# terminal - terminal, process\n# file - read_file, write_file, patch, search\n# browser - browser_navigate, browser_snapshot, browser_click, browser_type,\n# browser_scroll, browser_back, browser_press,\n# browser_get_images, browser_vision (requires BROWSERBASE_API_KEY)\n# vision - vision_analyze (requires OPENROUTER_API_KEY)\n# image_gen - image_generate (requires FAL_KEY)\n# skills - skills_list, skill_view\n# skills_hub - skill_hub (search/install/manage from online registries — user-driven only)\n# todo - todo (in-memory task planning, no deps)\n# tts - text_to_speech (Edge TTS free, or ELEVENLABS/OPENAI/MINIMAX/MISTRAL key)\n# cronjob - cronjob (create/list/update/pause/resume/run/remove scheduled tasks)\n#\n# PRESETS (curated bundles):\n# hermes-cli - All of the above except send_message\n# hermes-telegram - terminal, file, web, vision, image_gen, tts, browser,\n# skills, todo, cronjob, send_message\n# hermes-discord - Same as hermes-telegram\n# hermes-whatsapp - Same as hermes-telegram\n# hermes-slack - Same as hermes-telegram\n#\n# COMPOSITE:\n# debugging - terminal + web + file\n# safe - web + vision (no terminal access)\n# all - Everything available\n#\n# web - Web search and content extraction (web_search, web_extract)\n# search - Web search only, no scraping (web_search)\n# terminal - Command execution and process management (terminal, process)\n# file - File operations: read, write, patch, search\n# browser - Full browser automation (navigate, click, type, screenshot, etc.)\n# vision - Image analysis (vision_analyze)\n# image_gen - Image generation with FLUX (image_generate)\n# skills - Load skill documents (skills_list, skill_view)\n# todo - Task planning and tracking for multi-step work\n# memory - Persistent memory across sessions (personal notes + user profile)\n# session_search - Search and recall past conversations (FTS5 + Gemini Flash summarization)\n# tts - Text-to-speech (Edge TTS free, ElevenLabs, OpenAI, MiniMax, Mistral)\n# cronjob - Schedule and manage automated tasks (CLI-only)\n#\n# Composite toolsets:\n# debugging - terminal + web + file (for troubleshooting)\n# safe - web + vision (no terminal access)\n\n# NOTE: The top-level \"toolsets\" key is deprecated and ignored.\n# Tool configuration is managed per-platform via platform_toolsets above.\n# Use `hermes tools` to configure interactively, or edit platform_toolsets directly.\n#\n# CLI override: hermes chat --toolsets terminal,web,file\n\n# =============================================================================\n# MCP (Model Context Protocol) Servers\n# =============================================================================\n# Connect to external MCP servers to add tools from the MCP ecosystem.\n# Each server's tools are automatically discovered and registered.\n# See website/docs/user-guide/features/mcp.md for full documentation.\n#\n# Stdio servers (spawn a subprocess):\n# command: the executable to run\n# args: command-line arguments\n# env: environment variables (only these + safe defaults passed to subprocess)\n#\n# HTTP servers (connect to a URL):\n# url: the MCP server endpoint\n# headers: HTTP headers (e.g., for authentication)\n#\n# Optional per-server settings:\n# timeout: tool call timeout in seconds (default: 120)\n# connect_timeout: initial connection timeout (default: 60)\n# keepalive_interval: liveness ping cadence in seconds (default: 180).\n# Lower it below the server's session TTL for servers that expire idle\n# sessions quickly (e.g. Unreal Engine editor MCP, ~15s), otherwise idle\n# tool calls hit an expired session and pay a slow reconnect. Floored at 5s.\n#\n# mcp_servers:\n# time:\n# command: uvx\n# args: [\"mcp-server-time\"]\n# filesystem:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-filesystem\", \"/home/user\"]\n# notion:\n# url: https://mcp.notion.com/mcp\n# github:\n# command: npx\n# args: [\"-y\", \"@modelcontextprotocol/server-github\"]\n# env:\n# GITHUB_PERSONAL_ACCESS_TOKEN: \"ghp_...\"\n#\n# Sampling (server-initiated LLM requests) — enabled by default.\n# Per-server config under the 'sampling' key:\n# analysis:\n# command: npx\n# args: [\"-y\", \"analysis-server\"]\n# sampling:\n# enabled: true # default: true\n# model: \"gemini-3-flash\" # override model (optional)\n# max_tokens_cap: 4096 # max tokens per request\n# timeout: 30 # LLM call timeout (seconds)\n# max_rpm: 10 # max requests per minute\n# allowed_models: [] # model whitelist (empty = all)\n# max_tool_rounds: 5 # tool loop limit (0 = disable)\n# log_level: \"info\" # audit verbosity\n\n# =============================================================================\n# Text-to-Speech\n# =============================================================================\n# TTS defaults to Edge TTS unless changed in ~/.hermes/config.yaml.\n# Gemini TTS supports persona/director prompt files, and Gemini 3.1 Flash TTS\n# can use a hidden auxiliary rewrite pass to insert expressive square-bracket\n# audio tags into the TTS script without showing tags in chat.\n#\n# tts:\n# provider: \"gemini\"\n# gemini:\n# model: \"gemini-3.1-flash-tts-preview\"\n# voice: \"Kore\"\n# audio_tags: false\n# persona_prompt_file: \"\" # e.g. ~/.hermes/tts/radio-host.md\n\n# =============================================================================\n# Voice Transcription (Speech-to-Text)\n# =============================================================================\n# Automatically transcribe voice messages on messaging platforms.\n# Providers: local (free, faster-whisper) | groq (free tier) | openai (Whisper API) | mistral (Voxtral Transcribe)\n# Set the corresponding API key in .env: GROQ_API_KEY, OPENAI_API_KEY, or MISTRAL_API_KEY.\nstt:\n enabled: true\n # provider: \"local\" # auto-detected if omitted\n local:\n model: \"base\" # tiny | base | small | medium | large-v3 | turbo\n # language: \"\" # auto-detect; set to \"en\", \"es\", \"fr\", etc. to force\n openai:\n model: \"whisper-1\" # whisper-1 | gpt-4o-mini-transcribe | gpt-4o-transcribe\n # mistral:\n # model: \"voxtral-mini-latest\" # voxtral-mini-latest | voxtral-mini-2602\n # deepinfra:\n # # Model id is discovered live from the DeepInfra catalog filtered\n # # by the `stt` surface tag — leave `model` blank to take the first\n # # live result. Pin only when you need a specific Whisper variant.\n # model: \"\"\n\n# Text-to-speech. Only the deepinfra block is documented here — the\n# remaining providers (edge, openai, xai, minimax, mistral, gemini,\n# elevenlabs, neutts, kittentts, piper) inherit sensible defaults from\n# DEFAULT_CONFIG in hermes_cli/config.py.\n# tts:\n# provider: \"deepinfra\"\n# deepinfra:\n# # Model id is discovered live from the DeepInfra catalog filtered\n# # by the `tts` surface tag — leave `model` blank to take the first\n# # live result.\n# model: \"\"\n# voice: \"default\"\n\n# Image generation. Each provider plugin reads its own ``image_gen.``\n# block; deepinfra discovers models live from\n# api.deepinfra.com/v1/openai/models filtered by the ``image-gen`` tag —\n# no model id is hardcoded, so retired models disappear automatically.\n# image_gen:\n# provider: \"deepinfra\"\n# deepinfra:\n# # Leave `model` blank for the first live `image-gen`-tagged result.\n# model: \"\"\n\n# =============================================================================\n# Response Pacing (Messaging Platforms)\n# =============================================================================\n# Add human-like delays between message chunks.\n# human_delay:\n# mode: \"off\" # \"off\" | \"natural\" | \"custom\"\n# min_ms: 800 # Min delay (custom mode only)\n# max_ms: 2500 # Max delay (custom mode only)\n\n# =============================================================================\n# Session Logging\n# =============================================================================\n# Session trajectories are automatically saved to logs/ directory.\n# Each session creates: logs/session_YYYYMMDD_HHMMSS_UUID.json\n#\n# The session ID is displayed in the welcome banner for easy reference.\n# Logs contain full conversation history in trajectory format:\n# - System prompt, user messages, assistant responses\n# - Tool calls with inputs/outputs\n# - Timestamps for debugging\n#\n# No configuration needed - logging is always enabled.\n# To disable, you would need to modify the source code.\n\n# =============================================================================\n# Code Execution Sandbox (Programmatic Tool Calling)\n# =============================================================================\n# The execute_code tool runs Python scripts that call Hermes tools via RPC.\n# Intermediate tool results stay out of the LLM's context window.\ncode_execution:\n timeout: 300 # Max seconds per script before kill (default: 300 = 5 min)\n max_tool_calls: 50 # Max RPC tool calls per execution (default: 50)\n\n# =============================================================================\n# Subagent Delegation\n# =============================================================================\n# The delegate_task tool spawns child agents with isolated context.\n# Supports single tasks and batch mode (default 3 parallel, configurable).\ndelegation:\n max_iterations: 50 # Max tool-calling turns per child (default: 50)\n # max_concurrent_children: 3 # Max parallel child agents per batch (default: 3, floor: 1, no ceiling).\n # WARNING: values above 10 multiply API cost linearly.\n # max_spawn_depth: 1 # Delegation tree depth cap (range: 1-3, default: 1 = flat).\n # Raise to 2 to allow workers to spawn their own subagents.\n # Requires role=\"orchestrator\" on intermediate agents.\n # orchestrator_enabled: true # Kill switch for role=\"orchestrator\" children (default: true).\n # subagent_auto_approve: false # When a subagent hits a dangerous-command approval prompt, auto-deny (default: false)\n # or auto-approve \"once\" (true) instead of blocking on stdin.\n # The parent TUI owns stdin, so blocking would deadlock; non-interactive resolution is required.\n # Both choices emit a logger.warning audit line. Flip to true only for cron/batch pipelines.\n # inherit_mcp_toolsets: true # When explicit child toolsets are narrowed, also keep the parent's MCP toolsets (default: true). Set false for strict intersection.\n # model: \"google/gemini-3-flash-preview\" # Override model for subagents (empty = inherit parent)\n # provider: \"openrouter\" # Override provider for subagents (empty = inherit parent)\n # # Resolves full credentials (base_url, api_key) automatically.\n # # Supported: openrouter, nous, zai, kimi-coding, minimax\n\n# =============================================================================\n# Honcho Integration (Cross-Session User Modeling)\n# =============================================================================\n# AI-native persistent memory via Honcho (https://honcho.dev/).\n# Builds a deeper understanding of the user across sessions and tools.\n# Runs alongside USER.md — additive, not a replacement.\n#\n# Requires: pip install honcho-ai\n# Config: ~/.honcho/config.json (shared with Claude Code, Cursor, etc.)\n# API key: HONCHO_API_KEY in ~/.hermes/.env or ~/.honcho/config.json\n#\n# Hermes-specific overrides (optional — most config comes from ~/.honcho/config.json):\n# honcho: {}\n\n# =============================================================================\n# Display\n# =============================================================================\ndisplay:\n # Use compact banner mode (hides the ASCII-art banner, shows a single line).\n # true: Compact single-line banner\n # false: Full ASCII banner with tool/skill summary (default)\n compact: false\n\n # Tool progress display level (CLI and gateway)\n # off: Silent — no tool activity shown, just the final response\n # new: Show a tool indicator only when the tool changes (skip repeats)\n # all: Show every tool call with a short preview (default)\n # verbose: Full args, results, and debug logs (same as /verbose)\n # log: Silent in chat; append every tool call to ~/.hermes/logs/tool_calls.log (gateway only)\n # Toggle at runtime with /verbose in the CLI\n tool_progress: all\n\n # Per-platform defaults can be quieter than the global setting. Telegram\n # tunes for mobile: tool_progress and busy_ack_detail default off (no\n # per-tool breadcrumb stream, no \"iteration 21/60\" debug detail in busy\n # acks or heartbeats), but interim_assistant_messages and\n # long_running_notifications STAY ON so the user has real signal between\n # turn start and final answer (mid-turn assistant commentary + a single\n # edit-in-place \"⏳ Working — N min\" heartbeat). Override under\n # display.platforms.telegram.\n\n # Auto-cleanup of temporary progress bubbles after the final response lands.\n # On platforms that support message deletion (currently Telegram), this\n # removes the tool-progress bubble, \"⏳ Still working...\" notices, and\n # context-pressure status messages once the final reply has been delivered —\n # keeping long-running turns visible live, then tidy afterward. Failed runs\n # leave the bubbles in place as breadcrumbs. Off by default.\n # Per-platform override: display.platforms.telegram.cleanup_progress\n # true: Delete tracked progress/status bubbles on successful turn\n # false: Leave everything in place (default)\n # Example:\n # display:\n # platforms:\n # telegram:\n # cleanup_progress: true\n cleanup_progress: false\n\n # Natural mid-turn assistant updates.\n # On gateway platforms, when true, completed assistant status messages are\n # sent as separate chat messages. On the Desktop app, when true, mid-turn\n # assistant narration streamed between tool calls is kept in the transcript\n # instead of the bubble collapsing to only the final message on completion.\n # Independent of tool_progress and gateway streaming.\n # true: Keep/send mid-turn assistant updates (default)\n # false: Only keep/send the final response\n interim_assistant_messages: true\n\n # Gateway-only long-running status heartbeats.\n # When false, the platform does not receive periodic \"⏳ Working — N min\"\n # notifications even if agent.gateway_notify_interval is non-zero. The\n # heartbeat edits a single message in place (where the adapter supports\n # editing) instead of posting a new bubble each interval.\n # Default: true everywhere, including Telegram (silent agents are worse\n # than a single edit-in-place heartbeat).\n long_running_notifications: true\n\n # Include detailed iteration/tool/status context in busy acknowledgments\n # and long-running heartbeats. When true, busy acks show \"iteration 21/60,\n # terminal, 10 min\" and the heartbeat shows \"⏳ Working — 12 min,\n # iteration 21/60, terminal\". When false (Telegram default), both stay\n # terse: \"Interrupting current task\" and \"⏳ Working — 12 min, terminal\".\n busy_ack_detail: true\n\n # What Enter does when Hermes is already busy (CLI and gateway platforms).\n # interrupt: Interrupt the current run and redirect Hermes (default)\n # queue: Queue your message for the next turn\n # steer: Inject your message mid-run via /steer, arriving at the agent\n # after the next tool call — no interrupt, no role violation.\n # Falls back to 'queue' if the agent isn't running yet or if\n # images are attached (steer only carries text).\n # Ctrl+C (or /stop in gateway) always interrupts regardless of this setting.\n # Toggle at runtime with /busy .\n busy_input_mode: interrupt\n\n # Background process notifications (gateway/messaging only).\n # Controls how chatty the process watcher is when you use\n # terminal(background=true, notify_on_complete=true) from Telegram/Discord/etc.\n # off: No watcher messages at all\n # result: Only the final completion message\n # error: Only the final message when exit code != 0\n # all: Running output updates + final message (default)\n background_process_notifications: all\n\n\n # Play terminal bell when agent finishes a response.\n # Useful for long-running tasks — your terminal will ding when the agent is done.\n # Works over SSH. Most terminals can be configured to flash the taskbar or play a sound.\n # true: Ring the terminal bell on each response\n # false: Silent (default)\n bell_on_complete: false\n\n # Show model reasoning/thinking before each response.\n # When enabled, a dim box shows the model's thought process above the response.\n # Toggle at runtime with /reasoning show or /reasoning hide.\n # true: Show the reasoning box\n # false: Hide reasoning (default)\n show_reasoning: false\n\n # Stream tokens to the terminal as they arrive instead of waiting for the\n # full response. The response box opens on first token and text appears\n # line-by-line. Tool calls are still captured silently.\n # true: Stream tokens as they arrive (default)\n # false: Wait for the full response before rendering\n streaming: true\n\n # Show [HH:MM] timestamps on user input and assistant response labels.\n # timestamps: false\n\n # ───────────────────────────────────────────────────────────────────────────\n # Skin / Theme\n # ───────────────────────────────────────────────────────────────────────────\n # Customize CLI visual appearance — banner colors, spinner faces, tool prefix,\n # response box label, and branding text. Change at runtime with /skin .\n #\n # Built-in skins:\n # default — Classic Hermes gold/kawaii\n # ares — Crimson/bronze war-god theme with spinner wings\n # mono — Clean grayscale monochrome\n # slate — Cool blue developer-focused\n # daylight — Bright light-mode theme\n # warm-lightmode — Warm paper-tone light-mode theme\n # poseidon — Sea-green/teal Olympian theme\n # sisyphus — Earthy stone-and-moss theme\n # charizard — Fiery orange dragon theme\n #\n # Custom skins: drop a YAML file in ~/.hermes/skins/.yaml\n # Schema (all fields optional, missing values inherit from default):\n #\n # name: my-theme\n # description: Short description\n # colors:\n # banner_border: \"#HEX\" # Panel border\n # banner_title: \"#HEX\" # Panel title\n # banner_accent: \"#HEX\" # Section headers (Available Tools, etc.)\n # banner_dim: \"#HEX\" # Dim/muted text\n # banner_text: \"#HEX\" # Body text (tool names, skill names)\n # ui_accent: \"#HEX\" # UI accent color\n # response_border: \"#HEX\" # Response box border color\n # spinner:\n # waiting_faces: [\"(⚔)\", \"(⛨)\"] # Faces shown while waiting\n # thinking_faces: [\"(⚔)\", \"(⌁)\"] # Faces shown while thinking\n # thinking_verbs: [\"forging\", \"plotting\"] # Verbs for spinner messages\n # wings: # Optional left/right spinner decorations\n # - [\"⟪⚔\", \"⚔⟫\"]\n # - [\"⟪▲\", \"▲⟫\"]\n # branding:\n # agent_name: \"My Agent\" # Banner title and branding\n # welcome: \"Welcome message\" # Shown at CLI startup\n # response_label: \" ⚔ Agent \" # Response box header label\n # prompt_symbol: \"⚔\" # Prompt symbol (bare token; renderers add trailing space)\n # tool_prefix: \"╎\" # Tool output line prefix (default: ┊)\n #\n skin: default\n\n# =============================================================================\n# Model Aliases — short names for /model command\n# =============================================================================\n# Map short aliases to exact (model, provider, base_url) tuples.\n# Used by /model tab completion and resolve_alias().\n# Aliases are checked BEFORE the models.dev catalog, so they can route\n# to endpoints not in the catalog (e.g. Ollama Cloud, local servers).\n#\n# model_aliases:\n# opus:\n# model: claude-opus-4-6\n# provider: anthropic\n# qwen:\n# model: \"qwen3.5:397b\"\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n# glm:\n# model: glm-4.7\n# provider: custom\n# base_url: \"https://ollama.com/v1\"\n\n# =============================================================================\n# Privacy\n# =============================================================================\n# privacy:\n# # Redact PII from the LLM context prompt.\n# # When true, phone numbers are stripped and user/chat IDs are replaced\n# # with deterministic hashes before being sent to the model.\n# # Names and usernames are NOT affected (user-chosen, publicly visible).\n# # Routing/delivery still uses the original values internally.\n# redact_pii: false\n\n# =============================================================================\n# Shell-script hooks\n# =============================================================================\n# Register shell scripts as plugin-hook callbacks. Each entry is executed as\n# a subprocess (shell=False, shlex.split) with a JSON payload on stdin. On\n# stdout the script may return JSON that either blocks the tool call or\n# injects context into the next LLM call.\n#\n# Valid events (mirror hermes_cli.plugins.VALID_HOOKS):\n# pre_tool_call, post_tool_call, pre_llm_call, post_llm_call,\n# pre_api_request, post_api_request, on_session_start, on_session_end,\n# on_session_finalize, on_session_reset, subagent_stop\n#\n# First-use consent: each (event, command) pair prompts once on a TTY, then\n# is persisted to ~/.hermes/shell-hooks-allowlist.json. Non-interactive\n# runs (gateway, cron) need --accept-hooks, HERMES_ACCEPT_HOOKS=1, or the\n# hooks_auto_accept key below.\n#\n# See website/docs/user-guide/features/hooks.md for the full JSON wire\n# protocol and worked examples.\n#\n# hooks:\n# pre_tool_call:\n# - matcher: \"terminal\"\n# command: \"~/.hermes/agent-hooks/block-rm-rf.sh\"\n# timeout: 10\n# post_tool_call:\n# - matcher: \"write_file|patch\"\n# command: \"~/.hermes/agent-hooks/auto-format.sh\"\n# pre_llm_call:\n# - command: \"~/.hermes/agent-hooks/inject-cwd-context.sh\"\n# subagent_stop:\n# - command: \"~/.hermes/agent-hooks/log-orchestration.sh\"\n#\n# hooks_auto_accept: false\n\n\n# =============================================================================\n# Update Behavior\n# =============================================================================\nupdates:\n # Create a full HERMES_HOME zip before every `hermes update`.\n # Backups land in ~/.hermes/backups/ and can be restored with `hermes import`.\n # Off by default because large homes can add minutes to every update.\n pre_update_backup: false\n\n # Number of pre-update backup zips to retain.\n backup_keep: 5\n\n # What non-interactive updates do with local source edits in the Hermes repo.\n # Interactive terminal updates always prompt before restoring the autostash.\n #\n # stash - auto-stash before pull, then auto-restore after success (default)\n # discard - drop the update-created stash after success; use only on managed\n # installs where local source edits should not persist\n non_interactive_local_changes: \"stash\"\n\n\n# =============================================================================\n# Web Dashboard\n# =============================================================================\n# OAuth gate configuration for `hermes dashboard --host `.\n# The bundled Nous Portal plugin reads these on startup; settings here are\n# the canonical surface. Each can be overridden by an environment variable:\n#\n# dashboard.oauth.client_id <- HERMES_DASHBOARD_OAUTH_CLIENT_ID\n# dashboard.oauth.portal_url <- HERMES_DASHBOARD_PORTAL_URL\n# dashboard.public_url <- HERMES_DASHBOARD_PUBLIC_URL\n#\n# Env wins when set to a non-empty value. This is what Fly.io's platform-\n# secret injection uses to push per-deploy client_ids without needing to\n# bake a config.yaml into the image. Empty env values are treated as unset\n# so a provisioned-but-not-populated secret can't shadow a valid entry here.\n#\n# Local dev / on-prem deploys should typically set these via config.yaml\n# (the ~/.hermes/.env file is reserved for API keys and secrets).\n#\n# dashboard:\n# oauth:\n# client_id: \"\" # agent:{instance_id}; Portal provisions this at deploy\n# portal_url: \"\" # blank → default https://portal.nousresearch.com\n#\n# # Force the absolute base URL the OAuth callback (and any other public\n# # URL the dashboard hands to external systems) is built from. Set this\n# # for deploys behind reverse proxies that don't reliably forward\n# # X-Forwarded-Host / X-Forwarded-Proto / X-Forwarded-Prefix (manual\n# # nginx setups, on-prem ingresses, custom-domain Fly deploys without\n# # full proxy header chains).\n# #\n# # When set, the value is the complete authority: scheme + host +\n# # optional path prefix (e.g. \"https://example.com/hermes\"). The OAuth\n# # callback URL becomes \"/auth/callback\" — X-Forwarded-Prefix\n# # is IGNORED on this code path because the operator has explicitly\n# # declared the public URL and we no longer need to guess.\n# #\n# # Leave empty to use the existing proxy-header reconstruction (the\n# # default — works on Fly.io out of the box).\n# #\n# # public_url: \"https://example.com/hermes\"\n#\n# -----------------------------------------------------------------------------\n# Self-hosted OIDC dashboard auth (generic OpenID Connect — Authentik,\n# Keycloak, Zitadel, Authelia, Auth0, Okta, Google, …). Use this INSTEAD of the\n# nous block above when gating the dashboard with your own identity provider.\n# Each setting can be overridden by an environment variable:\n#\n# dashboard.oauth.self_hosted.issuer <- HERMES_DASHBOARD_OIDC_ISSUER\n# dashboard.oauth.self_hosted.client_id <- HERMES_DASHBOARD_OIDC_CLIENT_ID\n# dashboard.oauth.self_hosted.scopes <- HERMES_DASHBOARD_OIDC_SCOPES\n# dashboard.oauth.self_hosted.client_secret <- HERMES_DASHBOARD_OIDC_CLIENT_SECRET\n#\n# dashboard:\n# oauth:\n# provider: self-hosted\n# self_hosted:\n# issuer: \"https://auth.example.com/application/o/hermes/\" # required\n# client_id: \"hermes-dashboard\" # required\n# scopes: \"openid profile email\" # optional\n#\n# # OPTIONAL — set ONLY if your IDP registered the client as\n# # *confidential* (Authentik / Keycloak often default to this). When\n# # set, Hermes authenticates the client at the token endpoint\n# # (client_secret_basic or client_secret_post, auto-selected from the\n# # IDP's discovery doc) IN ADDITION to PKCE. Leave unset for a public\n# # (PKCE-only) client — the common case.\n# #\n# # This is a CREDENTIAL: prefer setting HERMES_DASHBOARD_OIDC_CLIENT_SECRET\n# # in ~/.hermes/.env over putting it here in config.yaml.\n# # client_secret: \"\"\n\n# =============================================================================\n# External secret sources\n# =============================================================================\n# Pull provider credentials from external secret managers at process startup\n# instead of storing them in ~/.hermes/.env. Only the manager's bootstrap\n# credential (e.g. BWS_ACCESS_TOKEN / OP_SERVICE_ACCOUNT_TOKEN) lives in .env\n# (or your shell / desktop session); everything else rotates centrally.\n# Failures never block startup — Hermes warns once and continues with\n# whatever .env already had.\n#\n# Multiple sources can be enabled at once:\n# - \"mapped\" sources (explicit VAR -> ref bindings, e.g. 1Password's env:\n# map) beat \"bulk\" sources (whole-project dumps like Bitwarden BSM)\n# - within a shape, the first source to claim a var wins; later claims\n# are skipped with a startup warning (never a silent clobber)\n# - a source's override_existing lets it beat .env/shell values, but\n# never another secret source's claim\n# Docs: https://hermes-agent.nousresearch.com/docs/user-guide/secrets/\n#\n# secrets:\n# # Optional explicit ordering of enabled sources.\n# # sources: [onepassword, bitwarden]\n#\n# # ---- Bitwarden Secrets Manager (bws CLI) --------------------------------\n# bitwarden:\n# enabled: false\n# access_token_env: BWS_ACCESS_TOKEN # bootstrap token, sourced from .env\n# project_id: \"\" # UUID of the BSM project to sync\n# server_url: \"\" # \"\" = US Cloud; EU/self-hosted URL otherwise\n# cache_ttl_seconds: 300 # 0 disables caching\n# override_existing: true # BSM values win over existing env\n# auto_install: true # lazy-download bws into ~/.hermes/bin\n#\n# # ---- 1Password (op CLI) -------------------------------------------------\n# onepassword:\n# enabled: false\n# # Map env-var names to op:// secret references. Each is resolved with a\n# # single `op read` at startup.\n# env:\n# OPENAI_API_KEY: \"op://Private/OpenAI/api key\"\n# ANTHROPIC_API_KEY: \"op://Private/Anthropic/credential\"\n# account: \"\" # op --account shorthand; \"\" = default\n# service_account_token_env: OP_SERVICE_ACCOUNT_TOKEN # headless auth; unset = desktop session\n# binary_path: \"\" # \"\" = resolve op via PATH; else absolute path\n# cache_ttl_seconds: 300 # 0 disables BOTH cache layers\n# override_existing: true # resolved values win over existing env\n"}}, "tests/gateway/test_model_command_flat_string_config.py": {"092257e26f459294b7beaff689c0e68b8d0d1812": {"t": 1785746884.5163212, "n": 7309, "h": "56012457446ca35c", "c": "\"\"\"Regression tests for gateway /model --global persistence when config.yaml\nhas a flat-string ``model:`` value instead of a nested dict.\n\nBefore fix: ``cfg.setdefault(\"model\", {})`` returned the existing string and\nthe next assignment raised ``TypeError: 'str' object does not support item\nassignment``, so every ``/model X --global`` from Telegram/Discord crashed\nsilently and the user-visible result was \"switch failed\" with no persist.\n\nAfter fix: the persist block coerces a scalar ``model:`` into a nested dict\nbefore mutation, so ``--global`` succeeds and the config is rewritten in\nthe proper ``model: {default: ..., provider: ...}`` form.\n\"\"\"\n\nimport yaml\nimport pytest\n\nfrom gateway.config import Platform\nfrom gateway.platforms.base import MessageEvent, MessageType\nfrom gateway.run import GatewayRunner\nfrom gateway.session import SessionSource\n\n\ndef _make_runner():\n runner = object.__new__(GatewayRunner)\n runner.adapters = {}\n runner._voice_mode = {}\n runner._session_model_overrides = {}\n runner._running_agents = {}\n return runner\n\n\ndef _make_event(text):\n return MessageEvent(\n text=text,\n message_type=MessageType.TEXT,\n source=SessionSource(platform=Platform.TELEGRAM, chat_id=\"12345\", chat_type=\"dm\"),\n )\n\n\ndef _fake_switch_result():\n \"\"\"Build a successful ModelSwitchResult that bypasses real provider resolution.\"\"\"\n from hermes_cli.model_switch import ModelSwitchResult\n\n return ModelSwitchResult(\n success=True,\n new_model=\"gpt-5.5\",\n target_provider=\"openrouter\",\n provider_changed=True,\n api_key=\"sk-test\",\n base_url=\"https://openrouter.ai/api/v1\",\n api_mode=\"chat_completions\",\n provider_label=\"OpenRouter\",\n is_global=True,\n )\n\n\ndef _setup_isolated_home(tmp_path, monkeypatch, model_yaml_value):\n \"\"\"Write a config.yaml with the given ``model:`` value and stub the heavy bits.\"\"\"\n import gateway.run as gateway_run\n\n hermes_home = tmp_path / \".hermes\"\n hermes_home.mkdir()\n cfg_path = hermes_home / \"config.yaml\"\n cfg_path.write_text(\n yaml.safe_dump({\"model\": model_yaml_value, \"providers\": {}}),\n encoding=\"utf-8\",\n )\n\n monkeypatch.setattr(gateway_run, \"_hermes_home\", hermes_home)\n monkeypatch.setattr(\"agent.models_dev.fetch_models_dev\", lambda: {})\n monkeypatch.setattr(\n \"hermes_cli.model_switch.switch_model\",\n lambda **kw: _fake_switch_result(),\n )\n # save_config writes to ``get_hermes_home() / config.yaml`` — point it here.\n monkeypatch.setattr(\"hermes_constants.get_hermes_home\", lambda: hermes_home)\n monkeypatch.setattr(\"hermes_cli.config.get_hermes_home\", lambda: hermes_home)\n return cfg_path\n\n\n@pytest.mark.asyncio\nasync def test_model_global_persists_when_config_has_flat_string_model(tmp_path, monkeypatch):\n \"\"\"Regression: ``model: deepseek-v4-flash`` (flat string) used to crash\n the gateway ``/model X --global`` persist branch with TypeError. After\n the fix, the flat string is coerced to ``{\"default\": ...}`` and the new\n model+provider are persisted on top.\n \"\"\"\n cfg_path = _setup_isolated_home(tmp_path, monkeypatch, \"deepseek-v4-flash\")\n\n result = await _make_runner()._handle_model_command(\n _make_event(\"/model gpt-5.5 --global\")\n )\n\n # Sanity: the handler returned a success-looking message (not a crash log).\n assert result is not None\n assert \"gpt-5.5\" in result\n\n # The persist block must have rewritten config.yaml as a nested dict.\n written = yaml.safe_load(cfg_path.read_text(encoding=\"utf-8\"))\n assert isinstance(written[\"model\"], dict), (\n \"model: should be coerced to a dict, got %r\" % (written[\"model\"],)\n )\n assert written[\"model\"][\"default\"] == \"gpt-5.5\"\n assert written[\"model\"][\"provider\"] == \"openrouter\"\n assert \"base_url\" not in written[\"model\"]\n\n\n@pytest.mark.asyncio\nasync def test_model_global_persists_when_config_has_missing_model(tmp_path, monkeypatch):\n \"\"\"Companion case: ``model:`` key absent entirely. setdefault would have\n worked here, but the coercion branch also has to handle this cleanly.\n \"\"\"\n import gateway.run as gateway_run\n\n hermes_home = tmp_path / \".hermes\"\n hermes_home.mkdir()\n cfg_path = hermes_home / \"config.yaml\"\n cfg_path.write_text(yaml.safe_dump({\"providers\": {}}), encoding=\"utf-8\")\n\n monkeypatch.setattr(gateway_run, \"_hermes_home\", hermes_home)\n monkeypatch.setattr(\"agent.models_dev.fetch_models_dev\", lambda: {})\n monkeypatch.setattr(\n \"hermes_cli.model_switch.switch_model\",\n lambda **kw: _fake_switch_result(),\n )\n monkeypatch.setattr(\"hermes_constants.get_hermes_home\", lambda: hermes_home)\n monkeypatch.setattr(\"hermes_cli.config.get_hermes_home\", lambda: hermes_home)\n\n result = await _make_runner()._handle_model_command(\n _make_event(\"/model gpt-5.5 --global\")\n )\n\n assert result is not None\n written = yaml.safe_load(cfg_path.read_text(encoding=\"utf-8\"))\n assert isinstance(written[\"model\"], dict)\n assert written[\"model\"][\"default\"] == \"gpt-5.5\"\n assert written[\"model\"][\"provider\"] == \"openrouter\"\n\n\n@pytest.mark.asyncio\nasync def test_model_global_persists_when_config_has_proper_dict_model(tmp_path, monkeypatch):\n \"\"\"Already-correct nested dict must still work — no regression on the\n common case.\n \"\"\"\n cfg_path = _setup_isolated_home(\n tmp_path,\n monkeypatch,\n {\"default\": \"old-model\", \"provider\": \"openai-codex\"},\n )\n\n result = await _make_runner()._handle_model_command(\n _make_event(\"/model gpt-5.5 --global\")\n )\n\n assert result is not None\n written = yaml.safe_load(cfg_path.read_text(encoding=\"utf-8\"))\n assert written[\"model\"][\"default\"] == \"gpt-5.5\"\n assert written[\"model\"][\"provider\"] == \"openrouter\"\n\n\n@pytest.mark.asyncio\nasync def test_model_no_flag_is_session_scoped_by_default(tmp_path, monkeypatch):\n \"\"\"A plain ``/model X`` (no --global) does NOT persist to config.yaml.\n\n This is the user-facing fix: switches are session-scoped unless the user\n opts in with ``--global`` or sets ``model.persist_switch_by_default: true``.\n \"\"\"\n cfg_path = _setup_isolated_home(\n tmp_path,\n monkeypatch,\n {\"default\": \"old-model\", \"provider\": \"openai-codex\"},\n )\n\n result = await _make_runner()._handle_model_command(\n _make_event(\"/model gpt-5.5\")\n )\n\n assert result is not None\n assert \"gpt-5.5\" in result\n written = yaml.safe_load(cfg_path.read_text(encoding=\"utf-8\"))\n assert written[\"model\"][\"default\"] == \"old-model\"\n\n\n@pytest.mark.asyncio\nasync def test_model_session_flag_does_not_persist(tmp_path, monkeypatch):\n \"\"\"``/model X --session`` opts out of persistence even under the new default.\"\"\"\n cfg_path = _setup_isolated_home(\n tmp_path,\n monkeypatch,\n {\"default\": \"old-model\", \"provider\": \"openai-codex\"},\n )\n\n result = await _make_runner()._handle_model_command(\n _make_event(\"/model gpt-5.5 --session\")\n )\n\n assert result is not None\n assert \"gpt-5.5\" in result\n written = yaml.safe_load(cfg_path.read_text(encoding=\"utf-8\"))\n # Config untouched — the session override is in-memory only.\n assert written[\"model\"][\"default\"] == \"old-model\"\n"}, "8b6fde3a35e565f687bcb5c4cd6f91d55871eae4": {"t": 1785767853.9325998, "n": 7309, "h": "56012457446ca35c", "c": "\"\"\"Regression tests for gateway /model --global persistence when config.yaml\nhas a flat-string ``model:`` value instead of a nested dict.\n\nBefore fix: ``cfg.setdefault(\"model\", {})`` returned the existing string and\nthe next assignment raised ``TypeError: 'str' object does not support item\nassignment``, so every ``/model X --global`` from Telegram/Discord crashed\nsilently and the user-visible result was \"switch failed\" with no persist.\n\nAfter fix: the persist block coerces a scalar ``model:`` into a nested dict\nbefore mutation, so ``--global`` succeeds and the config is rewritten in\nthe proper ``model: {default: ..., provider: ...}`` form.\n\"\"\"\n\nimport yaml\nimport pytest\n\nfrom gateway.config import Platform\nfrom gateway.platforms.base import MessageEvent, MessageType\nfrom gateway.run import GatewayRunner\nfrom gateway.session import SessionSource\n\n\ndef _make_runner():\n runner = object.__new__(GatewayRunner)\n runner.adapters = {}\n runner._voice_mode = {}\n runner._session_model_overrides = {}\n runner._running_agents = {}\n return runner\n\n\ndef _make_event(text):\n return MessageEvent(\n text=text,\n message_type=MessageType.TEXT,\n source=SessionSource(platform=Platform.TELEGRAM, chat_id=\"12345\", chat_type=\"dm\"),\n )\n\n\ndef _fake_switch_result():\n \"\"\"Build a successful ModelSwitchResult that bypasses real provider resolution.\"\"\"\n from hermes_cli.model_switch import ModelSwitchResult\n\n return ModelSwitchResult(\n success=True,\n new_model=\"gpt-5.5\",\n target_provider=\"openrouter\",\n provider_changed=True,\n api_key=\"sk-test\",\n base_url=\"https://openrouter.ai/api/v1\",\n api_mode=\"chat_completions\",\n provider_label=\"OpenRouter\",\n is_global=True,\n )\n\n\ndef _setup_isolated_home(tmp_path, monkeypatch, model_yaml_value):\n \"\"\"Write a config.yaml with the given ``model:`` value and stub the heavy bits.\"\"\"\n import gateway.run as gateway_run\n\n hermes_home = tmp_path / \".hermes\"\n hermes_home.mkdir()\n cfg_path = hermes_home / \"config.yaml\"\n cfg_path.write_text(\n yaml.safe_dump({\"model\": model_yaml_value, \"providers\": {}}),\n encoding=\"utf-8\",\n )\n\n monkeypatch.setattr(gateway_run, \"_hermes_home\", hermes_home)\n monkeypatch.setattr(\"agent.models_dev.fetch_models_dev\", lambda: {})\n monkeypatch.setattr(\n \"hermes_cli.model_switch.switch_model\",\n lambda **kw: _fake_switch_result(),\n )\n # save_config writes to ``get_hermes_home() / config.yaml`` — point it here.\n monkeypatch.setattr(\"hermes_constants.get_hermes_home\", lambda: hermes_home)\n monkeypatch.setattr(\"hermes_cli.config.get_hermes_home\", lambda: hermes_home)\n return cfg_path\n\n\n@pytest.mark.asyncio\nasync def test_model_global_persists_when_config_has_flat_string_model(tmp_path, monkeypatch):\n \"\"\"Regression: ``model: deepseek-v4-flash`` (flat string) used to crash\n the gateway ``/model X --global`` persist branch with TypeError. After\n the fix, the flat string is coerced to ``{\"default\": ...}`` and the new\n model+provider are persisted on top.\n \"\"\"\n cfg_path = _setup_isolated_home(tmp_path, monkeypatch, \"deepseek-v4-flash\")\n\n result = await _make_runner()._handle_model_command(\n _make_event(\"/model gpt-5.5 --global\")\n )\n\n # Sanity: the handler returned a success-looking message (not a crash log).\n assert result is not None\n assert \"gpt-5.5\" in result\n\n # The persist block must have rewritten config.yaml as a nested dict.\n written = yaml.safe_load(cfg_path.read_text(encoding=\"utf-8\"))\n assert isinstance(written[\"model\"], dict), (\n \"model: should be coerced to a dict, got %r\" % (written[\"model\"],)\n )\n assert written[\"model\"][\"default\"] == \"gpt-5.5\"\n assert written[\"model\"][\"provider\"] == \"openrouter\"\n assert \"base_url\" not in written[\"model\"]\n\n\n@pytest.mark.asyncio\nasync def test_model_global_persists_when_config_has_missing_model(tmp_path, monkeypatch):\n \"\"\"Companion case: ``model:`` key absent entirely. setdefault would have\n worked here, but the coercion branch also has to handle this cleanly.\n \"\"\"\n import gateway.run as gateway_run\n\n hermes_home = tmp_path / \".hermes\"\n hermes_home.mkdir()\n cfg_path = hermes_home / \"config.yaml\"\n cfg_path.write_text(yaml.safe_dump({\"providers\": {}}), encoding=\"utf-8\")\n\n monkeypatch.setattr(gateway_run, \"_hermes_home\", hermes_home)\n monkeypatch.setattr(\"agent.models_dev.fetch_models_dev\", lambda: {})\n monkeypatch.setattr(\n \"hermes_cli.model_switch.switch_model\",\n lambda **kw: _fake_switch_result(),\n )\n monkeypatch.setattr(\"hermes_constants.get_hermes_home\", lambda: hermes_home)\n monkeypatch.setattr(\"hermes_cli.config.get_hermes_home\", lambda: hermes_home)\n\n result = await _make_runner()._handle_model_command(\n _make_event(\"/model gpt-5.5 --global\")\n )\n\n assert result is not None\n written = yaml.safe_load(cfg_path.read_text(encoding=\"utf-8\"))\n assert isinstance(written[\"model\"], dict)\n assert written[\"model\"][\"default\"] == \"gpt-5.5\"\n assert written[\"model\"][\"provider\"] == \"openrouter\"\n\n\n@pytest.mark.asyncio\nasync def test_model_global_persists_when_config_has_proper_dict_model(tmp_path, monkeypatch):\n \"\"\"Already-correct nested dict must still work — no regression on the\n common case.\n \"\"\"\n cfg_path = _setup_isolated_home(\n tmp_path,\n monkeypatch,\n {\"default\": \"old-model\", \"provider\": \"openai-codex\"},\n )\n\n result = await _make_runner()._handle_model_command(\n _make_event(\"/model gpt-5.5 --global\")\n )\n\n assert result is not None\n written = yaml.safe_load(cfg_path.read_text(encoding=\"utf-8\"))\n assert written[\"model\"][\"default\"] == \"gpt-5.5\"\n assert written[\"model\"][\"provider\"] == \"openrouter\"\n\n\n@pytest.mark.asyncio\nasync def test_model_no_flag_is_session_scoped_by_default(tmp_path, monkeypatch):\n \"\"\"A plain ``/model X`` (no --global) does NOT persist to config.yaml.\n\n This is the user-facing fix: switches are session-scoped unless the user\n opts in with ``--global`` or sets ``model.persist_switch_by_default: true``.\n \"\"\"\n cfg_path = _setup_isolated_home(\n tmp_path,\n monkeypatch,\n {\"default\": \"old-model\", \"provider\": \"openai-codex\"},\n )\n\n result = await _make_runner()._handle_model_command(\n _make_event(\"/model gpt-5.5\")\n )\n\n assert result is not None\n assert \"gpt-5.5\" in result\n written = yaml.safe_load(cfg_path.read_text(encoding=\"utf-8\"))\n assert written[\"model\"][\"default\"] == \"old-model\"\n\n\n@pytest.mark.asyncio\nasync def test_model_session_flag_does_not_persist(tmp_path, monkeypatch):\n \"\"\"``/model X --session`` opts out of persistence even under the new default.\"\"\"\n cfg_path = _setup_isolated_home(\n tmp_path,\n monkeypatch,\n {\"default\": \"old-model\", \"provider\": \"openai-codex\"},\n )\n\n result = await _make_runner()._handle_model_command(\n _make_event(\"/model gpt-5.5 --session\")\n )\n\n assert result is not None\n assert \"gpt-5.5\" in result\n written = yaml.safe_load(cfg_path.read_text(encoding=\"utf-8\"))\n # Config untouched — the session override is in-memory only.\n assert written[\"model\"][\"default\"] == \"old-model\"\n"}, "f6deabca0d8010d485d4d3a8744d0ceb797eacb9": {"t": 1785767855.2386763, "n": 7269, "h": "dfd218cc090280e2", "c": "\"\"\"Regression tests for gateway /model --global persistence when config.yaml\nhas a flat-string ``model:`` value instead of a nested dict.\n\nBefore fix: ``cfg.setdefault(\"model\", {})`` returned the existing string and\nthe next assignment raised ``TypeError: 'str' object does not support item\nassignment``, so every ``/model X --global`` from Telegram/Discord crashed\nsilently and the user-visible result was \"switch failed\" with no persist.\n\nAfter fix: the persist block coerces a scalar ``model:`` into a nested dict\nbefore mutation, so ``--global`` succeeds and the config is rewritten in\nthe proper ``model: {default: ..., provider: ...}`` form.\n\"\"\"\n\nimport yaml\nimport pytest\n\nfrom gateway.config import Platform\nfrom gateway.platforms.base import MessageEvent, MessageType\nfrom gateway.run import GatewayRunner\nfrom gateway.session import SessionSource\n\n\ndef _make_runner():\n runner = object.__new__(GatewayRunner)\n runner.adapters = {}\n runner._voice_mode = {}\n runner._session_model_overrides = {}\n runner._running_agents = {}\n return runner\n\n\ndef _make_event(text):\n return MessageEvent(\n text=text,\n message_type=MessageType.TEXT,\n source=SessionSource(platform=Platform.TELEGRAM, chat_id=\"12345\", chat_type=\"dm\"),\n )\n\n\ndef _fake_switch_result():\n \"\"\"Build a successful ModelSwitchResult that bypasses real provider resolution.\"\"\"\n from hermes_cli.model_switch import ModelSwitchResult\n\n return ModelSwitchResult(\n success=True,\n new_model=\"gpt-5.5\",\n target_provider=\"openrouter\",\n provider_changed=True,\n api_key=\"sk-test\",\n base_url=\"https://openrouter.ai/api/v1\",\n api_mode=\"chat_completions\",\n provider_label=\"OpenRouter\",\n is_global=True,\n )\n\n\ndef _setup_isolated_home(tmp_path, monkeypatch, model_yaml_value):\n \"\"\"Write a config.yaml with the given ``model:`` value and stub the heavy bits.\"\"\"\n import gateway.run as gateway_run\n\n hermes_home = tmp_path / \".hermes\"\n hermes_home.mkdir()\n cfg_path = hermes_home / \"config.yaml\"\n cfg_path.write_text(\n yaml.safe_dump({\"model\": model_yaml_value, \"providers\": {}}),\n encoding=\"utf-8\",\n )\n\n monkeypatch.setattr(gateway_run, \"_hermes_home\", hermes_home)\n monkeypatch.setattr(\"agent.models_dev.fetch_models_dev\", lambda: {})\n monkeypatch.setattr(\n \"hermes_cli.model_switch.switch_model\",\n lambda **kw: _fake_switch_result(),\n )\n # save_config writes to ``get_hermes_home() / config.yaml`` — point it here.\n monkeypatch.setattr(\"hermes_constants.get_hermes_home\", lambda: hermes_home)\n monkeypatch.setattr(\"hermes_cli.config.get_hermes_home\", lambda: hermes_home)\n return cfg_path\n\n\n@pytest.mark.asyncio\nasync def test_model_global_persists_when_config_has_flat_string_model(tmp_path, monkeypatch):\n \"\"\"Regression: ``model: deepseek-v4-flash`` (flat string) used to crash\n the gateway ``/model X --global`` persist branch with TypeError. After\n the fix, the flat string is coerced to ``{\"default\": ...}`` and the new\n model+provider are persisted on top.\n \"\"\"\n cfg_path = _setup_isolated_home(tmp_path, monkeypatch, \"deepseek-v4-flash\")\n\n result = await _make_runner()._handle_model_command(\n _make_event(\"/model gpt-5.5 --global\")\n )\n\n # Sanity: the handler returned a success-looking message (not a crash log).\n assert result is not None\n assert \"gpt-5.5\" in result\n\n # The persist block must have rewritten config.yaml as a nested dict.\n written = yaml.safe_load(cfg_path.read_text(encoding=\"utf-8\"))\n assert isinstance(written[\"model\"], dict), (\n \"model: should be coerced to a dict, got %r\" % (written[\"model\"],)\n )\n assert written[\"model\"][\"default\"] == \"gpt-5.5\"\n assert written[\"model\"][\"provider\"] == \"openrouter\"\n assert \"base_url\" not in written[\"model\"]\n\n\n@pytest.mark.asyncio\nasync def test_model_global_persists_when_config_has_missing_model(tmp_path, monkeypatch):\n \"\"\"Companion case: ``model:`` key absent entirely. setdefault would have\n worked here, but the coercion branch also has to handle this cleanly.\n \"\"\"\n import gateway.run as gateway_run\n\n hermes_home = tmp_path / \".hermes\"\n hermes_home.mkdir()\n cfg_path = hermes_home / \"config.yaml\"\n cfg_path.write_text(yaml.safe_dump({\"providers\": {}}), encoding=\"utf-8\")\n\n monkeypatch.setattr(gateway_run, \"_hermes_home\", hermes_home)\n monkeypatch.setattr(\"agent.models_dev.fetch_models_dev\", lambda: {})\n monkeypatch.setattr(\n \"hermes_cli.model_switch.switch_model\",\n lambda **kw: _fake_switch_result(),\n )\n monkeypatch.setattr(\"hermes_constants.get_hermes_home\", lambda: hermes_home)\n monkeypatch.setattr(\"hermes_cli.config.get_hermes_home\", lambda: hermes_home)\n\n result = await _make_runner()._handle_model_command(\n _make_event(\"/model gpt-5.5 --global\")\n )\n\n assert result is not None\n written = yaml.safe_load(cfg_path.read_text(encoding=\"utf-8\"))\n assert isinstance(written[\"model\"], dict)\n assert written[\"model\"][\"default\"] == \"gpt-5.5\"\n assert written[\"model\"][\"provider\"] == \"openrouter\"\n\n\n@pytest.mark.asyncio\nasync def test_model_global_persists_when_config_has_proper_dict_model(tmp_path, monkeypatch):\n \"\"\"Already-correct nested dict must still work — no regression on the\n common case.\n \"\"\"\n cfg_path = _setup_isolated_home(\n tmp_path,\n monkeypatch,\n {\"default\": \"old-model\", \"provider\": \"openai-codex\"},\n )\n\n result = await _make_runner()._handle_model_command(\n _make_event(\"/model gpt-5.5 --global\")\n )\n\n assert result is not None\n written = yaml.safe_load(cfg_path.read_text(encoding=\"utf-8\"))\n assert written[\"model\"][\"default\"] == \"gpt-5.5\"\n assert written[\"model\"][\"provider\"] == \"openrouter\"\n\n\n@pytest.mark.asyncio\nasync def test_model_no_flag_persists_by_default(tmp_path, monkeypatch):\n \"\"\"A plain ``/model X`` (no --global) now persists to config.yaml.\n\n This is the user-facing fix: switching models in one session survives\n into the next without re-typing the switch every time.\n \"\"\"\n cfg_path = _setup_isolated_home(\n tmp_path,\n monkeypatch,\n {\"default\": \"old-model\", \"provider\": \"openai-codex\"},\n )\n\n result = await _make_runner()._handle_model_command(\n _make_event(\"/model gpt-5.5\")\n )\n\n assert result is not None\n assert \"gpt-5.5\" in result\n written = yaml.safe_load(cfg_path.read_text(encoding=\"utf-8\"))\n assert written[\"model\"][\"default\"] == \"gpt-5.5\"\n\n\n@pytest.mark.asyncio\nasync def test_model_session_flag_does_not_persist(tmp_path, monkeypatch):\n \"\"\"``/model X --session`` opts out of persistence even under the new default.\"\"\"\n cfg_path = _setup_isolated_home(\n tmp_path,\n monkeypatch,\n {\"default\": \"old-model\", \"provider\": \"openai-codex\"},\n )\n\n result = await _make_runner()._handle_model_command(\n _make_event(\"/model gpt-5.5 --session\")\n )\n\n assert result is not None\n assert \"gpt-5.5\" in result\n written = yaml.safe_load(cfg_path.read_text(encoding=\"utf-8\"))\n # Config untouched — the session override is in-memory only.\n assert written[\"model\"][\"default\"] == \"old-model\"\n"}, "fad4b40d9d38573641c7f5de29fa4fc6f66e6d16": {"t": 1785767856.429769, "n": 7297, "h": "4aa01988f6ef76ba", "c": "\"\"\"Regression tests for gateway /model --global persistence when config.yaml\nhas a flat-string ``model:`` value instead of a nested dict.\n\nBefore fix: ``cfg.setdefault(\"model\", {})`` returned the existing string and\nthe next assignment raised ``TypeError: 'str' object does not support item\nassignment``, so every ``/model X --global`` from Telegram/Discord crashed\nsilently and the user-visible result was \"switch failed\" with no persist.\n\nAfter fix: the persist block coerces a scalar ``model:`` into a nested dict\nbefore mutation, so ``--global`` succeeds and the config is rewritten in\nthe proper ``model: {default: ..., provider: ...}`` form.\n\"\"\"\n\nimport yaml\nimport pytest\n\nfrom gateway.config import Platform\nfrom gateway.platforms.base import MessageEvent, MessageType\nfrom gateway.run import GatewayRunner\nfrom gateway.session import SessionSource\n\n\ndef _make_runner():\n runner = object.__new__(GatewayRunner)\n runner.adapters = {}\n runner._voice_mode = {}\n runner._session_model_overrides = {}\n runner._running_agents = {}\n return runner\n\n\ndef _make_event(text):\n return MessageEvent(\n text=text,\n message_type=MessageType.TEXT,\n source=SessionSource(platform=Platform.TELEGRAM, chat_id=\"12345\", chat_type=\"dm\"),\n )\n\n\ndef _fake_switch_result():\n \"\"\"Build a successful ModelSwitchResult that bypasses real provider resolution.\"\"\"\n from hermes_cli.model_switch import ModelSwitchResult\n\n return ModelSwitchResult(\n success=True,\n new_model=\"gpt-5.5\",\n target_provider=\"openrouter\",\n provider_changed=True,\n api_key=\"sk-test\",\n base_url=\"https://openrouter.ai/api/v1\",\n api_mode=\"chat_completions\",\n provider_label=\"OpenRouter\",\n is_global=True,\n )\n\n\ndef _setup_isolated_home(tmp_path, monkeypatch, model_yaml_value):\n \"\"\"Write a config.yaml with the given ``model:`` value and stub the heavy bits.\"\"\"\n import gateway.run as gateway_run\n\n hermes_home = tmp_path / \".hermes\"\n hermes_home.mkdir()\n cfg_path = hermes_home / \"config.yaml\"\n cfg_path.write_text(\n yaml.safe_dump({\"model\": model_yaml_value, \"providers\": {}}),\n encoding=\"utf-8\",\n )\n\n monkeypatch.setattr(gateway_run, \"_hermes_home\", hermes_home)\n monkeypatch.setattr(\"agent.models_dev.fetch_models_dev\", lambda: {})\n monkeypatch.setattr(\n \"hermes_cli.model_switch.switch_model\",\n lambda **kw: _fake_switch_result(),\n )\n # save_config writes to ``get_hermes_home() / config.yaml`` — point it here.\n monkeypatch.setattr(\"hermes_constants.get_hermes_home\", lambda: hermes_home)\n monkeypatch.setattr(\"hermes_cli.config.get_hermes_home\", lambda: hermes_home)\n return cfg_path\n\n\n@pytest.mark.asyncio\nasync def test_model_global_persists_when_config_has_flat_string_model(tmp_path, monkeypatch):\n \"\"\"Regression: ``model: deepseek-v4-flash`` (flat string) used to crash\n the gateway ``/model X --global`` persist branch with TypeError. After\n the fix, the flat string is coerced to ``{\"default\": ...}`` and the new\n model+provider are persisted on top.\n \"\"\"\n cfg_path = _setup_isolated_home(tmp_path, monkeypatch, \"deepseek-v4-flash\")\n\n result = await _make_runner()._handle_model_command(\n _make_event(\"/model gpt-5.5 --global\")\n )\n\n # Sanity: the handler returned a success-looking message (not a crash log).\n assert result is not None\n assert \"gpt-5.5\" in result\n\n # The persist block must have rewritten config.yaml as a nested dict.\n written = yaml.safe_load(cfg_path.read_text(encoding=\"utf-8\"))\n assert isinstance(written[\"model\"], dict), (\n \"model: should be coerced to a dict, got %r\" % (written[\"model\"],)\n )\n assert written[\"model\"][\"default\"] == \"gpt-5.5\"\n assert written[\"model\"][\"provider\"] == \"openrouter\"\n assert written[\"model\"][\"base_url\"] == \"https://openrouter.ai/api/v1\"\n\n\n@pytest.mark.asyncio\nasync def test_model_global_persists_when_config_has_missing_model(tmp_path, monkeypatch):\n \"\"\"Companion case: ``model:`` key absent entirely. setdefault would have\n worked here, but the coercion branch also has to handle this cleanly.\n \"\"\"\n import gateway.run as gateway_run\n\n hermes_home = tmp_path / \".hermes\"\n hermes_home.mkdir()\n cfg_path = hermes_home / \"config.yaml\"\n cfg_path.write_text(yaml.safe_dump({\"providers\": {}}), encoding=\"utf-8\")\n\n monkeypatch.setattr(gateway_run, \"_hermes_home\", hermes_home)\n monkeypatch.setattr(\"agent.models_dev.fetch_models_dev\", lambda: {})\n monkeypatch.setattr(\n \"hermes_cli.model_switch.switch_model\",\n lambda **kw: _fake_switch_result(),\n )\n monkeypatch.setattr(\"hermes_constants.get_hermes_home\", lambda: hermes_home)\n monkeypatch.setattr(\"hermes_cli.config.get_hermes_home\", lambda: hermes_home)\n\n result = await _make_runner()._handle_model_command(\n _make_event(\"/model gpt-5.5 --global\")\n )\n\n assert result is not None\n written = yaml.safe_load(cfg_path.read_text(encoding=\"utf-8\"))\n assert isinstance(written[\"model\"], dict)\n assert written[\"model\"][\"default\"] == \"gpt-5.5\"\n assert written[\"model\"][\"provider\"] == \"openrouter\"\n\n\n@pytest.mark.asyncio\nasync def test_model_global_persists_when_config_has_proper_dict_model(tmp_path, monkeypatch):\n \"\"\"Already-correct nested dict must still work — no regression on the\n common case.\n \"\"\"\n cfg_path = _setup_isolated_home(\n tmp_path,\n monkeypatch,\n {\"default\": \"old-model\", \"provider\": \"openai-codex\"},\n )\n\n result = await _make_runner()._handle_model_command(\n _make_event(\"/model gpt-5.5 --global\")\n )\n\n assert result is not None\n written = yaml.safe_load(cfg_path.read_text(encoding=\"utf-8\"))\n assert written[\"model\"][\"default\"] == \"gpt-5.5\"\n assert written[\"model\"][\"provider\"] == \"openrouter\"\n\n\n@pytest.mark.asyncio\nasync def test_model_no_flag_persists_by_default(tmp_path, monkeypatch):\n \"\"\"A plain ``/model X`` (no --global) now persists to config.yaml.\n\n This is the user-facing fix: switching models in one session survives\n into the next without re-typing the switch every time.\n \"\"\"\n cfg_path = _setup_isolated_home(\n tmp_path,\n monkeypatch,\n {\"default\": \"old-model\", \"provider\": \"openai-codex\"},\n )\n\n result = await _make_runner()._handle_model_command(\n _make_event(\"/model gpt-5.5\")\n )\n\n assert result is not None\n assert \"gpt-5.5\" in result\n written = yaml.safe_load(cfg_path.read_text(encoding=\"utf-8\"))\n assert written[\"model\"][\"default\"] == \"gpt-5.5\"\n\n\n@pytest.mark.asyncio\nasync def test_model_session_flag_does_not_persist(tmp_path, monkeypatch):\n \"\"\"``/model X --session`` opts out of persistence even under the new default.\"\"\"\n cfg_path = _setup_isolated_home(\n tmp_path,\n monkeypatch,\n {\"default\": \"old-model\", \"provider\": \"openai-codex\"},\n )\n\n result = await _make_runner()._handle_model_command(\n _make_event(\"/model gpt-5.5 --session\")\n )\n\n assert result is not None\n assert \"gpt-5.5\" in result\n written = yaml.safe_load(cfg_path.read_text(encoding=\"utf-8\"))\n # Config untouched — the session override is in-memory only.\n assert written[\"model\"][\"default\"] == \"old-model\"\n"}, "2c6bbaf3529fbd7dca4330d53b5c819f3d223ba5": {"t": 1785767857.7243414, "n": 5874, "h": "a2cd020ef7c61dbe", "c": "\"\"\"Regression tests for gateway /model --global persistence when config.yaml\nhas a flat-string ``model:`` value instead of a nested dict.\n\nBefore fix: ``cfg.setdefault(\"model\", {})`` returned the existing string and\nthe next assignment raised ``TypeError: 'str' object does not support item\nassignment``, so every ``/model X --global`` from Telegram/Discord crashed\nsilently and the user-visible result was \"switch failed\" with no persist.\n\nAfter fix: the persist block coerces a scalar ``model:`` into a nested dict\nbefore mutation, so ``--global`` succeeds and the config is rewritten in\nthe proper ``model: {default: ..., provider: ...}`` form.\n\"\"\"\n\nimport yaml\nimport pytest\n\nfrom gateway.config import Platform\nfrom gateway.platforms.base import MessageEvent, MessageType\nfrom gateway.run import GatewayRunner\nfrom gateway.session import SessionSource\n\n\ndef _make_runner():\n runner = object.__new__(GatewayRunner)\n runner.adapters = {}\n runner._voice_mode = {}\n runner._session_model_overrides = {}\n runner._running_agents = {}\n return runner\n\n\ndef _make_event(text):\n return MessageEvent(\n text=text,\n message_type=MessageType.TEXT,\n source=SessionSource(platform=Platform.TELEGRAM, chat_id=\"12345\", chat_type=\"dm\"),\n )\n\n\ndef _fake_switch_result():\n \"\"\"Build a successful ModelSwitchResult that bypasses real provider resolution.\"\"\"\n from hermes_cli.model_switch import ModelSwitchResult\n\n return ModelSwitchResult(\n success=True,\n new_model=\"gpt-5.5\",\n target_provider=\"openrouter\",\n provider_changed=True,\n api_key=\"sk-test\",\n base_url=\"https://openrouter.ai/api/v1\",\n api_mode=\"chat_completions\",\n provider_label=\"OpenRouter\",\n is_global=True,\n )\n\n\ndef _setup_isolated_home(tmp_path, monkeypatch, model_yaml_value):\n \"\"\"Write a config.yaml with the given ``model:`` value and stub the heavy bits.\"\"\"\n import gateway.run as gateway_run\n\n hermes_home = tmp_path / \".hermes\"\n hermes_home.mkdir()\n cfg_path = hermes_home / \"config.yaml\"\n cfg_path.write_text(\n yaml.safe_dump({\"model\": model_yaml_value, \"providers\": {}}),\n encoding=\"utf-8\",\n )\n\n monkeypatch.setattr(gateway_run, \"_hermes_home\", hermes_home)\n monkeypatch.setattr(\"agent.models_dev.fetch_models_dev\", lambda: {})\n monkeypatch.setattr(\n \"hermes_cli.model_switch.switch_model\",\n lambda **kw: _fake_switch_result(),\n )\n # save_config writes to ``get_hermes_home() / config.yaml`` — point it here.\n monkeypatch.setattr(\"hermes_constants.get_hermes_home\", lambda: hermes_home)\n monkeypatch.setattr(\"hermes_cli.config.get_hermes_home\", lambda: hermes_home)\n return cfg_path\n\n\n@pytest.mark.asyncio\nasync def test_model_global_persists_when_config_has_flat_string_model(tmp_path, monkeypatch):\n \"\"\"Regression: ``model: deepseek-v4-flash`` (flat string) used to crash\n the gateway ``/model X --global`` persist branch with TypeError. After\n the fix, the flat string is coerced to ``{\"default\": ...}`` and the new\n model+provider are persisted on top.\n \"\"\"\n cfg_path = _setup_isolated_home(tmp_path, monkeypatch, \"deepseek-v4-flash\")\n\n result = await _make_runner()._handle_model_command(\n _make_event(\"/model gpt-5.5 --global\")\n )\n\n # Sanity: the handler returned a success-looking message (not a crash log).\n assert result is not None\n assert \"gpt-5.5\" in result\n\n # The persist block must have rewritten config.yaml as a nested dict.\n written = yaml.safe_load(cfg_path.read_text(encoding=\"utf-8\"))\n assert isinstance(written[\"model\"], dict), (\n \"model: should be coerced to a dict, got %r\" % (written[\"model\"],)\n )\n assert written[\"model\"][\"default\"] == \"gpt-5.5\"\n assert written[\"model\"][\"provider\"] == \"openrouter\"\n assert written[\"model\"][\"base_url\"] == \"https://openrouter.ai/api/v1\"\n\n\n@pytest.mark.asyncio\nasync def test_model_global_persists_when_config_has_missing_model(tmp_path, monkeypatch):\n \"\"\"Companion case: ``model:`` key absent entirely. setdefault would have\n worked here, but the coercion branch also has to handle this cleanly.\n \"\"\"\n import gateway.run as gateway_run\n\n hermes_home = tmp_path / \".hermes\"\n hermes_home.mkdir()\n cfg_path = hermes_home / \"config.yaml\"\n cfg_path.write_text(yaml.safe_dump({\"providers\": {}}), encoding=\"utf-8\")\n\n monkeypatch.setattr(gateway_run, \"_hermes_home\", hermes_home)\n monkeypatch.setattr(\"agent.models_dev.fetch_models_dev\", lambda: {})\n monkeypatch.setattr(\n \"hermes_cli.model_switch.switch_model\",\n lambda **kw: _fake_switch_result(),\n )\n monkeypatch.setattr(\"hermes_constants.get_hermes_home\", lambda: hermes_home)\n monkeypatch.setattr(\"hermes_cli.config.get_hermes_home\", lambda: hermes_home)\n\n result = await _make_runner()._handle_model_command(\n _make_event(\"/model gpt-5.5 --global\")\n )\n\n assert result is not None\n written = yaml.safe_load(cfg_path.read_text(encoding=\"utf-8\"))\n assert isinstance(written[\"model\"], dict)\n assert written[\"model\"][\"default\"] == \"gpt-5.5\"\n assert written[\"model\"][\"provider\"] == \"openrouter\"\n\n\n@pytest.mark.asyncio\nasync def test_model_global_persists_when_config_has_proper_dict_model(tmp_path, monkeypatch):\n \"\"\"Already-correct nested dict must still work — no regression on the\n common case.\n \"\"\"\n cfg_path = _setup_isolated_home(\n tmp_path,\n monkeypatch,\n {\"default\": \"old-model\", \"provider\": \"openai-codex\"},\n )\n\n result = await _make_runner()._handle_model_command(\n _make_event(\"/model gpt-5.5 --global\")\n )\n\n assert result is not None\n written = yaml.safe_load(cfg_path.read_text(encoding=\"utf-8\"))\n assert written[\"model\"][\"default\"] == \"gpt-5.5\"\n assert written[\"model\"][\"provider\"] == \"openrouter\"\n"}, "e5d7a4625a0f9e948105d5755614b13bc9fe7f1f": {"t": 1786068513.933768, "n": 7309, "h": "56012457446ca35c", "c": "\"\"\"Regression tests for gateway /model --global persistence when config.yaml\nhas a flat-string ``model:`` value instead of a nested dict.\n\nBefore fix: ``cfg.setdefault(\"model\", {})`` returned the existing string and\nthe next assignment raised ``TypeError: 'str' object does not support item\nassignment``, so every ``/model X --global`` from Telegram/Discord crashed\nsilently and the user-visible result was \"switch failed\" with no persist.\n\nAfter fix: the persist block coerces a scalar ``model:`` into a nested dict\nbefore mutation, so ``--global`` succeeds and the config is rewritten in\nthe proper ``model: {default: ..., provider: ...}`` form.\n\"\"\"\n\nimport yaml\nimport pytest\n\nfrom gateway.config import Platform\nfrom gateway.platforms.base import MessageEvent, MessageType\nfrom gateway.run import GatewayRunner\nfrom gateway.session import SessionSource\n\n\ndef _make_runner():\n runner = object.__new__(GatewayRunner)\n runner.adapters = {}\n runner._voice_mode = {}\n runner._session_model_overrides = {}\n runner._running_agents = {}\n return runner\n\n\ndef _make_event(text):\n return MessageEvent(\n text=text,\n message_type=MessageType.TEXT,\n source=SessionSource(platform=Platform.TELEGRAM, chat_id=\"12345\", chat_type=\"dm\"),\n )\n\n\ndef _fake_switch_result():\n \"\"\"Build a successful ModelSwitchResult that bypasses real provider resolution.\"\"\"\n from hermes_cli.model_switch import ModelSwitchResult\n\n return ModelSwitchResult(\n success=True,\n new_model=\"gpt-5.5\",\n target_provider=\"openrouter\",\n provider_changed=True,\n api_key=\"sk-test\",\n base_url=\"https://openrouter.ai/api/v1\",\n api_mode=\"chat_completions\",\n provider_label=\"OpenRouter\",\n is_global=True,\n )\n\n\ndef _setup_isolated_home(tmp_path, monkeypatch, model_yaml_value):\n \"\"\"Write a config.yaml with the given ``model:`` value and stub the heavy bits.\"\"\"\n import gateway.run as gateway_run\n\n hermes_home = tmp_path / \".hermes\"\n hermes_home.mkdir()\n cfg_path = hermes_home / \"config.yaml\"\n cfg_path.write_text(\n yaml.safe_dump({\"model\": model_yaml_value, \"providers\": {}}),\n encoding=\"utf-8\",\n )\n\n monkeypatch.setattr(gateway_run, \"_hermes_home\", hermes_home)\n monkeypatch.setattr(\"agent.models_dev.fetch_models_dev\", lambda: {})\n monkeypatch.setattr(\n \"hermes_cli.model_switch.switch_model\",\n lambda **kw: _fake_switch_result(),\n )\n # save_config writes to ``get_hermes_home() / config.yaml`` — point it here.\n monkeypatch.setattr(\"hermes_constants.get_hermes_home\", lambda: hermes_home)\n monkeypatch.setattr(\"hermes_cli.config.get_hermes_home\", lambda: hermes_home)\n return cfg_path\n\n\n@pytest.mark.asyncio\nasync def test_model_global_persists_when_config_has_flat_string_model(tmp_path, monkeypatch):\n \"\"\"Regression: ``model: deepseek-v4-flash`` (flat string) used to crash\n the gateway ``/model X --global`` persist branch with TypeError. After\n the fix, the flat string is coerced to ``{\"default\": ...}`` and the new\n model+provider are persisted on top.\n \"\"\"\n cfg_path = _setup_isolated_home(tmp_path, monkeypatch, \"deepseek-v4-flash\")\n\n result = await _make_runner()._handle_model_command(\n _make_event(\"/model gpt-5.5 --global\")\n )\n\n # Sanity: the handler returned a success-looking message (not a crash log).\n assert result is not None\n assert \"gpt-5.5\" in result\n\n # The persist block must have rewritten config.yaml as a nested dict.\n written = yaml.safe_load(cfg_path.read_text(encoding=\"utf-8\"))\n assert isinstance(written[\"model\"], dict), (\n \"model: should be coerced to a dict, got %r\" % (written[\"model\"],)\n )\n assert written[\"model\"][\"default\"] == \"gpt-5.5\"\n assert written[\"model\"][\"provider\"] == \"openrouter\"\n assert \"base_url\" not in written[\"model\"]\n\n\n@pytest.mark.asyncio\nasync def test_model_global_persists_when_config_has_missing_model(tmp_path, monkeypatch):\n \"\"\"Companion case: ``model:`` key absent entirely. setdefault would have\n worked here, but the coercion branch also has to handle this cleanly.\n \"\"\"\n import gateway.run as gateway_run\n\n hermes_home = tmp_path / \".hermes\"\n hermes_home.mkdir()\n cfg_path = hermes_home / \"config.yaml\"\n cfg_path.write_text(yaml.safe_dump({\"providers\": {}}), encoding=\"utf-8\")\n\n monkeypatch.setattr(gateway_run, \"_hermes_home\", hermes_home)\n monkeypatch.setattr(\"agent.models_dev.fetch_models_dev\", lambda: {})\n monkeypatch.setattr(\n \"hermes_cli.model_switch.switch_model\",\n lambda **kw: _fake_switch_result(),\n )\n monkeypatch.setattr(\"hermes_constants.get_hermes_home\", lambda: hermes_home)\n monkeypatch.setattr(\"hermes_cli.config.get_hermes_home\", lambda: hermes_home)\n\n result = await _make_runner()._handle_model_command(\n _make_event(\"/model gpt-5.5 --global\")\n )\n\n assert result is not None\n written = yaml.safe_load(cfg_path.read_text(encoding=\"utf-8\"))\n assert isinstance(written[\"model\"], dict)\n assert written[\"model\"][\"default\"] == \"gpt-5.5\"\n assert written[\"model\"][\"provider\"] == \"openrouter\"\n\n\n@pytest.mark.asyncio\nasync def test_model_global_persists_when_config_has_proper_dict_model(tmp_path, monkeypatch):\n \"\"\"Already-correct nested dict must still work — no regression on the\n common case.\n \"\"\"\n cfg_path = _setup_isolated_home(\n tmp_path,\n monkeypatch,\n {\"default\": \"old-model\", \"provider\": \"openai-codex\"},\n )\n\n result = await _make_runner()._handle_model_command(\n _make_event(\"/model gpt-5.5 --global\")\n )\n\n assert result is not None\n written = yaml.safe_load(cfg_path.read_text(encoding=\"utf-8\"))\n assert written[\"model\"][\"default\"] == \"gpt-5.5\"\n assert written[\"model\"][\"provider\"] == \"openrouter\"\n\n\n@pytest.mark.asyncio\nasync def test_model_no_flag_is_session_scoped_by_default(tmp_path, monkeypatch):\n \"\"\"A plain ``/model X`` (no --global) does NOT persist to config.yaml.\n\n This is the user-facing fix: switches are session-scoped unless the user\n opts in with ``--global`` or sets ``model.persist_switch_by_default: true``.\n \"\"\"\n cfg_path = _setup_isolated_home(\n tmp_path,\n monkeypatch,\n {\"default\": \"old-model\", \"provider\": \"openai-codex\"},\n )\n\n result = await _make_runner()._handle_model_command(\n _make_event(\"/model gpt-5.5\")\n )\n\n assert result is not None\n assert \"gpt-5.5\" in result\n written = yaml.safe_load(cfg_path.read_text(encoding=\"utf-8\"))\n assert written[\"model\"][\"default\"] == \"old-model\"\n\n\n@pytest.mark.asyncio\nasync def test_model_session_flag_does_not_persist(tmp_path, monkeypatch):\n \"\"\"``/model X --session`` opts out of persistence even under the new default.\"\"\"\n cfg_path = _setup_isolated_home(\n tmp_path,\n monkeypatch,\n {\"default\": \"old-model\", \"provider\": \"openai-codex\"},\n )\n\n result = await _make_runner()._handle_model_command(\n _make_event(\"/model gpt-5.5 --session\")\n )\n\n assert result is not None\n assert \"gpt-5.5\" in result\n written = yaml.safe_load(cfg_path.read_text(encoding=\"utf-8\"))\n # Config untouched — the session override is in-memory only.\n assert written[\"model\"][\"default\"] == \"old-model\"\n"}}}}