{"owner":"fathah","repo":"hermes-desktop","hasSkills":true,"totalSkillsCount":11,"totalTokensCount":34462,"categories":["anthropic-skill","root-instruction","codex-rule","claude-rule"],"hasMcp":true,"mcpConfig":{"mcpServers":{"hermes-desktop":{"command":"npx","args":["-y","@modelcontextprotocol/server-hermes-desktop"]}}},"found":[".claude/skills/hermes-agent/SKILL.md","AGENTS.md",".agents/skills/electron-pro/SKILL.md",".claude/skills/lat-md/SKILL.md",".agents/skills/lat-md/SKILL.md",".agents/skills/typescript-expert/SKILL.md",".agents/skills/hermes-agent/SKILL.md",".codex/hooks.json","CLAUDE.md",".agents/skills/typescript-expert/references/tsconfig-strict.json",".agents/skills/typescript-expert/references/typescript-cheatsheet.md"],"skills":{".claude/skills/hermes-agent/SKILL.md":"---\nname: hermes-agent\ndescription: Expert in building self-improving AI agents with tool use, multi-platform messaging, and a closed learning loop. Proficient in LLM orchestration, tool integration, session management, and agent autonomy.\n---\n\n# Hermes Agent - Complete Project Guide (A-Z)\n\n> **Purpose of this document:** A single, comprehensive reference that explains everything about the Hermes Agent project — its architecture, source code, features, release history, and design patterns — so that any AI or developer can fully understand the system.\n\n---\n\n## Table of Contents\n\n1. [Project Overview](#1-project-overview)\n2. [Key Features Summary](#2-key-features-summary)\n3. [Installation & Getting Started](#3-installation--getting-started)\n4. [Project Structure](#4-project-structure)\n5. [Core Architecture](#5-core-architecture)\n   - 5.1 [AIAgent Class (run_agent.py)](#51-aiagent-class-run_agentpy)\n   - 5.2 [Tool Orchestration (model_tools.py)](#52-tool-orchestration-model_toolspy)\n   - 5.3 [Toolset System (toolsets.py)](#53-toolset-system-toolsetspy)\n   - 5.4 [Tool Registry (tools/registry.py)](#54-tool-registry-toolsregistrypy)\n   - 5.5 [Session Database (hermes_state.py)](#55-session-database-hermes_statepy)\n   - 5.6 [Constants & Home Directory (hermes_constants.py)](#56-constants--home-directory-hermes_constantspy)\n6. [CLI System](#6-cli-system)\n   - 6.1 [Interactive CLI (cli.py)](#61-interactive-cli-clipy)\n   - 6.2 [CLI Entry Point (hermes_cli/main.py)](#62-cli-entry-point-hermes_climainpy)\n   - 6.3 [Configuration System (hermes_cli/config.py)](#63-configuration-system-hermes_cliconfigpy)\n   - 6.4 [Slash Command Registry (hermes_cli/commands.py)](#64-slash-command-registry-hermes_clicommandspy)\n   - 6.5 [Setup Wizard (hermes_cli/setup.py)](#65-setup-wizard-hermes_clisetupy)\n   - 6.6 [Model Catalog (hermes_cli/models.py)](#66-model-catalog-hermes_climodelspy)\n   - 6.7 [Skin/Theme Engine (hermes_cli/skin_engine.py)](#67-skintheme-engine-hermes_cliskin_enginepy)\n7. [Tool System](#7-tool-system)\n   - 7.1 [Terminal Tool (tools/terminal_tool.py)](#71-terminal-tool-toolsterminal_toolpy)\n   - 7.2 [File Tools (tools/file_tools.py)](#72-file-tools-toolsfile_toolspy)\n   - 7.3 [Web Tools (tools/web_tools.py)](#73-web-tools-toolsweb_toolspy)\n   - 7.4 [Browser Tool (tools/browser_tool.py)](#74-browser-tool-toolsbrowser_toolpy)\n   - 7.5 [Delegate Tool (tools/delegate_tool.py)](#75-delegate-tool-toolsdelegate_toolpy)\n   - 7.6 [MCP Tool (tools/mcp_tool.py)](#76-mcp-tool-toolsmcp_toolpy)\n   - 7.7 [Approval System (tools/approval.py)](#77-approval-system-toolsapprovalpy)\n   - 7.8 [Terminal Backends (tools/environments/)](#78-terminal-backends-toolsenvironments)\n8. [Agent Internals](#8-agent-internals)\n   - 8.1 [Prompt Builder (agent/prompt_builder.py)](#81-prompt-builder-agentprompt_builderpy)\n   - 8.2 [Context Compressor (agent/context_compressor.py)](#82-context-compressor-agentcontext_compressorpy)\n   - 8.3 [Prompt Caching (agent/prompt_caching.py)](#83-prompt-caching-agentprompt_cachingpy)\n   - 8.4 [Auxiliary Client (agent/auxiliary_client.py)](#84-auxiliary-client-agentauxiliary_clientpy)\n   - 8.5 [Display & Spinner (agent/display.py)](#85-display--spinner-agentdisplaypy)\n   - 8.6 [Skill Commands (agent/skill_commands.py)](#86-skill-commands-agentskill_commandspy)\n9. [Messaging Gateway](#9-messaging-gateway)\n   - 9.1 [GatewayRunner (gateway/run.py)](#91-gatewayrunner-gatewayrunpy)\n   - 9.2 [Session Store (gateway/session.py)](#92-session-store-gatewaysessionpy)\n   - 9.3 [Platform Adapters (gateway/platforms/)](#93-platform-adapters-gatewayplatforms)\n10. [Cron Scheduling](#10-cron-scheduling)\n11. [Skills System](#11-skills-system)\n12. [Plugin System](#12-plugin-system)\n13. [Memory System](#13-memory-system)\n14. [ACP Server (IDE Integration)](#14-acp-server-ide-integration)\n15. [API Server](#15-api-server)\n16. [MCP Server Mode](#16-mcp-server-mode)\n17. [RL Training Environments](#17-rl-training-environments)\n18. [Profiles (Multi-Instance)](#18-profiles-multi-instance)\n19. [Security Model](#19-security-model)\n20. [Provider & Model System](#20-provider--model-system)\n21. [Streaming & Reasoning](#21-streaming--reasoning)\n22. [Release History](#22-release-history)\n23. [File Dependency Chain](#23-file-dependency-chain)\n24. [Key Design Patterns](#24-key-design-patterns)\n25. [Configuration Reference](#25-configuration-reference)\n26. [Known Pitfalls](#26-known-pitfalls)\n\n---\n\n## 1. Project Overview\n\n**Hermes Agent** is a self-improving AI agent built by [Nous Research](https://nousresearch.com). It is an open-source (MIT licensed), Python-based project that provides:\n\n- A **full interactive terminal UI** (CLI) for conversing with LLMs\n- A **messaging gateway** supporting 16+ platforms (Telegram, Discord, Slack, WhatsApp, Signal, Matrix, Email, etc.)\n- A **closed learning loop** — the agent creates skills from experience, improves them during use, nudges itself to persist knowledge, searches past conversations, and builds a deepening model of who you are\n- **40+ built-in tools** — terminal execution, file manipulation, web search, browser automation, code execution, image generation, TTS/STT, and more\n- **Any LLM provider** — OpenRouter (200+ models), Nous Portal (400+ models), OpenAI, Anthropic, Hugging Face, GitHub Copilot, z.ai/GLM, Kimi/Moonshot, MiniMax, Alibaba/DashScope, custom endpoints\n- **Six terminal backends** — local, Docker, SSH, Modal (serverless), Daytona (serverless), Singularity (HPC)\n- **Scheduled automations** via built-in cron scheduler\n- **IDE integration** via ACP (Agent Communication Protocol) for VS Code, Zed, JetBrains\n- **MCP integration** — both client (connect to any MCP server) and server (expose Hermes to MCP clients)\n- **RL training** via Atropos environments for training the next generation of tool-calling models\n\n**Tech Stack:**\n\n- Python 3.11+ (core agent, tools, gateway, cron)\n- Node.js (browser automation via agent-browser)\n- SQLite with WAL mode and FTS5 (session storage, full-text search)\n- OpenAI-compatible API (primary inference interface)\n- Anthropic SDK (native Anthropic support)\n- Rich + prompt_toolkit (CLI rendering)\n\n**Repository:** `github.com/NousResearch/hermes-agent`\n**Version:** 0.7.0 (as of April 2026)\n**License:** MIT\n\n---\n\n## 2. Key Features Summary\n\n| Feature                      | Description                                                                                                                                                                      |\n| ---------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| **Terminal UI**              | Full TUI with multiline editing, slash-command autocomplete, conversation history, interrupt-and-redirect, streaming tool output                                                 |\n| **Multi-Platform Messaging** | Telegram, Discord, Slack, WhatsApp, Signal, Matrix, Email, Home Assistant, DingTalk, Feishu/Lark, WeCom, Mattermost, SMS, Webhook — all from a single gateway process            |\n| **Learning Loop**            | Agent-curated memory with periodic nudges, autonomous skill creation, skills self-improve during use, FTS5 session search with LLM summarization, Honcho dialectic user modeling |\n| **Scheduled Tasks**          | Built-in cron scheduler with delivery to any platform (daily reports, nightly backups, weekly audits)                                                                            |\n| **Subagent Delegation**      | Spawn isolated subagents for parallel workstreams with restricted toolsets                                                                                                       |\n| **Execute Code**             | Python scripts that call tools via RPC, collapsing multi-step pipelines into zero-context-cost turns                                                                             |\n| **Terminal Backends**        | Local, Docker, SSH, Modal, Daytona, Singularity — run on a $5 VPS or a GPU cluster                                                                                               |\n| **Skills**                   | 70+ bundled skills across 28 categories, Skills Hub for community discovery, agentskills.io compatibility                                                                        |\n| **Plugins**                  | Drop-in Python plugins with lifecycle hooks (pre_llm_call, post_llm_call, on_session_start, on_session_end)                                                                      |\n| **MCP**                      | Client (connect to MCP servers for extended tools) and Server (expose conversations to MCP clients)                                                                              |\n| **IDE Integration**          | VS Code, Zed, JetBrains via ACP server with session management and tool streaming                                                                                                |\n| **API Server**               | OpenAI-compatible `/v1/chat/completions` endpoint for headless integrations                                                                                                      |\n| **Profiles**                 | Multi-instance support — each profile gets isolated config, memory, sessions, skills, gateway                                                                                    |\n| **Security**                 | Command approval system, secret redaction, SSRF protection, PII redaction, injection detection, credential directory protection                                                  |\n| **RL Training**              | Atropos environments for batch trajectory generation and agent policy optimization                                                                                               |\n\n---\n\n## 3. Installation & Getting Started\n\n```bash\n# One-line install (Linux, macOS, WSL2)\ncurl -fsSL https://raw.githubusercontent.com/NousResearch/hermes-agent/main/scripts/install.sh | bash\n\n# After install\nsource ~/.bashrc    # or: source ~/.zshrc\nhermes              # start chatting\n\n# Key commands\nhermes model        # Choose LLM provider and model\nhermes tools        # Configure which tools are enabled\nhermes config set   # Set individual config values\nhermes gateway      # Start the messaging gateway\nhermes setup        # Run the full setup wizard\nhermes update       # Update to latest version\nhermes doctor       # Diagnose any issues\n```\n\n**For development:**\n\n```bash\ngit clone https://github.com/NousResearch/hermes-agent.git\ncd hermes-agent\ncurl -LsSf https://astral.sh/uv/install.sh | sh\nuv venv venv --python 3.11\nsource venv/bin/activate\nuv pip install -e \".[all,dev]\"\npython -m pytest tests/ -q    # ~3000 tests\n```\n\n---\n\n## 4. Project Structure\n\n```\nhermes-agent/\n├── run_agent.py              # AIAgent class — core conversation loop\n├── model_tools.py            # Tool orchestration, _discover_tools(), handle_function_call()\n├── toolsets.py               # Toolset definitions, _HERMES_CORE_TOOLS list\n├── toolset_distributions.py  # Toolset sampling distributions for RL\n├── cli.py                    # HermesCLI class — interactive CLI orchestrator\n├── hermes_state.py           # SessionDB — SQLite session store (FTS5 search)\n├── hermes_constants.py       # Shared constants, get_hermes_home()\n├── hermes_time.py            # Timezone handling\n├── utils.py                  # Shared utility functions\n├── batch_runner.py           # Parallel batch processing\n├── trajectory_compressor.py  # Trajectory compression for RL training\n├── mcp_serve.py              # MCP server mode entry point\n├── mini_swe_runner.py        # Minimal SWE benchmark runner\n├── rl_cli.py                 # RL CLI commands\n│\n├── agent/                    # Agent internals\n│   ├── prompt_builder.py         # System prompt assembly\n│   ├── context_compressor.py     # Auto context compression\n│   ├── prompt_caching.py         # Anthropic prompt caching\n│   ├── auxiliary_client.py       # Auxiliary LLM client (vision, summarization)\n│   ├── model_metadata.py         # Model context lengths, token estimation\n│   ├── models_dev.py             # models.dev registry integration\n│   ├── display.py                # KawaiiSpinner, tool preview formatting\n│   ├── skill_commands.py         # Skill slash commands (shared CLI/gateway)\n│   └── trajectory.py             # Trajectory saving helpers\n│\n├── hermes_cli/               # CLI subcommands and setup\n│   ├── main.py               # Entry point — all `hermes` subcommands\n│   ├── config.py             # DEFAULT_CONFIG, OPTIONAL_ENV_VARS, migration\n│   ├── commands.py           # Slash command definitions + SlashCommandCompleter\n│   ├── callbacks.py          # Terminal callbacks (clarify, sudo, approval)\n│   ├── setup.py              # Interactive setup wizard\n│   ├── skin_engine.py        # Skin/theme engine\n│   ├── skills_config.py      # `hermes skills` — skill management\n│   ├── tools_config.py       # `hermes tools` — tool management\n│   ├── skills_hub.py         # Skills Hub integration\n│   ├── models.py             # Model catalog, provider model lists\n│   ├── model_switch.py       # Shared /model switch pipeline\n│   └── auth.py               # Provider credential resolution\n│\n├── tools/                    # Tool implementations (one file per tool)\n│   ├── registry.py           # Central tool registry\n│   ├── approval.py           # Dangerous command detection\n│   ├── terminal_tool.py      # Terminal/shell execution\n│   ├── process_registry.py   # Background process management\n│   ├── file_tools.py         # File read/write/search/patch\n│   ├── web_tools.py          # Web search/extract\n│   ├── browser_tool.py       # Browser automation\n│   ├── code_execution_tool.py # execute_code sandbox\n│   ├── delegate_tool.py      # Subagent delegation\n│   ├── mcp_tool.py           # MCP client integration\n│   ├── skills_tool.py        # Skill management tool\n│   ├── todo_tool.py          # Todo/task tracking tool\n│   ├── memory_tool.py        # Memory read/write tool\n│   ├── tts_tool.py           # Text-to-speech\n│   ├── vision_tool.py        # Image analysis\n│   ├── image_gen_tool.py     # Image generation\n│   └── environments/         # Terminal backends\n│       ├── base.py               # BaseEnvironment ABC\n│       ├── local.py              # Local execution\n│       ├── docker.py             # Docker containers\n│       ├── ssh.py                # SSH remote execution\n│       ├── modal.py              # Modal serverless\n│       ├── managed_modal.py      # Nous-hosted Modal\n│       ├── daytona.py            # Daytona serverless\n│       ├── singularity.py        # Singularity HPC containers\n│       └── persistent_shell.py   # Persistent shell mixin\n│\n├── gateway/                  # Messaging platform gateway\n│   ├── run.py                # GatewayRunner — main message loop\n│   ├── session.py            # SessionStore — conversation persistence\n│   ├── status.py             # Gateway status, token locks\n│   └── platforms/            # 16 platform adapters\n│       ├── base.py               # BasePlatformAdapter ABC\n│       ├── telegram.py           # Telegram (polling + webhook)\n│       ├── discord.py            # Discord\n│       ├── slack.py              # Slack\n│       ├── whatsapp.py           # WhatsApp\n│       ├── matrix.py             # Matrix (E2EE)\n│       ├── signal.py             # Signal\n│       ├── email.py              # Email (IMAP/SMTP)\n│       ├── homeassistant.py      # Home Assistant\n│       ├── sms.py                # SMS (Twilio)\n│       ├── mattermost.py         # Mattermost\n│       ├── dingtalk.py           # DingTalk\n│       ├── feishu.py             # Feishu/Lark\n│       ├── wecom.py              # WeCom (Enterprise WeChat)\n│       ├── webhook.py            # Generic webhook\n│       └── api_server.py         # OpenAI-compatible API server\n│\n├── acp_adapter/              # ACP server (IDE integration)\n│   ├── server.py             # HermesACPAgent class\n│   ├── session.py            # SessionManager\n│   ├── events.py             # Streaming callbacks\n│   ├── permissions.py        # Approval callbacks\n│   └── entry.py              # Entry point\n│\n├── cron/                     # Scheduler\n│   ├── scheduler.py          # tick() — job execution engine\n│   └── jobs.py               # Job storage and CRUD\n│\n├── plugins/                  # Plugin system\n│   └── memory/               # 8 memory provider plugins\n│       ├── openviking/\n│       ├── mem0/\n│       ├── hindsight/\n│       ├── holographic/\n│       ├── honcho/\n│       ├── retaindb/\n│       └── byterover/\n│\n├── environments/             # RL training environments (Atropos)\n│   ├── hermes_base_env.py    # Abstract base RL environment\n│   ├── agent_loop.py         # HermesAgentLoop — rollout execution\n│   ├── tool_context.py       # ToolContext — sandbox for RL\n│   ├── web_research_env.py   # Web research tasks\n│   └── agentic_opd_env.py    # Observation-Prediction-Demo env\n│\n├── skills/                   # 70+ bundled skills across 28 categories\n├── optional-skills/          # Additional optional skills\n├── tests/                    # ~3000 pytest tests\n├── scripts/                  # Install, update, packaging scripts\n├── docker/                   # Docker build files\n├── docs/                     # Documentation source (Docusaurus)\n├── website/                  # Landing page\n├── desktop/                  # Desktop app (Electron, separate repo)\n├── tinker-atropos/           # RL submodule\n│\n├── pyproject.toml            # Python package config\n├── AGENTS.md                 # Developer guide for AI assistants\n├── RELEASE_v0.2.0.md → v0.7.0.md  # Release notes\n└── cli-config.yaml.example   # Example config\n```\n\n**User config directory:** `~/.hermes/`\n\n```\n~/.hermes/\n├── config.yaml           # User settings\n├── .env                  # API keys and secrets\n├── MEMORY.md             # Persistent agent memory\n├── USER.md               # User profile\n├── SOUL.md               # Agent personality/identity\n├── sessions.db           # SQLite session database\n├── skills/               # User-installed skills\n├── skins/                # Custom CLI themes\n├── plugins/              # User plugins\n├── cron/                 # Cron jobs and output\n│   ├── jobs.json\n│   └── output/\n├── cache/                # Image/audio cache\n├── plans/                # Generated plans\n├── profiles/             # Multi-instance profiles\n└── mcp/                  # MCP server configs\n```\n\n---\n\n## 5. Core Architecture\n\n### 5.1 AIAgent Class (run_agent.py)\n\nThe `AIAgent` class is the heart of the system — the core conversation loop that orchestrates LLM calls, tool execution, context management, and response delivery.\n\n**Constructor (~60 parameters):**\n\n```python\nclass AIAgent:\n    def __init__(self,\n        model: str = \"anthropic/claude-opus-4.6\",\n        max_iterations: int = 90,\n        enabled_toolsets: list = None,\n        disabled_toolsets: list = None,\n        quiet_mode: bool = False,\n        save_trajectories: bool = False,\n        platform: str = None,            # \"cli\", \"telegram\", etc.\n        session_id: str = None,\n        session_db: SessionDB = None,\n        skip_context_files: bool = False,\n        skip_memory: bool = False,\n        base_url: str = None,\n        api_key: str = None,\n        provider: str = None,\n        api_mode: str = \"chat_completions\",  # or \"anthropic_messages\" or \"codex_responses\"\n        tool_progress_callback = None,\n        stream_delta_callback = None,\n        thinking_callback = None,\n        status_callback = None,\n        iteration_budget: IterationBudget = None,\n        credential_pool = None,\n        checkpoints_enabled: bool = False,\n        # ... plus provider, routing, callback params\n    )\n```\n\n**Main Methods:**\n\n| Method                                                                          | Returns  | Purpose                                                                            |\n| ------------------------------------------------------------------------------- | -------- | ---------------------------------------------------------------------------------- |\n| `chat(message, stream_callback)`                                                | `str`    | Simple interface — returns final response text                                     |\n| `run_conversation(user_message, system_message, conversation_history, task_id)` | `dict`   | Full interface — returns `{final_response, messages, completed, api_calls, error}` |\n| `_interruptible_api_call(api_kwargs)`                                           | Response | Runs API request in background thread with interrupt support                       |\n| `_interruptible_streaming_api_call(api_kwargs, on_first_delta)`                 | Response | Streaming variant with delta callbacks                                             |\n\n**The Core Agent Loop** (inside `run_conversation()`):\n\n```python\nwhile api_call_count < self.max_iterations and self.iteration_budget.remaining > 0:\n    response = client.chat.completions.create(\n        model=model, messages=messages, tools=tool_schemas\n    )\n    if response.tool_calls:\n        for tool_call in response.tool_calls:\n            result = handle_function_call(tool_call.name, tool_call.args, task_id)\n            messages.append(tool_result_message(result))\n        api_call_count += 1\n    else:\n        return response.content  # Final text response\n```\n\n**Key Behaviors:**\n\n- **Three API modes:** `chat_completions` (OpenAI-compatible), `anthropic_messages` (Anthropic SDK), `codex_responses` (OpenAI Codex)\n- **Parallel tool execution:** Independent tool calls run concurrently via ThreadPoolExecutor (unless they share file paths or are in the never-parallel list)\n- **Interrupt support:** Background threads allow interrupt detection without blocking on HTTP\n- **Error recovery:** Automatic fallback chain (primary → fallback model), retry with exponential backoff, context compression on token overflow\n- **Budget pressure:** Warnings at 70% (caution) and 90% (urgent) of iteration budget\n- **Oversized results:** Tool results >100K chars are saved to temp files with a preview\n- **Stale connection detection:** 90s timeout for streaming, 60s read timeout\n\n**IterationBudget** (thread-safe):\n\n```python\nclass IterationBudget:\n    def consume() -> bool    # Check and consume one iteration\n    def refund()             # Give back iteration (for execute_code turns)\n    @property remaining      # Remaining iterations\n```\n\n---\n\n### 5.2 Tool Orchestration (model_tools.py)\n\nBridges the agent and tool registry — handles discovery, schema generation, and dispatch.\n\n**Key Functions:**\n\n| Function                                                                                | Purpose                                                                   |\n| --------------------------------------------------------------------------------------- | ------------------------------------------------------------------------- |\n| `_discover_tools()`                                                                     | Imports all tool modules (each calls `registry.register()` on import)     |\n| `get_tool_definitions(enabled_toolsets, disabled_toolsets, quiet_mode)`                 | Returns OpenAI-format tool schemas filtered by toolset                    |\n| `handle_function_call(function_name, function_args, task_id, user_task, enabled_tools)` | Main dispatcher — routes calls to registry with arg coercion              |\n| `coerce_tool_args(tool_name, args)`                                                     | Type coercion for LLM-generated arguments (string→int, string→bool, etc.) |\n\n**Tool Discovery Order:**\n\n1. Static tools (web_tools, terminal_tool, file_tools, browser_tool, etc.)\n2. Optional tools (fal_client for image gen, honcho, etc.) — graceful fallback if missing\n3. Plugin-registered tools\n4. MCP server tools (dynamic, via tools/list_changed notifications)\n\n**Special Tool Handling:**\n\n- **Agent-level tools** (todo, memory, session_search, delegate_task): Intercepted by `run_agent.py` before `handle_function_call()`\n- **execute_code**: Passes `enabled_tools` for sandbox tool list\n- **Dynamic schema adjustments**: `browser_navigate` strips web_search reference if tools unavailable\n\n**Async Bridging:**\n\n- Persistent event loops (not `asyncio.run()`) to prevent \"Event loop is closed\" errors\n- Main thread uses shared loop; worker threads get per-thread loops\n- `_run_async()` detects running loop and spins up disposable thread if needed\n\n---\n\n### 5.3 Toolset System (toolsets.py)\n\nProvides flexible tool grouping and composition.\n\n**Core Toolsets:**\n\n| Toolset          | Tools Included                                                                                   |\n| ---------------- | ------------------------------------------------------------------------------------------------ |\n| `web`            | web_search, web_extract, web_crawl                                                               |\n| `terminal`       | terminal                                                                                         |\n| `file`           | read_file, write_file, edit_file, list_files, search_files                                       |\n| `browser`        | browser_navigate, browser_snapshot, browser_click, browser_type, browser_scroll, browser_extract |\n| `vision`         | analyze_image                                                                                    |\n| `image_gen`      | generate_image                                                                                   |\n| `tts`            | text_to_speech                                                                                   |\n| `todo`           | todo_read, todo_write                                                                            |\n| `memory`         | memory_read, memory_write                                                                        |\n| `session_search` | session_search                                                                                   |\n| `delegation`     | delegate_task                                                                                    |\n| `code_execution` | execute_code                                                                                     |\n| `cronjob`        | create_job, list_jobs, delete_job                                                                |\n| `messaging`      | send_message                                                                                     |\n| `homeassistant`  | ha_get_states, ha_call_service, ...                                                              |\n\n**Composite Toolsets:**\n\n- `hermes-cli` — All core tools for CLI platform\n- `hermes-telegram`, `hermes-discord`, etc. — Platform-specific tool sets\n- `hermes-gateway` — Union of all platform tools\n- `debugging` — terminal + file + web\n- `safe` — Everything except terminal\n\n**Resolution:**\n\n```python\nresolve_toolset(name, visited=None) → List[str]\n# Recursively resolves toolset to tool names\n# Handles composition (includes) and cycle detection\n# Special aliases: \"all\" or \"*\" = all tools\n```\n\n---\n\n### 5.4 Tool Registry (tools/registry.py)\n\nSingleton managing all tool schemas and handlers. Circular-import safe — has no tool dependencies.\n\n**ToolEntry** (per-tool metadata):\n\n```python\n@dataclass(slots=True)\nclass ToolEntry:\n    name: str\n    toolset: str\n    schema: dict           # OpenAI-format tool definition\n    handler: Callable      # Sync or async handler function\n    check_fn: Callable     # Returns True if tool is available\n    requires_env: list     # Required environment variables\n    is_async: bool\n    description: str\n    emoji: str\n```\n\n**Key Methods:**\n\n```python\nregistry.register(name, toolset, schema, handler, check_fn, requires_env)\nregistry.get_definitions(tool_names, quiet)  # Returns filtered schemas\nregistry.dispatch(name, args, **kwargs)      # Execute with async bridging\nregistry.deregister(name)                    # Remove (for MCP tool refresh)\nregistry.check_tool_availability()           # Returns (available, unavailable)\n```\n\n---\n\n### 5.5 Session Database (hermes_state.py)\n\nSQLite-based persistent session storage with FTS5 full-text search.\n\n**Schema (v6):**\n\n```sql\n-- Sessions table\nsessions (\n    id TEXT PRIMARY KEY,\n    source TEXT, user_id TEXT, model TEXT, model_config TEXT,\n    system_prompt TEXT, parent_session_id TEXT,\n    started_at TEXT, ended_at TEXT, end_reason TEXT,\n    message_count INTEGER, tool_call_count INTEGER,\n    input_tokens INTEGER, output_tokens INTEGER,\n    cache_read_tokens INTEGER, cache_write_tokens INTEGER, reasoning_tokens INTEGER,\n    estimated_cost_usd REAL, actual_cost_usd REAL,\n    title TEXT  -- UNIQUE INDEX\n)\n\n-- Messages table\nmessages (\n    id INTEGER PRIMARY KEY AUTOINCREMENT,\n    session_id TEXT, role TEXT, content TEXT,\n    tool_call_id TEXT, tool_calls TEXT,  -- JSON\n    tool_name TEXT, timestamp TEXT,\n    token_count INTEGER, finish_reason TEXT,\n    reasoning TEXT, reasoning_details TEXT, codex_reasoning_items TEXT\n)\n\n-- FTS5 virtual table (auto-synced via triggers)\nmessages_fts (content)\n```\n\n**Concurrency Model:**\n\n- WAL (Write-Ahead Logging) for concurrent readers + single writer\n- `BEGIN IMMEDIATE` for write transactions (lock at start, not commit)\n- Jitter retry on lock: 20-150ms random backoff, max 15 retries\n- Periodic WAL checkpoint every 50 writes\n\n**Key Operations:**\n\n- `create_session()`, `end_session()`, `reopen_session()`\n- `add_message()`, `get_messages()`\n- `search_sessions(query)` — FTS5 full-text search\n- `update_token_counts()` — Supports both incremental (CLI) and absolute (gateway) modes\n\n---\n\n### 5.6 Constants & Home Directory (hermes_constants.py)\n\nImport-safe constants module with no circular dependencies.\n\n```python\nget_hermes_home() → Path          # HERMES_HOME env var or ~/.hermes\ndisplay_hermes_home() → str       # User-friendly display: \"~/.hermes\"\nget_optional_skills_dir() → Path  # HERMES_OPTIONAL_SKILLS env var\nparse_reasoning_effort(str) → Dict  # \"high\" → {\"enabled\": True, \"effort\": \"high\"}\n\n# Key constants\nOPENROUTER_BASE_URL = \"https://openrouter.ai/api/v1\"\nNOUS_API_BASE_URL = \"https://inference-api.nousresearch.com/v1\"\nAI_GATEWAY_BASE_URL = \"https://ai-gateway.vercel.sh/v1\"\nVALID_REASONING_EFFORTS = (\"xhigh\", \"high\", \"medium\", \"low\", \"minimal\")\n```\n\n---\n\n## 6. CLI System\n\n### 6.1 Interactive CLI (cli.py)\n\nThe `HermesCLI` class provides the interactive terminal interface.\n\n**Features:**\n\n- **Rich** for banner/panels, **prompt_toolkit** for input with autocomplete\n- **KawaiiSpinner** — animated faces during API calls, `┊` activity feed for tool results\n- Multiline editing with Shift+Enter\n- Slash-command autocomplete\n- Session history with up/down arrow navigation\n- Clipboard image paste (Alt+V / Ctrl+V)\n- Status bar showing model, provider, and token counts\n- Inline diff previews for file write/patch operations\n\n**Configuration Loading:**\n\n```python\nload_cli_config() → dict\n# Loads from ~/.hermes/config.yaml (or ./cli-config.yaml fallback)\n# Merges with hardcoded defaults\n# Expands ${ENV_VAR} references\n# Maps terminal config → env vars\n```\n\n---\n\n### 6.2 CLI Entry Point (hermes_cli/main.py)\n\nAll `hermes` subcommands are dispatched from here:\n\n```\nhermes                    # Default: interactive chat\nhermes chat               # Explicit interactive mode\nhermes gateway start|stop|status|install|uninstall\nhermes setup              # Setup wizard\nhermes model              # Select model/provider\nhermes tools              # Configure tools\nhermes skills             # Manage skills\nhermes config set|get     # Direct config manipulation\nhermes cron list|delete   # Cron job management\nhermes doctor             # Diagnose issues\nhermes sessions browse    # Session picker\nhermes profile create|list|switch|delete|export|import\nhermes mcp serve|add|remove  # MCP management\nhermes acp                # Start ACP server\nhermes update|uninstall|version\n```\n\n**Profile System:**\n\n- `_apply_profile_override()` runs BEFORE any imports to set `HERMES_HOME`\n- Pre-parses `--profile/-p` from argv\n- Allows fully isolated agent instances with separate config, memory, sessions, skills\n\n---\n\n### 6.3 Configuration System (hermes_cli/config.py)\n\n**Key Configuration Sections:**\n\n```yaml\nmodel: \"anthropic/claude-opus-4.6\" # or dict with provider/base_url/api_key\nproviders: {} # Provider-specific configs\nfallback_providers: [] # Ordered failover list\ncredential_pool: {} # Multiple API keys per provider\n\nagent:\n  max_turns: 90\n  gateway_timeout: 1800\n  tool_use_enforcement: \"auto\"\n\nterminal:\n  backend: \"local\" # local|docker|modal|daytona|ssh|singularity\n  timeout: 180\n  persistent_shell: true\n  docker_image: \"nikolaik/python-nodejs:...\"\n\ncompression:\n  enabled: true\n  threshold: 0.50 # Compress when 50% of context used\n  target_ratio: 0.20 # Summary = 20% of compressed content\n  protect_last_n: 20\n\nauxiliary:\n  vision: { provider, model }\n  web_extract: { provider, model }\n  compression: { provider, model }\n\nmemory:\n  memory_enabled: true\n  provider: \"\" # \"\" | \"honcho\" | \"mem0\" | etc.\n  memory_char_limit: 2200\n\ndisplay:\n  personality: \"kawaii\"\n  show_reasoning: false\n  inline_diffs: true\n  skin: \"default\"\n  streaming: true\n\ntts:\n  provider: \"edge\" # edge|elevenlabs|openai|neutts\n\nstt:\n  enabled: true\n  provider: \"local\" # local|groq|openai\n\nprivacy:\n  redact_pii: false\n\nmcp_servers: {} # MCP server configurations\n\nskills:\n  external_dirs: [] # Additional skill directories\n\napprovals:\n  mode: \"smart\" # smart|always|off\n```\n\n**Config Files:**\n\n- `~/.hermes/config.yaml` — User settings (authoritative)\n- `~/.hermes/.env` — API keys and secrets\n- Config version migration system (currently v5)\n\n---\n\n### 6.4 Slash Command Registry (hermes_cli/commands.py)\n\nAll slash commands defined centrally in `COMMAND_REGISTRY`:\n\n```python\nCommandDef(name, description, category, aliases, args_hint, cli_only, gateway_only)\n```\n\n**Derived automatically by:**\n\n- CLI `process_command()` — dispatch on canonical name\n- Gateway dispatch + help\n- Telegram BotCommand menu\n- Slack `/hermes` subcommands\n- Autocomplete + help text\n\n**Key Commands:**\n\n| Command        | Aliases    | Description                   |\n| -------------- | ---------- | ----------------------------- |\n| `/new`         | `/reset`   | Start fresh conversation      |\n| `/model`       |            | Show/switch model             |\n| `/personality` |            | Set agent personality         |\n| `/retry`       |            | Retry last turn               |\n| `/undo`        |            | Remove last turn              |\n| `/compress`    | `/compact` | Compress context              |\n| `/usage`       | `/cost`    | Show token usage              |\n| `/insights`    |            | Usage analytics               |\n| `/skills`      |            | Browse/install skills         |\n| `/background`  | `/bg`      | Manage background processes   |\n| `/plan`        |            | Generate implementation plan  |\n| `/rollback`    |            | Restore filesystem checkpoint |\n| `/verbose`     |            | Toggle debug output           |\n| `/reasoning`   |            | Set reasoning effort          |\n| `/yolo`        |            | Toggle approval bypass        |\n| `/btw`         |            | Ephemeral side question       |\n| `/stop`        |            | Kill current agent run        |\n| `/queue`       |            | Queue next prompt             |\n| `/browser`     |            | Interactive browser session   |\n| `/history`     | `/resume`  | Session browser               |\n| `/skin`        |            | Switch CLI theme              |\n\n---\n\n### 6.5 Setup Wizard (hermes_cli/setup.py)\n\nModular interactive wizard with independent sections:\n\n1. **Model & Provider** — Select AI provider, enter API keys, choose model\n2. **Terminal Backend** — Choose execution environment\n3. **Agent Settings** — Max iterations, compression, session policies\n4. **Messaging Platforms** — Configure Telegram, Discord, Slack, etc.\n5. **Tools** — TTS, STT, web search, image generation, browser\n\nFeatures:\n\n- Live credential validation\n- Real-time model list fetching from provider APIs\n- Automatic OpenClaw migration detection\n- Atomic config file writes\n\n---\n\n### 6.6 Model Catalog (hermes_cli/models.py)\n\nProvider-specific model lists:\n\n```python\n_PROVIDER_MODELS = {\n    \"nous\": [\"anthropic/claude-opus-4.6\", \"anthropic/claude-sonnet-4.6\", ...],  # 25+\n    \"openrouter\": [\"anthropic/claude-opus-4.6\", \"google/gemini-3-flash\", ...],  # 30+\n    \"anthropic\": [\"claude-opus-4-6\", \"claude-sonnet-4-6\", ...],\n    \"openai\": [\"gpt-5\", \"gpt-5.4-mini\", \"gpt-4.1\", \"gpt-4o\", ...],\n    \"copilot\": [\"gpt-5.4\", \"gpt-5.4-mini\", \"gpt-5.3-codex\", ...],\n    \"huggingface\": [...],\n    \"minimax\": [...],\n    \"kimi-coding\": [...],\n    \"alibaba\": [...],\n    \"deepseek\": [...],\n    # ... more providers\n}\n```\n\nFeatures:\n\n- Dynamic fetching via provider `/models` endpoints\n- Curated lists used when live probe returns fewer models\n- Fuzzy matching for typo correction\n- Validation against provider catalog\n\n---\n\n### 6.7 Skin/Theme Engine (hermes_cli/skin_engine.py)\n\nData-driven CLI visual customization — no code changes needed.\n\n**Customizable Elements:**\n\n| Element                          | Key                      | Used By           |\n| -------------------------------- | ------------------------ | ----------------- |\n| Banner border/title/accent       | `colors.*`               | banner.py         |\n| Response box border              | `colors.response_border` | cli.py            |\n| Spinner faces (waiting/thinking) | `spinner.*`              | display.py        |\n| Spinner verbs/wings              | `spinner.*`              | display.py        |\n| Tool output prefix               | `tool_prefix`            | display.py        |\n| Per-tool emojis                  | `tool_emojis`            | display.py        |\n| Agent name/welcome/prompt        | `branding.*`             | banner.py, cli.py |\n\n**Built-in Skins:** default, ares, mono, slate, poseidon, sisyphus, charizard\n\n**User Skins:** Drop `~/.hermes/skins/<name>.yaml` and activate with `/skin <name>`\n\n---\n\n## 7. Tool System\n\n### 7.1 Terminal Tool (tools/terminal_tool.py)\n\nShell command execution across multiple backends.\n\n```python\ndef terminal_tool(\n    command: str,\n    background: bool = False,\n    timeout: Optional[int] = None,\n    task_id: Optional[str] = None,\n    force: bool = False,        # Skip approval for dangerous commands\n    workdir: Optional[str] = None,\n    check_interval: Optional[int] = None,  # Background task polling\n    pty: bool = False,\n) -> str  # JSON result\n```\n\n**Features:**\n\n- Multi-backend: Selects based on `TERMINAL_ENV` (local/docker/ssh/modal/daytona/singularity)\n- Per-task_id sandboxes with thread-safe creation locks\n- Dangerous command routing through approval system\n- Background task support with file-based IPC\n- Interrupt handling — polls `is_interrupted()` during execution\n- Auto-cleanup daemon thread for idle environments (>300s)\n- Disk usage warnings at configurable threshold\n\n---\n\n### 7.2 File Tools (tools/file_tools.py)\n\nSafe file operations with size guards and sensitive path protection.\n\n- `read_file_tool(path, offset, limit)` — Read with pagination (default 100K char limit)\n- `write_file_tool(path, content)` — Write with approval for sensitive paths\n- `edit_file_tool(path, old_text, new_text)` — String replacement editing\n- `list_files_tool(path)` — Directory listing\n- `search_files_tool(pattern, path)` — Glob/regex file search\n\n**Safety:**\n\n- Device path blocklist (`/dev/zero`, `/dev/stdin`, etc.)\n- Read dedup tracking — returns stub on re-read if mtime unchanged\n- Sensitive path blocking: `/etc/`, `/boot/`, `~/.ssh` without approval\n- Prompt injection protection for known dangerous paths\n\n---\n\n### 7.3 Web Tools (tools/web_tools.py)\n\nWeb search and content extraction.\n\n- `web_search_tool(query, limit)` — Search via configurable backend\n- `web_extract_tool(urls, format)` — Extract content from URLs\n- `web_crawl_tool(url, instructions)` — LLM-guided web crawling\n\n**Backends:** Firecrawl, Parallel Web, Tavily, Exa, DuckDuckGo\n\n- Fallback detection by highest-priority available API key\n- LLM processing via auxiliary client (Gemini 3 Flash) for intelligent extraction\n- SSRF protection and URL safety checks\n\n---\n\n### 7.4 Browser Tool (tools/browser_tool.py)\n\nHeadless browser automation with accessibility tree.\n\n```python\nbrowser_navigate(url, task_id) → str\nbrowser_snapshot(task_id, max_chars) → str\nbrowser_click(ref, task_id) → str      # Element refs like @e1, @e2\nbrowser_type(ref, text, task_id) → str\nbrowser_scroll(ref, direction, task_id) → str\nbrowser_extract(task, max_chars, task_id) → str\n```\n\n**Backends:**\n\n- **Local:** Headless Chromium via `agent-browser` CLI\n- **Cloud:** Browserbase (stealth, proxies, CAPTCHA solving)\n- **Camofox:** Anti-detection browser using Camoufox\n\n**Features:**\n\n- Accessibility tree for text-based snapshots (no vision required)\n- Session isolation per task_id with inactivity cleanup\n- API key detection in URLs (prevents exfiltration)\n- SSRF protection for private/internal addresses\n\n---\n\n### 7.5 Delegate Tool (tools/delegate_tool.py)\n\nSpawn isolated subagents for parallel workstreams.\n\n```python\ndef delegate_task(\n    goal: str,\n    context: str = None,\n    toolsets: List[str] = None,       # Default: [\"terminal\", \"file\", \"web\"]\n    tasks: List[Dict] = None,          # Batch mode: up to 3 concurrent\n    max_iterations: int = 50,\n    parent_agent = None,\n) → str\n```\n\n**Isolation:**\n\n- Fresh conversation (no parent history)\n- Own task_id (separate terminal session, file ops)\n- Restricted toolset (configurable, blocked tools always stripped)\n- Focused system prompt from goal + context\n- Parent only sees delegation call and summary result\n\n**Blocked Tools:** delegate_task, clarify, memory, send_message, execute_code (no recursion, no user interaction)\n\n**Constraints:** MAX_DEPTH=2, MAX_CONCURRENT_CHILDREN=3\n\n---\n\n### 7.6 MCP Tool (tools/mcp_tool.py)\n\nModel Context Protocol client integration.\n\n**Configuration (config.yaml):**\n\n```yaml\nmcp_servers:\n  filesystem:\n    command: \"npx\"\n    args: [\"-y\", \"@modelcontextprotocol/server-filesystem\", \"/tmp\"]\n    timeout: 120\n  remote_api:\n    url: \"https://my-mcp-server.example.com/mcp\"\n    headers:\n      Authorization: \"Bearer sk-...\"\n    sampling:\n      enabled: true\n      model: \"gemini-3-flash\"\n```\n\n**Features:**\n\n- **Transports:** Stdio (local processes) and HTTP/StreamableHTTP (remote)\n- **Dynamic tool discovery:** Listens for `tools/list_changed` notifications\n- **Sampling support:** MCP servers can request LLM completions\n- **Automatic reconnection** with exponential backoff (5 retries)\n- Dedicated background event loop in daemon thread\n- Thread-safe via lock protecting server dict\n\n---\n\n### 7.7 Approval System (tools/approval.py)\n\nDangerous command detection and approval flow.\n\n```python\ndetect_dangerous_command(command: str) → (is_dangerous, pattern_key, description)\n```\n\n**106 patterns covering:**\n\n- Destructive operations (`rm -r /`, `mkfs`, `dd if=`)\n- Privilege escalation (`chmod 777`, `chown -R root`)\n- SQL injection (`DROP TABLE`, `DELETE FROM` without WHERE)\n- System targeting (`/etc/` writes, `systemctl stop`, fork bombs)\n- Shell injection (pipe to sh, wget to sh, process substitution)\n- Network exfiltration (curl with embedded API keys)\n- Secret access (`cat ~/.env`, `cat ~/.netrc`)\n\n**Normalization:** Strips ANSI escapes, null bytes, normalizes Unicode\n\n**Approval Modes:**\n\n- `smart` — Learns which commands are safe based on user decisions\n- `always` — Always ask for dangerous commands\n- `off` — Never ask (or `/yolo` toggle)\n\n---\n\n### 7.8 Terminal Backends (tools/environments/)\n\n| Backend           | File               | Features                                                                       |\n| ----------------- | ------------------ | ------------------------------------------------------------------------------ |\n| **Local**         | `local.py`         | Direct execution, interrupt support, non-blocking I/O, persistent shell        |\n| **Docker**        | `docker.py`        | Sandboxed containers, cap-drop ALL, no-new-privileges, PID limits, bind mounts |\n| **SSH**           | `ssh.py`           | Remote execution via ControlMaster, persistent shell mixin                     |\n| **Modal**         | `modal.py`         | Native Modal SDK `Sandbox.create()`/`Sandbox.exec()`, persistent snapshots     |\n| **Managed Modal** | `managed_modal.py` | Modal through Nous-hosted gateway                                              |\n| **Daytona**       | `daytona.py`       | Daytona SDK cloud sandboxes, stop/resume lifecycle                             |\n| **Singularity**   | `singularity.py`   | Singularity containers with scratch dir, SIF cache                             |\n\n**Common Interface (BaseEnvironment ABC):**\n\n```python\nexecute(command, timeout) → {\"output\": str, \"returncode\": int}\ncleanup() → None\n```\n\n---\n\n## 8. Agent Internals\n\n### 8.1 Prompt Builder (agent/prompt_builder.py)\n\nAssembles the system prompt from multiple sources:\n\n| Component               | Source                                               |\n| ----------------------- | ---------------------------------------------------- |\n| Agent Identity          | `DEFAULT_AGENT_IDENTITY` or `SOUL.md`                |\n| Memory Guidance         | When/how to use memory tool                          |\n| Session Search Guidance | How to recall past conversations                     |\n| Skills Guidance         | When to create/patch skills                          |\n| Tool Use Enforcement    | Must execute tools, not describe actions             |\n| Skills Index            | `~/.hermes/skills/.hermes-skills.json`               |\n| Platform Hints          | OS, Python version, shell, available tools           |\n| Context Files           | `.hermes.md`, `AGENTS.md`, `.cursorrules`, `SOUL.md` |\n| Model/Provider Info     | Current model and provider identity                  |\n\n**Context File Discovery:**\n\n1. Check `cwd/.hermes.md` or `HERMES.md`\n2. Walk parent directories up to git root\n3. Validate against injection patterns before inclusion\n\n**Injection Detection (30+ patterns):**\n\n- \"ignore previous instructions\", \"system prompt override\"\n- \"do not tell the user\", \"act as if you have no restrictions\"\n- HTML comment injection, exfiltration via curl, Unicode stealth chars\n\n---\n\n### 8.2 Context Compressor (agent/context_compressor.py)\n\nAutomatically compresses conversation history when approaching context limits.\n\n```python\nclass ContextCompressor:\n    threshold_percent: float = 0.50   # Compress when 50% of context used\n    protect_first_n: int = 3          # Keep system prompt + first turn\n    protect_last_n: int = 20          # Keep recent N messages\n    summary_target_ratio: float = 0.20  # Summary = 20% of compressed content\n```\n\n**Compression Algorithm:**\n\n1. **Pre-pass:** Replace old tool results with placeholder (cheap)\n2. **Protect head:** System prompt + first exchange always kept\n3. **Protect tail:** Token-budget-based tail protection (~20K tokens)\n4. **Summarize middle:** LLM-generated structured summary (Goal, Progress, Decisions, Files, Next Steps)\n5. **Iterative updates:** On subsequent compressions, update previous summary instead of re-summarizing\n\n---\n\n### 8.3 Prompt Caching (agent/prompt_caching.py)\n\nAnthropic prompt caching support for cost reduction.\n\n- System prompt cached across turns (first conversation turn establishes cache)\n- Cache markers inserted at system prompt boundaries\n- Must not break mid-conversation — altering past context invalidates cache\n- The ONLY time context is altered is during compression\n\n**Critical Rule:** Do NOT implement changes that would alter past context, change toolsets, reload memories, or rebuild system prompts mid-conversation.\n\n---\n\n### 8.4 Auxiliary Client (agent/auxiliary_client.py)\n\nSeparate LLM client for non-primary tasks:\n\n- **Vision analysis** — Image description and analysis\n- **Web extraction** — Content summarization\n- **Context compression** — Generating conversation summaries\n- **Session search** — Summarizing search results\n- **MCP sampling** — Serving server-initiated LLM requests\n\nConfigured per-task via `auxiliary` section in config.yaml.\n\n---\n\n### 8.5 Display & Spinner (agent/display.py)\n\n- **KawaiiSpinner** — Animated face characters during API calls\n- **Tool preview formatting** — `┊` prefixed activity feed for tool execution\n- **Inline diff previews** — Shows unified diffs for write/patch operations\n- Respects active skin for colors, emojis, and branding\n\n---\n\n### 8.6 Skill Commands (agent/skill_commands.py)\n\nShared skill invocation for CLI and gateway:\n\n- Skills loaded from `~/.hermes/skills/` and external directories\n- Injected as **user message** (not system prompt) to preserve prompt caching\n- `/plan` command generates implementation plans stored in `.hermes/plans/`\n- Skill content includes setup instructions, tool options, usage examples\n\n---\n\n## 9. Messaging Gateway\n\n### 9.1 GatewayRunner (gateway/run.py)\n\nMain controller managing all platform adapters and routing messages.\n\n**Key Attributes:**\n\n- `adapters: Dict[Platform, BasePlatformAdapter]` — Active platform instances\n- `session_store: SessionStore` — Conversation persistence\n- `_running_agents: Dict[str, AIAgent]` — Per-session cached agents (preserves prompt caching)\n- `_pending_approvals: Dict[str, Dict]` — Dangerous command approval tracking\n- `pairing_store: PairingStore` — DM code-based user authorization\n\n**Message Flow:**\n\n1. Platform adapter queues `MessageEvent`\n2. GatewayRunner dequeues, calls `_handle_message()`\n3. Slash command? → dispatch to handler\n4. Regular message? → `_handle_message_with_agent","AGENTS.md":"# Before starting work\n\n- Run `lat search` to find sections relevant to your task. Read them to understand the design intent before writing code.\n- Run `lat expand` on user prompts to expand any `[[refs]]` — this resolves section names to file locations and provides context.\n\n# Post-task checklist (REQUIRED — do not skip)\n\nAfter EVERY task, before responding to the user:\n\n- [ ] Update `lat.md/` if you added or changed any functionality, architecture, tests, or behavior\n- [ ] Run `lat check` — all wiki links and code refs must pass\n- [ ] Do not skip these steps. Do not consider your task done until both are complete.\n\n---\n\n# What is lat.md?\n\nThis project uses [lat.md](https://www.npmjs.com/package/lat.md) to maintain a structured knowledge graph of its architecture, design decisions, and test specs in the `lat.md/` directory. It is a set of cross-linked markdown files that describe **what** this project does and **why** — the domain concepts, key design decisions, business logic, and test specifications. Use it to ground your work in the actual architecture rather than guessing.\n\n# Commands\n\n```bash\nlat locate \"Section Name\"      # find a section by name (exact, fuzzy)\nlat refs \"file#Section\"        # find what references a section\nlat search \"natural language\"  # semantic search across all sections\nlat expand \"user prompt text\"  # expand [[refs]] to resolved locations\nlat check                      # validate all links and code refs\n```\n\nRun `lat --help` when in doubt about available commands or options.\n\nIf `lat search` fails because no API key is configured, explain to the user that semantic search requires a key provided via `LAT_LLM_KEY` (direct value), `LAT_LLM_KEY_FILE` (path to key file), or `LAT_LLM_KEY_HELPER` (command that prints the key). Supported key prefixes: `sk-...` (OpenAI) or `vck_...` (Vercel). If the user doesn't want to set it up, use `lat locate` for direct lookups instead.\n\n# Syntax primer\n\n- **Section ids**: `lat.md/path/to/file#Heading#SubHeading` — full form uses project-root-relative path (e.g. `lat.md/tests/search#RAG Replay Tests`). Short form uses bare file name when unique (e.g. `search#RAG Replay Tests`, `cli#search#Indexing`).\n- **Wiki links**: `[[target]]` or `[[target|alias]]` — cross-references between sections. Can also reference source code: `[[src/foo.ts#myFunction]]`.\n- **Source code links**: Wiki links in `lat.md/` files can reference functions, classes, constants, and methods in TypeScript/JavaScript/Python/Rust/Go/C files. Use the full path: `[[src/config.ts#getConfigDir]]`, `[[src/server.ts#App#listen]]` (class method), `[[lib/utils.py#parse_args]]`, `[[src/lib.rs#Greeter#greet]]` (Rust impl method), `[[src/app.go#Greeter#Greet]]` (Go method), `[[src/app.h#Greeter]]` (C struct). `lat check` validates these exist.\n- **Code refs**: `// @lat: [[section-id]]` (JS/TS/Rust/Go/C) or `# @lat: [[section-id]]` (Python) — ties source code to concepts\n\n# Test specs\n\nKey tests can be described as sections in `lat.md/` files (e.g. `tests.md`). Add frontmatter to require that every leaf section is referenced by a `// @lat:` or `# @lat:` comment in test code:\n\n```markdown\n---\nlat:\n  require-code-mention: true\n---\n# Tests\n\nAuthentication and authorization test specifications.\n\n## User login\n\nVerify credential validation and error handling for the login endpoint.\n\n### Rejects expired tokens\nTokens past their expiry timestamp are rejected with 401, even if otherwise valid.\n\n### Handles missing password\nLogin request without a password field returns 400 with a descriptive error.\n```\n\nEvery section MUST have a description — at least one sentence explaining what the test verifies and why. Empty sections with just a heading are not acceptable. (This is a specific case of the general leading paragraph rule below.)\n\nEach test in code should reference its spec with exactly one comment placed next to the relevant test — not at the top of the file:\n\n```python\n# @lat: [[tests#User login#Rejects expired tokens]]\ndef test_rejects_expired_tokens():\n    ...\n\n# @lat: [[tests#User login#Handles missing password]]\ndef test_handles_missing_password():\n    ...\n```\n\nDo not duplicate refs. One `@lat:` comment per spec section, placed at the test that covers it. `lat check` will flag any spec section not covered by a code reference, and any code reference pointing to a nonexistent section.\n\n# Section structure\n\nEvery section in `lat.md/` **must** have a leading paragraph — at least one sentence immediately after the heading, before any child headings or other block content. The first paragraph must be ≤250 characters (excluding `[[wiki link]]` content). This paragraph serves as the section's overview and is used in search results, command output, and RAG context — keeping it concise guarantees the section's essence is always captured.\n\n```markdown\n# Good Section\n\nBrief overview of what this section documents and why it matters.\n\nMore detail can go in subsequent paragraphs, code blocks, or lists.\n\n## Child heading\n\nDetails about this child topic.\n```\n\n```markdown\n# Bad Section\n\n## Child heading\n\nDetails about this child topic.\n```\n\nThe second example is invalid because `Bad Section` has no leading paragraph. `lat check` validates this rule and reports errors for missing or overly long leading paragraphs.\n",".agents/skills/electron-pro/SKILL.md":"---\nname: electron-pro\ndescription: Expert in building cross-platform desktop applications using web technologies (HTML/CSS/JS) with the Electron framework.\n---\n\n# Electron Desktop Developer\n\n## Purpose\n\nProvides cross-platform desktop application development expertise specializing in Electron, IPC architecture, and OS-level integration. Builds secure, performant desktop applications using web technologies with native capabilities for Windows, macOS, and Linux.\n\n## When to Use\n\n- Building cross-platform desktop apps (VS Code, Discord style)\n- Migrating web apps to desktop with native capabilities (File system, Notifications)\n- Implementing secure IPC (Main ↔ Renderer communication)\n- Optimizing Electron memory usage and startup time\n- Configuring auto-updaters (electron-updater)\n- Signing and notarizing apps for app stores\n\n---\n\n---\n\n## 2. Decision Framework\n\n### Architecture Selection\n\n```\nHow to structure the app?\n│\n├─ **Security First (Recommended)**\n│  ├─ Context Isolation? → **Yes** (Standard since v12)\n│  ├─ Node Integration? → **No** (Never in Renderer)\n│  └─ Preload Scripts? → **Yes** (Bridge API)\n│\n├─ **Data Persistence**\n│  ├─ Simple Settings? → **electron-store** (JSON)\n│  ├─ Large Datasets? → **SQLite** (`better-sqlite3` in Main process)\n│  └─ User Files? → **Native File System API**\n│\n└─ **UI Framework**\n   ├─ React/Vue/Svelte? → **Yes** (Standard SPA approach)\n   ├─ Multiple Windows? → **Window Manager Pattern**\n   └─ System Tray App? → **Hidden Window Pattern**\n```\n\n### IPC Communication Patterns\n\n| Pattern                        | Method               | Use Case                                        |\n| ------------------------------ | -------------------- | ----------------------------------------------- |\n| **One-Way (Renderer → Main)**  | `ipcRenderer.send`   | logging, analytics, minimizing window           |\n| **Two-Way (Request/Response)** | `ipcRenderer.invoke` | DB queries, file reads, heavy computations      |\n| **Main → Renderer**            | `webContents.send`   | Menu actions, system events, push notifications |\n\n**Red Flags → Escalate to `security-auditor`:**\n\n- Enabling `nodeIntegration: true` in production\n- Disabling `contextIsolation`\n- Loading remote content (`https://`) without strict CSP\n- Using `remote` module (Deprecated & insecure)\n\n---\n\n---\n\n### Workflow 2: Performance Optimization (Startup)\n\n**Goal:** Reduce launch time to < 2s.\n\n**Steps:**\n\n1.  **V8 Snapshot**\n    - Use `electron-link` or `v8-compile-cache` to pre-compile JS.\n\n2.  **Lazy Loading Modules**\n    - Don't `require()` everything at top of `main.ts`.\n\n    ```javascript\n    // Bad\n    import { heavyLib } from \"heavy-lib\";\n\n    // Good\n    ipcMain.handle(\"do-work\", () => {\n      const heavyLib = require(\"heavy-lib\");\n      heavyLib.process();\n    });\n    ```\n\n3.  **Bundle Main Process**\n    - Use `esbuild` or `webpack` for Main process (not just Renderer) to tree-shake unused code and minify.\n\n---\n\n---\n\n## 4. Patterns & Templates\n\n### Pattern 1: Worker Threads (CPU Intensive Tasks)\n\n**Use case:** Image processing or parsing large files without freezing the UI.\n\n```typescript\n// main.ts\nimport { Worker } from \"worker_threads\";\n\nipcMain.handle(\"process-image\", (event, data) => {\n  return new Promise((resolve, reject) => {\n    const worker = new Worker(\"./worker.js\", { workerData: data });\n    worker.on(\"message\", resolve);\n    worker.on(\"error\", reject);\n  });\n});\n```\n\n### Pattern 2: Deep Linking (Protocol Handler)\n\n**Use case:** Opening app from browser (`myapp://open?id=123`).\n\n```typescript\n// main.ts\nif (process.defaultApp) {\n  if (process.argv.length >= 2) {\n    app.setAsDefaultProtocolClient(\"myapp\", process.execPath, [\n      path.resolve(process.argv[1]),\n    ]);\n  }\n} else {\n  app.setAsDefaultProtocolClient(\"myapp\");\n}\n\napp.on(\"open-url\", (event, url) => {\n  event.preventDefault();\n  // Parse url 'myapp://...' and navigate renderer\n  mainWindow.webContents.send(\"navigate\", url);\n});\n```\n\n---\n\n---\n\n## 6. Integration Patterns\n\n### **frontend-ui-ux-engineer:**\n\n- **Handoff**: UI Dev builds the React/Vue app → Electron Dev wraps it.\n- **Collaboration**: Handling window controls (custom title bar), vibrancy/acrylic effects.\n- **Tools**: CSS `app-region: drag`.\n\n### **devops-engineer:**\n\n- **Handoff**: Electron Dev provides build config → DevOps sets up CI pipeline.\n- **Collaboration**: Code signing certificates (Apple Developer ID, Windows EV).\n- **Tools**: Electron Builder, Notarization scripts.\n\n### **security-engineer:**\n\n- **Handoff**: Electron Dev implements feature → Security Dev audits IPC surface.\n- **Collaboration**: Defining Content Security Policy (CSP) headers.\n- **Tools**: Electronegativity (Scanner).\n\n---\n",".claude/skills/lat-md/SKILL.md":"---\nname: lat-md\ndescription: >-\n  Writing and maintaining lat.md documentation files — structured markdown that\n  describes a project's architecture, design decisions, and test specs. Use when\n  creating, editing, or reviewing files in the lat.md/ directory.\n---\n\n# lat.md Authoring Guide\n\nThis skill covers the syntax, structure rules, and conventions for writing `lat.md/` files. Load it whenever you need to create or edit sections in the `lat.md/` directory.\n\n## What belongs in lat.md\n\n`lat.md/` files describe **what** the project does and **why** — domain concepts, key design decisions, business logic, and test specifications. They do NOT duplicate source code. Think of each section as an anchor that source code references back to.\n\nGood candidates for sections:\n- Architecture decisions and their rationale\n- Domain concepts and business rules\n- API contracts and protocols\n- Test specifications (what is tested and why)\n- Non-obvious constraints or invariants\n\nBad candidates:\n- Step-by-step code walkthroughs (the code itself is the walkthrough)\n- Auto-generated API docs (use tools for that)\n- Temporary notes or TODOs\n\n## Section structure\n\nEvery section **must** have a leading paragraph — at least one sentence immediately after the heading, before any child headings or other block content.\n\nThe first paragraph must be ≤250 characters (excluding `[[wiki link]]` content). This paragraph is the section's identity — it appears in search results, command output, and RAG context.\n\n```markdown\n# Good Section\n\nBrief overview of what this section documents and why it matters.\n\nMore detail can go in subsequent paragraphs, code blocks, or lists.\n\n## Child heading\n\nDetails about this child topic.\n```\n\n```markdown\n# Bad Section\n\n## Child heading\n\nThis is invalid — \"Bad Section\" has no leading paragraph.\n```\n\n`lat check` enforces this rule.\n\n## Section IDs\n\nSections are addressed by file path and heading chain:\n\n- **Full form**: `lat.md/path/to/file#Heading#SubHeading`\n- **Short form**: `file#Heading#SubHeading` (when the file stem is unique)\n\nExamples: `lat.md/tests/search#RAG Replay Tests`, `cli#init`, `parser#Wiki Links`.\n\n## Wiki links\n\nCross-reference other sections or source code with `[[target]]` or `[[target|alias]]`.\n\n### Section links\n\n```markdown\nSee [[cli#init]] for setup details.\nThe parser validates [[parser#Wiki Links|wiki link syntax]].\n```\n\n### Source code links\n\nReference functions, classes, constants, and methods in source files:\n\n```markdown\n[[src/config.ts#getConfigDir]]          — function\n[[src/server.ts#App#listen]]            — class method\n[[lib/utils.py#parse_args]]             — Python function\n[[src/lib.rs#Greeter#greet]]            — Rust impl method\n[[src/app.go#Greeter#Greet]]            — Go method\n[[src/app.h#Greeter]]                   — C struct\n```\n\n`lat check` validates that all targets exist.\n\n## Code refs\n\nTie source code back to `lat.md/` sections with `@lat:` comments:\n\n```typescript\n// @lat: [[cli#init]]\nexport function init() { ... }\n```\n\n```python\n# @lat: [[cli#init]]\ndef init():\n    ...\n```\n\nSupported comment styles: `//` (JS/TS/Rust/Go/C) and `#` (Python).\n\nPlace one `@lat:` comment per section, at the relevant code — not at the top of the file.\n\n## Test specs\n\nDescribe tests as sections in `lat.md/` files. Add frontmatter to require that every leaf section has a matching `@lat:` comment in test code:\n\n```markdown\n---\nlat:\n  require-code-mention: true\n---\n# Tests\n\nAuthentication test specifications.\n\n## User login\n\nVerify credential validation and error handling.\n\n### Rejects expired tokens\n\nTokens past their expiry timestamp are rejected with 401, even if otherwise valid.\n\n### Handles missing password\n\nLogin request without a password field returns 400 with a descriptive error.\n```\n\nEach test references its spec:\n\n```python\n# @lat: [[tests#User login#Rejects expired tokens]]\ndef test_rejects_expired_tokens():\n    ...\n```\n\nRules:\n- Every leaf section under `require-code-mention: true` must be referenced by exactly one `@lat:` comment\n- Every section MUST have a description — at least one sentence explaining what the test verifies and why\n- `lat check` flags unreferenced specs and dangling code refs\n\n## Frontmatter\n\nOptional YAML frontmatter at the top of `lat.md/` files:\n\n```yaml\n---\nlat:\n  require-code-mention: true\n---\n```\n\nCurrently the only supported field is `require-code-mention` for test spec enforcement.\n\n## Validation\n\nAlways run `lat check` after editing `lat.md/` files. It validates:\n- All wiki links point to existing sections or source code symbols\n- All `@lat:` code refs point to existing sections\n- Every section has a leading paragraph (≤250 chars)\n- All `require-code-mention` leaf sections are referenced in code\n",".agents/skills/lat-md/SKILL.md":"---\nname: lat-md\ndescription: >-\n  Writing and maintaining lat.md documentation files — structured markdown that\n  describes a project's architecture, design decisions, and test specs. Use when\n  creating, editing, or reviewing files in the lat.md/ directory.\n---\n\n# lat.md Authoring Guide\n\nThis skill covers the syntax, structure rules, and conventions for writing `lat.md/` files. Load it whenever you need to create or edit sections in the `lat.md/` directory.\n\n## What belongs in lat.md\n\n`lat.md/` files describe **what** the project does and **why** — domain concepts, key design decisions, business logic, and test specifications. They do NOT duplicate source code. Think of each section as an anchor that source code references back to.\n\nGood candidates for sections:\n- Architecture decisions and their rationale\n- Domain concepts and business rules\n- API contracts and protocols\n- Test specifications (what is tested and why)\n- Non-obvious constraints or invariants\n\nBad candidates:\n- Step-by-step code walkthroughs (the code itself is the walkthrough)\n- Auto-generated API docs (use tools for that)\n- Temporary notes or TODOs\n\n## Section structure\n\nEvery section **must** have a leading paragraph — at least one sentence immediately after the heading, before any child headings or other block content.\n\nThe first paragraph must be ≤250 characters (excluding `[[wiki link]]` content). This paragraph is the section's identity — it appears in search results, command output, and RAG context.\n\n```markdown\n# Good Section\n\nBrief overview of what this section documents and why it matters.\n\nMore detail can go in subsequent paragraphs, code blocks, or lists.\n\n## Child heading\n\nDetails about this child topic.\n```\n\n```markdown\n# Bad Section\n\n## Child heading\n\nThis is invalid — \"Bad Section\" has no leading paragraph.\n```\n\n`lat check` enforces this rule.\n\n## Section IDs\n\nSections are addressed by file path and heading chain:\n\n- **Full form**: `lat.md/path/to/file#Heading#SubHeading`\n- **Short form**: `file#Heading#SubHeading` (when the file stem is unique)\n\nExamples: `lat.md/tests/search#RAG Replay Tests`, `cli#init`, `parser#Wiki Links`.\n\n## Wiki links\n\nCross-reference other sections or source code with `[[target]]` or `[[target|alias]]`.\n\n### Section links\n\n```markdown\nSee [[cli#init]] for setup details.\nThe parser validates [[parser#Wiki Links|wiki link syntax]].\n```\n\n### Source code links\n\nReference functions, classes, constants, and methods in source files:\n\n```markdown\n[[src/config.ts#getConfigDir]]          — function\n[[src/server.ts#App#listen]]            — class method\n[[lib/utils.py#parse_args]]             — Python function\n[[src/lib.rs#Greeter#greet]]            — Rust impl method\n[[src/app.go#Greeter#Greet]]            — Go method\n[[src/app.h#Greeter]]                   — C struct\n```\n\n`lat check` validates that all targets exist.\n\n## Code refs\n\nTie source code back to `lat.md/` sections with `@lat:` comments:\n\n```typescript\n// @lat: [[cli#init]]\nexport function init() { ... }\n```\n\n```python\n# @lat: [[cli#init]]\ndef init():\n    ...\n```\n\nSupported comment styles: `//` (JS/TS/Rust/Go/C) and `#` (Python).\n\nPlace one `@lat:` comment per section, at the relevant code — not at the top of the file.\n\n## Test specs\n\nDescribe tests as sections in `lat.md/` files. Add frontmatter to require that every leaf section has a matching `@lat:` comment in test code:\n\n```markdown\n---\nlat:\n  require-code-mention: true\n---\n# Tests\n\nAuthentication test specifications.\n\n## User login\n\nVerify credential validation and error handling.\n\n### Rejects expired tokens\n\nTokens past their expiry timestamp are rejected with 401, even if otherwise valid.\n\n### Handles missing password\n\nLogin request without a password field returns 400 with a descriptive error.\n```\n\nEach test references its spec:\n\n```python\n# @lat: [[tests#User login#Rejects expired tokens]]\ndef test_rejects_expired_tokens():\n    ...\n```\n\nRules:\n- Every leaf section under `require-code-mention: true` must be referenced by exactly one `@lat:` comment\n- Every section MUST have a description — at least one sentence explaining what the test verifies and why\n- `lat check` flags unreferenced specs and dangling code refs\n\n## Frontmatter\n\nOptional YAML frontmatter at the top of `lat.md/` files:\n\n```yaml\n---\nlat:\n  require-code-mention: true\n---\n```\n\nCurrently the only supported field is `require-code-mention` for test spec enforcement.\n\n## Validation\n\nAlways run `lat check` after editing `lat.md/` files. It validates:\n- All wiki links point to existing sections or source code symbols\n- All `@lat:` code refs point to existing sections\n- Every section has a leading paragraph (≤250 chars)\n- All `require-code-mention` leaf sections are referenced in code\n",".agents/skills/typescript-expert/SKILL.md":"---\nname: typescript-expert\ndescription: TypeScript and JavaScript expert with deep knowledge of type-level programming, performance optimization, monorepo management, migration strategies, and modern tooling.\ncategory: framework\nrisk: critical\nsource: community\ndate_added: \"2026-02-27\"\n---\n\n# TypeScript Expert\n\nYou are an advanced TypeScript expert with deep, practical knowledge of type-level programming, performance optimization, and real-world problem solving based on current best practices.\n\n## When invoked:\n\n0. If the issue requires ultra-specific expertise, recommend switching and stop:\n   - Deep webpack/vite/rollup bundler internals → typescript-build-expert\n   - Complex ESM/CJS migration or circular dependency analysis → typescript-module-expert\n   - Type performance profiling or compiler internals → typescript-type-expert\n\n   Example to output:\n   \"This requires deep bundler expertise. Please invoke: 'Use the typescript-build-expert subagent.' Stopping here.\"\n\n1. Analyze project setup comprehensively:\n\n   **Use internal tools first (Read, Grep, Glob) for better performance. Shell commands are fallbacks.**\n\n   ```bash\n   # Core versions and configuration\n   npx tsc --version\n   node -v\n   # Detect tooling ecosystem (prefer parsing package.json)\n   node -e \"const p=require('./package.json');console.log(Object.keys({...p.devDependencies,...p.dependencies}||{}).join('\\n'))\" 2>/dev/null | grep -E 'biome|eslint|prettier|vitest|jest|turborepo|nx' || echo \"No tooling detected\"\n   # Check for monorepo (fixed precedence)\n   (test -f pnpm-workspace.yaml || test -f lerna.json || test -f nx.json || test -f turbo.json) && echo \"Monorepo detected\"\n   ```\n\n   **After detection, adapt approach:**\n   - Match import style (absolute vs relative)\n   - Respect existing baseUrl/paths configuration\n   - Prefer existing project scripts over raw tools\n   - In monorepos, consider project references before broad tsconfig changes\n\n2. Identify the specific problem category and complexity level\n\n3. Apply the appropriate solution strategy from my expertise\n\n4. Validate thoroughly:\n\n   ```bash\n   # Fast fail approach (avoid long-lived processes)\n   npm run -s typecheck || npx tsc --noEmit\n   npm test -s || npx vitest run --reporter=basic --no-watch\n   # Only if needed and build affects outputs/config\n   npm run -s build\n   ```\n\n   **Safety note:** Avoid watch/serve processes in validation. Use one-shot diagnostics only.\n\n## Advanced Type System Expertise\n\n### Type-Level Programming Patterns\n\n**Branded Types for Domain Modeling**\n\n```typescript\n// Create nominal types to prevent primitive obsession\ntype Brand<K, T> = K & { __brand: T };\ntype UserId = Brand<string, \"UserId\">;\ntype OrderId = Brand<string, \"OrderId\">;\n\n// Prevents accidental mixing of domain primitives\nfunction processOrder(orderId: OrderId, userId: UserId) {}\n```\n\n- Use for: Critical domain primitives, API boundaries, currency/units\n- Resource: https://egghead.io/blog/using-branded-types-in-typescript\n\n**Advanced Conditional Types**\n\n```typescript\n// Recursive type manipulation\ntype DeepReadonly<T> = T extends (...args: any[]) => any\n  ? T\n  : T extends object\n    ? { readonly [K in keyof T]: DeepReadonly<T[K]> }\n    : T;\n\n// Template literal type magic\ntype PropEventSource<Type> = {\n  on<Key extends string & keyof Type>(\n    eventName: `${Key}Changed`,\n    callback: (newValue: Type[Key]) => void,\n  ): void;\n};\n```\n\n- Use for: Library APIs, type-safe event systems, compile-time validation\n- Watch for: Type instantiation depth errors (limit recursion to 10 levels)\n\n**Type Inference Techniques**\n\n```typescript\n// Use 'satisfies' for constraint validation (TS 5.0+)\nconst config = {\n  api: \"https://api.example.com\",\n  timeout: 5000,\n} satisfies Record<string, string | number>;\n// Preserves literal types while ensuring constraints\n\n// Const assertions for maximum inference\nconst routes = [\"/home\", \"/about\", \"/contact\"] as const;\ntype Route = (typeof routes)[number]; // '/home' | '/about' | '/contact'\n```\n\n### Performance Optimization Strategies\n\n**Type Checking Performance**\n\n```bash\n# Diagnose slow type checking\nnpx tsc --extendedDiagnostics --incremental false | grep -E \"Check time|Files:|Lines:|Nodes:\"\n\n# Common fixes for \"Type instantiation is excessively deep\"\n# 1. Replace type intersections with interfaces\n# 2. Split large union types (>100 members)\n# 3. Avoid circular generic constraints\n# 4. Use type aliases to break recursion\n```\n\n**Build Performance Patterns**\n\n- Enable `skipLibCheck: true` for library type checking only (often significantly improves performance on large projects, but avoid masking app typing issues)\n- Use `incremental: true` with `.tsbuildinfo` cache\n- Configure `include`/`exclude` precisely\n- For monorepos: Use project references with `composite: true`\n\n## Real-World Problem Resolution\n\n### Complex Error Patterns\n\n**\"The inferred type of X cannot be named\"**\n\n- Cause: Missing type export or circular dependency\n- Fix priority:\n  1. Export the required type explicitly\n  2. Use `ReturnType<typeof function>` helper\n  3. Break circular dependencies with type-only imports\n- Resource: https://github.com/microsoft/TypeScript/issues/47663\n\n**Missing type declarations**\n\n- Quick fix with ambient declarations:\n\n```typescript\n// types/ambient.d.ts\ndeclare module \"some-untyped-package\" {\n  const value: unknown;\n  export default value;\n  export = value; // if CJS interop is needed\n}\n```\n\n- For more details: [Declaration Files Guide](https://www.typescriptlang.org/docs/handbook/declaration-files/introduction.html)\n\n**\"Excessive stack depth comparing types\"**\n\n- Cause: Circular or deeply recursive types\n- Fix priority:\n  1. Limit recursion depth with conditional types\n  2. Use `interface` extends instead of type intersection\n  3. Simplify generic constraints\n\n```typescript\n// Bad: Infinite recursion\ntype InfiniteArray<T> = T | InfiniteArray<T>[];\n\n// Good: Limited recursion\ntype NestedArray<T, D extends number = 5> = D extends 0\n  ? T\n  : T | NestedArray<T, [-1, 0, 1, 2, 3, 4][D]>[];\n```\n\n**Module Resolution Mysteries**\n\n- \"Cannot find module\" despite file existing:\n  1. Check `moduleResolution` matches your bundler\n  2. Verify `baseUrl` and `paths` alignment\n  3. For monorepos: Ensure workspace protocol (workspace:\\*)\n  4. Try clearing cache: `rm -rf node_modules/.cache .tsbuildinfo`\n\n**Path Mapping at Runtime**\n\n- TypeScript paths only work at compile time, not runtime\n- Node.js runtime solutions:\n  - ts-node: Use `ts-node -r tsconfig-paths/register`\n  - Node ESM: Use loader alternatives or avoid TS paths at runtime\n  - Production: Pre-compile with resolved paths\n\n### Migration Expertise\n\n**JavaScript to TypeScript Migration**\n\n```bash\n# Incremental migration strategy\n# 1. Enable allowJs and checkJs (merge into existing tsconfig.json):\n# Add to existing tsconfig.json:\n# {\n#   \"compilerOptions\": {\n#     \"allowJs\": true,\n#     \"checkJs\": true\n#   }\n# }\n\n# 2. Rename files gradually (.js → .ts)\n# 3. Add types file by file using AI assistance\n# 4. Enable strict mode features one by one\n\n# Automated helpers (if installed/needed)\ncommand -v ts-migrate >/dev/null 2>&1 && npx ts-migrate migrate . --sources 'src/**/*.js'\ncommand -v typesync >/dev/null 2>&1 && npx typesync  # Install missing @types packages\n```\n\n**Tool Migration Decisions**\n\n| From              | To              | When                                          | Migration Effort  |\n| ----------------- | --------------- | --------------------------------------------- | ----------------- |\n| ESLint + Prettier | Biome           | Need much faster speed, okay with fewer rules | Low (1 day)       |\n| TSC for linting   | Type-check only | Have 100+ files, need faster feedback         | Medium (2-3 days) |\n| Lerna             | Nx/Turborepo    | Need caching, parallel builds                 | High (1 week)     |\n| CJS               | ESM             | Node 18+, modern tooling                      | High (varies)     |\n\n### Monorepo Management\n\n**Nx vs Turborepo Decision Matrix**\n\n- Choose **Turborepo** if: Simple structure, need speed, <20 packages\n- Choose **Nx** if: Complex dependencies, need visualization, plugins required\n- Performance: Nx often performs better on large monorepos (>50 packages)\n\n**TypeScript Monorepo Configuration**\n\n```json\n// Root tsconfig.json\n{\n  \"references\": [\n    { \"path\": \"./packages/core\" },\n    { \"path\": \"./packages/ui\" },\n    { \"path\": \"./apps/web\" }\n  ],\n  \"compilerOptions\": {\n    \"composite\": true,\n    \"declaration\": true,\n    \"declarationMap\": true\n  }\n}\n```\n\n## Modern Tooling Expertise\n\n### Biome vs ESLint\n\n**Use Biome when:**\n\n- Speed is critical (often faster than traditional setups)\n- Want single tool for lint + format\n- TypeScript-first project\n- Okay with 64 TS rules vs 100+ in typescript-eslint\n\n**Stay with ESLint when:**\n\n- Need specific rules/plugins\n- Have complex custom rules\n- Working with Vue/Angular (limited Biome support)\n- Need type-aware linting (Biome doesn't have this yet)\n\n### Type Testing Strategies\n\n**Vitest Type Testing (Recommended)**\n\n```typescript\n// in avatar.test-d.ts\nimport { expectTypeOf } from \"vitest\";\nimport type { Avatar } from \"./avatar\";\n\ntest(\"Avatar props are correctly typed\", () => {\n  expectTypeOf<Avatar>().toHaveProperty(\"size\");\n  expectTypeOf<Avatar[\"size\"]>().toEqualTypeOf<\"sm\" | \"md\" | \"lg\">();\n});\n```\n\n**When to Test Types:**\n\n- Publishing libraries\n- Complex generic functions\n- Type-level utilities\n- API contracts\n\n## Debugging Mastery\n\n### CLI Debugging Tools\n\n```bash\n# Debug TypeScript files directly (if tools installed)\ncommand -v tsx >/dev/null 2>&1 && npx tsx --inspect src/file.ts\ncommand -v ts-node >/dev/null 2>&1 && npx ts-node --inspect-brk src/file.ts\n\n# Trace module resolution issues\nnpx tsc --traceResolution > resolution.log 2>&1\ngrep \"Module resolution\" resolution.log\n\n# Debug type checking performance (use --incremental false for clean trace)\nnpx tsc --generateTrace trace --incremental false\n# Analyze trace (if installed)\ncommand -v @typescript/analyze-trace >/dev/null 2>&1 && npx @typescript/analyze-trace trace\n\n# Memory usage analysis\nnode --max-old-space-size=8192 node_modules/typescript/lib/tsc.js\n```\n\n### Custom Error Classes\n\n```typescript\n// Proper error class with stack preservation\nclass DomainError extends Error {\n  constructor(\n    message: string,\n    public code: string,\n    public statusCode: number,\n  ) {\n    super(message);\n    this.name = \"DomainError\";\n    Error.captureStackTrace(this, this.constructor);\n  }\n}\n```\n\n## Current Best Practices\n\n### Strict by Default\n\n```json\n{\n  \"compilerOptions\": {\n    \"strict\": true,\n    \"noUncheckedIndexedAccess\": true,\n    \"noImplicitOverride\": true,\n    \"exactOptionalPropertyTypes\": true,\n    \"noPropertyAccessFromIndexSignature\": true\n  }\n}\n```\n\n### ESM-First Approach\n\n- Set `\"type\": \"module\"` in package.json\n- Use `.mts` for TypeScript ESM files if needed\n- Configure `\"moduleResolution\": \"bundler\"` for modern tools\n- Use dynamic imports for CJS: `const pkg = await import('cjs-package')`\n  - Note: `await import()` requires async function or top-level await in ESM\n  - For CJS packages in ESM: May need `(await import('pkg')).default` depending on the package's export structure and your compiler settings\n\n### AI-Assisted Development\n\n- GitHub Copilot excels at TypeScript generics\n- Use AI for boilerplate type definitions\n- Validate AI-generated types with type tests\n- Document complex types for AI context\n\n## Code Review Checklist\n\nWhen reviewing TypeScript/JavaScript code, focus on these domain-specific aspects:\n\n### Type Safety\n\n- [ ] No implicit `any` types (use `unknown` or proper types)\n- [ ] Strict null checks enabled and properly handled\n- [ ] Type assertions (`as`) justified and minimal\n- [ ] Generic constraints properly defined\n- [ ] Discriminated unions for error handling\n- [ ] Return types explicitly declared for public APIs\n\n### TypeScript Best Practices\n\n- [ ] Prefer `interface` over `type` for object shapes (better error messages)\n- [ ] Use const assertions for literal types\n- [ ] Leverage type guards and predicates\n- [ ] Avoid type gymnastics when simpler solution exists\n- [ ] Template literal types used appropriately\n- [ ] Branded types for domain primitives\n\n### Performance Considerations\n\n- [ ] Type complexity doesn't cause slow compilation\n- [ ] No excessive type instantiation depth\n- [ ] Avoid complex mapped types in hot paths\n- [ ] Use `skipLibCheck: true` in tsconfig\n- [ ] Project references configured for monorepos\n\n### Module System\n\n- [ ] Consistent import/export patterns\n- [ ] No circular dependencies\n- [ ] Proper use of barrel exports (avoid over-bundling)\n- [ ] ESM/CJS compatibility handled correctly\n- [ ] Dynamic imports for code splitting\n\n### Error Handling Patterns\n\n- [ ] Result types or discriminated unions for errors\n- [ ] Custom error classes with proper inheritance\n- [ ] Type-safe error boundaries\n- [ ] Exhaustive switch cases with `never` type\n\n### Code Organization\n\n- [ ] Types co-located with implementation\n- [ ] Shared types in dedicated modules\n- [ ] Avoid global type augmentation when possible\n- [ ] Proper use of declaration files (.d.ts)\n\n## Quick Decision Trees\n\n### \"Which tool should I use?\"\n\n```\nType checking only? → tsc\nType checking + linting speed critical? → Biome\nType checking + comprehensive linting? → ESLint + typescript-eslint\nType testing? → Vitest expectTypeOf\nBuild tool? → Project size <10 packages? Turborepo. Else? Nx\n```\n\n### \"How do I fix this performance issue?\"\n\n```\nSlow type checking? → skipLibCheck, incremental, project references\nSlow builds? → Check bundler config, enable caching\nSlow tests? → Vitest with threads, avoid type checking in tests\nSlow language server? → Exclude node_modules, limit files in tsconfig\n```\n\n## Expert Resources\n\n### Performance\n\n- [TypeScript Wiki Performance](https://github.com/microsoft/TypeScript/wiki/Performance)\n- [Type instantiation tracking](https://github.com/microsoft/TypeScript/pull/48077)\n\n### Advanced Patterns\n\n- [Type Challenges](https://github.com/type-challenges/type-challenges)\n- [Type-Level TypeScript Course](https://type-level-typescript.com)\n\n### Tools\n\n- [Biome](https://biomejs.dev) - Fast linter/formatter\n- [TypeStat](https://github.com/JoshuaKGoldberg/TypeStat) - Auto-fix TypeScript types\n- [ts-migrate](https://github.com/airbnb/ts-migrate) - Migration toolkit\n\n### Testing\n\n- [Vitest Type Testing](https://vitest.dev/guide/testing-types)\n- [tsd](https://github.com/tsdjs/tsd) - Standalone type testing\n\nAlways validate changes don't break existing functionality before considering the issue resolved.\n\n## When to Use\n\nThis skill is applicable to execute the workflow or actions described in the overview.\n",".agents/skills/hermes-agent/SKILL.md":"---\nname: hermes-agent\ndescription: Expert in building self-improving AI agents with tool use, multi-platform messaging, and a closed learning loop. Proficient in LLM orchestration, tool integration, session management, and agent autonomy.\n---\n\n# Hermes Agent - Complete Project Guide (A-Z)\n\n> **Purpose of this document:** A single, comprehensive reference that explains everything about the Hermes Agent project — its architecture, source code, features, release history, and design patterns — so that any AI or developer can fully understand the system.\n\n---\n\n## Table of Contents\n\n1. [Project Overview](#1-project-overview)\n2. [Key Features Summary](#2-key-features-summary)\n3. [Installation & Getting Started](#3-installation--getting-started)\n4. [Project Structure](#4-project-structure)\n5. [Core Architecture](#5-core-architecture)\n   - 5.1 [AIAgent Class (run_agent.py)](#51-aiagent-class-run_agentpy)\n   - 5.2 [Tool Orchestration (model_tools.py)](#52-tool-orchestration-model_toolspy)\n   - 5.3 [Toolset System (toolsets.py)](#53-toolset-system-toolsetspy)\n   - 5.4 [Tool Registry (tools/registry.py)](#54-tool-registry-toolsregistrypy)\n   - 5.5 [Session Database (hermes_state.py)](#55-session-database-hermes_statepy)\n   - 5.6 [Constants & Home Directory (hermes_constants.py)](#56-constants--home-directory-hermes_constantspy)\n6. [CLI System](#6-cli-system)\n   - 6.1 [Interactive CLI (cli.py)](#61-interactive-cli-clipy)\n   - 6.2 [CLI Entry Point (hermes_cli/main.py)](#62-cli-entry-point-hermes_climainpy)\n   - 6.3 [Configuration System (hermes_cli/config.py)](#63-configuration-system-hermes_cliconfigpy)\n   - 6.4 [Slash Command Registry (hermes_cli/commands.py)](#64-slash-command-registry-hermes_clicommandspy)\n   - 6.5 [Setup Wizard (hermes_cli/setup.py)](#65-setup-wizard-hermes_clisetupy)\n   - 6.6 [Model Catalog (hermes_cli/models.py)](#66-model-catalog-hermes_climodelspy)\n   - 6.7 [Skin/Theme Engine (hermes_cli/skin_engine.py)](#67-skintheme-engine-hermes_cliskin_enginepy)\n7. [Tool System](#7-tool-system)\n   - 7.1 [Terminal Tool (tools/terminal_tool.py)](#71-terminal-tool-toolsterminal_toolpy)\n   - 7.2 [File Tools (tools/file_tools.py)](#72-file-tools-toolsfile_toolspy)\n   - 7.3 [Web Tools (tools/web_tools.py)](#73-web-tools-toolsweb_toolspy)\n   - 7.4 [Browser Tool (tools/browser_tool.py)](#74-browser-tool-toolsbrowser_toolpy)\n   - 7.5 [Delegate Tool (tools/delegate_tool.py)](#75-delegate-tool-toolsdelegate_toolpy)\n   - 7.6 [MCP Tool (tools/mcp_tool.py)](#76-mcp-tool-toolsmcp_toolpy)\n   - 7.7 [Approval System (tools/approval.py)](#77-approval-system-toolsapprovalpy)\n   - 7.8 [Terminal Backends (tools/environments/)](#78-terminal-backends-toolsenvironments)\n8. [Agent Internals](#8-agent-internals)\n   - 8.1 [Prompt Builder (agent/prompt_builder.py)](#81-prompt-builder-agentprompt_builderpy)\n   - 8.2 [Context Compressor (agent/context_compressor.py)](#82-context-compressor-agentcontext_compressorpy)\n   - 8.3 [Prompt Caching (agent/prompt_caching.py)](#83-prompt-caching-agentprompt_cachingpy)\n   - 8.4 [Auxiliary Client (agent/auxiliary_client.py)](#84-auxiliary-client-agentauxiliary_clientpy)\n   - 8.5 [Display & Spinner (agent/display.py)](#85-display--spinner-agentdisplaypy)\n   - 8.6 [Skill Commands (agent/skill_commands.py)](#86-skill-commands-agentskill_commandspy)\n9. [Messaging Gateway](#9-messaging-gateway)\n   - 9.1 [GatewayRunner (gateway/run.py)](#91-gatewayrunner-gatewayrunpy)\n   - 9.2 [Session Store (gateway/session.py)](#92-session-store-gatewaysessionpy)\n   - 9.3 [Platform Adapters (gateway/platforms/)](#93-platform-adapters-gatewayplatforms)\n10. [Cron Scheduling](#10-cron-scheduling)\n11. [Skills System](#11-skills-system)\n12. [Plugin System](#12-plugin-system)\n13. [Memory System](#13-memory-system)\n14. [ACP Server (IDE Integration)](#14-acp-server-ide-integration)\n15. [API Server](#15-api-server)\n16. [MCP Server Mode](#16-mcp-server-mode)\n17. [RL Training Environments](#17-rl-training-environments)\n18. [Profiles (Multi-Instance)](#18-profiles-multi-instance)\n19. [Security Model](#19-security-model)\n20. [Provider & Model System](#20-provider--model-system)\n21. [Streaming & Reasoning](#21-streaming--reasoning)\n22. [Release History](#22-release-history)\n23. [File Dependency Chain](#23-file-dependency-chain)\n24. [Key Design Patterns](#24-key-design-patterns)\n25. [Configuration Reference](#25-configuration-reference)\n26. [Known Pitfalls](#26-known-pitfalls)\n\n---\n\n## 1. Project Overview\n\n**Hermes Agent** is a self-improving AI agent built by [Nous Research](https://nousresearch.com). It is an open-source (MIT licensed), Python-based project that provides:\n\n- A **full interactive terminal UI** (CLI) for conversing with LLMs\n- A **messaging gateway** supporting 16+ platforms (Telegram, Discord, Slack, WhatsApp, Signal, Matrix, Email, etc.)\n- A **closed learning loop** — the agent creates skills from experience, improves them during use, nudges itself to persist knowledge, searches past conversations, and builds a deepening model of who you are\n- **40+ built-in tools** — terminal execution, file manipulation, web search, browser automation, code execution, image generation, TTS/STT, and more\n- **Any LLM provider** — OpenRouter (200+ models), Nous Portal (400+ models), OpenAI, Anthropic, Hugging Face, GitHub Copilot, z.ai/GLM, Kimi/Moonshot, MiniMax, Alibaba/DashScope, custom endpoints\n- **Six terminal backends** — local, Docker, SSH, Modal (serverless), Daytona (serverless), Singularity (HPC)\n- **Scheduled automations** via built-in cron scheduler\n- **IDE integration** via ACP (Agent Communication Protocol) for VS Code, Zed, JetBrains\n- **MCP integration** — both client (connect to any MCP server) and server (expose Hermes to MCP clients)\n- **RL training** via Atropos environments for training the next generation of tool-calling models\n\n**Tech Stack:**\n\n- Python 3.11+ (core agent, tools, gateway, cron)\n- Node.js (browser automation via agent-browser)\n- SQLite with WAL mode and FTS5 (session storage, full-text search)\n- OpenAI-compatible API (primary inference interface)\n- Anthropic SDK (native Anthropic support)\n- Rich + prompt_toolkit (CLI rendering)\n\n**Repository:** `github.com/NousResearch/hermes-agent`\n**Version:** 0.7.0 (as of April 2026)\n**License:** MIT\n\n---\n\n## 2. Key Features Summary\n\n| Feature                      | Description                                                                                                                                                                      |\n| ---------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| **Terminal UI**              | Full TUI with multiline editing, slash-command autocomplete, conversation history, interrupt-and-redirect, streaming tool output                                                 |\n| **Multi-Platform Messaging** | Telegram, Discord, Slack, WhatsApp, Signal, Matrix, Email, Home Assistant, DingTalk, Feishu/Lark, WeCom, Mattermost, SMS, Webhook — all from a single gateway process            |\n| **Learning Loop**            | Agent-curated memory with periodic nudges, autonomous skill creation, skills self-improve during use, FTS5 session search with LLM summarization, Honcho dialectic user modeling |\n| **Scheduled Tasks**          | Built-in cron scheduler with delivery to any platform (daily reports, nightly backups, weekly audits)                                                                            |\n| **Subagent Delegation**      | Spawn isolated subagents for parallel workstreams with restricted toolsets                                                                                                       |\n| **Execute Code**             | Python scripts that call tools via RPC, collapsing multi-step pipelines into zero-context-cost turns                                                                             |\n| **Terminal Backends**        | Local, Docker, SSH, Modal, Daytona, Singularity — run on a $5 VPS or a GPU cluster                                                                                               |\n| **Skills**                   | 70+ bundled skills across 28 categories, Skills Hub for community discovery, agentskills.io compatibility                                                                        |\n| **Plugins**                  | Drop-in Python plugins with lifecycle hooks (pre_llm_call, post_llm_call, on_session_start, on_session_end)                                                                      |\n| **MCP**                      | Client (connect to MCP servers for extended tools) and Server (expose conversations to MCP clients)                                                                              |\n| **IDE Integration**          | VS Code, Zed, JetBrains via ACP server with session management and tool streaming                                                                                                |\n| **API Server**               | OpenAI-compatible `/v1/chat/completions` endpoint for headless integrations                                                                                                      |\n| **Profiles**                 | Multi-instance support — each profile gets isolated config, memory, sessions, skills, gateway                                                                                    |\n| **Security**                 | Command approval system, secret redaction, SSRF protection, PII redaction, injection detection, credential directory protection                                                  |\n| **RL Training**              | Atropos environments for batch trajectory generation and agent policy optimization                                                                                               |\n\n---\n\n## 3. Installation & Getting Started\n\n```bash\n# One-line install (Linux, macOS, WSL2)\ncurl -fsSL https://raw.githubusercontent.com/NousResearch/hermes-agent/main/scripts/install.sh | bash\n\n# After install\nsource ~/.bashrc    # or: source ~/.zshrc\nhermes              # start chatting\n\n# Key commands\nhermes model        # Choose LLM provider and model\nhermes tools        # Configure which tools are enabled\nhermes config set   # Set individual config values\nhermes gateway      # Start the messaging gateway\nhermes setup        # Run the full setup wizard\nhermes update       # Update to latest version\nhermes doctor       # Diagnose any issues\n```\n\n**For development:**\n\n```bash\ngit clone https://github.com/NousResearch/hermes-agent.git\ncd hermes-agent\ncurl -LsSf https://astral.sh/uv/install.sh | sh\nuv venv venv --python 3.11\nsource venv/bin/activate\nuv pip install -e \".[all,dev]\"\npython -m pytest tests/ -q    # ~3000 tests\n```\n\n---\n\n## 4. Project Structure\n\n```\nhermes-agent/\n├── run_agent.py              # AIAgent class — core conversation loop\n├── model_tools.py            # Tool orchestration, _discover_tools(), handle_function_call()\n├── toolsets.py               # Toolset definitions, _HERMES_CORE_TOOLS list\n├── toolset_distributions.py  # Toolset sampling distributions for RL\n├── cli.py                    # HermesCLI class — interactive CLI orchestrator\n├── hermes_state.py           # SessionDB — SQLite session store (FTS5 search)\n├── hermes_constants.py       # Shared constants, get_hermes_home()\n├── hermes_time.py            # Timezone handling\n├── utils.py                  # Shared utility functions\n├── batch_runner.py           # Parallel batch processing\n├── trajectory_compressor.py  # Trajectory compression for RL training\n├── mcp_serve.py              # MCP server mode entry point\n├── mini_swe_runner.py        # Minimal SWE benchmark runner\n├── rl_cli.py                 # RL CLI commands\n│\n├── agent/                    # Agent internals\n│   ├── prompt_builder.py         # System prompt assembly\n│   ├── context_compressor.py     # Auto context compression\n│   ├── prompt_caching.py         # Anthropic prompt caching\n│   ├── auxiliary_client.py       # Auxiliary LLM client (vision, summarization)\n│   ├── model_metadata.py         # Model context lengths, token estimation\n│   ├── models_dev.py             # models.dev registry integration\n│   ├── display.py                # KawaiiSpinner, tool preview formatting\n│   ├── skill_commands.py         # Skill slash commands (shared CLI/gateway)\n│   └── trajectory.py             # Trajectory saving helpers\n│\n├── hermes_cli/               # CLI subcommands and setup\n│   ├── main.py               # Entry point — all `hermes` subcommands\n│   ├── config.py             # DEFAULT_CONFIG, OPTIONAL_ENV_VARS, migration\n│   ├── commands.py           # Slash command definitions + SlashCommandCompleter\n│   ├── callbacks.py          # Terminal callbacks (clarify, sudo, approval)\n│   ├── setup.py              # Interactive setup wizard\n│   ├── skin_engine.py        # Skin/theme engine\n│   ├── skills_config.py      # `hermes skills` — skill management\n│   ├── tools_config.py       # `hermes tools` — tool management\n│   ├── skills_hub.py         # Skills Hub integration\n│   ├── models.py             # Model catalog, provider model lists\n│   ├── model_switch.py       # Shared /model switch pipeline\n│   └── auth.py               # Provider credential resolution\n│\n├── tools/                    # Tool implementations (one file per tool)\n│   ├── registry.py           # Central tool registry\n│   ├── approval.py           # Dangerous command detection\n│   ├── terminal_tool.py      # Terminal/shell execution\n│   ├── process_registry.py   # Background process management\n│   ├── file_tools.py         # File read/write/search/patch\n│   ├── web_tools.py          # Web search/extract\n│   ├── browser_tool.py       # Browser automation\n│   ├── code_execution_tool.py # execute_code sandbox\n│   ├── delegate_tool.py      # Subagent delegation\n│   ├── mcp_tool.py           # MCP client integration\n│   ├── skills_tool.py        # Skill management tool\n│   ├── todo_tool.py          # Todo/task tracking tool\n│   ├── memory_tool.py        # Memory read/write tool\n│   ├── tts_tool.py           # Text-to-speech\n│   ├── vision_tool.py        # Image analysis\n│   ├── image_gen_tool.py     # Image generation\n│   └── environments/         # Terminal backends\n│       ├── base.py               # BaseEnvironment ABC\n│       ├── local.py              # Local execution\n│       ├── docker.py             # Docker containers\n│       ├── ssh.py                # SSH remote execution\n│       ├── modal.py              # Modal serverless\n│       ├── managed_modal.py      # Nous-hosted Modal\n│       ├── daytona.py            # Daytona serverless\n│       ├── singularity.py        # Singularity HPC containers\n│       └── persistent_shell.py   # Persistent shell mixin\n│\n├── gateway/                  # Messaging platform gateway\n│   ├── run.py                # GatewayRunner — main message loop\n│   ├── session.py            # SessionStore — conversation persistence\n│   ├── status.py             # Gateway status, token locks\n│   └── platforms/            # 16 platform adapters\n│       ├── base.py               # BasePlatformAdapter ABC\n│       ├── telegram.py           # Telegram (polling + webhook)\n│       ├── discord.py            # Discord\n│       ├── slack.py              # Slack\n│       ├── whatsapp.py           # WhatsApp\n│       ├── matrix.py             # Matrix (E2EE)\n│       ├── signal.py             # Signal\n│       ├── email.py              # Email (IMAP/SMTP)\n│       ├── homeassistant.py      # Home Assistant\n│       ├── sms.py                # SMS (Twilio)\n│       ├── mattermost.py         # Mattermost\n│       ├── dingtalk.py           # DingTalk\n│       ├── feishu.py             # Feishu/Lark\n│       ├── wecom.py              # WeCom (Enterprise WeChat)\n│       ├── webhook.py            # Generic webhook\n│       └── api_server.py         # OpenAI-compatible API server\n│\n├── acp_adapter/              # ACP server (IDE integration)\n│   ├── server.py             # HermesACPAgent class\n│   ├── session.py            # SessionManager\n│   ├── events.py             # Streaming callbacks\n│   ├── permissions.py        # Approval callbacks\n│   └── entry.py              # Entry point\n│\n├── cron/                     # Scheduler\n│   ├── scheduler.py          # tick() — job execution engine\n│   └── jobs.py               # Job storage and CRUD\n│\n├── plugins/                  # Plugin system\n│   └── memory/               # 8 memory provider plugins\n│       ├── openviking/\n│       ├── mem0/\n│       ├── hindsight/\n│       ├── holographic/\n│       ├── honcho/\n│       ├── retaindb/\n│       └── byterover/\n│\n├── environments/             # RL training environments (Atropos)\n│   ├── hermes_base_env.py    # Abstract base RL environment\n│   ├── agent_loop.py         # HermesAgentLoop — rollout execution\n│   ├── tool_context.py       # ToolContext — sandbox for RL\n│   ├── web_research_env.py   # Web research tasks\n│   └── agentic_opd_env.py    # Observation-Prediction-Demo env\n│\n├── skills/                   # 70+ bundled skills across 28 categories\n├── optional-skills/          # Additional optional skills\n├── tests/                    # ~3000 pytest tests\n├── scripts/                  # Install, update, packaging scripts\n├── docker/                   # Docker build files\n├── docs/                     # Documentation source (Docusaurus)\n├── website/                  # Landing page\n├── desktop/                  # Desktop app (Electron, separate repo)\n├── tinker-atropos/           # RL submodule\n│\n├── pyproject.toml            # Python package config\n├── AGENTS.md                 # Developer guide for AI assistants\n├── RELEASE_v0.2.0.md → v0.7.0.md  # Release notes\n└── cli-config.yaml.example   # Example config\n```\n\n**User config directory:** `~/.hermes/`\n\n```\n~/.hermes/\n├── config.yaml           # User settings\n├── .env                  # API keys and secrets\n├── MEMORY.md             # Persistent agent memory\n├── USER.md               # User profile\n├── SOUL.md               # Agent personality/identity\n├── sessions.db           # SQLite session database\n├── skills/               # User-installed skills\n├── skins/                # Custom CLI themes\n├── plugins/              # User plugins\n├── cron/                 # Cron jobs and output\n│   ├── jobs.json\n│   └── output/\n├── cache/                # Image/audio cache\n├── plans/                # Generated plans\n├── profiles/             # Multi-instance profiles\n└── mcp/                  # MCP server configs\n```\n\n---\n\n## 5. Core Architecture\n\n### 5.1 AIAgent Class (run_agent.py)\n\nThe `AIAgent` class is the heart of the system — the core conversation loop that orchestrates LLM calls, tool execution, context management, and response delivery.\n\n**Constructor (~60 parameters):**\n\n```python\nclass AIAgent:\n    def __init__(self,\n        model: str = \"anthropic/claude-opus-4.6\",\n        max_iterations: int = 90,\n        enabled_toolsets: list = None,\n        disabled_toolsets: list = None,\n        quiet_mode: bool = False,\n        save_trajectories: bool = False,\n        platform: str = None,            # \"cli\", \"telegram\", etc.\n        session_id: str = None,\n        session_db: SessionDB = None,\n        skip_context_files: bool = False,\n        skip_memory: bool = False,\n        base_url: str = None,\n        api_key: str = None,\n        provider: str = None,\n        api_mode: str = \"chat_completions\",  # or \"anthropic_messages\" or \"codex_responses\"\n        tool_progress_callback = None,\n        stream_delta_callback = None,\n        thinking_callback = None,\n        status_callback = None,\n        iteration_budget: IterationBudget = None,\n        credential_pool = None,\n        checkpoints_enabled: bool = False,\n        # ... plus provider, routing, callback params\n    )\n```\n\n**Main Methods:**\n\n| Method                                                                          | Returns  | Purpose                                                                            |\n| ------------------------------------------------------------------------------- | -------- | ---------------------------------------------------------------------------------- |\n| `chat(message, stream_callback)`                                                | `str`    | Simple interface — returns final response text                                     |\n| `run_conversation(user_message, system_message, conversation_history, task_id)` | `dict`   | Full interface — returns `{final_response, messages, completed, api_calls, error}` |\n| `_interruptible_api_call(api_kwargs)`                                           | Response | Runs API request in background thread with interrupt support                       |\n| `_interruptible_streaming_api_call(api_kwargs, on_first_delta)`                 | Response | Streaming variant with delta callbacks                                             |\n\n**The Core Agent Loop** (inside `run_conversation()`):\n\n```python\nwhile api_call_count < self.max_iterations and self.iteration_budget.remaining > 0:\n    response = client.chat.completions.create(\n        model=model, messages=messages, tools=tool_schemas\n    )\n    if response.tool_calls:\n        for tool_call in response.tool_calls:\n            result = handle_function_call(tool_call.name, tool_call.args, task_id)\n            messages.append(tool_result_message(result))\n        api_call_count += 1\n    else:\n        return response.content  # Final text response\n```\n\n**Key Behaviors:**\n\n- **Three API modes:** `chat_completions` (OpenAI-compatible), `anthropic_messages` (Anthropic SDK), `codex_responses` (OpenAI Codex)\n- **Parallel tool execution:** Independent tool calls run concurrently via ThreadPoolExecutor (unless they share file paths or are in the never-parallel list)\n- **Interrupt support:** Background threads allow interrupt detection without blocking on HTTP\n- **Error recovery:** Automatic fallback chain (primary → fallback model), retry with exponential backoff, context compression on token overflow\n- **Budget pressure:** Warnings at 70% (caution) and 90% (urgent) of iteration budget\n- **Oversized results:** Tool results >100K chars are saved to temp files with a preview\n- **Stale connection detection:** 90s timeout for streaming, 60s read timeout\n\n**IterationBudget** (thread-safe):\n\n```python\nclass IterationBudget:\n    def consume() -> bool    # Check and consume one iteration\n    def refund()             # Give back iteration (for execute_code turns)\n    @property remaining      # Remaining iterations\n```\n\n---\n\n### 5.2 Tool Orchestration (model_tools.py)\n\nBridges the agent and tool registry — handles discovery, schema generation, and dispatch.\n\n**Key Functions:**\n\n| Function                                                                                | Purpose                                                                   |\n| --------------------------------------------------------------------------------------- | ------------------------------------------------------------------------- |\n| `_discover_tools()`                                                                     | Imports all tool modules (each calls `registry.register()` on import)     |\n| `get_tool_definitions(enabled_toolsets, disabled_toolsets, quiet_mode)`                 | Returns OpenAI-format tool schemas filtered by toolset                    |\n| `handle_function_call(function_name, function_args, task_id, user_task, enabled_tools)` | Main dispatcher — routes calls to registry with arg coercion              |\n| `coerce_tool_args(tool_name, args)`                                                     | Type coercion for LLM-generated arguments (string→int, string→bool, etc.) |\n\n**Tool Discovery Order:**\n\n1. Static tools (web_tools, terminal_tool, file_tools, browser_tool, etc.)\n2. Optional tools (fal_client for image gen, honcho, etc.) — graceful fallback if missing\n3. Plugin-registered tools\n4. MCP server tools (dynamic, via tools/list_changed notifications)\n\n**Special Tool Handling:**\n\n- **Agent-level tools** (todo, memory, session_search, delegate_task): Intercepted by `run_agent.py` before `handle_function_call()`\n- **execute_code**: Passes `enabled_tools` for sandbox tool list\n- **Dynamic schema adjustments**: `browser_navigate` strips web_search reference if tools unavailable\n\n**Async Bridging:**\n\n- Persistent event loops (not `asyncio.run()`) to prevent \"Event loop is closed\" errors\n- Main thread uses shared loop; worker threads get per-thread loops\n- `_run_async()` detects running loop and spins up disposable thread if needed\n\n---\n\n### 5.3 Toolset System (toolsets.py)\n\nProvides flexible tool grouping and composition.\n\n**Core Toolsets:**\n\n| Toolset          | Tools Included                                                                                   |\n| ---------------- | ------------------------------------------------------------------------------------------------ |\n| `web`            | web_search, web_extract, web_crawl                                                               |\n| `terminal`       | terminal                                                                                         |\n| `file`           | read_file, write_file, edit_file, list_files, search_files                                       |\n| `browser`        | browser_navigate, browser_snapshot, browser_click, browser_type, browser_scroll, browser_extract |\n| `vision`         | analyze_image                                                                                    |\n| `image_gen`      | generate_image                                                                                   |\n| `tts`            | text_to_speech                                                                                   |\n| `todo`           | todo_read, todo_write                                                                            |\n| `memory`         | memory_read, memory_write                                                                        |\n| `session_search` | session_search                                                                                   |\n| `delegation`     | delegate_task                                                                                    |\n| `code_execution` | execute_code                                                                                     |\n| `cronjob`        | create_job, list_jobs, delete_job                                                                |\n| `messaging`      | send_message                                                                                     |\n| `homeassistant`  | ha_get_states, ha_call_service, ...                                                              |\n\n**Composite Toolsets:**\n\n- `hermes-cli` — All core tools for CLI platform\n- `hermes-telegram`, `hermes-discord`, etc. — Platform-specific tool sets\n- `hermes-gateway` — Union of all platform tools\n- `debugging` — terminal + file + web\n- `safe` — Everything except terminal\n\n**Resolution:**\n\n```python\nresolve_toolset(name, visited=None) → List[str]\n# Recursively resolves toolset to tool names\n# Handles composition (includes) and cycle detection\n# Special aliases: \"all\" or \"*\" = all tools\n```\n\n---\n\n### 5.4 Tool Registry (tools/registry.py)\n\nSingleton managing all tool schemas and handlers. Circular-import safe — has no tool dependencies.\n\n**ToolEntry** (per-tool metadata):\n\n```python\n@dataclass(slots=True)\nclass ToolEntry:\n    name: str\n    toolset: str\n    schema: dict           # OpenAI-format tool definition\n    handler: Callable      # Sync or async handler function\n    check_fn: Callable     # Returns True if tool is available\n    requires_env: list     # Required environment variables\n    is_async: bool\n    description: str\n    emoji: str\n```\n\n**Key Methods:**\n\n```python\nregistry.register(name, toolset, schema, handler, check_fn, requires_env)\nregistry.get_definitions(tool_names, quiet)  # Returns filtered schemas\nregistry.dispatch(name, args, **kwargs)      # Execute with async bridging\nregistry.deregister(name)                    # Remove (for MCP tool refresh)\nregistry.check_tool_availability()           # Returns (available, unavailable)\n```\n\n---\n\n### 5.5 Session Database (hermes_state.py)\n\nSQLite-based persistent session storage with FTS5 full-text search.\n\n**Schema (v6):**\n\n```sql\n-- Sessions table\nsessions (\n    id TEXT PRIMARY KEY,\n    source TEXT, user_id TEXT, model TEXT, model_config TEXT,\n    system_prompt TEXT, parent_session_id TEXT,\n    started_at TEXT, ended_at TEXT, end_reason TEXT,\n    message_count INTEGER, tool_call_count INTEGER,\n    input_tokens INTEGER, output_tokens INTEGER,\n    cache_read_tokens INTEGER, cache_write_tokens INTEGER, reasoning_tokens INTEGER,\n    estimated_cost_usd REAL, actual_cost_usd REAL,\n    title TEXT  -- UNIQUE INDEX\n)\n\n-- Messages table\nmessages (\n    id INTEGER PRIMARY KEY AUTOINCREMENT,\n    session_id TEXT, role TEXT, content TEXT,\n    tool_call_id TEXT, tool_calls TEXT,  -- JSON\n    tool_name TEXT, timestamp TEXT,\n    token_count INTEGER, finish_reason TEXT,\n    reasoning TEXT, reasoning_details TEXT, codex_reasoning_items TEXT\n)\n\n-- FTS5 virtual table (auto-synced via triggers)\nmessages_fts (content)\n```\n\n**Concurrency Model:**\n\n- WAL (Write-Ahead Logging) for concurrent readers + single writer\n- `BEGIN IMMEDIATE` for write transactions (lock at start, not commit)\n- Jitter retry on lock: 20-150ms random backoff, max 15 retries\n- Periodic WAL checkpoint every 50 writes\n\n**Key Operations:**\n\n- `create_session()`, `end_session()`, `reopen_session()`\n- `add_message()`, `get_messages()`\n- `search_sessions(query)` — FTS5 full-text search\n- `update_token_counts()` — Supports both incremental (CLI) and absolute (gateway) modes\n\n---\n\n### 5.6 Constants & Home Directory (hermes_constants.py)\n\nImport-safe constants module with no circular dependencies.\n\n```python\nget_hermes_home() → Path          # HERMES_HOME env var or ~/.hermes\ndisplay_hermes_home() → str       # User-friendly display: \"~/.hermes\"\nget_optional_skills_dir() → Path  # HERMES_OPTIONAL_SKILLS env var\nparse_reasoning_effort(str) → Dict  # \"high\" → {\"enabled\": True, \"effort\": \"high\"}\n\n# Key constants\nOPENROUTER_BASE_URL = \"https://openrouter.ai/api/v1\"\nNOUS_API_BASE_URL = \"https://inference-api.nousresearch.com/v1\"\nAI_GATEWAY_BASE_URL = \"https://ai-gateway.vercel.sh/v1\"\nVALID_REASONING_EFFORTS = (\"xhigh\", \"high\", \"medium\", \"low\", \"minimal\")\n```\n\n---\n\n## 6. CLI System\n\n### 6.1 Interactive CLI (cli.py)\n\nThe `HermesCLI` class provides the interactive terminal interface.\n\n**Features:**\n\n- **Rich** for banner/panels, **prompt_toolkit** for input with autocomplete\n- **KawaiiSpinner** — animated faces during API calls, `┊` activity feed for tool results\n- Multiline editing with Shift+Enter\n- Slash-command autocomplete\n- Session history with up/down arrow navigation\n- Clipboard image paste (Alt+V / Ctrl+V)\n- Status bar showing model, provider, and token counts\n- Inline diff previews for file write/patch operations\n\n**Configuration Loading:**\n\n```python\nload_cli_config() → dict\n# Loads from ~/.hermes/config.yaml (or ./cli-config.yaml fallback)\n# Merges with hardcoded defaults\n# Expands ${ENV_VAR} references\n# Maps terminal config → env vars\n```\n\n---\n\n### 6.2 CLI Entry Point (hermes_cli/main.py)\n\nAll `hermes` subcommands are dispatched from here:\n\n```\nhermes                    # Default: interactive chat\nhermes chat               # Explicit interactive mode\nhermes gateway start|stop|status|install|uninstall\nhermes setup              # Setup wizard\nhermes model              # Select model/provider\nhermes tools              # Configure tools\nhermes skills             # Manage skills\nhermes config set|get     # Direct config manipulation\nhermes cron list|delete   # Cron job management\nhermes doctor             # Diagnose issues\nhermes sessions browse    # Session picker\nhermes profile create|list|switch|delete|export|import\nhermes mcp serve|add|remove  # MCP management\nhermes acp                # Start ACP server\nhermes update|uninstall|version\n```\n\n**Profile System:**\n\n- `_apply_profile_override()` runs BEFORE any imports to set `HERMES_HOME`\n- Pre-parses `--profile/-p` from argv\n- Allows fully isolated agent instances with separate config, memory, sessions, skills\n\n---\n\n### 6.3 Configuration System (hermes_cli/config.py)\n\n**Key Configuration Sections:**\n\n```yaml\nmodel: \"anthropic/claude-opus-4.6\" # or dict with provider/base_url/api_key\nproviders: {} # Provider-specific configs\nfallback_providers: [] # Ordered failover list\ncredential_pool: {} # Multiple API keys per provider\n\nagent:\n  max_turns: 90\n  gateway_timeout: 1800\n  tool_use_enforcement: \"auto\"\n\nterminal:\n  backend: \"local\" # local|docker|modal|daytona|ssh|singularity\n  timeout: 180\n  persistent_shell: true\n  docker_image: \"nikolaik/python-nodejs:...\"\n\ncompression:\n  enabled: true\n  threshold: 0.50 # Compress when 50% of context used\n  target_ratio: 0.20 # Summary = 20% of compressed content\n  protect_last_n: 20\n\nauxiliary:\n  vision: { provider, model }\n  web_extract: { provider, model }\n  compression: { provider, model }\n\nmemory:\n  memory_enabled: true\n  provider: \"\" # \"\" | \"honcho\" | \"mem0\" | etc.\n  memory_char_limit: 2200\n\ndisplay:\n  personality: \"kawaii\"\n  show_reasoning: false\n  inline_diffs: true\n  skin: \"default\"\n  streaming: true\n\ntts:\n  provider: \"edge\" # edge|elevenlabs|openai|neutts\n\nstt:\n  enabled: true\n  provider: \"local\" # local|groq|openai\n\nprivacy:\n  redact_pii: false\n\nmcp_servers: {} # MCP server configurations\n\nskills:\n  external_dirs: [] # Additional skill directories\n\napprovals:\n  mode: \"smart\" # smart|always|off\n```\n\n**Config Files:**\n\n- `~/.hermes/config.yaml` — User settings (authoritative)\n- `~/.hermes/.env` — API keys and secrets\n- Config version migration system (currently v5)\n\n---\n\n### 6.4 Slash Command Registry (hermes_cli/commands.py)\n\nAll slash commands defined centrally in `COMMAND_REGISTRY`:\n\n```python\nCommandDef(name, description, category, aliases, args_hint, cli_only, gateway_only)\n```\n\n**Derived automatically by:**\n\n- CLI `process_command()` — dispatch on canonical name\n- Gateway dispatch + help\n- Telegram BotCommand menu\n- Slack `/hermes` subcommands\n- Autocomplete + help text\n\n**Key Commands:**\n\n| Command        | Aliases    | Description                   |\n| -------------- | ---------- | ----------------------------- |\n| `/new`         | `/reset`   | Start fresh conversation      |\n| `/model`       |            | Show/switch model             |\n| `/personality` |            | Set agent personality         |\n| `/retry`       |            | Retry last turn               |\n| `/undo`        |            | Remove last turn              |\n| `/compress`    | `/compact` | Compress context              |\n| `/usage`       | `/cost`    | Show token usage              |\n| `/insights`    |            | Usage analytics               |\n| `/skills`      |            | Browse/install skills         |\n| `/background`  | `/bg`      | Manage background processes   |\n| `/plan`        |            | Generate implementation plan  |\n| `/rollback`    |            | Restore filesystem checkpoint |\n| `/verbose`     |            | Toggle debug output           |\n| `/reasoning`   |            | Set reasoning effort          |\n| `/yolo`        |            | Toggle approval bypass        |\n| `/btw`         |            | Ephemeral side question       |\n| `/stop`        |            | Kill current agent run        |\n| `/queue`       |            | Queue next prompt             |\n| `/browser`     |            | Interactive browser session   |\n| `/history`     | `/resume`  | Session browser               |\n| `/skin`        |            | Switch CLI theme              |\n\n---\n\n### 6.5 Setup Wizard (hermes_cli/setup.py)\n\nModular interactive wizard with independent sections:\n\n1. **Model & Provider** — Select AI provider, enter API keys, choose model\n2. **Terminal Backend** — Choose execution environment\n3. **Agent Settings** — Max iterations, compression, session policies\n4. **Messaging Platforms** — Configure Telegram, Discord, Slack, etc.\n5. **Tools** — TTS, STT, web search, image generation, browser\n\nFeatures:\n\n- Live credential validation\n- Real-time model list fetching from provider APIs\n- Automatic OpenClaw migration detection\n- Atomic config file writes\n\n---\n\n### 6.6 Model Catalog (hermes_cli/models.py)\n\nProvider-specific model lists:\n\n```python\n_PROVIDER_MODELS = {\n    \"nous\": [\"anthropic/claude-opus-4.6\", \"anthropic/claude-sonnet-4.6\", ...],  # 25+\n    \"openrouter\": [\"anthropic/claude-opus-4.6\", \"google/gemini-3-flash\", ...],  # 30+\n    \"anthropic\": [\"claude-opus-4-6\", \"claude-sonnet-4-6\", ...],\n    \"openai\": [\"gpt-5\", \"gpt-5.4-mini\", \"gpt-4.1\", \"gpt-4o\", ...],\n    \"copilot\": [\"gpt-5.4\", \"gpt-5.4-mini\", \"gpt-5.3-codex\", ...],\n    \"huggingface\": [...],\n    \"minimax\": [...],\n    \"kimi-coding\": [...],\n    \"alibaba\": [...],\n    \"deepseek\": [...],\n    # ... more providers\n}\n```\n\nFeatures:\n\n- Dynamic fetching via provider `/models` endpoints\n- Curated lists used when live probe returns fewer models\n- Fuzzy matching for typo correction\n- Validation against provider catalog\n\n---\n\n### 6.7 Skin/Theme Engine (hermes_cli/skin_engine.py)\n\nData-driven CLI visual customization — no code changes needed.\n\n**Customizable Elements:**\n\n| Element                          | Key                      | Used By           |\n| -------------------------------- | ------------------------ | ----------------- |\n| Banner border/title/accent       | `colors.*`               | banner.py         |\n| Response box border              | `colors.response_border` | cli.py            |\n| Spinner faces (waiting/thinking) | `spinner.*`              | display.py        |\n| Spinner verbs/wings              | `spinner.*`              | display.py        |\n| Tool output prefix               | `tool_prefix`            | display.py        |\n| Per-tool emojis                  | `tool_emojis`            | display.py        |\n| Agent name/welcome/prompt        | `branding.*`             | banner.py, cli.py |\n\n**Built-in Skins:** default, ares, mono, slate, poseidon, sisyphus, charizard\n\n**User Skins:** Drop `~/.hermes/skins/<name>.yaml` and activate with `/skin <name>`\n\n---\n\n## 7. Tool System\n\n### 7.1 Terminal Tool (tools/terminal_tool.py)\n\nShell command execution across multiple backends.\n\n```python\ndef terminal_tool(\n    command: str,\n    background: bool = False,\n    timeout: Optional[int] = None,\n    task_id: Optional[str] = None,\n    force: bool = False,        # Skip approval for dangerous commands\n    workdir: Optional[str] = None,\n    check_interval: Optional[int] = None,  # Background task polling\n    pty: bool = False,\n) -> str  # JSON result\n```\n\n**Features:**\n\n- Multi-backend: Selects based on `TERMINAL_ENV` (local/docker/ssh/modal/daytona/singularity)\n- Per-task_id sandboxes with thread-safe creation locks\n- Dangerous command routing through approval system\n- Background task support with file-based IPC\n- Interrupt handling — polls `is_interrupted()` during execution\n- Auto-cleanup daemon thread for idle environments (>300s)\n- Disk usage warnings at configurable threshold\n\n---\n\n### 7.2 File Tools (tools/file_tools.py)\n\nSafe file operations with size guards and sensitive path protection.\n\n- `read_file_tool(path, offset, limit)` — Read with pagination (default 100K char limit)\n- `write_file_tool(path, content)` — Write with approval for sensitive paths\n- `edit_file_tool(path, old_text, new_text)` — String replacement editing\n- `list_files_tool(path)` — Directory listing\n- `search_files_tool(pattern, path)` — Glob/regex file search\n\n**Safety:**\n\n- Device path blocklist (`/dev/zero`, `/dev/stdin`, etc.)\n- Read dedup tracking — returns stub on re-read if mtime unchanged\n- Sensitive path blocking: `/etc/`, `/boot/`, `~/.ssh` without approval\n- Prompt injection protection for known dangerous paths\n\n---\n\n### 7.3 Web Tools (tools/web_tools.py)\n\nWeb search and content extraction.\n\n- `web_search_tool(query, limit)` — Search via configurable backend\n- `web_extract_tool(urls, format)` — Extract content from URLs\n- `web_crawl_tool(url, instructions)` — LLM-guided web crawling\n\n**Backends:** Firecrawl, Parallel Web, Tavily, Exa, DuckDuckGo\n\n- Fallback detection by highest-priority available API key\n- LLM processing via auxiliary client (Gemini 3 Flash) for intelligent extraction\n- SSRF protection and URL safety checks\n\n---\n\n### 7.4 Browser Tool (tools/browser_tool.py)\n\nHeadless browser automation with accessibility tree.\n\n```python\nbrowser_navigate(url, task_id) → str\nbrowser_snapshot(task_id, max_chars) → str\nbrowser_click(ref, task_id) → str      # Element refs like @e1, @e2\nbrowser_type(ref, text, task_id) → str\nbrowser_scroll(ref, direction, task_id) → str\nbrowser_extract(task, max_chars, task_id) → str\n```\n\n**Backends:**\n\n- **Local:** Headless Chromium via `agent-browser` CLI\n- **Cloud:** Browserbase (stealth, proxies, CAPTCHA solving)\n- **Camofox:** Anti-detection browser using Camoufox\n\n**Features:**\n\n- Accessibility tree for text-based snapshots (no vision required)\n- Session isolation per task_id with inactivity cleanup\n- API key detection in URLs (prevents exfiltration)\n- SSRF protection for private/internal addresses\n\n---\n\n### 7.5 Delegate Tool (tools/delegate_tool.py)\n\nSpawn isolated subagents for parallel workstreams.\n\n```python\ndef delegate_task(\n    goal: str,\n    context: str = None,\n    toolsets: List[str] = None,       # Default: [\"terminal\", \"file\", \"web\"]\n    tasks: List[Dict] = None,          # Batch mode: up to 3 concurrent\n    max_iterations: int = 50,\n    parent_agent = None,\n) → str\n```\n\n**Isolation:**\n\n- Fresh conversation (no parent history)\n- Own task_id (separate terminal session, file ops)\n- Restricted toolset (configurable, blocked tools always stripped)\n- Focused system prompt from goal + context\n- Parent only sees delegation call and summary result\n\n**Blocked Tools:** delegate_task, clarify, memory, send_message, execute_code (no recursion, no user interaction)\n\n**Constraints:** MAX_DEPTH=2, MAX_CONCURRENT_CHILDREN=3\n\n---\n\n### 7.6 MCP Tool (tools/mcp_tool.py)\n\nModel Context Protocol client integration.\n\n**Configuration (config.yaml):**\n\n```yaml\nmcp_servers:\n  filesystem:\n    command: \"npx\"\n    args: [\"-y\", \"@modelcontextprotocol/server-filesystem\", \"/tmp\"]\n    timeout: 120\n  remote_api:\n    url: \"https://my-mcp-server.example.com/mcp\"\n    headers:\n      Authorization: \"Bearer sk-...\"\n    sampling:\n      enabled: true\n      model: \"gemini-3-flash\"\n```\n\n**Features:**\n\n- **Transports:** Stdio (local processes) and HTTP/StreamableHTTP (remote)\n- **Dynamic tool discovery:** Listens for `tools/list_changed` notifications\n- **Sampling support:** MCP servers can request LLM completions\n- **Automatic reconnection** with exponential backoff (5 retries)\n- Dedicated background event loop in daemon thread\n- Thread-safe via lock protecting server dict\n\n---\n\n### 7.7 Approval System (tools/approval.py)\n\nDangerous command detection and approval flow.\n\n```python\ndetect_dangerous_command(command: str) → (is_dangerous, pattern_key, description)\n```\n\n**106 patterns covering:**\n\n- Destructive operations (`rm -r /`, `mkfs`, `dd if=`)\n- Privilege escalation (`chmod 777`, `chown -R root`)\n- SQL injection (`DROP TABLE`, `DELETE FROM` without WHERE)\n- System targeting (`/etc/` writes, `systemctl stop`, fork bombs)\n- Shell injection (pipe to sh, wget to sh, process substitution)\n- Network exfiltration (curl with embedded API keys)\n- Secret access (`cat ~/.env`, `cat ~/.netrc`)\n\n**Normalization:** Strips ANSI escapes, null bytes, normalizes Unicode\n\n**Approval Modes:**\n\n- `smart` — Learns which commands are safe based on user decisions\n- `always` — Always ask for dangerous commands\n- `off` — Never ask (or `/yolo` toggle)\n\n---\n\n### 7.8 Terminal Backends (tools/environments/)\n\n| Backend           | File               | Features                                                                       |\n| ----------------- | ------------------ | ------------------------------------------------------------------------------ |\n| **Local**         | `local.py`         | Direct execution, interrupt support, non-blocking I/O, persistent shell        |\n| **Docker**        | `docker.py`        | Sandboxed containers, cap-drop ALL, no-new-privileges, PID limits, bind mounts |\n| **SSH**           | `ssh.py`           | Remote execution via ControlMaster, persistent shell mixin                     |\n| **Modal**         | `modal.py`         | Native Modal SDK `Sandbox.create()`/`Sandbox.exec()`, persistent snapshots     |\n| **Managed Modal** | `managed_modal.py` | Modal through Nous-hosted gateway                                              |\n| **Daytona**       | `daytona.py`       | Daytona SDK cloud sandboxes, stop/resume lifecycle                             |\n| **Singularity**   | `singularity.py`   | Singularity containers with scratch dir, SIF cache                             |\n\n**Common Interface (BaseEnvironment ABC):**\n\n```python\nexecute(command, timeout) → {\"output\": str, \"returncode\": int}\ncleanup() → None\n```\n\n---\n\n## 8. Agent Internals\n\n### 8.1 Prompt Builder (agent/prompt_builder.py)\n\nAssembles the system prompt from multiple sources:\n\n| Component               | Source                                               |\n| ----------------------- | ---------------------------------------------------- |\n| Agent Identity          | `DEFAULT_AGENT_IDENTITY` or `SOUL.md`                |\n| Memory Guidance         | When/how to use memory tool                          |\n| Session Search Guidance | How to recall past conversations                     |\n| Skills Guidance         | When to create/patch skills                          |\n| Tool Use Enforcement    | Must execute tools, not describe actions             |\n| Skills Index            | `~/.hermes/skills/.hermes-skills.json`               |\n| Platform Hints          | OS, Python version, shell, available tools           |\n| Context Files           | `.hermes.md`, `AGENTS.md`, `.cursorrules`, `SOUL.md` |\n| Model/Provider Info     | Current model and provider identity                  |\n\n**Context File Discovery:**\n\n1. Check `cwd/.hermes.md` or `HERMES.md`\n2. Walk parent directories up to git root\n3. Validate against injection patterns before inclusion\n\n**Injection Detection (30+ patterns):**\n\n- \"ignore previous instructions\", \"system prompt override\"\n- \"do not tell the user\", \"act as if you have no restrictions\"\n- HTML comment injection, exfiltration via curl, Unicode stealth chars\n\n---\n\n### 8.2 Context Compressor (agent/context_compressor.py)\n\nAutomatically compresses conversation history when approaching context limits.\n\n```python\nclass ContextCompressor:\n    threshold_percent: float = 0.50   # Compress when 50% of context used\n    protect_first_n: int = 3          # Keep system prompt + first turn\n    protect_last_n: int = 20          # Keep recent N messages\n    summary_target_ratio: float = 0.20  # Summary = 20% of compressed content\n```\n\n**Compression Algorithm:**\n\n1. **Pre-pass:** Replace old tool results with placeholder (cheap)\n2. **Protect head:** System prompt + first exchange always kept\n3. **Protect tail:** Token-budget-based tail protection (~20K tokens)\n4. **Summarize middle:** LLM-generated structured summary (Goal, Progress, Decisions, Files, Next Steps)\n5. **Iterative updates:** On subsequent compressions, update previous summary instead of re-summarizing\n\n---\n\n### 8.3 Prompt Caching (agent/prompt_caching.py)\n\nAnthropic prompt caching support for cost reduction.\n\n- System prompt cached across turns (first conversation turn establishes cache)\n- Cache markers inserted at system prompt boundaries\n- Must not break mid-conversation — altering past context invalidates cache\n- The ONLY time context is altered is during compression\n\n**Critical Rule:** Do NOT implement changes that would alter past context, change toolsets, reload memories, or rebuild system prompts mid-conversation.\n\n---\n\n### 8.4 Auxiliary Client (agent/auxiliary_client.py)\n\nSeparate LLM client for non-primary tasks:\n\n- **Vision analysis** — Image description and analysis\n- **Web extraction** — Content summarization\n- **Context compression** — Generating conversation summaries\n- **Session search** — Summarizing search results\n- **MCP sampling** — Serving server-initiated LLM requests\n\nConfigured per-task via `auxiliary` section in config.yaml.\n\n---\n\n### 8.5 Display & Spinner (agent/display.py)\n\n- **KawaiiSpinner** — Animated face characters during API calls\n- **Tool preview formatting** — `┊` prefixed activity feed for tool execution\n- **Inline diff previews** — Shows unified diffs for write/patch operations\n- Respects active skin for colors, emojis, and branding\n\n---\n\n### 8.6 Skill Commands (agent/skill_commands.py)\n\nShared skill invocation for CLI and gateway:\n\n- Skills loaded from `~/.hermes/skills/` and external directories\n- Injected as **user message** (not system prompt) to preserve prompt caching\n- `/plan` command generates implementation plans stored in `.hermes/plans/`\n- Skill content includes setup instructions, tool options, usage examples\n\n---\n\n## 9. Messaging Gateway\n\n### 9.1 GatewayRunner (gateway/run.py)\n\nMain controller managing all platform adapters and routing messages.\n\n**Key Attributes:**\n\n- `adapters: Dict[Platform, BasePlatformAdapter]` — Active platform instances\n- `session_store: SessionStore` — Conversation persistence\n- `_running_agents: Dict[str, AIAgent]` — Per-session cached agents (preserves prompt caching)\n- `_pending_approvals: Dict[str, Dict]` — Dangerous command approval tracking\n- `pairing_store: PairingStore` — DM code-based user authorization\n\n**Message Flow:**\n\n1. Platform adapter queues `MessageEvent`\n2. GatewayRunner dequeues, calls `_handle_message()`\n3. Slash command? → dispatch to handler\n4. Regular message? → `_handle_message_with_agent",".codex/hooks.json":"{\n  \"hooks\": {\n    \"UserPromptSubmit\": [\n      {\n        \"hooks\": [\n          {\n            \"type\": \"command\",\n            \"command\": \"lat hook claude UserPromptSubmit\"\n          }\n        ]\n      }\n    ],\n    \"Stop\": [\n      {\n        \"hooks\": [\n          {\n            \"type\": \"command\",\n            \"command\": \"lat hook claude Stop\"\n          }\n        ]\n      }\n    ]\n  }\n}\n","CLAUDE.md":"# Before starting work\n\n- Run `lat search` to find sections relevant to your task. Read them to understand the design intent before writing code.\n- Run `lat expand` on user prompts to expand any `[[refs]]` — this resolves section names to file locations and provides context.\n\n# Post-task checklist (REQUIRED — do not skip)\n\nAfter EVERY task, before responding to the user:\n\n- [ ] Update `lat.md/` if you added or changed any functionality, architecture, tests, or behavior\n- [ ] Run `lat check` — all wiki links and code refs must pass\n- [ ] Do not skip these steps. Do not consider your task done until both are complete.\n\n---\n\n# What is lat.md?\n\nThis project uses [lat.md](https://www.npmjs.com/package/lat.md) to maintain a structured knowledge graph of its architecture, design decisions, and test specs in the `lat.md/` directory. It is a set of cross-linked markdown files that describe **what** this project does and **why** — the domain concepts, key design decisions, business logic, and test specifications. Use it to ground your work in the actual architecture rather than guessing.\n\n# Commands\n\n```bash\nlat locate \"Section Name\"      # find a section by name (exact, fuzzy)\nlat refs \"file#Section\"        # find what references a section\nlat search \"natural language\"  # semantic search across all sections\nlat expand \"user prompt text\"  # expand [[refs]] to resolved locations\nlat check                      # validate all links and code refs\n```\n\nRun `lat --help` when in doubt about available commands or options.\n\nIf `lat search` fails because no API key is configured, explain to the user that semantic search requires a key provided via `LAT_LLM_KEY` (direct value), `LAT_LLM_KEY_FILE` (path to key file), or `LAT_LLM_KEY_HELPER` (command that prints the key). Supported key prefixes: `sk-...` (OpenAI) or `vck_...` (Vercel). If the user doesn't want to set it up, use `lat locate` for direct lookups instead.\n\n# Syntax primer\n\n- **Section ids**: `lat.md/path/to/file#Heading#SubHeading` — full form uses project-root-relative path (e.g. `lat.md/tests/search#RAG Replay Tests`). Short form uses bare file name when unique (e.g. `search#RAG Replay Tests`, `cli#search#Indexing`).\n- **Wiki links**: `[[target]]` or `[[target|alias]]` — cross-references between sections. Can also reference source code: `[[src/foo.ts#myFunction]]`.\n- **Source code links**: Wiki links in `lat.md/` files can reference functions, classes, constants, and methods in TypeScript/JavaScript/Python/Rust/Go/C files. Use the full path: `[[src/config.ts#getConfigDir]]`, `[[src/server.ts#App#listen]]` (class method), `[[lib/utils.py#parse_args]]`, `[[src/lib.rs#Greeter#greet]]` (Rust impl method), `[[src/app.go#Greeter#Greet]]` (Go method), `[[src/app.h#Greeter]]` (C struct). `lat check` validates these exist.\n- **Code refs**: `// @lat: [[section-id]]` (JS/TS/Rust/Go/C) or `# @lat: [[section-id]]` (Python) — ties source code to concepts\n\n# Test specs\n\nKey tests can be described as sections in `lat.md/` files (e.g. `tests.md`). Add frontmatter to require that every leaf section is referenced by a `// @lat:` or `# @lat:` comment in test code:\n\n```markdown\n---\nlat:\n  require-code-mention: true\n---\n# Tests\n\nAuthentication and authorization test specifications.\n\n## User login\n\nVerify credential validation and error handling for the login endpoint.\n\n### Rejects expired tokens\nTokens past their expiry timestamp are rejected with 401, even if otherwise valid.\n\n### Handles missing password\nLogin request without a password field returns 400 with a descriptive error.\n```\n\nEvery section MUST have a description — at least one sentence explaining what the test verifies and why. Empty sections with just a heading are not acceptable. (This is a specific case of the general leading paragraph rule below.)\n\nEach test in code should reference its spec with exactly one comment placed next to the relevant test — not at the top of the file:\n\n```python\n# @lat: [[tests#User login#Rejects expired tokens]]\ndef test_rejects_expired_tokens():\n    ...\n\n# @lat: [[tests#User login#Handles missing password]]\ndef test_handles_missing_password():\n    ...\n```\n\nDo not duplicate refs. One `@lat:` comment per spec section, placed at the test that covers it. `lat check` will flag any spec section not covered by a code reference, and any code reference pointing to a nonexistent section.\n\n# Section structure\n\nEvery section in `lat.md/` **must** have a leading paragraph — at least one sentence immediately after the heading, before any child headings or other block content. The first paragraph must be ≤250 characters (excluding `[[wiki link]]` content). This paragraph serves as the section's overview and is used in search results, command output, and RAG context — keeping it concise guarantees the section's essence is always captured.\n\n```markdown\n# Good Section\n\nBrief overview of what this section documents and why it matters.\n\nMore detail can go in subsequent paragraphs, code blocks, or lists.\n\n## Child heading\n\nDetails about this child topic.\n```\n\n```markdown\n# Bad Section\n\n## Child heading\n\nDetails about this child topic.\n```\n\nThe second example is invalid because `Bad Section` has no leading paragraph. `lat check` validates this rule and reports errors for missing or overly long leading paragraphs.\n",".agents/skills/typescript-expert/references/tsconfig-strict.json":"{\n  \"$schema\": \"https://json.schemastore.org/tsconfig\",\n  \"display\": \"Strict TypeScript 5.x\",\n  \"compilerOptions\": {\n    // =========================================================================\n    // STRICTNESS (Maximum Type Safety)\n    // =========================================================================\n    \"strict\": true,\n    \"noUncheckedIndexedAccess\": true,\n    \"noImplicitOverride\": true,\n    \"noPropertyAccessFromIndexSignature\": true,\n    \"exactOptionalPropertyTypes\": true,\n    \"noFallthroughCasesInSwitch\": true,\n    \"forceConsistentCasingInFileNames\": true,\n    // =========================================================================\n    // MODULE SYSTEM (Modern ESM)\n    // =========================================================================\n    \"module\": \"ESNext\",\n    \"moduleResolution\": \"bundler\",\n    \"resolveJsonModule\": true,\n    \"esModuleInterop\": true,\n    \"allowSyntheticDefaultImports\": true,\n    \"isolatedModules\": true,\n    \"verbatimModuleSyntax\": true,\n    // =========================================================================\n    // OUTPUT\n    // =========================================================================\n    \"target\": \"ES2022\",\n    \"lib\": [\"ES2022\", \"DOM\", \"DOM.Iterable\"],\n    \"declaration\": true,\n    \"declarationMap\": true,\n    \"sourceMap\": true,\n    // =========================================================================\n    // PERFORMANCE\n    // =========================================================================\n    \"skipLibCheck\": true,\n    \"incremental\": true,\n    // =========================================================================\n    // PATH ALIASES\n    // =========================================================================\n    \"baseUrl\": \".\",\n    \"paths\": {\n      \"@/*\": [\"./src/*\"],\n      \"@/components/*\": [\"./src/components/*\"],\n      \"@/lib/*\": [\"./src/lib/*\"],\n      \"@/types/*\": [\"./src/types/*\"],\n      \"@/utils/*\": [\"./src/utils/*\"]\n    },\n    // =========================================================================\n    // JSX (for React projects)\n    // =========================================================================\n    // \"jsx\": \"react-jsx\",\n    // =========================================================================\n    // EMIT\n    // =========================================================================\n    \"noEmit\": true // Let bundler handle emit\n    // \"outDir\": \"./dist\",\n    // \"rootDir\": \"./src\",\n    // =========================================================================\n    // DECORATORS (if needed)\n    // =========================================================================\n    // \"experimentalDecorators\": true,\n    // \"emitDecoratorMetadata\": true\n  },\n  \"include\": [\"src/**/*.ts\", \"src/**/*.tsx\", \"src/**/*.d.ts\"],\n  \"exclude\": [\n    \"node_modules\",\n    \"dist\",\n    \"build\",\n    \"coverage\",\n    \"**/*.test.ts\",\n    \"**/*.spec.ts\"\n  ]\n}\n",".agents/skills/typescript-expert/references/typescript-cheatsheet.md":"# TypeScript Cheatsheet\n\n## Type Basics\n\n```typescript\n// Primitives\nconst name: string = \"John\";\nconst age: number = 30;\nconst isActive: boolean = true;\nconst nothing: null = null;\nconst notDefined: undefined = undefined;\n\n// Arrays\nconst numbers: number[] = [1, 2, 3];\nconst strings: Array<string> = [\"a\", \"b\", \"c\"];\n\n// Tuple\nconst tuple: [string, number] = [\"hello\", 42];\n\n// Object\nconst user: { name: string; age: number } = { name: \"John\", age: 30 };\n\n// Union\nconst value: string | number = \"hello\";\n\n// Literal\nconst direction: \"up\" | \"down\" | \"left\" | \"right\" = \"up\";\n\n// Any vs Unknown\nconst anyValue: any = \"anything\"; // ❌ Avoid\nconst unknownValue: unknown = \"safe\"; // ✅ Prefer, requires narrowing\n```\n\n## Type Aliases & Interfaces\n\n```typescript\n// Type Alias\ntype Point = {\n  x: number;\n  y: number;\n};\n\n// Interface (preferred for objects)\ninterface User {\n  id: string;\n  name: string;\n  email?: string; // Optional\n  readonly createdAt: Date; // Readonly\n}\n\n// Extending\ninterface Admin extends User {\n  permissions: string[];\n}\n\n// Intersection\ntype AdminUser = User & { permissions: string[] };\n```\n\n## Generics\n\n```typescript\n// Generic function\nfunction identity<T>(value: T): T {\n  return value;\n}\n\n// Generic with constraint\nfunction getLength<T extends { length: number }>(item: T): number {\n  return item.length;\n}\n\n// Generic interface\ninterface ApiResponse<T> {\n  data: T;\n  status: number;\n  message: string;\n}\n\n// Generic with default\ntype Container<T = string> = {\n  value: T;\n};\n\n// Multiple generics\nfunction merge<T, U>(obj1: T, obj2: U): T & U {\n  return { ...obj1, ...obj2 };\n}\n```\n\n## Utility Types\n\n```typescript\ninterface User {\n  id: string;\n  name: string;\n  email: string;\n  age: number;\n}\n\n// Partial - all optional\ntype PartialUser = Partial<User>;\n\n// Required - all required\ntype RequiredUser = Required<User>;\n\n// Readonly - all readonly\ntype ReadonlyUser = Readonly<User>;\n\n// Pick - select properties\ntype UserName = Pick<User, \"id\" | \"name\">;\n\n// Omit - exclude properties\ntype UserWithoutEmail = Omit<User, \"email\">;\n\n// Record - key-value map\ntype UserMap = Record<string, User>;\n\n// Extract - extract from union\ntype StringOrNumber = string | number | boolean;\ntype OnlyStrings = Extract<StringOrNumber, string>;\n\n// Exclude - exclude from union\ntype NotString = Exclude<StringOrNumber, string>;\n\n// NonNullable - remove null/undefined\ntype MaybeString = string | null | undefined;\ntype DefinitelyString = NonNullable<MaybeString>;\n\n// ReturnType - get function return type\nfunction getUser() {\n  return { name: \"John\" };\n}\ntype UserReturn = ReturnType<typeof getUser>;\n\n// Parameters - get function parameters\ntype GetUserParams = Parameters<typeof getUser>;\n\n// Awaited - unwrap Promise\ntype ResolvedUser = Awaited<Promise<User>>;\n```\n\n## Conditional Types\n\n```typescript\n// Basic conditional\ntype IsString<T> = T extends string ? true : false;\n\n// Infer keyword\ntype UnwrapPromise<T> = T extends Promise<infer U> ? U : T;\n\n// Distributive conditional\ntype ToArray<T> = T extends any ? T[] : never;\ntype Result = ToArray<string | number>; // string[] | number[]\n\n// NonDistributive\ntype ToArrayNonDist<T> = [T] extends [any] ? T[] : never;\n```\n\n## Template Literal Types\n\n```typescript\ntype Color = \"red\" | \"green\" | \"blue\";\ntype Size = \"small\" | \"medium\" | \"large\";\n\n// Combine\ntype ColorSize = `${Color}-${Size}`;\n// 'red-small' | 'red-medium' | 'red-large' | ...\n\n// Event handlers\ntype EventName = \"click\" | \"focus\" | \"blur\";\ntype EventHandler = `on${Capitalize<EventName>}`;\n// 'onClick' | 'onFocus' | 'onBlur'\n```\n\n## Mapped Types\n\n```typescript\n// Basic mapped type\ntype Optional<T> = {\n  [K in keyof T]?: T[K];\n};\n\n// With key remapping\ntype Getters<T> = {\n  [K in keyof T as `get${Capitalize<string & K>}`]: () => T[K];\n};\n\n// Filter keys\ntype OnlyStrings<T> = {\n  [K in keyof T as T[K] extends string ? K : never]: T[K];\n};\n```\n\n## Type Guards\n\n```typescript\n// typeof guard\nfunction process(value: string | number) {\n  if (typeof value === \"string\") {\n    return value.toUpperCase(); // string\n  }\n  return value.toFixed(2); // number\n}\n\n// instanceof guard\nclass Dog {\n  bark() {}\n}\nclass Cat {\n  meow() {}\n}\n\nfunction makeSound(animal: Dog | Cat) {\n  if (animal instanceof Dog) {\n    animal.bark();\n  } else {\n    animal.meow();\n  }\n}\n\n// in guard\ninterface Bird {\n  fly(): void;\n}\ninterface Fish {\n  swim(): void;\n}\n\nfunction move(animal: Bird | Fish) {\n  if (\"fly\" in animal) {\n    animal.fly();\n  } else {\n    animal.swim();\n  }\n}\n\n// Custom type guard\nfunction isString(value: unknown): value is string {\n  return typeof value === \"string\";\n}\n\n// Assertion function\nfunction assertIsString(value: unknown): asserts value is string {\n  if (typeof value !== \"string\") {\n    throw new Error(\"Not a string\");\n  }\n}\n```\n\n## Discriminated Unions\n\n```typescript\n// With type discriminant\ntype Success<T> = { type: \"success\"; data: T };\ntype Error = { type: \"error\"; message: string };\ntype Loading = { type: \"loading\" };\n\ntype State<T> = Success<T> | Error | Loading;\n\nfunction handle<T>(state: State<T>) {\n  switch (state.type) {\n    case \"success\":\n      return state.data; // T\n    case \"error\":\n      return state.message; // string\n    case \"loading\":\n      return null;\n  }\n}\n\n// Exhaustive check\nfunction assertNever(value: never): never {\n  throw new Error(`Unexpected value: ${value}`);\n}\n```\n\n## Branded Types\n\n```typescript\n// Create branded type\ntype Brand<K, T> = K & { __brand: T };\n\ntype UserId = Brand<string, \"UserId\">;\ntype OrderId = Brand<string, \"OrderId\">;\n\n// Constructor functions\nfunction createUserId(id: string): UserId {\n  return id as UserId;\n}\n\nfunction createOrderId(id: string): OrderId {\n  return id as OrderId;\n}\n\n// Usage - prevents mixing\nfunction getOrder(orderId: OrderId, userId: UserId) {}\n\nconst userId = createUserId(\"user-123\");\nconst orderId = createOrderId(\"order-456\");\n\ngetOrder(orderId, userId); // ✅ OK\n// getOrder(userId, orderId)  // ❌ Error - types don't match\n```\n\n## Module Declarations\n\n```typescript\n// Declare module for untyped package\ndeclare module \"untyped-package\" {\n  export function doSomething(): void;\n  export const value: string;\n}\n\n// Augment existing module\ndeclare module \"express\" {\n  interface Request {\n    user?: { id: string };\n  }\n}\n\n// Declare global\ndeclare global {\n  interface Window {\n    myGlobal: string;\n  }\n}\n```\n\n## TSConfig Essentials\n\n```json\n{\n  \"compilerOptions\": {\n    // Strictness\n    \"strict\": true,\n    \"noUncheckedIndexedAccess\": true,\n    \"noImplicitOverride\": true,\n\n    // Modules\n    \"module\": \"ESNext\",\n    \"moduleResolution\": \"bundler\",\n    \"esModuleInterop\": true,\n\n    // Output\n    \"target\": \"ES2022\",\n    \"lib\": [\"ES2022\", \"DOM\"],\n\n    // Performance\n    \"skipLibCheck\": true,\n    \"incremental\": true,\n\n    // Paths\n    \"baseUrl\": \".\",\n    \"paths\": {\n      \"@/*\": [\"./src/*\"]\n    }\n  }\n}\n```\n\n## Best Practices\n\n```typescript\n// ✅ Prefer interface for objects\ninterface User {\n  name: string;\n}\n\n// ✅ Use const assertions\nconst routes = [\"home\", \"about\"] as const;\n\n// ✅ Use satisfies for validation\nconst config = {\n  api: \"https://api.example.com\",\n} satisfies Record<string, string>;\n\n// ✅ Use unknown over any\nfunction parse(input: unknown) {\n  if (typeof input === \"string\") {\n    return JSON.parse(input);\n  }\n}\n\n// ✅ Explicit return types for public APIs\nexport function getUser(id: string): User | null {\n  // ...\n}\n\n// ❌ Avoid\nconst data: any = fetchData();\ndata.anything.goes.wrong; // No type safety\n```\n"},"files":{".claude/skills/hermes-agent/SKILL.md":"---\nname: hermes-agent\ndescription: Expert in building self-improving AI agents with tool use, multi-platform messaging, and a closed learning loop. Proficient in LLM orchestration, tool integration, session management, and agent autonomy.\n---\n\n# Hermes Agent - Complete Project Guide (A-Z)\n\n> **Purpose of this document:** A single, comprehensive reference that explains everything about the Hermes Agent project — its architecture, source code, features, release history, and design patterns — so that any AI or developer can fully understand the system.\n\n---\n\n## Table of Contents\n\n1. [Project Overview](#1-project-overview)\n2. [Key Features Summary](#2-key-features-summary)\n3. [Installation & Getting Started](#3-installation--getting-started)\n4. [Project Structure](#4-project-structure)\n5. [Core Architecture](#5-core-architecture)\n   - 5.1 [AIAgent Class (run_agent.py)](#51-aiagent-class-run_agentpy)\n   - 5.2 [Tool Orchestration (model_tools.py)](#52-tool-orchestration-model_toolspy)\n   - 5.3 [Toolset System (toolsets.py)](#53-toolset-system-toolsetspy)\n   - 5.4 [Tool Registry (tools/registry.py)](#54-tool-registry-toolsregistrypy)\n   - 5.5 [Session Database (hermes_state.py)](#55-session-database-hermes_statepy)\n   - 5.6 [Constants & Home Directory (hermes_constants.py)](#56-constants--home-directory-hermes_constantspy)\n6. [CLI System](#6-cli-system)\n   - 6.1 [Interactive CLI (cli.py)](#61-interactive-cli-clipy)\n   - 6.2 [CLI Entry Point (hermes_cli/main.py)](#62-cli-entry-point-hermes_climainpy)\n   - 6.3 [Configuration System (hermes_cli/config.py)](#63-configuration-system-hermes_cliconfigpy)\n   - 6.4 [Slash Command Registry (hermes_cli/commands.py)](#64-slash-command-registry-hermes_clicommandspy)\n   - 6.5 [Setup Wizard (hermes_cli/setup.py)](#65-setup-wizard-hermes_clisetupy)\n   - 6.6 [Model Catalog (hermes_cli/models.py)](#66-model-catalog-hermes_climodelspy)\n   - 6.7 [Skin/Theme Engine (hermes_cli/skin_engine.py)](#67-skintheme-engine-hermes_cliskin_enginepy)\n7. [Tool System](#7-tool-system)\n   - 7.1 [Terminal Tool (tools/terminal_tool.py)](#71-terminal-tool-toolsterminal_toolpy)\n   - 7.2 [File Tools (tools/file_tools.py)](#72-file-tools-toolsfile_toolspy)\n   - 7.3 [Web Tools (tools/web_tools.py)](#73-web-tools-toolsweb_toolspy)\n   - 7.4 [Browser Tool (tools/browser_tool.py)](#74-browser-tool-toolsbrowser_toolpy)\n   - 7.5 [Delegate Tool (tools/delegate_tool.py)](#75-delegate-tool-toolsdelegate_toolpy)\n   - 7.6 [MCP Tool (tools/mcp_tool.py)](#76-mcp-tool-toolsmcp_toolpy)\n   - 7.7 [Approval System (tools/approval.py)](#77-approval-system-toolsapprovalpy)\n   - 7.8 [Terminal Backends (tools/environments/)](#78-terminal-backends-toolsenvironments)\n8. [Agent Internals](#8-agent-internals)\n   - 8.1 [Prompt Builder (agent/prompt_builder.py)](#81-prompt-builder-agentprompt_builderpy)\n   - 8.2 [Context Compressor (agent/context_compressor.py)](#82-context-compressor-agentcontext_compressorpy)\n   - 8.3 [Prompt Caching (agent/prompt_caching.py)](#83-prompt-caching-agentprompt_cachingpy)\n   - 8.4 [Auxiliary Client (agent/auxiliary_client.py)](#84-auxiliary-client-agentauxiliary_clientpy)\n   - 8.5 [Display & Spinner (agent/display.py)](#85-display--spinner-agentdisplaypy)\n   - 8.6 [Skill Commands (agent/skill_commands.py)](#86-skill-commands-agentskill_commandspy)\n9. [Messaging Gateway](#9-messaging-gateway)\n   - 9.1 [GatewayRunner (gateway/run.py)](#91-gatewayrunner-gatewayrunpy)\n   - 9.2 [Session Store (gateway/session.py)](#92-session-store-gatewaysessionpy)\n   - 9.3 [Platform Adapters (gateway/platforms/)](#93-platform-adapters-gatewayplatforms)\n10. [Cron Scheduling](#10-cron-scheduling)\n11. [Skills System](#11-skills-system)\n12. [Plugin System](#12-plugin-system)\n13. [Memory System](#13-memory-system)\n14. [ACP Server (IDE Integration)](#14-acp-server-ide-integration)\n15. [API Server](#15-api-server)\n16. [MCP Server Mode](#16-mcp-server-mode)\n17. [RL Training Environments](#17-rl-training-environments)\n18. [Profiles (Multi-Instance)](#18-profiles-multi-instance)\n19. [Security Model](#19-security-model)\n20. [Provider & Model System](#20-provider--model-system)\n21. [Streaming & Reasoning](#21-streaming--reasoning)\n22. [Release History](#22-release-history)\n23. [File Dependency Chain](#23-file-dependency-chain)\n24. [Key Design Patterns](#24-key-design-patterns)\n25. [Configuration Reference](#25-configuration-reference)\n26. [Known Pitfalls](#26-known-pitfalls)\n\n---\n\n## 1. Project Overview\n\n**Hermes Agent** is a self-improving AI agent built by [Nous Research](https://nousresearch.com). It is an open-source (MIT licensed), Python-based project that provides:\n\n- A **full interactive terminal UI** (CLI) for conversing with LLMs\n- A **messaging gateway** supporting 16+ platforms (Telegram, Discord, Slack, WhatsApp, Signal, Matrix, Email, etc.)\n- A **closed learning loop** — the agent creates skills from experience, improves them during use, nudges itself to persist knowledge, searches past conversations, and builds a deepening model of who you are\n- **40+ built-in tools** — terminal execution, file manipulation, web search, browser automation, code execution, image generation, TTS/STT, and more\n- **Any LLM provider** — OpenRouter (200+ models), Nous Portal (400+ models), OpenAI, Anthropic, Hugging Face, GitHub Copilot, z.ai/GLM, Kimi/Moonshot, MiniMax, Alibaba/DashScope, custom endpoints\n- **Six terminal backends** — local, Docker, SSH, Modal (serverless), Daytona (serverless), Singularity (HPC)\n- **Scheduled automations** via built-in cron scheduler\n- **IDE integration** via ACP (Agent Communication Protocol) for VS Code, Zed, JetBrains\n- **MCP integration** — both client (connect to any MCP server) and server (expose Hermes to MCP clients)\n- **RL training** via Atropos environments for training the next generation of tool-calling models\n\n**Tech Stack:**\n\n- Python 3.11+ (core agent, tools, gateway, cron)\n- Node.js (browser automation via agent-browser)\n- SQLite with WAL mode and FTS5 (session storage, full-text search)\n- OpenAI-compatible API (primary inference interface)\n- Anthropic SDK (native Anthropic support)\n- Rich + prompt_toolkit (CLI rendering)\n\n**Repository:** `github.com/NousResearch/hermes-agent`\n**Version:** 0.7.0 (as of April 2026)\n**License:** MIT\n\n---\n\n## 2. Key Features Summary\n\n| Feature                      | Description                                                                                                                                                                      |\n| ---------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| **Terminal UI**              | Full TUI with multiline editing, slash-command autocomplete, conversation history, interrupt-and-redirect, streaming tool output                                                 |\n| **Multi-Platform Messaging** | Telegram, Discord, Slack, WhatsApp, Signal, Matrix, Email, Home Assistant, DingTalk, Feishu/Lark, WeCom, Mattermost, SMS, Webhook — all from a single gateway process            |\n| **Learning Loop**            | Agent-curated memory with periodic nudges, autonomous skill creation, skills self-improve during use, FTS5 session search with LLM summarization, Honcho dialectic user modeling |\n| **Scheduled Tasks**          | Built-in cron scheduler with delivery to any platform (daily reports, nightly backups, weekly audits)                                                                            |\n| **Subagent Delegation**      | Spawn isolated subagents for parallel workstreams with restricted toolsets                                                                                                       |\n| **Execute Code**             | Python scripts that call tools via RPC, collapsing multi-step pipelines into zero-context-cost turns                                                                             |\n| **Terminal Backends**        | Local, Docker, SSH, Modal, Daytona, Singularity — run on a $5 VPS or a GPU cluster                                                                                               |\n| **Skills**                   | 70+ bundled skills across 28 categories, Skills Hub for community discovery, agentskills.io compatibility                                                                        |\n| **Plugins**                  | Drop-in Python plugins with lifecycle hooks (pre_llm_call, post_llm_call, on_session_start, on_session_end)                                                                      |\n| **MCP**                      | Client (connect to MCP servers for extended tools) and Server (expose conversations to MCP clients)                                                                              |\n| **IDE Integration**          | VS Code, Zed, JetBrains via ACP server with session management and tool streaming                                                                                                |\n| **API Server**               | OpenAI-compatible `/v1/chat/completions` endpoint for headless integrations                                                                                                      |\n| **Profiles**                 | Multi-instance support — each profile gets isolated config, memory, sessions, skills, gateway                                                                                    |\n| **Security**                 | Command approval system, secret redaction, SSRF protection, PII redaction, injection detection, credential directory protection                                                  |\n| **RL Training**              | Atropos environments for batch trajectory generation and agent policy optimization                                                                                               |\n\n---\n\n## 3. Installation & Getting Started\n\n```bash\n# One-line install (Linux, macOS, WSL2)\ncurl -fsSL https://raw.githubusercontent.com/NousResearch/hermes-agent/main/scripts/install.sh | bash\n\n# After install\nsource ~/.bashrc    # or: source ~/.zshrc\nhermes              # start chatting\n\n# Key commands\nhermes model        # Choose LLM provider and model\nhermes tools        # Configure which tools are enabled\nhermes config set   # Set individual config values\nhermes gateway      # Start the messaging gateway\nhermes setup        # Run the full setup wizard\nhermes update       # Update to latest version\nhermes doctor       # Diagnose any issues\n```\n\n**For development:**\n\n```bash\ngit clone https://github.com/NousResearch/hermes-agent.git\ncd hermes-agent\ncurl -LsSf https://astral.sh/uv/install.sh | sh\nuv venv venv --python 3.11\nsource venv/bin/activate\nuv pip install -e \".[all,dev]\"\npython -m pytest tests/ -q    # ~3000 tests\n```\n\n---\n\n## 4. Project Structure\n\n```\nhermes-agent/\n├── run_agent.py              # AIAgent class — core conversation loop\n├── model_tools.py            # Tool orchestration, _discover_tools(), handle_function_call()\n├── toolsets.py               # Toolset definitions, _HERMES_CORE_TOOLS list\n├── toolset_distributions.py  # Toolset sampling distributions for RL\n├── cli.py                    # HermesCLI class — interactive CLI orchestrator\n├── hermes_state.py           # SessionDB — SQLite session store (FTS5 search)\n├── hermes_constants.py       # Shared constants, get_hermes_home()\n├── hermes_time.py            # Timezone handling\n├── utils.py                  # Shared utility functions\n├── batch_runner.py           # Parallel batch processing\n├── trajectory_compressor.py  # Trajectory compression for RL training\n├── mcp_serve.py              # MCP server mode entry point\n├── mini_swe_runner.py        # Minimal SWE benchmark runner\n├── rl_cli.py                 # RL CLI commands\n│\n├── agent/                    # Agent internals\n│   ├── prompt_builder.py         # System prompt assembly\n│   ├── context_compressor.py     # Auto context compression\n│   ├── prompt_caching.py         # Anthropic prompt caching\n│   ├── auxiliary_client.py       # Auxiliary LLM client (vision, summarization)\n│   ├── model_metadata.py         # Model context lengths, token estimation\n│   ├── models_dev.py             # models.dev registry integration\n│   ├── display.py                # KawaiiSpinner, tool preview formatting\n│   ├── skill_commands.py         # Skill slash commands (shared CLI/gateway)\n│   └── trajectory.py             # Trajectory saving helpers\n│\n├── hermes_cli/               # CLI subcommands and setup\n│   ├── main.py               # Entry point — all `hermes` subcommands\n│   ├── config.py             # DEFAULT_CONFIG, OPTIONAL_ENV_VARS, migration\n│   ├── commands.py           # Slash command definitions + SlashCommandCompleter\n│   ├── callbacks.py          # Terminal callbacks (clarify, sudo, approval)\n│   ├── setup.py              # Interactive setup wizard\n│   ├── skin_engine.py        # Skin/theme engine\n│   ├── skills_config.py      # `hermes skills` — skill management\n│   ├── tools_config.py       # `hermes tools` — tool management\n│   ├── skills_hub.py         # Skills Hub integration\n│   ├── models.py             # Model catalog, provider model lists\n│   ├── model_switch.py       # Shared /model switch pipeline\n│   └── auth.py               # Provider credential resolution\n│\n├── tools/                    # Tool implementations (one file per tool)\n│   ├── registry.py           # Central tool registry\n│   ├── approval.py           # Dangerous command detection\n│   ├── terminal_tool.py      # Terminal/shell execution\n│   ├── process_registry.py   # Background process management\n│   ├── file_tools.py         # File read/write/search/patch\n│   ├── web_tools.py          # Web search/extract\n│   ├── browser_tool.py       # Browser automation\n│   ├── code_execution_tool.py # execute_code sandbox\n│   ├── delegate_tool.py      # Subagent delegation\n│   ├── mcp_tool.py           # MCP client integration\n│   ├── skills_tool.py        # Skill management tool\n│   ├── todo_tool.py          # Todo/task tracking tool\n│   ├── memory_tool.py        # Memory read/write tool\n│   ├── tts_tool.py           # Text-to-speech\n│   ├── vision_tool.py        # Image analysis\n│   ├── image_gen_tool.py     # Image generation\n│   └── environments/         # Terminal backends\n│       ├── base.py               # BaseEnvironment ABC\n│       ├── local.py              # Local execution\n│       ├── docker.py             # Docker containers\n│       ├── ssh.py                # SSH remote execution\n│       ├── modal.py              # Modal serverless\n│       ├── managed_modal.py      # Nous-hosted Modal\n│       ├── daytona.py            # Daytona serverless\n│       ├── singularity.py        # Singularity HPC containers\n│       └── persistent_shell.py   # Persistent shell mixin\n│\n├── gateway/                  # Messaging platform gateway\n│   ├── run.py                # GatewayRunner — main message loop\n│   ├── session.py            # SessionStore — conversation persistence\n│   ├── status.py             # Gateway status, token locks\n│   └── platforms/            # 16 platform adapters\n│       ├── base.py               # BasePlatformAdapter ABC\n│       ├── telegram.py           # Telegram (polling + webhook)\n│       ├── discord.py            # Discord\n│       ├── slack.py              # Slack\n│       ├── whatsapp.py           # WhatsApp\n│       ├── matrix.py             # Matrix (E2EE)\n│       ├── signal.py             # Signal\n│       ├── email.py              # Email (IMAP/SMTP)\n│       ├── homeassistant.py      # Home Assistant\n│       ├── sms.py                # SMS (Twilio)\n│       ├── mattermost.py         # Mattermost\n│       ├── dingtalk.py           # DingTalk\n│       ├── feishu.py             # Feishu/Lark\n│       ├── wecom.py              # WeCom (Enterprise WeChat)\n│       ├── webhook.py            # Generic webhook\n│       └── api_server.py         # OpenAI-compatible API server\n│\n├── acp_adapter/              # ACP server (IDE integration)\n│   ├── server.py             # HermesACPAgent class\n│   ├── session.py            # SessionManager\n│   ├── events.py             # Streaming callbacks\n│   ├── permissions.py        # Approval callbacks\n│   └── entry.py              # Entry point\n│\n├── cron/                     # Scheduler\n│   ├── scheduler.py          # tick() — job execution engine\n│   └── jobs.py               # Job storage and CRUD\n│\n├── plugins/                  # Plugin system\n│   └── memory/               # 8 memory provider plugins\n│       ├── openviking/\n│       ├── mem0/\n│       ├── hindsight/\n│       ├── holographic/\n│       ├── honcho/\n│       ├── retaindb/\n│       └── byterover/\n│\n├── environments/             # RL training environments (Atropos)\n│   ├── hermes_base_env.py    # Abstract base RL environment\n│   ├── agent_loop.py         # HermesAgentLoop — rollout execution\n│   ├── tool_context.py       # ToolContext — sandbox for RL\n│   ├── web_research_env.py   # Web research tasks\n│   └── agentic_opd_env.py    # Observation-Prediction-Demo env\n│\n├── skills/                   # 70+ bundled skills across 28 categories\n├── optional-skills/          # Additional optional skills\n├── tests/                    # ~3000 pytest tests\n├── scripts/                  # Install, update, packaging scripts\n├── docker/                   # Docker build files\n├── docs/                     # Documentation source (Docusaurus)\n├── website/                  # Landing page\n├── desktop/                  # Desktop app (Electron, separate repo)\n├── tinker-atropos/           # RL submodule\n│\n├── pyproject.toml            # Python package config\n├── AGENTS.md                 # Developer guide for AI assistants\n├── RELEASE_v0.2.0.md → v0.7.0.md  # Release notes\n└── cli-config.yaml.example   # Example config\n```\n\n**User config directory:** `~/.hermes/`\n\n```\n~/.hermes/\n├── config.yaml           # User settings\n├── .env                  # API keys and secrets\n├── MEMORY.md             # Persistent agent memory\n├── USER.md               # User profile\n├── SOUL.md               # Agent personality/identity\n├── sessions.db           # SQLite session database\n├── skills/               # User-installed skills\n├── skins/                # Custom CLI themes\n├── plugins/              # User plugins\n├── cron/                 # Cron jobs and output\n│   ├── jobs.json\n│   └── output/\n├── cache/                # Image/audio cache\n├── plans/                # Generated plans\n├── profiles/             # Multi-instance profiles\n└── mcp/                  # MCP server configs\n```\n\n---\n\n## 5. Core Architecture\n\n### 5.1 AIAgent Class (run_agent.py)\n\nThe `AIAgent` class is the heart of the system — the core conversation loop that orchestrates LLM calls, tool execution, context management, and response delivery.\n\n**Constructor (~60 parameters):**\n\n```python\nclass AIAgent:\n    def __init__(self,\n        model: str = \"anthropic/claude-opus-4.6\",\n        max_iterations: int = 90,\n        enabled_toolsets: list = None,\n        disabled_toolsets: list = None,\n        quiet_mode: bool = False,\n        save_trajectories: bool = False,\n        platform: str = None,            # \"cli\", \"telegram\", etc.\n        session_id: str = None,\n        session_db: SessionDB = None,\n        skip_context_files: bool = False,\n        skip_memory: bool = False,\n        base_url: str = None,\n        api_key: str = None,\n        provider: str = None,\n        api_mode: str = \"chat_completions\",  # or \"anthropic_messages\" or \"codex_responses\"\n        tool_progress_callback = None,\n        stream_delta_callback = None,\n        thinking_callback = None,\n        status_callback = None,\n        iteration_budget: IterationBudget = None,\n        credential_pool = None,\n        checkpoints_enabled: bool = False,\n        # ... plus provider, routing, callback params\n    )\n```\n\n**Main Methods:**\n\n| Method                                                                          | Returns  | Purpose                                                                            |\n| ------------------------------------------------------------------------------- | -------- | ---------------------------------------------------------------------------------- |\n| `chat(message, stream_callback)`                                                | `str`    | Simple interface — returns final response text                                     |\n| `run_conversation(user_message, system_message, conversation_history, task_id)` | `dict`   | Full interface — returns `{final_response, messages, completed, api_calls, error}` |\n| `_interruptible_api_call(api_kwargs)`                                           | Response | Runs API request in background thread with interrupt support                       |\n| `_interruptible_streaming_api_call(api_kwargs, on_first_delta)`                 | Response | Streaming variant with delta callbacks                                             |\n\n**The Core Agent Loop** (inside `run_conversation()`):\n\n```python\nwhile api_call_count < self.max_iterations and self.iteration_budget.remaining > 0:\n    response = client.chat.completions.create(\n        model=model, messages=messages, tools=tool_schemas\n    )\n    if response.tool_calls:\n        for tool_call in response.tool_calls:\n            result = handle_function_call(tool_call.name, tool_call.args, task_id)\n            messages.append(tool_result_message(result))\n        api_call_count += 1\n    else:\n        return response.content  # Final text response\n```\n\n**Key Behaviors:**\n\n- **Three API modes:** `chat_completions` (OpenAI-compatible), `anthropic_messages` (Anthropic SDK), `codex_responses` (OpenAI Codex)\n- **Parallel tool execution:** Independent tool calls run concurrently via ThreadPoolExecutor (unless they share file paths or are in the never-parallel list)\n- **Interrupt support:** Background threads allow interrupt detection without blocking on HTTP\n- **Error recovery:** Automatic fallback chain (primary → fallback model), retry with exponential backoff, context compression on token overflow\n- **Budget pressure:** Warnings at 70% (caution) and 90% (urgent) of iteration budget\n- **Oversized results:** Tool results >100K chars are saved to temp files with a preview\n- **Stale connection detection:** 90s timeout for streaming, 60s read timeout\n\n**IterationBudget** (thread-safe):\n\n```python\nclass IterationBudget:\n    def consume() -> bool    # Check and consume one iteration\n    def refund()             # Give back iteration (for execute_code turns)\n    @property remaining      # Remaining iterations\n```\n\n---\n\n### 5.2 Tool Orchestration (model_tools.py)\n\nBridges the agent and tool registry — handles discovery, schema generation, and dispatch.\n\n**Key Functions:**\n\n| Function                                                                                | Purpose                                                                   |\n| --------------------------------------------------------------------------------------- | ------------------------------------------------------------------------- |\n| `_discover_tools()`                                                                     | Imports all tool modules (each calls `registry.register()` on import)     |\n| `get_tool_definitions(enabled_toolsets, disabled_toolsets, quiet_mode)`                 | Returns OpenAI-format tool schemas filtered by toolset                    |\n| `handle_function_call(function_name, function_args, task_id, user_task, enabled_tools)` | Main dispatcher — routes calls to registry with arg coercion              |\n| `coerce_tool_args(tool_name, args)`                                                     | Type coercion for LLM-generated arguments (string→int, string→bool, etc.) |\n\n**Tool Discovery Order:**\n\n1. Static tools (web_tools, terminal_tool, file_tools, browser_tool, etc.)\n2. Optional tools (fal_client for image gen, honcho, etc.) — graceful fallback if missing\n3. Plugin-registered tools\n4. MCP server tools (dynamic, via tools/list_changed notifications)\n\n**Special Tool Handling:**\n\n- **Agent-level tools** (todo, memory, session_search, delegate_task): Intercepted by `run_agent.py` before `handle_function_call()`\n- **execute_code**: Passes `enabled_tools` for sandbox tool list\n- **Dynamic schema adjustments**: `browser_navigate` strips web_search reference if tools unavailable\n\n**Async Bridging:**\n\n- Persistent event loops (not `asyncio.run()`) to prevent \"Event loop is closed\" errors\n- Main thread uses shared loop; worker threads get per-thread loops\n- `_run_async()` detects running loop and spins up disposable thread if needed\n\n---\n\n### 5.3 Toolset System (toolsets.py)\n\nProvides flexible tool grouping and composition.\n\n**Core Toolsets:**\n\n| Toolset          | Tools Included                                                                                   |\n| ---------------- | ------------------------------------------------------------------------------------------------ |\n| `web`            | web_search, web_extract, web_crawl                                                               |\n| `terminal`       | terminal                                                                                         |\n| `file`           | read_file, write_file, edit_file, list_files, search_files                                       |\n| `browser`        | browser_navigate, browser_snapshot, browser_click, browser_type, browser_scroll, browser_extract |\n| `vision`         | analyze_image                                                                                    |\n| `image_gen`      | generate_image                                                                                   |\n| `tts`            | text_to_speech                                                                                   |\n| `todo`           | todo_read, todo_write                                                                            |\n| `memory`         | memory_read, memory_write                                                                        |\n| `session_search` | session_search                                                                                   |\n| `delegation`     | delegate_task                                                                                    |\n| `code_execution` | execute_code                                                                                     |\n| `cronjob`        | create_job, list_jobs, delete_job                                                                |\n| `messaging`      | send_message                                                                                     |\n| `homeassistant`  | ha_get_states, ha_call_service, ...                                                              |\n\n**Composite Toolsets:**\n\n- `hermes-cli` — All core tools for CLI platform\n- `hermes-telegram`, `hermes-discord`, etc. — Platform-specific tool sets\n- `hermes-gateway` — Union of all platform tools\n- `debugging` — terminal + file + web\n- `safe` — Everything except terminal\n\n**Resolution:**\n\n```python\nresolve_toolset(name, visited=None) → List[str]\n# Recursively resolves toolset to tool names\n# Handles composition (includes) and cycle detection\n# Special aliases: \"all\" or \"*\" = all tools\n```\n\n---\n\n### 5.4 Tool Registry (tools/registry.py)\n\nSingleton managing all tool schemas and handlers. Circular-import safe — has no tool dependencies.\n\n**ToolEntry** (per-tool metadata):\n\n```python\n@dataclass(slots=True)\nclass ToolEntry:\n    name: str\n    toolset: str\n    schema: dict           # OpenAI-format tool definition\n    handler: Callable      # Sync or async handler function\n    check_fn: Callable     # Returns True if tool is available\n    requires_env: list     # Required environment variables\n    is_async: bool\n    description: str\n    emoji: str\n```\n\n**Key Methods:**\n\n```python\nregistry.register(name, toolset, schema, handler, check_fn, requires_env)\nregistry.get_definitions(tool_names, quiet)  # Returns filtered schemas\nregistry.dispatch(name, args, **kwargs)      # Execute with async bridging\nregistry.deregister(name)                    # Remove (for MCP tool refresh)\nregistry.check_tool_availability()           # Returns (available, unavailable)\n```\n\n---\n\n### 5.5 Session Database (hermes_state.py)\n\nSQLite-based persistent session storage with FTS5 full-text search.\n\n**Schema (v6):**\n\n```sql\n-- Sessions table\nsessions (\n    id TEXT PRIMARY KEY,\n    source TEXT, user_id TEXT, model TEXT, model_config TEXT,\n    system_prompt TEXT, parent_session_id TEXT,\n    started_at TEXT, ended_at TEXT, end_reason TEXT,\n    message_count INTEGER, tool_call_count INTEGER,\n    input_tokens INTEGER, output_tokens INTEGER,\n    cache_read_tokens INTEGER, cache_write_tokens INTEGER, reasoning_tokens INTEGER,\n    estimated_cost_usd REAL, actual_cost_usd REAL,\n    title TEXT  -- UNIQUE INDEX\n)\n\n-- Messages table\nmessages (\n    id INTEGER PRIMARY KEY AUTOINCREMENT,\n    session_id TEXT, role TEXT, content TEXT,\n    tool_call_id TEXT, tool_calls TEXT,  -- JSON\n    tool_name TEXT, timestamp TEXT,\n    token_count INTEGER, finish_reason TEXT,\n    reasoning TEXT, reasoning_details TEXT, codex_reasoning_items TEXT\n)\n\n-- FTS5 virtual table (auto-synced via triggers)\nmessages_fts (content)\n```\n\n**Concurrency Model:**\n\n- WAL (Write-Ahead Logging) for concurrent readers + single writer\n- `BEGIN IMMEDIATE` for write transactions (lock at start, not commit)\n- Jitter retry on lock: 20-150ms random backoff, max 15 retries\n- Periodic WAL checkpoint every 50 writes\n\n**Key Operations:**\n\n- `create_session()`, `end_session()`, `reopen_session()`\n- `add_message()`, `get_messages()`\n- `search_sessions(query)` — FTS5 full-text search\n- `update_token_counts()` — Supports both incremental (CLI) and absolute (gateway) modes\n\n---\n\n### 5.6 Constants & Home Directory (hermes_constants.py)\n\nImport-safe constants module with no circular dependencies.\n\n```python\nget_hermes_home() → Path          # HERMES_HOME env var or ~/.hermes\ndisplay_hermes_home() → str       # User-friendly display: \"~/.hermes\"\nget_optional_skills_dir() → Path  # HERMES_OPTIONAL_SKILLS env var\nparse_reasoning_effort(str) → Dict  # \"high\" → {\"enabled\": True, \"effort\": \"high\"}\n\n# Key constants\nOPENROUTER_BASE_URL = \"https://openrouter.ai/api/v1\"\nNOUS_API_BASE_URL = \"https://inference-api.nousresearch.com/v1\"\nAI_GATEWAY_BASE_URL = \"https://ai-gateway.vercel.sh/v1\"\nVALID_REASONING_EFFORTS = (\"xhigh\", \"high\", \"medium\", \"low\", \"minimal\")\n```\n\n---\n\n## 6. CLI System\n\n### 6.1 Interactive CLI (cli.py)\n\nThe `HermesCLI` class provides the interactive terminal interface.\n\n**Features:**\n\n- **Rich** for banner/panels, **prompt_toolkit** for input with autocomplete\n- **KawaiiSpinner** — animated faces during API calls, `┊` activity feed for tool results\n- Multiline editing with Shift+Enter\n- Slash-command autocomplete\n- Session history with up/down arrow navigation\n- Clipboard image paste (Alt+V / Ctrl+V)\n- Status bar showing model, provider, and token counts\n- Inline diff previews for file write/patch operations\n\n**Configuration Loading:**\n\n```python\nload_cli_config() → dict\n# Loads from ~/.hermes/config.yaml (or ./cli-config.yaml fallback)\n# Merges with hardcoded defaults\n# Expands ${ENV_VAR} references\n# Maps terminal config → env vars\n```\n\n---\n\n### 6.2 CLI Entry Point (hermes_cli/main.py)\n\nAll `hermes` subcommands are dispatched from here:\n\n```\nhermes                    # Default: interactive chat\nhermes chat               # Explicit interactive mode\nhermes gateway start|stop|status|install|uninstall\nhermes setup              # Setup wizard\nhermes model              # Select model/provider\nhermes tools              # Configure tools\nhermes skills             # Manage skills\nhermes config set|get     # Direct config manipulation\nhermes cron list|delete   # Cron job management\nhermes doctor             # Diagnose issues\nhermes sessions browse    # Session picker\nhermes profile create|list|switch|delete|export|import\nhermes mcp serve|add|remove  # MCP management\nhermes acp                # Start ACP server\nhermes update|uninstall|version\n```\n\n**Profile System:**\n\n- `_apply_profile_override()` runs BEFORE any imports to set `HERMES_HOME`\n- Pre-parses `--profile/-p` from argv\n- Allows fully isolated agent instances with separate config, memory, sessions, skills\n\n---\n\n### 6.3 Configuration System (hermes_cli/config.py)\n\n**Key Configuration Sections:**\n\n```yaml\nmodel: \"anthropic/claude-opus-4.6\" # or dict with provider/base_url/api_key\nproviders: {} # Provider-specific configs\nfallback_providers: [] # Ordered failover list\ncredential_pool: {} # Multiple API keys per provider\n\nagent:\n  max_turns: 90\n  gateway_timeout: 1800\n  tool_use_enforcement: \"auto\"\n\nterminal:\n  backend: \"local\" # local|docker|modal|daytona|ssh|singularity\n  timeout: 180\n  persistent_shell: true\n  docker_image: \"nikolaik/python-nodejs:...\"\n\ncompression:\n  enabled: true\n  threshold: 0.50 # Compress when 50% of context used\n  target_ratio: 0.20 # Summary = 20% of compressed content\n  protect_last_n: 20\n\nauxiliary:\n  vision: { provider, model }\n  web_extract: { provider, model }\n  compression: { provider, model }\n\nmemory:\n  memory_enabled: true\n  provider: \"\" # \"\" | \"honcho\" | \"mem0\" | etc.\n  memory_char_limit: 2200\n\ndisplay:\n  personality: \"kawaii\"\n  show_reasoning: false\n  inline_diffs: true\n  skin: \"default\"\n  streaming: true\n\ntts:\n  provider: \"edge\" # edge|elevenlabs|openai|neutts\n\nstt:\n  enabled: true\n  provider: \"local\" # local|groq|openai\n\nprivacy:\n  redact_pii: false\n\nmcp_servers: {} # MCP server configurations\n\nskills:\n  external_dirs: [] # Additional skill directories\n\napprovals:\n  mode: \"smart\" # smart|always|off\n```\n\n**Config Files:**\n\n- `~/.hermes/config.yaml` — User settings (authoritative)\n- `~/.hermes/.env` — API keys and secrets\n- Config version migration system (currently v5)\n\n---\n\n### 6.4 Slash Command Registry (hermes_cli/commands.py)\n\nAll slash commands defined centrally in `COMMAND_REGISTRY`:\n\n```python\nCommandDef(name, description, category, aliases, args_hint, cli_only, gateway_only)\n```\n\n**Derived automatically by:**\n\n- CLI `process_command()` — dispatch on canonical name\n- Gateway dispatch + help\n- Telegram BotCommand menu\n- Slack `/hermes` subcommands\n- Autocomplete + help text\n\n**Key Commands:**\n\n| Command        | Aliases    | Description                   |\n| -------------- | ---------- | ----------------------------- |\n| `/new`         | `/reset`   | Start fresh conversation      |\n| `/model`       |            | Show/switch model             |\n| `/personality` |            | Set agent personality         |\n| `/retry`       |            | Retry last turn               |\n| `/undo`        |            | Remove last turn              |\n| `/compress`    | `/compact` | Compress context              |\n| `/usage`       | `/cost`    | Show token usage              |\n| `/insights`    |            | Usage analytics               |\n| `/skills`      |            | Browse/install skills         |\n| `/background`  | `/bg`      | Manage background processes   |\n| `/plan`        |            | Generate implementation plan  |\n| `/rollback`    |            | Restore filesystem checkpoint |\n| `/verbose`     |            | Toggle debug output           |\n| `/reasoning`   |            | Set reasoning effort          |\n| `/yolo`        |            | Toggle approval bypass        |\n| `/btw`         |            | Ephemeral side question       |\n| `/stop`        |            | Kill current agent run        |\n| `/queue`       |            | Queue next prompt             |\n| `/browser`     |            | Interactive browser session   |\n| `/history`     | `/resume`  | Session browser               |\n| `/skin`        |            | Switch CLI theme              |\n\n---\n\n### 6.5 Setup Wizard (hermes_cli/setup.py)\n\nModular interactive wizard with independent sections:\n\n1. **Model & Provider** — Select AI provider, enter API keys, choose model\n2. **Terminal Backend** — Choose execution environment\n3. **Agent Settings** — Max iterations, compression, session policies\n4. **Messaging Platforms** — Configure Telegram, Discord, Slack, etc.\n5. **Tools** — TTS, STT, web search, image generation, browser\n\nFeatures:\n\n- Live credential validation\n- Real-time model list fetching from provider APIs\n- Automatic OpenClaw migration detection\n- Atomic config file writes\n\n---\n\n### 6.6 Model Catalog (hermes_cli/models.py)\n\nProvider-specific model lists:\n\n```python\n_PROVIDER_MODELS = {\n    \"nous\": [\"anthropic/claude-opus-4.6\", \"anthropic/claude-sonnet-4.6\", ...],  # 25+\n    \"openrouter\": [\"anthropic/claude-opus-4.6\", \"google/gemini-3-flash\", ...],  # 30+\n    \"anthropic\": [\"claude-opus-4-6\", \"claude-sonnet-4-6\", ...],\n    \"openai\": [\"gpt-5\", \"gpt-5.4-mini\", \"gpt-4.1\", \"gpt-4o\", ...],\n    \"copilot\": [\"gpt-5.4\", \"gpt-5.4-mini\", \"gpt-5.3-codex\", ...],\n    \"huggingface\": [...],\n    \"minimax\": [...],\n    \"kimi-coding\": [...],\n    \"alibaba\": [...],\n    \"deepseek\": [...],\n    # ... more providers\n}\n```\n\nFeatures:\n\n- Dynamic fetching via provider `/models` endpoints\n- Curated lists used when live probe returns fewer models\n- Fuzzy matching for typo correction\n- Validation against provider catalog\n\n---\n\n### 6.7 Skin/Theme Engine (hermes_cli/skin_engine.py)\n\nData-driven CLI visual customization — no code changes needed.\n\n**Customizable Elements:**\n\n| Element                          | Key                      | Used By           |\n| -------------------------------- | ------------------------ | ----------------- |\n| Banner border/title/accent       | `colors.*`               | banner.py         |\n| Response box border              | `colors.response_border` | cli.py            |\n| Spinner faces (waiting/thinking) | `spinner.*`              | display.py        |\n| Spinner verbs/wings              | `spinner.*`              | display.py        |\n| Tool output prefix               | `tool_prefix`            | display.py        |\n| Per-tool emojis                  | `tool_emojis`            | display.py        |\n| Agent name/welcome/prompt        | `branding.*`             | banner.py, cli.py |\n\n**Built-in Skins:** default, ares, mono, slate, poseidon, sisyphus, charizard\n\n**User Skins:** Drop `~/.hermes/skins/<name>.yaml` and activate with `/skin <name>`\n\n---\n\n## 7. Tool System\n\n### 7.1 Terminal Tool (tools/terminal_tool.py)\n\nShell command execution across multiple backends.\n\n```python\ndef terminal_tool(\n    command: str,\n    background: bool = False,\n    timeout: Optional[int] = None,\n    task_id: Optional[str] = None,\n    force: bool = False,        # Skip approval for dangerous commands\n    workdir: Optional[str] = None,\n    check_interval: Optional[int] = None,  # Background task polling\n    pty: bool = False,\n) -> str  # JSON result\n```\n\n**Features:**\n\n- Multi-backend: Selects based on `TERMINAL_ENV` (local/docker/ssh/modal/daytona/singularity)\n- Per-task_id sandboxes with thread-safe creation locks\n- Dangerous command routing through approval system\n- Background task support with file-based IPC\n- Interrupt handling — polls `is_interrupted()` during execution\n- Auto-cleanup daemon thread for idle environments (>300s)\n- Disk usage warnings at configurable threshold\n\n---\n\n### 7.2 File Tools (tools/file_tools.py)\n\nSafe file operations with size guards and sensitive path protection.\n\n- `read_file_tool(path, offset, limit)` — Read with pagination (default 100K char limit)\n- `write_file_tool(path, content)` — Write with approval for sensitive paths\n- `edit_file_tool(path, old_text, new_text)` — String replacement editing\n- `list_files_tool(path)` — Directory listing\n- `search_files_tool(pattern, path)` — Glob/regex file search\n\n**Safety:**\n\n- Device path blocklist (`/dev/zero`, `/dev/stdin`, etc.)\n- Read dedup tracking — returns stub on re-read if mtime unchanged\n- Sensitive path blocking: `/etc/`, `/boot/`, `~/.ssh` without approval\n- Prompt injection protection for known dangerous paths\n\n---\n\n### 7.3 Web Tools (tools/web_tools.py)\n\nWeb search and content extraction.\n\n- `web_search_tool(query, limit)` — Search via configurable backend\n- `web_extract_tool(urls, format)` — Extract content from URLs\n- `web_crawl_tool(url, instructions)` — LLM-guided web crawling\n\n**Backends:** Firecrawl, Parallel Web, Tavily, Exa, DuckDuckGo\n\n- Fallback detection by highest-priority available API key\n- LLM processing via auxiliary client (Gemini 3 Flash) for intelligent extraction\n- SSRF protection and URL safety checks\n\n---\n\n### 7.4 Browser Tool (tools/browser_tool.py)\n\nHeadless browser automation with accessibility tree.\n\n```python\nbrowser_navigate(url, task_id) → str\nbrowser_snapshot(task_id, max_chars) → str\nbrowser_click(ref, task_id) → str      # Element refs like @e1, @e2\nbrowser_type(ref, text, task_id) → str\nbrowser_scroll(ref, direction, task_id) → str\nbrowser_extract(task, max_chars, task_id) → str\n```\n\n**Backends:**\n\n- **Local:** Headless Chromium via `agent-browser` CLI\n- **Cloud:** Browserbase (stealth, proxies, CAPTCHA solving)\n- **Camofox:** Anti-detection browser using Camoufox\n\n**Features:**\n\n- Accessibility tree for text-based snapshots (no vision required)\n- Session isolation per task_id with inactivity cleanup\n- API key detection in URLs (prevents exfiltration)\n- SSRF protection for private/internal addresses\n\n---\n\n### 7.5 Delegate Tool (tools/delegate_tool.py)\n\nSpawn isolated subagents for parallel workstreams.\n\n```python\ndef delegate_task(\n    goal: str,\n    context: str = None,\n    toolsets: List[str] = None,       # Default: [\"terminal\", \"file\", \"web\"]\n    tasks: List[Dict] = None,          # Batch mode: up to 3 concurrent\n    max_iterations: int = 50,\n    parent_agent = None,\n) → str\n```\n\n**Isolation:**\n\n- Fresh conversation (no parent history)\n- Own task_id (separate terminal session, file ops)\n- Restricted toolset (configurable, blocked tools always stripped)\n- Focused system prompt from goal + context\n- Parent only sees delegation call and summary result\n\n**Blocked Tools:** delegate_task, clarify, memory, send_message, execute_code (no recursion, no user interaction)\n\n**Constraints:** MAX_DEPTH=2, MAX_CONCURRENT_CHILDREN=3\n\n---\n\n### 7.6 MCP Tool (tools/mcp_tool.py)\n\nModel Context Protocol client integration.\n\n**Configuration (config.yaml):**\n\n```yaml\nmcp_servers:\n  filesystem:\n    command: \"npx\"\n    args: [\"-y\", \"@modelcontextprotocol/server-filesystem\", \"/tmp\"]\n    timeout: 120\n  remote_api:\n    url: \"https://my-mcp-server.example.com/mcp\"\n    headers:\n      Authorization: \"Bearer sk-...\"\n    sampling:\n      enabled: true\n      model: \"gemini-3-flash\"\n```\n\n**Features:**\n\n- **Transports:** Stdio (local processes) and HTTP/StreamableHTTP (remote)\n- **Dynamic tool discovery:** Listens for `tools/list_changed` notifications\n- **Sampling support:** MCP servers can request LLM completions\n- **Automatic reconnection** with exponential backoff (5 retries)\n- Dedicated background event loop in daemon thread\n- Thread-safe via lock protecting server dict\n\n---\n\n### 7.7 Approval System (tools/approval.py)\n\nDangerous command detection and approval flow.\n\n```python\ndetect_dangerous_command(command: str) → (is_dangerous, pattern_key, description)\n```\n\n**106 patterns covering:**\n\n- Destructive operations (`rm -r /`, `mkfs`, `dd if=`)\n- Privilege escalation (`chmod 777`, `chown -R root`)\n- SQL injection (`DROP TABLE`, `DELETE FROM` without WHERE)\n- System targeting (`/etc/` writes, `systemctl stop`, fork bombs)\n- Shell injection (pipe to sh, wget to sh, process substitution)\n- Network exfiltration (curl with embedded API keys)\n- Secret access (`cat ~/.env`, `cat ~/.netrc`)\n\n**Normalization:** Strips ANSI escapes, null bytes, normalizes Unicode\n\n**Approval Modes:**\n\n- `smart` — Learns which commands are safe based on user decisions\n- `always` — Always ask for dangerous commands\n- `off` — Never ask (or `/yolo` toggle)\n\n---\n\n### 7.8 Terminal Backends (tools/environments/)\n\n| Backend           | File               | Features                                                                       |\n| ----------------- | ------------------ | ------------------------------------------------------------------------------ |\n| **Local**         | `local.py`         | Direct execution, interrupt support, non-blocking I/O, persistent shell        |\n| **Docker**        | `docker.py`        | Sandboxed containers, cap-drop ALL, no-new-privileges, PID limits, bind mounts |\n| **SSH**           | `ssh.py`           | Remote execution via ControlMaster, persistent shell mixin                     |\n| **Modal**         | `modal.py`         | Native Modal SDK `Sandbox.create()`/`Sandbox.exec()`, persistent snapshots     |\n| **Managed Modal** | `managed_modal.py` | Modal through Nous-hosted gateway                                              |\n| **Daytona**       | `daytona.py`       | Daytona SDK cloud sandboxes, stop/resume lifecycle                             |\n| **Singularity**   | `singularity.py`   | Singularity containers with scratch dir, SIF cache                             |\n\n**Common Interface (BaseEnvironment ABC):**\n\n```python\nexecute(command, timeout) → {\"output\": str, \"returncode\": int}\ncleanup() → None\n```\n\n---\n\n## 8. Agent Internals\n\n### 8.1 Prompt Builder (agent/prompt_builder.py)\n\nAssembles the system prompt from multiple sources:\n\n| Component               | Source                                               |\n| ----------------------- | ---------------------------------------------------- |\n| Agent Identity          | `DEFAULT_AGENT_IDENTITY` or `SOUL.md`                |\n| Memory Guidance         | When/how to use memory tool                          |\n| Session Search Guidance | How to recall past conversations                     |\n| Skills Guidance         | When to create/patch skills                          |\n| Tool Use Enforcement    | Must execute tools, not describe actions             |\n| Skills Index            | `~/.hermes/skills/.hermes-skills.json`               |\n| Platform Hints          | OS, Python version, shell, available tools           |\n| Context Files           | `.hermes.md`, `AGENTS.md`, `.cursorrules`, `SOUL.md` |\n| Model/Provider Info     | Current model and provider identity                  |\n\n**Context File Discovery:**\n\n1. Check `cwd/.hermes.md` or `HERMES.md`\n2. Walk parent directories up to git root\n3. Validate against injection patterns before inclusion\n\n**Injection Detection (30+ patterns):**\n\n- \"ignore previous instructions\", \"system prompt override\"\n- \"do not tell the user\", \"act as if you have no restrictions\"\n- HTML comment injection, exfiltration via curl, Unicode stealth chars\n\n---\n\n### 8.2 Context Compressor (agent/context_compressor.py)\n\nAutomatically compresses conversation history when approaching context limits.\n\n```python\nclass ContextCompressor:\n    threshold_percent: float = 0.50   # Compress when 50% of context used\n    protect_first_n: int = 3          # Keep system prompt + first turn\n    protect_last_n: int = 20          # Keep recent N messages\n    summary_target_ratio: float = 0.20  # Summary = 20% of compressed content\n```\n\n**Compression Algorithm:**\n\n1. **Pre-pass:** Replace old tool results with placeholder (cheap)\n2. **Protect head:** System prompt + first exchange always kept\n3. **Protect tail:** Token-budget-based tail protection (~20K tokens)\n4. **Summarize middle:** LLM-generated structured summary (Goal, Progress, Decisions, Files, Next Steps)\n5. **Iterative updates:** On subsequent compressions, update previous summary instead of re-summarizing\n\n---\n\n### 8.3 Prompt Caching (agent/prompt_caching.py)\n\nAnthropic prompt caching support for cost reduction.\n\n- System prompt cached across turns (first conversation turn establishes cache)\n- Cache markers inserted at system prompt boundaries\n- Must not break mid-conversation — altering past context invalidates cache\n- The ONLY time context is altered is during compression\n\n**Critical Rule:** Do NOT implement changes that would alter past context, change toolsets, reload memories, or rebuild system prompts mid-conversation.\n\n---\n\n### 8.4 Auxiliary Client (agent/auxiliary_client.py)\n\nSeparate LLM client for non-primary tasks:\n\n- **Vision analysis** — Image description and analysis\n- **Web extraction** — Content summarization\n- **Context compression** — Generating conversation summaries\n- **Session search** — Summarizing search results\n- **MCP sampling** — Serving server-initiated LLM requests\n\nConfigured per-task via `auxiliary` section in config.yaml.\n\n---\n\n### 8.5 Display & Spinner (agent/display.py)\n\n- **KawaiiSpinner** — Animated face characters during API calls\n- **Tool preview formatting** — `┊` prefixed activity feed for tool execution\n- **Inline diff previews** — Shows unified diffs for write/patch operations\n- Respects active skin for colors, emojis, and branding\n\n---\n\n### 8.6 Skill Commands (agent/skill_commands.py)\n\nShared skill invocation for CLI and gateway:\n\n- Skills loaded from `~/.hermes/skills/` and external directories\n- Injected as **user message** (not system prompt) to preserve prompt caching\n- `/plan` command generates implementation plans stored in `.hermes/plans/`\n- Skill content includes setup instructions, tool options, usage examples\n\n---\n\n## 9. Messaging Gateway\n\n### 9.1 GatewayRunner (gateway/run.py)\n\nMain controller managing all platform adapters and routing messages.\n\n**Key Attributes:**\n\n- `adapters: Dict[Platform, BasePlatformAdapter]` — Active platform instances\n- `session_store: SessionStore` — Conversation persistence\n- `_running_agents: Dict[str, AIAgent]` — Per-session cached agents (preserves prompt caching)\n- `_pending_approvals: Dict[str, Dict]` — Dangerous command approval tracking\n- `pairing_store: PairingStore` — DM code-based user authorization\n\n**Message Flow:**\n\n1. Platform adapter queues `MessageEvent`\n2. GatewayRunner dequeues, calls `_handle_message()`\n3. Slash command? → dispatch to handler\n4. Regular message? → `_handle_message_with_agent","AGENTS.md":"# Before starting work\n\n- Run `lat search` to find sections relevant to your task. Read them to understand the design intent before writing code.\n- Run `lat expand` on user prompts to expand any `[[refs]]` — this resolves section names to file locations and provides context.\n\n# Post-task checklist (REQUIRED — do not skip)\n\nAfter EVERY task, before responding to the user:\n\n- [ ] Update `lat.md/` if you added or changed any functionality, architecture, tests, or behavior\n- [ ] Run `lat check` — all wiki links and code refs must pass\n- [ ] Do not skip these steps. Do not consider your task done until both are complete.\n\n---\n\n# What is lat.md?\n\nThis project uses [lat.md](https://www.npmjs.com/package/lat.md) to maintain a structured knowledge graph of its architecture, design decisions, and test specs in the `lat.md/` directory. It is a set of cross-linked markdown files that describe **what** this project does and **why** — the domain concepts, key design decisions, business logic, and test specifications. Use it to ground your work in the actual architecture rather than guessing.\n\n# Commands\n\n```bash\nlat locate \"Section Name\"      # find a section by name (exact, fuzzy)\nlat refs \"file#Section\"        # find what references a section\nlat search \"natural language\"  # semantic search across all sections\nlat expand \"user prompt text\"  # expand [[refs]] to resolved locations\nlat check                      # validate all links and code refs\n```\n\nRun `lat --help` when in doubt about available commands or options.\n\nIf `lat search` fails because no API key is configured, explain to the user that semantic search requires a key provided via `LAT_LLM_KEY` (direct value), `LAT_LLM_KEY_FILE` (path to key file), or `LAT_LLM_KEY_HELPER` (command that prints the key). Supported key prefixes: `sk-...` (OpenAI) or `vck_...` (Vercel). If the user doesn't want to set it up, use `lat locate` for direct lookups instead.\n\n# Syntax primer\n\n- **Section ids**: `lat.md/path/to/file#Heading#SubHeading` — full form uses project-root-relative path (e.g. `lat.md/tests/search#RAG Replay Tests`). Short form uses bare file name when unique (e.g. `search#RAG Replay Tests`, `cli#search#Indexing`).\n- **Wiki links**: `[[target]]` or `[[target|alias]]` — cross-references between sections. Can also reference source code: `[[src/foo.ts#myFunction]]`.\n- **Source code links**: Wiki links in `lat.md/` files can reference functions, classes, constants, and methods in TypeScript/JavaScript/Python/Rust/Go/C files. Use the full path: `[[src/config.ts#getConfigDir]]`, `[[src/server.ts#App#listen]]` (class method), `[[lib/utils.py#parse_args]]`, `[[src/lib.rs#Greeter#greet]]` (Rust impl method), `[[src/app.go#Greeter#Greet]]` (Go method), `[[src/app.h#Greeter]]` (C struct). `lat check` validates these exist.\n- **Code refs**: `// @lat: [[section-id]]` (JS/TS/Rust/Go/C) or `# @lat: [[section-id]]` (Python) — ties source code to concepts\n\n# Test specs\n\nKey tests can be described as sections in `lat.md/` files (e.g. `tests.md`). Add frontmatter to require that every leaf section is referenced by a `// @lat:` or `# @lat:` comment in test code:\n\n```markdown\n---\nlat:\n  require-code-mention: true\n---\n# Tests\n\nAuthentication and authorization test specifications.\n\n## User login\n\nVerify credential validation and error handling for the login endpoint.\n\n### Rejects expired tokens\nTokens past their expiry timestamp are rejected with 401, even if otherwise valid.\n\n### Handles missing password\nLogin request without a password field returns 400 with a descriptive error.\n```\n\nEvery section MUST have a description — at least one sentence explaining what the test verifies and why. Empty sections with just a heading are not acceptable. (This is a specific case of the general leading paragraph rule below.)\n\nEach test in code should reference its spec with exactly one comment placed next to the relevant test — not at the top of the file:\n\n```python\n# @lat: [[tests#User login#Rejects expired tokens]]\ndef test_rejects_expired_tokens():\n    ...\n\n# @lat: [[tests#User login#Handles missing password]]\ndef test_handles_missing_password():\n    ...\n```\n\nDo not duplicate refs. One `@lat:` comment per spec section, placed at the test that covers it. `lat check` will flag any spec section not covered by a code reference, and any code reference pointing to a nonexistent section.\n\n# Section structure\n\nEvery section in `lat.md/` **must** have a leading paragraph — at least one sentence immediately after the heading, before any child headings or other block content. The first paragraph must be ≤250 characters (excluding `[[wiki link]]` content). This paragraph serves as the section's overview and is used in search results, command output, and RAG context — keeping it concise guarantees the section's essence is always captured.\n\n```markdown\n# Good Section\n\nBrief overview of what this section documents and why it matters.\n\nMore detail can go in subsequent paragraphs, code blocks, or lists.\n\n## Child heading\n\nDetails about this child topic.\n```\n\n```markdown\n# Bad Section\n\n## Child heading\n\nDetails about this child topic.\n```\n\nThe second example is invalid because `Bad Section` has no leading paragraph. `lat check` validates this rule and reports errors for missing or overly long leading paragraphs.\n",".agents/skills/electron-pro/SKILL.md":"---\nname: electron-pro\ndescription: Expert in building cross-platform desktop applications using web technologies (HTML/CSS/JS) with the Electron framework.\n---\n\n# Electron Desktop Developer\n\n## Purpose\n\nProvides cross-platform desktop application development expertise specializing in Electron, IPC architecture, and OS-level integration. Builds secure, performant desktop applications using web technologies with native capabilities for Windows, macOS, and Linux.\n\n## When to Use\n\n- Building cross-platform desktop apps (VS Code, Discord style)\n- Migrating web apps to desktop with native capabilities (File system, Notifications)\n- Implementing secure IPC (Main ↔ Renderer communication)\n- Optimizing Electron memory usage and startup time\n- Configuring auto-updaters (electron-updater)\n- Signing and notarizing apps for app stores\n\n---\n\n---\n\n## 2. Decision Framework\n\n### Architecture Selection\n\n```\nHow to structure the app?\n│\n├─ **Security First (Recommended)**\n│  ├─ Context Isolation? → **Yes** (Standard since v12)\n│  ├─ Node Integration? → **No** (Never in Renderer)\n│  └─ Preload Scripts? → **Yes** (Bridge API)\n│\n├─ **Data Persistence**\n│  ├─ Simple Settings? → **electron-store** (JSON)\n│  ├─ Large Datasets? → **SQLite** (`better-sqlite3` in Main process)\n│  └─ User Files? → **Native File System API**\n│\n└─ **UI Framework**\n   ├─ React/Vue/Svelte? → **Yes** (Standard SPA approach)\n   ├─ Multiple Windows? → **Window Manager Pattern**\n   └─ System Tray App? → **Hidden Window Pattern**\n```\n\n### IPC Communication Patterns\n\n| Pattern                        | Method               | Use Case                                        |\n| ------------------------------ | -------------------- | ----------------------------------------------- |\n| **One-Way (Renderer → Main)**  | `ipcRenderer.send`   | logging, analytics, minimizing window           |\n| **Two-Way (Request/Response)** | `ipcRenderer.invoke` | DB queries, file reads, heavy computations      |\n| **Main → Renderer**            | `webContents.send`   | Menu actions, system events, push notifications |\n\n**Red Flags → Escalate to `security-auditor`:**\n\n- Enabling `nodeIntegration: true` in production\n- Disabling `contextIsolation`\n- Loading remote content (`https://`) without strict CSP\n- Using `remote` module (Deprecated & insecure)\n\n---\n\n---\n\n### Workflow 2: Performance Optimization (Startup)\n\n**Goal:** Reduce launch time to < 2s.\n\n**Steps:**\n\n1.  **V8 Snapshot**\n    - Use `electron-link` or `v8-compile-cache` to pre-compile JS.\n\n2.  **Lazy Loading Modules**\n    - Don't `require()` everything at top of `main.ts`.\n\n    ```javascript\n    // Bad\n    import { heavyLib } from \"heavy-lib\";\n\n    // Good\n    ipcMain.handle(\"do-work\", () => {\n      const heavyLib = require(\"heavy-lib\");\n      heavyLib.process();\n    });\n    ```\n\n3.  **Bundle Main Process**\n    - Use `esbuild` or `webpack` for Main process (not just Renderer) to tree-shake unused code and minify.\n\n---\n\n---\n\n## 4. Patterns & Templates\n\n### Pattern 1: Worker Threads (CPU Intensive Tasks)\n\n**Use case:** Image processing or parsing large files without freezing the UI.\n\n```typescript\n// main.ts\nimport { Worker } from \"worker_threads\";\n\nipcMain.handle(\"process-image\", (event, data) => {\n  return new Promise((resolve, reject) => {\n    const worker = new Worker(\"./worker.js\", { workerData: data });\n    worker.on(\"message\", resolve);\n    worker.on(\"error\", reject);\n  });\n});\n```\n\n### Pattern 2: Deep Linking (Protocol Handler)\n\n**Use case:** Opening app from browser (`myapp://open?id=123`).\n\n```typescript\n// main.ts\nif (process.defaultApp) {\n  if (process.argv.length >= 2) {\n    app.setAsDefaultProtocolClient(\"myapp\", process.execPath, [\n      path.resolve(process.argv[1]),\n    ]);\n  }\n} else {\n  app.setAsDefaultProtocolClient(\"myapp\");\n}\n\napp.on(\"open-url\", (event, url) => {\n  event.preventDefault();\n  // Parse url 'myapp://...' and navigate renderer\n  mainWindow.webContents.send(\"navigate\", url);\n});\n```\n\n---\n\n---\n\n## 6. Integration Patterns\n\n### **frontend-ui-ux-engineer:**\n\n- **Handoff**: UI Dev builds the React/Vue app → Electron Dev wraps it.\n- **Collaboration**: Handling window controls (custom title bar), vibrancy/acrylic effects.\n- **Tools**: CSS `app-region: drag`.\n\n### **devops-engineer:**\n\n- **Handoff**: Electron Dev provides build config → DevOps sets up CI pipeline.\n- **Collaboration**: Code signing certificates (Apple Developer ID, Windows EV).\n- **Tools**: Electron Builder, Notarization scripts.\n\n### **security-engineer:**\n\n- **Handoff**: Electron Dev implements feature → Security Dev audits IPC surface.\n- **Collaboration**: Defining Content Security Policy (CSP) headers.\n- **Tools**: Electronegativity (Scanner).\n\n---\n",".claude/skills/lat-md/SKILL.md":"---\nname: lat-md\ndescription: >-\n  Writing and maintaining lat.md documentation files — structured markdown that\n  describes a project's architecture, design decisions, and test specs. Use when\n  creating, editing, or reviewing files in the lat.md/ directory.\n---\n\n# lat.md Authoring Guide\n\nThis skill covers the syntax, structure rules, and conventions for writing `lat.md/` files. Load it whenever you need to create or edit sections in the `lat.md/` directory.\n\n## What belongs in lat.md\n\n`lat.md/` files describe **what** the project does and **why** — domain concepts, key design decisions, business logic, and test specifications. They do NOT duplicate source code. Think of each section as an anchor that source code references back to.\n\nGood candidates for sections:\n- Architecture decisions and their rationale\n- Domain concepts and business rules\n- API contracts and protocols\n- Test specifications (what is tested and why)\n- Non-obvious constraints or invariants\n\nBad candidates:\n- Step-by-step code walkthroughs (the code itself is the walkthrough)\n- Auto-generated API docs (use tools for that)\n- Temporary notes or TODOs\n\n## Section structure\n\nEvery section **must** have a leading paragraph — at least one sentence immediately after the heading, before any child headings or other block content.\n\nThe first paragraph must be ≤250 characters (excluding `[[wiki link]]` content). This paragraph is the section's identity — it appears in search results, command output, and RAG context.\n\n```markdown\n# Good Section\n\nBrief overview of what this section documents and why it matters.\n\nMore detail can go in subsequent paragraphs, code blocks, or lists.\n\n## Child heading\n\nDetails about this child topic.\n```\n\n```markdown\n# Bad Section\n\n## Child heading\n\nThis is invalid — \"Bad Section\" has no leading paragraph.\n```\n\n`lat check` enforces this rule.\n\n## Section IDs\n\nSections are addressed by file path and heading chain:\n\n- **Full form**: `lat.md/path/to/file#Heading#SubHeading`\n- **Short form**: `file#Heading#SubHeading` (when the file stem is unique)\n\nExamples: `lat.md/tests/search#RAG Replay Tests`, `cli#init`, `parser#Wiki Links`.\n\n## Wiki links\n\nCross-reference other sections or source code with `[[target]]` or `[[target|alias]]`.\n\n### Section links\n\n```markdown\nSee [[cli#init]] for setup details.\nThe parser validates [[parser#Wiki Links|wiki link syntax]].\n```\n\n### Source code links\n\nReference functions, classes, constants, and methods in source files:\n\n```markdown\n[[src/config.ts#getConfigDir]]          — function\n[[src/server.ts#App#listen]]            — class method\n[[lib/utils.py#parse_args]]             — Python function\n[[src/lib.rs#Greeter#greet]]            — Rust impl method\n[[src/app.go#Greeter#Greet]]            — Go method\n[[src/app.h#Greeter]]                   — C struct\n```\n\n`lat check` validates that all targets exist.\n\n## Code refs\n\nTie source code back to `lat.md/` sections with `@lat:` comments:\n\n```typescript\n// @lat: [[cli#init]]\nexport function init() { ... }\n```\n\n```python\n# @lat: [[cli#init]]\ndef init():\n    ...\n```\n\nSupported comment styles: `//` (JS/TS/Rust/Go/C) and `#` (Python).\n\nPlace one `@lat:` comment per section, at the relevant code — not at the top of the file.\n\n## Test specs\n\nDescribe tests as sections in `lat.md/` files. Add frontmatter to require that every leaf section has a matching `@lat:` comment in test code:\n\n```markdown\n---\nlat:\n  require-code-mention: true\n---\n# Tests\n\nAuthentication test specifications.\n\n## User login\n\nVerify credential validation and error handling.\n\n### Rejects expired tokens\n\nTokens past their expiry timestamp are rejected with 401, even if otherwise valid.\n\n### Handles missing password\n\nLogin request without a password field returns 400 with a descriptive error.\n```\n\nEach test references its spec:\n\n```python\n# @lat: [[tests#User login#Rejects expired tokens]]\ndef test_rejects_expired_tokens():\n    ...\n```\n\nRules:\n- Every leaf section under `require-code-mention: true` must be referenced by exactly one `@lat:` comment\n- Every section MUST have a description — at least one sentence explaining what the test verifies and why\n- `lat check` flags unreferenced specs and dangling code refs\n\n## Frontmatter\n\nOptional YAML frontmatter at the top of `lat.md/` files:\n\n```yaml\n---\nlat:\n  require-code-mention: true\n---\n```\n\nCurrently the only supported field is `require-code-mention` for test spec enforcement.\n\n## Validation\n\nAlways run `lat check` after editing `lat.md/` files. It validates:\n- All wiki links point to existing sections or source code symbols\n- All `@lat:` code refs point to existing sections\n- Every section has a leading paragraph (≤250 chars)\n- All `require-code-mention` leaf sections are referenced in code\n",".agents/skills/lat-md/SKILL.md":"---\nname: lat-md\ndescription: >-\n  Writing and maintaining lat.md documentation files — structured markdown that\n  describes a project's architecture, design decisions, and test specs. Use when\n  creating, editing, or reviewing files in the lat.md/ directory.\n---\n\n# lat.md Authoring Guide\n\nThis skill covers the syntax, structure rules, and conventions for writing `lat.md/` files. Load it whenever you need to create or edit sections in the `lat.md/` directory.\n\n## What belongs in lat.md\n\n`lat.md/` files describe **what** the project does and **why** — domain concepts, key design decisions, business logic, and test specifications. They do NOT duplicate source code. Think of each section as an anchor that source code references back to.\n\nGood candidates for sections:\n- Architecture decisions and their rationale\n- Domain concepts and business rules\n- API contracts and protocols\n- Test specifications (what is tested and why)\n- Non-obvious constraints or invariants\n\nBad candidates:\n- Step-by-step code walkthroughs (the code itself is the walkthrough)\n- Auto-generated API docs (use tools for that)\n- Temporary notes or TODOs\n\n## Section structure\n\nEvery section **must** have a leading paragraph — at least one sentence immediately after the heading, before any child headings or other block content.\n\nThe first paragraph must be ≤250 characters (excluding `[[wiki link]]` content). This paragraph is the section's identity — it appears in search results, command output, and RAG context.\n\n```markdown\n# Good Section\n\nBrief overview of what this section documents and why it matters.\n\nMore detail can go in subsequent paragraphs, code blocks, or lists.\n\n## Child heading\n\nDetails about this child topic.\n```\n\n```markdown\n# Bad Section\n\n## Child heading\n\nThis is invalid — \"Bad Section\" has no leading paragraph.\n```\n\n`lat check` enforces this rule.\n\n## Section IDs\n\nSections are addressed by file path and heading chain:\n\n- **Full form**: `lat.md/path/to/file#Heading#SubHeading`\n- **Short form**: `file#Heading#SubHeading` (when the file stem is unique)\n\nExamples: `lat.md/tests/search#RAG Replay Tests`, `cli#init`, `parser#Wiki Links`.\n\n## Wiki links\n\nCross-reference other sections or source code with `[[target]]` or `[[target|alias]]`.\n\n### Section links\n\n```markdown\nSee [[cli#init]] for setup details.\nThe parser validates [[parser#Wiki Links|wiki link syntax]].\n```\n\n### Source code links\n\nReference functions, classes, constants, and methods in source files:\n\n```markdown\n[[src/config.ts#getConfigDir]]          — function\n[[src/server.ts#App#listen]]            — class method\n[[lib/utils.py#parse_args]]             — Python function\n[[src/lib.rs#Greeter#greet]]            — Rust impl method\n[[src/app.go#Greeter#Greet]]            — Go method\n[[src/app.h#Greeter]]                   — C struct\n```\n\n`lat check` validates that all targets exist.\n\n## Code refs\n\nTie source code back to `lat.md/` sections with `@lat:` comments:\n\n```typescript\n// @lat: [[cli#init]]\nexport function init() { ... }\n```\n\n```python\n# @lat: [[cli#init]]\ndef init():\n    ...\n```\n\nSupported comment styles: `//` (JS/TS/Rust/Go/C) and `#` (Python).\n\nPlace one `@lat:` comment per section, at the relevant code — not at the top of the file.\n\n## Test specs\n\nDescribe tests as sections in `lat.md/` files. Add frontmatter to require that every leaf section has a matching `@lat:` comment in test code:\n\n```markdown\n---\nlat:\n  require-code-mention: true\n---\n# Tests\n\nAuthentication test specifications.\n\n## User login\n\nVerify credential validation and error handling.\n\n### Rejects expired tokens\n\nTokens past their expiry timestamp are rejected with 401, even if otherwise valid.\n\n### Handles missing password\n\nLogin request without a password field returns 400 with a descriptive error.\n```\n\nEach test references its spec:\n\n```python\n# @lat: [[tests#User login#Rejects expired tokens]]\ndef test_rejects_expired_tokens():\n    ...\n```\n\nRules:\n- Every leaf section under `require-code-mention: true` must be referenced by exactly one `@lat:` comment\n- Every section MUST have a description — at least one sentence explaining what the test verifies and why\n- `lat check` flags unreferenced specs and dangling code refs\n\n## Frontmatter\n\nOptional YAML frontmatter at the top of `lat.md/` files:\n\n```yaml\n---\nlat:\n  require-code-mention: true\n---\n```\n\nCurrently the only supported field is `require-code-mention` for test spec enforcement.\n\n## Validation\n\nAlways run `lat check` after editing `lat.md/` files. It validates:\n- All wiki links point to existing sections or source code symbols\n- All `@lat:` code refs point to existing sections\n- Every section has a leading paragraph (≤250 chars)\n- All `require-code-mention` leaf sections are referenced in code\n",".agents/skills/typescript-expert/SKILL.md":"---\nname: typescript-expert\ndescription: TypeScript and JavaScript expert with deep knowledge of type-level programming, performance optimization, monorepo management, migration strategies, and modern tooling.\ncategory: framework\nrisk: critical\nsource: community\ndate_added: \"2026-02-27\"\n---\n\n# TypeScript Expert\n\nYou are an advanced TypeScript expert with deep, practical knowledge of type-level programming, performance optimization, and real-world problem solving based on current best practices.\n\n## When invoked:\n\n0. If the issue requires ultra-specific expertise, recommend switching and stop:\n   - Deep webpack/vite/rollup bundler internals → typescript-build-expert\n   - Complex ESM/CJS migration or circular dependency analysis → typescript-module-expert\n   - Type performance profiling or compiler internals → typescript-type-expert\n\n   Example to output:\n   \"This requires deep bundler expertise. Please invoke: 'Use the typescript-build-expert subagent.' Stopping here.\"\n\n1. Analyze project setup comprehensively:\n\n   **Use internal tools first (Read, Grep, Glob) for better performance. Shell commands are fallbacks.**\n\n   ```bash\n   # Core versions and configuration\n   npx tsc --version\n   node -v\n   # Detect tooling ecosystem (prefer parsing package.json)\n   node -e \"const p=require('./package.json');console.log(Object.keys({...p.devDependencies,...p.dependencies}||{}).join('\\n'))\" 2>/dev/null | grep -E 'biome|eslint|prettier|vitest|jest|turborepo|nx' || echo \"No tooling detected\"\n   # Check for monorepo (fixed precedence)\n   (test -f pnpm-workspace.yaml || test -f lerna.json || test -f nx.json || test -f turbo.json) && echo \"Monorepo detected\"\n   ```\n\n   **After detection, adapt approach:**\n   - Match import style (absolute vs relative)\n   - Respect existing baseUrl/paths configuration\n   - Prefer existing project scripts over raw tools\n   - In monorepos, consider project references before broad tsconfig changes\n\n2. Identify the specific problem category and complexity level\n\n3. Apply the appropriate solution strategy from my expertise\n\n4. Validate thoroughly:\n\n   ```bash\n   # Fast fail approach (avoid long-lived processes)\n   npm run -s typecheck || npx tsc --noEmit\n   npm test -s || npx vitest run --reporter=basic --no-watch\n   # Only if needed and build affects outputs/config\n   npm run -s build\n   ```\n\n   **Safety note:** Avoid watch/serve processes in validation. Use one-shot diagnostics only.\n\n## Advanced Type System Expertise\n\n### Type-Level Programming Patterns\n\n**Branded Types for Domain Modeling**\n\n```typescript\n// Create nominal types to prevent primitive obsession\ntype Brand<K, T> = K & { __brand: T };\ntype UserId = Brand<string, \"UserId\">;\ntype OrderId = Brand<string, \"OrderId\">;\n\n// Prevents accidental mixing of domain primitives\nfunction processOrder(orderId: OrderId, userId: UserId) {}\n```\n\n- Use for: Critical domain primitives, API boundaries, currency/units\n- Resource: https://egghead.io/blog/using-branded-types-in-typescript\n\n**Advanced Conditional Types**\n\n```typescript\n// Recursive type manipulation\ntype DeepReadonly<T> = T extends (...args: any[]) => any\n  ? T\n  : T extends object\n    ? { readonly [K in keyof T]: DeepReadonly<T[K]> }\n    : T;\n\n// Template literal type magic\ntype PropEventSource<Type> = {\n  on<Key extends string & keyof Type>(\n    eventName: `${Key}Changed`,\n    callback: (newValue: Type[Key]) => void,\n  ): void;\n};\n```\n\n- Use for: Library APIs, type-safe event systems, compile-time validation\n- Watch for: Type instantiation depth errors (limit recursion to 10 levels)\n\n**Type Inference Techniques**\n\n```typescript\n// Use 'satisfies' for constraint validation (TS 5.0+)\nconst config = {\n  api: \"https://api.example.com\",\n  timeout: 5000,\n} satisfies Record<string, string | number>;\n// Preserves literal types while ensuring constraints\n\n// Const assertions for maximum inference\nconst routes = [\"/home\", \"/about\", \"/contact\"] as const;\ntype Route = (typeof routes)[number]; // '/home' | '/about' | '/contact'\n```\n\n### Performance Optimization Strategies\n\n**Type Checking Performance**\n\n```bash\n# Diagnose slow type checking\nnpx tsc --extendedDiagnostics --incremental false | grep -E \"Check time|Files:|Lines:|Nodes:\"\n\n# Common fixes for \"Type instantiation is excessively deep\"\n# 1. Replace type intersections with interfaces\n# 2. Split large union types (>100 members)\n# 3. Avoid circular generic constraints\n# 4. Use type aliases to break recursion\n```\n\n**Build Performance Patterns**\n\n- Enable `skipLibCheck: true` for library type checking only (often significantly improves performance on large projects, but avoid masking app typing issues)\n- Use `incremental: true` with `.tsbuildinfo` cache\n- Configure `include`/`exclude` precisely\n- For monorepos: Use project references with `composite: true`\n\n## Real-World Problem Resolution\n\n### Complex Error Patterns\n\n**\"The inferred type of X cannot be named\"**\n\n- Cause: Missing type export or circular dependency\n- Fix priority:\n  1. Export the required type explicitly\n  2. Use `ReturnType<typeof function>` helper\n  3. Break circular dependencies with type-only imports\n- Resource: https://github.com/microsoft/TypeScript/issues/47663\n\n**Missing type declarations**\n\n- Quick fix with ambient declarations:\n\n```typescript\n// types/ambient.d.ts\ndeclare module \"some-untyped-package\" {\n  const value: unknown;\n  export default value;\n  export = value; // if CJS interop is needed\n}\n```\n\n- For more details: [Declaration Files Guide](https://www.typescriptlang.org/docs/handbook/declaration-files/introduction.html)\n\n**\"Excessive stack depth comparing types\"**\n\n- Cause: Circular or deeply recursive types\n- Fix priority:\n  1. Limit recursion depth with conditional types\n  2. Use `interface` extends instead of type intersection\n  3. Simplify generic constraints\n\n```typescript\n// Bad: Infinite recursion\ntype InfiniteArray<T> = T | InfiniteArray<T>[];\n\n// Good: Limited recursion\ntype NestedArray<T, D extends number = 5> = D extends 0\n  ? T\n  : T | NestedArray<T, [-1, 0, 1, 2, 3, 4][D]>[];\n```\n\n**Module Resolution Mysteries**\n\n- \"Cannot find module\" despite file existing:\n  1. Check `moduleResolution` matches your bundler\n  2. Verify `baseUrl` and `paths` alignment\n  3. For monorepos: Ensure workspace protocol (workspace:\\*)\n  4. Try clearing cache: `rm -rf node_modules/.cache .tsbuildinfo`\n\n**Path Mapping at Runtime**\n\n- TypeScript paths only work at compile time, not runtime\n- Node.js runtime solutions:\n  - ts-node: Use `ts-node -r tsconfig-paths/register`\n  - Node ESM: Use loader alternatives or avoid TS paths at runtime\n  - Production: Pre-compile with resolved paths\n\n### Migration Expertise\n\n**JavaScript to TypeScript Migration**\n\n```bash\n# Incremental migration strategy\n# 1. Enable allowJs and checkJs (merge into existing tsconfig.json):\n# Add to existing tsconfig.json:\n# {\n#   \"compilerOptions\": {\n#     \"allowJs\": true,\n#     \"checkJs\": true\n#   }\n# }\n\n# 2. Rename files gradually (.js → .ts)\n# 3. Add types file by file using AI assistance\n# 4. Enable strict mode features one by one\n\n# Automated helpers (if installed/needed)\ncommand -v ts-migrate >/dev/null 2>&1 && npx ts-migrate migrate . --sources 'src/**/*.js'\ncommand -v typesync >/dev/null 2>&1 && npx typesync  # Install missing @types packages\n```\n\n**Tool Migration Decisions**\n\n| From              | To              | When                                          | Migration Effort  |\n| ----------------- | --------------- | --------------------------------------------- | ----------------- |\n| ESLint + Prettier | Biome           | Need much faster speed, okay with fewer rules | Low (1 day)       |\n| TSC for linting   | Type-check only | Have 100+ files, need faster feedback         | Medium (2-3 days) |\n| Lerna             | Nx/Turborepo    | Need caching, parallel builds                 | High (1 week)     |\n| CJS               | ESM             | Node 18+, modern tooling                      | High (varies)     |\n\n### Monorepo Management\n\n**Nx vs Turborepo Decision Matrix**\n\n- Choose **Turborepo** if: Simple structure, need speed, <20 packages\n- Choose **Nx** if: Complex dependencies, need visualization, plugins required\n- Performance: Nx often performs better on large monorepos (>50 packages)\n\n**TypeScript Monorepo Configuration**\n\n```json\n// Root tsconfig.json\n{\n  \"references\": [\n    { \"path\": \"./packages/core\" },\n    { \"path\": \"./packages/ui\" },\n    { \"path\": \"./apps/web\" }\n  ],\n  \"compilerOptions\": {\n    \"composite\": true,\n    \"declaration\": true,\n    \"declarationMap\": true\n  }\n}\n```\n\n## Modern Tooling Expertise\n\n### Biome vs ESLint\n\n**Use Biome when:**\n\n- Speed is critical (often faster than traditional setups)\n- Want single tool for lint + format\n- TypeScript-first project\n- Okay with 64 TS rules vs 100+ in typescript-eslint\n\n**Stay with ESLint when:**\n\n- Need specific rules/plugins\n- Have complex custom rules\n- Working with Vue/Angular (limited Biome support)\n- Need type-aware linting (Biome doesn't have this yet)\n\n### Type Testing Strategies\n\n**Vitest Type Testing (Recommended)**\n\n```typescript\n// in avatar.test-d.ts\nimport { expectTypeOf } from \"vitest\";\nimport type { Avatar } from \"./avatar\";\n\ntest(\"Avatar props are correctly typed\", () => {\n  expectTypeOf<Avatar>().toHaveProperty(\"size\");\n  expectTypeOf<Avatar[\"size\"]>().toEqualTypeOf<\"sm\" | \"md\" | \"lg\">();\n});\n```\n\n**When to Test Types:**\n\n- Publishing libraries\n- Complex generic functions\n- Type-level utilities\n- API contracts\n\n## Debugging Mastery\n\n### CLI Debugging Tools\n\n```bash\n# Debug TypeScript files directly (if tools installed)\ncommand -v tsx >/dev/null 2>&1 && npx tsx --inspect src/file.ts\ncommand -v ts-node >/dev/null 2>&1 && npx ts-node --inspect-brk src/file.ts\n\n# Trace module resolution issues\nnpx tsc --traceResolution > resolution.log 2>&1\ngrep \"Module resolution\" resolution.log\n\n# Debug type checking performance (use --incremental false for clean trace)\nnpx tsc --generateTrace trace --incremental false\n# Analyze trace (if installed)\ncommand -v @typescript/analyze-trace >/dev/null 2>&1 && npx @typescript/analyze-trace trace\n\n# Memory usage analysis\nnode --max-old-space-size=8192 node_modules/typescript/lib/tsc.js\n```\n\n### Custom Error Classes\n\n```typescript\n// Proper error class with stack preservation\nclass DomainError extends Error {\n  constructor(\n    message: string,\n    public code: string,\n    public statusCode: number,\n  ) {\n    super(message);\n    this.name = \"DomainError\";\n    Error.captureStackTrace(this, this.constructor);\n  }\n}\n```\n\n## Current Best Practices\n\n### Strict by Default\n\n```json\n{\n  \"compilerOptions\": {\n    \"strict\": true,\n    \"noUncheckedIndexedAccess\": true,\n    \"noImplicitOverride\": true,\n    \"exactOptionalPropertyTypes\": true,\n    \"noPropertyAccessFromIndexSignature\": true\n  }\n}\n```\n\n### ESM-First Approach\n\n- Set `\"type\": \"module\"` in package.json\n- Use `.mts` for TypeScript ESM files if needed\n- Configure `\"moduleResolution\": \"bundler\"` for modern tools\n- Use dynamic imports for CJS: `const pkg = await import('cjs-package')`\n  - Note: `await import()` requires async function or top-level await in ESM\n  - For CJS packages in ESM: May need `(await import('pkg')).default` depending on the package's export structure and your compiler settings\n\n### AI-Assisted Development\n\n- GitHub Copilot excels at TypeScript generics\n- Use AI for boilerplate type definitions\n- Validate AI-generated types with type tests\n- Document complex types for AI context\n\n## Code Review Checklist\n\nWhen reviewing TypeScript/JavaScript code, focus on these domain-specific aspects:\n\n### Type Safety\n\n- [ ] No implicit `any` types (use `unknown` or proper types)\n- [ ] Strict null checks enabled and properly handled\n- [ ] Type assertions (`as`) justified and minimal\n- [ ] Generic constraints properly defined\n- [ ] Discriminated unions for error handling\n- [ ] Return types explicitly declared for public APIs\n\n### TypeScript Best Practices\n\n- [ ] Prefer `interface` over `type` for object shapes (better error messages)\n- [ ] Use const assertions for literal types\n- [ ] Leverage type guards and predicates\n- [ ] Avoid type gymnastics when simpler solution exists\n- [ ] Template literal types used appropriately\n- [ ] Branded types for domain primitives\n\n### Performance Considerations\n\n- [ ] Type complexity doesn't cause slow compilation\n- [ ] No excessive type instantiation depth\n- [ ] Avoid complex mapped types in hot paths\n- [ ] Use `skipLibCheck: true` in tsconfig\n- [ ] Project references configured for monorepos\n\n### Module System\n\n- [ ] Consistent import/export patterns\n- [ ] No circular dependencies\n- [ ] Proper use of barrel exports (avoid over-bundling)\n- [ ] ESM/CJS compatibility handled correctly\n- [ ] Dynamic imports for code splitting\n\n### Error Handling Patterns\n\n- [ ] Result types or discriminated unions for errors\n- [ ] Custom error classes with proper inheritance\n- [ ] Type-safe error boundaries\n- [ ] Exhaustive switch cases with `never` type\n\n### Code Organization\n\n- [ ] Types co-located with implementation\n- [ ] Shared types in dedicated modules\n- [ ] Avoid global type augmentation when possible\n- [ ] Proper use of declaration files (.d.ts)\n\n## Quick Decision Trees\n\n### \"Which tool should I use?\"\n\n```\nType checking only? → tsc\nType checking + linting speed critical? → Biome\nType checking + comprehensive linting? → ESLint + typescript-eslint\nType testing? → Vitest expectTypeOf\nBuild tool? → Project size <10 packages? Turborepo. Else? Nx\n```\n\n### \"How do I fix this performance issue?\"\n\n```\nSlow type checking? → skipLibCheck, incremental, project references\nSlow builds? → Check bundler config, enable caching\nSlow tests? → Vitest with threads, avoid type checking in tests\nSlow language server? → Exclude node_modules, limit files in tsconfig\n```\n\n## Expert Resources\n\n### Performance\n\n- [TypeScript Wiki Performance](https://github.com/microsoft/TypeScript/wiki/Performance)\n- [Type instantiation tracking](https://github.com/microsoft/TypeScript/pull/48077)\n\n### Advanced Patterns\n\n- [Type Challenges](https://github.com/type-challenges/type-challenges)\n- [Type-Level TypeScript Course](https://type-level-typescript.com)\n\n### Tools\n\n- [Biome](https://biomejs.dev) - Fast linter/formatter\n- [TypeStat](https://github.com/JoshuaKGoldberg/TypeStat) - Auto-fix TypeScript types\n- [ts-migrate](https://github.com/airbnb/ts-migrate) - Migration toolkit\n\n### Testing\n\n- [Vitest Type Testing](https://vitest.dev/guide/testing-types)\n- [tsd](https://github.com/tsdjs/tsd) - Standalone type testing\n\nAlways validate changes don't break existing functionality before considering the issue resolved.\n\n## When to Use\n\nThis skill is applicable to execute the workflow or actions described in the overview.\n",".agents/skills/hermes-agent/SKILL.md":"---\nname: hermes-agent\ndescription: Expert in building self-improving AI agents with tool use, multi-platform messaging, and a closed learning loop. Proficient in LLM orchestration, tool integration, session management, and agent autonomy.\n---\n\n# Hermes Agent - Complete Project Guide (A-Z)\n\n> **Purpose of this document:** A single, comprehensive reference that explains everything about the Hermes Agent project — its architecture, source code, features, release history, and design patterns — so that any AI or developer can fully understand the system.\n\n---\n\n## Table of Contents\n\n1. [Project Overview](#1-project-overview)\n2. [Key Features Summary](#2-key-features-summary)\n3. [Installation & Getting Started](#3-installation--getting-started)\n4. [Project Structure](#4-project-structure)\n5. [Core Architecture](#5-core-architecture)\n   - 5.1 [AIAgent Class (run_agent.py)](#51-aiagent-class-run_agentpy)\n   - 5.2 [Tool Orchestration (model_tools.py)](#52-tool-orchestration-model_toolspy)\n   - 5.3 [Toolset System (toolsets.py)](#53-toolset-system-toolsetspy)\n   - 5.4 [Tool Registry (tools/registry.py)](#54-tool-registry-toolsregistrypy)\n   - 5.5 [Session Database (hermes_state.py)](#55-session-database-hermes_statepy)\n   - 5.6 [Constants & Home Directory (hermes_constants.py)](#56-constants--home-directory-hermes_constantspy)\n6. [CLI System](#6-cli-system)\n   - 6.1 [Interactive CLI (cli.py)](#61-interactive-cli-clipy)\n   - 6.2 [CLI Entry Point (hermes_cli/main.py)](#62-cli-entry-point-hermes_climainpy)\n   - 6.3 [Configuration System (hermes_cli/config.py)](#63-configuration-system-hermes_cliconfigpy)\n   - 6.4 [Slash Command Registry (hermes_cli/commands.py)](#64-slash-command-registry-hermes_clicommandspy)\n   - 6.5 [Setup Wizard (hermes_cli/setup.py)](#65-setup-wizard-hermes_clisetupy)\n   - 6.6 [Model Catalog (hermes_cli/models.py)](#66-model-catalog-hermes_climodelspy)\n   - 6.7 [Skin/Theme Engine (hermes_cli/skin_engine.py)](#67-skintheme-engine-hermes_cliskin_enginepy)\n7. [Tool System](#7-tool-system)\n   - 7.1 [Terminal Tool (tools/terminal_tool.py)](#71-terminal-tool-toolsterminal_toolpy)\n   - 7.2 [File Tools (tools/file_tools.py)](#72-file-tools-toolsfile_toolspy)\n   - 7.3 [Web Tools (tools/web_tools.py)](#73-web-tools-toolsweb_toolspy)\n   - 7.4 [Browser Tool (tools/browser_tool.py)](#74-browser-tool-toolsbrowser_toolpy)\n   - 7.5 [Delegate Tool (tools/delegate_tool.py)](#75-delegate-tool-toolsdelegate_toolpy)\n   - 7.6 [MCP Tool (tools/mcp_tool.py)](#76-mcp-tool-toolsmcp_toolpy)\n   - 7.7 [Approval System (tools/approval.py)](#77-approval-system-toolsapprovalpy)\n   - 7.8 [Terminal Backends (tools/environments/)](#78-terminal-backends-toolsenvironments)\n8. [Agent Internals](#8-agent-internals)\n   - 8.1 [Prompt Builder (agent/prompt_builder.py)](#81-prompt-builder-agentprompt_builderpy)\n   - 8.2 [Context Compressor (agent/context_compressor.py)](#82-context-compressor-agentcontext_compressorpy)\n   - 8.3 [Prompt Caching (agent/prompt_caching.py)](#83-prompt-caching-agentprompt_cachingpy)\n   - 8.4 [Auxiliary Client (agent/auxiliary_client.py)](#84-auxiliary-client-agentauxiliary_clientpy)\n   - 8.5 [Display & Spinner (agent/display.py)](#85-display--spinner-agentdisplaypy)\n   - 8.6 [Skill Commands (agent/skill_commands.py)](#86-skill-commands-agentskill_commandspy)\n9. [Messaging Gateway](#9-messaging-gateway)\n   - 9.1 [GatewayRunner (gateway/run.py)](#91-gatewayrunner-gatewayrunpy)\n   - 9.2 [Session Store (gateway/session.py)](#92-session-store-gatewaysessionpy)\n   - 9.3 [Platform Adapters (gateway/platforms/)](#93-platform-adapters-gatewayplatforms)\n10. [Cron Scheduling](#10-cron-scheduling)\n11. [Skills System](#11-skills-system)\n12. [Plugin System](#12-plugin-system)\n13. [Memory System](#13-memory-system)\n14. [ACP Server (IDE Integration)](#14-acp-server-ide-integration)\n15. [API Server](#15-api-server)\n16. [MCP Server Mode](#16-mcp-server-mode)\n17. [RL Training Environments](#17-rl-training-environments)\n18. [Profiles (Multi-Instance)](#18-profiles-multi-instance)\n19. [Security Model](#19-security-model)\n20. [Provider & Model System](#20-provider--model-system)\n21. [Streaming & Reasoning](#21-streaming--reasoning)\n22. [Release History](#22-release-history)\n23. [File Dependency Chain](#23-file-dependency-chain)\n24. [Key Design Patterns](#24-key-design-patterns)\n25. [Configuration Reference](#25-configuration-reference)\n26. [Known Pitfalls](#26-known-pitfalls)\n\n---\n\n## 1. Project Overview\n\n**Hermes Agent** is a self-improving AI agent built by [Nous Research](https://nousresearch.com). It is an open-source (MIT licensed), Python-based project that provides:\n\n- A **full interactive terminal UI** (CLI) for conversing with LLMs\n- A **messaging gateway** supporting 16+ platforms (Telegram, Discord, Slack, WhatsApp, Signal, Matrix, Email, etc.)\n- A **closed learning loop** — the agent creates skills from experience, improves them during use, nudges itself to persist knowledge, searches past conversations, and builds a deepening model of who you are\n- **40+ built-in tools** — terminal execution, file manipulation, web search, browser automation, code execution, image generation, TTS/STT, and more\n- **Any LLM provider** — OpenRouter (200+ models), Nous Portal (400+ models), OpenAI, Anthropic, Hugging Face, GitHub Copilot, z.ai/GLM, Kimi/Moonshot, MiniMax, Alibaba/DashScope, custom endpoints\n- **Six terminal backends** — local, Docker, SSH, Modal (serverless), Daytona (serverless), Singularity (HPC)\n- **Scheduled automations** via built-in cron scheduler\n- **IDE integration** via ACP (Agent Communication Protocol) for VS Code, Zed, JetBrains\n- **MCP integration** — both client (connect to any MCP server) and server (expose Hermes to MCP clients)\n- **RL training** via Atropos environments for training the next generation of tool-calling models\n\n**Tech Stack:**\n\n- Python 3.11+ (core agent, tools, gateway, cron)\n- Node.js (browser automation via agent-browser)\n- SQLite with WAL mode and FTS5 (session storage, full-text search)\n- OpenAI-compatible API (primary inference interface)\n- Anthropic SDK (native Anthropic support)\n- Rich + prompt_toolkit (CLI rendering)\n\n**Repository:** `github.com/NousResearch/hermes-agent`\n**Version:** 0.7.0 (as of April 2026)\n**License:** MIT\n\n---\n\n## 2. Key Features Summary\n\n| Feature                      | Description                                                                                                                                                                      |\n| ---------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| **Terminal UI**              | Full TUI with multiline editing, slash-command autocomplete, conversation history, interrupt-and-redirect, streaming tool output                                                 |\n| **Multi-Platform Messaging** | Telegram, Discord, Slack, WhatsApp, Signal, Matrix, Email, Home Assistant, DingTalk, Feishu/Lark, WeCom, Mattermost, SMS, Webhook — all from a single gateway process            |\n| **Learning Loop**            | Agent-curated memory with periodic nudges, autonomous skill creation, skills self-improve during use, FTS5 session search with LLM summarization, Honcho dialectic user modeling |\n| **Scheduled Tasks**          | Built-in cron scheduler with delivery to any platform (daily reports, nightly backups, weekly audits)                                                                            |\n| **Subagent Delegation**      | Spawn isolated subagents for parallel workstreams with restricted toolsets                                                                                                       |\n| **Execute Code**             | Python scripts that call tools via RPC, collapsing multi-step pipelines into zero-context-cost turns                                                                             |\n| **Terminal Backends**        | Local, Docker, SSH, Modal, Daytona, Singularity — run on a $5 VPS or a GPU cluster                                                                                               |\n| **Skills**                   | 70+ bundled skills across 28 categories, Skills Hub for community discovery, agentskills.io compatibility                                                                        |\n| **Plugins**                  | Drop-in Python plugins with lifecycle hooks (pre_llm_call, post_llm_call, on_session_start, on_session_end)                                                                      |\n| **MCP**                      | Client (connect to MCP servers for extended tools) and Server (expose conversations to MCP clients)                                                                              |\n| **IDE Integration**          | VS Code, Zed, JetBrains via ACP server with session management and tool streaming                                                                                                |\n| **API Server**               | OpenAI-compatible `/v1/chat/completions` endpoint for headless integrations                                                                                                      |\n| **Profiles**                 | Multi-instance support — each profile gets isolated config, memory, sessions, skills, gateway                                                                                    |\n| **Security**                 | Command approval system, secret redaction, SSRF protection, PII redaction, injection detection, credential directory protection                                                  |\n| **RL Training**              | Atropos environments for batch trajectory generation and agent policy optimization                                                                                               |\n\n---\n\n## 3. Installation & Getting Started\n\n```bash\n# One-line install (Linux, macOS, WSL2)\ncurl -fsSL https://raw.githubusercontent.com/NousResearch/hermes-agent/main/scripts/install.sh | bash\n\n# After install\nsource ~/.bashrc    # or: source ~/.zshrc\nhermes              # start chatting\n\n# Key commands\nhermes model        # Choose LLM provider and model\nhermes tools        # Configure which tools are enabled\nhermes config set   # Set individual config values\nhermes gateway      # Start the messaging gateway\nhermes setup        # Run the full setup wizard\nhermes update       # Update to latest version\nhermes doctor       # Diagnose any issues\n```\n\n**For development:**\n\n```bash\ngit clone https://github.com/NousResearch/hermes-agent.git\ncd hermes-agent\ncurl -LsSf https://astral.sh/uv/install.sh | sh\nuv venv venv --python 3.11\nsource venv/bin/activate\nuv pip install -e \".[all,dev]\"\npython -m pytest tests/ -q    # ~3000 tests\n```\n\n---\n\n## 4. Project Structure\n\n```\nhermes-agent/\n├── run_agent.py              # AIAgent class — core conversation loop\n├── model_tools.py            # Tool orchestration, _discover_tools(), handle_function_call()\n├── toolsets.py               # Toolset definitions, _HERMES_CORE_TOOLS list\n├── toolset_distributions.py  # Toolset sampling distributions for RL\n├── cli.py                    # HermesCLI class — interactive CLI orchestrator\n├── hermes_state.py           # SessionDB — SQLite session store (FTS5 search)\n├── hermes_constants.py       # Shared constants, get_hermes_home()\n├── hermes_time.py            # Timezone handling\n├── utils.py                  # Shared utility functions\n├── batch_runner.py           # Parallel batch processing\n├── trajectory_compressor.py  # Trajectory compression for RL training\n├── mcp_serve.py              # MCP server mode entry point\n├── mini_swe_runner.py        # Minimal SWE benchmark runner\n├── rl_cli.py                 # RL CLI commands\n│\n├── agent/                    # Agent internals\n│   ├── prompt_builder.py         # System prompt assembly\n│   ├── context_compressor.py     # Auto context compression\n│   ├── prompt_caching.py         # Anthropic prompt caching\n│   ├── auxiliary_client.py       # Auxiliary LLM client (vision, summarization)\n│   ├── model_metadata.py         # Model context lengths, token estimation\n│   ├── models_dev.py             # models.dev registry integration\n│   ├── display.py                # KawaiiSpinner, tool preview formatting\n│   ├── skill_commands.py         # Skill slash commands (shared CLI/gateway)\n│   └── trajectory.py             # Trajectory saving helpers\n│\n├── hermes_cli/               # CLI subcommands and setup\n│   ├── main.py               # Entry point — all `hermes` subcommands\n│   ├── config.py             # DEFAULT_CONFIG, OPTIONAL_ENV_VARS, migration\n│   ├── commands.py           # Slash command definitions + SlashCommandCompleter\n│   ├── callbacks.py          # Terminal callbacks (clarify, sudo, approval)\n│   ├── setup.py              # Interactive setup wizard\n│   ├── skin_engine.py        # Skin/theme engine\n│   ├── skills_config.py      # `hermes skills` — skill management\n│   ├── tools_config.py       # `hermes tools` — tool management\n│   ├── skills_hub.py         # Skills Hub integration\n│   ├── models.py             # Model catalog, provider model lists\n│   ├── model_switch.py       # Shared /model switch pipeline\n│   └── auth.py               # Provider credential resolution\n│\n├── tools/                    # Tool implementations (one file per tool)\n│   ├── registry.py           # Central tool registry\n│   ├── approval.py           # Dangerous command detection\n│   ├── terminal_tool.py      # Terminal/shell execution\n│   ├── process_registry.py   # Background process management\n│   ├── file_tools.py         # File read/write/search/patch\n│   ├── web_tools.py          # Web search/extract\n│   ├── browser_tool.py       # Browser automation\n│   ├── code_execution_tool.py # execute_code sandbox\n│   ├── delegate_tool.py      # Subagent delegation\n│   ├── mcp_tool.py           # MCP client integration\n│   ├── skills_tool.py        # Skill management tool\n│   ├── todo_tool.py          # Todo/task tracking tool\n│   ├── memory_tool.py        # Memory read/write tool\n│   ├── tts_tool.py           # Text-to-speech\n│   ├── vision_tool.py        # Image analysis\n│   ├── image_gen_tool.py     # Image generation\n│   └── environments/         # Terminal backends\n│       ├── base.py               # BaseEnvironment ABC\n│       ├── local.py              # Local execution\n│       ├── docker.py             # Docker containers\n│       ├── ssh.py                # SSH remote execution\n│       ├── modal.py              # Modal serverless\n│       ├── managed_modal.py      # Nous-hosted Modal\n│       ├── daytona.py            # Daytona serverless\n│       ├── singularity.py        # Singularity HPC containers\n│       └── persistent_shell.py   # Persistent shell mixin\n│\n├── gateway/                  # Messaging platform gateway\n│   ├── run.py                # GatewayRunner — main message loop\n│   ├── session.py            # SessionStore — conversation persistence\n│   ├── status.py             # Gateway status, token locks\n│   └── platforms/            # 16 platform adapters\n│       ├── base.py               # BasePlatformAdapter ABC\n│       ├── telegram.py           # Telegram (polling + webhook)\n│       ├── discord.py            # Discord\n│       ├── slack.py              # Slack\n│       ├── whatsapp.py           # WhatsApp\n│       ├── matrix.py             # Matrix (E2EE)\n│       ├── signal.py             # Signal\n│       ├── email.py              # Email (IMAP/SMTP)\n│       ├── homeassistant.py      # Home Assistant\n│       ├── sms.py                # SMS (Twilio)\n│       ├── mattermost.py         # Mattermost\n│       ├── dingtalk.py           # DingTalk\n│       ├── feishu.py             # Feishu/Lark\n│       ├── wecom.py              # WeCom (Enterprise WeChat)\n│       ├── webhook.py            # Generic webhook\n│       └── api_server.py         # OpenAI-compatible API server\n│\n├── acp_adapter/              # ACP server (IDE integration)\n│   ├── server.py             # HermesACPAgent class\n│   ├── session.py            # SessionManager\n│   ├── events.py             # Streaming callbacks\n│   ├── permissions.py        # Approval callbacks\n│   └── entry.py              # Entry point\n│\n├── cron/                     # Scheduler\n│   ├── scheduler.py          # tick() — job execution engine\n│   └── jobs.py               # Job storage and CRUD\n│\n├── plugins/                  # Plugin system\n│   └── memory/               # 8 memory provider plugins\n│       ├── openviking/\n│       ├── mem0/\n│       ├── hindsight/\n│       ├── holographic/\n│       ├── honcho/\n│       ├── retaindb/\n│       └── byterover/\n│\n├── environments/             # RL training environments (Atropos)\n│   ├── hermes_base_env.py    # Abstract base RL environment\n│   ├── agent_loop.py         # HermesAgentLoop — rollout execution\n│   ├── tool_context.py       # ToolContext — sandbox for RL\n│   ├── web_research_env.py   # Web research tasks\n│   └── agentic_opd_env.py    # Observation-Prediction-Demo env\n│\n├── skills/                   # 70+ bundled skills across 28 categories\n├── optional-skills/          # Additional optional skills\n├── tests/                    # ~3000 pytest tests\n├── scripts/                  # Install, update, packaging scripts\n├── docker/                   # Docker build files\n├── docs/                     # Documentation source (Docusaurus)\n├── website/                  # Landing page\n├── desktop/                  # Desktop app (Electron, separate repo)\n├── tinker-atropos/           # RL submodule\n│\n├── pyproject.toml            # Python package config\n├── AGENTS.md                 # Developer guide for AI assistants\n├── RELEASE_v0.2.0.md → v0.7.0.md  # Release notes\n└── cli-config.yaml.example   # Example config\n```\n\n**User config directory:** `~/.hermes/`\n\n```\n~/.hermes/\n├── config.yaml           # User settings\n├── .env                  # API keys and secrets\n├── MEMORY.md             # Persistent agent memory\n├── USER.md               # User profile\n├── SOUL.md               # Agent personality/identity\n├── sessions.db           # SQLite session database\n├── skills/               # User-installed skills\n├── skins/                # Custom CLI themes\n├── plugins/              # User plugins\n├── cron/                 # Cron jobs and output\n│   ├── jobs.json\n│   └── output/\n├── cache/                # Image/audio cache\n├── plans/                # Generated plans\n├── profiles/             # Multi-instance profiles\n└── mcp/                  # MCP server configs\n```\n\n---\n\n## 5. Core Architecture\n\n### 5.1 AIAgent Class (run_agent.py)\n\nThe `AIAgent` class is the heart of the system — the core conversation loop that orchestrates LLM calls, tool execution, context management, and response delivery.\n\n**Constructor (~60 parameters):**\n\n```python\nclass AIAgent:\n    def __init__(self,\n        model: str = \"anthropic/claude-opus-4.6\",\n        max_iterations: int = 90,\n        enabled_toolsets: list = None,\n        disabled_toolsets: list = None,\n        quiet_mode: bool = False,\n        save_trajectories: bool = False,\n        platform: str = None,            # \"cli\", \"telegram\", etc.\n        session_id: str = None,\n        session_db: SessionDB = None,\n        skip_context_files: bool = False,\n        skip_memory: bool = False,\n        base_url: str = None,\n        api_key: str = None,\n        provider: str = None,\n        api_mode: str = \"chat_completions\",  # or \"anthropic_messages\" or \"codex_responses\"\n        tool_progress_callback = None,\n        stream_delta_callback = None,\n        thinking_callback = None,\n        status_callback = None,\n        iteration_budget: IterationBudget = None,\n        credential_pool = None,\n        checkpoints_enabled: bool = False,\n        # ... plus provider, routing, callback params\n    )\n```\n\n**Main Methods:**\n\n| Method                                                                          | Returns  | Purpose                                                                            |\n| ------------------------------------------------------------------------------- | -------- | ---------------------------------------------------------------------------------- |\n| `chat(message, stream_callback)`                                                | `str`    | Simple interface — returns final response text                                     |\n| `run_conversation(user_message, system_message, conversation_history, task_id)` | `dict`   | Full interface — returns `{final_response, messages, completed, api_calls, error}` |\n| `_interruptible_api_call(api_kwargs)`                                           | Response | Runs API request in background thread with interrupt support                       |\n| `_interruptible_streaming_api_call(api_kwargs, on_first_delta)`                 | Response | Streaming variant with delta callbacks                                             |\n\n**The Core Agent Loop** (inside `run_conversation()`):\n\n```python\nwhile api_call_count < self.max_iterations and self.iteration_budget.remaining > 0:\n    response = client.chat.completions.create(\n        model=model, messages=messages, tools=tool_schemas\n    )\n    if response.tool_calls:\n        for tool_call in response.tool_calls:\n            result = handle_function_call(tool_call.name, tool_call.args, task_id)\n            messages.append(tool_result_message(result))\n        api_call_count += 1\n    else:\n        return response.content  # Final text response\n```\n\n**Key Behaviors:**\n\n- **Three API modes:** `chat_completions` (OpenAI-compatible), `anthropic_messages` (Anthropic SDK), `codex_responses` (OpenAI Codex)\n- **Parallel tool execution:** Independent tool calls run concurrently via ThreadPoolExecutor (unless they share file paths or are in the never-parallel list)\n- **Interrupt support:** Background threads allow interrupt detection without blocking on HTTP\n- **Error recovery:** Automatic fallback chain (primary → fallback model), retry with exponential backoff, context compression on token overflow\n- **Budget pressure:** Warnings at 70% (caution) and 90% (urgent) of iteration budget\n- **Oversized results:** Tool results >100K chars are saved to temp files with a preview\n- **Stale connection detection:** 90s timeout for streaming, 60s read timeout\n\n**IterationBudget** (thread-safe):\n\n```python\nclass IterationBudget:\n    def consume() -> bool    # Check and consume one iteration\n    def refund()             # Give back iteration (for execute_code turns)\n    @property remaining      # Remaining iterations\n```\n\n---\n\n### 5.2 Tool Orchestration (model_tools.py)\n\nBridges the agent and tool registry — handles discovery, schema generation, and dispatch.\n\n**Key Functions:**\n\n| Function                                                                                | Purpose                                                                   |\n| --------------------------------------------------------------------------------------- | ------------------------------------------------------------------------- |\n| `_discover_tools()`                                                                     | Imports all tool modules (each calls `registry.register()` on import)     |\n| `get_tool_definitions(enabled_toolsets, disabled_toolsets, quiet_mode)`                 | Returns OpenAI-format tool schemas filtered by toolset                    |\n| `handle_function_call(function_name, function_args, task_id, user_task, enabled_tools)` | Main dispatcher — routes calls to registry with arg coercion              |\n| `coerce_tool_args(tool_name, args)`                                                     | Type coercion for LLM-generated arguments (string→int, string→bool, etc.) |\n\n**Tool Discovery Order:**\n\n1. Static tools (web_tools, terminal_tool, file_tools, browser_tool, etc.)\n2. Optional tools (fal_client for image gen, honcho, etc.) — graceful fallback if missing\n3. Plugin-registered tools\n4. MCP server tools (dynamic, via tools/list_changed notifications)\n\n**Special Tool Handling:**\n\n- **Agent-level tools** (todo, memory, session_search, delegate_task): Intercepted by `run_agent.py` before `handle_function_call()`\n- **execute_code**: Passes `enabled_tools` for sandbox tool list\n- **Dynamic schema adjustments**: `browser_navigate` strips web_search reference if tools unavailable\n\n**Async Bridging:**\n\n- Persistent event loops (not `asyncio.run()`) to prevent \"Event loop is closed\" errors\n- Main thread uses shared loop; worker threads get per-thread loops\n- `_run_async()` detects running loop and spins up disposable thread if needed\n\n---\n\n### 5.3 Toolset System (toolsets.py)\n\nProvides flexible tool grouping and composition.\n\n**Core Toolsets:**\n\n| Toolset          | Tools Included                                                                                   |\n| ---------------- | ------------------------------------------------------------------------------------------------ |\n| `web`            | web_search, web_extract, web_crawl                                                               |\n| `terminal`       | terminal                                                                                         |\n| `file`           | read_file, write_file, edit_file, list_files, search_files                                       |\n| `browser`        | browser_navigate, browser_snapshot, browser_click, browser_type, browser_scroll, browser_extract |\n| `vision`         | analyze_image                                                                                    |\n| `image_gen`      | generate_image                                                                                   |\n| `tts`            | text_to_speech                                                                                   |\n| `todo`           | todo_read, todo_write                                                                            |\n| `memory`         | memory_read, memory_write                                                                        |\n| `session_search` | session_search                                                                                   |\n| `delegation`     | delegate_task                                                                                    |\n| `code_execution` | execute_code                                                                                     |\n| `cronjob`        | create_job, list_jobs, delete_job                                                                |\n| `messaging`      | send_message                                                                                     |\n| `homeassistant`  | ha_get_states, ha_call_service, ...                                                              |\n\n**Composite Toolsets:**\n\n- `hermes-cli` — All core tools for CLI platform\n- `hermes-telegram`, `hermes-discord`, etc. — Platform-specific tool sets\n- `hermes-gateway` — Union of all platform tools\n- `debugging` — terminal + file + web\n- `safe` — Everything except terminal\n\n**Resolution:**\n\n```python\nresolve_toolset(name, visited=None) → List[str]\n# Recursively resolves toolset to tool names\n# Handles composition (includes) and cycle detection\n# Special aliases: \"all\" or \"*\" = all tools\n```\n\n---\n\n### 5.4 Tool Registry (tools/registry.py)\n\nSingleton managing all tool schemas and handlers. Circular-import safe — has no tool dependencies.\n\n**ToolEntry** (per-tool metadata):\n\n```python\n@dataclass(slots=True)\nclass ToolEntry:\n    name: str\n    toolset: str\n    schema: dict           # OpenAI-format tool definition\n    handler: Callable      # Sync or async handler function\n    check_fn: Callable     # Returns True if tool is available\n    requires_env: list     # Required environment variables\n    is_async: bool\n    description: str\n    emoji: str\n```\n\n**Key Methods:**\n\n```python\nregistry.register(name, toolset, schema, handler, check_fn, requires_env)\nregistry.get_definitions(tool_names, quiet)  # Returns filtered schemas\nregistry.dispatch(name, args, **kwargs)      # Execute with async bridging\nregistry.deregister(name)                    # Remove (for MCP tool refresh)\nregistry.check_tool_availability()           # Returns (available, unavailable)\n```\n\n---\n\n### 5.5 Session Database (hermes_state.py)\n\nSQLite-based persistent session storage with FTS5 full-text search.\n\n**Schema (v6):**\n\n```sql\n-- Sessions table\nsessions (\n    id TEXT PRIMARY KEY,\n    source TEXT, user_id TEXT, model TEXT, model_config TEXT,\n    system_prompt TEXT, parent_session_id TEXT,\n    started_at TEXT, ended_at TEXT, end_reason TEXT,\n    message_count INTEGER, tool_call_count INTEGER,\n    input_tokens INTEGER, output_tokens INTEGER,\n    cache_read_tokens INTEGER, cache_write_tokens INTEGER, reasoning_tokens INTEGER,\n    estimated_cost_usd REAL, actual_cost_usd REAL,\n    title TEXT  -- UNIQUE INDEX\n)\n\n-- Messages table\nmessages (\n    id INTEGER PRIMARY KEY AUTOINCREMENT,\n    session_id TEXT, role TEXT, content TEXT,\n    tool_call_id TEXT, tool_calls TEXT,  -- JSON\n    tool_name TEXT, timestamp TEXT,\n    token_count INTEGER, finish_reason TEXT,\n    reasoning TEXT, reasoning_details TEXT, codex_reasoning_items TEXT\n)\n\n-- FTS5 virtual table (auto-synced via triggers)\nmessages_fts (content)\n```\n\n**Concurrency Model:**\n\n- WAL (Write-Ahead Logging) for concurrent readers + single writer\n- `BEGIN IMMEDIATE` for write transactions (lock at start, not commit)\n- Jitter retry on lock: 20-150ms random backoff, max 15 retries\n- Periodic WAL checkpoint every 50 writes\n\n**Key Operations:**\n\n- `create_session()`, `end_session()`, `reopen_session()`\n- `add_message()`, `get_messages()`\n- `search_sessions(query)` — FTS5 full-text search\n- `update_token_counts()` — Supports both incremental (CLI) and absolute (gateway) modes\n\n---\n\n### 5.6 Constants & Home Directory (hermes_constants.py)\n\nImport-safe constants module with no circular dependencies.\n\n```python\nget_hermes_home() → Path          # HERMES_HOME env var or ~/.hermes\ndisplay_hermes_home() → str       # User-friendly display: \"~/.hermes\"\nget_optional_skills_dir() → Path  # HERMES_OPTIONAL_SKILLS env var\nparse_reasoning_effort(str) → Dict  # \"high\" → {\"enabled\": True, \"effort\": \"high\"}\n\n# Key constants\nOPENROUTER_BASE_URL = \"https://openrouter.ai/api/v1\"\nNOUS_API_BASE_URL = \"https://inference-api.nousresearch.com/v1\"\nAI_GATEWAY_BASE_URL = \"https://ai-gateway.vercel.sh/v1\"\nVALID_REASONING_EFFORTS = (\"xhigh\", \"high\", \"medium\", \"low\", \"minimal\")\n```\n\n---\n\n## 6. CLI System\n\n### 6.1 Interactive CLI (cli.py)\n\nThe `HermesCLI` class provides the interactive terminal interface.\n\n**Features:**\n\n- **Rich** for banner/panels, **prompt_toolkit** for input with autocomplete\n- **KawaiiSpinner** — animated faces during API calls, `┊` activity feed for tool results\n- Multiline editing with Shift+Enter\n- Slash-command autocomplete\n- Session history with up/down arrow navigation\n- Clipboard image paste (Alt+V / Ctrl+V)\n- Status bar showing model, provider, and token counts\n- Inline diff previews for file write/patch operations\n\n**Configuration Loading:**\n\n```python\nload_cli_config() → dict\n# Loads from ~/.hermes/config.yaml (or ./cli-config.yaml fallback)\n# Merges with hardcoded defaults\n# Expands ${ENV_VAR} references\n# Maps terminal config → env vars\n```\n\n---\n\n### 6.2 CLI Entry Point (hermes_cli/main.py)\n\nAll `hermes` subcommands are dispatched from here:\n\n```\nhermes                    # Default: interactive chat\nhermes chat               # Explicit interactive mode\nhermes gateway start|stop|status|install|uninstall\nhermes setup              # Setup wizard\nhermes model              # Select model/provider\nhermes tools              # Configure tools\nhermes skills             # Manage skills\nhermes config set|get     # Direct config manipulation\nhermes cron list|delete   # Cron job management\nhermes doctor             # Diagnose issues\nhermes sessions browse    # Session picker\nhermes profile create|list|switch|delete|export|import\nhermes mcp serve|add|remove  # MCP management\nhermes acp                # Start ACP server\nhermes update|uninstall|version\n```\n\n**Profile System:**\n\n- `_apply_profile_override()` runs BEFORE any imports to set `HERMES_HOME`\n- Pre-parses `--profile/-p` from argv\n- Allows fully isolated agent instances with separate config, memory, sessions, skills\n\n---\n\n### 6.3 Configuration System (hermes_cli/config.py)\n\n**Key Configuration Sections:**\n\n```yaml\nmodel: \"anthropic/claude-opus-4.6\" # or dict with provider/base_url/api_key\nproviders: {} # Provider-specific configs\nfallback_providers: [] # Ordered failover list\ncredential_pool: {} # Multiple API keys per provider\n\nagent:\n  max_turns: 90\n  gateway_timeout: 1800\n  tool_use_enforcement: \"auto\"\n\nterminal:\n  backend: \"local\" # local|docker|modal|daytona|ssh|singularity\n  timeout: 180\n  persistent_shell: true\n  docker_image: \"nikolaik/python-nodejs:...\"\n\ncompression:\n  enabled: true\n  threshold: 0.50 # Compress when 50% of context used\n  target_ratio: 0.20 # Summary = 20% of compressed content\n  protect_last_n: 20\n\nauxiliary:\n  vision: { provider, model }\n  web_extract: { provider, model }\n  compression: { provider, model }\n\nmemory:\n  memory_enabled: true\n  provider: \"\" # \"\" | \"honcho\" | \"mem0\" | etc.\n  memory_char_limit: 2200\n\ndisplay:\n  personality: \"kawaii\"\n  show_reasoning: false\n  inline_diffs: true\n  skin: \"default\"\n  streaming: true\n\ntts:\n  provider: \"edge\" # edge|elevenlabs|openai|neutts\n\nstt:\n  enabled: true\n  provider: \"local\" # local|groq|openai\n\nprivacy:\n  redact_pii: false\n\nmcp_servers: {} # MCP server configurations\n\nskills:\n  external_dirs: [] # Additional skill directories\n\napprovals:\n  mode: \"smart\" # smart|always|off\n```\n\n**Config Files:**\n\n- `~/.hermes/config.yaml` — User settings (authoritative)\n- `~/.hermes/.env` — API keys and secrets\n- Config version migration system (currently v5)\n\n---\n\n### 6.4 Slash Command Registry (hermes_cli/commands.py)\n\nAll slash commands defined centrally in `COMMAND_REGISTRY`:\n\n```python\nCommandDef(name, description, category, aliases, args_hint, cli_only, gateway_only)\n```\n\n**Derived automatically by:**\n\n- CLI `process_command()` — dispatch on canonical name\n- Gateway dispatch + help\n- Telegram BotCommand menu\n- Slack `/hermes` subcommands\n- Autocomplete + help text\n\n**Key Commands:**\n\n| Command        | Aliases    | Description                   |\n| -------------- | ---------- | ----------------------------- |\n| `/new`         | `/reset`   | Start fresh conversation      |\n| `/model`       |            | Show/switch model             |\n| `/personality` |            | Set agent personality         |\n| `/retry`       |            | Retry last turn               |\n| `/undo`        |            | Remove last turn              |\n| `/compress`    | `/compact` | Compress context              |\n| `/usage`       | `/cost`    | Show token usage              |\n| `/insights`    |            | Usage analytics               |\n| `/skills`      |            | Browse/install skills         |\n| `/background`  | `/bg`      | Manage background processes   |\n| `/plan`        |            | Generate implementation plan  |\n| `/rollback`    |            | Restore filesystem checkpoint |\n| `/verbose`     |            | Toggle debug output           |\n| `/reasoning`   |            | Set reasoning effort          |\n| `/yolo`        |            | Toggle approval bypass        |\n| `/btw`         |            | Ephemeral side question       |\n| `/stop`        |            | Kill current agent run        |\n| `/queue`       |            | Queue next prompt             |\n| `/browser`     |            | Interactive browser session   |\n| `/history`     | `/resume`  | Session browser               |\n| `/skin`        |            | Switch CLI theme              |\n\n---\n\n### 6.5 Setup Wizard (hermes_cli/setup.py)\n\nModular interactive wizard with independent sections:\n\n1. **Model & Provider** — Select AI provider, enter API keys, choose model\n2. **Terminal Backend** — Choose execution environment\n3. **Agent Settings** — Max iterations, compression, session policies\n4. **Messaging Platforms** — Configure Telegram, Discord, Slack, etc.\n5. **Tools** — TTS, STT, web search, image generation, browser\n\nFeatures:\n\n- Live credential validation\n- Real-time model list fetching from provider APIs\n- Automatic OpenClaw migration detection\n- Atomic config file writes\n\n---\n\n### 6.6 Model Catalog (hermes_cli/models.py)\n\nProvider-specific model lists:\n\n```python\n_PROVIDER_MODELS = {\n    \"nous\": [\"anthropic/claude-opus-4.6\", \"anthropic/claude-sonnet-4.6\", ...],  # 25+\n    \"openrouter\": [\"anthropic/claude-opus-4.6\", \"google/gemini-3-flash\", ...],  # 30+\n    \"anthropic\": [\"claude-opus-4-6\", \"claude-sonnet-4-6\", ...],\n    \"openai\": [\"gpt-5\", \"gpt-5.4-mini\", \"gpt-4.1\", \"gpt-4o\", ...],\n    \"copilot\": [\"gpt-5.4\", \"gpt-5.4-mini\", \"gpt-5.3-codex\", ...],\n    \"huggingface\": [...],\n    \"minimax\": [...],\n    \"kimi-coding\": [...],\n    \"alibaba\": [...],\n    \"deepseek\": [...],\n    # ... more providers\n}\n```\n\nFeatures:\n\n- Dynamic fetching via provider `/models` endpoints\n- Curated lists used when live probe returns fewer models\n- Fuzzy matching for typo correction\n- Validation against provider catalog\n\n---\n\n### 6.7 Skin/Theme Engine (hermes_cli/skin_engine.py)\n\nData-driven CLI visual customization — no code changes needed.\n\n**Customizable Elements:**\n\n| Element                          | Key                      | Used By           |\n| -------------------------------- | ------------------------ | ----------------- |\n| Banner border/title/accent       | `colors.*`               | banner.py         |\n| Response box border              | `colors.response_border` | cli.py            |\n| Spinner faces (waiting/thinking) | `spinner.*`              | display.py        |\n| Spinner verbs/wings              | `spinner.*`              | display.py        |\n| Tool output prefix               | `tool_prefix`            | display.py        |\n| Per-tool emojis                  | `tool_emojis`            | display.py        |\n| Agent name/welcome/prompt        | `branding.*`             | banner.py, cli.py |\n\n**Built-in Skins:** default, ares, mono, slate, poseidon, sisyphus, charizard\n\n**User Skins:** Drop `~/.hermes/skins/<name>.yaml` and activate with `/skin <name>`\n\n---\n\n## 7. Tool System\n\n### 7.1 Terminal Tool (tools/terminal_tool.py)\n\nShell command execution across multiple backends.\n\n```python\ndef terminal_tool(\n    command: str,\n    background: bool = False,\n    timeout: Optional[int] = None,\n    task_id: Optional[str] = None,\n    force: bool = False,        # Skip approval for dangerous commands\n    workdir: Optional[str] = None,\n    check_interval: Optional[int] = None,  # Background task polling\n    pty: bool = False,\n) -> str  # JSON result\n```\n\n**Features:**\n\n- Multi-backend: Selects based on `TERMINAL_ENV` (local/docker/ssh/modal/daytona/singularity)\n- Per-task_id sandboxes with thread-safe creation locks\n- Dangerous command routing through approval system\n- Background task support with file-based IPC\n- Interrupt handling — polls `is_interrupted()` during execution\n- Auto-cleanup daemon thread for idle environments (>300s)\n- Disk usage warnings at configurable threshold\n\n---\n\n### 7.2 File Tools (tools/file_tools.py)\n\nSafe file operations with size guards and sensitive path protection.\n\n- `read_file_tool(path, offset, limit)` — Read with pagination (default 100K char limit)\n- `write_file_tool(path, content)` — Write with approval for sensitive paths\n- `edit_file_tool(path, old_text, new_text)` — String replacement editing\n- `list_files_tool(path)` — Directory listing\n- `search_files_tool(pattern, path)` — Glob/regex file search\n\n**Safety:**\n\n- Device path blocklist (`/dev/zero`, `/dev/stdin`, etc.)\n- Read dedup tracking — returns stub on re-read if mtime unchanged\n- Sensitive path blocking: `/etc/`, `/boot/`, `~/.ssh` without approval\n- Prompt injection protection for known dangerous paths\n\n---\n\n### 7.3 Web Tools (tools/web_tools.py)\n\nWeb search and content extraction.\n\n- `web_search_tool(query, limit)` — Search via configurable backend\n- `web_extract_tool(urls, format)` — Extract content from URLs\n- `web_crawl_tool(url, instructions)` — LLM-guided web crawling\n\n**Backends:** Firecrawl, Parallel Web, Tavily, Exa, DuckDuckGo\n\n- Fallback detection by highest-priority available API key\n- LLM processing via auxiliary client (Gemini 3 Flash) for intelligent extraction\n- SSRF protection and URL safety checks\n\n---\n\n### 7.4 Browser Tool (tools/browser_tool.py)\n\nHeadless browser automation with accessibility tree.\n\n```python\nbrowser_navigate(url, task_id) → str\nbrowser_snapshot(task_id, max_chars) → str\nbrowser_click(ref, task_id) → str      # Element refs like @e1, @e2\nbrowser_type(ref, text, task_id) → str\nbrowser_scroll(ref, direction, task_id) → str\nbrowser_extract(task, max_chars, task_id) → str\n```\n\n**Backends:**\n\n- **Local:** Headless Chromium via `agent-browser` CLI\n- **Cloud:** Browserbase (stealth, proxies, CAPTCHA solving)\n- **Camofox:** Anti-detection browser using Camoufox\n\n**Features:**\n\n- Accessibility tree for text-based snapshots (no vision required)\n- Session isolation per task_id with inactivity cleanup\n- API key detection in URLs (prevents exfiltration)\n- SSRF protection for private/internal addresses\n\n---\n\n### 7.5 Delegate Tool (tools/delegate_tool.py)\n\nSpawn isolated subagents for parallel workstreams.\n\n```python\ndef delegate_task(\n    goal: str,\n    context: str = None,\n    toolsets: List[str] = None,       # Default: [\"terminal\", \"file\", \"web\"]\n    tasks: List[Dict] = None,          # Batch mode: up to 3 concurrent\n    max_iterations: int = 50,\n    parent_agent = None,\n) → str\n```\n\n**Isolation:**\n\n- Fresh conversation (no parent history)\n- Own task_id (separate terminal session, file ops)\n- Restricted toolset (configurable, blocked tools always stripped)\n- Focused system prompt from goal + context\n- Parent only sees delegation call and summary result\n\n**Blocked Tools:** delegate_task, clarify, memory, send_message, execute_code (no recursion, no user interaction)\n\n**Constraints:** MAX_DEPTH=2, MAX_CONCURRENT_CHILDREN=3\n\n---\n\n### 7.6 MCP Tool (tools/mcp_tool.py)\n\nModel Context Protocol client integration.\n\n**Configuration (config.yaml):**\n\n```yaml\nmcp_servers:\n  filesystem:\n    command: \"npx\"\n    args: [\"-y\", \"@modelcontextprotocol/server-filesystem\", \"/tmp\"]\n    timeout: 120\n  remote_api:\n    url: \"https://my-mcp-server.example.com/mcp\"\n    headers:\n      Authorization: \"Bearer sk-...\"\n    sampling:\n      enabled: true\n      model: \"gemini-3-flash\"\n```\n\n**Features:**\n\n- **Transports:** Stdio (local processes) and HTTP/StreamableHTTP (remote)\n- **Dynamic tool discovery:** Listens for `tools/list_changed` notifications\n- **Sampling support:** MCP servers can request LLM completions\n- **Automatic reconnection** with exponential backoff (5 retries)\n- Dedicated background event loop in daemon thread\n- Thread-safe via lock protecting server dict\n\n---\n\n### 7.7 Approval System (tools/approval.py)\n\nDangerous command detection and approval flow.\n\n```python\ndetect_dangerous_command(command: str) → (is_dangerous, pattern_key, description)\n```\n\n**106 patterns covering:**\n\n- Destructive operations (`rm -r /`, `mkfs`, `dd if=`)\n- Privilege escalation (`chmod 777`, `chown -R root`)\n- SQL injection (`DROP TABLE`, `DELETE FROM` without WHERE)\n- System targeting (`/etc/` writes, `systemctl stop`, fork bombs)\n- Shell injection (pipe to sh, wget to sh, process substitution)\n- Network exfiltration (curl with embedded API keys)\n- Secret access (`cat ~/.env`, `cat ~/.netrc`)\n\n**Normalization:** Strips ANSI escapes, null bytes, normalizes Unicode\n\n**Approval Modes:**\n\n- `smart` — Learns which commands are safe based on user decisions\n- `always` — Always ask for dangerous commands\n- `off` — Never ask (or `/yolo` toggle)\n\n---\n\n### 7.8 Terminal Backends (tools/environments/)\n\n| Backend           | File               | Features                                                                       |\n| ----------------- | ------------------ | ------------------------------------------------------------------------------ |\n| **Local**         | `local.py`         | Direct execution, interrupt support, non-blocking I/O, persistent shell        |\n| **Docker**        | `docker.py`        | Sandboxed containers, cap-drop ALL, no-new-privileges, PID limits, bind mounts |\n| **SSH**           | `ssh.py`           | Remote execution via ControlMaster, persistent shell mixin                     |\n| **Modal**         | `modal.py`         | Native Modal SDK `Sandbox.create()`/`Sandbox.exec()`, persistent snapshots     |\n| **Managed Modal** | `managed_modal.py` | Modal through Nous-hosted gateway                                              |\n| **Daytona**       | `daytona.py`       | Daytona SDK cloud sandboxes, stop/resume lifecycle                             |\n| **Singularity**   | `singularity.py`   | Singularity containers with scratch dir, SIF cache                             |\n\n**Common Interface (BaseEnvironment ABC):**\n\n```python\nexecute(command, timeout) → {\"output\": str, \"returncode\": int}\ncleanup() → None\n```\n\n---\n\n## 8. Agent Internals\n\n### 8.1 Prompt Builder (agent/prompt_builder.py)\n\nAssembles the system prompt from multiple sources:\n\n| Component               | Source                                               |\n| ----------------------- | ---------------------------------------------------- |\n| Agent Identity          | `DEFAULT_AGENT_IDENTITY` or `SOUL.md`                |\n| Memory Guidance         | When/how to use memory tool                          |\n| Session Search Guidance | How to recall past conversations                     |\n| Skills Guidance         | When to create/patch skills                          |\n| Tool Use Enforcement    | Must execute tools, not describe actions             |\n| Skills Index            | `~/.hermes/skills/.hermes-skills.json`               |\n| Platform Hints          | OS, Python version, shell, available tools           |\n| Context Files           | `.hermes.md`, `AGENTS.md`, `.cursorrules`, `SOUL.md` |\n| Model/Provider Info     | Current model and provider identity                  |\n\n**Context File Discovery:**\n\n1. Check `cwd/.hermes.md` or `HERMES.md`\n2. Walk parent directories up to git root\n3. Validate against injection patterns before inclusion\n\n**Injection Detection (30+ patterns):**\n\n- \"ignore previous instructions\", \"system prompt override\"\n- \"do not tell the user\", \"act as if you have no restrictions\"\n- HTML comment injection, exfiltration via curl, Unicode stealth chars\n\n---\n\n### 8.2 Context Compressor (agent/context_compressor.py)\n\nAutomatically compresses conversation history when approaching context limits.\n\n```python\nclass ContextCompressor:\n    threshold_percent: float = 0.50   # Compress when 50% of context used\n    protect_first_n: int = 3          # Keep system prompt + first turn\n    protect_last_n: int = 20          # Keep recent N messages\n    summary_target_ratio: float = 0.20  # Summary = 20% of compressed content\n```\n\n**Compression Algorithm:**\n\n1. **Pre-pass:** Replace old tool results with placeholder (cheap)\n2. **Protect head:** System prompt + first exchange always kept\n3. **Protect tail:** Token-budget-based tail protection (~20K tokens)\n4. **Summarize middle:** LLM-generated structured summary (Goal, Progress, Decisions, Files, Next Steps)\n5. **Iterative updates:** On subsequent compressions, update previous summary instead of re-summarizing\n\n---\n\n### 8.3 Prompt Caching (agent/prompt_caching.py)\n\nAnthropic prompt caching support for cost reduction.\n\n- System prompt cached across turns (first conversation turn establishes cache)\n- Cache markers inserted at system prompt boundaries\n- Must not break mid-conversation — altering past context invalidates cache\n- The ONLY time context is altered is during compression\n\n**Critical Rule:** Do NOT implement changes that would alter past context, change toolsets, reload memories, or rebuild system prompts mid-conversation.\n\n---\n\n### 8.4 Auxiliary Client (agent/auxiliary_client.py)\n\nSeparate LLM client for non-primary tasks:\n\n- **Vision analysis** — Image description and analysis\n- **Web extraction** — Content summarization\n- **Context compression** — Generating conversation summaries\n- **Session search** — Summarizing search results\n- **MCP sampling** — Serving server-initiated LLM requests\n\nConfigured per-task via `auxiliary` section in config.yaml.\n\n---\n\n### 8.5 Display & Spinner (agent/display.py)\n\n- **KawaiiSpinner** — Animated face characters during API calls\n- **Tool preview formatting** — `┊` prefixed activity feed for tool execution\n- **Inline diff previews** — Shows unified diffs for write/patch operations\n- Respects active skin for colors, emojis, and branding\n\n---\n\n### 8.6 Skill Commands (agent/skill_commands.py)\n\nShared skill invocation for CLI and gateway:\n\n- Skills loaded from `~/.hermes/skills/` and external directories\n- Injected as **user message** (not system prompt) to preserve prompt caching\n- `/plan` command generates implementation plans stored in `.hermes/plans/`\n- Skill content includes setup instructions, tool options, usage examples\n\n---\n\n## 9. Messaging Gateway\n\n### 9.1 GatewayRunner (gateway/run.py)\n\nMain controller managing all platform adapters and routing messages.\n\n**Key Attributes:**\n\n- `adapters: Dict[Platform, BasePlatformAdapter]` — Active platform instances\n- `session_store: SessionStore` — Conversation persistence\n- `_running_agents: Dict[str, AIAgent]` — Per-session cached agents (preserves prompt caching)\n- `_pending_approvals: Dict[str, Dict]` — Dangerous command approval tracking\n- `pairing_store: PairingStore` — DM code-based user authorization\n\n**Message Flow:**\n\n1. Platform adapter queues `MessageEvent`\n2. GatewayRunner dequeues, calls `_handle_message()`\n3. Slash command? → dispatch to handler\n4. Regular message? → `_handle_message_with_agent",".codex/hooks.json":"{\n  \"hooks\": {\n    \"UserPromptSubmit\": [\n      {\n        \"hooks\": [\n          {\n            \"type\": \"command\",\n            \"command\": \"lat hook claude UserPromptSubmit\"\n          }\n        ]\n      }\n    ],\n    \"Stop\": [\n      {\n        \"hooks\": [\n          {\n            \"type\": \"command\",\n            \"command\": \"lat hook claude Stop\"\n          }\n        ]\n      }\n    ]\n  }\n}\n","CLAUDE.md":"# Before starting work\n\n- Run `lat search` to find sections relevant to your task. Read them to understand the design intent before writing code.\n- Run `lat expand` on user prompts to expand any `[[refs]]` — this resolves section names to file locations and provides context.\n\n# Post-task checklist (REQUIRED — do not skip)\n\nAfter EVERY task, before responding to the user:\n\n- [ ] Update `lat.md/` if you added or changed any functionality, architecture, tests, or behavior\n- [ ] Run `lat check` — all wiki links and code refs must pass\n- [ ] Do not skip these steps. Do not consider your task done until both are complete.\n\n---\n\n# What is lat.md?\n\nThis project uses [lat.md](https://www.npmjs.com/package/lat.md) to maintain a structured knowledge graph of its architecture, design decisions, and test specs in the `lat.md/` directory. It is a set of cross-linked markdown files that describe **what** this project does and **why** — the domain concepts, key design decisions, business logic, and test specifications. Use it to ground your work in the actual architecture rather than guessing.\n\n# Commands\n\n```bash\nlat locate \"Section Name\"      # find a section by name (exact, fuzzy)\nlat refs \"file#Section\"        # find what references a section\nlat search \"natural language\"  # semantic search across all sections\nlat expand \"user prompt text\"  # expand [[refs]] to resolved locations\nlat check                      # validate all links and code refs\n```\n\nRun `lat --help` when in doubt about available commands or options.\n\nIf `lat search` fails because no API key is configured, explain to the user that semantic search requires a key provided via `LAT_LLM_KEY` (direct value), `LAT_LLM_KEY_FILE` (path to key file), or `LAT_LLM_KEY_HELPER` (command that prints the key). Supported key prefixes: `sk-...` (OpenAI) or `vck_...` (Vercel). If the user doesn't want to set it up, use `lat locate` for direct lookups instead.\n\n# Syntax primer\n\n- **Section ids**: `lat.md/path/to/file#Heading#SubHeading` — full form uses project-root-relative path (e.g. `lat.md/tests/search#RAG Replay Tests`). Short form uses bare file name when unique (e.g. `search#RAG Replay Tests`, `cli#search#Indexing`).\n- **Wiki links**: `[[target]]` or `[[target|alias]]` — cross-references between sections. Can also reference source code: `[[src/foo.ts#myFunction]]`.\n- **Source code links**: Wiki links in `lat.md/` files can reference functions, classes, constants, and methods in TypeScript/JavaScript/Python/Rust/Go/C files. Use the full path: `[[src/config.ts#getConfigDir]]`, `[[src/server.ts#App#listen]]` (class method), `[[lib/utils.py#parse_args]]`, `[[src/lib.rs#Greeter#greet]]` (Rust impl method), `[[src/app.go#Greeter#Greet]]` (Go method), `[[src/app.h#Greeter]]` (C struct). `lat check` validates these exist.\n- **Code refs**: `// @lat: [[section-id]]` (JS/TS/Rust/Go/C) or `# @lat: [[section-id]]` (Python) — ties source code to concepts\n\n# Test specs\n\nKey tests can be described as sections in `lat.md/` files (e.g. `tests.md`). Add frontmatter to require that every leaf section is referenced by a `// @lat:` or `# @lat:` comment in test code:\n\n```markdown\n---\nlat:\n  require-code-mention: true\n---\n# Tests\n\nAuthentication and authorization test specifications.\n\n## User login\n\nVerify credential validation and error handling for the login endpoint.\n\n### Rejects expired tokens\nTokens past their expiry timestamp are rejected with 401, even if otherwise valid.\n\n### Handles missing password\nLogin request without a password field returns 400 with a descriptive error.\n```\n\nEvery section MUST have a description — at least one sentence explaining what the test verifies and why. Empty sections with just a heading are not acceptable. (This is a specific case of the general leading paragraph rule below.)\n\nEach test in code should reference its spec with exactly one comment placed next to the relevant test — not at the top of the file:\n\n```python\n# @lat: [[tests#User login#Rejects expired tokens]]\ndef test_rejects_expired_tokens():\n    ...\n\n# @lat: [[tests#User login#Handles missing password]]\ndef test_handles_missing_password():\n    ...\n```\n\nDo not duplicate refs. One `@lat:` comment per spec section, placed at the test that covers it. `lat check` will flag any spec section not covered by a code reference, and any code reference pointing to a nonexistent section.\n\n# Section structure\n\nEvery section in `lat.md/` **must** have a leading paragraph — at least one sentence immediately after the heading, before any child headings or other block content. The first paragraph must be ≤250 characters (excluding `[[wiki link]]` content). This paragraph serves as the section's overview and is used in search results, command output, and RAG context — keeping it concise guarantees the section's essence is always captured.\n\n```markdown\n# Good Section\n\nBrief overview of what this section documents and why it matters.\n\nMore detail can go in subsequent paragraphs, code blocks, or lists.\n\n## Child heading\n\nDetails about this child topic.\n```\n\n```markdown\n# Bad Section\n\n## Child heading\n\nDetails about this child topic.\n```\n\nThe second example is invalid because `Bad Section` has no leading paragraph. `lat check` validates this rule and reports errors for missing or overly long leading paragraphs.\n",".agents/skills/typescript-expert/references/tsconfig-strict.json":"{\n  \"$schema\": \"https://json.schemastore.org/tsconfig\",\n  \"display\": \"Strict TypeScript 5.x\",\n  \"compilerOptions\": {\n    // =========================================================================\n    // STRICTNESS (Maximum Type Safety)\n    // =========================================================================\n    \"strict\": true,\n    \"noUncheckedIndexedAccess\": true,\n    \"noImplicitOverride\": true,\n    \"noPropertyAccessFromIndexSignature\": true,\n    \"exactOptionalPropertyTypes\": true,\n    \"noFallthroughCasesInSwitch\": true,\n    \"forceConsistentCasingInFileNames\": true,\n    // =========================================================================\n    // MODULE SYSTEM (Modern ESM)\n    // =========================================================================\n    \"module\": \"ESNext\",\n    \"moduleResolution\": \"bundler\",\n    \"resolveJsonModule\": true,\n    \"esModuleInterop\": true,\n    \"allowSyntheticDefaultImports\": true,\n    \"isolatedModules\": true,\n    \"verbatimModuleSyntax\": true,\n    // =========================================================================\n    // OUTPUT\n    // =========================================================================\n    \"target\": \"ES2022\",\n    \"lib\": [\"ES2022\", \"DOM\", \"DOM.Iterable\"],\n    \"declaration\": true,\n    \"declarationMap\": true,\n    \"sourceMap\": true,\n    // =========================================================================\n    // PERFORMANCE\n    // =========================================================================\n    \"skipLibCheck\": true,\n    \"incremental\": true,\n    // =========================================================================\n    // PATH ALIASES\n    // =========================================================================\n    \"baseUrl\": \".\",\n    \"paths\": {\n      \"@/*\": [\"./src/*\"],\n      \"@/components/*\": [\"./src/components/*\"],\n      \"@/lib/*\": [\"./src/lib/*\"],\n      \"@/types/*\": [\"./src/types/*\"],\n      \"@/utils/*\": [\"./src/utils/*\"]\n    },\n    // =========================================================================\n    // JSX (for React projects)\n    // =========================================================================\n    // \"jsx\": \"react-jsx\",\n    // =========================================================================\n    // EMIT\n    // =========================================================================\n    \"noEmit\": true // Let bundler handle emit\n    // \"outDir\": \"./dist\",\n    // \"rootDir\": \"./src\",\n    // =========================================================================\n    // DECORATORS (if needed)\n    // =========================================================================\n    // \"experimentalDecorators\": true,\n    // \"emitDecoratorMetadata\": true\n  },\n  \"include\": [\"src/**/*.ts\", \"src/**/*.tsx\", \"src/**/*.d.ts\"],\n  \"exclude\": [\n    \"node_modules\",\n    \"dist\",\n    \"build\",\n    \"coverage\",\n    \"**/*.test.ts\",\n    \"**/*.spec.ts\"\n  ]\n}\n",".agents/skills/typescript-expert/references/typescript-cheatsheet.md":"# TypeScript Cheatsheet\n\n## Type Basics\n\n```typescript\n// Primitives\nconst name: string = \"John\";\nconst age: number = 30;\nconst isActive: boolean = true;\nconst nothing: null = null;\nconst notDefined: undefined = undefined;\n\n// Arrays\nconst numbers: number[] = [1, 2, 3];\nconst strings: Array<string> = [\"a\", \"b\", \"c\"];\n\n// Tuple\nconst tuple: [string, number] = [\"hello\", 42];\n\n// Object\nconst user: { name: string; age: number } = { name: \"John\", age: 30 };\n\n// Union\nconst value: string | number = \"hello\";\n\n// Literal\nconst direction: \"up\" | \"down\" | \"left\" | \"right\" = \"up\";\n\n// Any vs Unknown\nconst anyValue: any = \"anything\"; // ❌ Avoid\nconst unknownValue: unknown = \"safe\"; // ✅ Prefer, requires narrowing\n```\n\n## Type Aliases & Interfaces\n\n```typescript\n// Type Alias\ntype Point = {\n  x: number;\n  y: number;\n};\n\n// Interface (preferred for objects)\ninterface User {\n  id: string;\n  name: string;\n  email?: string; // Optional\n  readonly createdAt: Date; // Readonly\n}\n\n// Extending\ninterface Admin extends User {\n  permissions: string[];\n}\n\n// Intersection\ntype AdminUser = User & { permissions: string[] };\n```\n\n## Generics\n\n```typescript\n// Generic function\nfunction identity<T>(value: T): T {\n  return value;\n}\n\n// Generic with constraint\nfunction getLength<T extends { length: number }>(item: T): number {\n  return item.length;\n}\n\n// Generic interface\ninterface ApiResponse<T> {\n  data: T;\n  status: number;\n  message: string;\n}\n\n// Generic with default\ntype Container<T = string> = {\n  value: T;\n};\n\n// Multiple generics\nfunction merge<T, U>(obj1: T, obj2: U): T & U {\n  return { ...obj1, ...obj2 };\n}\n```\n\n## Utility Types\n\n```typescript\ninterface User {\n  id: string;\n  name: string;\n  email: string;\n  age: number;\n}\n\n// Partial - all optional\ntype PartialUser = Partial<User>;\n\n// Required - all required\ntype RequiredUser = Required<User>;\n\n// Readonly - all readonly\ntype ReadonlyUser = Readonly<User>;\n\n// Pick - select properties\ntype UserName = Pick<User, \"id\" | \"name\">;\n\n// Omit - exclude properties\ntype UserWithoutEmail = Omit<User, \"email\">;\n\n// Record - key-value map\ntype UserMap = Record<string, User>;\n\n// Extract - extract from union\ntype StringOrNumber = string | number | boolean;\ntype OnlyStrings = Extract<StringOrNumber, string>;\n\n// Exclude - exclude from union\ntype NotString = Exclude<StringOrNumber, string>;\n\n// NonNullable - remove null/undefined\ntype MaybeString = string | null | undefined;\ntype DefinitelyString = NonNullable<MaybeString>;\n\n// ReturnType - get function return type\nfunction getUser() {\n  return { name: \"John\" };\n}\ntype UserReturn = ReturnType<typeof getUser>;\n\n// Parameters - get function parameters\ntype GetUserParams = Parameters<typeof getUser>;\n\n// Awaited - unwrap Promise\ntype ResolvedUser = Awaited<Promise<User>>;\n```\n\n## Conditional Types\n\n```typescript\n// Basic conditional\ntype IsString<T> = T extends string ? true : false;\n\n// Infer keyword\ntype UnwrapPromise<T> = T extends Promise<infer U> ? U : T;\n\n// Distributive conditional\ntype ToArray<T> = T extends any ? T[] : never;\ntype Result = ToArray<string | number>; // string[] | number[]\n\n// NonDistributive\ntype ToArrayNonDist<T> = [T] extends [any] ? T[] : never;\n```\n\n## Template Literal Types\n\n```typescript\ntype Color = \"red\" | \"green\" | \"blue\";\ntype Size = \"small\" | \"medium\" | \"large\";\n\n// Combine\ntype ColorSize = `${Color}-${Size}`;\n// 'red-small' | 'red-medium' | 'red-large' | ...\n\n// Event handlers\ntype EventName = \"click\" | \"focus\" | \"blur\";\ntype EventHandler = `on${Capitalize<EventName>}`;\n// 'onClick' | 'onFocus' | 'onBlur'\n```\n\n## Mapped Types\n\n```typescript\n// Basic mapped type\ntype Optional<T> = {\n  [K in keyof T]?: T[K];\n};\n\n// With key remapping\ntype Getters<T> = {\n  [K in keyof T as `get${Capitalize<string & K>}`]: () => T[K];\n};\n\n// Filter keys\ntype OnlyStrings<T> = {\n  [K in keyof T as T[K] extends string ? K : never]: T[K];\n};\n```\n\n## Type Guards\n\n```typescript\n// typeof guard\nfunction process(value: string | number) {\n  if (typeof value === \"string\") {\n    return value.toUpperCase(); // string\n  }\n  return value.toFixed(2); // number\n}\n\n// instanceof guard\nclass Dog {\n  bark() {}\n}\nclass Cat {\n  meow() {}\n}\n\nfunction makeSound(animal: Dog | Cat) {\n  if (animal instanceof Dog) {\n    animal.bark();\n  } else {\n    animal.meow();\n  }\n}\n\n// in guard\ninterface Bird {\n  fly(): void;\n}\ninterface Fish {\n  swim(): void;\n}\n\nfunction move(animal: Bird | Fish) {\n  if (\"fly\" in animal) {\n    animal.fly();\n  } else {\n    animal.swim();\n  }\n}\n\n// Custom type guard\nfunction isString(value: unknown): value is string {\n  return typeof value === \"string\";\n}\n\n// Assertion function\nfunction assertIsString(value: unknown): asserts value is string {\n  if (typeof value !== \"string\") {\n    throw new Error(\"Not a string\");\n  }\n}\n```\n\n## Discriminated Unions\n\n```typescript\n// With type discriminant\ntype Success<T> = { type: \"success\"; data: T };\ntype Error = { type: \"error\"; message: string };\ntype Loading = { type: \"loading\" };\n\ntype State<T> = Success<T> | Error | Loading;\n\nfunction handle<T>(state: State<T>) {\n  switch (state.type) {\n    case \"success\":\n      return state.data; // T\n    case \"error\":\n      return state.message; // string\n    case \"loading\":\n      return null;\n  }\n}\n\n// Exhaustive check\nfunction assertNever(value: never): never {\n  throw new Error(`Unexpected value: ${value}`);\n}\n```\n\n## Branded Types\n\n```typescript\n// Create branded type\ntype Brand<K, T> = K & { __brand: T };\n\ntype UserId = Brand<string, \"UserId\">;\ntype OrderId = Brand<string, \"OrderId\">;\n\n// Constructor functions\nfunction createUserId(id: string): UserId {\n  return id as UserId;\n}\n\nfunction createOrderId(id: string): OrderId {\n  return id as OrderId;\n}\n\n// Usage - prevents mixing\nfunction getOrder(orderId: OrderId, userId: UserId) {}\n\nconst userId = createUserId(\"user-123\");\nconst orderId = createOrderId(\"order-456\");\n\ngetOrder(orderId, userId); // ✅ OK\n// getOrder(userId, orderId)  // ❌ Error - types don't match\n```\n\n## Module Declarations\n\n```typescript\n// Declare module for untyped package\ndeclare module \"untyped-package\" {\n  export function doSomething(): void;\n  export const value: string;\n}\n\n// Augment existing module\ndeclare module \"express\" {\n  interface Request {\n    user?: { id: string };\n  }\n}\n\n// Declare global\ndeclare global {\n  interface Window {\n    myGlobal: string;\n  }\n}\n```\n\n## TSConfig Essentials\n\n```json\n{\n  \"compilerOptions\": {\n    // Strictness\n    \"strict\": true,\n    \"noUncheckedIndexedAccess\": true,\n    \"noImplicitOverride\": true,\n\n    // Modules\n    \"module\": \"ESNext\",\n    \"moduleResolution\": \"bundler\",\n    \"esModuleInterop\": true,\n\n    // Output\n    \"target\": \"ES2022\",\n    \"lib\": [\"ES2022\", \"DOM\"],\n\n    // Performance\n    \"skipLibCheck\": true,\n    \"incremental\": true,\n\n    // Paths\n    \"baseUrl\": \".\",\n    \"paths\": {\n      \"@/*\": [\"./src/*\"]\n    }\n  }\n}\n```\n\n## Best Practices\n\n```typescript\n// ✅ Prefer interface for objects\ninterface User {\n  name: string;\n}\n\n// ✅ Use const assertions\nconst routes = [\"home\", \"about\"] as const;\n\n// ✅ Use satisfies for validation\nconst config = {\n  api: \"https://api.example.com\",\n} satisfies Record<string, string>;\n\n// ✅ Use unknown over any\nfunction parse(input: unknown) {\n  if (typeof input === \"string\") {\n    return JSON.parse(input);\n  }\n}\n\n// ✅ Explicit return types for public APIs\nexport function getUser(id: string): User | null {\n  // ...\n}\n\n// ❌ Avoid\nconst data: any = fetchData();\ndata.anything.goes.wrong; // No type safety\n```\n"},"items":[{"name":"AGENTS.md","path":"AGENTS.md","rawUrl":"https://raw.githubusercontent.com/fathah/hermes-desktop/HEAD/AGENTS.md","title":"AI Agent Protocol & Instructions","category":"root-instruction","format":"markdown","content":"# Before starting work\n\n- Run `lat search` to find sections relevant to your task. Read them to understand the design intent before writing code.\n- Run `lat expand` on user prompts to expand any `[[refs]]` — this resolves section names to file locations and provides context.\n\n# Post-task checklist (REQUIRED — do not skip)\n\nAfter EVERY task, before responding to the user:\n\n- [ ] Update `lat.md/` if you added or changed any functionality, architecture, tests, or behavior\n- [ ] Run `lat check` — all wiki links and code refs must pass\n- [ ] Do not skip these steps. Do not consider your task done until both are complete.\n\n---\n\n# What is lat.md?\n\nThis project uses [lat.md](https://www.npmjs.com/package/lat.md) to maintain a structured knowledge graph of its architecture, design decisions, and test specs in the `lat.md/` directory. It is a set of cross-linked markdown files that describe **what** this project does and **why** — the domain concepts, key design decisions, business logic, and test specifications. Use it to ground your work in the actual architecture rather than guessing.\n\n# Commands\n\n```bash\nlat locate \"Section Name\"      # find a section by name (exact, fuzzy)\nlat refs \"file#Section\"        # find what references a section\nlat search \"natural language\"  # semantic search across all sections\nlat expand \"user prompt text\"  # expand [[refs]] to resolved locations\nlat check                      # validate all links and code refs\n```\n\nRun `lat --help` when in doubt about available commands or options.\n\nIf `lat search` fails because no API key is configured, explain to the user that semantic search requires a key provided via `LAT_LLM_KEY` (direct value), `LAT_LLM_KEY_FILE` (path to key file), or `LAT_LLM_KEY_HELPER` (command that prints the key). Supported key prefixes: `sk-...` (OpenAI) or `vck_...` (Vercel). If the user doesn't want to set it up, use `lat locate` for direct lookups instead.\n\n# Syntax primer\n\n- **Section ids**: `lat.md/path/to/file#Heading#SubHeading` — full form uses project-root-relative path (e.g. `lat.md/tests/search#RAG Replay Tests`). Short form uses bare file name when unique (e.g. `search#RAG Replay Tests`, `cli#search#Indexing`).\n- **Wiki links**: `[[target]]` or `[[target|alias]]` — cross-references between sections. Can also reference source code: `[[src/foo.ts#myFunction]]`.\n- **Source code links**: Wiki links in `lat.md/` files can reference functions, classes, constants, and methods in TypeScript/JavaScript/Python/Rust/Go/C files. Use the full path: `[[src/config.ts#getConfigDir]]`, `[[src/server.ts#App#listen]]` (class method), `[[lib/utils.py#parse_args]]`, `[[src/lib.rs#Greeter#greet]]` (Rust impl method), `[[src/app.go#Greeter#Greet]]` (Go method), `[[src/app.h#Greeter]]` (C struct). `lat check` validates these exist.\n- **Code refs**: `// @lat: [[section-id]]` (JS/TS/Rust/Go/C) or `# @lat: [[section-id]]` (Python) — ties source code to concepts\n\n# Test specs\n\nKey tests can be described as sections in `lat.md/` files (e.g. `tests.md`). Add frontmatter to require that every leaf section is referenced by a `// @lat:` or `# @lat:` comment in test code:\n\n```markdown\n---\nlat:\n  require-code-mention: true\n---\n# Tests\n\nAuthentication and authorization test specifications.\n\n## User login\n\nVerify credential validation and error handling for the login endpoint.\n\n### Rejects expired tokens\nTokens past their expiry timestamp are rejected with 401, even if otherwise valid.\n\n### Handles missing password\nLogin request without a password field returns 400 with a descriptive error.\n```\n\nEvery section MUST have a description — at least one sentence explaining what the test verifies and why. Empty sections with just a heading are not acceptable. (This is a specific case of the general leading paragraph rule below.)\n\nEach test in code should reference its spec with exactly one comment placed next to the relevant test — not at the top of the file:\n\n```python\n# @lat: [[tests#User login#Rejects expired tokens]]\ndef test_rejects_expired_tokens():\n    ...\n\n# @lat: [[tests#User login#Handles missing password]]\ndef test_handles_missing_password():\n    ...\n```\n\nDo not duplicate refs. One `@lat:` comment per spec section, placed at the test that covers it. `lat check` will flag any spec section not covered by a code reference, and any code reference pointing to a nonexistent section.\n\n# Section structure\n\nEvery section in `lat.md/` **must** have a leading paragraph — at least one sentence immediately after the heading, before any child headings or other block content. The first paragraph must be ≤250 characters (excluding `[[wiki link]]` content). This paragraph serves as the section's overview and is used in search results, command output, and RAG context — keeping it concise guarantees the section's essence is always captured.\n\n```markdown\n# Good Section\n\nBrief overview of what this section documents and why it matters.\n\nMore detail can go in subsequent paragraphs, code blocks, or lists.\n\n## Child heading\n\nDetails about this child topic.\n```\n\n```markdown\n# Bad Section\n\n## Child heading\n\nDetails about this child topic.\n```\n\nThe second example is invalid because `Bad Section` has no leading paragraph. `lat check` validates this rule and reports errors for missing or overly long leading paragraphs.\n","isInternal":false,"tokens":1243,"sizeBytes":5330},{"name":"SKILL.md","path":".agents/skills/electron-pro/SKILL.md","rawUrl":"https://raw.githubusercontent.com/fathah/hermes-desktop/HEAD/.agents/skills/electron-pro/SKILL.md","title":"electron-pro","category":"anthropic-skill","format":"markdown","content":"---\nname: electron-pro\ndescription: Expert in building cross-platform desktop applications using web technologies (HTML/CSS/JS) with the Electron framework.\n---\n\n# Electron Desktop Developer\n\n## Purpose\n\nProvides cross-platform desktop application development expertise specializing in Electron, IPC architecture, and OS-level integration. Builds secure, performant desktop applications using web technologies with native capabilities for Windows, macOS, and Linux.\n\n## When to Use\n\n- Building cross-platform desktop apps (VS Code, Discord style)\n- Migrating web apps to desktop with native capabilities (File system, Notifications)\n- Implementing secure IPC (Main ↔ Renderer communication)\n- Optimizing Electron memory usage and startup time\n- Configuring auto-updaters (electron-updater)\n- Signing and notarizing apps for app stores\n\n---\n\n---\n\n## 2. Decision Framework\n\n### Architecture Selection\n\n```\nHow to structure the app?\n│\n├─ **Security First (Recommended)**\n│  ├─ Context Isolation? → **Yes** (Standard since v12)\n│  ├─ Node Integration? → **No** (Never in Renderer)\n│  └─ Preload Scripts? → **Yes** (Bridge API)\n│\n├─ **Data Persistence**\n│  ├─ Simple Settings? → **electron-store** (JSON)\n│  ├─ Large Datasets? → **SQLite** (`better-sqlite3` in Main process)\n│  └─ User Files? → **Native File System API**\n│\n└─ **UI Framework**\n   ├─ React/Vue/Svelte? → **Yes** (Standard SPA approach)\n   ├─ Multiple Windows? → **Window Manager Pattern**\n   └─ System Tray App? → **Hidden Window Pattern**\n```\n\n### IPC Communication Patterns\n\n| Pattern                        | Method               | Use Case                                        |\n| ------------------------------ | -------------------- | ----------------------------------------------- |\n| **One-Way (Renderer → Main)**  | `ipcRenderer.send`   | logging, analytics, minimizing window           |\n| **Two-Way (Request/Response)** | `ipcRenderer.invoke` | DB queries, file reads, heavy computations      |\n| **Main → Renderer**            | `webContents.send`   | Menu actions, system events, push notifications |\n\n**Red Flags → Escalate to `security-auditor`:**\n\n- Enabling `nodeIntegration: true` in production\n- Disabling `contextIsolation`\n- Loading remote content (`https://`) without strict CSP\n- Using `remote` module (Deprecated & insecure)\n\n---\n\n---\n\n### Workflow 2: Performance Optimization (Startup)\n\n**Goal:** Reduce launch time to < 2s.\n\n**Steps:**\n\n1.  **V8 Snapshot**\n    - Use `electron-link` or `v8-compile-cache` to pre-compile JS.\n\n2.  **Lazy Loading Modules**\n    - Don't `require()` everything at top of `main.ts`.\n\n    ```javascript\n    // Bad\n    import { heavyLib } from \"heavy-lib\";\n\n    // Good\n    ipcMain.handle(\"do-work\", () => {\n      const heavyLib = require(\"heavy-lib\");\n      heavyLib.process();\n    });\n    ```\n\n3.  **Bundle Main Process**\n    - Use `esbuild` or `webpack` for Main process (not just Renderer) to tree-shake unused code and minify.\n\n---\n\n---\n\n## 4. Patterns & Templates\n\n### Pattern 1: Worker Threads (CPU Intensive Tasks)\n\n**Use case:** Image processing or parsing large files without freezing the UI.\n\n```typescript\n// main.ts\nimport { Worker } from \"worker_threads\";\n\nipcMain.handle(\"process-image\", (event, data) => {\n  return new Promise((resolve, reject) => {\n    const worker = new Worker(\"./worker.js\", { workerData: data });\n    worker.on(\"message\", resolve);\n    worker.on(\"error\", reject);\n  });\n});\n```\n\n### Pattern 2: Deep Linking (Protocol Handler)\n\n**Use case:** Opening app from browser (`myapp://open?id=123`).\n\n```typescript\n// main.ts\nif (process.defaultApp) {\n  if (process.argv.length >= 2) {\n    app.setAsDefaultProtocolClient(\"myapp\", process.execPath, [\n      path.resolve(process.argv[1]),\n    ]);\n  }\n} else {\n  app.setAsDefaultProtocolClient(\"myapp\");\n}\n\napp.on(\"open-url\", (event, url) => {\n  event.preventDefault();\n  // Parse url 'myapp://...' and navigate renderer\n  mainWindow.webContents.send(\"navigate\", url);\n});\n```\n\n---\n\n---\n\n## 6. Integration Patterns\n\n### **frontend-ui-ux-engineer:**\n\n- **Handoff**: UI Dev builds the React/Vue app → Electron Dev wraps it.\n- **Collaboration**: Handling window controls (custom title bar), vibrancy/acrylic effects.\n- **Tools**: CSS `app-region: drag`.\n\n### **devops-engineer:**\n\n- **Handoff**: Electron Dev provides build config → DevOps sets up CI pipeline.\n- **Collaboration**: Code signing certificates (Apple Developer ID, Windows EV).\n- **Tools**: Electron Builder, Notarization scripts.\n\n### **security-engineer:**\n\n- **Handoff**: Electron Dev implements feature → Security Dev audits IPC surface.\n- **Collaboration**: Defining Content Security Policy (CSP) headers.\n- **Tools**: Electronegativity (Scanner).\n\n---\n","frontmatter":{"name":"electron-pro","description":"Expert in building cross-platform desktop applications using web technologies (HTML/CSS/JS) with the Electron framework."},"isInternal":false,"tokens":1124,"sizeBytes":4807},{"name":"SKILL.md","path":".agents/skills/hermes-agent/SKILL.md","rawUrl":"https://raw.githubusercontent.com/fathah/hermes-desktop/HEAD/.agents/skills/hermes-agent/SKILL.md","title":"hermes-agent","category":"anthropic-skill","format":"markdown","content":"---\nname: hermes-agent\ndescription: Expert in building self-improving AI agents with tool use, multi-platform messaging, and a closed learning loop. Proficient in LLM orchestration, tool integration, session management, and agent autonomy.\n---\n\n# Hermes Agent - Complete Project Guide (A-Z)\n\n> **Purpose of this document:** A single, comprehensive reference that explains everything about the Hermes Agent project — its architecture, source code, features, release history, and design patterns — so that any AI or developer can fully understand the system.\n\n---\n\n## Table of Contents\n\n1. [Project Overview](#1-project-overview)\n2. [Key Features Summary](#2-key-features-summary)\n3. [Installation & Getting Started](#3-installation--getting-started)\n4. [Project Structure](#4-project-structure)\n5. [Core Architecture](#5-core-architecture)\n   - 5.1 [AIAgent Class (run_agent.py)](#51-aiagent-class-run_agentpy)\n   - 5.2 [Tool Orchestration (model_tools.py)](#52-tool-orchestration-model_toolspy)\n   - 5.3 [Toolset System (toolsets.py)](#53-toolset-system-toolsetspy)\n   - 5.4 [Tool Registry (tools/registry.py)](#54-tool-registry-toolsregistrypy)\n   - 5.5 [Session Database (hermes_state.py)](#55-session-database-hermes_statepy)\n   - 5.6 [Constants & Home Directory (hermes_constants.py)](#56-constants--home-directory-hermes_constantspy)\n6. [CLI System](#6-cli-system)\n   - 6.1 [Interactive CLI (cli.py)](#61-interactive-cli-clipy)\n   - 6.2 [CLI Entry Point (hermes_cli/main.py)](#62-cli-entry-point-hermes_climainpy)\n   - 6.3 [Configuration System (hermes_cli/config.py)](#63-configuration-system-hermes_cliconfigpy)\n   - 6.4 [Slash Command Registry (hermes_cli/commands.py)](#64-slash-command-registry-hermes_clicommandspy)\n   - 6.5 [Setup Wizard (hermes_cli/setup.py)](#65-setup-wizard-hermes_clisetupy)\n   - 6.6 [Model Catalog (hermes_cli/models.py)](#66-model-catalog-hermes_climodelspy)\n   - 6.7 [Skin/Theme Engine (hermes_cli/skin_engine.py)](#67-skintheme-engine-hermes_cliskin_enginepy)\n7. [Tool System](#7-tool-system)\n   - 7.1 [Terminal Tool (tools/terminal_tool.py)](#71-terminal-tool-toolsterminal_toolpy)\n   - 7.2 [File Tools (tools/file_tools.py)](#72-file-tools-toolsfile_toolspy)\n   - 7.3 [Web Tools (tools/web_tools.py)](#73-web-tools-toolsweb_toolspy)\n   - 7.4 [Browser Tool (tools/browser_tool.py)](#74-browser-tool-toolsbrowser_toolpy)\n   - 7.5 [Delegate Tool (tools/delegate_tool.py)](#75-delegate-tool-toolsdelegate_toolpy)\n   - 7.6 [MCP Tool (tools/mcp_tool.py)](#76-mcp-tool-toolsmcp_toolpy)\n   - 7.7 [Approval System (tools/approval.py)](#77-approval-system-toolsapprovalpy)\n   - 7.8 [Terminal Backends (tools/environments/)](#78-terminal-backends-toolsenvironments)\n8. [Agent Internals](#8-agent-internals)\n   - 8.1 [Prompt Builder (agent/prompt_builder.py)](#81-prompt-builder-agentprompt_builderpy)\n   - 8.2 [Context Compressor (agent/context_compressor.py)](#82-context-compressor-agentcontext_compressorpy)\n   - 8.3 [Prompt Caching (agent/prompt_caching.py)](#83-prompt-caching-agentprompt_cachingpy)\n   - 8.4 [Auxiliary Client (agent/auxiliary_client.py)](#84-auxiliary-client-agentauxiliary_clientpy)\n   - 8.5 [Display & Spinner (agent/display.py)](#85-display--spinner-agentdisplaypy)\n   - 8.6 [Skill Commands (agent/skill_commands.py)](#86-skill-commands-agentskill_commandspy)\n9. [Messaging Gateway](#9-messaging-gateway)\n   - 9.1 [GatewayRunner (gateway/run.py)](#91-gatewayrunner-gatewayrunpy)\n   - 9.2 [Session Store (gateway/session.py)](#92-session-store-gatewaysessionpy)\n   - 9.3 [Platform Adapters (gateway/platforms/)](#93-platform-adapters-gatewayplatforms)\n10. [Cron Scheduling](#10-cron-scheduling)\n11. [Skills System](#11-skills-system)\n12. [Plugin System](#12-plugin-system)\n13. [Memory System](#13-memory-system)\n14. [ACP Server (IDE Integration)](#14-acp-server-ide-integration)\n15. [API Server](#15-api-server)\n16. [MCP Server Mode](#16-mcp-server-mode)\n17. [RL Training Environments](#17-rl-training-environments)\n18. [Profiles (Multi-Instance)](#18-profiles-multi-instance)\n19. [Security Model](#19-security-model)\n20. [Provider & Model System](#20-provider--model-system)\n21. [Streaming & Reasoning](#21-streaming--reasoning)\n22. [Release History](#22-release-history)\n23. [File Dependency Chain](#23-file-dependency-chain)\n24. [Key Design Patterns](#24-key-design-patterns)\n25. [Configuration Reference](#25-configuration-reference)\n26. [Known Pitfalls](#26-known-pitfalls)\n\n---\n\n## 1. Project Overview\n\n**Hermes Agent** is a self-improving AI agent built by [Nous Research](https://nousresearch.com). It is an open-source (MIT licensed), Python-based project that provides:\n\n- A **full interactive terminal UI** (CLI) for conversing with LLMs\n- A **messaging gateway** supporting 16+ platforms (Telegram, Discord, Slack, WhatsApp, Signal, Matrix, Email, etc.)\n- A **closed learning loop** — the agent creates skills from experience, improves them during use, nudges itself to persist knowledge, searches past conversations, and builds a deepening model of who you are\n- **40+ built-in tools** — terminal execution, file manipulation, web search, browser automation, code execution, image generation, TTS/STT, and more\n- **Any LLM provider** — OpenRouter (200+ models), Nous Portal (400+ models), OpenAI, Anthropic, Hugging Face, GitHub Copilot, z.ai/GLM, Kimi/Moonshot, MiniMax, Alibaba/DashScope, custom endpoints\n- **Six terminal backends** — local, Docker, SSH, Modal (serverless), Daytona (serverless), Singularity (HPC)\n- **Scheduled automations** via built-in cron scheduler\n- **IDE integration** via ACP (Agent Communication Protocol) for VS Code, Zed, JetBrains\n- **MCP integration** — both client (connect to any MCP server) and server (expose Hermes to MCP clients)\n- **RL training** via Atropos environments for training the next generation of tool-calling models\n\n**Tech Stack:**\n\n- Python 3.11+ (core agent, tools, gateway, cron)\n- Node.js (browser automation via agent-browser)\n- SQLite with WAL mode and FTS5 (session storage, full-text search)\n- OpenAI-compatible API (primary inference interface)\n- Anthropic SDK (native Anthropic support)\n- Rich + prompt_toolkit (CLI rendering)\n\n**Repository:** `github.com/NousResearch/hermes-agent`\n**Version:** 0.7.0 (as of April 2026)\n**License:** MIT\n\n---\n\n## 2. Key Features Summary\n\n| Feature                      | Description                                                                                                                                                                      |\n| ---------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| **Terminal UI**              | Full TUI with multiline editing, slash-command autocomplete, conversation history, interrupt-and-redirect, streaming tool output                                                 |\n| **Multi-Platform Messaging** | Telegram, Discord, Slack, WhatsApp, Signal, Matrix, Email, Home Assistant, DingTalk, Feishu/Lark, WeCom, Mattermost, SMS, Webhook — all from a single gateway process            |\n| **Learning Loop**            | Agent-curated memory with periodic nudges, autonomous skill creation, skills self-improve during use, FTS5 session search with LLM summarization, Honcho dialectic user modeling |\n| **Scheduled Tasks**          | Built-in cron scheduler with delivery to any platform (daily reports, nightly backups, weekly audits)                                                                            |\n| **Subagent Delegation**      | Spawn isolated subagents for parallel workstreams with restricted toolsets                                                                                                       |\n| **Execute Code**             | Python scripts that call tools via RPC, collapsing multi-step pipelines into zero-context-cost turns                                                                             |\n| **Terminal Backends**        | Local, Docker, SSH, Modal, Daytona, Singularity — run on a $5 VPS or a GPU cluster                                                                                               |\n| **Skills**                   | 70+ bundled skills across 28 categories, Skills Hub for community discovery, agentskills.io compatibility                                                                        |\n| **Plugins**                  | Drop-in Python plugins with lifecycle hooks (pre_llm_call, post_llm_call, on_session_start, on_session_end)                                                                      |\n| **MCP**                      | Client (connect to MCP servers for extended tools) and Server (expose conversations to MCP clients)                                                                              |\n| **IDE Integration**          | VS Code, Zed, JetBrains via ACP server with session management and tool streaming                                                                                                |\n| **API Server**               | OpenAI-compatible `/v1/chat/completions` endpoint for headless integrations                                                                                                      |\n| **Profiles**                 | Multi-instance support — each profile gets isolated config, memory, sessions, skills, gateway                                                                                    |\n| **Security**                 | Command approval system, secret redaction, SSRF protection, PII redaction, injection detection, credential directory protection                                                  |\n| **RL Training**              | Atropos environments for batch trajectory generation and agent policy optimization                                                                                               |\n\n---\n\n## 3. Installation & Getting Started\n\n```bash\n# One-line install (Linux, macOS, WSL2)\ncurl -fsSL https://raw.githubusercontent.com/NousResearch/hermes-agent/main/scripts/install.sh | bash\n\n# After install\nsource ~/.bashrc    # or: source ~/.zshrc\nhermes              # start chatting\n\n# Key commands\nhermes model        # Choose LLM provider and model\nhermes tools        # Configure which tools are enabled\nhermes config set   # Set individual config values\nhermes gateway      # Start the messaging gateway\nhermes setup        # Run the full setup wizard\nhermes update       # Update to latest version\nhermes doctor       # Diagnose any issues\n```\n\n**For development:**\n\n```bash\ngit clone https://github.com/NousResearch/hermes-agent.git\ncd hermes-agent\ncurl -LsSf https://astral.sh/uv/install.sh | sh\nuv venv venv --python 3.11\nsource venv/bin/activate\nuv pip install -e \".[all,dev]\"\npython -m pytest tests/ -q    # ~3000 tests\n```\n\n---\n\n## 4. Project Structure\n\n```\nhermes-agent/\n├── run_agent.py              # AIAgent class — core conversation loop\n├── model_tools.py            # Tool orchestration, _discover_tools(), handle_function_call()\n├── toolsets.py               # Toolset definitions, _HERMES_CORE_TOOLS list\n├── toolset_distributions.py  # Toolset sampling distributions for RL\n├── cli.py                    # HermesCLI class — interactive CLI orchestrator\n├── hermes_state.py           # SessionDB — SQLite session store (FTS5 search)\n├── hermes_constants.py       # Shared constants, get_hermes_home()\n├── hermes_time.py            # Timezone handling\n├── utils.py                  # Shared utility functions\n├── batch_runner.py           # Parallel batch processing\n├── trajectory_compressor.py  # Trajectory compression for RL training\n├── mcp_serve.py              # MCP server mode entry point\n├── mini_swe_runner.py        # Minimal SWE benchmark runner\n├── rl_cli.py                 # RL CLI commands\n│\n├── agent/                    # Agent internals\n│   ├── prompt_builder.py         # System prompt assembly\n│   ├── context_compressor.py     # Auto context compression\n│   ├── prompt_caching.py         # Anthropic prompt caching\n│   ├── auxiliary_client.py       # Auxiliary LLM client (vision, summarization)\n│   ├── model_metadata.py         # Model context lengths, token estimation\n│   ├── models_dev.py             # models.dev registry integration\n│   ├── display.py                # KawaiiSpinner, tool preview formatting\n│   ├── skill_commands.py         # Skill slash commands (shared CLI/gateway)\n│   └── trajectory.py             # Trajectory saving helpers\n│\n├── hermes_cli/               # CLI subcommands and setup\n│   ├── main.py               # Entry point — all `hermes` subcommands\n│   ├── config.py             # DEFAULT_CONFIG, OPTIONAL_ENV_VARS, migration\n│   ├── commands.py           # Slash command definitions + SlashCommandCompleter\n│   ├── callbacks.py          # Terminal callbacks (clarify, sudo, approval)\n│   ├── setup.py              # Interactive setup wizard\n│   ├── skin_engine.py        # Skin/theme engine\n│   ├── skills_config.py      # `hermes skills` — skill management\n│   ├── tools_config.py       # `hermes tools` — tool management\n│   ├── skills_hub.py         # Skills Hub integration\n│   ├── models.py             # Model catalog, provider model lists\n│   ├── model_switch.py       # Shared /model switch pipeline\n│   └── auth.py               # Provider credential resolution\n│\n├── tools/                    # Tool implementations (one file per tool)\n│   ├── registry.py           # Central tool registry\n│   ├── approval.py           # Dangerous command detection\n│   ├── terminal_tool.py      # Terminal/shell execution\n│   ├── process_registry.py   # Background process management\n│   ├── file_tools.py         # File read/write/search/patch\n│   ├── web_tools.py          # Web search/extract\n│   ├── browser_tool.py       # Browser automation\n│   ├── code_execution_tool.py # execute_code sandbox\n│   ├── delegate_tool.py      # Subagent delegation\n│   ├── mcp_tool.py           # MCP client integration\n│   ├── skills_tool.py        # Skill management tool\n│   ├── todo_tool.py          # Todo/task tracking tool\n│   ├── memory_tool.py        # Memory read/write tool\n│   ├── tts_tool.py           # Text-to-speech\n│   ├── vision_tool.py        # Image analysis\n│   ├── image_gen_tool.py     # Image generation\n│   └── environments/         # Terminal backends\n│       ├── base.py               # BaseEnvironment ABC\n│       ├── local.py              # Local execution\n│       ├── docker.py             # Docker containers\n│       ├── ssh.py                # SSH remote execution\n│       ├── modal.py              # Modal serverless\n│       ├── managed_modal.py      # Nous-hosted Modal\n│       ├── daytona.py            # Daytona serverless\n│       ├── singularity.py        # Singularity HPC containers\n│       └── persistent_shell.py   # Persistent shell mixin\n│\n├── gateway/                  # Messaging platform gateway\n│   ├── run.py                # GatewayRunner — main message loop\n│   ├── session.py            # SessionStore — conversation persistence\n│   ├── status.py             # Gateway status, token locks\n│   └── platforms/            # 16 platform adapters\n│       ├── base.py               # BasePlatformAdapter ABC\n│       ├── telegram.py           # Telegram (polling + webhook)\n│       ├── discord.py            # Discord\n│       ├── slack.py              # Slack\n│       ├── whatsapp.py           # WhatsApp\n│       ├── matrix.py             # Matrix (E2EE)\n│       ├── signal.py             # Signal\n│       ├── email.py              # Email (IMAP/SMTP)\n│       ├── homeassistant.py      # Home Assistant\n│       ├── sms.py                # SMS (Twilio)\n│       ├── mattermost.py         # Mattermost\n│       ├── dingtalk.py           # DingTalk\n│       ├── feishu.py             # Feishu/Lark\n│       ├── wecom.py              # WeCom (Enterprise WeChat)\n│       ├── webhook.py            # Generic webhook\n│       └── api_server.py         # OpenAI-compatible API server\n│\n├── acp_adapter/              # ACP server (IDE integration)\n│   ├── server.py             # HermesACPAgent class\n│   ├── session.py            # SessionManager\n│   ├── events.py             # Streaming callbacks\n│   ├── permissions.py        # Approval callbacks\n│   └── entry.py              # Entry point\n│\n├── cron/                     # Scheduler\n│   ├── scheduler.py          # tick() — job execution engine\n│   └── jobs.py               # Job storage and CRUD\n│\n├── plugins/                  # Plugin system\n│   └── memory/               # 8 memory provider plugins\n│       ├── openviking/\n│       ├── mem0/\n│       ├── hindsight/\n│       ├── holographic/\n│       ├── honcho/\n│       ├── retaindb/\n│       └── byterover/\n│\n├── environments/             # RL training environments (Atropos)\n│   ├── hermes_base_env.py    # Abstract base RL environment\n│   ├── agent_loop.py         # HermesAgentLoop — rollout execution\n│   ├── tool_context.py       # ToolContext — sandbox for RL\n│   ├── web_research_env.py   # Web research tasks\n│   └── agentic_opd_env.py    # Observation-Prediction-Demo env\n│\n├── skills/                   # 70+ bundled skills across 28 categories\n├── optional-skills/          # Additional optional skills\n├── tests/                    # ~3000 pytest tests\n├── scripts/                  # Install, update, packaging scripts\n├── docker/                   # Docker build files\n├── docs/                     # Documentation source (Docusaurus)\n├── website/                  # Landing page\n├── desktop/                  # Desktop app (Electron, separate repo)\n├── tinker-atropos/           # RL submodule\n│\n├── pyproject.toml            # Python package config\n├── AGENTS.md                 # Developer guide for AI assistants\n├── RELEASE_v0.2.0.md → v0.7.0.md  # Release notes\n└── cli-config.yaml.example   # Example config\n```\n\n**User config directory:** `~/.hermes/`\n\n```\n~/.hermes/\n├── config.yaml           # User settings\n├── .env                  # API keys and secrets\n├── MEMORY.md             # Persistent agent memory\n├── USER.md               # User profile\n├── SOUL.md               # Agent personality/identity\n├── sessions.db           # SQLite session database\n├── skills/               # User-installed skills\n├── skins/                # Custom CLI themes\n├── plugins/              # User plugins\n├── cron/                 # Cron jobs and output\n│   ├── jobs.json\n│   └── output/\n├── cache/                # Image/audio cache\n├── plans/                # Generated plans\n├── profiles/             # Multi-instance profiles\n└── mcp/                  # MCP server configs\n```\n\n---\n\n## 5. Core Architecture\n\n### 5.1 AIAgent Class (run_agent.py)\n\nThe `AIAgent` class is the heart of the system — the core conversation loop that orchestrates LLM calls, tool execution, context management, and response delivery.\n\n**Constructor (~60 parameters):**\n\n```python\nclass AIAgent:\n    def __init__(self,\n        model: str = \"anthropic/claude-opus-4.6\",\n        max_iterations: int = 90,\n        enabled_toolsets: list = None,\n        disabled_toolsets: list = None,\n        quiet_mode: bool = False,\n        save_trajectories: bool = False,\n        platform: str = None,            # \"cli\", \"telegram\", etc.\n        session_id: str = None,\n        session_db: SessionDB = None,\n        skip_context_files: bool = False,\n        skip_memory: bool = False,\n        base_url: str = None,\n        api_key: str = None,\n        provider: str = None,\n        api_mode: str = \"chat_completions\",  # or \"anthropic_messages\" or \"codex_responses\"\n        tool_progress_callback = None,\n        stream_delta_callback = None,\n        thinking_callback = None,\n        status_callback = None,\n        iteration_budget: IterationBudget = None,\n        credential_pool = None,\n        checkpoints_enabled: bool = False,\n        # ... plus provider, routing, callback params\n    )\n```\n\n**Main Methods:**\n\n| Method                                                                          | Returns  | Purpose                                                                            |\n| ------------------------------------------------------------------------------- | -------- | ---------------------------------------------------------------------------------- |\n| `chat(message, stream_callback)`                                                | `str`    | Simple interface — returns final response text                                     |\n| `run_conversation(user_message, system_message, conversation_history, task_id)` | `dict`   | Full interface — returns `{final_response, messages, completed, api_calls, error}` |\n| `_interruptible_api_call(api_kwargs)`                                           | Response | Runs API request in background thread with interrupt support                       |\n| `_interruptible_streaming_api_call(api_kwargs, on_first_delta)`                 | Response | Streaming variant with delta callbacks                                             |\n\n**The Core Agent Loop** (inside `run_conversation()`):\n\n```python\nwhile api_call_count < self.max_iterations and self.iteration_budget.remaining > 0:\n    response = client.chat.completions.create(\n        model=model, messages=messages, tools=tool_schemas\n    )\n    if response.tool_calls:\n        for tool_call in response.tool_calls:\n            result = handle_function_call(tool_call.name, tool_call.args, task_id)\n            messages.append(tool_result_message(result))\n        api_call_count += 1\n    else:\n        return response.content  # Final text response\n```\n\n**Key Behaviors:**\n\n- **Three API modes:** `chat_completions` (OpenAI-compatible), `anthropic_messages` (Anthropic SDK), `codex_responses` (OpenAI Codex)\n- **Parallel tool execution:** Independent tool calls run concurrently via ThreadPoolExecutor (unless they share file paths or are in the never-parallel list)\n- **Interrupt support:** Background threads allow interrupt detection without blocking on HTTP\n- **Error recovery:** Automatic fallback chain (primary → fallback model), retry with exponential backoff, context compression on token overflow\n- **Budget pressure:** Warnings at 70% (caution) and 90% (urgent) of iteration budget\n- **Oversized results:** Tool results >100K chars are saved to temp files with a preview\n- **Stale connection detection:** 90s timeout for streaming, 60s read timeout\n\n**IterationBudget** (thread-safe):\n\n```python\nclass IterationBudget:\n    def consume() -> bool    # Check and consume one iteration\n    def refund()             # Give back iteration (for execute_code turns)\n    @property remaining      # Remaining iterations\n```\n\n---\n\n### 5.2 Tool Orchestration (model_tools.py)\n\nBridges the agent and tool registry — handles discovery, schema generation, and dispatch.\n\n**Key Functions:**\n\n| Function                                                                                | Purpose                                                                   |\n| --------------------------------------------------------------------------------------- | ------------------------------------------------------------------------- |\n| `_discover_tools()`                                                                     | Imports all tool modules (each calls `registry.register()` on import)     |\n| `get_tool_definitions(enabled_toolsets, disabled_toolsets, quiet_mode)`                 | Returns OpenAI-format tool schemas filtered by toolset                    |\n| `handle_function_call(function_name, function_args, task_id, user_task, enabled_tools)` | Main dispatcher — routes calls to registry with arg coercion              |\n| `coerce_tool_args(tool_name, args)`                                                     | Type coercion for LLM-generated arguments (string→int, string→bool, etc.) |\n\n**Tool Discovery Order:**\n\n1. Static tools (web_tools, terminal_tool, file_tools, browser_tool, etc.)\n2. Optional tools (fal_client for image gen, honcho, etc.) — graceful fallback if missing\n3. Plugin-registered tools\n4. MCP server tools (dynamic, via tools/list_changed notifications)\n\n**Special Tool Handling:**\n\n- **Agent-level tools** (todo, memory, session_search, delegate_task): Intercepted by `run_agent.py` before `handle_function_call()`\n- **execute_code**: Passes `enabled_tools` for sandbox tool list\n- **Dynamic schema adjustments**: `browser_navigate` strips web_search reference if tools unavailable\n\n**Async Bridging:**\n\n- Persistent event loops (not `asyncio.run()`) to prevent \"Event loop is closed\" errors\n- Main thread uses shared loop; worker threads get per-thread loops\n- `_run_async()` detects running loop and spins up disposable thread if needed\n\n---\n\n### 5.3 Toolset System (toolsets.py)\n\nProvides flexible tool grouping and composition.\n\n**Core Toolsets:**\n\n| Toolset          | Tools Included                                                                                   |\n| ---------------- | ------------------------------------------------------------------------------------------------ |\n| `web`            | web_search, web_extract, web_crawl                                                               |\n| `terminal`       | terminal                                                                                         |\n| `file`           | read_file, write_file, edit_file, list_files, search_files                                       |\n| `browser`        | browser_navigate, browser_snapshot, browser_click, browser_type, browser_scroll, browser_extract |\n| `vision`         | analyze_image                                                                                    |\n| `image_gen`      | generate_image                                                                                   |\n| `tts`            | text_to_speech                                                                                   |\n| `todo`           | todo_read, todo_write                                                                            |\n| `memory`         | memory_read, memory_write                                                                        |\n| `session_search` | session_search                                                                                   |\n| `delegation`     | delegate_task                                                                                    |\n| `code_execution` | execute_code                                                                                     |\n| `cronjob`        | create_job, list_jobs, delete_job                                                                |\n| `messaging`      | send_message                                                                                     |\n| `homeassistant`  | ha_get_states, ha_call_service, ...                                                              |\n\n**Composite Toolsets:**\n\n- `hermes-cli` — All core tools for CLI platform\n- `hermes-telegram`, `hermes-discord`, etc. — Platform-specific tool sets\n- `hermes-gateway` — Union of all platform tools\n- `debugging` — terminal + file + web\n- `safe` — Everything except terminal\n\n**Resolution:**\n\n```python\nresolve_toolset(name, visited=None) → List[str]\n# Recursively resolves toolset to tool names\n# Handles composition (includes) and cycle detection\n# Special aliases: \"all\" or \"*\" = all tools\n```\n\n---\n\n### 5.4 Tool Registry (tools/registry.py)\n\nSingleton managing all tool schemas and handlers. Circular-import safe — has no tool dependencies.\n\n**ToolEntry** (per-tool metadata):\n\n```python\n@dataclass(slots=True)\nclass ToolEntry:\n    name: str\n    toolset: str\n    schema: dict           # OpenAI-format tool definition\n    handler: Callable      # Sync or async handler function\n    check_fn: Callable     # Returns True if tool is available\n    requires_env: list     # Required environment variables\n    is_async: bool\n    description: str\n    emoji: str\n```\n\n**Key Methods:**\n\n```python\nregistry.register(name, toolset, schema, handler, check_fn, requires_env)\nregistry.get_definitions(tool_names, quiet)  # Returns filtered schemas\nregistry.dispatch(name, args, **kwargs)      # Execute with async bridging\nregistry.deregister(name)                    # Remove (for MCP tool refresh)\nregistry.check_tool_availability()           # Returns (available, unavailable)\n```\n\n---\n\n### 5.5 Session Database (hermes_state.py)\n\nSQLite-based persistent session storage with FTS5 full-text search.\n\n**Schema (v6):**\n\n```sql\n-- Sessions table\nsessions (\n    id TEXT PRIMARY KEY,\n    source TEXT, user_id TEXT, model TEXT, model_config TEXT,\n    system_prompt TEXT, parent_session_id TEXT,\n    started_at TEXT, ended_at TEXT, end_reason TEXT,\n    message_count INTEGER, tool_call_count INTEGER,\n    input_tokens INTEGER, output_tokens INTEGER,\n    cache_read_tokens INTEGER, cache_write_tokens INTEGER, reasoning_tokens INTEGER,\n    estimated_cost_usd REAL, actual_cost_usd REAL,\n    title TEXT  -- UNIQUE INDEX\n)\n\n-- Messages table\nmessages (\n    id INTEGER PRIMARY KEY AUTOINCREMENT,\n    session_id TEXT, role TEXT, content TEXT,\n    tool_call_id TEXT, tool_calls TEXT,  -- JSON\n    tool_name TEXT, timestamp TEXT,\n    token_count INTEGER, finish_reason TEXT,\n    reasoning TEXT, reasoning_details TEXT, codex_reasoning_items TEXT\n)\n\n-- FTS5 virtual table (auto-synced via triggers)\nmessages_fts (content)\n```\n\n**Concurrency Model:**\n\n- WAL (Write-Ahead Logging) for concurrent readers + single writer\n- `BEGIN IMMEDIATE` for write transactions (lock at start, not commit)\n- Jitter retry on lock: 20-150ms random backoff, max 15 retries\n- Periodic WAL checkpoint every 50 writes\n\n**Key Operations:**\n\n- `create_session()`, `end_session()`, `reopen_session()`\n- `add_message()`, `get_messages()`\n- `search_sessions(query)` — FTS5 full-text search\n- `update_token_counts()` — Supports both incremental (CLI) and absolute (gateway) modes\n\n---\n\n### 5.6 Constants & Home Directory (hermes_constants.py)\n\nImport-safe constants module with no circular dependencies.\n\n```python\nget_hermes_home() → Path          # HERMES_HOME env var or ~/.hermes\ndisplay_hermes_home() → str       # User-friendly display: \"~/.hermes\"\nget_optional_skills_dir() → Path  # HERMES_OPTIONAL_SKILLS env var\nparse_reasoning_effort(str) → Dict  # \"high\" → {\"enabled\": True, \"effort\": \"high\"}\n\n# Key constants\nOPENROUTER_BASE_URL = \"https://openrouter.ai/api/v1\"\nNOUS_API_BASE_URL = \"https://inference-api.nousresearch.com/v1\"\nAI_GATEWAY_BASE_URL = \"https://ai-gateway.vercel.sh/v1\"\nVALID_REASONING_EFFORTS = (\"xhigh\", \"high\", \"medium\", \"low\", \"minimal\")\n```\n\n---\n\n## 6. CLI System\n\n### 6.1 Interactive CLI (cli.py)\n\nThe `HermesCLI` class provides the interactive terminal interface.\n\n**Features:**\n\n- **Rich** for banner/panels, **prompt_toolkit** for input with autocomplete\n- **KawaiiSpinner** — animated faces during API calls, `┊` activity feed for tool results\n- Multiline editing with Shift+Enter\n- Slash-command autocomplete\n- Session history with up/down arrow navigation\n- Clipboard image paste (Alt+V / Ctrl+V)\n- Status bar showing model, provider, and token counts\n- Inline diff previews for file write/patch operations\n\n**Configuration Loading:**\n\n```python\nload_cli_config() → dict\n# Loads from ~/.hermes/config.yaml (or ./cli-config.yaml fallback)\n# Merges with hardcoded defaults\n# Expands ${ENV_VAR} references\n# Maps terminal config → env vars\n```\n\n---\n\n### 6.2 CLI Entry Point (hermes_cli/main.py)\n\nAll `hermes` subcommands are dispatched from here:\n\n```\nhermes                    # Default: interactive chat\nhermes chat               # Explicit interactive mode\nhermes gateway start|stop|status|install|uninstall\nhermes setup              # Setup wizard\nhermes model              # Select model/provider\nhermes tools              # Configure tools\nhermes skills             # Manage skills\nhermes config set|get     # Direct config manipulation\nhermes cron list|delete   # Cron job management\nhermes doctor             # Diagnose issues\nhermes sessions browse    # Session picker\nhermes profile create|list|switch|delete|export|import\nhermes mcp serve|add|remove  # MCP management\nhermes acp                # Start ACP server\nhermes update|uninstall|version\n```\n\n**Profile System:**\n\n- `_apply_profile_override()` runs BEFORE any imports to set `HERMES_HOME`\n- Pre-parses `--profile/-p` from argv\n- Allows fully isolated agent instances with separate config, memory, sessions, skills\n\n---\n\n### 6.3 Configuration System (hermes_cli/config.py)\n\n**Key Configuration Sections:**\n\n```yaml\nmodel: \"anthropic/claude-opus-4.6\" # or dict with provider/base_url/api_key\nproviders: {} # Provider-specific configs\nfallback_providers: [] # Ordered failover list\ncredential_pool: {} # Multiple API keys per provider\n\nagent:\n  max_turns: 90\n  gateway_timeout: 1800\n  tool_use_enforcement: \"auto\"\n\nterminal:\n  backend: \"local\" # local|docker|modal|daytona|ssh|singularity\n  timeout: 180\n  persistent_shell: true\n  docker_image: \"nikolaik/python-nodejs:...\"\n\ncompression:\n  enabled: true\n  threshold: 0.50 # Compress when 50% of context used\n  target_ratio: 0.20 # Summary = 20% of compressed content\n  protect_last_n: 20\n\nauxiliary:\n  vision: { provider, model }\n  web_extract: { provider, model }\n  compression: { provider, model }\n\nmemory:\n  memory_enabled: true\n  provider: \"\" # \"\" | \"honcho\" | \"mem0\" | etc.\n  memory_char_limit: 2200\n\ndisplay:\n  personality: \"kawaii\"\n  show_reasoning: false\n  inline_diffs: true\n  skin: \"default\"\n  streaming: true\n\ntts:\n  provider: \"edge\" # edge|elevenlabs|openai|neutts\n\nstt:\n  enabled: true\n  provider: \"local\" # local|groq|openai\n\nprivacy:\n  redact_pii: false\n\nmcp_servers: {} # MCP server configurations\n\nskills:\n  external_dirs: [] # Additional skill directories\n\napprovals:\n  mode: \"smart\" # smart|always|off\n```\n\n**Config Files:**\n\n- `~/.hermes/config.yaml` — User settings (authoritative)\n- `~/.hermes/.env` — API keys and secrets\n- Config version migration system (currently v5)\n\n---\n\n### 6.4 Slash Command Registry (hermes_cli/commands.py)\n\nAll slash commands defined centrally in `COMMAND_REGISTRY`:\n\n```python\nCommandDef(name, description, category, aliases, args_hint, cli_only, gateway_only)\n```\n\n**Derived automatically by:**\n\n- CLI `process_command()` — dispatch on canonical name\n- Gateway dispatch + help\n- Telegram BotCommand menu\n- Slack `/hermes` subcommands\n- Autocomplete + help text\n\n**Key Commands:**\n\n| Command        | Aliases    | Description                   |\n| -------------- | ---------- | ----------------------------- |\n| `/new`         | `/reset`   | Start fresh conversation      |\n| `/model`       |            | Show/switch model             |\n| `/personality` |            | Set agent personality         |\n| `/retry`       |            | Retry last turn               |\n| `/undo`        |            | Remove last turn              |\n| `/compress`    | `/compact` | Compress context              |\n| `/usage`       | `/cost`    | Show token usage              |\n| `/insights`    |            | Usage analytics               |\n| `/skills`      |            | Browse/install skills         |\n| `/background`  | `/bg`      | Manage background processes   |\n| `/plan`        |            | Generate implementation plan  |\n| `/rollback`    |            | Restore filesystem checkpoint |\n| `/verbose`     |            | Toggle debug output           |\n| `/reasoning`   |            | Set reasoning effort          |\n| `/yolo`        |            | Toggle approval bypass        |\n| `/btw`         |            | Ephemeral side question       |\n| `/stop`        |            | Kill current agent run        |\n| `/queue`       |            | Queue next prompt             |\n| `/browser`     |            | Interactive browser session   |\n| `/history`     | `/resume`  | Session browser               |\n| `/skin`        |            | Switch CLI theme              |\n\n---\n\n### 6.5 Setup Wizard (hermes_cli/setup.py)\n\nModular interactive wizard with independent sections:\n\n1. **Model & Provider** — Select AI provider, enter API keys, choose model\n2. **Terminal Backend** — Choose execution environment\n3. **Agent Settings** — Max iterations, compression, session policies\n4. **Messaging Platforms** — Configure Telegram, Discord, Slack, etc.\n5. **Tools** — TTS, STT, web search, image generation, browser\n\nFeatures:\n\n- Live credential validation\n- Real-time model list fetching from provider APIs\n- Automatic OpenClaw migration detection\n- Atomic config file writes\n\n---\n\n### 6.6 Model Catalog (hermes_cli/models.py)\n\nProvider-specific model lists:\n\n```python\n_PROVIDER_MODELS = {\n    \"nous\": [\"anthropic/claude-opus-4.6\", \"anthropic/claude-sonnet-4.6\", ...],  # 25+\n    \"openrouter\": [\"anthropic/claude-opus-4.6\", \"google/gemini-3-flash\", ...],  # 30+\n    \"anthropic\": [\"claude-opus-4-6\", \"claude-sonnet-4-6\", ...],\n    \"openai\": [\"gpt-5\", \"gpt-5.4-mini\", \"gpt-4.1\", \"gpt-4o\", ...],\n    \"copilot\": [\"gpt-5.4\", \"gpt-5.4-mini\", \"gpt-5.3-codex\", ...],\n    \"huggingface\": [...],\n    \"minimax\": [...],\n    \"kimi-coding\": [...],\n    \"alibaba\": [...],\n    \"deepseek\": [...],\n    # ... more providers\n}\n```\n\nFeatures:\n\n- Dynamic fetching via provider `/models` endpoints\n- Curated lists used when live probe returns fewer models\n- Fuzzy matching for typo correction\n- Validation against provider catalog\n\n---\n\n### 6.7 Skin/Theme Engine (hermes_cli/skin_engine.py)\n\nData-driven CLI visual customization — no code changes needed.\n\n**Customizable Elements:**\n\n| Element                          | Key                      | Used By           |\n| -------------------------------- | ------------------------ | ----------------- |\n| Banner border/title/accent       | `colors.*`               | banner.py         |\n| Response box border              | `colors.response_border` | cli.py            |\n| Spinner faces (waiting/thinking) | `spinner.*`              | display.py        |\n| Spinner verbs/wings              | `spinner.*`              | display.py        |\n| Tool output prefix               | `tool_prefix`            | display.py        |\n| Per-tool emojis                  | `tool_emojis`            | display.py        |\n| Agent name/welcome/prompt        | `branding.*`             | banner.py, cli.py |\n\n**Built-in Skins:** default, ares, mono, slate, poseidon, sisyphus, charizard\n\n**User Skins:** Drop `~/.hermes/skins/<name>.yaml` and activate with `/skin <name>`\n\n---\n\n## 7. Tool System\n\n### 7.1 Terminal Tool (tools/terminal_tool.py)\n\nShell command execution across multiple backends.\n\n```python\ndef terminal_tool(\n    command: str,\n    background: bool = False,\n    timeout: Optional[int] = None,\n    task_id: Optional[str] = None,\n    force: bool = False,        # Skip approval for dangerous commands\n    workdir: Optional[str] = None,\n    check_interval: Optional[int] = None,  # Background task polling\n    pty: bool = False,\n) -> str  # JSON result\n```\n\n**Features:**\n\n- Multi-backend: Selects based on `TERMINAL_ENV` (local/docker/ssh/modal/daytona/singularity)\n- Per-task_id sandboxes with thread-safe creation locks\n- Dangerous command routing through approval system\n- Background task support with file-based IPC\n- Interrupt handling — polls `is_interrupted()` during execution\n- Auto-cleanup daemon thread for idle environments (>300s)\n- Disk usage warnings at configurable threshold\n\n---\n\n### 7.2 File Tools (tools/file_tools.py)\n\nSafe file operations with size guards and sensitive path protection.\n\n- `read_file_tool(path, offset, limit)` — Read with pagination (default 100K char limit)\n- `write_file_tool(path, content)` — Write with approval for sensitive paths\n- `edit_file_tool(path, old_text, new_text)` — String replacement editing\n- `list_files_tool(path)` — Directory listing\n- `search_files_tool(pattern, path)` — Glob/regex file search\n\n**Safety:**\n\n- Device path blocklist (`/dev/zero`, `/dev/stdin`, etc.)\n- Read dedup tracking — returns stub on re-read if mtime unchanged\n- Sensitive path blocking: `/etc/`, `/boot/`, `~/.ssh` without approval\n- Prompt injection protection for known dangerous paths\n\n---\n\n### 7.3 Web Tools (tools/web_tools.py)\n\nWeb search and content extraction.\n\n- `web_search_tool(query, limit)` — Search via configurable backend\n- `web_extract_tool(urls, format)` — Extract content from URLs\n- `web_crawl_tool(url, instructions)` — LLM-guided web crawling\n\n**Backends:** Firecrawl, Parallel Web, Tavily, Exa, DuckDuckGo\n\n- Fallback detection by highest-priority available API key\n- LLM processing via auxiliary client (Gemini 3 Flash) for intelligent extraction\n- SSRF protection and URL safety checks\n\n---\n\n### 7.4 Browser Tool (tools/browser_tool.py)\n\nHeadless browser automation with accessibility tree.\n\n```python\nbrowser_navigate(url, task_id) → str\nbrowser_snapshot(task_id, max_chars) → str\nbrowser_click(ref, task_id) → str      # Element refs like @e1, @e2\nbrowser_type(ref, text, task_id) → str\nbrowser_scroll(ref, direction, task_id) → str\nbrowser_extract(task, max_chars, task_id) → str\n```\n\n**Backends:**\n\n- **Local:** Headless Chromium via `agent-browser` CLI\n- **Cloud:** Browserbase (stealth, proxies, CAPTCHA solving)\n- **Camofox:** Anti-detection browser using Camoufox\n\n**Features:**\n\n- Accessibility tree for text-based snapshots (no vision required)\n- Session isolation per task_id with inactivity cleanup\n- API key detection in URLs (prevents exfiltration)\n- SSRF protection for private/internal addresses\n\n---\n\n### 7.5 Delegate Tool (tools/delegate_tool.py)\n\nSpawn isolated subagents for parallel workstreams.\n\n```python\ndef delegate_task(\n    goal: str,\n    context: str = None,\n    toolsets: List[str] = None,       # Default: [\"terminal\", \"file\", \"web\"]\n    tasks: List[Dict] = None,          # Batch mode: up to 3 concurrent\n    max_iterations: int = 50,\n    parent_agent = None,\n) → str\n```\n\n**Isolation:**\n\n- Fresh conversation (no parent history)\n- Own task_id (separate terminal session, file ops)\n- Restricted toolset (configurable, blocked tools always stripped)\n- Focused system prompt from goal + context\n- Parent only sees delegation call and summary result\n\n**Blocked Tools:** delegate_task, clarify, memory, send_message, execute_code (no recursion, no user interaction)\n\n**Constraints:** MAX_DEPTH=2, MAX_CONCURRENT_CHILDREN=3\n\n---\n\n### 7.6 MCP Tool (tools/mcp_tool.py)\n\nModel Context Protocol client integration.\n\n**Configuration (config.yaml):**\n\n```yaml\nmcp_servers:\n  filesystem:\n    command: \"npx\"\n    args: [\"-y\", \"@modelcontextprotocol/server-filesystem\", \"/tmp\"]\n    timeout: 120\n  remote_api:\n    url: \"https://my-mcp-server.example.com/mcp\"\n    headers:\n      Authorization: \"Bearer sk-...\"\n    sampling:\n      enabled: true\n      model: \"gemini-3-flash\"\n```\n\n**Features:**\n\n- **Transports:** Stdio (local processes) and HTTP/StreamableHTTP (remote)\n- **Dynamic tool discovery:** Listens for `tools/list_changed` notifications\n- **Sampling support:** MCP servers can request LLM completions\n- **Automatic reconnection** with exponential backoff (5 retries)\n- Dedicated background event loop in daemon thread\n- Thread-safe via lock protecting server dict\n\n---\n\n### 7.7 Approval System (tools/approval.py)\n\nDangerous command detection and approval flow.\n\n```python\ndetect_dangerous_command(command: str) → (is_dangerous, pattern_key, description)\n```\n\n**106 patterns covering:**\n\n- Destructive operations (`rm -r /`, `mkfs`, `dd if=`)\n- Privilege escalation (`chmod 777`, `chown -R root`)\n- SQL injection (`DROP TABLE`, `DELETE FROM` without WHERE)\n- System targeting (`/etc/` writes, `systemctl stop`, fork bombs)\n- Shell injection (pipe to sh, wget to sh, process substitution)\n- Network exfiltration (curl with embedded API keys)\n- Secret access (`cat ~/.env`, `cat ~/.netrc`)\n\n**Normalization:** Strips ANSI escapes, null bytes, normalizes Unicode\n\n**Approval Modes:**\n\n- `smart` — Learns which commands are safe based on user decisions\n- `always` — Always ask for dangerous commands\n- `off` — Never ask (or `/yolo` toggle)\n\n---\n\n### 7.8 Terminal Backends (tools/environments/)\n\n| Backend           | File               | Features                                                                       |\n| ----------------- | ------------------ | ------------------------------------------------------------------------------ |\n| **Local**         | `local.py`         | Direct execution, interrupt support, non-blocking I/O, persistent shell        |\n| **Docker**        | `docker.py`        | Sandboxed containers, cap-drop ALL, no-new-privileges, PID limits, bind mounts |\n| **SSH**           | `ssh.py`           | Remote execution via ControlMaster, persistent shell mixin                     |\n| **Modal**         | `modal.py`         | Native Modal SDK `Sandbox.create()`/`Sandbox.exec()`, persistent snapshots     |\n| **Managed Modal** | `managed_modal.py` | Modal through Nous-hosted gateway                                              |\n| **Daytona**       | `daytona.py`       | Daytona SDK cloud sandboxes, stop/resume lifecycle                             |\n| **Singularity**   | `singularity.py`   | Singularity containers with scratch dir, SIF cache                             |\n\n**Common Interface (BaseEnvironment ABC):**\n\n```python\nexecute(command, timeout) → {\"output\": str, \"returncode\": int}\ncleanup() → None\n```\n\n---\n\n## 8. Agent Internals\n\n### 8.1 Prompt Builder (agent/prompt_builder.py)\n\nAssembles the system prompt from multiple sources:\n\n| Component               | Source                                               |\n| ----------------------- | ---------------------------------------------------- |\n| Agent Identity          | `DEFAULT_AGENT_IDENTITY` or `SOUL.md`                |\n| Memory Guidance         | When/how to use memory tool                          |\n| Session Search Guidance | How to recall past conversations                     |\n| Skills Guidance         | When to create/patch skills                          |\n| Tool Use Enforcement    | Must execute tools, not describe actions             |\n| Skills Index            | `~/.hermes/skills/.hermes-skills.json`               |\n| Platform Hints          | OS, Python version, shell, available tools           |\n| Context Files           | `.hermes.md`, `AGENTS.md`, `.cursorrules`, `SOUL.md` |\n| Model/Provider Info     | Current model and provider identity                  |\n\n**Context File Discovery:**\n\n1. Check `cwd/.hermes.md` or `HERMES.md`\n2. Walk parent directories up to git root\n3. Validate against injection patterns before inclusion\n\n**Injection Detection (30+ patterns):**\n\n- \"ignore previous instructions\", \"system prompt override\"\n- \"do not tell the user\", \"act as if you have no restrictions\"\n- HTML comment injection, exfiltration via curl, Unicode stealth chars\n\n---\n\n### 8.2 Context Compressor (agent/context_compressor.py)\n\nAutomatically compresses conversation history when approaching context limits.\n\n```python\nclass ContextCompressor:\n    threshold_percent: float = 0.50   # Compress when 50% of context used\n    protect_first_n: int = 3          # Keep system prompt + first turn\n    protect_last_n: int = 20          # Keep recent N messages\n    summary_target_ratio: float = 0.20  # Summary = 20% of compressed content\n```\n\n**Compression Algorithm:**\n\n1. **Pre-pass:** Replace old tool results with placeholder (cheap)\n2. **Protect head:** System prompt + first exchange always kept\n3. **Protect tail:** Token-budget-based tail protection (~20K tokens)\n4. **Summarize middle:** LLM-generated structured summary (Goal, Progress, Decisions, Files, Next Steps)\n5. **Iterative updates:** On subsequent compressions, update previous summary instead of re-summarizing\n\n---\n\n### 8.3 Prompt Caching (agent/prompt_caching.py)\n\nAnthropic prompt caching support for cost reduction.\n\n- System prompt cached across turns (first conversation turn establishes cache)\n- Cache markers inserted at system prompt boundaries\n- Must not break mid-conversation — altering past context invalidates cache\n- The ONLY time context is altered is during compression\n\n**Critical Rule:** Do NOT implement changes that would alter past context, change toolsets, reload memories, or rebuild system prompts mid-conversation.\n\n---\n\n### 8.4 Auxiliary Client (agent/auxiliary_client.py)\n\nSeparate LLM client for non-primary tasks:\n\n- **Vision analysis** — Image description and analysis\n- **Web extraction** — Content summarization\n- **Context compression** — Generating conversation summaries\n- **Session search** — Summarizing search results\n- **MCP sampling** — Serving server-initiated LLM requests\n\nConfigured per-task via `auxiliary` section in config.yaml.\n\n---\n\n### 8.5 Display & Spinner (agent/display.py)\n\n- **KawaiiSpinner** — Animated face characters during API calls\n- **Tool preview formatting** — `┊` prefixed activity feed for tool execution\n- **Inline diff previews** — Shows unified diffs for write/patch operations\n- Respects active skin for colors, emojis, and branding\n\n---\n\n### 8.6 Skill Commands (agent/skill_commands.py)\n\nShared skill invocation for CLI and gateway:\n\n- Skills loaded from `~/.hermes/skills/` and external directories\n- Injected as **user message** (not system prompt) to preserve prompt caching\n- `/plan` command generates implementation plans stored in `.hermes/plans/`\n- Skill content includes setup instructions, tool options, usage examples\n\n---\n\n## 9. Messaging Gateway\n\n### 9.1 GatewayRunner (gateway/run.py)\n\nMain controller managing all platform adapters and routing messages.\n\n**Key Attributes:**\n\n- `adapters: Dict[Platform, BasePlatformAdapter]` — Active platform instances\n- `session_store: SessionStore` — Conversation persistence\n- `_running_agents: Dict[str, AIAgent]` — Per-session cached agents (preserves prompt caching)\n- `_pending_approvals: Dict[str, Dict]` — Dangerous command approval tracking\n- `pairing_store: PairingStore` — DM code-based user authorization\n\n**Message Flow:**\n\n1. Platform adapter queues `MessageEvent`\n2. GatewayRunner dequeues, calls `_handle_message()`\n3. Slash command? → dispatch to handler\n4. Regular message? → `_handle_message_with_agent","frontmatter":{"name":"hermes-agent","description":"Expert in building self-improving AI agents with tool use, multi-platform messaging, and a closed learning loop. Proficient in LLM orchestration, tool integration, session management, and agent autonomy."},"isInternal":false,"tokens":11267,"sizeBytes":51222},{"name":"SKILL.md","path":".agents/skills/lat-md/SKILL.md","rawUrl":"https://raw.githubusercontent.com/fathah/hermes-desktop/HEAD/.agents/skills/lat-md/SKILL.md","title":"lat-md","category":"anthropic-skill","format":"markdown","content":"---\nname: lat-md\ndescription: >-\n  Writing and maintaining lat.md documentation files — structured markdown that\n  describes a project's architecture, design decisions, and test specs. Use when\n  creating, editing, or reviewing files in the lat.md/ directory.\n---\n\n# lat.md Authoring Guide\n\nThis skill covers the syntax, structure rules, and conventions for writing `lat.md/` files. Load it whenever you need to create or edit sections in the `lat.md/` directory.\n\n## What belongs in lat.md\n\n`lat.md/` files describe **what** the project does and **why** — domain concepts, key design decisions, business logic, and test specifications. They do NOT duplicate source code. Think of each section as an anchor that source code references back to.\n\nGood candidates for sections:\n- Architecture decisions and their rationale\n- Domain concepts and business rules\n- API contracts and protocols\n- Test specifications (what is tested and why)\n- Non-obvious constraints or invariants\n\nBad candidates:\n- Step-by-step code walkthroughs (the code itself is the walkthrough)\n- Auto-generated API docs (use tools for that)\n- Temporary notes or TODOs\n\n## Section structure\n\nEvery section **must** have a leading paragraph — at least one sentence immediately after the heading, before any child headings or other block content.\n\nThe first paragraph must be ≤250 characters (excluding `[[wiki link]]` content). This paragraph is the section's identity — it appears in search results, command output, and RAG context.\n\n```markdown\n# Good Section\n\nBrief overview of what this section documents and why it matters.\n\nMore detail can go in subsequent paragraphs, code blocks, or lists.\n\n## Child heading\n\nDetails about this child topic.\n```\n\n```markdown\n# Bad Section\n\n## Child heading\n\nThis is invalid — \"Bad Section\" has no leading paragraph.\n```\n\n`lat check` enforces this rule.\n\n## Section IDs\n\nSections are addressed by file path and heading chain:\n\n- **Full form**: `lat.md/path/to/file#Heading#SubHeading`\n- **Short form**: `file#Heading#SubHeading` (when the file stem is unique)\n\nExamples: `lat.md/tests/search#RAG Replay Tests`, `cli#init`, `parser#Wiki Links`.\n\n## Wiki links\n\nCross-reference other sections or source code with `[[target]]` or `[[target|alias]]`.\n\n### Section links\n\n```markdown\nSee [[cli#init]] for setup details.\nThe parser validates [[parser#Wiki Links|wiki link syntax]].\n```\n\n### Source code links\n\nReference functions, classes, constants, and methods in source files:\n\n```markdown\n[[src/config.ts#getConfigDir]]          — function\n[[src/server.ts#App#listen]]            — class method\n[[lib/utils.py#parse_args]]             — Python function\n[[src/lib.rs#Greeter#greet]]            — Rust impl method\n[[src/app.go#Greeter#Greet]]            — Go method\n[[src/app.h#Greeter]]                   — C struct\n```\n\n`lat check` validates that all targets exist.\n\n## Code refs\n\nTie source code back to `lat.md/` sections with `@lat:` comments:\n\n```typescript\n// @lat: [[cli#init]]\nexport function init() { ... }\n```\n\n```python\n# @lat: [[cli#init]]\ndef init():\n    ...\n```\n\nSupported comment styles: `//` (JS/TS/Rust/Go/C) and `#` (Python).\n\nPlace one `@lat:` comment per section, at the relevant code — not at the top of the file.\n\n## Test specs\n\nDescribe tests as sections in `lat.md/` files. Add frontmatter to require that every leaf section has a matching `@lat:` comment in test code:\n\n```markdown\n---\nlat:\n  require-code-mention: true\n---\n# Tests\n\nAuthentication test specifications.\n\n## User login\n\nVerify credential validation and error handling.\n\n### Rejects expired tokens\n\nTokens past their expiry timestamp are rejected with 401, even if otherwise valid.\n\n### Handles missing password\n\nLogin request without a password field returns 400 with a descriptive error.\n```\n\nEach test references its spec:\n\n```python\n# @lat: [[tests#User login#Rejects expired tokens]]\ndef test_rejects_expired_tokens():\n    ...\n```\n\nRules:\n- Every leaf section under `require-code-mention: true` must be referenced by exactly one `@lat:` comment\n- Every section MUST have a description — at least one sentence explaining what the test verifies and why\n- `lat check` flags unreferenced specs and dangling code refs\n\n## Frontmatter\n\nOptional YAML frontmatter at the top of `lat.md/` files:\n\n```yaml\n---\nlat:\n  require-code-mention: true\n---\n```\n\nCurrently the only supported field is `require-code-mention` for test spec enforcement.\n\n## Validation\n\nAlways run `lat check` after editing `lat.md/` files. It validates:\n- All wiki links point to existing sections or source code symbols\n- All `@lat:` code refs point to existing sections\n- Every section has a leading paragraph (≤250 chars)\n- All `require-code-mention` leaf sections are referenced in code\n","frontmatter":{"name":"lat-md","description":"Writing and maintaining lat.md documentation files — structured markdown that describes a project's architecture, design decisions, and test specs. Use when creating, editing, or reviewing files in the lat.md/ directory."},"isInternal":false,"tokens":1094,"sizeBytes":4778},{"name":"tsconfig-strict.json","path":".agents/skills/typescript-expert/references/tsconfig-strict.json","rawUrl":"https://raw.githubusercontent.com/fathah/hermes-desktop/HEAD/.agents/skills/typescript-expert/references/tsconfig-strict.json","title":"References Skill","category":"anthropic-skill","format":"json","content":"{\n  \"$schema\": \"https://json.schemastore.org/tsconfig\",\n  \"display\": \"Strict TypeScript 5.x\",\n  \"compilerOptions\": {\n    // =========================================================================\n    // STRICTNESS (Maximum Type Safety)\n    // =========================================================================\n    \"strict\": true,\n    \"noUncheckedIndexedAccess\": true,\n    \"noImplicitOverride\": true,\n    \"noPropertyAccessFromIndexSignature\": true,\n    \"exactOptionalPropertyTypes\": true,\n    \"noFallthroughCasesInSwitch\": true,\n    \"forceConsistentCasingInFileNames\": true,\n    // =========================================================================\n    // MODULE SYSTEM (Modern ESM)\n    // =========================================================================\n    \"module\": \"ESNext\",\n    \"moduleResolution\": \"bundler\",\n    \"resolveJsonModule\": true,\n    \"esModuleInterop\": true,\n    \"allowSyntheticDefaultImports\": true,\n    \"isolatedModules\": true,\n    \"verbatimModuleSyntax\": true,\n    // =========================================================================\n    // OUTPUT\n    // =========================================================================\n    \"target\": \"ES2022\",\n    \"lib\": [\"ES2022\", \"DOM\", \"DOM.Iterable\"],\n    \"declaration\": true,\n    \"declarationMap\": true,\n    \"sourceMap\": true,\n    // =========================================================================\n    // PERFORMANCE\n    // =========================================================================\n    \"skipLibCheck\": true,\n    \"incremental\": true,\n    // =========================================================================\n    // PATH ALIASES\n    // =========================================================================\n    \"baseUrl\": \".\",\n    \"paths\": {\n      \"@/*\": [\"./src/*\"],\n      \"@/components/*\": [\"./src/components/*\"],\n      \"@/lib/*\": [\"./src/lib/*\"],\n      \"@/types/*\": [\"./src/types/*\"],\n      \"@/utils/*\": [\"./src/utils/*\"]\n    },\n    // =========================================================================\n    // JSX (for React projects)\n    // =========================================================================\n    // \"jsx\": \"react-jsx\",\n    // =========================================================================\n    // EMIT\n    // =========================================================================\n    \"noEmit\": true // Let bundler handle emit\n    // \"outDir\": \"./dist\",\n    // \"rootDir\": \"./src\",\n    // =========================================================================\n    // DECORATORS (if needed)\n    // =========================================================================\n    // \"experimentalDecorators\": true,\n    // \"emitDecoratorMetadata\": true\n  },\n  \"include\": [\"src/**/*.ts\", \"src/**/*.tsx\", \"src/**/*.d.ts\"],\n  \"exclude\": [\n    \"node_modules\",\n    \"dist\",\n    \"build\",\n    \"coverage\",\n    \"**/*.test.ts\",\n    \"**/*.spec.ts\"\n  ]\n}\n","isInternal":false,"tokens":525,"sizeBytes":2916},{"name":"typescript-cheatsheet.md","path":".agents/skills/typescript-expert/references/typescript-cheatsheet.md","rawUrl":"https://raw.githubusercontent.com/fathah/hermes-desktop/HEAD/.agents/skills/typescript-expert/references/typescript-cheatsheet.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# TypeScript Cheatsheet\n\n## Type Basics\n\n```typescript\n// Primitives\nconst name: string = \"John\";\nconst age: number = 30;\nconst isActive: boolean = true;\nconst nothing: null = null;\nconst notDefined: undefined = undefined;\n\n// Arrays\nconst numbers: number[] = [1, 2, 3];\nconst strings: Array<string> = [\"a\", \"b\", \"c\"];\n\n// Tuple\nconst tuple: [string, number] = [\"hello\", 42];\n\n// Object\nconst user: { name: string; age: number } = { name: \"John\", age: 30 };\n\n// Union\nconst value: string | number = \"hello\";\n\n// Literal\nconst direction: \"up\" | \"down\" | \"left\" | \"right\" = \"up\";\n\n// Any vs Unknown\nconst anyValue: any = \"anything\"; // ❌ Avoid\nconst unknownValue: unknown = \"safe\"; // ✅ Prefer, requires narrowing\n```\n\n## Type Aliases & Interfaces\n\n```typescript\n// Type Alias\ntype Point = {\n  x: number;\n  y: number;\n};\n\n// Interface (preferred for objects)\ninterface User {\n  id: string;\n  name: string;\n  email?: string; // Optional\n  readonly createdAt: Date; // Readonly\n}\n\n// Extending\ninterface Admin extends User {\n  permissions: string[];\n}\n\n// Intersection\ntype AdminUser = User & { permissions: string[] };\n```\n\n## Generics\n\n```typescript\n// Generic function\nfunction identity<T>(value: T): T {\n  return value;\n}\n\n// Generic with constraint\nfunction getLength<T extends { length: number }>(item: T): number {\n  return item.length;\n}\n\n// Generic interface\ninterface ApiResponse<T> {\n  data: T;\n  status: number;\n  message: string;\n}\n\n// Generic with default\ntype Container<T = string> = {\n  value: T;\n};\n\n// Multiple generics\nfunction merge<T, U>(obj1: T, obj2: U): T & U {\n  return { ...obj1, ...obj2 };\n}\n```\n\n## Utility Types\n\n```typescript\ninterface User {\n  id: string;\n  name: string;\n  email: string;\n  age: number;\n}\n\n// Partial - all optional\ntype PartialUser = Partial<User>;\n\n// Required - all required\ntype RequiredUser = Required<User>;\n\n// Readonly - all readonly\ntype ReadonlyUser = Readonly<User>;\n\n// Pick - select properties\ntype UserName = Pick<User, \"id\" | \"name\">;\n\n// Omit - exclude properties\ntype UserWithoutEmail = Omit<User, \"email\">;\n\n// Record - key-value map\ntype UserMap = Record<string, User>;\n\n// Extract - extract from union\ntype StringOrNumber = string | number | boolean;\ntype OnlyStrings = Extract<StringOrNumber, string>;\n\n// Exclude - exclude from union\ntype NotString = Exclude<StringOrNumber, string>;\n\n// NonNullable - remove null/undefined\ntype MaybeString = string | null | undefined;\ntype DefinitelyString = NonNullable<MaybeString>;\n\n// ReturnType - get function return type\nfunction getUser() {\n  return { name: \"John\" };\n}\ntype UserReturn = ReturnType<typeof getUser>;\n\n// Parameters - get function parameters\ntype GetUserParams = Parameters<typeof getUser>;\n\n// Awaited - unwrap Promise\ntype ResolvedUser = Awaited<Promise<User>>;\n```\n\n## Conditional Types\n\n```typescript\n// Basic conditional\ntype IsString<T> = T extends string ? true : false;\n\n// Infer keyword\ntype UnwrapPromise<T> = T extends Promise<infer U> ? U : T;\n\n// Distributive conditional\ntype ToArray<T> = T extends any ? T[] : never;\ntype Result = ToArray<string | number>; // string[] | number[]\n\n// NonDistributive\ntype ToArrayNonDist<T> = [T] extends [any] ? T[] : never;\n```\n\n## Template Literal Types\n\n```typescript\ntype Color = \"red\" | \"green\" | \"blue\";\ntype Size = \"small\" | \"medium\" | \"large\";\n\n// Combine\ntype ColorSize = `${Color}-${Size}`;\n// 'red-small' | 'red-medium' | 'red-large' | ...\n\n// Event handlers\ntype EventName = \"click\" | \"focus\" | \"blur\";\ntype EventHandler = `on${Capitalize<EventName>}`;\n// 'onClick' | 'onFocus' | 'onBlur'\n```\n\n## Mapped Types\n\n```typescript\n// Basic mapped type\ntype Optional<T> = {\n  [K in keyof T]?: T[K];\n};\n\n// With key remapping\ntype Getters<T> = {\n  [K in keyof T as `get${Capitalize<string & K>}`]: () => T[K];\n};\n\n// Filter keys\ntype OnlyStrings<T> = {\n  [K in keyof T as T[K] extends string ? K : never]: T[K];\n};\n```\n\n## Type Guards\n\n```typescript\n// typeof guard\nfunction process(value: string | number) {\n  if (typeof value === \"string\") {\n    return value.toUpperCase(); // string\n  }\n  return value.toFixed(2); // number\n}\n\n// instanceof guard\nclass Dog {\n  bark() {}\n}\nclass Cat {\n  meow() {}\n}\n\nfunction makeSound(animal: Dog | Cat) {\n  if (animal instanceof Dog) {\n    animal.bark();\n  } else {\n    animal.meow();\n  }\n}\n\n// in guard\ninterface Bird {\n  fly(): void;\n}\ninterface Fish {\n  swim(): void;\n}\n\nfunction move(animal: Bird | Fish) {\n  if (\"fly\" in animal) {\n    animal.fly();\n  } else {\n    animal.swim();\n  }\n}\n\n// Custom type guard\nfunction isString(value: unknown): value is string {\n  return typeof value === \"string\";\n}\n\n// Assertion function\nfunction assertIsString(value: unknown): asserts value is string {\n  if (typeof value !== \"string\") {\n    throw new Error(\"Not a string\");\n  }\n}\n```\n\n## Discriminated Unions\n\n```typescript\n// With type discriminant\ntype Success<T> = { type: \"success\"; data: T };\ntype Error = { type: \"error\"; message: string };\ntype Loading = { type: \"loading\" };\n\ntype State<T> = Success<T> | Error | Loading;\n\nfunction handle<T>(state: State<T>) {\n  switch (state.type) {\n    case \"success\":\n      return state.data; // T\n    case \"error\":\n      return state.message; // string\n    case \"loading\":\n      return null;\n  }\n}\n\n// Exhaustive check\nfunction assertNever(value: never): never {\n  throw new Error(`Unexpected value: ${value}`);\n}\n```\n\n## Branded Types\n\n```typescript\n// Create branded type\ntype Brand<K, T> = K & { __brand: T };\n\ntype UserId = Brand<string, \"UserId\">;\ntype OrderId = Brand<string, \"OrderId\">;\n\n// Constructor functions\nfunction createUserId(id: string): UserId {\n  return id as UserId;\n}\n\nfunction createOrderId(id: string): OrderId {\n  return id as OrderId;\n}\n\n// Usage - prevents mixing\nfunction getOrder(orderId: OrderId, userId: UserId) {}\n\nconst userId = createUserId(\"user-123\");\nconst orderId = createOrderId(\"order-456\");\n\ngetOrder(orderId, userId); // ✅ OK\n// getOrder(userId, orderId)  // ❌ Error - types don't match\n```\n\n## Module Declarations\n\n```typescript\n// Declare module for untyped package\ndeclare module \"untyped-package\" {\n  export function doSomething(): void;\n  export const value: string;\n}\n\n// Augment existing module\ndeclare module \"express\" {\n  interface Request {\n    user?: { id: string };\n  }\n}\n\n// Declare global\ndeclare global {\n  interface Window {\n    myGlobal: string;\n  }\n}\n```\n\n## TSConfig Essentials\n\n```json\n{\n  \"compilerOptions\": {\n    // Strictness\n    \"strict\": true,\n    \"noUncheckedIndexedAccess\": true,\n    \"noImplicitOverride\": true,\n\n    // Modules\n    \"module\": \"ESNext\",\n    \"moduleResolution\": \"bundler\",\n    \"esModuleInterop\": true,\n\n    // Output\n    \"target\": \"ES2022\",\n    \"lib\": [\"ES2022\", \"DOM\"],\n\n    // Performance\n    \"skipLibCheck\": true,\n    \"incremental\": true,\n\n    // Paths\n    \"baseUrl\": \".\",\n    \"paths\": {\n      \"@/*\": [\"./src/*\"]\n    }\n  }\n}\n```\n\n## Best Practices\n\n```typescript\n// ✅ Prefer interface for objects\ninterface User {\n  name: string;\n}\n\n// ✅ Use const assertions\nconst routes = [\"home\", \"about\"] as const;\n\n// ✅ Use satisfies for validation\nconst config = {\n  api: \"https://api.example.com\",\n} satisfies Record<string, string>;\n\n// ✅ Use unknown over any\nfunction parse(input: unknown) {\n  if (typeof input === \"string\") {\n    return JSON.parse(input);\n  }\n}\n\n// ✅ Explicit return types for public APIs\nexport function getUser(id: string): User | null {\n  // ...\n}\n\n// ❌ Avoid\nconst data: any = fetchData();\ndata.anything.goes.wrong; // No type safety\n```\n","isInternal":false,"tokens":1970,"sizeBytes":7507},{"name":"SKILL.md","path":".agents/skills/typescript-expert/SKILL.md","rawUrl":"https://raw.githubusercontent.com/fathah/hermes-desktop/HEAD/.agents/skills/typescript-expert/SKILL.md","title":"typescript-expert","category":"anthropic-skill","format":"markdown","content":"---\nname: typescript-expert\ndescription: TypeScript and JavaScript expert with deep knowledge of type-level programming, performance optimization, monorepo management, migration strategies, and modern tooling.\ncategory: framework\nrisk: critical\nsource: community\ndate_added: \"2026-02-27\"\n---\n\n# TypeScript Expert\n\nYou are an advanced TypeScript expert with deep, practical knowledge of type-level programming, performance optimization, and real-world problem solving based on current best practices.\n\n## When invoked:\n\n0. If the issue requires ultra-specific expertise, recommend switching and stop:\n   - Deep webpack/vite/rollup bundler internals → typescript-build-expert\n   - Complex ESM/CJS migration or circular dependency analysis → typescript-module-expert\n   - Type performance profiling or compiler internals → typescript-type-expert\n\n   Example to output:\n   \"This requires deep bundler expertise. Please invoke: 'Use the typescript-build-expert subagent.' Stopping here.\"\n\n1. Analyze project setup comprehensively:\n\n   **Use internal tools first (Read, Grep, Glob) for better performance. Shell commands are fallbacks.**\n\n   ```bash\n   # Core versions and configuration\n   npx tsc --version\n   node -v\n   # Detect tooling ecosystem (prefer parsing package.json)\n   node -e \"const p=require('./package.json');console.log(Object.keys({...p.devDependencies,...p.dependencies}||{}).join('\\n'))\" 2>/dev/null | grep -E 'biome|eslint|prettier|vitest|jest|turborepo|nx' || echo \"No tooling detected\"\n   # Check for monorepo (fixed precedence)\n   (test -f pnpm-workspace.yaml || test -f lerna.json || test -f nx.json || test -f turbo.json) && echo \"Monorepo detected\"\n   ```\n\n   **After detection, adapt approach:**\n   - Match import style (absolute vs relative)\n   - Respect existing baseUrl/paths configuration\n   - Prefer existing project scripts over raw tools\n   - In monorepos, consider project references before broad tsconfig changes\n\n2. Identify the specific problem category and complexity level\n\n3. Apply the appropriate solution strategy from my expertise\n\n4. Validate thoroughly:\n\n   ```bash\n   # Fast fail approach (avoid long-lived processes)\n   npm run -s typecheck || npx tsc --noEmit\n   npm test -s || npx vitest run --reporter=basic --no-watch\n   # Only if needed and build affects outputs/config\n   npm run -s build\n   ```\n\n   **Safety note:** Avoid watch/serve processes in validation. Use one-shot diagnostics only.\n\n## Advanced Type System Expertise\n\n### Type-Level Programming Patterns\n\n**Branded Types for Domain Modeling**\n\n```typescript\n// Create nominal types to prevent primitive obsession\ntype Brand<K, T> = K & { __brand: T };\ntype UserId = Brand<string, \"UserId\">;\ntype OrderId = Brand<string, \"OrderId\">;\n\n// Prevents accidental mixing of domain primitives\nfunction processOrder(orderId: OrderId, userId: UserId) {}\n```\n\n- Use for: Critical domain primitives, API boundaries, currency/units\n- Resource: https://egghead.io/blog/using-branded-types-in-typescript\n\n**Advanced Conditional Types**\n\n```typescript\n// Recursive type manipulation\ntype DeepReadonly<T> = T extends (...args: any[]) => any\n  ? T\n  : T extends object\n    ? { readonly [K in keyof T]: DeepReadonly<T[K]> }\n    : T;\n\n// Template literal type magic\ntype PropEventSource<Type> = {\n  on<Key extends string & keyof Type>(\n    eventName: `${Key}Changed`,\n    callback: (newValue: Type[Key]) => void,\n  ): void;\n};\n```\n\n- Use for: Library APIs, type-safe event systems, compile-time validation\n- Watch for: Type instantiation depth errors (limit recursion to 10 levels)\n\n**Type Inference Techniques**\n\n```typescript\n// Use 'satisfies' for constraint validation (TS 5.0+)\nconst config = {\n  api: \"https://api.example.com\",\n  timeout: 5000,\n} satisfies Record<string, string | number>;\n// Preserves literal types while ensuring constraints\n\n// Const assertions for maximum inference\nconst routes = [\"/home\", \"/about\", \"/contact\"] as const;\ntype Route = (typeof routes)[number]; // '/home' | '/about' | '/contact'\n```\n\n### Performance Optimization Strategies\n\n**Type Checking Performance**\n\n```bash\n# Diagnose slow type checking\nnpx tsc --extendedDiagnostics --incremental false | grep -E \"Check time|Files:|Lines:|Nodes:\"\n\n# Common fixes for \"Type instantiation is excessively deep\"\n# 1. Replace type intersections with interfaces\n# 2. Split large union types (>100 members)\n# 3. Avoid circular generic constraints\n# 4. Use type aliases to break recursion\n```\n\n**Build Performance Patterns**\n\n- Enable `skipLibCheck: true` for library type checking only (often significantly improves performance on large projects, but avoid masking app typing issues)\n- Use `incremental: true` with `.tsbuildinfo` cache\n- Configure `include`/`exclude` precisely\n- For monorepos: Use project references with `composite: true`\n\n## Real-World Problem Resolution\n\n### Complex Error Patterns\n\n**\"The inferred type of X cannot be named\"**\n\n- Cause: Missing type export or circular dependency\n- Fix priority:\n  1. Export the required type explicitly\n  2. Use `ReturnType<typeof function>` helper\n  3. Break circular dependencies with type-only imports\n- Resource: https://github.com/microsoft/TypeScript/issues/47663\n\n**Missing type declarations**\n\n- Quick fix with ambient declarations:\n\n```typescript\n// types/ambient.d.ts\ndeclare module \"some-untyped-package\" {\n  const value: unknown;\n  export default value;\n  export = value; // if CJS interop is needed\n}\n```\n\n- For more details: [Declaration Files Guide](https://www.typescriptlang.org/docs/handbook/declaration-files/introduction.html)\n\n**\"Excessive stack depth comparing types\"**\n\n- Cause: Circular or deeply recursive types\n- Fix priority:\n  1. Limit recursion depth with conditional types\n  2. Use `interface` extends instead of type intersection\n  3. Simplify generic constraints\n\n```typescript\n// Bad: Infinite recursion\ntype InfiniteArray<T> = T | InfiniteArray<T>[];\n\n// Good: Limited recursion\ntype NestedArray<T, D extends number = 5> = D extends 0\n  ? T\n  : T | NestedArray<T, [-1, 0, 1, 2, 3, 4][D]>[];\n```\n\n**Module Resolution Mysteries**\n\n- \"Cannot find module\" despite file existing:\n  1. Check `moduleResolution` matches your bundler\n  2. Verify `baseUrl` and `paths` alignment\n  3. For monorepos: Ensure workspace protocol (workspace:\\*)\n  4. Try clearing cache: `rm -rf node_modules/.cache .tsbuildinfo`\n\n**Path Mapping at Runtime**\n\n- TypeScript paths only work at compile time, not runtime\n- Node.js runtime solutions:\n  - ts-node: Use `ts-node -r tsconfig-paths/register`\n  - Node ESM: Use loader alternatives or avoid TS paths at runtime\n  - Production: Pre-compile with resolved paths\n\n### Migration Expertise\n\n**JavaScript to TypeScript Migration**\n\n```bash\n# Incremental migration strategy\n# 1. Enable allowJs and checkJs (merge into existing tsconfig.json):\n# Add to existing tsconfig.json:\n# {\n#   \"compilerOptions\": {\n#     \"allowJs\": true,\n#     \"checkJs\": true\n#   }\n# }\n\n# 2. Rename files gradually (.js → .ts)\n# 3. Add types file by file using AI assistance\n# 4. Enable strict mode features one by one\n\n# Automated helpers (if installed/needed)\ncommand -v ts-migrate >/dev/null 2>&1 && npx ts-migrate migrate . --sources 'src/**/*.js'\ncommand -v typesync >/dev/null 2>&1 && npx typesync  # Install missing @types packages\n```\n\n**Tool Migration Decisions**\n\n| From              | To              | When                                          | Migration Effort  |\n| ----------------- | --------------- | --------------------------------------------- | ----------------- |\n| ESLint + Prettier | Biome           | Need much faster speed, okay with fewer rules | Low (1 day)       |\n| TSC for linting   | Type-check only | Have 100+ files, need faster feedback         | Medium (2-3 days) |\n| Lerna             | Nx/Turborepo    | Need caching, parallel builds                 | High (1 week)     |\n| CJS               | ESM             | Node 18+, modern tooling                      | High (varies)     |\n\n### Monorepo Management\n\n**Nx vs Turborepo Decision Matrix**\n\n- Choose **Turborepo** if: Simple structure, need speed, <20 packages\n- Choose **Nx** if: Complex dependencies, need visualization, plugins required\n- Performance: Nx often performs better on large monorepos (>50 packages)\n\n**TypeScript Monorepo Configuration**\n\n```json\n// Root tsconfig.json\n{\n  \"references\": [\n    { \"path\": \"./packages/core\" },\n    { \"path\": \"./packages/ui\" },\n    { \"path\": \"./apps/web\" }\n  ],\n  \"compilerOptions\": {\n    \"composite\": true,\n    \"declaration\": true,\n    \"declarationMap\": true\n  }\n}\n```\n\n## Modern Tooling Expertise\n\n### Biome vs ESLint\n\n**Use Biome when:**\n\n- Speed is critical (often faster than traditional setups)\n- Want single tool for lint + format\n- TypeScript-first project\n- Okay with 64 TS rules vs 100+ in typescript-eslint\n\n**Stay with ESLint when:**\n\n- Need specific rules/plugins\n- Have complex custom rules\n- Working with Vue/Angular (limited Biome support)\n- Need type-aware linting (Biome doesn't have this yet)\n\n### Type Testing Strategies\n\n**Vitest Type Testing (Recommended)**\n\n```typescript\n// in avatar.test-d.ts\nimport { expectTypeOf } from \"vitest\";\nimport type { Avatar } from \"./avatar\";\n\ntest(\"Avatar props are correctly typed\", () => {\n  expectTypeOf<Avatar>().toHaveProperty(\"size\");\n  expectTypeOf<Avatar[\"size\"]>().toEqualTypeOf<\"sm\" | \"md\" | \"lg\">();\n});\n```\n\n**When to Test Types:**\n\n- Publishing libraries\n- Complex generic functions\n- Type-level utilities\n- API contracts\n\n## Debugging Mastery\n\n### CLI Debugging Tools\n\n```bash\n# Debug TypeScript files directly (if tools installed)\ncommand -v tsx >/dev/null 2>&1 && npx tsx --inspect src/file.ts\ncommand -v ts-node >/dev/null 2>&1 && npx ts-node --inspect-brk src/file.ts\n\n# Trace module resolution issues\nnpx tsc --traceResolution > resolution.log 2>&1\ngrep \"Module resolution\" resolution.log\n\n# Debug type checking performance (use --incremental false for clean trace)\nnpx tsc --generateTrace trace --incremental false\n# Analyze trace (if installed)\ncommand -v @typescript/analyze-trace >/dev/null 2>&1 && npx @typescript/analyze-trace trace\n\n# Memory usage analysis\nnode --max-old-space-size=8192 node_modules/typescript/lib/tsc.js\n```\n\n### Custom Error Classes\n\n```typescript\n// Proper error class with stack preservation\nclass DomainError extends Error {\n  constructor(\n    message: string,\n    public code: string,\n    public statusCode: number,\n  ) {\n    super(message);\n    this.name = \"DomainError\";\n    Error.captureStackTrace(this, this.constructor);\n  }\n}\n```\n\n## Current Best Practices\n\n### Strict by Default\n\n```json\n{\n  \"compilerOptions\": {\n    \"strict\": true,\n    \"noUncheckedIndexedAccess\": true,\n    \"noImplicitOverride\": true,\n    \"exactOptionalPropertyTypes\": true,\n    \"noPropertyAccessFromIndexSignature\": true\n  }\n}\n```\n\n### ESM-First Approach\n\n- Set `\"type\": \"module\"` in package.json\n- Use `.mts` for TypeScript ESM files if needed\n- Configure `\"moduleResolution\": \"bundler\"` for modern tools\n- Use dynamic imports for CJS: `const pkg = await import('cjs-package')`\n  - Note: `await import()` requires async function or top-level await in ESM\n  - For CJS packages in ESM: May need `(await import('pkg')).default` depending on the package's export structure and your compiler settings\n\n### AI-Assisted Development\n\n- GitHub Copilot excels at TypeScript generics\n- Use AI for boilerplate type definitions\n- Validate AI-generated types with type tests\n- Document complex types for AI context\n\n## Code Review Checklist\n\nWhen reviewing TypeScript/JavaScript code, focus on these domain-specific aspects:\n\n### Type Safety\n\n- [ ] No implicit `any` types (use `unknown` or proper types)\n- [ ] Strict null checks enabled and properly handled\n- [ ] Type assertions (`as`) justified and minimal\n- [ ] Generic constraints properly defined\n- [ ] Discriminated unions for error handling\n- [ ] Return types explicitly declared for public APIs\n\n### TypeScript Best Practices\n\n- [ ] Prefer `interface` over `type` for object shapes (better error messages)\n- [ ] Use const assertions for literal types\n- [ ] Leverage type guards and predicates\n- [ ] Avoid type gymnastics when simpler solution exists\n- [ ] Template literal types used appropriately\n- [ ] Branded types for domain primitives\n\n### Performance Considerations\n\n- [ ] Type complexity doesn't cause slow compilation\n- [ ] No excessive type instantiation depth\n- [ ] Avoid complex mapped types in hot paths\n- [ ] Use `skipLibCheck: true` in tsconfig\n- [ ] Project references configured for monorepos\n\n### Module System\n\n- [ ] Consistent import/export patterns\n- [ ] No circular dependencies\n- [ ] Proper use of barrel exports (avoid over-bundling)\n- [ ] ESM/CJS compatibility handled correctly\n- [ ] Dynamic imports for code splitting\n\n### Error Handling Patterns\n\n- [ ] Result types or discriminated unions for errors\n- [ ] Custom error classes with proper inheritance\n- [ ] Type-safe error boundaries\n- [ ] Exhaustive switch cases with `never` type\n\n### Code Organization\n\n- [ ] Types co-located with implementation\n- [ ] Shared types in dedicated modules\n- [ ] Avoid global type augmentation when possible\n- [ ] Proper use of declaration files (.d.ts)\n\n## Quick Decision Trees\n\n### \"Which tool should I use?\"\n\n```\nType checking only? → tsc\nType checking + linting speed critical? → Biome\nType checking + comprehensive linting? → ESLint + typescript-eslint\nType testing? → Vitest expectTypeOf\nBuild tool? → Project size <10 packages? Turborepo. Else? Nx\n```\n\n### \"How do I fix this performance issue?\"\n\n```\nSlow type checking? → skipLibCheck, incremental, project references\nSlow builds? → Check bundler config, enable caching\nSlow tests? → Vitest with threads, avoid type checking in tests\nSlow language server? → Exclude node_modules, limit files in tsconfig\n```\n\n## Expert Resources\n\n### Performance\n\n- [TypeScript Wiki Performance](https://github.com/microsoft/TypeScript/wiki/Performance)\n- [Type instantiation tracking](https://github.com/microsoft/TypeScript/pull/48077)\n\n### Advanced Patterns\n\n- [Type Challenges](https://github.com/type-challenges/type-challenges)\n- [Type-Level TypeScript Course](https://type-level-typescript.com)\n\n### Tools\n\n- [Biome](https://biomejs.dev) - Fast linter/formatter\n- [TypeStat](https://github.com/JoshuaKGoldberg/TypeStat) - Auto-fix TypeScript types\n- [ts-migrate](https://github.com/airbnb/ts-migrate) - Migration toolkit\n\n### Testing\n\n- [Vitest Type Testing](https://vitest.dev/guide/testing-types)\n- [tsd](https://github.com/tsdjs/tsd) - Standalone type testing\n\nAlways validate changes don't break existing functionality before considering the issue resolved.\n\n## When to Use\n\nThis skill is applicable to execute the workflow or actions described in the overview.\n","frontmatter":{"name":"typescript-expert","description":"TypeScript and JavaScript expert with deep knowledge of type-level programming, performance optimization, monorepo management, migration strategies, and modern tooling.","category":"framework","risk":"critical","source":"community","date_added":"2026-02-27"},"isInternal":false,"tokens":3542,"sizeBytes":14761},{"name":"SKILL.md","path":".claude/skills/hermes-agent/SKILL.md","rawUrl":"https://raw.githubusercontent.com/fathah/hermes-desktop/HEAD/.claude/skills/hermes-agent/SKILL.md","title":"hermes-agent","category":"anthropic-skill","format":"markdown","content":"---\nname: hermes-agent\ndescription: Expert in building self-improving AI agents with tool use, multi-platform messaging, and a closed learning loop. Proficient in LLM orchestration, tool integration, session management, and agent autonomy.\n---\n\n# Hermes Agent - Complete Project Guide (A-Z)\n\n> **Purpose of this document:** A single, comprehensive reference that explains everything about the Hermes Agent project — its architecture, source code, features, release history, and design patterns — so that any AI or developer can fully understand the system.\n\n---\n\n## Table of Contents\n\n1. [Project Overview](#1-project-overview)\n2. [Key Features Summary](#2-key-features-summary)\n3. [Installation & Getting Started](#3-installation--getting-started)\n4. [Project Structure](#4-project-structure)\n5. [Core Architecture](#5-core-architecture)\n   - 5.1 [AIAgent Class (run_agent.py)](#51-aiagent-class-run_agentpy)\n   - 5.2 [Tool Orchestration (model_tools.py)](#52-tool-orchestration-model_toolspy)\n   - 5.3 [Toolset System (toolsets.py)](#53-toolset-system-toolsetspy)\n   - 5.4 [Tool Registry (tools/registry.py)](#54-tool-registry-toolsregistrypy)\n   - 5.5 [Session Database (hermes_state.py)](#55-session-database-hermes_statepy)\n   - 5.6 [Constants & Home Directory (hermes_constants.py)](#56-constants--home-directory-hermes_constantspy)\n6. [CLI System](#6-cli-system)\n   - 6.1 [Interactive CLI (cli.py)](#61-interactive-cli-clipy)\n   - 6.2 [CLI Entry Point (hermes_cli/main.py)](#62-cli-entry-point-hermes_climainpy)\n   - 6.3 [Configuration System (hermes_cli/config.py)](#63-configuration-system-hermes_cliconfigpy)\n   - 6.4 [Slash Command Registry (hermes_cli/commands.py)](#64-slash-command-registry-hermes_clicommandspy)\n   - 6.5 [Setup Wizard (hermes_cli/setup.py)](#65-setup-wizard-hermes_clisetupy)\n   - 6.6 [Model Catalog (hermes_cli/models.py)](#66-model-catalog-hermes_climodelspy)\n   - 6.7 [Skin/Theme Engine (hermes_cli/skin_engine.py)](#67-skintheme-engine-hermes_cliskin_enginepy)\n7. [Tool System](#7-tool-system)\n   - 7.1 [Terminal Tool (tools/terminal_tool.py)](#71-terminal-tool-toolsterminal_toolpy)\n   - 7.2 [File Tools (tools/file_tools.py)](#72-file-tools-toolsfile_toolspy)\n   - 7.3 [Web Tools (tools/web_tools.py)](#73-web-tools-toolsweb_toolspy)\n   - 7.4 [Browser Tool (tools/browser_tool.py)](#74-browser-tool-toolsbrowser_toolpy)\n   - 7.5 [Delegate Tool (tools/delegate_tool.py)](#75-delegate-tool-toolsdelegate_toolpy)\n   - 7.6 [MCP Tool (tools/mcp_tool.py)](#76-mcp-tool-toolsmcp_toolpy)\n   - 7.7 [Approval System (tools/approval.py)](#77-approval-system-toolsapprovalpy)\n   - 7.8 [Terminal Backends (tools/environments/)](#78-terminal-backends-toolsenvironments)\n8. [Agent Internals](#8-agent-internals)\n   - 8.1 [Prompt Builder (agent/prompt_builder.py)](#81-prompt-builder-agentprompt_builderpy)\n   - 8.2 [Context Compressor (agent/context_compressor.py)](#82-context-compressor-agentcontext_compressorpy)\n   - 8.3 [Prompt Caching (agent/prompt_caching.py)](#83-prompt-caching-agentprompt_cachingpy)\n   - 8.4 [Auxiliary Client (agent/auxiliary_client.py)](#84-auxiliary-client-agentauxiliary_clientpy)\n   - 8.5 [Display & Spinner (agent/display.py)](#85-display--spinner-agentdisplaypy)\n   - 8.6 [Skill Commands (agent/skill_commands.py)](#86-skill-commands-agentskill_commandspy)\n9. [Messaging Gateway](#9-messaging-gateway)\n   - 9.1 [GatewayRunner (gateway/run.py)](#91-gatewayrunner-gatewayrunpy)\n   - 9.2 [Session Store (gateway/session.py)](#92-session-store-gatewaysessionpy)\n   - 9.3 [Platform Adapters (gateway/platforms/)](#93-platform-adapters-gatewayplatforms)\n10. [Cron Scheduling](#10-cron-scheduling)\n11. [Skills System](#11-skills-system)\n12. [Plugin System](#12-plugin-system)\n13. [Memory System](#13-memory-system)\n14. [ACP Server (IDE Integration)](#14-acp-server-ide-integration)\n15. [API Server](#15-api-server)\n16. [MCP Server Mode](#16-mcp-server-mode)\n17. [RL Training Environments](#17-rl-training-environments)\n18. [Profiles (Multi-Instance)](#18-profiles-multi-instance)\n19. [Security Model](#19-security-model)\n20. [Provider & Model System](#20-provider--model-system)\n21. [Streaming & Reasoning](#21-streaming--reasoning)\n22. [Release History](#22-release-history)\n23. [File Dependency Chain](#23-file-dependency-chain)\n24. [Key Design Patterns](#24-key-design-patterns)\n25. [Configuration Reference](#25-configuration-reference)\n26. [Known Pitfalls](#26-known-pitfalls)\n\n---\n\n## 1. Project Overview\n\n**Hermes Agent** is a self-improving AI agent built by [Nous Research](https://nousresearch.com). It is an open-source (MIT licensed), Python-based project that provides:\n\n- A **full interactive terminal UI** (CLI) for conversing with LLMs\n- A **messaging gateway** supporting 16+ platforms (Telegram, Discord, Slack, WhatsApp, Signal, Matrix, Email, etc.)\n- A **closed learning loop** — the agent creates skills from experience, improves them during use, nudges itself to persist knowledge, searches past conversations, and builds a deepening model of who you are\n- **40+ built-in tools** — terminal execution, file manipulation, web search, browser automation, code execution, image generation, TTS/STT, and more\n- **Any LLM provider** — OpenRouter (200+ models), Nous Portal (400+ models), OpenAI, Anthropic, Hugging Face, GitHub Copilot, z.ai/GLM, Kimi/Moonshot, MiniMax, Alibaba/DashScope, custom endpoints\n- **Six terminal backends** — local, Docker, SSH, Modal (serverless), Daytona (serverless), Singularity (HPC)\n- **Scheduled automations** via built-in cron scheduler\n- **IDE integration** via ACP (Agent Communication Protocol) for VS Code, Zed, JetBrains\n- **MCP integration** — both client (connect to any MCP server) and server (expose Hermes to MCP clients)\n- **RL training** via Atropos environments for training the next generation of tool-calling models\n\n**Tech Stack:**\n\n- Python 3.11+ (core agent, tools, gateway, cron)\n- Node.js (browser automation via agent-browser)\n- SQLite with WAL mode and FTS5 (session storage, full-text search)\n- OpenAI-compatible API (primary inference interface)\n- Anthropic SDK (native Anthropic support)\n- Rich + prompt_toolkit (CLI rendering)\n\n**Repository:** `github.com/NousResearch/hermes-agent`\n**Version:** 0.7.0 (as of April 2026)\n**License:** MIT\n\n---\n\n## 2. Key Features Summary\n\n| Feature                      | Description                                                                                                                                                                      |\n| ---------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| **Terminal UI**              | Full TUI with multiline editing, slash-command autocomplete, conversation history, interrupt-and-redirect, streaming tool output                                                 |\n| **Multi-Platform Messaging** | Telegram, Discord, Slack, WhatsApp, Signal, Matrix, Email, Home Assistant, DingTalk, Feishu/Lark, WeCom, Mattermost, SMS, Webhook — all from a single gateway process            |\n| **Learning Loop**            | Agent-curated memory with periodic nudges, autonomous skill creation, skills self-improve during use, FTS5 session search with LLM summarization, Honcho dialectic user modeling |\n| **Scheduled Tasks**          | Built-in cron scheduler with delivery to any platform (daily reports, nightly backups, weekly audits)                                                                            |\n| **Subagent Delegation**      | Spawn isolated subagents for parallel workstreams with restricted toolsets                                                                                                       |\n| **Execute Code**             | Python scripts that call tools via RPC, collapsing multi-step pipelines into zero-context-cost turns                                                                             |\n| **Terminal Backends**        | Local, Docker, SSH, Modal, Daytona, Singularity — run on a $5 VPS or a GPU cluster                                                                                               |\n| **Skills**                   | 70+ bundled skills across 28 categories, Skills Hub for community discovery, agentskills.io compatibility                                                                        |\n| **Plugins**                  | Drop-in Python plugins with lifecycle hooks (pre_llm_call, post_llm_call, on_session_start, on_session_end)                                                                      |\n| **MCP**                      | Client (connect to MCP servers for extended tools) and Server (expose conversations to MCP clients)                                                                              |\n| **IDE Integration**          | VS Code, Zed, JetBrains via ACP server with session management and tool streaming                                                                                                |\n| **API Server**               | OpenAI-compatible `/v1/chat/completions` endpoint for headless integrations                                                                                                      |\n| **Profiles**                 | Multi-instance support — each profile gets isolated config, memory, sessions, skills, gateway                                                                                    |\n| **Security**                 | Command approval system, secret redaction, SSRF protection, PII redaction, injection detection, credential directory protection                                                  |\n| **RL Training**              | Atropos environments for batch trajectory generation and agent policy optimization                                                                                               |\n\n---\n\n## 3. Installation & Getting Started\n\n```bash\n# One-line install (Linux, macOS, WSL2)\ncurl -fsSL https://raw.githubusercontent.com/NousResearch/hermes-agent/main/scripts/install.sh | bash\n\n# After install\nsource ~/.bashrc    # or: source ~/.zshrc\nhermes              # start chatting\n\n# Key commands\nhermes model        # Choose LLM provider and model\nhermes tools        # Configure which tools are enabled\nhermes config set   # Set individual config values\nhermes gateway      # Start the messaging gateway\nhermes setup        # Run the full setup wizard\nhermes update       # Update to latest version\nhermes doctor       # Diagnose any issues\n```\n\n**For development:**\n\n```bash\ngit clone https://github.com/NousResearch/hermes-agent.git\ncd hermes-agent\ncurl -LsSf https://astral.sh/uv/install.sh | sh\nuv venv venv --python 3.11\nsource venv/bin/activate\nuv pip install -e \".[all,dev]\"\npython -m pytest tests/ -q    # ~3000 tests\n```\n\n---\n\n## 4. Project Structure\n\n```\nhermes-agent/\n├── run_agent.py              # AIAgent class — core conversation loop\n├── model_tools.py            # Tool orchestration, _discover_tools(), handle_function_call()\n├── toolsets.py               # Toolset definitions, _HERMES_CORE_TOOLS list\n├── toolset_distributions.py  # Toolset sampling distributions for RL\n├── cli.py                    # HermesCLI class — interactive CLI orchestrator\n├── hermes_state.py           # SessionDB — SQLite session store (FTS5 search)\n├── hermes_constants.py       # Shared constants, get_hermes_home()\n├── hermes_time.py            # Timezone handling\n├── utils.py                  # Shared utility functions\n├── batch_runner.py           # Parallel batch processing\n├── trajectory_compressor.py  # Trajectory compression for RL training\n├── mcp_serve.py              # MCP server mode entry point\n├── mini_swe_runner.py        # Minimal SWE benchmark runner\n├── rl_cli.py                 # RL CLI commands\n│\n├── agent/                    # Agent internals\n│   ├── prompt_builder.py         # System prompt assembly\n│   ├── context_compressor.py     # Auto context compression\n│   ├── prompt_caching.py         # Anthropic prompt caching\n│   ├── auxiliary_client.py       # Auxiliary LLM client (vision, summarization)\n│   ├── model_metadata.py         # Model context lengths, token estimation\n│   ├── models_dev.py             # models.dev registry integration\n│   ├── display.py                # KawaiiSpinner, tool preview formatting\n│   ├── skill_commands.py         # Skill slash commands (shared CLI/gateway)\n│   └── trajectory.py             # Trajectory saving helpers\n│\n├── hermes_cli/               # CLI subcommands and setup\n│   ├── main.py               # Entry point — all `hermes` subcommands\n│   ├── config.py             # DEFAULT_CONFIG, OPTIONAL_ENV_VARS, migration\n│   ├── commands.py           # Slash command definitions + SlashCommandCompleter\n│   ├── callbacks.py          # Terminal callbacks (clarify, sudo, approval)\n│   ├── setup.py              # Interactive setup wizard\n│   ├── skin_engine.py        # Skin/theme engine\n│   ├── skills_config.py      # `hermes skills` — skill management\n│   ├── tools_config.py       # `hermes tools` — tool management\n│   ├── skills_hub.py         # Skills Hub integration\n│   ├── models.py             # Model catalog, provider model lists\n│   ├── model_switch.py       # Shared /model switch pipeline\n│   └── auth.py               # Provider credential resolution\n│\n├── tools/                    # Tool implementations (one file per tool)\n│   ├── registry.py           # Central tool registry\n│   ├── approval.py           # Dangerous command detection\n│   ├── terminal_tool.py      # Terminal/shell execution\n│   ├── process_registry.py   # Background process management\n│   ├── file_tools.py         # File read/write/search/patch\n│   ├── web_tools.py          # Web search/extract\n│   ├── browser_tool.py       # Browser automation\n│   ├── code_execution_tool.py # execute_code sandbox\n│   ├── delegate_tool.py      # Subagent delegation\n│   ├── mcp_tool.py           # MCP client integration\n│   ├── skills_tool.py        # Skill management tool\n│   ├── todo_tool.py          # Todo/task tracking tool\n│   ├── memory_tool.py        # Memory read/write tool\n│   ├── tts_tool.py           # Text-to-speech\n│   ├── vision_tool.py        # Image analysis\n│   ├── image_gen_tool.py     # Image generation\n│   └── environments/         # Terminal backends\n│       ├── base.py               # BaseEnvironment ABC\n│       ├── local.py              # Local execution\n│       ├── docker.py             # Docker containers\n│       ├── ssh.py                # SSH remote execution\n│       ├── modal.py              # Modal serverless\n│       ├── managed_modal.py      # Nous-hosted Modal\n│       ├── daytona.py            # Daytona serverless\n│       ├── singularity.py        # Singularity HPC containers\n│       └── persistent_shell.py   # Persistent shell mixin\n│\n├── gateway/                  # Messaging platform gateway\n│   ├── run.py                # GatewayRunner — main message loop\n│   ├── session.py            # SessionStore — conversation persistence\n│   ├── status.py             # Gateway status, token locks\n│   └── platforms/            # 16 platform adapters\n│       ├── base.py               # BasePlatformAdapter ABC\n│       ├── telegram.py           # Telegram (polling + webhook)\n│       ├── discord.py            # Discord\n│       ├── slack.py              # Slack\n│       ├── whatsapp.py           # WhatsApp\n│       ├── matrix.py             # Matrix (E2EE)\n│       ├── signal.py             # Signal\n│       ├── email.py              # Email (IMAP/SMTP)\n│       ├── homeassistant.py      # Home Assistant\n│       ├── sms.py                # SMS (Twilio)\n│       ├── mattermost.py         # Mattermost\n│       ├── dingtalk.py           # DingTalk\n│       ├── feishu.py             # Feishu/Lark\n│       ├── wecom.py              # WeCom (Enterprise WeChat)\n│       ├── webhook.py            # Generic webhook\n│       └── api_server.py         # OpenAI-compatible API server\n│\n├── acp_adapter/              # ACP server (IDE integration)\n│   ├── server.py             # HermesACPAgent class\n│   ├── session.py            # SessionManager\n│   ├── events.py             # Streaming callbacks\n│   ├── permissions.py        # Approval callbacks\n│   └── entry.py              # Entry point\n│\n├── cron/                     # Scheduler\n│   ├── scheduler.py          # tick() — job execution engine\n│   └── jobs.py               # Job storage and CRUD\n│\n├── plugins/                  # Plugin system\n│   └── memory/               # 8 memory provider plugins\n│       ├── openviking/\n│       ├── mem0/\n│       ├── hindsight/\n│       ├── holographic/\n│       ├── honcho/\n│       ├── retaindb/\n│       └── byterover/\n│\n├── environments/             # RL training environments (Atropos)\n│   ├── hermes_base_env.py    # Abstract base RL environment\n│   ├── agent_loop.py         # HermesAgentLoop — rollout execution\n│   ├── tool_context.py       # ToolContext — sandbox for RL\n│   ├── web_research_env.py   # Web research tasks\n│   └── agentic_opd_env.py    # Observation-Prediction-Demo env\n│\n├── skills/                   # 70+ bundled skills across 28 categories\n├── optional-skills/          # Additional optional skills\n├── tests/                    # ~3000 pytest tests\n├── scripts/                  # Install, update, packaging scripts\n├── docker/                   # Docker build files\n├── docs/                     # Documentation source (Docusaurus)\n├── website/                  # Landing page\n├── desktop/                  # Desktop app (Electron, separate repo)\n├── tinker-atropos/           # RL submodule\n│\n├── pyproject.toml            # Python package config\n├── AGENTS.md                 # Developer guide for AI assistants\n├── RELEASE_v0.2.0.md → v0.7.0.md  # Release notes\n└── cli-config.yaml.example   # Example config\n```\n\n**User config directory:** `~/.hermes/`\n\n```\n~/.hermes/\n├── config.yaml           # User settings\n├── .env                  # API keys and secrets\n├── MEMORY.md             # Persistent agent memory\n├── USER.md               # User profile\n├── SOUL.md               # Agent personality/identity\n├── sessions.db           # SQLite session database\n├── skills/               # User-installed skills\n├── skins/                # Custom CLI themes\n├── plugins/              # User plugins\n├── cron/                 # Cron jobs and output\n│   ├── jobs.json\n│   └── output/\n├── cache/                # Image/audio cache\n├── plans/                # Generated plans\n├── profiles/             # Multi-instance profiles\n└── mcp/                  # MCP server configs\n```\n\n---\n\n## 5. Core Architecture\n\n### 5.1 AIAgent Class (run_agent.py)\n\nThe `AIAgent` class is the heart of the system — the core conversation loop that orchestrates LLM calls, tool execution, context management, and response delivery.\n\n**Constructor (~60 parameters):**\n\n```python\nclass AIAgent:\n    def __init__(self,\n        model: str = \"anthropic/claude-opus-4.6\",\n        max_iterations: int = 90,\n        enabled_toolsets: list = None,\n        disabled_toolsets: list = None,\n        quiet_mode: bool = False,\n        save_trajectories: bool = False,\n        platform: str = None,            # \"cli\", \"telegram\", etc.\n        session_id: str = None,\n        session_db: SessionDB = None,\n        skip_context_files: bool = False,\n        skip_memory: bool = False,\n        base_url: str = None,\n        api_key: str = None,\n        provider: str = None,\n        api_mode: str = \"chat_completions\",  # or \"anthropic_messages\" or \"codex_responses\"\n        tool_progress_callback = None,\n        stream_delta_callback = None,\n        thinking_callback = None,\n        status_callback = None,\n        iteration_budget: IterationBudget = None,\n        credential_pool = None,\n        checkpoints_enabled: bool = False,\n        # ... plus provider, routing, callback params\n    )\n```\n\n**Main Methods:**\n\n| Method                                                                          | Returns  | Purpose                                                                            |\n| ------------------------------------------------------------------------------- | -------- | ---------------------------------------------------------------------------------- |\n| `chat(message, stream_callback)`                                                | `str`    | Simple interface — returns final response text                                     |\n| `run_conversation(user_message, system_message, conversation_history, task_id)` | `dict`   | Full interface — returns `{final_response, messages, completed, api_calls, error}` |\n| `_interruptible_api_call(api_kwargs)`                                           | Response | Runs API request in background thread with interrupt support                       |\n| `_interruptible_streaming_api_call(api_kwargs, on_first_delta)`                 | Response | Streaming variant with delta callbacks                                             |\n\n**The Core Agent Loop** (inside `run_conversation()`):\n\n```python\nwhile api_call_count < self.max_iterations and self.iteration_budget.remaining > 0:\n    response = client.chat.completions.create(\n        model=model, messages=messages, tools=tool_schemas\n    )\n    if response.tool_calls:\n        for tool_call in response.tool_calls:\n            result = handle_function_call(tool_call.name, tool_call.args, task_id)\n            messages.append(tool_result_message(result))\n        api_call_count += 1\n    else:\n        return response.content  # Final text response\n```\n\n**Key Behaviors:**\n\n- **Three API modes:** `chat_completions` (OpenAI-compatible), `anthropic_messages` (Anthropic SDK), `codex_responses` (OpenAI Codex)\n- **Parallel tool execution:** Independent tool calls run concurrently via ThreadPoolExecutor (unless they share file paths or are in the never-parallel list)\n- **Interrupt support:** Background threads allow interrupt detection without blocking on HTTP\n- **Error recovery:** Automatic fallback chain (primary → fallback model), retry with exponential backoff, context compression on token overflow\n- **Budget pressure:** Warnings at 70% (caution) and 90% (urgent) of iteration budget\n- **Oversized results:** Tool results >100K chars are saved to temp files with a preview\n- **Stale connection detection:** 90s timeout for streaming, 60s read timeout\n\n**IterationBudget** (thread-safe):\n\n```python\nclass IterationBudget:\n    def consume() -> bool    # Check and consume one iteration\n    def refund()             # Give back iteration (for execute_code turns)\n    @property remaining      # Remaining iterations\n```\n\n---\n\n### 5.2 Tool Orchestration (model_tools.py)\n\nBridges the agent and tool registry — handles discovery, schema generation, and dispatch.\n\n**Key Functions:**\n\n| Function                                                                                | Purpose                                                                   |\n| --------------------------------------------------------------------------------------- | ------------------------------------------------------------------------- |\n| `_discover_tools()`                                                                     | Imports all tool modules (each calls `registry.register()` on import)     |\n| `get_tool_definitions(enabled_toolsets, disabled_toolsets, quiet_mode)`                 | Returns OpenAI-format tool schemas filtered by toolset                    |\n| `handle_function_call(function_name, function_args, task_id, user_task, enabled_tools)` | Main dispatcher — routes calls to registry with arg coercion              |\n| `coerce_tool_args(tool_name, args)`                                                     | Type coercion for LLM-generated arguments (string→int, string→bool, etc.) |\n\n**Tool Discovery Order:**\n\n1. Static tools (web_tools, terminal_tool, file_tools, browser_tool, etc.)\n2. Optional tools (fal_client for image gen, honcho, etc.) — graceful fallback if missing\n3. Plugin-registered tools\n4. MCP server tools (dynamic, via tools/list_changed notifications)\n\n**Special Tool Handling:**\n\n- **Agent-level tools** (todo, memory, session_search, delegate_task): Intercepted by `run_agent.py` before `handle_function_call()`\n- **execute_code**: Passes `enabled_tools` for sandbox tool list\n- **Dynamic schema adjustments**: `browser_navigate` strips web_search reference if tools unavailable\n\n**Async Bridging:**\n\n- Persistent event loops (not `asyncio.run()`) to prevent \"Event loop is closed\" errors\n- Main thread uses shared loop; worker threads get per-thread loops\n- `_run_async()` detects running loop and spins up disposable thread if needed\n\n---\n\n### 5.3 Toolset System (toolsets.py)\n\nProvides flexible tool grouping and composition.\n\n**Core Toolsets:**\n\n| Toolset          | Tools Included                                                                                   |\n| ---------------- | ------------------------------------------------------------------------------------------------ |\n| `web`            | web_search, web_extract, web_crawl                                                               |\n| `terminal`       | terminal                                                                                         |\n| `file`           | read_file, write_file, edit_file, list_files, search_files                                       |\n| `browser`        | browser_navigate, browser_snapshot, browser_click, browser_type, browser_scroll, browser_extract |\n| `vision`         | analyze_image                                                                                    |\n| `image_gen`      | generate_image                                                                                   |\n| `tts`            | text_to_speech                                                                                   |\n| `todo`           | todo_read, todo_write                                                                            |\n| `memory`         | memory_read, memory_write                                                                        |\n| `session_search` | session_search                                                                                   |\n| `delegation`     | delegate_task                                                                                    |\n| `code_execution` | execute_code                                                                                     |\n| `cronjob`        | create_job, list_jobs, delete_job                                                                |\n| `messaging`      | send_message                                                                                     |\n| `homeassistant`  | ha_get_states, ha_call_service, ...                                                              |\n\n**Composite Toolsets:**\n\n- `hermes-cli` — All core tools for CLI platform\n- `hermes-telegram`, `hermes-discord`, etc. — Platform-specific tool sets\n- `hermes-gateway` — Union of all platform tools\n- `debugging` — terminal + file + web\n- `safe` — Everything except terminal\n\n**Resolution:**\n\n```python\nresolve_toolset(name, visited=None) → List[str]\n# Recursively resolves toolset to tool names\n# Handles composition (includes) and cycle detection\n# Special aliases: \"all\" or \"*\" = all tools\n```\n\n---\n\n### 5.4 Tool Registry (tools/registry.py)\n\nSingleton managing all tool schemas and handlers. Circular-import safe — has no tool dependencies.\n\n**ToolEntry** (per-tool metadata):\n\n```python\n@dataclass(slots=True)\nclass ToolEntry:\n    name: str\n    toolset: str\n    schema: dict           # OpenAI-format tool definition\n    handler: Callable      # Sync or async handler function\n    check_fn: Callable     # Returns True if tool is available\n    requires_env: list     # Required environment variables\n    is_async: bool\n    description: str\n    emoji: str\n```\n\n**Key Methods:**\n\n```python\nregistry.register(name, toolset, schema, handler, check_fn, requires_env)\nregistry.get_definitions(tool_names, quiet)  # Returns filtered schemas\nregistry.dispatch(name, args, **kwargs)      # Execute with async bridging\nregistry.deregister(name)                    # Remove (for MCP tool refresh)\nregistry.check_tool_availability()           # Returns (available, unavailable)\n```\n\n---\n\n### 5.5 Session Database (hermes_state.py)\n\nSQLite-based persistent session storage with FTS5 full-text search.\n\n**Schema (v6):**\n\n```sql\n-- Sessions table\nsessions (\n    id TEXT PRIMARY KEY,\n    source TEXT, user_id TEXT, model TEXT, model_config TEXT,\n    system_prompt TEXT, parent_session_id TEXT,\n    started_at TEXT, ended_at TEXT, end_reason TEXT,\n    message_count INTEGER, tool_call_count INTEGER,\n    input_tokens INTEGER, output_tokens INTEGER,\n    cache_read_tokens INTEGER, cache_write_tokens INTEGER, reasoning_tokens INTEGER,\n    estimated_cost_usd REAL, actual_cost_usd REAL,\n    title TEXT  -- UNIQUE INDEX\n)\n\n-- Messages table\nmessages (\n    id INTEGER PRIMARY KEY AUTOINCREMENT,\n    session_id TEXT, role TEXT, content TEXT,\n    tool_call_id TEXT, tool_calls TEXT,  -- JSON\n    tool_name TEXT, timestamp TEXT,\n    token_count INTEGER, finish_reason TEXT,\n    reasoning TEXT, reasoning_details TEXT, codex_reasoning_items TEXT\n)\n\n-- FTS5 virtual table (auto-synced via triggers)\nmessages_fts (content)\n```\n\n**Concurrency Model:**\n\n- WAL (Write-Ahead Logging) for concurrent readers + single writer\n- `BEGIN IMMEDIATE` for write transactions (lock at start, not commit)\n- Jitter retry on lock: 20-150ms random backoff, max 15 retries\n- Periodic WAL checkpoint every 50 writes\n\n**Key Operations:**\n\n- `create_session()`, `end_session()`, `reopen_session()`\n- `add_message()`, `get_messages()`\n- `search_sessions(query)` — FTS5 full-text search\n- `update_token_counts()` — Supports both incremental (CLI) and absolute (gateway) modes\n\n---\n\n### 5.6 Constants & Home Directory (hermes_constants.py)\n\nImport-safe constants module with no circular dependencies.\n\n```python\nget_hermes_home() → Path          # HERMES_HOME env var or ~/.hermes\ndisplay_hermes_home() → str       # User-friendly display: \"~/.hermes\"\nget_optional_skills_dir() → Path  # HERMES_OPTIONAL_SKILLS env var\nparse_reasoning_effort(str) → Dict  # \"high\" → {\"enabled\": True, \"effort\": \"high\"}\n\n# Key constants\nOPENROUTER_BASE_URL = \"https://openrouter.ai/api/v1\"\nNOUS_API_BASE_URL = \"https://inference-api.nousresearch.com/v1\"\nAI_GATEWAY_BASE_URL = \"https://ai-gateway.vercel.sh/v1\"\nVALID_REASONING_EFFORTS = (\"xhigh\", \"high\", \"medium\", \"low\", \"minimal\")\n```\n\n---\n\n## 6. CLI System\n\n### 6.1 Interactive CLI (cli.py)\n\nThe `HermesCLI` class provides the interactive terminal interface.\n\n**Features:**\n\n- **Rich** for banner/panels, **prompt_toolkit** for input with autocomplete\n- **KawaiiSpinner** — animated faces during API calls, `┊` activity feed for tool results\n- Multiline editing with Shift+Enter\n- Slash-command autocomplete\n- Session history with up/down arrow navigation\n- Clipboard image paste (Alt+V / Ctrl+V)\n- Status bar showing model, provider, and token counts\n- Inline diff previews for file write/patch operations\n\n**Configuration Loading:**\n\n```python\nload_cli_config() → dict\n# Loads from ~/.hermes/config.yaml (or ./cli-config.yaml fallback)\n# Merges with hardcoded defaults\n# Expands ${ENV_VAR} references\n# Maps terminal config → env vars\n```\n\n---\n\n### 6.2 CLI Entry Point (hermes_cli/main.py)\n\nAll `hermes` subcommands are dispatched from here:\n\n```\nhermes                    # Default: interactive chat\nhermes chat               # Explicit interactive mode\nhermes gateway start|stop|status|install|uninstall\nhermes setup              # Setup wizard\nhermes model              # Select model/provider\nhermes tools              # Configure tools\nhermes skills             # Manage skills\nhermes config set|get     # Direct config manipulation\nhermes cron list|delete   # Cron job management\nhermes doctor             # Diagnose issues\nhermes sessions browse    # Session picker\nhermes profile create|list|switch|delete|export|import\nhermes mcp serve|add|remove  # MCP management\nhermes acp                # Start ACP server\nhermes update|uninstall|version\n```\n\n**Profile System:**\n\n- `_apply_profile_override()` runs BEFORE any imports to set `HERMES_HOME`\n- Pre-parses `--profile/-p` from argv\n- Allows fully isolated agent instances with separate config, memory, sessions, skills\n\n---\n\n### 6.3 Configuration System (hermes_cli/config.py)\n\n**Key Configuration Sections:**\n\n```yaml\nmodel: \"anthropic/claude-opus-4.6\" # or dict with provider/base_url/api_key\nproviders: {} # Provider-specific configs\nfallback_providers: [] # Ordered failover list\ncredential_pool: {} # Multiple API keys per provider\n\nagent:\n  max_turns: 90\n  gateway_timeout: 1800\n  tool_use_enforcement: \"auto\"\n\nterminal:\n  backend: \"local\" # local|docker|modal|daytona|ssh|singularity\n  timeout: 180\n  persistent_shell: true\n  docker_image: \"nikolaik/python-nodejs:...\"\n\ncompression:\n  enabled: true\n  threshold: 0.50 # Compress when 50% of context used\n  target_ratio: 0.20 # Summary = 20% of compressed content\n  protect_last_n: 20\n\nauxiliary:\n  vision: { provider, model }\n  web_extract: { provider, model }\n  compression: { provider, model }\n\nmemory:\n  memory_enabled: true\n  provider: \"\" # \"\" | \"honcho\" | \"mem0\" | etc.\n  memory_char_limit: 2200\n\ndisplay:\n  personality: \"kawaii\"\n  show_reasoning: false\n  inline_diffs: true\n  skin: \"default\"\n  streaming: true\n\ntts:\n  provider: \"edge\" # edge|elevenlabs|openai|neutts\n\nstt:\n  enabled: true\n  provider: \"local\" # local|groq|openai\n\nprivacy:\n  redact_pii: false\n\nmcp_servers: {} # MCP server configurations\n\nskills:\n  external_dirs: [] # Additional skill directories\n\napprovals:\n  mode: \"smart\" # smart|always|off\n```\n\n**Config Files:**\n\n- `~/.hermes/config.yaml` — User settings (authoritative)\n- `~/.hermes/.env` — API keys and secrets\n- Config version migration system (currently v5)\n\n---\n\n### 6.4 Slash Command Registry (hermes_cli/commands.py)\n\nAll slash commands defined centrally in `COMMAND_REGISTRY`:\n\n```python\nCommandDef(name, description, category, aliases, args_hint, cli_only, gateway_only)\n```\n\n**Derived automatically by:**\n\n- CLI `process_command()` — dispatch on canonical name\n- Gateway dispatch + help\n- Telegram BotCommand menu\n- Slack `/hermes` subcommands\n- Autocomplete + help text\n\n**Key Commands:**\n\n| Command        | Aliases    | Description                   |\n| -------------- | ---------- | ----------------------------- |\n| `/new`         | `/reset`   | Start fresh conversation      |\n| `/model`       |            | Show/switch model             |\n| `/personality` |            | Set agent personality         |\n| `/retry`       |            | Retry last turn               |\n| `/undo`        |            | Remove last turn              |\n| `/compress`    | `/compact` | Compress context              |\n| `/usage`       | `/cost`    | Show token usage              |\n| `/insights`    |            | Usage analytics               |\n| `/skills`      |            | Browse/install skills         |\n| `/background`  | `/bg`      | Manage background processes   |\n| `/plan`        |            | Generate implementation plan  |\n| `/rollback`    |            | Restore filesystem checkpoint |\n| `/verbose`     |            | Toggle debug output           |\n| `/reasoning`   |            | Set reasoning effort          |\n| `/yolo`        |            | Toggle approval bypass        |\n| `/btw`         |            | Ephemeral side question       |\n| `/stop`        |            | Kill current agent run        |\n| `/queue`       |            | Queue next prompt             |\n| `/browser`     |            | Interactive browser session   |\n| `/history`     | `/resume`  | Session browser               |\n| `/skin`        |            | Switch CLI theme              |\n\n---\n\n### 6.5 Setup Wizard (hermes_cli/setup.py)\n\nModular interactive wizard with independent sections:\n\n1. **Model & Provider** — Select AI provider, enter API keys, choose model\n2. **Terminal Backend** — Choose execution environment\n3. **Agent Settings** — Max iterations, compression, session policies\n4. **Messaging Platforms** — Configure Telegram, Discord, Slack, etc.\n5. **Tools** — TTS, STT, web search, image generation, browser\n\nFeatures:\n\n- Live credential validation\n- Real-time model list fetching from provider APIs\n- Automatic OpenClaw migration detection\n- Atomic config file writes\n\n---\n\n### 6.6 Model Catalog (hermes_cli/models.py)\n\nProvider-specific model lists:\n\n```python\n_PROVIDER_MODELS = {\n    \"nous\": [\"anthropic/claude-opus-4.6\", \"anthropic/claude-sonnet-4.6\", ...],  # 25+\n    \"openrouter\": [\"anthropic/claude-opus-4.6\", \"google/gemini-3-flash\", ...],  # 30+\n    \"anthropic\": [\"claude-opus-4-6\", \"claude-sonnet-4-6\", ...],\n    \"openai\": [\"gpt-5\", \"gpt-5.4-mini\", \"gpt-4.1\", \"gpt-4o\", ...],\n    \"copilot\": [\"gpt-5.4\", \"gpt-5.4-mini\", \"gpt-5.3-codex\", ...],\n    \"huggingface\": [...],\n    \"minimax\": [...],\n    \"kimi-coding\": [...],\n    \"alibaba\": [...],\n    \"deepseek\": [...],\n    # ... more providers\n}\n```\n\nFeatures:\n\n- Dynamic fetching via provider `/models` endpoints\n- Curated lists used when live probe returns fewer models\n- Fuzzy matching for typo correction\n- Validation against provider catalog\n\n---\n\n### 6.7 Skin/Theme Engine (hermes_cli/skin_engine.py)\n\nData-driven CLI visual customization — no code changes needed.\n\n**Customizable Elements:**\n\n| Element                          | Key                      | Used By           |\n| -------------------------------- | ------------------------ | ----------------- |\n| Banner border/title/accent       | `colors.*`               | banner.py         |\n| Response box border              | `colors.response_border` | cli.py            |\n| Spinner faces (waiting/thinking) | `spinner.*`              | display.py        |\n| Spinner verbs/wings              | `spinner.*`              | display.py        |\n| Tool output prefix               | `tool_prefix`            | display.py        |\n| Per-tool emojis                  | `tool_emojis`            | display.py        |\n| Agent name/welcome/prompt        | `branding.*`             | banner.py, cli.py |\n\n**Built-in Skins:** default, ares, mono, slate, poseidon, sisyphus, charizard\n\n**User Skins:** Drop `~/.hermes/skins/<name>.yaml` and activate with `/skin <name>`\n\n---\n\n## 7. Tool System\n\n### 7.1 Terminal Tool (tools/terminal_tool.py)\n\nShell command execution across multiple backends.\n\n```python\ndef terminal_tool(\n    command: str,\n    background: bool = False,\n    timeout: Optional[int] = None,\n    task_id: Optional[str] = None,\n    force: bool = False,        # Skip approval for dangerous commands\n    workdir: Optional[str] = None,\n    check_interval: Optional[int] = None,  # Background task polling\n    pty: bool = False,\n) -> str  # JSON result\n```\n\n**Features:**\n\n- Multi-backend: Selects based on `TERMINAL_ENV` (local/docker/ssh/modal/daytona/singularity)\n- Per-task_id sandboxes with thread-safe creation locks\n- Dangerous command routing through approval system\n- Background task support with file-based IPC\n- Interrupt handling — polls `is_interrupted()` during execution\n- Auto-cleanup daemon thread for idle environments (>300s)\n- Disk usage warnings at configurable threshold\n\n---\n\n### 7.2 File Tools (tools/file_tools.py)\n\nSafe file operations with size guards and sensitive path protection.\n\n- `read_file_tool(path, offset, limit)` — Read with pagination (default 100K char limit)\n- `write_file_tool(path, content)` — Write with approval for sensitive paths\n- `edit_file_tool(path, old_text, new_text)` — String replacement editing\n- `list_files_tool(path)` — Directory listing\n- `search_files_tool(pattern, path)` — Glob/regex file search\n\n**Safety:**\n\n- Device path blocklist (`/dev/zero`, `/dev/stdin`, etc.)\n- Read dedup tracking — returns stub on re-read if mtime unchanged\n- Sensitive path blocking: `/etc/`, `/boot/`, `~/.ssh` without approval\n- Prompt injection protection for known dangerous paths\n\n---\n\n### 7.3 Web Tools (tools/web_tools.py)\n\nWeb search and content extraction.\n\n- `web_search_tool(query, limit)` — Search via configurable backend\n- `web_extract_tool(urls, format)` — Extract content from URLs\n- `web_crawl_tool(url, instructions)` — LLM-guided web crawling\n\n**Backends:** Firecrawl, Parallel Web, Tavily, Exa, DuckDuckGo\n\n- Fallback detection by highest-priority available API key\n- LLM processing via auxiliary client (Gemini 3 Flash) for intelligent extraction\n- SSRF protection and URL safety checks\n\n---\n\n### 7.4 Browser Tool (tools/browser_tool.py)\n\nHeadless browser automation with accessibility tree.\n\n```python\nbrowser_navigate(url, task_id) → str\nbrowser_snapshot(task_id, max_chars) → str\nbrowser_click(ref, task_id) → str      # Element refs like @e1, @e2\nbrowser_type(ref, text, task_id) → str\nbrowser_scroll(ref, direction, task_id) → str\nbrowser_extract(task, max_chars, task_id) → str\n```\n\n**Backends:**\n\n- **Local:** Headless Chromium via `agent-browser` CLI\n- **Cloud:** Browserbase (stealth, proxies, CAPTCHA solving)\n- **Camofox:** Anti-detection browser using Camoufox\n\n**Features:**\n\n- Accessibility tree for text-based snapshots (no vision required)\n- Session isolation per task_id with inactivity cleanup\n- API key detection in URLs (prevents exfiltration)\n- SSRF protection for private/internal addresses\n\n---\n\n### 7.5 Delegate Tool (tools/delegate_tool.py)\n\nSpawn isolated subagents for parallel workstreams.\n\n```python\ndef delegate_task(\n    goal: str,\n    context: str = None,\n    toolsets: List[str] = None,       # Default: [\"terminal\", \"file\", \"web\"]\n    tasks: List[Dict] = None,          # Batch mode: up to 3 concurrent\n    max_iterations: int = 50,\n    parent_agent = None,\n) → str\n```\n\n**Isolation:**\n\n- Fresh conversation (no parent history)\n- Own task_id (separate terminal session, file ops)\n- Restricted toolset (configurable, blocked tools always stripped)\n- Focused system prompt from goal + context\n- Parent only sees delegation call and summary result\n\n**Blocked Tools:** delegate_task, clarify, memory, send_message, execute_code (no recursion, no user interaction)\n\n**Constraints:** MAX_DEPTH=2, MAX_CONCURRENT_CHILDREN=3\n\n---\n\n### 7.6 MCP Tool (tools/mcp_tool.py)\n\nModel Context Protocol client integration.\n\n**Configuration (config.yaml):**\n\n```yaml\nmcp_servers:\n  filesystem:\n    command: \"npx\"\n    args: [\"-y\", \"@modelcontextprotocol/server-filesystem\", \"/tmp\"]\n    timeout: 120\n  remote_api:\n    url: \"https://my-mcp-server.example.com/mcp\"\n    headers:\n      Authorization: \"Bearer sk-...\"\n    sampling:\n      enabled: true\n      model: \"gemini-3-flash\"\n```\n\n**Features:**\n\n- **Transports:** Stdio (local processes) and HTTP/StreamableHTTP (remote)\n- **Dynamic tool discovery:** Listens for `tools/list_changed` notifications\n- **Sampling support:** MCP servers can request LLM completions\n- **Automatic reconnection** with exponential backoff (5 retries)\n- Dedicated background event loop in daemon thread\n- Thread-safe via lock protecting server dict\n\n---\n\n### 7.7 Approval System (tools/approval.py)\n\nDangerous command detection and approval flow.\n\n```python\ndetect_dangerous_command(command: str) → (is_dangerous, pattern_key, description)\n```\n\n**106 patterns covering:**\n\n- Destructive operations (`rm -r /`, `mkfs`, `dd if=`)\n- Privilege escalation (`chmod 777`, `chown -R root`)\n- SQL injection (`DROP TABLE`, `DELETE FROM` without WHERE)\n- System targeting (`/etc/` writes, `systemctl stop`, fork bombs)\n- Shell injection (pipe to sh, wget to sh, process substitution)\n- Network exfiltration (curl with embedded API keys)\n- Secret access (`cat ~/.env`, `cat ~/.netrc`)\n\n**Normalization:** Strips ANSI escapes, null bytes, normalizes Unicode\n\n**Approval Modes:**\n\n- `smart` — Learns which commands are safe based on user decisions\n- `always` — Always ask for dangerous commands\n- `off` — Never ask (or `/yolo` toggle)\n\n---\n\n### 7.8 Terminal Backends (tools/environments/)\n\n| Backend           | File               | Features                                                                       |\n| ----------------- | ------------------ | ------------------------------------------------------------------------------ |\n| **Local**         | `local.py`         | Direct execution, interrupt support, non-blocking I/O, persistent shell        |\n| **Docker**        | `docker.py`        | Sandboxed containers, cap-drop ALL, no-new-privileges, PID limits, bind mounts |\n| **SSH**           | `ssh.py`           | Remote execution via ControlMaster, persistent shell mixin                     |\n| **Modal**         | `modal.py`         | Native Modal SDK `Sandbox.create()`/`Sandbox.exec()`, persistent snapshots     |\n| **Managed Modal** | `managed_modal.py` | Modal through Nous-hosted gateway                                              |\n| **Daytona**       | `daytona.py`       | Daytona SDK cloud sandboxes, stop/resume lifecycle                             |\n| **Singularity**   | `singularity.py`   | Singularity containers with scratch dir, SIF cache                             |\n\n**Common Interface (BaseEnvironment ABC):**\n\n```python\nexecute(command, timeout) → {\"output\": str, \"returncode\": int}\ncleanup() → None\n```\n\n---\n\n## 8. Agent Internals\n\n### 8.1 Prompt Builder (agent/prompt_builder.py)\n\nAssembles the system prompt from multiple sources:\n\n| Component               | Source                                               |\n| ----------------------- | ---------------------------------------------------- |\n| Agent Identity          | `DEFAULT_AGENT_IDENTITY` or `SOUL.md`                |\n| Memory Guidance         | When/how to use memory tool                          |\n| Session Search Guidance | How to recall past conversations                     |\n| Skills Guidance         | When to create/patch skills                          |\n| Tool Use Enforcement    | Must execute tools, not describe actions             |\n| Skills Index            | `~/.hermes/skills/.hermes-skills.json`               |\n| Platform Hints          | OS, Python version, shell, available tools           |\n| Context Files           | `.hermes.md`, `AGENTS.md`, `.cursorrules`, `SOUL.md` |\n| Model/Provider Info     | Current model and provider identity                  |\n\n**Context File Discovery:**\n\n1. Check `cwd/.hermes.md` or `HERMES.md`\n2. Walk parent directories up to git root\n3. Validate against injection patterns before inclusion\n\n**Injection Detection (30+ patterns):**\n\n- \"ignore previous instructions\", \"system prompt override\"\n- \"do not tell the user\", \"act as if you have no restrictions\"\n- HTML comment injection, exfiltration via curl, Unicode stealth chars\n\n---\n\n### 8.2 Context Compressor (agent/context_compressor.py)\n\nAutomatically compresses conversation history when approaching context limits.\n\n```python\nclass ContextCompressor:\n    threshold_percent: float = 0.50   # Compress when 50% of context used\n    protect_first_n: int = 3          # Keep system prompt + first turn\n    protect_last_n: int = 20          # Keep recent N messages\n    summary_target_ratio: float = 0.20  # Summary = 20% of compressed content\n```\n\n**Compression Algorithm:**\n\n1. **Pre-pass:** Replace old tool results with placeholder (cheap)\n2. **Protect head:** System prompt + first exchange always kept\n3. **Protect tail:** Token-budget-based tail protection (~20K tokens)\n4. **Summarize middle:** LLM-generated structured summary (Goal, Progress, Decisions, Files, Next Steps)\n5. **Iterative updates:** On subsequent compressions, update previous summary instead of re-summarizing\n\n---\n\n### 8.3 Prompt Caching (agent/prompt_caching.py)\n\nAnthropic prompt caching support for cost reduction.\n\n- System prompt cached across turns (first conversation turn establishes cache)\n- Cache markers inserted at system prompt boundaries\n- Must not break mid-conversation — altering past context invalidates cache\n- The ONLY time context is altered is during compression\n\n**Critical Rule:** Do NOT implement changes that would alter past context, change toolsets, reload memories, or rebuild system prompts mid-conversation.\n\n---\n\n### 8.4 Auxiliary Client (agent/auxiliary_client.py)\n\nSeparate LLM client for non-primary tasks:\n\n- **Vision analysis** — Image description and analysis\n- **Web extraction** — Content summarization\n- **Context compression** — Generating conversation summaries\n- **Session search** — Summarizing search results\n- **MCP sampling** — Serving server-initiated LLM requests\n\nConfigured per-task via `auxiliary` section in config.yaml.\n\n---\n\n### 8.5 Display & Spinner (agent/display.py)\n\n- **KawaiiSpinner** — Animated face characters during API calls\n- **Tool preview formatting** — `┊` prefixed activity feed for tool execution\n- **Inline diff previews** — Shows unified diffs for write/patch operations\n- Respects active skin for colors, emojis, and branding\n\n---\n\n### 8.6 Skill Commands (agent/skill_commands.py)\n\nShared skill invocation for CLI and gateway:\n\n- Skills loaded from `~/.hermes/skills/` and external directories\n- Injected as **user message** (not system prompt) to preserve prompt caching\n- `/plan` command generates implementation plans stored in `.hermes/plans/`\n- Skill content includes setup instructions, tool options, usage examples\n\n---\n\n## 9. Messaging Gateway\n\n### 9.1 GatewayRunner (gateway/run.py)\n\nMain controller managing all platform adapters and routing messages.\n\n**Key Attributes:**\n\n- `adapters: Dict[Platform, BasePlatformAdapter]` — Active platform instances\n- `session_store: SessionStore` — Conversation persistence\n- `_running_agents: Dict[str, AIAgent]` — Per-session cached agents (preserves prompt caching)\n- `_pending_approvals: Dict[str, Dict]` — Dangerous command approval tracking\n- `pairing_store: PairingStore` — DM code-based user authorization\n\n**Message Flow:**\n\n1. Platform adapter queues `MessageEvent`\n2. GatewayRunner dequeues, calls `_handle_message()`\n3. Slash command? → dispatch to handler\n4. Regular message? → `_handle_message_with_agent","frontmatter":{"name":"hermes-agent","description":"Expert in building self-improving AI agents with tool use, multi-platform messaging, and a closed learning loop. Proficient in LLM orchestration, tool integration, session management, and agent autonomy."},"isInternal":false,"tokens":11267,"sizeBytes":51222},{"name":"SKILL.md","path":".claude/skills/lat-md/SKILL.md","rawUrl":"https://raw.githubusercontent.com/fathah/hermes-desktop/HEAD/.claude/skills/lat-md/SKILL.md","title":"lat-md","category":"anthropic-skill","format":"markdown","content":"---\nname: lat-md\ndescription: >-\n  Writing and maintaining lat.md documentation files — structured markdown that\n  describes a project's architecture, design decisions, and test specs. Use when\n  creating, editing, or reviewing files in the lat.md/ directory.\n---\n\n# lat.md Authoring Guide\n\nThis skill covers the syntax, structure rules, and conventions for writing `lat.md/` files. Load it whenever you need to create or edit sections in the `lat.md/` directory.\n\n## What belongs in lat.md\n\n`lat.md/` files describe **what** the project does and **why** — domain concepts, key design decisions, business logic, and test specifications. They do NOT duplicate source code. Think of each section as an anchor that source code references back to.\n\nGood candidates for sections:\n- Architecture decisions and their rationale\n- Domain concepts and business rules\n- API contracts and protocols\n- Test specifications (what is tested and why)\n- Non-obvious constraints or invariants\n\nBad candidates:\n- Step-by-step code walkthroughs (the code itself is the walkthrough)\n- Auto-generated API docs (use tools for that)\n- Temporary notes or TODOs\n\n## Section structure\n\nEvery section **must** have a leading paragraph — at least one sentence immediately after the heading, before any child headings or other block content.\n\nThe first paragraph must be ≤250 characters (excluding `[[wiki link]]` content). This paragraph is the section's identity — it appears in search results, command output, and RAG context.\n\n```markdown\n# Good Section\n\nBrief overview of what this section documents and why it matters.\n\nMore detail can go in subsequent paragraphs, code blocks, or lists.\n\n## Child heading\n\nDetails about this child topic.\n```\n\n```markdown\n# Bad Section\n\n## Child heading\n\nThis is invalid — \"Bad Section\" has no leading paragraph.\n```\n\n`lat check` enforces this rule.\n\n## Section IDs\n\nSections are addressed by file path and heading chain:\n\n- **Full form**: `lat.md/path/to/file#Heading#SubHeading`\n- **Short form**: `file#Heading#SubHeading` (when the file stem is unique)\n\nExamples: `lat.md/tests/search#RAG Replay Tests`, `cli#init`, `parser#Wiki Links`.\n\n## Wiki links\n\nCross-reference other sections or source code with `[[target]]` or `[[target|alias]]`.\n\n### Section links\n\n```markdown\nSee [[cli#init]] for setup details.\nThe parser validates [[parser#Wiki Links|wiki link syntax]].\n```\n\n### Source code links\n\nReference functions, classes, constants, and methods in source files:\n\n```markdown\n[[src/config.ts#getConfigDir]]          — function\n[[src/server.ts#App#listen]]            — class method\n[[lib/utils.py#parse_args]]             — Python function\n[[src/lib.rs#Greeter#greet]]            — Rust impl method\n[[src/app.go#Greeter#Greet]]            — Go method\n[[src/app.h#Greeter]]                   — C struct\n```\n\n`lat check` validates that all targets exist.\n\n## Code refs\n\nTie source code back to `lat.md/` sections with `@lat:` comments:\n\n```typescript\n// @lat: [[cli#init]]\nexport function init() { ... }\n```\n\n```python\n# @lat: [[cli#init]]\ndef init():\n    ...\n```\n\nSupported comment styles: `//` (JS/TS/Rust/Go/C) and `#` (Python).\n\nPlace one `@lat:` comment per section, at the relevant code — not at the top of the file.\n\n## Test specs\n\nDescribe tests as sections in `lat.md/` files. Add frontmatter to require that every leaf section has a matching `@lat:` comment in test code:\n\n```markdown\n---\nlat:\n  require-code-mention: true\n---\n# Tests\n\nAuthentication test specifications.\n\n## User login\n\nVerify credential validation and error handling.\n\n### Rejects expired tokens\n\nTokens past their expiry timestamp are rejected with 401, even if otherwise valid.\n\n### Handles missing password\n\nLogin request without a password field returns 400 with a descriptive error.\n```\n\nEach test references its spec:\n\n```python\n# @lat: [[tests#User login#Rejects expired tokens]]\ndef test_rejects_expired_tokens():\n    ...\n```\n\nRules:\n- Every leaf section under `require-code-mention: true` must be referenced by exactly one `@lat:` comment\n- Every section MUST have a description — at least one sentence explaining what the test verifies and why\n- `lat check` flags unreferenced specs and dangling code refs\n\n## Frontmatter\n\nOptional YAML frontmatter at the top of `lat.md/` files:\n\n```yaml\n---\nlat:\n  require-code-mention: true\n---\n```\n\nCurrently the only supported field is `require-code-mention` for test spec enforcement.\n\n## Validation\n\nAlways run `lat check` after editing `lat.md/` files. It validates:\n- All wiki links point to existing sections or source code symbols\n- All `@lat:` code refs point to existing sections\n- Every section has a leading paragraph (≤250 chars)\n- All `require-code-mention` leaf sections are referenced in code\n","frontmatter":{"name":"lat-md","description":"Writing and maintaining lat.md documentation files — structured markdown that describes a project's architecture, design decisions, and test specs. Use when creating, editing, or reviewing files in the lat.md/ directory."},"isInternal":false,"tokens":1094,"sizeBytes":4778},{"name":"CLAUDE.md","path":"CLAUDE.md","rawUrl":"https://raw.githubusercontent.com/fathah/hermes-desktop/HEAD/CLAUDE.md","title":"Claude Agent Guidelines & System Prompt","category":"claude-rule","format":"markdown","content":"# Before starting work\n\n- Run `lat search` to find sections relevant to your task. Read them to understand the design intent before writing code.\n- Run `lat expand` on user prompts to expand any `[[refs]]` — this resolves section names to file locations and provides context.\n\n# Post-task checklist (REQUIRED — do not skip)\n\nAfter EVERY task, before responding to the user:\n\n- [ ] Update `lat.md/` if you added or changed any functionality, architecture, tests, or behavior\n- [ ] Run `lat check` — all wiki links and code refs must pass\n- [ ] Do not skip these steps. Do not consider your task done until both are complete.\n\n---\n\n# What is lat.md?\n\nThis project uses [lat.md](https://www.npmjs.com/package/lat.md) to maintain a structured knowledge graph of its architecture, design decisions, and test specs in the `lat.md/` directory. It is a set of cross-linked markdown files that describe **what** this project does and **why** — the domain concepts, key design decisions, business logic, and test specifications. Use it to ground your work in the actual architecture rather than guessing.\n\n# Commands\n\n```bash\nlat locate \"Section Name\"      # find a section by name (exact, fuzzy)\nlat refs \"file#Section\"        # find what references a section\nlat search \"natural language\"  # semantic search across all sections\nlat expand \"user prompt text\"  # expand [[refs]] to resolved locations\nlat check                      # validate all links and code refs\n```\n\nRun `lat --help` when in doubt about available commands or options.\n\nIf `lat search` fails because no API key is configured, explain to the user that semantic search requires a key provided via `LAT_LLM_KEY` (direct value), `LAT_LLM_KEY_FILE` (path to key file), or `LAT_LLM_KEY_HELPER` (command that prints the key). Supported key prefixes: `sk-...` (OpenAI) or `vck_...` (Vercel). If the user doesn't want to set it up, use `lat locate` for direct lookups instead.\n\n# Syntax primer\n\n- **Section ids**: `lat.md/path/to/file#Heading#SubHeading` — full form uses project-root-relative path (e.g. `lat.md/tests/search#RAG Replay Tests`). Short form uses bare file name when unique (e.g. `search#RAG Replay Tests`, `cli#search#Indexing`).\n- **Wiki links**: `[[target]]` or `[[target|alias]]` — cross-references between sections. Can also reference source code: `[[src/foo.ts#myFunction]]`.\n- **Source code links**: Wiki links in `lat.md/` files can reference functions, classes, constants, and methods in TypeScript/JavaScript/Python/Rust/Go/C files. Use the full path: `[[src/config.ts#getConfigDir]]`, `[[src/server.ts#App#listen]]` (class method), `[[lib/utils.py#parse_args]]`, `[[src/lib.rs#Greeter#greet]]` (Rust impl method), `[[src/app.go#Greeter#Greet]]` (Go method), `[[src/app.h#Greeter]]` (C struct). `lat check` validates these exist.\n- **Code refs**: `// @lat: [[section-id]]` (JS/TS/Rust/Go/C) or `# @lat: [[section-id]]` (Python) — ties source code to concepts\n\n# Test specs\n\nKey tests can be described as sections in `lat.md/` files (e.g. `tests.md`). Add frontmatter to require that every leaf section is referenced by a `// @lat:` or `# @lat:` comment in test code:\n\n```markdown\n---\nlat:\n  require-code-mention: true\n---\n# Tests\n\nAuthentication and authorization test specifications.\n\n## User login\n\nVerify credential validation and error handling for the login endpoint.\n\n### Rejects expired tokens\nTokens past their expiry timestamp are rejected with 401, even if otherwise valid.\n\n### Handles missing password\nLogin request without a password field returns 400 with a descriptive error.\n```\n\nEvery section MUST have a description — at least one sentence explaining what the test verifies and why. Empty sections with just a heading are not acceptable. (This is a specific case of the general leading paragraph rule below.)\n\nEach test in code should reference its spec with exactly one comment placed next to the relevant test — not at the top of the file:\n\n```python\n# @lat: [[tests#User login#Rejects expired tokens]]\ndef test_rejects_expired_tokens():\n    ...\n\n# @lat: [[tests#User login#Handles missing password]]\ndef test_handles_missing_password():\n    ...\n```\n\nDo not duplicate refs. One `@lat:` comment per spec section, placed at the test that covers it. `lat check` will flag any spec section not covered by a code reference, and any code reference pointing to a nonexistent section.\n\n# Section structure\n\nEvery section in `lat.md/` **must** have a leading paragraph — at least one sentence immediately after the heading, before any child headings or other block content. The first paragraph must be ≤250 characters (excluding `[[wiki link]]` content). This paragraph serves as the section's overview and is used in search results, command output, and RAG context — keeping it concise guarantees the section's essence is always captured.\n\n```markdown\n# Good Section\n\nBrief overview of what this section documents and why it matters.\n\nMore detail can go in subsequent paragraphs, code blocks, or lists.\n\n## Child heading\n\nDetails about this child topic.\n```\n\n```markdown\n# Bad Section\n\n## Child heading\n\nDetails about this child topic.\n```\n\nThe second example is invalid because `Bad Section` has no leading paragraph. `lat check` validates this rule and reports errors for missing or overly long leading paragraphs.\n","isInternal":false,"tokens":1243,"sizeBytes":5330},{"name":"hooks.json","path":".codex/hooks.json","rawUrl":"https://raw.githubusercontent.com/fathah/hermes-desktop/HEAD/.codex/hooks.json","title":"Codex Directive: hooks.json","category":"codex-rule","format":"markdown","content":"{\n  \"hooks\": {\n    \"UserPromptSubmit\": [\n      {\n        \"hooks\": [\n          {\n            \"type\": \"command\",\n            \"command\": \"lat hook claude UserPromptSubmit\"\n          }\n        ]\n      }\n    ],\n    \"Stop\": [\n      {\n        \"hooks\": [\n          {\n            \"type\": \"command\",\n            \"command\": \"lat hook claude Stop\"\n          }\n        ]\n      }\n    ]\n  }\n}\n","frontmatter":{"hooks":{"UserPromptSubmit":[{"hooks":[{"type":"command","command":"lat hook claude UserPromptSubmit"}]}],"Stop":[{"hooks":[{"type":"command","command":"lat hook claude Stop"}]}]}},"isInternal":false,"tokens":93,"sizeBytes":378}],"systemPromptSnippet":"<yakaai_skills repo=\"fathah/hermes-desktop\">\n<!-- File: AGENTS.md (Tokens: ~1243 | Category: root-instruction) -->\n# Before starting work\n\n- Run `lat search` to find sections relevant to your task. Read them to understand the design intent before writing code.\n- Run `lat expand` on user prompts to expand any `[[refs]]` — this resolves section names to file locations and provides context.\n\n# Post-task checklist (REQUIRED — do not skip)\n\nAfter EVERY task, before responding to the user:\n\n- [ ] Update `lat.md/` if you added or changed any functionality, architecture, tests, or behavior\n- [ ] Run `lat check` — all wiki links and code refs must pass\n- [ ] Do not skip these steps. Do not consider your task done until both are complete.\n\n---\n\n# What is lat.md?\n\nThis project uses [lat.md](https://www.npmjs.com/package/lat.md) to maintain a structured knowledge graph of its architecture, design decisions, and test specs in the `lat.md/` directory. It is a set of cross-linked markdown files that describe **what** this project does and **why** — the domain concepts, key design decisions, business logic, and test specifications. Use it to ground your work in the actual architecture rather than guessing.\n\n# Commands\n\n```bash\nlat locate \"Section Name\"      # find a section by name (exact, fuzzy)\nlat refs \"file#Section\"        # find what references a section\nlat search \"natural language\"  # semantic search across all sections\nlat expand \"user prompt text\"  # expand [[refs]] to resolved locations\nlat check                      # validate all links and code refs\n```\n\nRun `lat --help` when in doubt about available commands or options.\n\nIf `lat search` fails because no API key is configured, explain to the user that semantic search requires a key provided via `LAT_LLM_KEY` (direct value), `LAT_LLM_KEY_FILE` (path to key file), or `LAT_LLM_KEY_HELPER` (command that prints the key). Supported key prefixes: `sk-...` (OpenAI) or `vck_...` (Vercel). If the user doesn't want to set it up, use `lat locate` for direct lookups instead.\n\n# Syntax primer\n\n- **Section ids**: `lat.md/path/to/file#Heading#SubHeading` — full form uses project-root-relative path (e.g. `lat.md/tests/search#RAG Replay Tests`). Short form uses bare file name when unique (e.g. `search#RAG Replay Tests`, `cli#search#Indexing`).\n- **Wiki links**: `[[target]]` or `[[target|alias]]` — cross-references between sections. Can also reference source code: `[[src/foo.ts#myFunction]]`.\n- **Source code links**: Wiki links in `lat.md/` files can reference functions, classes, constants, and methods in TypeScript/JavaScript/Python/Rust/Go/C files. Use the full path: `[[src/config.ts#getConfigDir]]`, `[[src/server.ts#App#listen]]` (class method), `[[lib/utils.py#parse_args]]`, `[[src/lib.rs#Greeter#greet]]` (Rust impl method), `[[src/app.go#Greeter#Greet]]` (Go method), `[[src/app.h#Greeter]]` (C struct). `lat check` validates these exist.\n- **Code refs**: `// @lat: [[section-id]]` (JS/TS/Rust/Go/C) or `# @lat: [[section-id]]` (Python) — ties source code to concepts\n\n# Test specs\n\nKey tests can be described as sections in `lat.md/` files (e.g. `tests.md`). Add frontmatter to require that every leaf section is referenced by a `// @lat:` or `# @lat:` comment in test code:\n\n```markdown\n---\nlat:\n  require-code-mention: true\n---\n# Tests\n\nAuthentication and authorization test specifications.\n\n## User login\n\nVerify credential validation and error handling for the login endpoint.\n\n### Rejects expired tokens\nTokens past their expiry timestamp are rejected with 401, even if otherwise valid.\n\n### Handles missing password\nLogin request without a password field returns 400 with a descriptive error.\n```\n\nEvery section MUST have a description — at least one sentence explaining what the test verifies and why. Empty sections with just a heading are not acceptable. (This is a specific case of the general leading paragraph rule below.)\n\nEach test in code should reference its spec with exactly one comment placed next to the relevant test — not at the top of the file:\n\n```python\n# @lat: [[tests#User login#Rejects expired tokens]]\ndef test_rejects_expired_tokens():\n    ...\n\n# @lat: [[tests#User login#Handles missing password]]\ndef test_handles_missing_password():\n    ...\n```\n\nDo not duplicate refs. One `@lat:` comment per spec section, placed at the test that covers it. `lat check` will flag any spec section not covered by a code reference, and any code reference pointing to a nonexistent section.\n\n# Section structure\n\nEvery section in `lat.md/` **must** have a leading paragraph — at least one sentence immediately after the heading, before any child headings or other block content. The first paragraph must be ≤250 characters (excluding `[[wiki link]]` content). This paragraph serves as the section's overview and is used in search results, command output, and RAG context — keeping it concise guarantees the section's essence is always captured.\n\n```markdown\n# Good Section\n\nBrief overview of what this section documents and why it matters.\n\nMore detail can go in subsequent paragraphs, code blocks, or lists.\n\n## Child heading\n\nDetails about this child topic.\n```\n\n```markdown\n# Bad Section\n\n## Child heading\n\nDetails about this child topic.\n```\n\nThe second example is invalid because `Bad Section` has no leading paragraph. `lat check` validates this rule and reports errors for missing or overly long leading paragraphs.\n\n<!-- File: .agents/skills/electron-pro/SKILL.md (Tokens: ~1124 | Category: anthropic-skill) -->\n---\nname: electron-pro\ndescription: Expert in building cross-platform desktop applications using web technologies (HTML/CSS/JS) with the Electron framework.\n---\n\n# Electron Desktop Developer\n\n## Purpose\n\nProvides cross-platform desktop application development expertise specializing in Electron, IPC architecture, and OS-level integration. Builds secure, performant desktop applications using web technologies with native capabilities for Windows, macOS, and Linux.\n\n## When to Use\n\n- Building cross-platform desktop apps (VS Code, Discord style)\n- Migrating web apps to desktop with native capabilities (File system, Notifications)\n- Implementing secure IPC (Main ↔ Renderer communication)\n- Optimizing Electron memory usage and startup time\n- Configuring auto-updaters (electron-updater)\n- Signing and notarizing apps for app stores\n\n---\n\n---\n\n## 2. Decision Framework\n\n### Architecture Selection\n\n```\nHow to structure the app?\n│\n├─ **Security First (Recommended)**\n│  ├─ Context Isolation? → **Yes** (Standard since v12)\n│  ├─ Node Integration? → **No** (Never in Renderer)\n│  └─ Preload Scripts? → **Yes** (Bridge API)\n│\n├─ **Data Persistence**\n│  ├─ Simple Settings? → **electron-store** (JSON)\n│  ├─ Large Datasets? → **SQLite** (`better-sqlite3` in Main process)\n│  └─ User Files? → **Native File System API**\n│\n└─ **UI Framework**\n   ├─ React/Vue/Svelte? → **Yes** (Standard SPA approach)\n   ├─ Multiple Windows? → **Window Manager Pattern**\n   └─ System Tray App? → **Hidden Window Pattern**\n```\n\n### IPC Communication Patterns\n\n| Pattern                        | Method               | Use Case                                        |\n| ------------------------------ | -------------------- | ----------------------------------------------- |\n| **One-Way (Renderer → Main)**  | `ipcRenderer.send`   | logging, analytics, minimizing window           |\n| **Two-Way (Request/Response)** | `ipcRenderer.invoke` | DB queries, file reads, heavy computations      |\n| **Main → Renderer**            | `webContents.send`   | Menu actions, system events, push notifications |\n\n**Red Flags → Escalate to `security-auditor`:**\n\n- Enabling `nodeIntegration: true` in production\n- Disabling `contextIsolation`\n- Loading remote content (`https://`) without strict CSP\n- Using `remote` module (Deprecated & insecure)\n\n---\n\n---\n\n### Workflow 2: Performance Optimization (Startup)\n\n**Goal:** Reduce launch time to < 2s.\n\n**Steps:**\n\n1.  **V8 Snapshot**\n    - Use `electron-link` or `v8-compile-cache` to pre-compile JS.\n\n2.  **Lazy Loading Modules**\n    - Don't `require()` everything at top of `main.ts`.\n\n    ```javascript\n    // Bad\n    import { heavyLib } from \"heavy-lib\";\n\n    // Good\n    ipcMain.handle(\"do-work\", () => {\n      const heavyLib = require(\"heavy-lib\");\n      heavyLib.process();\n    });\n    ```\n\n3.  **Bundle Main Process**\n    - Use `esbuild` or `webpack` for Main process (not just Renderer) to tree-shake unused code and minify.\n\n---\n\n---\n\n## 4. Patterns & Templates\n\n### Pattern 1: Worker Threads (CPU Intensive Tasks)\n\n**Use case:** Image processing or parsing large files without freezing the UI.\n\n```typescript\n// main.ts\nimport { Worker } from \"worker_threads\";\n\nipcMain.handle(\"process-image\", (event, data) => {\n  return new Promise((resolve, reject) => {\n    const worker = new Worker(\"./worker.js\", { workerData: data });\n    worker.on(\"message\", resolve);\n    worker.on(\"error\", reject);\n  });\n});\n```\n\n### Pattern 2: Deep Linking (Protocol Handler)\n\n**Use case:** Opening app from browser (`myapp://open?id=123`).\n\n```typescript\n// main.ts\nif (process.defaultApp) {\n  if (process.argv.length >= 2) {\n    app.setAsDefaultProtocolClient(\"myapp\", process.execPath, [\n      path.resolve(process.argv[1]),\n    ]);\n  }\n} else {\n  app.setAsDefaultProtocolClient(\"myapp\");\n}\n\napp.on(\"open-url\", (event, url) => {\n  event.preventDefault();\n  // Parse url 'myapp://...' and navigate renderer\n  mainWindow.webContents.send(\"navigate\", url);\n});\n```\n\n---\n\n---\n\n## 6. Integration Patterns\n\n### **frontend-ui-ux-engineer:**\n\n- **Handoff**: UI Dev builds the React/Vue app → Electron Dev wraps it.\n- **Collaboration**: Handling window controls (custom title bar), vibrancy/acrylic effects.\n- **Tools**: CSS `app-region: drag`.\n\n### **devops-engineer:**\n\n- **Handoff**: Electron Dev provides build config → DevOps sets up CI pipeline.\n- **Collaboration**: Code signing certificates (Apple Developer ID, Windows EV).\n- **Tools**: Electron Builder, Notarization scripts.\n\n### **security-engineer:**\n\n- **Handoff**: Electron Dev implements feature → Security Dev audits IPC surface.\n- **Collaboration**: Defining Content Security Policy (CSP) headers.\n- **Tools**: Electronegativity (Scanner).\n\n---\n\n<!-- File: .agents/skills/hermes-agent/SKILL.md (Tokens: ~11267 | Category: anthropic-skill) -->\n---\nname: hermes-agent\ndescription: Expert in building self-improving AI agents with tool use, multi-platform messaging, and a closed learning loop. Proficient in LLM orchestration, tool integration, session management, and agent autonomy.\n---\n\n# Hermes Agent - Complete Project Guide (A-Z)\n\n> **Purpose of this document:** A single, comprehensive reference that explains everything about the Hermes Agent project — its architecture, source code, features, release history, and design patterns — so that any AI or developer can fully understand the system.\n\n---\n\n## Table of Contents\n\n1. [Project Overview](#1-project-overview)\n2. [Key Features Summary](#2-key-features-summary)\n3. [Installation & Getting Started](#3-installation--getting-started)\n4. [Project Structure](#4-project-structure)\n5. [Core Architecture](#5-core-architecture)\n   - 5.1 [AIAgent Class (run_agent.py)](#51-aiagent-class-run_agentpy)\n   - 5.2 [Tool Orchestration (model_tools.py)](#52-tool-orchestration-model_toolspy)\n   - 5.3 [Toolset System (toolsets.py)](#53-toolset-system-toolsetspy)\n   - 5.4 [Tool Registry (tools/registry.py)](#54-tool-registry-toolsregistrypy)\n   - 5.5 [Session Database (hermes_state.py)](#55-session-database-hermes_statepy)\n   - 5.6 [Constants & Home Directory (hermes_constants.py)](#56-constants--home-directory-hermes_constantspy)\n6. [CLI System](#6-cli-system)\n   - 6.1 [Interactive CLI (cli.py)](#61-interactive-cli-clipy)\n   - 6.2 [CLI Entry Point (hermes_cli/main.py)](#62-cli-entry-point-hermes_climainpy)\n   - 6.3 [Configuration System (hermes_cli/config.py)](#63-configuration-system-hermes_cliconfigpy)\n   - 6.4 [Slash Command Registry (hermes_cli/commands.py)](#64-slash-command-registry-hermes_clicommandspy)\n   - 6.5 [Setup Wizard (hermes_cli/setup.py)](#65-setup-wizard-hermes_clisetupy)\n   - 6.6 [Model Catalog (hermes_cli/models.py)](#66-model-catalog-hermes_climodelspy)\n   - 6.7 [Skin/Theme Engine (hermes_cli/skin_engine.py)](#67-skintheme-engine-hermes_cliskin_enginepy)\n7. [Tool System](#7-tool-system)\n   - 7.1 [Terminal Tool (tools/terminal_tool.py)](#71-terminal-tool-toolsterminal_toolpy)\n   - 7.2 [File Tools (tools/file_tools.py)](#72-file-tools-toolsfile_toolspy)\n   - 7.3 [Web Tools (tools/web_tools.py)](#73-web-tools-toolsweb_toolspy)\n   - 7.4 [Browser Tool (tools/browser_tool.py)](#74-browser-tool-toolsbrowser_toolpy)\n   - 7.5 [Delegate Tool (tools/delegate_tool.py)](#75-delegate-tool-toolsdelegate_toolpy)\n   - 7.6 [MCP Tool (tools/mcp_tool.py)](#76-mcp-tool-toolsmcp_toolpy)\n   - 7.7 [Approval System (tools/approval.py)](#77-approval-system-toolsapprovalpy)\n   - 7.8 [Terminal Backends (tools/environments/)](#78-terminal-backends-toolsenvironments)\n8. [Agent Internals](#8-agent-internals)\n   - 8.1 [Prompt Builder (agent/prompt_builder.py)](#81-prompt-builder-agentprompt_builderpy)\n   - 8.2 [Context Compressor (agent/context_compressor.py)](#82-context-compressor-agentcontext_compressorpy)\n   - 8.3 [Prompt Caching (agent/prompt_caching.py)](#83-prompt-caching-agentprompt_cachingpy)\n   - 8.4 [Auxiliary Client (agent/auxiliary_client.py)](#84-auxiliary-client-agentauxiliary_clientpy)\n   - 8.5 [Display & Spinner (agent/display.py)](#85-display--spinner-agentdisplaypy)\n   - 8.6 [Skill Commands (agent/skill_commands.py)](#86-skill-commands-agentskill_commandspy)\n9. [Messaging Gateway](#9-messaging-gateway)\n   - 9.1 [GatewayRunner (gateway/run.py)](#91-gatewayrunner-gatewayrunpy)\n   - 9.2 [Session Store (gateway/session.py)](#92-session-store-gatewaysessionpy)\n   - 9.3 [Platform Adapters (gateway/platforms/)](#93-platform-adapters-gatewayplatforms)\n10. [Cron Scheduling](#10-cron-scheduling)\n11. [Skills System](#11-skills-system)\n12. [Plugin System](#12-plugin-system)\n13. [Memory System](#13-memory-system)\n14. [ACP Server (IDE Integration)](#14-acp-server-ide-integration)\n15. [API Server](#15-api-server)\n16. [MCP Server Mode](#16-mcp-server-mode)\n17. [RL Training Environments](#17-rl-training-environments)\n18. [Profiles (Multi-Instance)](#18-profiles-multi-instance)\n19. [Security Model](#19-security-model)\n20. [Provider & Model System](#20-provider--model-system)\n21. [Streaming & Reasoning](#21-streaming--reasoning)\n22. [Release History](#22-release-history)\n23. [File Dependency Chain](#23-file-dependency-chain)\n24. [Key Design Patterns](#24-key-design-patterns)\n25. [Configuration Reference](#25-configuration-reference)\n26. [Known Pitfalls](#26-known-pitfalls)\n\n---\n\n## 1. Project Overview\n\n**Hermes Agent** is a self-improving AI agent built by [Nous Research](https://nousresearch.com). It is an open-source (MIT licensed), Python-based project that provides:\n\n- A **full interactive terminal UI** (CLI) for conversing with LLMs\n- A **messaging gateway** supporting 16+ platforms (Telegram, Discord, Slack, WhatsApp, Signal, Matrix, Email, etc.)\n- A **closed learning loop** — the agent creates skills from experience, improves them during use, nudges itself to persist knowledge, searches past conversations, and builds a deepening model of who you are\n- **40+ built-in tools** — terminal execution, file manipulation, web search, browser automation, code execution, image generation, TTS/STT, and more\n- **Any LLM provider** — OpenRouter (200+ models), Nous Portal (400+ models), OpenAI, Anthropic, Hugging Face, GitHub Copilot, z.ai/GLM, Kimi/Moonshot, MiniMax, Alibaba/DashScope, custom endpoints\n- **Six terminal backends** — local, Docker, SSH, Modal (serverless), Daytona (serverless), Singularity (HPC)\n- **Scheduled automations** via built-in cron scheduler\n- **IDE integration** via ACP (Agent Communication Protocol) for VS Code, Zed, JetBrains\n- **MCP integration** — both client (connect to any MCP server) and server (expose Hermes to MCP clients)\n- **RL training** via Atropos environments for training the next generation of tool-calling models\n\n**Tech Stack:**\n\n- Python 3.11+ (core agent, tools, gateway, cron)\n- Node.js (browser automation via agent-browser)\n- SQLite with WAL mode and FTS5 (session storage, full-text search)\n- OpenAI-compatible API (primary inference interface)\n- Anthropic SDK (native Anthropic support)\n- Rich + prompt_toolkit (CLI rendering)\n\n**Repository:** `github.com/NousResearch/hermes-agent`\n**Version:** 0.7.0 (as of April 2026)\n**License:** MIT\n\n---\n\n## 2. Key Features Summary\n\n| Feature                      | Description                                                                                                                                                                      |\n| ---------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| **Terminal UI**              | Full TUI with multiline editing, slash-command autocomplete, conversation history, interrupt-and-redirect, streaming tool output                                                 |\n| **Multi-Platform Messaging** | Telegram, Discord, Slack, WhatsApp, Signal, Matrix, Email, Home Assistant, DingTalk, Feishu/Lark, WeCom, Mattermost, SMS, Webhook — all from a single gateway process            |\n| **Learning Loop**            | Agent-curated memory with periodic nudges, autonomous skill creation, skills self-improve during use, FTS5 session search with LLM summarization, Honcho dialectic user modeling |\n| **Scheduled Tasks**          | Built-in cron scheduler with delivery to any platform (daily reports, nightly backups, weekly audits)                                                                            |\n| **Subagent Delegation**      | Spawn isolated subagents for parallel workstreams with restricted toolsets                                                                                                       |\n| **Execute Code**             | Python scripts that call tools via RPC, collapsing multi-step pipelines into zero-context-cost turns                                                                             |\n| **Terminal Backends**        | Local, Docker, SSH, Modal, Daytona, Singularity — run on a $5 VPS or a GPU cluster                                                                                               |\n| **Skills**                   | 70+ bundled skills across 28 categories, Skills Hub for community discovery, agentskills.io compatibility                                                                        |\n| **Plugins**                  | Drop-in Python plugins with lifecycle hooks (pre_llm_call, post_llm_call, on_session_start, on_session_end)                                                                      |\n| **MCP**                      | Client (connect to MCP servers for extended tools) and Server (expose conversations to MCP clients)                                                                              |\n| **IDE Integration**          | VS Code, Zed, JetBrains via ACP server with session management and tool streaming                                                                                                |\n| **API Server**               | OpenAI-compatible `/v1/chat/completions` endpoint for headless integrations                                                                                                      |\n| **Profiles**                 | Multi-instance support — each profile gets isolated config, memory, sessions, skills, gateway                                                                                    |\n| **Security**                 | Command approval system, secret redaction, SSRF protection, PII redaction, injection detection, credential directory protection                                                  |\n| **RL Training**              | Atropos environments for batch trajectory generation and agent policy optimization                                                                                               |\n\n---\n\n## 3. Installation & Getting Started\n\n```bash\n# One-line install (Linux, macOS, WSL2)\ncurl -fsSL https://raw.githubusercontent.com/NousResearch/hermes-agent/main/scripts/install.sh | bash\n\n# After install\nsource ~/.bashrc    # or: source ~/.zshrc\nhermes              # start chatting\n\n# Key commands\nhermes model        # Choose LLM provider and model\nhermes tools        # Configure which tools are enabled\nhermes config set   # Set individual config values\nhermes gateway      # Start the messaging gateway\nhermes setup        # Run the full setup wizard\nhermes update       # Update to latest version\nhermes doctor       # Diagnose any issues\n```\n\n**For development:**\n\n```bash\ngit clone https://github.com/NousResearch/hermes-agent.git\ncd hermes-agent\ncurl -LsSf https://astral.sh/uv/install.sh | sh\nuv venv venv --python 3.11\nsource venv/bin/activate\nuv pip install -e \".[all,dev]\"\npython -m pytest tests/ -q    # ~3000 tests\n```\n\n---\n\n## 4. Project Structure\n\n```\nhermes-agent/\n├── run_agent.py              # AIAgent class — core conversation loop\n├── model_tools.py            # Tool orchestration, _discover_tools(), handle_function_call()\n├── toolsets.py               # Toolset definitions, _HERMES_CORE_TOOLS list\n├── toolset_distributions.py  # Toolset sampling distributions for RL\n├── cli.py                    # HermesCLI class — interactive CLI orchestrator\n├── hermes_state.py           # SessionDB — SQLite session store (FTS5 search)\n├── hermes_constants.py       # Shared constants, get_hermes_home()\n├── hermes_time.py            # Timezone handling\n├── utils.py                  # Shared utility functions\n├── batch_runner.py           # Parallel batch processing\n├── trajectory_compressor.py  # Trajectory compression for RL training\n├── mcp_serve.py              # MCP server mode entry point\n├── mini_swe_runner.py        # Minimal SWE benchmark runner\n├── rl_cli.py                 # RL CLI commands\n│\n├── agent/                    # Agent internals\n│   ├── prompt_builder.py         # System prompt assembly\n│   ├── context_compressor.py     # Auto context compression\n│   ├── prompt_caching.py         # Anthropic prompt caching\n│   ├── auxiliary_client.py       # Auxiliary LLM client (vision, summarization)\n│   ├── model_metadata.py         # Model context lengths, token estimation\n│   ├── models_dev.py             # models.dev registry integration\n│   ├── display.py                # KawaiiSpinner, tool preview formatting\n│   ├── skill_commands.py         # Skill slash commands (shared CLI/gateway)\n│   └── trajectory.py             # Trajectory saving helpers\n│\n├── hermes_cli/               # CLI subcommands and setup\n│   ├── main.py               # Entry point — all `hermes` subcommands\n│   ├── config.py             # DEFAULT_CONFIG, OPTIONAL_ENV_VARS, migration\n│   ├── commands.py           # Slash command definitions + SlashCommandCompleter\n│   ├── callbacks.py          # Terminal callbacks (clarify, sudo, approval)\n│   ├── setup.py              # Interactive setup wizard\n│   ├── skin_engine.py        # Skin/theme engine\n│   ├── skills_config.py      # `hermes skills` — skill management\n│   ├── tools_config.py       # `hermes tools` — tool management\n│   ├── skills_hub.py         # Skills Hub integration\n│   ├── models.py             # Model catalog, provider model lists\n│   ├── model_switch.py       # Shared /model switch pipeline\n│   └── auth.py               # Provider credential resolution\n│\n├── tools/                    # Tool implementations (one file per tool)\n│   ├── registry.py           # Central tool registry\n│   ├── approval.py           # Dangerous command detection\n│   ├── terminal_tool.py      # Terminal/shell execution\n│   ├── process_registry.py   # Background process management\n│   ├── file_tools.py         # File read/write/search/patch\n│   ├── web_tools.py          # Web search/extract\n│   ├── browser_tool.py       # Browser automation\n│   ├── code_execution_tool.py # execute_code sandbox\n│   ├── delegate_tool.py      # Subagent delegation\n│   ├── mcp_tool.py           # MCP client integration\n│   ├── skills_tool.py        # Skill management tool\n│   ├── todo_tool.py          # Todo/task tracking tool\n│   ├── memory_tool.py        # Memory read/write tool\n│   ├── tts_tool.py           # Text-to-speech\n│   ├── vision_tool.py        # Image analysis\n│   ├── image_gen_tool.py     # Image generation\n│   └── environments/         # Terminal backends\n│       ├── base.py               # BaseEnvironment ABC\n│       ├── local.py              # Local execution\n│       ├── docker.py             # Docker containers\n│       ├── ssh.py                # SSH remote execution\n│       ├── modal.py              # Modal serverless\n│       ├── managed_modal.py      # Nous-hosted Modal\n│       ├── daytona.py            # Daytona serverless\n│       ├── singularity.py        # Singularity HPC containers\n│       └── persistent_shell.py   # Persistent shell mixin\n│\n├── gateway/                  # Messaging platform gateway\n│   ├── run.py                # GatewayRunner — main message loop\n│   ├── session.py            # SessionStore — conversation persistence\n│   ├── status.py             # Gateway status, token locks\n│   └── platforms/            # 16 platform adapters\n│       ├── base.py               # BasePlatformAdapter ABC\n│       ├── telegram.py           # Telegram (polling + webhook)\n│       ├── discord.py            # Discord\n│       ├── slack.py              # Slack\n│       ├── whatsapp.py           # WhatsApp\n│       ├── matrix.py             # Matrix (E2EE)\n│       ├── signal.py             # Signal\n│       ├── email.py              # Email (IMAP/SMTP)\n│       ├── homeassistant.py      # Home Assistant\n│       ├── sms.py                # SMS (Twilio)\n│       ├── mattermost.py         # Mattermost\n│       ├── dingtalk.py           # DingTalk\n│       ├── feishu.py             # Feishu/Lark\n│       ├── wecom.py              # WeCom (Enterprise WeChat)\n│       ├── webhook.py            # Generic webhook\n│       └── api_server.py         # OpenAI-compatible API server\n│\n├── acp_adapter/              # ACP server (IDE integration)\n│   ├── server.py             # HermesACPAgent class\n│   ├── session.py            # SessionManager\n│   ├── events.py             # Streaming callbacks\n│   ├── permissions.py        # Approval callbacks\n│   └── entry.py              # Entry point\n│\n├── cron/                     # Scheduler\n│   ├── scheduler.py          # tick() — job execution engine\n│   └── jobs.py               # Job storage and CRUD\n│\n├── plugins/                  # Plugin system\n│   └── memory/               # 8 memory provider plugins\n│       ├── openviking/\n│       ├── mem0/\n│       ├── hindsight/\n│       ├── holographic/\n│       ├── honcho/\n│       ├── retaindb/\n│       └── byterover/\n│\n├── environments/             # RL training environments (Atropos)\n│   ├── hermes_base_env.py    # Abstract base RL environment\n│   ├── agent_loop.py         # HermesAgentLoop — rollout execution\n│   ├── tool_context.py       # ToolContext — sandbox for RL\n│   ├── web_research_env.py   # Web research tasks\n│   └── agentic_opd_env.py    # Observation-Prediction-Demo env\n│\n├── skills/                   # 70+ bundled skills across 28 categories\n├── optional-skills/          # Additional optional skills\n├── tests/                    # ~3000 pytest tests\n├── scripts/                  # Install, update, packaging scripts\n├── docker/                   # Docker build files\n├── docs/                     # Documentation source (Docusaurus)\n├── website/                  # Landing page\n├── desktop/                  # Desktop app (Electron, separate repo)\n├── tinker-atropos/           # RL submodule\n│\n├── pyproject.toml            # Python package config\n├── AGENTS.md                 # Developer guide for AI assistants\n├── RELEASE_v0.2.0.md → v0.7.0.md  # Release notes\n└── cli-config.yaml.example   # Example config\n```\n\n**User config directory:** `~/.hermes/`\n\n```\n~/.hermes/\n├── config.yaml           # User settings\n├── .env                  # API keys and secrets\n├── MEMORY.md             # Persistent agent memory\n├── USER.md               # User profile\n├── SOUL.md               # Agent personality/identity\n├── sessions.db           # SQLite session database\n├── skills/               # User-installed skills\n├── skins/                # Custom CLI themes\n├── plugins/              # User plugins\n├── cron/                 # Cron jobs and output\n│   ├── jobs.json\n│   └── output/\n├── cache/                # Image/audio cache\n├── plans/                # Generated plans\n├── profiles/             # Multi-instance profiles\n└── mcp/                  # MCP server configs\n```\n\n---\n\n## 5. Core Architecture\n\n### 5.1 AIAgent Class (run_agent.py)\n\nThe `AIAgent` class is the heart of the system — the core conversation loop that orchestrates LLM calls, tool execution, context management, and response delivery.\n\n**Constructor (~60 parameters):**\n\n```python\nclass AIAgent:\n    def __init__(self,\n        model: str = \"anthropic/claude-opus-4.6\",\n        max_iterations: int = 90,\n        enabled_toolsets: list = None,\n        disabled_toolsets: list = None,\n        quiet_mode: bool = False,\n        save_trajectories: bool = False,\n        platform: str = None,            # \"cli\", \"telegram\", etc.\n        session_id: str = None,\n        session_db: SessionDB = None,\n        skip_context_files: bool = False,\n        skip_memory: bool = False,\n        base_url: str = None,\n        api_key: str = None,\n        provider: str = None,\n        api_mode: str = \"chat_completions\",  # or \"anthropic_messages\" or \"codex_responses\"\n        tool_progress_callback = None,\n        stream_delta_callback = None,\n        thinking_callback = None,\n        status_callback = None,\n        iteration_budget: IterationBudget = None,\n        credential_pool = None,\n        checkpoints_enabled: bool = False,\n        # ... plus provider, routing, callback params\n    )\n```\n\n**Main Methods:**\n\n| Method                                                                          | Returns  | Purpose                                                                            |\n| ------------------------------------------------------------------------------- | -------- | ---------------------------------------------------------------------------------- |\n| `chat(message, stream_callback)`                                                | `str`    | Simple interface — returns final response text                                     |\n| `run_conversation(user_message, system_message, conversation_history, task_id)` | `dict`   | Full interface — returns `{final_response, messages, completed, api_calls, error}` |\n| `_interruptible_api_call(api_kwargs)`                                           | Response | Runs API request in background thread with interrupt support                       |\n| `_interruptible_streaming_api_call(api_kwargs, on_first_delta)`                 | Response | Streaming variant with delta callbacks                                             |\n\n**The Core Agent Loop** (inside `run_conversation()`):\n\n```python\nwhile api_call_count < self.max_iterations and self.iteration_budget.remaining > 0:\n    response = client.chat.completions.create(\n        model=model, messages=messages, tools=tool_schemas\n    )\n    if response.tool_calls:\n        for tool_call in response.tool_calls:\n            result = handle_function_call(tool_call.name, tool_call.args, task_id)\n            messages.append(tool_result_message(result))\n        api_call_count += 1\n    else:\n        return response.content  # Final text response\n```\n\n**Key Behaviors:**\n\n- **Three API modes:** `chat_completions` (OpenAI-compatible), `anthropic_messages` (Anthropic SDK), `codex_responses` (OpenAI Codex)\n- **Parallel tool execution:** Independent tool calls run concurrently via ThreadPoolExecutor (unless they share file paths or are in the never-parallel list)\n- **Interrupt support:** Background threads allow interrupt detection without blocking on HTTP\n- **Error recovery:** Automatic fallback chain (primary → fallback model), retry with exponential backoff, context compression on token overflow\n- **Budget pressure:** Warnings at 70% (caution) and 90% (urgent) of iteration budget\n- **Oversized results:** Tool results >100K chars are saved to temp files with a preview\n- **Stale connection detection:** 90s timeout for streaming, 60s read timeout\n\n**IterationBudget** (thread-safe):\n\n```python\nclass IterationBudget:\n    def consume() -> bool    # Check and consume one iteration\n    def refund()             # Give back iteration (for execute_code turns)\n    @property remaining      # Remaining iterations\n```\n\n---\n\n### 5.2 Tool Orchestration (model_tools.py)\n\nBridges the agent and tool registry — handles discovery, schema generation, and dispatch.\n\n**Key Functions:**\n\n| Function                                                                                | Purpose                                                                   |\n| --------------------------------------------------------------------------------------- | ------------------------------------------------------------------------- |\n| `_discover_tools()`                                                                     | Imports all tool modules (each calls `registry.register()` on import)     |\n| `get_tool_definitions(enabled_toolsets, disabled_toolsets, quiet_mode)`                 | Returns OpenAI-format tool schemas filtered by toolset                    |\n| `handle_function_call(function_name, function_args, task_id, user_task, enabled_tools)` | Main dispatcher — routes calls to registry with arg coercion              |\n| `coerce_tool_args(tool_name, args)`                                                     | Type coercion for LLM-generated arguments (string→int, string→bool, etc.) |\n\n**Tool Discovery Order:**\n\n1. Static tools (web_tools, terminal_tool, file_tools, browser_tool, etc.)\n2. Optional tools (fal_client for image gen, honcho, etc.) — graceful fallback if missing\n3. Plugin-registered tools\n4. MCP server tools (dynamic, via tools/list_changed notifications)\n\n**Special Tool Handling:**\n\n- **Agent-level tools** (todo, memory, session_search, delegate_task): Intercepted by `run_agent.py` before `handle_function_call()`\n- **execute_code**: Passes `enabled_tools` for sandbox tool list\n- **Dynamic schema adjustments**: `browser_navigate` strips web_search reference if tools unavailable\n\n**Async Bridging:**\n\n- Persistent event loops (not `asyncio.run()`) to prevent \"Event loop is closed\" errors\n- Main thread uses shared loop; worker threads get per-thread loops\n- `_run_async()` detects running loop and spins up disposable thread if needed\n\n---\n\n### 5.3 Toolset System (toolsets.py)\n\nProvides flexible tool grouping and composition.\n\n**Core Toolsets:**\n\n| Toolset          | Tools Included                                                                                   |\n| ---------------- | ------------------------------------------------------------------------------------------------ |\n| `web`            | web_search, web_extract, web_crawl                                                               |\n| `terminal`       | terminal                                                                                         |\n| `file`           | read_file, write_file, edit_file, list_files, search_files                                       |\n| `browser`        | browser_navigate, browser_snapshot, browser_click, browser_type, browser_scroll, browser_extract |\n| `vision`         | analyze_image                                                                                    |\n| `image_gen`      | generate_image                                                                                   |\n| `tts`            | text_to_speech                                                                                   |\n| `todo`           | todo_read, todo_write                                                                            |\n| `memory`         | memory_read, memory_write                                                                        |\n| `session_search` | session_search                                                                                   |\n| `delegation`     | delegate_task                                                                                    |\n| `code_execution` | execute_code                                                                                     |\n| `cronjob`        | create_job, list_jobs, delete_job                                                                |\n| `messaging`      | send_message                                                                                     |\n| `homeassistant`  | ha_get_states, ha_call_service, ...                                                              |\n\n**Composite Toolsets:**\n\n- `hermes-cli` — All core tools for CLI platform\n- `hermes-telegram`, `hermes-discord`, etc. — Platform-specific tool sets\n- `hermes-gateway` — Union of all platform tools\n- `debugging` — terminal + file + web\n- `safe` — Everything except terminal\n\n**Resolution:**\n\n```python\nresolve_toolset(name, visited=None) → List[str]\n# Recursively resolves toolset to tool names\n# Handles composition (includes) and cycle detection\n# Special aliases: \"all\" or \"*\" = all tools\n```\n\n---\n\n### 5.4 Tool Registry (tools/registry.py)\n\nSingleton managing all tool schemas and handlers. Circular-import safe — has no tool dependencies.\n\n**ToolEntry** (per-tool metadata):\n\n```python\n@dataclass(slots=True)\nclass ToolEntry:\n    name: str\n    toolset: str\n    schema: dict           # OpenAI-format tool definition\n    handler: Callable      # Sync or async handler function\n    check_fn: Callable     # Returns True if tool is available\n    requires_env: list     # Required environment variables\n    is_async: bool\n    description: str\n    emoji: str\n```\n\n**Key Methods:**\n\n```python\nregistry.register(name, toolset, schema, handler, check_fn, requires_env)\nregistry.get_definitions(tool_names, quiet)  # Returns filtered schemas\nregistry.dispatch(name, args, **kwargs)      # Execute with async bridging\nregistry.deregister(name)                    # Remove (for MCP tool refresh)\nregistry.check_tool_availability()           # Returns (available, unavailable)\n```\n\n---\n\n### 5.5 Session Database (hermes_state.py)\n\nSQLite-based persistent session storage with FTS5 full-text search.\n\n**Schema (v6):**\n\n```sql\n-- Sessions table\nsessions (\n    id TEXT PRIMARY KEY,\n    source TEXT, user_id TEXT, model TEXT, model_config TEXT,\n    system_prompt TEXT, parent_session_id TEXT,\n    started_at TEXT, ended_at TEXT, end_reason TEXT,\n    message_count INTEGER, tool_call_count INTEGER,\n    input_tokens INTEGER, output_tokens INTEGER,\n    cache_read_tokens INTEGER, cache_write_tokens INTEGER, reasoning_tokens INTEGER,\n    estimated_cost_usd REAL, actual_cost_usd REAL,\n    title TEXT  -- UNIQUE INDEX\n)\n\n-- Messages table\nmessages (\n    id INTEGER PRIMARY KEY AUTOINCREMENT,\n    session_id TEXT, role TEXT, content TEXT,\n    tool_call_id TEXT, tool_calls TEXT,  -- JSON\n    tool_name TEXT, timestamp TEXT,\n    token_count INTEGER, finish_reason TEXT,\n    reasoning TEXT, reasoning_details TEXT, codex_reasoning_items TEXT\n)\n\n-- FTS5 virtual table (auto-synced via triggers)\nmessages_fts (content)\n```\n\n**Concurrency Model:**\n\n- WAL (Write-Ahead Logging) for concurrent readers + single writer\n- `BEGIN IMMEDIATE` for write transactions (lock at start, not commit)\n- Jitter retry on lock: 20-150ms random backoff, max 15 retries\n- Periodic WAL checkpoint every 50 writes\n\n**Key Operations:**\n\n- `create_session()`, `end_session()`, `reopen_session()`\n- `add_message()`, `get_messages()`\n- `search_sessions(query)` — FTS5 full-text search\n- `update_token_counts()` — Supports both incremental (CLI) and absolute (gateway) modes\n\n---\n\n### 5.6 Constants & Home Directory (hermes_constants.py)\n\nImport-safe constants module with no circular dependencies.\n\n```python\nget_hermes_home() → Path          # HERMES_HOME env var or ~/.hermes\ndisplay_hermes_home() → str       # User-friendly display: \"~/.hermes\"\nget_optional_skills_dir() → Path  # HERMES_OPTIONAL_SKILLS env var\nparse_reasoning_effort(str) → Dict  # \"high\" → {\"enabled\": True, \"effort\": \"high\"}\n\n# Key constants\nOPENROUTER_BASE_URL = \"https://openrouter.ai/api/v1\"\nNOUS_API_BASE_URL = \"https://inference-api.nousresearch.com/v1\"\nAI_GATEWAY_BASE_URL = \"https://ai-gateway.vercel.sh/v1\"\nVALID_REASONING_EFFORTS = (\"xhigh\", \"high\", \"medium\", \"low\", \"minimal\")\n```\n\n---\n\n## 6. CLI System\n\n### 6.1 Interactive CLI (cli.py)\n\nThe `HermesCLI` class provides the interactive terminal interface.\n\n**Features:**\n\n- **Rich** for banner/panels, **prompt_toolkit** for input with autocomplete\n- **KawaiiSpinner** — animated faces during API calls, `┊` activity feed for tool results\n- Multiline editing with Shift+Enter\n- Slash-command autocomplete\n- Session history with up/down arrow navigation\n- Clipboard image paste (Alt+V / Ctrl+V)\n- Status bar showing model, provider, and token counts\n- Inline diff previews for file write/patch operations\n\n**Configuration Loading:**\n\n```python\nload_cli_config() → dict\n# Loads from ~/.hermes/config.yaml (or ./cli-config.yaml fallback)\n# Merges with hardcoded defaults\n# Expands ${ENV_VAR} references\n# Maps terminal config → env vars\n```\n\n---\n\n### 6.2 CLI Entry Point (hermes_cli/main.py)\n\nAll `hermes` subcommands are dispatched from here:\n\n```\nhermes                    # Default: interactive chat\nhermes chat               # Explicit interactive mode\nhermes gateway start|stop|status|install|uninstall\nhermes setup              # Setup wizard\nhermes model              # Select model/provider\nhermes tools              # Configure tools\nhermes skills             # Manage skills\nhermes config set|get     # Direct config manipulation\nhermes cron list|delete   # Cron job management\nhermes doctor             # Diagnose issues\nhermes sessions browse    # Session picker\nhermes profile create|list|switch|delete|export|import\nhermes mcp serve|add|remove  # MCP management\nhermes acp                # Start ACP server\nhermes update|uninstall|version\n```\n\n**Profile System:**\n\n- `_apply_profile_override()` runs BEFORE any imports to set `HERMES_HOME`\n- Pre-parses `--profile/-p` from argv\n- Allows fully isolated agent instances with separate config, memory, sessions, skills\n\n---\n\n### 6.3 Configuration System (hermes_cli/config.py)\n\n**Key Configuration Sections:**\n\n```yaml\nmodel: \"anthropic/claude-opus-4.6\" # or dict with provider/base_url/api_key\nproviders: {} # Provider-specific configs\nfallback_providers: [] # Ordered failover list\ncredential_pool: {} # Multiple API keys per provider\n\nagent:\n  max_turns: 90\n  gateway_timeout: 1800\n  tool_use_enforcement: \"auto\"\n\nterminal:\n  backend: \"local\" # local|docker|modal|daytona|ssh|singularity\n  timeout: 180\n  persistent_shell: true\n  docker_image: \"nikolaik/python-nodejs:...\"\n\ncompression:\n  enabled: true\n  threshold: 0.50 # Compress when 50% of context used\n  target_ratio: 0.20 # Summary = 20% of compressed content\n  protect_last_n: 20\n\nauxiliary:\n  vision: { provider, model }\n  web_extract: { provider, model }\n  compression: { provider, model }\n\nmemory:\n  memory_enabled: true\n  provider: \"\" # \"\" | \"honcho\" | \"mem0\" | etc.\n  memory_char_limit: 2200\n\ndisplay:\n  personality: \"kawaii\"\n  show_reasoning: false\n  inline_diffs: true\n  skin: \"default\"\n  streaming: true\n\ntts:\n  provider: \"edge\" # edge|elevenlabs|openai|neutts\n\nstt:\n  enabled: true\n  provider: \"local\" # local|groq|openai\n\nprivacy:\n  redact_pii: false\n\nmcp_servers: {} # MCP server configurations\n\nskills:\n  external_dirs: [] # Additional skill directories\n\napprovals:\n  mode: \"smart\" # smart|always|off\n```\n\n**Config Files:**\n\n- `~/.hermes/config.yaml` — User settings (authoritative)\n- `~/.hermes/.env` — API keys and secrets\n- Config version migration system (currently v5)\n\n---\n\n### 6.4 Slash Command Registry (hermes_cli/commands.py)\n\nAll slash commands defined centrally in `COMMAND_REGISTRY`:\n\n```python\nCommandDef(name, description, category, aliases, args_hint, cli_only, gateway_only)\n```\n\n**Derived automatically by:**\n\n- CLI `process_command()` — dispatch on canonical name\n- Gateway dispatch + help\n- Telegram BotCommand menu\n- Slack `/hermes` subcommands\n- Autocomplete + help text\n\n**Key Commands:**\n\n| Command        | Aliases    | Description                   |\n| -------------- | ---------- | ----------------------------- |\n| `/new`         | `/reset`   | Start fresh conversation      |\n| `/model`       |            | Show/switch model             |\n| `/personality` |            | Set agent personality         |\n| `/retry`       |            | Retry last turn               |\n| `/undo`        |            | Remove last turn              |\n| `/compress`    | `/compact` | Compress context              |\n| `/usage`       | `/cost`    | Show token usage              |\n| `/insights`    |            | Usage analytics               |\n| `/skills`      |            | Browse/install skills         |\n| `/background`  | `/bg`      | Manage background processes   |\n| `/plan`        |            | Generate implementation plan  |\n| `/rollback`    |            | Restore filesystem checkpoint |\n| `/verbose`     |            | Toggle debug output           |\n| `/reasoning`   |            | Set reasoning effort          |\n| `/yolo`        |            | Toggle approval bypass        |\n| `/btw`         |            | Ephemeral side question       |\n| `/stop`        |            | Kill current agent run        |\n| `/queue`       |            | Queue next prompt             |\n| `/browser`     |            | Interactive browser session   |\n| `/history`     | `/resume`  | Session browser               |\n| `/skin`        |            | Switch CLI theme              |\n\n---\n\n### 6.5 Setup Wizard (hermes_cli/setup.py)\n\nModular interactive wizard with independent sections:\n\n1. **Model & Provider** — Select AI provider, enter API keys, choose model\n2. **Terminal Backend** — Choose execution environment\n3. **Agent Settings** — Max iterations, compression, session policies\n4. **Messaging Platforms** — Configure Telegram, Discord, Slack, etc.\n5. **Tools** — TTS, STT, web search, image generation, browser\n\nFeatures:\n\n- Live credential validation\n- Real-time model list fetching from provider APIs\n- Automatic OpenClaw migration detection\n- Atomic config file writes\n\n---\n\n### 6.6 Model Catalog (hermes_cli/models.py)\n\nProvider-specific model lists:\n\n```python\n_PROVIDER_MODELS = {\n    \"nous\": [\"anthropic/claude-opus-4.6\", \"anthropic/claude-sonnet-4.6\", ...],  # 25+\n    \"openrouter\": [\"anthropic/claude-opus-4.6\", \"google/gemini-3-flash\", ...],  # 30+\n    \"anthropic\": [\"claude-opus-4-6\", \"claude-sonnet-4-6\", ...],\n    \"openai\": [\"gpt-5\", \"gpt-5.4-mini\", \"gpt-4.1\", \"gpt-4o\", ...],\n    \"copilot\": [\"gpt-5.4\", \"gpt-5.4-mini\", \"gpt-5.3-codex\", ...],\n    \"huggingface\": [...],\n    \"minimax\": [...],\n    \"kimi-coding\": [...],\n    \"alibaba\": [...],\n    \"deepseek\": [...],\n    # ... more providers\n}\n```\n\nFeatures:\n\n- Dynamic fetching via provider `/models` endpoints\n- Curated lists used when live probe returns fewer models\n- Fuzzy matching for typo correction\n- Validation against provider catalog\n\n---\n\n### 6.7 Skin/Theme Engine (hermes_cli/skin_engine.py)\n\nData-driven CLI visual customization — no code changes needed.\n\n**Customizable Elements:**\n\n| Element                          | Key                      | Used By           |\n| -------------------------------- | ------------------------ | ----------------- |\n| Banner border/title/accent       | `colors.*`               | banner.py         |\n| Response box border              | `colors.response_border` | cli.py            |\n| Spinner faces (waiting/thinking) | `spinner.*`              | display.py        |\n| Spinner verbs/wings              | `spinner.*`              | display.py        |\n| Tool output prefix               | `tool_prefix`            | display.py        |\n| Per-tool emojis                  | `tool_emojis`            | display.py        |\n| Agent name/welcome/prompt        | `branding.*`             | banner.py, cli.py |\n\n**Built-in Skins:** default, ares, mono, slate, poseidon, sisyphus, charizard\n\n**User Skins:** Drop `~/.hermes/skins/<name>.yaml` and activate with `/skin <name>`\n\n---\n\n## 7. Tool System\n\n### 7.1 Terminal Tool (tools/terminal_tool.py)\n\nShell command execution across multiple backends.\n\n```python\ndef terminal_tool(\n    command: str,\n    background: bool = False,\n    timeout: Optional[int] = None,\n    task_id: Optional[str] = None,\n    force: bool = False,        # Skip approval for dangerous commands\n    workdir: Optional[str] = None,\n    check_interval: Optional[int] = None,  # Background task polling\n    pty: bool = False,\n) -> str  # JSON result\n```\n\n**Features:**\n\n- Multi-backend: Selects based on `TERMINAL_ENV` (local/docker/ssh/modal/daytona/singularity)\n- Per-task_id sandboxes with thread-safe creation locks\n- Dangerous command routing through approval system\n- Background task support with file-based IPC\n- Interrupt handling — polls `is_interrupted()` during execution\n- Auto-cleanup daemon thread for idle environments (>300s)\n- Disk usage warnings at configurable threshold\n\n---\n\n### 7.2 File Tools (tools/file_tools.py)\n\nSafe file operations with size guards and sensitive path protection.\n\n- `read_file_tool(path, offset, limit)` — Read with pagination (default 100K char limit)\n- `write_file_tool(path, content)` — Write with approval for sensitive paths\n- `edit_file_tool(path, old_text, new_text)` — String replacement editing\n- `list_files_tool(path)` — Directory listing\n- `search_files_tool(pattern, path)` — Glob/regex file search\n\n**Safety:**\n\n- Device path blocklist (`/dev/zero`, `/dev/stdin`, etc.)\n- Read dedup tracking — returns stub on re-read if mtime unchanged\n- Sensitive path blocking: `/etc/`, `/boot/`, `~/.ssh` without approval\n- Prompt injection protection for known dangerous paths\n\n---\n\n### 7.3 Web Tools (tools/web_tools.py)\n\nWeb search and content extraction.\n\n- `web_search_tool(query, limit)` — Search via configurable backend\n- `web_extract_tool(urls, format)` — Extract content from URLs\n- `web_crawl_tool(url, instructions)` — LLM-guided web crawling\n\n**Backends:** Firecrawl, Parallel Web, Tavily, Exa, DuckDuckGo\n\n- Fallback detection by highest-priority available API key\n- LLM processing via auxiliary client (Gemini 3 Flash) for intelligent extraction\n- SSRF protection and URL safety checks\n\n---\n\n### 7.4 Browser Tool (tools/browser_tool.py)\n\nHeadless browser automation with accessibility tree.\n\n```python\nbrowser_navigate(url, task_id) → str\nbrowser_snapshot(task_id, max_chars) → str\nbrowser_click(ref, task_id) → str      # Element refs like @e1, @e2\nbrowser_type(ref, text, task_id) → str\nbrowser_scroll(ref, direction, task_id) → str\nbrowser_extract(task, max_chars, task_id) → str\n```\n\n**Backends:**\n\n- **Local:** Headless Chromium via `agent-browser` CLI\n- **Cloud:** Browserbase (stealth, proxies, CAPTCHA solving)\n- **Camofox:** Anti-detection browser using Camoufox\n\n**Features:**\n\n- Accessibility tree for text-based snapshots (no vision required)\n- Session isolation per task_id with inactivity cleanup\n- API key detection in URLs (prevents exfiltration)\n- SSRF protection for private/internal addresses\n\n---\n\n### 7.5 Delegate Tool (tools/delegate_tool.py)\n\nSpawn isolated subagents for parallel workstreams.\n\n```python\ndef delegate_task(\n    goal: str,\n    context: str = None,\n    toolsets: List[str] = None,       # Default: [\"terminal\", \"file\", \"web\"]\n    tasks: List[Dict] = None,          # Batch mode: up to 3 concurrent\n    max_iterations: int = 50,\n    parent_agent = None,\n) → str\n```\n\n**Isolation:**\n\n- Fresh conversation (no parent history)\n- Own task_id (separate terminal session, file ops)\n- Restricted toolset (configurable, blocked tools always stripped)\n- Focused system prompt from goal + context\n- Parent only sees delegation call and summary result\n\n**Blocked Tools:** delegate_task, clarify, memory, send_message, execute_code (no recursion, no user interaction)\n\n**Constraints:** MAX_DEPTH=2, MAX_CONCURRENT_CHILDREN=3\n\n---\n\n### 7.6 MCP Tool (tools/mcp_tool.py)\n\nModel Context Protocol client integration.\n\n**Configuration (config.yaml):**\n\n```yaml\nmcp_servers:\n  filesystem:\n    command: \"npx\"\n    args: [\"-y\", \"@modelcontextprotocol/server-filesystem\", \"/tmp\"]\n    timeout: 120\n  remote_api:\n    url: \"https://my-mcp-server.example.com/mcp\"\n    headers:\n      Authorization: \"Bearer sk-...\"\n    sampling:\n      enabled: true\n      model: \"gemini-3-flash\"\n```\n\n**Features:**\n\n- **Transports:** Stdio (local processes) and HTTP/StreamableHTTP (remote)\n- **Dynamic tool discovery:** Listens for `tools/list_changed` notifications\n- **Sampling support:** MCP servers can request LLM completions\n- **Automatic reconnection** with exponential backoff (5 retries)\n- Dedicated background event loop in daemon thread\n- Thread-safe via lock protecting server dict\n\n---\n\n### 7.7 Approval System (tools/approval.py)\n\nDangerous command detection and approval flow.\n\n```python\ndetect_dangerous_command(command: str) → (is_dangerous, pattern_key, description)\n```\n\n**106 patterns covering:**\n\n- Destructive operations (`rm -r /`, `mkfs`, `dd if=`)\n- Privilege escalation (`chmod 777`, `chown -R root`)\n- SQL injection (`DROP TABLE`, `DELETE FROM` without WHERE)\n- System targeting (`/etc/` writes, `systemctl stop`, fork bombs)\n- Shell injection (pipe to sh, wget to sh, process substitution)\n- Network exfiltration (curl with embedded API keys)\n- Secret access (`cat ~/.env`, `cat ~/.netrc`)\n\n**Normalization:** Strips ANSI escapes, null bytes, normalizes Unicode\n\n**Approval Modes:**\n\n- `smart` — Learns which commands are safe based on user decisions\n- `always` — Always ask for dangerous commands\n- `off` — Never ask (or `/yolo` toggle)\n\n---\n\n### 7.8 Terminal Backends (tools/environments/)\n\n| Backend           | File               | Features                                                                       |\n| ----------------- | ------------------ | ------------------------------------------------------------------------------ |\n| **Local**         | `local.py`         | Direct execution, interrupt support, non-blocking I/O, persistent shell        |\n| **Docker**        | `docker.py`        | Sandboxed containers, cap-drop ALL, no-new-privileges, PID limits, bind mounts |\n| **SSH**           | `ssh.py`           | Remote execution via ControlMaster, persistent shell mixin                     |\n| **Modal**         | `modal.py`         | Native Modal SDK `Sandbox.create()`/`Sandbox.exec()`, persistent snapshots     |\n| **Managed Modal** | `managed_modal.py` | Modal through Nous-hosted gateway                                              |\n| **Daytona**       | `daytona.py`       | Daytona SDK cloud sandboxes, stop/resume lifecycle                             |\n| **Singularity**   | `singularity.py`   | Singularity containers with scratch dir, SIF cache                             |\n\n**Common Interface (BaseEnvironment ABC):**\n\n```python\nexecute(command, timeout) → {\"output\": str, \"returncode\": int}\ncleanup() → None\n```\n\n---\n\n## 8. Agent Internals\n\n### 8.1 Prompt Builder (agent/prompt_builder.py)\n\nAssembles the system prompt from multiple sources:\n\n| Component               | Source                                               |\n| ----------------------- | ---------------------------------------------------- |\n| Agent Identity          | `DEFAULT_AGENT_IDENTITY` or `SOUL.md`                |\n| Memory Guidance         | When/how to use memory tool                          |\n| Session Search Guidance | How to recall past conversations                     |\n| Skills Guidance         | When to create/patch skills                          |\n| Tool Use Enforcement    | Must execute tools, not describe actions             |\n| Skills Index            | `~/.hermes/skills/.hermes-skills.json`               |\n| Platform Hints          | OS, Python version, shell, available tools           |\n| Context Files           | `.hermes.md`, `AGENTS.md`, `.cursorrules`, `SOUL.md` |\n| Model/Provider Info     | Current model and provider identity                  |\n\n**Context File Discovery:**\n\n1. Check `cwd/.hermes.md` or `HERMES.md`\n2. Walk parent directories up to git root\n3. Validate against injection patterns before inclusion\n\n**Injection Detection (30+ patterns):**\n\n- \"ignore previous instructions\", \"system prompt override\"\n- \"do not tell the user\", \"act as if you have no restrictions\"\n- HTML comment injection, exfiltration via curl, Unicode stealth chars\n\n---\n\n### 8.2 Context Compressor (agent/context_compressor.py)\n\nAutomatically compresses conversation history when approaching context limits.\n\n```python\nclass ContextCompressor:\n    threshold_percent: float = 0.50   # Compress when 50% of context used\n    protect_first_n: int = 3          # Keep system prompt + first turn\n    protect_last_n: int = 20          # Keep recent N messages\n    summary_target_ratio: float = 0.20  # Summary = 20% of compressed content\n```\n\n**Compression Algorithm:**\n\n1. **Pre-pass:** Replace old tool results with placeholder (cheap)\n2. **Protect head:** System prompt + first exchange always kept\n3. **Protect tail:** Token-budget-based tail protection (~20K tokens)\n4. **Summarize middle:** LLM-generated structured summary (Goal, Progress, Decisions, Files, Next Steps)\n5. **Iterative updates:** On subsequent compressions, update previous summary instead of re-summarizing\n\n---\n\n### 8.3 Prompt Caching (agent/prompt_caching.py)\n\nAnthropic prompt caching support for cost reduction.\n\n- System prompt cached across turns (first conversation turn establishes cache)\n- Cache markers inserted at system prompt boundaries\n- Must not break mid-conversation — altering past context invalidates cache\n- The ONLY time context is altered is during compression\n\n**Critical Rule:** Do NOT implement changes that would alter past context, change toolsets, reload memories, or rebuild system prompts mid-conversation.\n\n---\n\n### 8.4 Auxiliary Client (agent/auxiliary_client.py)\n\nSeparate LLM client for non-primary tasks:\n\n- **Vision analysis** — Image description and analysis\n- **Web extraction** — Content summarization\n- **Context compression** — Generating conversation summaries\n- **Session search** — Summarizing search results\n- **MCP sampling** — Serving server-initiated LLM requests\n\nConfigured per-task via `auxiliary` section in config.yaml.\n\n---\n\n### 8.5 Display & Spinner (agent/display.py)\n\n- **KawaiiSpinner** — Animated face characters during API calls\n- **Tool preview formatting** — `┊` prefixed activity feed for tool execution\n- **Inline diff previews** — Shows unified diffs for write/patch operations\n- Respects active skin for colors, emojis, and branding\n\n---\n\n### 8.6 Skill Commands (agent/skill_commands.py)\n\nShared skill invocation for CLI and gateway:\n\n- Skills loaded from `~/.hermes/skills/` and external directories\n- Injected as **user message** (not system prompt) to preserve prompt caching\n- `/plan` command generates implementation plans stored in `.hermes/plans/`\n- Skill content includes setup instructions, tool options, usage examples\n\n---\n\n## 9. Messaging Gateway\n\n### 9.1 GatewayRunner (gateway/run.py)\n\nMain controller managing all platform adapters and routing messages.\n\n**Key Attributes:**\n\n- `adapters: Dict[Platform, BasePlatformAdapter]` — Active platform instances\n- `session_store: SessionStore` — Conversation persistence\n- `_running_agents: Dict[str, AIAgent]` — Per-session cached agents (preserves prompt caching)\n- `_pending_approvals: Dict[str, Dict]` — Dangerous command approval tracking\n- `pairing_store: PairingStore` — DM code-based user authorization\n\n**Message Flow:**\n\n1. Platform adapter queues `MessageEvent`\n2. GatewayRunner dequeues, calls `_handle_message()`\n3. Slash command? → dispatch to handler\n4. Regular message? → `_handle_message_with_agent\n\n<!-- File: .agents/skills/lat-md/SKILL.md (Tokens: ~1094 | Category: anthropic-skill) -->\n---\nname: lat-md\ndescription: >-\n  Writing and maintaining lat.md documentation files — structured markdown that\n  describes a project's architecture, design decisions, and test specs. Use when\n  creating, editing, or reviewing files in the lat.md/ directory.\n---\n\n# lat.md Authoring Guide\n\nThis skill covers the syntax, structure rules, and conventions for writing `lat.md/` files. Load it whenever you need to create or edit sections in the `lat.md/` directory.\n\n## What belongs in lat.md\n\n`lat.md/` files describe **what** the project does and **why** — domain concepts, key design decisions, business logic, and test specifications. They do NOT duplicate source code. Think of each section as an anchor that source code references back to.\n\nGood candidates for sections:\n- Architecture decisions and their rationale\n- Domain concepts and business rules\n- API contracts and protocols\n- Test specifications (what is tested and why)\n- Non-obvious constraints or invariants\n\nBad candidates:\n- Step-by-step code walkthroughs (the code itself is the walkthrough)\n- Auto-generated API docs (use tools for that)\n- Temporary notes or TODOs\n\n## Section structure\n\nEvery section **must** have a leading paragraph — at least one sentence immediately after the heading, before any child headings or other block content.\n\nThe first paragraph must be ≤250 characters (excluding `[[wiki link]]` content). This paragraph is the section's identity — it appears in search results, command output, and RAG context.\n\n```markdown\n# Good Section\n\nBrief overview of what this section documents and why it matters.\n\nMore detail can go in subsequent paragraphs, code blocks, or lists.\n\n## Child heading\n\nDetails about this child topic.\n```\n\n```markdown\n# Bad Section\n\n## Child heading\n\nThis is invalid — \"Bad Section\" has no leading paragraph.\n```\n\n`lat check` enforces this rule.\n\n## Section IDs\n\nSections are addressed by file path and heading chain:\n\n- **Full form**: `lat.md/path/to/file#Heading#SubHeading`\n- **Short form**: `file#Heading#SubHeading` (when the file stem is unique)\n\nExamples: `lat.md/tests/search#RAG Replay Tests`, `cli#init`, `parser#Wiki Links`.\n\n## Wiki links\n\nCross-reference other sections or source code with `[[target]]` or `[[target|alias]]`.\n\n### Section links\n\n```markdown\nSee [[cli#init]] for setup details.\nThe parser validates [[parser#Wiki Links|wiki link syntax]].\n```\n\n### Source code links\n\nReference functions, classes, constants, and methods in source files:\n\n```markdown\n[[src/config.ts#getConfigDir]]          — function\n[[src/server.ts#App#listen]]            — class method\n[[lib/utils.py#parse_args]]             — Python function\n[[src/lib.rs#Greeter#greet]]            — Rust impl method\n[[src/app.go#Greeter#Greet]]            — Go method\n[[src/app.h#Greeter]]                   — C struct\n```\n\n`lat check` validates that all targets exist.\n\n## Code refs\n\nTie source code back to `lat.md/` sections with `@lat:` comments:\n\n```typescript\n// @lat: [[cli#init]]\nexport function init() { ... }\n```\n\n```python\n# @lat: [[cli#init]]\ndef init():\n    ...\n```\n\nSupported comment styles: `//` (JS/TS/Rust/Go/C) and `#` (Python).\n\nPlace one `@lat:` comment per section, at the relevant code — not at the top of the file.\n\n## Test specs\n\nDescribe tests as sections in `lat.md/` files. Add frontmatter to require that every leaf section has a matching `@lat:` comment in test code:\n\n```markdown\n---\nlat:\n  require-code-mention: true\n---\n# Tests\n\nAuthentication test specifications.\n\n## User login\n\nVerify credential validation and error handling.\n\n### Rejects expired tokens\n\nTokens past their expiry timestamp are rejected with 401, even if otherwise valid.\n\n### Handles missing password\n\nLogin request without a password field returns 400 with a descriptive error.\n```\n\nEach test references its spec:\n\n```python\n# @lat: [[tests#User login#Rejects expired tokens]]\ndef test_rejects_expired_tokens():\n    ...\n```\n\nRules:\n- Every leaf section under `require-code-mention: true` must be referenced by exactly one `@lat:` comment\n- Every section MUST have a description — at least one sentence explaining what the test verifies and why\n- `lat check` flags unreferenced specs and dangling code refs\n\n## Frontmatter\n\nOptional YAML frontmatter at the top of `lat.md/` files:\n\n```yaml\n---\nlat:\n  require-code-mention: true\n---\n```\n\nCurrently the only supported field is `require-code-mention` for test spec enforcement.\n\n## Validation\n\nAlways run `lat check` after editing `lat.md/` files. It validates:\n- All wiki links point to existing sections or source code symbols\n- All `@lat:` code refs point to existing sections\n- Every section has a leading paragraph (≤250 chars)\n- All `require-code-mention` leaf sections are referenced in code\n\n<!-- File: .agents/skills/typescript-expert/references/tsconfig-strict.json (Tokens: ~525 | Category: anthropic-skill) -->\n{\n  \"$schema\": \"https://json.schemastore.org/tsconfig\",\n  \"display\": \"Strict TypeScript 5.x\",\n  \"compilerOptions\": {\n    // =========================================================================\n    // STRICTNESS (Maximum Type Safety)\n    // =========================================================================\n    \"strict\": true,\n    \"noUncheckedIndexedAccess\": true,\n    \"noImplicitOverride\": true,\n    \"noPropertyAccessFromIndexSignature\": true,\n    \"exactOptionalPropertyTypes\": true,\n    \"noFallthroughCasesInSwitch\": true,\n    \"forceConsistentCasingInFileNames\": true,\n    // =========================================================================\n    // MODULE SYSTEM (Modern ESM)\n    // =========================================================================\n    \"module\": \"ESNext\",\n    \"moduleResolution\": \"bundler\",\n    \"resolveJsonModule\": true,\n    \"esModuleInterop\": true,\n    \"allowSyntheticDefaultImports\": true,\n    \"isolatedModules\": true,\n    \"verbatimModuleSyntax\": true,\n    // =========================================================================\n    // OUTPUT\n    // =========================================================================\n    \"target\": \"ES2022\",\n    \"lib\": [\"ES2022\", \"DOM\", \"DOM.Iterable\"],\n    \"declaration\": true,\n    \"declarationMap\": true,\n    \"sourceMap\": true,\n    // =========================================================================\n    // PERFORMANCE\n    // =========================================================================\n    \"skipLibCheck\": true,\n    \"incremental\": true,\n    // =========================================================================\n    // PATH ALIASES\n    // =========================================================================\n    \"baseUrl\": \".\",\n    \"paths\": {\n      \"@/*\": [\"./src/*\"],\n      \"@/components/*\": [\"./src/components/*\"],\n      \"@/lib/*\": [\"./src/lib/*\"],\n      \"@/types/*\": [\"./src/types/*\"],\n      \"@/utils/*\": [\"./src/utils/*\"]\n    },\n    // =========================================================================\n    // JSX (for React projects)\n    // =========================================================================\n    // \"jsx\": \"react-jsx\",\n    // =========================================================================\n    // EMIT\n    // =========================================================================\n    \"noEmit\": true // Let bundler handle emit\n    // \"outDir\": \"./dist\",\n    // \"rootDir\": \"./src\",\n    // =========================================================================\n    // DECORATORS (if needed)\n    // =========================================================================\n    // \"experimentalDecorators\": true,\n    // \"emitDecoratorMetadata\": true\n  },\n  \"include\": [\"src/**/*.ts\", \"src/**/*.tsx\", \"src/**/*.d.ts\"],\n  \"exclude\": [\n    \"node_modules\",\n    \"dist\",\n    \"build\",\n    \"coverage\",\n    \"**/*.test.ts\",\n    \"**/*.spec.ts\"\n  ]\n}\n\n<!-- File: .agents/skills/typescript-expert/references/typescript-cheatsheet.md (Tokens: ~1970 | Category: anthropic-skill) -->\n# TypeScript Cheatsheet\n\n## Type Basics\n\n```typescript\n// Primitives\nconst name: string = \"John\";\nconst age: number = 30;\nconst isActive: boolean = true;\nconst nothing: null = null;\nconst notDefined: undefined = undefined;\n\n// Arrays\nconst numbers: number[] = [1, 2, 3];\nconst strings: Array<string> = [\"a\", \"b\", \"c\"];\n\n// Tuple\nconst tuple: [string, number] = [\"hello\", 42];\n\n// Object\nconst user: { name: string; age: number } = { name: \"John\", age: 30 };\n\n// Union\nconst value: string | number = \"hello\";\n\n// Literal\nconst direction: \"up\" | \"down\" | \"left\" | \"right\" = \"up\";\n\n// Any vs Unknown\nconst anyValue: any = \"anything\"; // ❌ Avoid\nconst unknownValue: unknown = \"safe\"; // ✅ Prefer, requires narrowing\n```\n\n## Type Aliases & Interfaces\n\n```typescript\n// Type Alias\ntype Point = {\n  x: number;\n  y: number;\n};\n\n// Interface (preferred for objects)\ninterface User {\n  id: string;\n  name: string;\n  email?: string; // Optional\n  readonly createdAt: Date; // Readonly\n}\n\n// Extending\ninterface Admin extends User {\n  permissions: string[];\n}\n\n// Intersection\ntype AdminUser = User & { permissions: string[] };\n```\n\n## Generics\n\n```typescript\n// Generic function\nfunction identity<T>(value: T): T {\n  return value;\n}\n\n// Generic with constraint\nfunction getLength<T extends { length: number }>(item: T): number {\n  return item.length;\n}\n\n// Generic interface\ninterface ApiResponse<T> {\n  data: T;\n  status: number;\n  message: string;\n}\n\n// Generic with default\ntype Container<T = string> = {\n  value: T;\n};\n\n// Multiple generics\nfunction merge<T, U>(obj1: T, obj2: U): T & U {\n  return { ...obj1, ...obj2 };\n}\n```\n\n## Utility Types\n\n```typescript\ninterface User {\n  id: string;\n  name: string;\n  email: string;\n  age: number;\n}\n\n// Partial - all optional\ntype PartialUser = Partial<User>;\n\n// Required - all required\ntype RequiredUser = Required<User>;\n\n// Readonly - all readonly\ntype ReadonlyUser = Readonly<User>;\n\n// Pick - select properties\ntype UserName = Pick<User, \"id\" | \"name\">;\n\n// Omit - exclude properties\ntype UserWithoutEmail = Omit<User, \"email\">;\n\n// Record - key-value map\ntype UserMap = Record<string, User>;\n\n// Extract - extract from union\ntype StringOrNumber = string | number | boolean;\ntype OnlyStrings = Extract<StringOrNumber, string>;\n\n// Exclude - exclude from union\ntype NotString = Exclude<StringOrNumber, string>;\n\n// NonNullable - remove null/undefined\ntype MaybeString = string | null | undefined;\ntype DefinitelyString = NonNullable<MaybeString>;\n\n// ReturnType - get function return type\nfunction getUser() {\n  return { name: \"John\" };\n}\ntype UserReturn = ReturnType<typeof getUser>;\n\n// Parameters - get function parameters\ntype GetUserParams = Parameters<typeof getUser>;\n\n// Awaited - unwrap Promise\ntype ResolvedUser = Awaited<Promise<User>>;\n```\n\n## Conditional Types\n\n```typescript\n// Basic conditional\ntype IsString<T> = T extends string ? true : false;\n\n// Infer keyword\ntype UnwrapPromise<T> = T extends Promise<infer U> ? U : T;\n\n// Distributive conditional\ntype ToArray<T> = T extends any ? T[] : never;\ntype Result = ToArray<string | number>; // string[] | number[]\n\n// NonDistributive\ntype ToArrayNonDist<T> = [T] extends [any] ? T[] : never;\n```\n\n## Template Literal Types\n\n```typescript\ntype Color = \"red\" | \"green\" | \"blue\";\ntype Size = \"small\" | \"medium\" | \"large\";\n\n// Combine\ntype ColorSize = `${Color}-${Size}`;\n// 'red-small' | 'red-medium' | 'red-large' | ...\n\n// Event handlers\ntype EventName = \"click\" | \"focus\" | \"blur\";\ntype EventHandler = `on${Capitalize<EventName>}`;\n// 'onClick' | 'onFocus' | 'onBlur'\n```\n\n## Mapped Types\n\n```typescript\n// Basic mapped type\ntype Optional<T> = {\n  [K in keyof T]?: T[K];\n};\n\n// With key remapping\ntype Getters<T> = {\n  [K in keyof T as `get${Capitalize<string & K>}`]: () => T[K];\n};\n\n// Filter keys\ntype OnlyStrings<T> = {\n  [K in keyof T as T[K] extends string ? K : never]: T[K];\n};\n```\n\n## Type Guards\n\n```typescript\n// typeof guard\nfunction process(value: string | number) {\n  if (typeof value === \"string\") {\n    return value.toUpperCase(); // string\n  }\n  return value.toFixed(2); // number\n}\n\n// instanceof guard\nclass Dog {\n  bark() {}\n}\nclass Cat {\n  meow() {}\n}\n\nfunction makeSound(animal: Dog | Cat) {\n  if (animal instanceof Dog) {\n    animal.bark();\n  } else {\n    animal.meow();\n  }\n}\n\n// in guard\ninterface Bird {\n  fly(): void;\n}\ninterface Fish {\n  swim(): void;\n}\n\nfunction move(animal: Bird | Fish) {\n  if (\"fly\" in animal) {\n    animal.fly();\n  } else {\n    animal.swim();\n  }\n}\n\n// Custom type guard\nfunction isString(value: unknown): value is string {\n  return typeof value === \"string\";\n}\n\n// Assertion function\nfunction assertIsString(value: unknown): asserts value is string {\n  if (typeof value !== \"string\") {\n    throw new Error(\"Not a string\");\n  }\n}\n```\n\n## Discriminated Unions\n\n```typescript\n// With type discriminant\ntype Success<T> = { type: \"success\"; data: T };\ntype Error = { type: \"error\"; message: string };\ntype Loading = { type: \"loading\" };\n\ntype State<T> = Success<T> | Error | Loading;\n\nfunction handle<T>(state: State<T>) {\n  switch (state.type) {\n    case \"success\":\n      return state.data; // T\n    case \"error\":\n      return state.message; // string\n    case \"loading\":\n      return null;\n  }\n}\n\n// Exhaustive check\nfunction assertNever(value: never): never {\n  throw new Error(`Unexpected value: ${value}`);\n}\n```\n\n## Branded Types\n\n```typescript\n// Create branded type\ntype Brand<K, T> = K & { __brand: T };\n\ntype UserId = Brand<string, \"UserId\">;\ntype OrderId = Brand<string, \"OrderId\">;\n\n// Constructor functions\nfunction createUserId(id: string): UserId {\n  return id as UserId;\n}\n\nfunction createOrderId(id: string): OrderId {\n  return id as OrderId;\n}\n\n// Usage - prevents mixing\nfunction getOrder(orderId: OrderId, userId: UserId) {}\n\nconst userId = createUserId(\"user-123\");\nconst orderId = createOrderId(\"order-456\");\n\ngetOrder(orderId, userId); // ✅ OK\n// getOrder(userId, orderId)  // ❌ Error - types don't match\n```\n\n## Module Declarations\n\n```typescript\n// Declare module for untyped package\ndeclare module \"untyped-package\" {\n  export function doSomething(): void;\n  export const value: string;\n}\n\n// Augment existing module\ndeclare module \"express\" {\n  interface Request {\n    user?: { id: string };\n  }\n}\n\n// Declare global\ndeclare global {\n  interface Window {\n    myGlobal: string;\n  }\n}\n```\n\n## TSConfig Essentials\n\n```json\n{\n  \"compilerOptions\": {\n    // Strictness\n    \"strict\": true,\n    \"noUncheckedIndexedAccess\": true,\n    \"noImplicitOverride\": true,\n\n    // Modules\n    \"module\": \"ESNext\",\n    \"moduleResolution\": \"bundler\",\n    \"esModuleInterop\": true,\n\n    // Output\n    \"target\": \"ES2022\",\n    \"lib\": [\"ES2022\", \"DOM\"],\n\n    // Performance\n    \"skipLibCheck\": true,\n    \"incremental\": true,\n\n    // Paths\n    \"baseUrl\": \".\",\n    \"paths\": {\n      \"@/*\": [\"./src/*\"]\n    }\n  }\n}\n```\n\n## Best Practices\n\n```typescript\n// ✅ Prefer interface for objects\ninterface User {\n  name: string;\n}\n\n// ✅ Use const assertions\nconst routes = [\"home\", \"about\"] as const;\n\n// ✅ Use satisfies for validation\nconst config = {\n  api: \"https://api.example.com\",\n} satisfies Record<string, string>;\n\n// ✅ Use unknown over any\nfunction parse(input: unknown) {\n  if (typeof input === \"string\") {\n    return JSON.parse(input);\n  }\n}\n\n// ✅ Explicit return types for public APIs\nexport function getUser(id: string): User | null {\n  // ...\n}\n\n// ❌ Avoid\nconst data: any = fetchData();\ndata.anything.goes.wrong; // No type safety\n```\n\n<!-- File: .agents/skills/typescript-expert/SKILL.md (Tokens: ~3542 | Category: anthropic-skill) -->\n---\nname: typescript-expert\ndescription: TypeScript and JavaScript expert with deep knowledge of type-level programming, performance optimization, monorepo management, migration strategies, and modern tooling.\ncategory: framework\nrisk: critical\nsource: community\ndate_added: \"2026-02-27\"\n---\n\n# TypeScript Expert\n\nYou are an advanced TypeScript expert with deep, practical knowledge of type-level programming, performance optimization, and real-world problem solving based on current best practices.\n\n## When invoked:\n\n0. If the issue requires ultra-specific expertise, recommend switching and stop:\n   - Deep webpack/vite/rollup bundler internals → typescript-build-expert\n   - Complex ESM/CJS migration or circular dependency analysis → typescript-module-expert\n   - Type performance profiling or compiler internals → typescript-type-expert\n\n   Example to output:\n   \"This requires deep bundler expertise. Please invoke: 'Use the typescript-build-expert subagent.' Stopping here.\"\n\n1. Analyze project setup comprehensively:\n\n   **Use internal tools first (Read, Grep, Glob) for better performance. Shell commands are fallbacks.**\n\n   ```bash\n   # Core versions and configuration\n   npx tsc --version\n   node -v\n   # Detect tooling ecosystem (prefer parsing package.json)\n   node -e \"const p=require('./package.json');console.log(Object.keys({...p.devDependencies,...p.dependencies}||{}).join('\\n'))\" 2>/dev/null | grep -E 'biome|eslint|prettier|vitest|jest|turborepo|nx' || echo \"No tooling detected\"\n   # Check for monorepo (fixed precedence)\n   (test -f pnpm-workspace.yaml || test -f lerna.json || test -f nx.json || test -f turbo.json) && echo \"Monorepo detected\"\n   ```\n\n   **After detection, adapt approach:**\n   - Match import style (absolute vs relative)\n   - Respect existing baseUrl/paths configuration\n   - Prefer existing project scripts over raw tools\n   - In monorepos, consider project references before broad tsconfig changes\n\n2. Identify the specific problem category and complexity level\n\n3. Apply the appropriate solution strategy from my expertise\n\n4. Validate thoroughly:\n\n   ```bash\n   # Fast fail approach (avoid long-lived processes)\n   npm run -s typecheck || npx tsc --noEmit\n   npm test -s || npx vitest run --reporter=basic --no-watch\n   # Only if needed and build affects outputs/config\n   npm run -s build\n   ```\n\n   **Safety note:** Avoid watch/serve processes in validation. Use one-shot diagnostics only.\n\n## Advanced Type System Expertise\n\n### Type-Level Programming Patterns\n\n**Branded Types for Domain Modeling**\n\n```typescript\n// Create nominal types to prevent primitive obsession\ntype Brand<K, T> = K & { __brand: T };\ntype UserId = Brand<string, \"UserId\">;\ntype OrderId = Brand<string, \"OrderId\">;\n\n// Prevents accidental mixing of domain primitives\nfunction processOrder(orderId: OrderId, userId: UserId) {}\n```\n\n- Use for: Critical domain primitives, API boundaries, currency/units\n- Resource: https://egghead.io/blog/using-branded-types-in-typescript\n\n**Advanced Conditional Types**\n\n```typescript\n// Recursive type manipulation\ntype DeepReadonly<T> = T extends (...args: any[]) => any\n  ? T\n  : T extends object\n    ? { readonly [K in keyof T]: DeepReadonly<T[K]> }\n    : T;\n\n// Template literal type magic\ntype PropEventSource<Type> = {\n  on<Key extends string & keyof Type>(\n    eventName: `${Key}Changed`,\n    callback: (newValue: Type[Key]) => void,\n  ): void;\n};\n```\n\n- Use for: Library APIs, type-safe event systems, compile-time validation\n- Watch for: Type instantiation depth errors (limit recursion to 10 levels)\n\n**Type Inference Techniques**\n\n```typescript\n// Use 'satisfies' for constraint validation (TS 5.0+)\nconst config = {\n  api: \"https://api.example.com\",\n  timeout: 5000,\n} satisfies Record<string, string | number>;\n// Preserves literal types while ensuring constraints\n\n// Const assertions for maximum inference\nconst routes = [\"/home\", \"/about\", \"/contact\"] as const;\ntype Route = (typeof routes)[number]; // '/home' | '/about' | '/contact'\n```\n\n### Performance Optimization Strategies\n\n**Type Checking Performance**\n\n```bash\n# Diagnose slow type checking\nnpx tsc --extendedDiagnostics --incremental false | grep -E \"Check time|Files:|Lines:|Nodes:\"\n\n# Common fixes for \"Type instantiation is excessively deep\"\n# 1. Replace type intersections with interfaces\n# 2. Split large union types (>100 members)\n# 3. Avoid circular generic constraints\n# 4. Use type aliases to break recursion\n```\n\n**Build Performance Patterns**\n\n- Enable `skipLibCheck: true` for library type checking only (often significantly improves performance on large projects, but avoid masking app typing issues)\n- Use `incremental: true` with `.tsbuildinfo` cache\n- Configure `include`/`exclude` precisely\n- For monorepos: Use project references with `composite: true`\n\n## Real-World Problem Resolution\n\n### Complex Error Patterns\n\n**\"The inferred type of X cannot be named\"**\n\n- Cause: Missing type export or circular dependency\n- Fix priority:\n  1. Export the required type explicitly\n  2. Use `ReturnType<typeof function>` helper\n  3. Break circular dependencies with type-only imports\n- Resource: https://github.com/microsoft/TypeScript/issues/47663\n\n**Missing type declarations**\n\n- Quick fix with ambient declarations:\n\n```typescript\n// types/ambient.d.ts\ndeclare module \"some-untyped-package\" {\n  const value: unknown;\n  export default value;\n  export = value; // if CJS interop is needed\n}\n```\n\n- For more details: [Declaration Files Guide](https://www.typescriptlang.org/docs/handbook/declaration-files/introduction.html)\n\n**\"Excessive stack depth comparing types\"**\n\n- Cause: Circular or deeply recursive types\n- Fix priority:\n  1. Limit recursion depth with conditional types\n  2. Use `interface` extends instead of type intersection\n  3. Simplify generic constraints\n\n```typescript\n// Bad: Infinite recursion\ntype InfiniteArray<T> = T | InfiniteArray<T>[];\n\n// Good: Limited recursion\ntype NestedArray<T, D extends number = 5> = D extends 0\n  ? T\n  : T | NestedArray<T, [-1, 0, 1, 2, 3, 4][D]>[];\n```\n\n**Module Resolution Mysteries**\n\n- \"Cannot find module\" despite file existing:\n  1. Check `moduleResolution` matches your bundler\n  2. Verify `baseUrl` and `paths` alignment\n  3. For monorepos: Ensure workspace protocol (workspace:\\*)\n  4. Try clearing cache: `rm -rf node_modules/.cache .tsbuildinfo`\n\n**Path Mapping at Runtime**\n\n- TypeScript paths only work at compile time, not runtime\n- Node.js runtime solutions:\n  - ts-node: Use `ts-node -r tsconfig-paths/register`\n  - Node ESM: Use loader alternatives or avoid TS paths at runtime\n  - Production: Pre-compile with resolved paths\n\n### Migration Expertise\n\n**JavaScript to TypeScript Migration**\n\n```bash\n# Incremental migration strategy\n# 1. Enable allowJs and checkJs (merge into existing tsconfig.json):\n# Add to existing tsconfig.json:\n# {\n#   \"compilerOptions\": {\n#     \"allowJs\": true,\n#     \"checkJs\": true\n#   }\n# }\n\n# 2. Rename files gradually (.js → .ts)\n# 3. Add types file by file using AI assistance\n# 4. Enable strict mode features one by one\n\n# Automated helpers (if installed/needed)\ncommand -v ts-migrate >/dev/null 2>&1 && npx ts-migrate migrate . --sources 'src/**/*.js'\ncommand -v typesync >/dev/null 2>&1 && npx typesync  # Install missing @types packages\n```\n\n**Tool Migration Decisions**\n\n| From              | To              | When                                          | Migration Effort  |\n| ----------------- | --------------- | --------------------------------------------- | ----------------- |\n| ESLint + Prettier | Biome           | Need much faster speed, okay with fewer rules | Low (1 day)       |\n| TSC for linting   | Type-check only | Have 100+ files, need faster feedback         | Medium (2-3 days) |\n| Lerna             | Nx/Turborepo    | Need caching, parallel builds                 | High (1 week)     |\n| CJS               | ESM             | Node 18+, modern tooling                      | High (varies)     |\n\n### Monorepo Management\n\n**Nx vs Turborepo Decision Matrix**\n\n- Choose **Turborepo** if: Simple structure, need speed, <20 packages\n- Choose **Nx** if: Complex dependencies, need visualization, plugins required\n- Performance: Nx often performs better on large monorepos (>50 packages)\n\n**TypeScript Monorepo Configuration**\n\n```json\n// Root tsconfig.json\n{\n  \"references\": [\n    { \"path\": \"./packages/core\" },\n    { \"path\": \"./packages/ui\" },\n    { \"path\": \"./apps/web\" }\n  ],\n  \"compilerOptions\": {\n    \"composite\": true,\n    \"declaration\": true,\n    \"declarationMap\": true\n  }\n}\n```\n\n## Modern Tooling Expertise\n\n### Biome vs ESLint\n\n**Use Biome when:**\n\n- Speed is critical (often faster than traditional setups)\n- Want single tool for lint + format\n- TypeScript-first project\n- Okay with 64 TS rules vs 100+ in typescript-eslint\n\n**Stay with ESLint when:**\n\n- Need specific rules/plugins\n- Have complex custom rules\n- Working with Vue/Angular (limited Biome support)\n- Need type-aware linting (Biome doesn't have this yet)\n\n### Type Testing Strategies\n\n**Vitest Type Testing (Recommended)**\n\n```typescript\n// in avatar.test-d.ts\nimport { expectTypeOf } from \"vitest\";\nimport type { Avatar } from \"./avatar\";\n\ntest(\"Avatar props are correctly typed\", () => {\n  expectTypeOf<Avatar>().toHaveProperty(\"size\");\n  expectTypeOf<Avatar[\"size\"]>().toEqualTypeOf<\"sm\" | \"md\" | \"lg\">();\n});\n```\n\n**When to Test Types:**\n\n- Publishing libraries\n- Complex generic functions\n- Type-level utilities\n- API contracts\n\n## Debugging Mastery\n\n### CLI Debugging Tools\n\n```bash\n# Debug TypeScript files directly (if tools installed)\ncommand -v tsx >/dev/null 2>&1 && npx tsx --inspect src/file.ts\ncommand -v ts-node >/dev/null 2>&1 && npx ts-node --inspect-brk src/file.ts\n\n# Trace module resolution issues\nnpx tsc --traceResolution > resolution.log 2>&1\ngrep \"Module resolution\" resolution.log\n\n# Debug type checking performance (use --incremental false for clean trace)\nnpx tsc --generateTrace trace --incremental false\n# Analyze trace (if installed)\ncommand -v @typescript/analyze-trace >/dev/null 2>&1 && npx @typescript/analyze-trace trace\n\n# Memory usage analysis\nnode --max-old-space-size=8192 node_modules/typescript/lib/tsc.js\n```\n\n### Custom Error Classes\n\n```typescript\n// Proper error class with stack preservation\nclass DomainError extends Error {\n  constructor(\n    message: string,\n    public code: string,\n    public statusCode: number,\n  ) {\n    super(message);\n    this.name = \"DomainError\";\n    Error.captureStackTrace(this, this.constructor);\n  }\n}\n```\n\n## Current Best Practices\n\n### Strict by Default\n\n```json\n{\n  \"compilerOptions\": {\n    \"strict\": true,\n    \"noUncheckedIndexedAccess\": true,\n    \"noImplicitOverride\": true,\n    \"exactOptionalPropertyTypes\": true,\n    \"noPropertyAccessFromIndexSignature\": true\n  }\n}\n```\n\n### ESM-First Approach\n\n- Set `\"type\": \"module\"` in package.json\n- Use `.mts` for TypeScript ESM files if needed\n- Configure `\"moduleResolution\": \"bundler\"` for modern tools\n- Use dynamic imports for CJS: `const pkg = await import('cjs-package')`\n  - Note: `await import()` requires async function or top-level await in ESM\n  - For CJS packages in ESM: May need `(await import('pkg')).default` depending on the package's export structure and your compiler settings\n\n### AI-Assisted Development\n\n- GitHub Copilot excels at TypeScript generics\n- Use AI for boilerplate type definitions\n- Validate AI-generated types with type tests\n- Document complex types for AI context\n\n## Code Review Checklist\n\nWhen reviewing TypeScript/JavaScript code, focus on these domain-specific aspects:\n\n### Type Safety\n\n- [ ] No implicit `any` types (use `unknown` or proper types)\n- [ ] Strict null checks enabled and properly handled\n- [ ] Type assertions (`as`) justified and minimal\n- [ ] Generic constraints properly defined\n- [ ] Discriminated unions for error handling\n- [ ] Return types explicitly declared for public APIs\n\n### TypeScript Best Practices\n\n- [ ] Prefer `interface` over `type` for object shapes (better error messages)\n- [ ] Use const assertions for literal types\n- [ ] Leverage type guards and predicates\n- [ ] Avoid type gymnastics when simpler solution exists\n- [ ] Template literal types used appropriately\n- [ ] Branded types for domain primitives\n\n### Performance Considerations\n\n- [ ] Type complexity doesn't cause slow compilation\n- [ ] No excessive type instantiation depth\n- [ ] Avoid complex mapped types in hot paths\n- [ ] Use `skipLibCheck: true` in tsconfig\n- [ ] Project references configured for monorepos\n\n### Module System\n\n- [ ] Consistent import/export patterns\n- [ ] No circular dependencies\n- [ ] Proper use of barrel exports (avoid over-bundling)\n- [ ] ESM/CJS compatibility handled correctly\n- [ ] Dynamic imports for code splitting\n\n### Error Handling Patterns\n\n- [ ] Result types or discriminated unions for errors\n- [ ] Custom error classes with proper inheritance\n- [ ] Type-safe error boundaries\n- [ ] Exhaustive switch cases with `never` type\n\n### Code Organization\n\n- [ ] Types co-located with implementation\n- [ ] Shared types in dedicated modules\n- [ ] Avoid global type augmentation when possible\n- [ ] Proper use of declaration files (.d.ts)\n\n## Quick Decision Trees\n\n### \"Which tool should I use?\"\n\n```\nType checking only? → tsc\nType checking + linting speed critical? → Biome\nType checking + comprehensive linting? → ESLint + typescript-eslint\nType testing? → Vitest expectTypeOf\nBuild tool? → Project size <10 packages? Turborepo. Else? Nx\n```\n\n### \"How do I fix this performance issue?\"\n\n```\nSlow type checking? → skipLibCheck, incremental, project references\nSlow builds? → Check bundler config, enable caching\nSlow tests? → Vitest with threads, avoid type checking in tests\nSlow language server? → Exclude node_modules, limit files in tsconfig\n```\n\n## Expert Resources\n\n### Performance\n\n- [TypeScript Wiki Performance](https://github.com/microsoft/TypeScript/wiki/Performance)\n- [Type instantiation tracking](https://github.com/microsoft/TypeScript/pull/48077)\n\n### Advanced Patterns\n\n- [Type Challenges](https://github.com/type-challenges/type-challenges)\n- [Type-Level TypeScript Course](https://type-level-typescript.com)\n\n### Tools\n\n- [Biome](https://biomejs.dev) - Fast linter/formatter\n- [TypeStat](https://github.com/JoshuaKGoldberg/TypeStat) - Auto-fix TypeScript types\n- [ts-migrate](https://github.com/airbnb/ts-migrate) - Migration toolkit\n\n### Testing\n\n- [Vitest Type Testing](https://vitest.dev/guide/testing-types)\n- [tsd](https://github.com/tsdjs/tsd) - Standalone type testing\n\nAlways validate changes don't break existing functionality before considering the issue resolved.\n\n## When to Use\n\nThis skill is applicable to execute the workflow or actions described in the overview.\n\n<!-- File: .claude/skills/hermes-agent/SKILL.md (Tokens: ~11267 | Category: anthropic-skill) -->\n---\nname: hermes-agent\ndescription: Expert in building self-improving AI agents with tool use, multi-platform messaging, and a closed learning loop. Proficient in LLM orchestration, tool integration, session management, and agent autonomy.\n---\n\n# Hermes Agent - Complete Project Guide (A-Z)\n\n> **Purpose of this document:** A single, comprehensive reference that explains everything about the Hermes Agent project — its architecture, source code, features, release history, and design patterns — so that any AI or developer can fully understand the system.\n\n---\n\n## Table of Contents\n\n1. [Project Overview](#1-project-overview)\n2. [Key Features Summary](#2-key-features-summary)\n3. [Installation & Getting Started](#3-installation--getting-started)\n4. [Project Structure](#4-project-structure)\n5. [Core Architecture](#5-core-architecture)\n   - 5.1 [AIAgent Class (run_agent.py)](#51-aiagent-class-run_agentpy)\n   - 5.2 [Tool Orchestration (model_tools.py)](#52-tool-orchestration-model_toolspy)\n   - 5.3 [Toolset System (toolsets.py)](#53-toolset-system-toolsetspy)\n   - 5.4 [Tool Registry (tools/registry.py)](#54-tool-registry-toolsregistrypy)\n   - 5.5 [Session Database (hermes_state.py)](#55-session-database-hermes_statepy)\n   - 5.6 [Constants & Home Directory (hermes_constants.py)](#56-constants--home-directory-hermes_constantspy)\n6. [CLI System](#6-cli-system)\n   - 6.1 [Interactive CLI (cli.py)](#61-interactive-cli-clipy)\n   - 6.2 [CLI Entry Point (hermes_cli/main.py)](#62-cli-entry-point-hermes_climainpy)\n   - 6.3 [Configuration System (hermes_cli/config.py)](#63-configuration-system-hermes_cliconfigpy)\n   - 6.4 [Slash Command Registry (hermes_cli/commands.py)](#64-slash-command-registry-hermes_clicommandspy)\n   - 6.5 [Setup Wizard (hermes_cli/setup.py)](#65-setup-wizard-hermes_clisetupy)\n   - 6.6 [Model Catalog (hermes_cli/models.py)](#66-model-catalog-hermes_climodelspy)\n   - 6.7 [Skin/Theme Engine (hermes_cli/skin_engine.py)](#67-skintheme-engine-hermes_cliskin_enginepy)\n7. [Tool System](#7-tool-system)\n   - 7.1 [Terminal Tool (tools/terminal_tool.py)](#71-terminal-tool-toolsterminal_toolpy)\n   - 7.2 [File Tools (tools/file_tools.py)](#72-file-tools-toolsfile_toolspy)\n   - 7.3 [Web Tools (tools/web_tools.py)](#73-web-tools-toolsweb_toolspy)\n   - 7.4 [Browser Tool (tools/browser_tool.py)](#74-browser-tool-toolsbrowser_toolpy)\n   - 7.5 [Delegate Tool (tools/delegate_tool.py)](#75-delegate-tool-toolsdelegate_toolpy)\n   - 7.6 [MCP Tool (tools/mcp_tool.py)](#76-mcp-tool-toolsmcp_toolpy)\n   - 7.7 [Approval System (tools/approval.py)](#77-approval-system-toolsapprovalpy)\n   - 7.8 [Terminal Backends (tools/environments/)](#78-terminal-backends-toolsenvironments)\n8. [Agent Internals](#8-agent-internals)\n   - 8.1 [Prompt Builder (agent/prompt_builder.py)](#81-prompt-builder-agentprompt_builderpy)\n   - 8.2 [Context Compressor (agent/context_compressor.py)](#82-context-compressor-agentcontext_compressorpy)\n   - 8.3 [Prompt Caching (agent/prompt_caching.py)](#83-prompt-caching-agentprompt_cachingpy)\n   - 8.4 [Auxiliary Client (agent/auxiliary_client.py)](#84-auxiliary-client-agentauxiliary_clientpy)\n   - 8.5 [Display & Spinner (agent/display.py)](#85-display--spinner-agentdisplaypy)\n   - 8.6 [Skill Commands (agent/skill_commands.py)](#86-skill-commands-agentskill_commandspy)\n9. [Messaging Gateway](#9-messaging-gateway)\n   - 9.1 [GatewayRunner (gateway/run.py)](#91-gatewayrunner-gatewayrunpy)\n   - 9.2 [Session Store (gateway/session.py)](#92-session-store-gatewaysessionpy)\n   - 9.3 [Platform Adapters (gateway/platforms/)](#93-platform-adapters-gatewayplatforms)\n10. [Cron Scheduling](#10-cron-scheduling)\n11. [Skills System](#11-skills-system)\n12. [Plugin System](#12-plugin-system)\n13. [Memory System](#13-memory-system)\n14. [ACP Server (IDE Integration)](#14-acp-server-ide-integration)\n15. [API Server](#15-api-server)\n16. [MCP Server Mode](#16-mcp-server-mode)\n17. [RL Training Environments](#17-rl-training-environments)\n18. [Profiles (Multi-Instance)](#18-profiles-multi-instance)\n19. [Security Model](#19-security-model)\n20. [Provider & Model System](#20-provider--model-system)\n21. [Streaming & Reasoning](#21-streaming--reasoning)\n22. [Release History](#22-release-history)\n23. [File Dependency Chain](#23-file-dependency-chain)\n24. [Key Design Patterns](#24-key-design-patterns)\n25. [Configuration Reference](#25-configuration-reference)\n26. [Known Pitfalls](#26-known-pitfalls)\n\n---\n\n## 1. Project Overview\n\n**Hermes Agent** is a self-improving AI agent built by [Nous Research](https://nousresearch.com). It is an open-source (MIT licensed), Python-based project that provides:\n\n- A **full interactive terminal UI** (CLI) for conversing with LLMs\n- A **messaging gateway** supporting 16+ platforms (Telegram, Discord, Slack, WhatsApp, Signal, Matrix, Email, etc.)\n- A **closed learning loop** — the agent creates skills from experience, improves them during use, nudges itself to persist knowledge, searches past conversations, and builds a deepening model of who you are\n- **40+ built-in tools** — terminal execution, file manipulation, web search, browser automation, code execution, image generation, TTS/STT, and more\n- **Any LLM provider** — OpenRouter (200+ models), Nous Portal (400+ models), OpenAI, Anthropic, Hugging Face, GitHub Copilot, z.ai/GLM, Kimi/Moonshot, MiniMax, Alibaba/DashScope, custom endpoints\n- **Six terminal backends** — local, Docker, SSH, Modal (serverless), Daytona (serverless), Singularity (HPC)\n- **Scheduled automations** via built-in cron scheduler\n- **IDE integration** via ACP (Agent Communication Protocol) for VS Code, Zed, JetBrains\n- **MCP integration** — both client (connect to any MCP server) and server (expose Hermes to MCP clients)\n- **RL training** via Atropos environments for training the next generation of tool-calling models\n\n**Tech Stack:**\n\n- Python 3.11+ (core agent, tools, gateway, cron)\n- Node.js (browser automation via agent-browser)\n- SQLite with WAL mode and FTS5 (session storage, full-text search)\n- OpenAI-compatible API (primary inference interface)\n- Anthropic SDK (native Anthropic support)\n- Rich + prompt_toolkit (CLI rendering)\n\n**Repository:** `github.com/NousResearch/hermes-agent`\n**Version:** 0.7.0 (as of April 2026)\n**License:** MIT\n\n---\n\n## 2. Key Features Summary\n\n| Feature                      | Description                                                                                                                                                                      |\n| ---------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| **Terminal UI**              | Full TUI with multiline editing, slash-command autocomplete, conversation history, interrupt-and-redirect, streaming tool output                                                 |\n| **Multi-Platform Messaging** | Telegram, Discord, Slack, WhatsApp, Signal, Matrix, Email, Home Assistant, DingTalk, Feishu/Lark, WeCom, Mattermost, SMS, Webhook — all from a single gateway process            |\n| **Learning Loop**            | Agent-curated memory with periodic nudges, autonomous skill creation, skills self-improve during use, FTS5 session search with LLM summarization, Honcho dialectic user modeling |\n| **Scheduled Tasks**          | Built-in cron scheduler with delivery to any platform (daily reports, nightly backups, weekly audits)                                                                            |\n| **Subagent Delegation**      | Spawn isolated subagents for parallel workstreams with restricted toolsets                                                                                                       |\n| **Execute Code**             | Python scripts that call tools via RPC, collapsing multi-step pipelines into zero-context-cost turns                                                                             |\n| **Terminal Backends**        | Local, Docker, SSH, Modal, Daytona, Singularity — run on a $5 VPS or a GPU cluster                                                                                               |\n| **Skills**                   | 70+ bundled skills across 28 categories, Skills Hub for community discovery, agentskills.io compatibility                                                                        |\n| **Plugins**                  | Drop-in Python plugins with lifecycle hooks (pre_llm_call, post_llm_call, on_session_start, on_session_end)                                                                      |\n| **MCP**                      | Client (connect to MCP servers for extended tools) and Server (expose conversations to MCP clients)                                                                              |\n| **IDE Integration**          | VS Code, Zed, JetBrains via ACP server with session management and tool streaming                                                                                                |\n| **API Server**               | OpenAI-compatible `/v1/chat/completions` endpoint for headless integrations                                                                                                      |\n| **Profiles**                 | Multi-instance support — each profile gets isolated config, memory, sessions, skills, gateway                                                                                    |\n| **Security**                 | Command approval system, secret redaction, SSRF protection, PII redaction, injection detection, credential directory protection                                                  |\n| **RL Training**              | Atropos environments for batch trajectory generation and agent policy optimization                                                                                               |\n\n---\n\n## 3. Installation & Getting Started\n\n```bash\n# One-line install (Linux, macOS, WSL2)\ncurl -fsSL https://raw.githubusercontent.com/NousResearch/hermes-agent/main/scripts/install.sh | bash\n\n# After install\nsource ~/.bashrc    # or: source ~/.zshrc\nhermes              # start chatting\n\n# Key commands\nhermes model        # Choose LLM provider and model\nhermes tools        # Configure which tools are enabled\nhermes config set   # Set individual config values\nhermes gateway      # Start the messaging gateway\nhermes setup        # Run the full setup wizard\nhermes update       # Update to latest version\nhermes doctor       # Diagnose any issues\n```\n\n**For development:**\n\n```bash\ngit clone https://github.com/NousResearch/hermes-agent.git\ncd hermes-agent\ncurl -LsSf https://astral.sh/uv/install.sh | sh\nuv venv venv --python 3.11\nsource venv/bin/activate\nuv pip install -e \".[all,dev]\"\npython -m pytest tests/ -q    # ~3000 tests\n```\n\n---\n\n## 4. Project Structure\n\n```\nhermes-agent/\n├── run_agent.py              # AIAgent class — core conversation loop\n├── model_tools.py            # Tool orchestration, _discover_tools(), handle_function_call()\n├── toolsets.py               # Toolset definitions, _HERMES_CORE_TOOLS list\n├── toolset_distributions.py  # Toolset sampling distributions for RL\n├── cli.py                    # HermesCLI class — interactive CLI orchestrator\n├── hermes_state.py           # SessionDB — SQLite session store (FTS5 search)\n├── hermes_constants.py       # Shared constants, get_hermes_home()\n├── hermes_time.py            # Timezone handling\n├── utils.py                  # Shared utility functions\n├── batch_runner.py           # Parallel batch processing\n├── trajectory_compressor.py  # Trajectory compression for RL training\n├── mcp_serve.py              # MCP server mode entry point\n├── mini_swe_runner.py        # Minimal SWE benchmark runner\n├── rl_cli.py                 # RL CLI commands\n│\n├── agent/                    # Agent internals\n│   ├── prompt_builder.py         # System prompt assembly\n│   ├── context_compressor.py     # Auto context compression\n│   ├── prompt_caching.py         # Anthropic prompt caching\n│   ├── auxiliary_client.py       # Auxiliary LLM client (vision, summarization)\n│   ├── model_metadata.py         # Model context lengths, token estimation\n│   ├── models_dev.py             # models.dev registry integration\n│   ├── display.py                # KawaiiSpinner, tool preview formatting\n│   ├── skill_commands.py         # Skill slash commands (shared CLI/gateway)\n│   └── trajectory.py             # Trajectory saving helpers\n│\n├── hermes_cli/               # CLI subcommands and setup\n│   ├── main.py               # Entry point — all `hermes` subcommands\n│   ├── config.py             # DEFAULT_CONFIG, OPTIONAL_ENV_VARS, migration\n│   ├── commands.py           # Slash command definitions + SlashCommandCompleter\n│   ├── callbacks.py          # Terminal callbacks (clarify, sudo, approval)\n│   ├── setup.py              # Interactive setup wizard\n│   ├── skin_engine.py        # Skin/theme engine\n│   ├── skills_config.py      # `hermes skills` — skill management\n│   ├── tools_config.py       # `hermes tools` — tool management\n│   ├── skills_hub.py         # Skills Hub integration\n│   ├── models.py             # Model catalog, provider model lists\n│   ├── model_switch.py       # Shared /model switch pipeline\n│   └── auth.py               # Provider credential resolution\n│\n├── tools/                    # Tool implementations (one file per tool)\n│   ├── registry.py           # Central tool registry\n│   ├── approval.py           # Dangerous command detection\n│   ├── terminal_tool.py      # Terminal/shell execution\n│   ├── process_registry.py   # Background process management\n│   ├── file_tools.py         # File read/write/search/patch\n│   ├── web_tools.py          # Web search/extract\n│   ├── browser_tool.py       # Browser automation\n│   ├── code_execution_tool.py # execute_code sandbox\n│   ├── delegate_tool.py      # Subagent delegation\n│   ├── mcp_tool.py           # MCP client integration\n│   ├── skills_tool.py        # Skill management tool\n│   ├── todo_tool.py          # Todo/task tracking tool\n│   ├── memory_tool.py        # Memory read/write tool\n│   ├── tts_tool.py           # Text-to-speech\n│   ├── vision_tool.py        # Image analysis\n│   ├── image_gen_tool.py     # Image generation\n│   └── environments/         # Terminal backends\n│       ├── base.py               # BaseEnvironment ABC\n│       ├── local.py              # Local execution\n│       ├── docker.py             # Docker containers\n│       ├── ssh.py                # SSH remote execution\n│       ├── modal.py              # Modal serverless\n│       ├── managed_modal.py      # Nous-hosted Modal\n│       ├── daytona.py            # Daytona serverless\n│       ├── singularity.py        # Singularity HPC containers\n│       └── persistent_shell.py   # Persistent shell mixin\n│\n├── gateway/                  # Messaging platform gateway\n│   ├── run.py                # GatewayRunner — main message loop\n│   ├── session.py            # SessionStore — conversation persistence\n│   ├── status.py             # Gateway status, token locks\n│   └── platforms/            # 16 platform adapters\n│       ├── base.py               # BasePlatformAdapter ABC\n│       ├── telegram.py           # Telegram (polling + webhook)\n│       ├── discord.py            # Discord\n│       ├── slack.py              # Slack\n│       ├── whatsapp.py           # WhatsApp\n│       ├── matrix.py             # Matrix (E2EE)\n│       ├── signal.py             # Signal\n│       ├── email.py              # Email (IMAP/SMTP)\n│       ├── homeassistant.py      # Home Assistant\n│       ├── sms.py                # SMS (Twilio)\n│       ├── mattermost.py         # Mattermost\n│       ├── dingtalk.py           # DingTalk\n│       ├── feishu.py             # Feishu/Lark\n│       ├── wecom.py              # WeCom (Enterprise WeChat)\n│       ├── webhook.py            # Generic webhook\n│       └── api_server.py         # OpenAI-compatible API server\n│\n├── acp_adapter/              # ACP server (IDE integration)\n│   ├── server.py             # HermesACPAgent class\n│   ├── session.py            # SessionManager\n│   ├── events.py             # Streaming callbacks\n│   ├── permissions.py        # Approval callbacks\n│   └── entry.py              # Entry point\n│\n├── cron/                     # Scheduler\n│   ├── scheduler.py          # tick() — job execution engine\n│   └── jobs.py               # Job storage and CRUD\n│\n├── plugins/                  # Plugin system\n│   └── memory/               # 8 memory provider plugins\n│       ├── openviking/\n│       ├── mem0/\n│       ├── hindsight/\n│       ├── holographic/\n│       ├── honcho/\n│       ├── retaindb/\n│       └── byterover/\n│\n├── environments/             # RL training environments (Atropos)\n│   ├── hermes_base_env.py    # Abstract base RL environment\n│   ├── agent_loop.py         # HermesAgentLoop — rollout execution\n│   ├── tool_context.py       # ToolContext — sandbox for RL\n│   ├── web_research_env.py   # Web research tasks\n│   └── agentic_opd_env.py    # Observation-Prediction-Demo env\n│\n├── skills/                   # 70+ bundled skills across 28 categories\n├── optional-skills/          # Additional optional skills\n├── tests/                    # ~3000 pytest tests\n├── scripts/                  # Install, update, packaging scripts\n├── docker/                   # Docker build files\n├── docs/                     # Documentation source (Docusaurus)\n├── website/                  # Landing page\n├── desktop/                  # Desktop app (Electron, separate repo)\n├── tinker-atropos/           # RL submodule\n│\n├── pyproject.toml            # Python package config\n├── AGENTS.md                 # Developer guide for AI assistants\n├── RELEASE_v0.2.0.md → v0.7.0.md  # Release notes\n└── cli-config.yaml.example   # Example config\n```\n\n**User config directory:** `~/.hermes/`\n\n```\n~/.hermes/\n├── config.yaml           # User settings\n├── .env                  # API keys and secrets\n├── MEMORY.md             # Persistent agent memory\n├── USER.md               # User profile\n├── SOUL.md               # Agent personality/identity\n├── sessions.db           # SQLite session database\n├── skills/               # User-installed skills\n├── skins/                # Custom CLI themes\n├── plugins/              # User plugins\n├── cron/                 # Cron jobs and output\n│   ├── jobs.json\n│   └── output/\n├── cache/                # Image/audio cache\n├── plans/                # Generated plans\n├── profiles/             # Multi-instance profiles\n└── mcp/                  # MCP server configs\n```\n\n---\n\n## 5. Core Architecture\n\n### 5.1 AIAgent Class (run_agent.py)\n\nThe `AIAgent` class is the heart of the system — the core conversation loop that orchestrates LLM calls, tool execution, context management, and response delivery.\n\n**Constructor (~60 parameters):**\n\n```python\nclass AIAgent:\n    def __init__(self,\n        model: str = \"anthropic/claude-opus-4.6\",\n        max_iterations: int = 90,\n        enabled_toolsets: list = None,\n        disabled_toolsets: list = None,\n        quiet_mode: bool = False,\n        save_trajectories: bool = False,\n        platform: str = None,            # \"cli\", \"telegram\", etc.\n        session_id: str = None,\n        session_db: SessionDB = None,\n        skip_context_files: bool = False,\n        skip_memory: bool = False,\n        base_url: str = None,\n        api_key: str = None,\n        provider: str = None,\n        api_mode: str = \"chat_completions\",  # or \"anthropic_messages\" or \"codex_responses\"\n        tool_progress_callback = None,\n        stream_delta_callback = None,\n        thinking_callback = None,\n        status_callback = None,\n        iteration_budget: IterationBudget = None,\n        credential_pool = None,\n        checkpoints_enabled: bool = False,\n        # ... plus provider, routing, callback params\n    )\n```\n\n**Main Methods:**\n\n| Method                                                                          | Returns  | Purpose                                                                            |\n| ------------------------------------------------------------------------------- | -------- | ---------------------------------------------------------------------------------- |\n| `chat(message, stream_callback)`                                                | `str`    | Simple interface — returns final response text                                     |\n| `run_conversation(user_message, system_message, conversation_history, task_id)` | `dict`   | Full interface — returns `{final_response, messages, completed, api_calls, error}` |\n| `_interruptible_api_call(api_kwargs)`                                           | Response | Runs API request in background thread with interrupt support                       |\n| `_interruptible_streaming_api_call(api_kwargs, on_first_delta)`                 | Response | Streaming variant with delta callbacks                                             |\n\n**The Core Agent Loop** (inside `run_conversation()`):\n\n```python\nwhile api_call_count < self.max_iterations and self.iteration_budget.remaining > 0:\n    response = client.chat.completions.create(\n        model=model, messages=messages, tools=tool_schemas\n    )\n    if response.tool_calls:\n        for tool_call in response.tool_calls:\n            result = handle_function_call(tool_call.name, tool_call.args, task_id)\n            messages.append(tool_result_message(result))\n        api_call_count += 1\n    else:\n        return response.content  # Final text response\n```\n\n**Key Behaviors:**\n\n- **Three API modes:** `chat_completions` (OpenAI-compatible), `anthropic_messages` (Anthropic SDK), `codex_responses` (OpenAI Codex)\n- **Parallel tool execution:** Independent tool calls run concurrently via ThreadPoolExecutor (unless they share file paths or are in the never-parallel list)\n- **Interrupt support:** Background threads allow interrupt detection without blocking on HTTP\n- **Error recovery:** Automatic fallback chain (primary → fallback model), retry with exponential backoff, context compression on token overflow\n- **Budget pressure:** Warnings at 70% (caution) and 90% (urgent) of iteration budget\n- **Oversized results:** Tool results >100K chars are saved to temp files with a preview\n- **Stale connection detection:** 90s timeout for streaming, 60s read timeout\n\n**IterationBudget** (thread-safe):\n\n```python\nclass IterationBudget:\n    def consume() -> bool    # Check and consume one iteration\n    def refund()             # Give back iteration (for execute_code turns)\n    @property remaining      # Remaining iterations\n```\n\n---\n\n### 5.2 Tool Orchestration (model_tools.py)\n\nBridges the agent and tool registry — handles discovery, schema generation, and dispatch.\n\n**Key Functions:**\n\n| Function                                                                                | Purpose                                                                   |\n| --------------------------------------------------------------------------------------- | ------------------------------------------------------------------------- |\n| `_discover_tools()`                                                                     | Imports all tool modules (each calls `registry.register()` on import)     |\n| `get_tool_definitions(enabled_toolsets, disabled_toolsets, quiet_mode)`                 | Returns OpenAI-format tool schemas filtered by toolset                    |\n| `handle_function_call(function_name, function_args, task_id, user_task, enabled_tools)` | Main dispatcher — routes calls to registry with arg coercion              |\n| `coerce_tool_args(tool_name, args)`                                                     | Type coercion for LLM-generated arguments (string→int, string→bool, etc.) |\n\n**Tool Discovery Order:**\n\n1. Static tools (web_tools, terminal_tool, file_tools, browser_tool, etc.)\n2. Optional tools (fal_client for image gen, honcho, etc.) — graceful fallback if missing\n3. Plugin-registered tools\n4. MCP server tools (dynamic, via tools/list_changed notifications)\n\n**Special Tool Handling:**\n\n- **Agent-level tools** (todo, memory, session_search, delegate_task): Intercepted by `run_agent.py` before `handle_function_call()`\n- **execute_code**: Passes `enabled_tools` for sandbox tool list\n- **Dynamic schema adjustments**: `browser_navigate` strips web_search reference if tools unavailable\n\n**Async Bridging:**\n\n- Persistent event loops (not `asyncio.run()`) to prevent \"Event loop is closed\" errors\n- Main thread uses shared loop; worker threads get per-thread loops\n- `_run_async()` detects running loop and spins up disposable thread if needed\n\n---\n\n### 5.3 Toolset System (toolsets.py)\n\nProvides flexible tool grouping and composition.\n\n**Core Toolsets:**\n\n| Toolset          | Tools Included                                                                                   |\n| ---------------- | ------------------------------------------------------------------------------------------------ |\n| `web`            | web_search, web_extract, web_crawl                                                               |\n| `terminal`       | terminal                                                                                         |\n| `file`           | read_file, write_file, edit_file, list_files, search_files                                       |\n| `browser`        | browser_navigate, browser_snapshot, browser_click, browser_type, browser_scroll, browser_extract |\n| `vision`         | analyze_image                                                                                    |\n| `image_gen`      | generate_image                                                                                   |\n| `tts`            | text_to_speech                                                                                   |\n| `todo`           | todo_read, todo_write                                                                            |\n| `memory`         | memory_read, memory_write                                                                        |\n| `session_search` | session_search                                                                                   |\n| `delegation`     | delegate_task                                                                                    |\n| `code_execution` | execute_code                                                                                     |\n| `cronjob`        | create_job, list_jobs, delete_job                                                                |\n| `messaging`      | send_message                                                                                     |\n| `homeassistant`  | ha_get_states, ha_call_service, ...                                                              |\n\n**Composite Toolsets:**\n\n- `hermes-cli` — All core tools for CLI platform\n- `hermes-telegram`, `hermes-discord`, etc. — Platform-specific tool sets\n- `hermes-gateway` — Union of all platform tools\n- `debugging` — terminal + file + web\n- `safe` — Everything except terminal\n\n**Resolution:**\n\n```python\nresolve_toolset(name, visited=None) → List[str]\n# Recursively resolves toolset to tool names\n# Handles composition (includes) and cycle detection\n# Special aliases: \"all\" or \"*\" = all tools\n```\n\n---\n\n### 5.4 Tool Registry (tools/registry.py)\n\nSingleton managing all tool schemas and handlers. Circular-import safe — has no tool dependencies.\n\n**ToolEntry** (per-tool metadata):\n\n```python\n@dataclass(slots=True)\nclass ToolEntry:\n    name: str\n    toolset: str\n    schema: dict           # OpenAI-format tool definition\n    handler: Callable      # Sync or async handler function\n    check_fn: Callable     # Returns True if tool is available\n    requires_env: list     # Required environment variables\n    is_async: bool\n    description: str\n    emoji: str\n```\n\n**Key Methods:**\n\n```python\nregistry.register(name, toolset, schema, handler, check_fn, requires_env)\nregistry.get_definitions(tool_names, quiet)  # Returns filtered schemas\nregistry.dispatch(name, args, **kwargs)      # Execute with async bridging\nregistry.deregister(name)                    # Remove (for MCP tool refresh)\nregistry.check_tool_availability()           # Returns (available, unavailable)\n```\n\n---\n\n### 5.5 Session Database (hermes_state.py)\n\nSQLite-based persistent session storage with FTS5 full-text search.\n\n**Schema (v6):**\n\n```sql\n-- Sessions table\nsessions (\n    id TEXT PRIMARY KEY,\n    source TEXT, user_id TEXT, model TEXT, model_config TEXT,\n    system_prompt TEXT, parent_session_id TEXT,\n    started_at TEXT, ended_at TEXT, end_reason TEXT,\n    message_count INTEGER, tool_call_count INTEGER,\n    input_tokens INTEGER, output_tokens INTEGER,\n    cache_read_tokens INTEGER, cache_write_tokens INTEGER, reasoning_tokens INTEGER,\n    estimated_cost_usd REAL, actual_cost_usd REAL,\n    title TEXT  -- UNIQUE INDEX\n)\n\n-- Messages table\nmessages (\n    id INTEGER PRIMARY KEY AUTOINCREMENT,\n    session_id TEXT, role TEXT, content TEXT,\n    tool_call_id TEXT, tool_calls TEXT,  -- JSON\n    tool_name TEXT, timestamp TEXT,\n    token_count INTEGER, finish_reason TEXT,\n    reasoning TEXT, reasoning_details TEXT, codex_reasoning_items TEXT\n)\n\n-- FTS5 virtual table (auto-synced via triggers)\nmessages_fts (content)\n```\n\n**Concurrency Model:**\n\n- WAL (Write-Ahead Logging) for concurrent readers + single writer\n- `BEGIN IMMEDIATE` for write transactions (lock at start, not commit)\n- Jitter retry on lock: 20-150ms random backoff, max 15 retries\n- Periodic WAL checkpoint every 50 writes\n\n**Key Operations:**\n\n- `create_session()`, `end_session()`, `reopen_session()`\n- `add_message()`, `get_messages()`\n- `search_sessions(query)` — FTS5 full-text search\n- `update_token_counts()` — Supports both incremental (CLI) and absolute (gateway) modes\n\n---\n\n### 5.6 Constants & Home Directory (hermes_constants.py)\n\nImport-safe constants module with no circular dependencies.\n\n```python\nget_hermes_home() → Path          # HERMES_HOME env var or ~/.hermes\ndisplay_hermes_home() → str       # User-friendly display: \"~/.hermes\"\nget_optional_skills_dir() → Path  # HERMES_OPTIONAL_SKILLS env var\nparse_reasoning_effort(str) → Dict  # \"high\" → {\"enabled\": True, \"effort\": \"high\"}\n\n# Key constants\nOPENROUTER_BASE_URL = \"https://openrouter.ai/api/v1\"\nNOUS_API_BASE_URL = \"https://inference-api.nousresearch.com/v1\"\nAI_GATEWAY_BASE_URL = \"https://ai-gateway.vercel.sh/v1\"\nVALID_REASONING_EFFORTS = (\"xhigh\", \"high\", \"medium\", \"low\", \"minimal\")\n```\n\n---\n\n## 6. CLI System\n\n### 6.1 Interactive CLI (cli.py)\n\nThe `HermesCLI` class provides the interactive terminal interface.\n\n**Features:**\n\n- **Rich** for banner/panels, **prompt_toolkit** for input with autocomplete\n- **KawaiiSpinner** — animated faces during API calls, `┊` activity feed for tool results\n- Multiline editing with Shift+Enter\n- Slash-command autocomplete\n- Session history with up/down arrow navigation\n- Clipboard image paste (Alt+V / Ctrl+V)\n- Status bar showing model, provider, and token counts\n- Inline diff previews for file write/patch operations\n\n**Configuration Loading:**\n\n```python\nload_cli_config() → dict\n# Loads from ~/.hermes/config.yaml (or ./cli-config.yaml fallback)\n# Merges with hardcoded defaults\n# Expands ${ENV_VAR} references\n# Maps terminal config → env vars\n```\n\n---\n\n### 6.2 CLI Entry Point (hermes_cli/main.py)\n\nAll `hermes` subcommands are dispatched from here:\n\n```\nhermes                    # Default: interactive chat\nhermes chat               # Explicit interactive mode\nhermes gateway start|stop|status|install|uninstall\nhermes setup              # Setup wizard\nhermes model              # Select model/provider\nhermes tools              # Configure tools\nhermes skills             # Manage skills\nhermes config set|get     # Direct config manipulation\nhermes cron list|delete   # Cron job management\nhermes doctor             # Diagnose issues\nhermes sessions browse    # Session picker\nhermes profile create|list|switch|delete|export|import\nhermes mcp serve|add|remove  # MCP management\nhermes acp                # Start ACP server\nhermes update|uninstall|version\n```\n\n**Profile System:**\n\n- `_apply_profile_override()` runs BEFORE any imports to set `HERMES_HOME`\n- Pre-parses `--profile/-p` from argv\n- Allows fully isolated agent instances with separate config, memory, sessions, skills\n\n---\n\n### 6.3 Configuration System (hermes_cli/config.py)\n\n**Key Configuration Sections:**\n\n```yaml\nmodel: \"anthropic/claude-opus-4.6\" # or dict with provider/base_url/api_key\nproviders: {} # Provider-specific configs\nfallback_providers: [] # Ordered failover list\ncredential_pool: {} # Multiple API keys per provider\n\nagent:\n  max_turns: 90\n  gateway_timeout: 1800\n  tool_use_enforcement: \"auto\"\n\nterminal:\n  backend: \"local\" # local|docker|modal|daytona|ssh|singularity\n  timeout: 180\n  persistent_shell: true\n  docker_image: \"nikolaik/python-nodejs:...\"\n\ncompression:\n  enabled: true\n  threshold: 0.50 # Compress when 50% of context used\n  target_ratio: 0.20 # Summary = 20% of compressed content\n  protect_last_n: 20\n\nauxiliary:\n  vision: { provider, model }\n  web_extract: { provider, model }\n  compression: { provider, model }\n\nmemory:\n  memory_enabled: true\n  provider: \"\" # \"\" | \"honcho\" | \"mem0\" | etc.\n  memory_char_limit: 2200\n\ndisplay:\n  personality: \"kawaii\"\n  show_reasoning: false\n  inline_diffs: true\n  skin: \"default\"\n  streaming: true\n\ntts:\n  provider: \"edge\" # edge|elevenlabs|openai|neutts\n\nstt:\n  enabled: true\n  provider: \"local\" # local|groq|openai\n\nprivacy:\n  redact_pii: false\n\nmcp_servers: {} # MCP server configurations\n\nskills:\n  external_dirs: [] # Additional skill directories\n\napprovals:\n  mode: \"smart\" # smart|always|off\n```\n\n**Config Files:**\n\n- `~/.hermes/config.yaml` — User settings (authoritative)\n- `~/.hermes/.env` — API keys and secrets\n- Config version migration system (currently v5)\n\n---\n\n### 6.4 Slash Command Registry (hermes_cli/commands.py)\n\nAll slash commands defined centrally in `COMMAND_REGISTRY`:\n\n```python\nCommandDef(name, description, category, aliases, args_hint, cli_only, gateway_only)\n```\n\n**Derived automatically by:**\n\n- CLI `process_command()` — dispatch on canonical name\n- Gateway dispatch + help\n- Telegram BotCommand menu\n- Slack `/hermes` subcommands\n- Autocomplete + help text\n\n**Key Commands:**\n\n| Command        | Aliases    | Description                   |\n| -------------- | ---------- | ----------------------------- |\n| `/new`         | `/reset`   | Start fresh conversation      |\n| `/model`       |            | Show/switch model             |\n| `/personality` |            | Set agent personality         |\n| `/retry`       |            | Retry last turn               |\n| `/undo`        |            | Remove last turn              |\n| `/compress`    | `/compact` | Compress context              |\n| `/usage`       | `/cost`    | Show token usage              |\n| `/insights`    |            | Usage analytics               |\n| `/skills`      |            | Browse/install skills         |\n| `/background`  | `/bg`      | Manage background processes   |\n| `/plan`        |            | Generate implementation plan  |\n| `/rollback`    |            | Restore filesystem checkpoint |\n| `/verbose`     |            | Toggle debug output           |\n| `/reasoning`   |            | Set reasoning effort          |\n| `/yolo`        |            | Toggle approval bypass        |\n| `/btw`         |            | Ephemeral side question       |\n| `/stop`        |            | Kill current agent run        |\n| `/queue`       |            | Queue next prompt             |\n| `/browser`     |            | Interactive browser session   |\n| `/history`     | `/resume`  | Session browser               |\n| `/skin`        |            | Switch CLI theme              |\n\n---\n\n### 6.5 Setup Wizard (hermes_cli/setup.py)\n\nModular interactive wizard with independent sections:\n\n1. **Model & Provider** — Select AI provider, enter API keys, choose model\n2. **Terminal Backend** — Choose execution environment\n3. **Agent Settings** — Max iterations, compression, session policies\n4. **Messaging Platforms** — Configure Telegram, Discord, Slack, etc.\n5. **Tools** — TTS, STT, web search, image generation, browser\n\nFeatures:\n\n- Live credential validation\n- Real-time model list fetching from provider APIs\n- Automatic OpenClaw migration detection\n- Atomic config file writes\n\n---\n\n### 6.6 Model Catalog (hermes_cli/models.py)\n\nProvider-specific model lists:\n\n```python\n_PROVIDER_MODELS = {\n    \"nous\": [\"anthropic/claude-opus-4.6\", \"anthropic/claude-sonnet-4.6\", ...],  # 25+\n    \"openrouter\": [\"anthropic/claude-opus-4.6\", \"google/gemini-3-flash\", ...],  # 30+\n    \"anthropic\": [\"claude-opus-4-6\", \"claude-sonnet-4-6\", ...],\n    \"openai\": [\"gpt-5\", \"gpt-5.4-mini\", \"gpt-4.1\", \"gpt-4o\", ...],\n    \"copilot\": [\"gpt-5.4\", \"gpt-5.4-mini\", \"gpt-5.3-codex\", ...],\n    \"huggingface\": [...],\n    \"minimax\": [...],\n    \"kimi-coding\": [...],\n    \"alibaba\": [...],\n    \"deepseek\": [...],\n    # ... more providers\n}\n```\n\nFeatures:\n\n- Dynamic fetching via provider `/models` endpoints\n- Curated lists used when live probe returns fewer models\n- Fuzzy matching for typo correction\n- Validation against provider catalog\n\n---\n\n### 6.7 Skin/Theme Engine (hermes_cli/skin_engine.py)\n\nData-driven CLI visual customization — no code changes needed.\n\n**Customizable Elements:**\n\n| Element                          | Key                      | Used By           |\n| -------------------------------- | ------------------------ | ----------------- |\n| Banner border/title/accent       | `colors.*`               | banner.py         |\n| Response box border              | `colors.response_border` | cli.py            |\n| Spinner faces (waiting/thinking) | `spinner.*`              | display.py        |\n| Spinner verbs/wings              | `spinner.*`              | display.py        |\n| Tool output prefix               | `tool_prefix`            | display.py        |\n| Per-tool emojis                  | `tool_emojis`            | display.py        |\n| Agent name/welcome/prompt        | `branding.*`             | banner.py, cli.py |\n\n**Built-in Skins:** default, ares, mono, slate, poseidon, sisyphus, charizard\n\n**User Skins:** Drop `~/.hermes/skins/<name>.yaml` and activate with `/skin <name>`\n\n---\n\n## 7. Tool System\n\n### 7.1 Terminal Tool (tools/terminal_tool.py)\n\nShell command execution across multiple backends.\n\n```python\ndef terminal_tool(\n    command: str,\n    background: bool = False,\n    timeout: Optional[int] = None,\n    task_id: Optional[str] = None,\n    force: bool = False,        # Skip approval for dangerous commands\n    workdir: Optional[str] = None,\n    check_interval: Optional[int] = None,  # Background task polling\n    pty: bool = False,\n) -> str  # JSON result\n```\n\n**Features:**\n\n- Multi-backend: Selects based on `TERMINAL_ENV` (local/docker/ssh/modal/daytona/singularity)\n- Per-task_id sandboxes with thread-safe creation locks\n- Dangerous command routing through approval system\n- Background task support with file-based IPC\n- Interrupt handling — polls `is_interrupted()` during execution\n- Auto-cleanup daemon thread for idle environments (>300s)\n- Disk usage warnings at configurable threshold\n\n---\n\n### 7.2 File Tools (tools/file_tools.py)\n\nSafe file operations with size guards and sensitive path protection.\n\n- `read_file_tool(path, offset, limit)` — Read with pagination (default 100K char limit)\n- `write_file_tool(path, content)` — Write with approval for sensitive paths\n- `edit_file_tool(path, old_text, new_text)` — String replacement editing\n- `list_files_tool(path)` — Directory listing\n- `search_files_tool(pattern, path)` — Glob/regex file search\n\n**Safety:**\n\n- Device path blocklist (`/dev/zero`, `/dev/stdin`, etc.)\n- Read dedup tracking — returns stub on re-read if mtime unchanged\n- Sensitive path blocking: `/etc/`, `/boot/`, `~/.ssh` without approval\n- Prompt injection protection for known dangerous paths\n\n---\n\n### 7.3 Web Tools (tools/web_tools.py)\n\nWeb search and content extraction.\n\n- `web_search_tool(query, limit)` — Search via configurable backend\n- `web_extract_tool(urls, format)` — Extract content from URLs\n- `web_crawl_tool(url, instructions)` — LLM-guided web crawling\n\n**Backends:** Firecrawl, Parallel Web, Tavily, Exa, DuckDuckGo\n\n- Fallback detection by highest-priority available API key\n- LLM processing via auxiliary client (Gemini 3 Flash) for intelligent extraction\n- SSRF protection and URL safety checks\n\n---\n\n### 7.4 Browser Tool (tools/browser_tool.py)\n\nHeadless browser automation with accessibility tree.\n\n```python\nbrowser_navigate(url, task_id) → str\nbrowser_snapshot(task_id, max_chars) → str\nbrowser_click(ref, task_id) → str      # Element refs like @e1, @e2\nbrowser_type(ref, text, task_id) → str\nbrowser_scroll(ref, direction, task_id) → str\nbrowser_extract(task, max_chars, task_id) → str\n```\n\n**Backends:**\n\n- **Local:** Headless Chromium via `agent-browser` CLI\n- **Cloud:** Browserbase (stealth, proxies, CAPTCHA solving)\n- **Camofox:** Anti-detection browser using Camoufox\n\n**Features:**\n\n- Accessibility tree for text-based snapshots (no vision required)\n- Session isolation per task_id with inactivity cleanup\n- API key detection in URLs (prevents exfiltration)\n- SSRF protection for private/internal addresses\n\n---\n\n### 7.5 Delegate Tool (tools/delegate_tool.py)\n\nSpawn isolated subagents for parallel workstreams.\n\n```python\ndef delegate_task(\n    goal: str,\n    context: str = None,\n    toolsets: List[str] = None,       # Default: [\"terminal\", \"file\", \"web\"]\n    tasks: List[Dict] = None,          # Batch mode: up to 3 concurrent\n    max_iterations: int = 50,\n    parent_agent = None,\n) → str\n```\n\n**Isolation:**\n\n- Fresh conversation (no parent history)\n- Own task_id (separate terminal session, file ops)\n- Restricted toolset (configurable, blocked tools always stripped)\n- Focused system prompt from goal + context\n- Parent only sees delegation call and summary result\n\n**Blocked Tools:** delegate_task, clarify, memory, send_message, execute_code (no recursion, no user interaction)\n\n**Constraints:** MAX_DEPTH=2, MAX_CONCURRENT_CHILDREN=3\n\n---\n\n### 7.6 MCP Tool (tools/mcp_tool.py)\n\nModel Context Protocol client integration.\n\n**Configuration (config.yaml):**\n\n```yaml\nmcp_servers:\n  filesystem:\n    command: \"npx\"\n    args: [\"-y\", \"@modelcontextprotocol/server-filesystem\", \"/tmp\"]\n    timeout: 120\n  remote_api:\n    url: \"https://my-mcp-server.example.com/mcp\"\n    headers:\n      Authorization: \"Bearer sk-...\"\n    sampling:\n      enabled: true\n      model: \"gemini-3-flash\"\n```\n\n**Features:**\n\n- **Transports:** Stdio (local processes) and HTTP/StreamableHTTP (remote)\n- **Dynamic tool discovery:** Listens for `tools/list_changed` notifications\n- **Sampling support:** MCP servers can request LLM completions\n- **Automatic reconnection** with exponential backoff (5 retries)\n- Dedicated background event loop in daemon thread\n- Thread-safe via lock protecting server dict\n\n---\n\n### 7.7 Approval System (tools/approval.py)\n\nDangerous command detection and approval flow.\n\n```python\ndetect_dangerous_command(command: str) → (is_dangerous, pattern_key, description)\n```\n\n**106 patterns covering:**\n\n- Destructive operations (`rm -r /`, `mkfs`, `dd if=`)\n- Privilege escalation (`chmod 777`, `chown -R root`)\n- SQL injection (`DROP TABLE`, `DELETE FROM` without WHERE)\n- System targeting (`/etc/` writes, `systemctl stop`, fork bombs)\n- Shell injection (pipe to sh, wget to sh, process substitution)\n- Network exfiltration (curl with embedded API keys)\n- Secret access (`cat ~/.env`, `cat ~/.netrc`)\n\n**Normalization:** Strips ANSI escapes, null bytes, normalizes Unicode\n\n**Approval Modes:**\n\n- `smart` — Learns which commands are safe based on user decisions\n- `always` — Always ask for dangerous commands\n- `off` — Never ask (or `/yolo` toggle)\n\n---\n\n### 7.8 Terminal Backends (tools/environments/)\n\n| Backend           | File               | Features                                                                       |\n| ----------------- | ------------------ | ------------------------------------------------------------------------------ |\n| **Local**         | `local.py`         | Direct execution, interrupt support, non-blocking I/O, persistent shell        |\n| **Docker**        | `docker.py`        | Sandboxed containers, cap-drop ALL, no-new-privileges, PID limits, bind mounts |\n| **SSH**           | `ssh.py`           | Remote execution via ControlMaster, persistent shell mixin                     |\n| **Modal**         | `modal.py`         | Native Modal SDK `Sandbox.create()`/`Sandbox.exec()`, persistent snapshots     |\n| **Managed Modal** | `managed_modal.py` | Modal through Nous-hosted gateway                                              |\n| **Daytona**       | `daytona.py`       | Daytona SDK cloud sandboxes, stop/resume lifecycle                             |\n| **Singularity**   | `singularity.py`   | Singularity containers with scratch dir, SIF cache                             |\n\n**Common Interface (BaseEnvironment ABC):**\n\n```python\nexecute(command, timeout) → {\"output\": str, \"returncode\": int}\ncleanup() → None\n```\n\n---\n\n## 8. Agent Internals\n\n### 8.1 Prompt Builder (agent/prompt_builder.py)\n\nAssembles the system prompt from multiple sources:\n\n| Component               | Source                                               |\n| ----------------------- | ---------------------------------------------------- |\n| Agent Identity          | `DEFAULT_AGENT_IDENTITY` or `SOUL.md`                |\n| Memory Guidance         | When/how to use memory tool                          |\n| Session Search Guidance | How to recall past conversations                     |\n| Skills Guidance         | When to create/patch skills                          |\n| Tool Use Enforcement    | Must execute tools, not describe actions             |\n| Skills Index            | `~/.hermes/skills/.hermes-skills.json`               |\n| Platform Hints          | OS, Python version, shell, available tools           |\n| Context Files           | `.hermes.md`, `AGENTS.md`, `.cursorrules`, `SOUL.md` |\n| Model/Provider Info     | Current model and provider identity                  |\n\n**Context File Discovery:**\n\n1. Check `cwd/.hermes.md` or `HERMES.md`\n2. Walk parent directories up to git root\n3. Validate against injection patterns before inclusion\n\n**Injection Detection (30+ patterns):**\n\n- \"ignore previous instructions\", \"system prompt override\"\n- \"do not tell the user\", \"act as if you have no restrictions\"\n- HTML comment injection, exfiltration via curl, Unicode stealth chars\n\n---\n\n### 8.2 Context Compressor (agent/context_compressor.py)\n\nAutomatically compresses conversation history when approaching context limits.\n\n```python\nclass ContextCompressor:\n    threshold_percent: float = 0.50   # Compress when 50% of context used\n    protect_first_n: int = 3          # Keep system prompt + first turn\n    protect_last_n: int = 20          # Keep recent N messages\n    summary_target_ratio: float = 0.20  # Summary = 20% of compressed content\n```\n\n**Compression Algorithm:**\n\n1. **Pre-pass:** Replace old tool results with placeholder (cheap)\n2. **Protect head:** System prompt + first exchange always kept\n3. **Protect tail:** Token-budget-based tail protection (~20K tokens)\n4. **Summarize middle:** LLM-generated structured summary (Goal, Progress, Decisions, Files, Next Steps)\n5. **Iterative updates:** On subsequent compressions, update previous summary instead of re-summarizing\n\n---\n\n### 8.3 Prompt Caching (agent/prompt_caching.py)\n\nAnthropic prompt caching support for cost reduction.\n\n- System prompt cached across turns (first conversation turn establishes cache)\n- Cache markers inserted at system prompt boundaries\n- Must not break mid-conversation — altering past context invalidates cache\n- The ONLY time context is altered is during compression\n\n**Critical Rule:** Do NOT implement changes that would alter past context, change toolsets, reload memories, or rebuild system prompts mid-conversation.\n\n---\n\n### 8.4 Auxiliary Client (agent/auxiliary_client.py)\n\nSeparate LLM client for non-primary tasks:\n\n- **Vision analysis** — Image description and analysis\n- **Web extraction** — Content summarization\n- **Context compression** — Generating conversation summaries\n- **Session search** — Summarizing search results\n- **MCP sampling** — Serving server-initiated LLM requests\n\nConfigured per-task via `auxiliary` section in config.yaml.\n\n---\n\n### 8.5 Display & Spinner (agent/display.py)\n\n- **KawaiiSpinner** — Animated face characters during API calls\n- **Tool preview formatting** — `┊` prefixed activity feed for tool execution\n- **Inline diff previews** — Shows unified diffs for write/patch operations\n- Respects active skin for colors, emojis, and branding\n\n---\n\n### 8.6 Skill Commands (agent/skill_commands.py)\n\nShared skill invocation for CLI and gateway:\n\n- Skills loaded from `~/.hermes/skills/` and external directories\n- Injected as **user message** (not system prompt) to preserve prompt caching\n- `/plan` command generates implementation plans stored in `.hermes/plans/`\n- Skill content includes setup instructions, tool options, usage examples\n\n---\n\n## 9. Messaging Gateway\n\n### 9.1 GatewayRunner (gateway/run.py)\n\nMain controller managing all platform adapters and routing messages.\n\n**Key Attributes:**\n\n- `adapters: Dict[Platform, BasePlatformAdapter]` — Active platform instances\n- `session_store: SessionStore` — Conversation persistence\n- `_running_agents: Dict[str, AIAgent]` — Per-session cached agents (preserves prompt caching)\n- `_pending_approvals: Dict[str, Dict]` — Dangerous command approval tracking\n- `pairing_store: PairingStore` — DM code-based user authorization\n\n**Message Flow:**\n\n1. Platform adapter queues `MessageEvent`\n2. GatewayRunner dequeues, calls `_handle_message()`\n3. Slash command? → dispatch to handler\n4. Regular message? → `_handle_message_with_agent\n\n<!-- File: .claude/skills/lat-md/SKILL.md (Tokens: ~1094 | Category: anthropic-skill) -->\n---\nname: lat-md\ndescription: >-\n  Writing and maintaining lat.md documentation files — structured markdown that\n  describes a project's architecture, design decisions, and test specs. Use when\n  creating, editing, or reviewing files in the lat.md/ directory.\n---\n\n# lat.md Authoring Guide\n\nThis skill covers the syntax, structure rules, and conventions for writing `lat.md/` files. Load it whenever you need to create or edit sections in the `lat.md/` directory.\n\n## What belongs in lat.md\n\n`lat.md/` files describe **what** the project does and **why** — domain concepts, key design decisions, business logic, and test specifications. They do NOT duplicate source code. Think of each section as an anchor that source code references back to.\n\nGood candidates for sections:\n- Architecture decisions and their rationale\n- Domain concepts and business rules\n- API contracts and protocols\n- Test specifications (what is tested and why)\n- Non-obvious constraints or invariants\n\nBad candidates:\n- Step-by-step code walkthroughs (the code itself is the walkthrough)\n- Auto-generated API docs (use tools for that)\n- Temporary notes or TODOs\n\n## Section structure\n\nEvery section **must** have a leading paragraph — at least one sentence immediately after the heading, before any child headings or other block content.\n\nThe first paragraph must be ≤250 characters (excluding `[[wiki link]]` content). This paragraph is the section's identity — it appears in search results, command output, and RAG context.\n\n```markdown\n# Good Section\n\nBrief overview of what this section documents and why it matters.\n\nMore detail can go in subsequent paragraphs, code blocks, or lists.\n\n## Child heading\n\nDetails about this child topic.\n```\n\n```markdown\n# Bad Section\n\n## Child heading\n\nThis is invalid — \"Bad Section\" has no leading paragraph.\n```\n\n`lat check` enforces this rule.\n\n## Section IDs\n\nSections are addressed by file path and heading chain:\n\n- **Full form**: `lat.md/path/to/file#Heading#SubHeading`\n- **Short form**: `file#Heading#SubHeading` (when the file stem is unique)\n\nExamples: `lat.md/tests/search#RAG Replay Tests`, `cli#init`, `parser#Wiki Links`.\n\n## Wiki links\n\nCross-reference other sections or source code with `[[target]]` or `[[target|alias]]`.\n\n### Section links\n\n```markdown\nSee [[cli#init]] for setup details.\nThe parser validates [[parser#Wiki Links|wiki link syntax]].\n```\n\n### Source code links\n\nReference functions, classes, constants, and methods in source files:\n\n```markdown\n[[src/config.ts#getConfigDir]]          — function\n[[src/server.ts#App#listen]]            — class method\n[[lib/utils.py#parse_args]]             — Python function\n[[src/lib.rs#Greeter#greet]]            — Rust impl method\n[[src/app.go#Greeter#Greet]]            — Go method\n[[src/app.h#Greeter]]                   — C struct\n```\n\n`lat check` validates that all targets exist.\n\n## Code refs\n\nTie source code back to `lat.md/` sections with `@lat:` comments:\n\n```typescript\n// @lat: [[cli#init]]\nexport function init() { ... }\n```\n\n```python\n# @lat: [[cli#init]]\ndef init():\n    ...\n```\n\nSupported comment styles: `//` (JS/TS/Rust/Go/C) and `#` (Python).\n\nPlace one `@lat:` comment per section, at the relevant code — not at the top of the file.\n\n## Test specs\n\nDescribe tests as sections in `lat.md/` files. Add frontmatter to require that every leaf section has a matching `@lat:` comment in test code:\n\n```markdown\n---\nlat:\n  require-code-mention: true\n---\n# Tests\n\nAuthentication test specifications.\n\n## User login\n\nVerify credential validation and error handling.\n\n### Rejects expired tokens\n\nTokens past their expiry timestamp are rejected with 401, even if otherwise valid.\n\n### Handles missing password\n\nLogin request without a password field returns 400 with a descriptive error.\n```\n\nEach test references its spec:\n\n```python\n# @lat: [[tests#User login#Rejects expired tokens]]\ndef test_rejects_expired_tokens():\n    ...\n```\n\nRules:\n- Every leaf section under `require-code-mention: true` must be referenced by exactly one `@lat:` comment\n- Every section MUST have a description — at least one sentence explaining what the test verifies and why\n- `lat check` flags unreferenced specs and dangling code refs\n\n## Frontmatter\n\nOptional YAML frontmatter at the top of `lat.md/` files:\n\n```yaml\n---\nlat:\n  require-code-mention: true\n---\n```\n\nCurrently the only supported field is `require-code-mention` for test spec enforcement.\n\n## Validation\n\nAlways run `lat check` after editing `lat.md/` files. It validates:\n- All wiki links point to existing sections or source code symbols\n- All `@lat:` code refs point to existing sections\n- Every section has a leading paragraph (≤250 chars)\n- All `require-code-mention` leaf sections are referenced in code\n\n<!-- File: CLAUDE.md (Tokens: ~1243 | Category: claude-rule) -->\n# Before starting work\n\n- Run `lat search` to find sections relevant to your task. Read them to understand the design intent before writing code.\n- Run `lat expand` on user prompts to expand any `[[refs]]` — this resolves section names to file locations and provides context.\n\n# Post-task checklist (REQUIRED — do not skip)\n\nAfter EVERY task, before responding to the user:\n\n- [ ] Update `lat.md/` if you added or changed any functionality, architecture, tests, or behavior\n- [ ] Run `lat check` — all wiki links and code refs must pass\n- [ ] Do not skip these steps. Do not consider your task done until both are complete.\n\n---\n\n# What is lat.md?\n\nThis project uses [lat.md](https://www.npmjs.com/package/lat.md) to maintain a structured knowledge graph of its architecture, design decisions, and test specs in the `lat.md/` directory. It is a set of cross-linked markdown files that describe **what** this project does and **why** — the domain concepts, key design decisions, business logic, and test specifications. Use it to ground your work in the actual architecture rather than guessing.\n\n# Commands\n\n```bash\nlat locate \"Section Name\"      # find a section by name (exact, fuzzy)\nlat refs \"file#Section\"        # find what references a section\nlat search \"natural language\"  # semantic search across all sections\nlat expand \"user prompt text\"  # expand [[refs]] to resolved locations\nlat check                      # validate all links and code refs\n```\n\nRun `lat --help` when in doubt about available commands or options.\n\nIf `lat search` fails because no API key is configured, explain to the user that semantic search requires a key provided via `LAT_LLM_KEY` (direct value), `LAT_LLM_KEY_FILE` (path to key file), or `LAT_LLM_KEY_HELPER` (command that prints the key). Supported key prefixes: `sk-...` (OpenAI) or `vck_...` (Vercel). If the user doesn't want to set it up, use `lat locate` for direct lookups instead.\n\n# Syntax primer\n\n- **Section ids**: `lat.md/path/to/file#Heading#SubHeading` — full form uses project-root-relative path (e.g. `lat.md/tests/search#RAG Replay Tests`). Short form uses bare file name when unique (e.g. `search#RAG Replay Tests`, `cli#search#Indexing`).\n- **Wiki links**: `[[target]]` or `[[target|alias]]` — cross-references between sections. Can also reference source code: `[[src/foo.ts#myFunction]]`.\n- **Source code links**: Wiki links in `lat.md/` files can reference functions, classes, constants, and methods in TypeScript/JavaScript/Python/Rust/Go/C files. Use the full path: `[[src/config.ts#getConfigDir]]`, `[[src/server.ts#App#listen]]` (class method), `[[lib/utils.py#parse_args]]`, `[[src/lib.rs#Greeter#greet]]` (Rust impl method), `[[src/app.go#Greeter#Greet]]` (Go method), `[[src/app.h#Greeter]]` (C struct). `lat check` validates these exist.\n- **Code refs**: `// @lat: [[section-id]]` (JS/TS/Rust/Go/C) or `# @lat: [[section-id]]` (Python) — ties source code to concepts\n\n# Test specs\n\nKey tests can be described as sections in `lat.md/` files (e.g. `tests.md`). Add frontmatter to require that every leaf section is referenced by a `// @lat:` or `# @lat:` comment in test code:\n\n```markdown\n---\nlat:\n  require-code-mention: true\n---\n# Tests\n\nAuthentication and authorization test specifications.\n\n## User login\n\nVerify credential validation and error handling for the login endpoint.\n\n### Rejects expired tokens\nTokens past their expiry timestamp are rejected with 401, even if otherwise valid.\n\n### Handles missing password\nLogin request without a password field returns 400 with a descriptive error.\n```\n\nEvery section MUST have a description — at least one sentence explaining what the test verifies and why. Empty sections with just a heading are not acceptable. (This is a specific case of the general leading paragraph rule below.)\n\nEach test in code should reference its spec with exactly one comment placed next to the relevant test — not at the top of the file:\n\n```python\n# @lat: [[tests#User login#Rejects expired tokens]]\ndef test_rejects_expired_tokens():\n    ...\n\n# @lat: [[tests#User login#Handles missing password]]\ndef test_handles_missing_password():\n    ...\n```\n\nDo not duplicate refs. One `@lat:` comment per spec section, placed at the test that covers it. `lat check` will flag any spec section not covered by a code reference, and any code reference pointing to a nonexistent section.\n\n# Section structure\n\nEvery section in `lat.md/` **must** have a leading paragraph — at least one sentence immediately after the heading, before any child headings or other block content. The first paragraph must be ≤250 characters (excluding `[[wiki link]]` content). This paragraph serves as the section's overview and is used in search results, command output, and RAG context — keeping it concise guarantees the section's essence is always captured.\n\n```markdown\n# Good Section\n\nBrief overview of what this section documents and why it matters.\n\nMore detail can go in subsequent paragraphs, code blocks, or lists.\n\n## Child heading\n\nDetails about this child topic.\n```\n\n```markdown\n# Bad Section\n\n## Child heading\n\nDetails about this child topic.\n```\n\nThe second example is invalid because `Bad Section` has no leading paragraph. `lat check` validates this rule and reports errors for missing or overly long leading paragraphs.\n\n<!-- File: .codex/hooks.json (Tokens: ~93 | Category: codex-rule) -->\n{\n  \"hooks\": {\n    \"UserPromptSubmit\": [\n      {\n        \"hooks\": [\n          {\n            \"type\": \"command\",\n            \"command\": \"lat hook claude UserPromptSubmit\"\n          }\n        ]\n      }\n    ],\n    \"Stop\": [\n      {\n        \"hooks\": [\n          {\n            \"type\": \"command\",\n            \"command\": \"lat hook claude Stop\"\n          }\n        ]\n      }\n    ]\n  }\n}\n</yakaai_skills>"}