Files
hermes-agent/AGENTS.md

386 lines
16 KiB
Markdown
Raw Normal View History

# Hermes Agent - Development Guide
Instructions for AI coding assistants and developers working on the hermes-agent codebase.
## Development Environment
```bash
source .venv/bin/activate # ALWAYS activate before running Python
```
## Project Structure
```
hermes-agent/
├── run_agent.py # AIAgent class — core conversation loop
├── model_tools.py # Tool orchestration, _discover_tools(), handle_function_call()
├── toolsets.py # Toolset definitions, _HERMES_CORE_TOOLS list
├── cli.py # HermesCLI class — interactive CLI orchestrator
├── hermes_state.py # SessionDB — SQLite session store (FTS5 search)
├── agent/ # Agent internals
│ ├── prompt_builder.py # System prompt assembly
│ ├── context_compressor.py # Auto context compression
│ ├── prompt_caching.py # Anthropic prompt caching
docs: comprehensive AGENTS.md audit and corrections Major fixes: - Default model: claude-sonnet-4.6 → claude-opus-4.6 - max_iterations default: 60 → 90 (also fixed in config.py OPTIONAL_ENV_VARS description) - chat() signature: chat(user_message, task_id) → chat(message) - Agent loop: _run_agent_loop() doesn't exist, loop is in run_conversation() - Removed async/await references (agent is entirely synchronous) - KawaiiSpinner location: run_agent.py → agent/display.py - NOUS_API_KEY removed (not used by any tool), replaced with VOICE_TOOLS_OPENAI_KEY - OPENAI_API_KEY for Whisper → VOICE_TOOLS_OPENAI_KEY - check_for_missing_config() → check_config_version() + get_missing_env_vars() - Adding tools: '2 files' → '3 files' (tool + model_tools.py + toolsets.py) - Venv path: venv/ → .venv/ - Trajectory output path: trajectories/*.jsonl → trajectory_samples.jsonl - process_command() location clarified (HermesCLI in cli.py, not commands.py) - REQUIRED_ENV_VARS noted as intentionally empty - _config_version noted as currently at version 5 New content: - Project structure: added 40+ missing files across agent/, hermes_cli/, tools/, gateway/ - Full gateway/ directory listing with all modules and platforms/ - Added honcho_integration/, scripts/, tests/ directories - Added hermes_constants.py, hermes_time.py, trajectory_compressor.py, utils.py - CLI commands table: added 25+ missing commands (model, login, logout, whatsapp, skills subsystem, tools, insights, gateway start/stop/restart/status/uninstall, sessions export/delete/prune/stats, config path/env-path/show) - Gateway slash commands section with all 20+ commands - Platform toolsets: added hermes-cli, hermes-slack, hermes-homeassistant, hermes-gateway - Gateway: added Home Assistant as supported platform
2026-03-08 17:38:05 -07:00
│ ├── auxiliary_client.py # Auxiliary LLM client (vision, summarization)
│ ├── model_metadata.py # Model context lengths, token estimation
│ ├── display.py # KawaiiSpinner, tool preview formatting
│ ├── skill_commands.py # Skill slash commands (shared CLI/gateway)
│ └── trajectory.py # Trajectory saving helpers
├── hermes_cli/ # CLI subcommands and setup
│ ├── main.py # Entry point — all `hermes` subcommands
│ ├── config.py # DEFAULT_CONFIG, OPTIONAL_ENV_VARS, migration
docs: comprehensive AGENTS.md audit and corrections Major fixes: - Default model: claude-sonnet-4.6 → claude-opus-4.6 - max_iterations default: 60 → 90 (also fixed in config.py OPTIONAL_ENV_VARS description) - chat() signature: chat(user_message, task_id) → chat(message) - Agent loop: _run_agent_loop() doesn't exist, loop is in run_conversation() - Removed async/await references (agent is entirely synchronous) - KawaiiSpinner location: run_agent.py → agent/display.py - NOUS_API_KEY removed (not used by any tool), replaced with VOICE_TOOLS_OPENAI_KEY - OPENAI_API_KEY for Whisper → VOICE_TOOLS_OPENAI_KEY - check_for_missing_config() → check_config_version() + get_missing_env_vars() - Adding tools: '2 files' → '3 files' (tool + model_tools.py + toolsets.py) - Venv path: venv/ → .venv/ - Trajectory output path: trajectories/*.jsonl → trajectory_samples.jsonl - process_command() location clarified (HermesCLI in cli.py, not commands.py) - REQUIRED_ENV_VARS noted as intentionally empty - _config_version noted as currently at version 5 New content: - Project structure: added 40+ missing files across agent/, hermes_cli/, tools/, gateway/ - Full gateway/ directory listing with all modules and platforms/ - Added honcho_integration/, scripts/, tests/ directories - Added hermes_constants.py, hermes_time.py, trajectory_compressor.py, utils.py - CLI commands table: added 25+ missing commands (model, login, logout, whatsapp, skills subsystem, tools, insights, gateway start/stop/restart/status/uninstall, sessions export/delete/prune/stats, config path/env-path/show) - Gateway slash commands section with all 20+ commands - Platform toolsets: added hermes-cli, hermes-slack, hermes-homeassistant, hermes-gateway - Gateway: added Home Assistant as supported platform
2026-03-08 17:38:05 -07:00
│ ├── commands.py # Slash command definitions + SlashCommandCompleter
│ ├── callbacks.py # Terminal callbacks (clarify, sudo, approval)
│ ├── setup.py # Interactive setup wizard
│ ├── skin_engine.py # Skin/theme engine — CLI visual customization
│ ├── skills_config.py # `hermes skills` — enable/disable skills per platform
│ ├── tools_config.py # `hermes tools` — enable/disable tools per platform
│ ├── skills_hub.py # `/skills` slash command (search, browse, install)
│ ├── models.py # Model catalog, provider model lists
│ └── auth.py # Provider credential resolution
├── tools/ # Tool implementations (one file per tool)
│ ├── registry.py # Central tool registry (schemas, handlers, dispatch)
│ ├── approval.py # Dangerous command detection
│ ├── terminal_tool.py # Terminal orchestration
│ ├── process_registry.py # Background process management
│ ├── file_tools.py # File read/write/search/patch
feat(web): add Parallel as alternative web search/extract backend (#1696) * feat(web): add Parallel as alternative web search/extract backend Adds Parallel (parallel.ai) as a drop-in alternative to Firecrawl for web_search and web_extract tools using the official parallel-web SDK. - Backend selection via WEB_SEARCH_BACKEND env var (auto/parallel/firecrawl) - Auto mode prefers Firecrawl when both keys present; Parallel when sole backend - web_crawl remains Firecrawl-only with clear error when unavailable - Lazy SDK imports, interrupt support, singleton clients - 16 new unit tests for backend selection and client config Co-authored-by: s-jag <s-jag@users.noreply.github.com> * fix: add PARALLEL_API_KEY to config registry and fix web_crawl policy tests Follow-up for Parallel backend integration: - Add PARALLEL_API_KEY to OPTIONAL_ENV_VARS (hermes doctor, env blocklist) - Add to set_config_value api_keys list (hermes config set) - Add to doctor keys display - Fix 2 web_crawl policy tests that didn't set FIRECRAWL_API_KEY (needed now that web_crawl has a Firecrawl availability guard) * refactor: explicit backend selection via hermes tools, not auto-detect Replace the auto-detect backend selection with explicit user choice: - hermes tools saves WEB_SEARCH_BACKEND to .env when user picks a provider - _get_backend() reads the explicit choice first - Fallback only for manual/legacy config (uses whichever key is present) - _is_provider_active() shows [active] for the selected web backend - Updated tests, docs, and .env.example to remove 'auto' mode language * refactor: use config.yaml for web backend, not env var Match the TTS/browser pattern — web.backend is stored in config.yaml (set by hermes tools), not as a WEB_SEARCH_BACKEND env var. - _load_web_config() reads web: section from config.yaml - _get_backend() reads web.backend from config, falls back to key detection - _configure_provider() saves to config dict (saved to config.yaml) - _is_provider_active() reads from config dict - Removed WEB_SEARCH_BACKEND from .env.example, set_config_value, docs - Updated all tests to mock _load_web_config instead of env vars --------- Co-authored-by: s-jag <s-jag@users.noreply.github.com>
2026-03-17 04:02:02 -07:00
│ ├── web_tools.py # Web search/extract (Parallel + Firecrawl)
│ ├── browser_tool.py # Browserbase browser automation
docs: comprehensive AGENTS.md audit and corrections Major fixes: - Default model: claude-sonnet-4.6 → claude-opus-4.6 - max_iterations default: 60 → 90 (also fixed in config.py OPTIONAL_ENV_VARS description) - chat() signature: chat(user_message, task_id) → chat(message) - Agent loop: _run_agent_loop() doesn't exist, loop is in run_conversation() - Removed async/await references (agent is entirely synchronous) - KawaiiSpinner location: run_agent.py → agent/display.py - NOUS_API_KEY removed (not used by any tool), replaced with VOICE_TOOLS_OPENAI_KEY - OPENAI_API_KEY for Whisper → VOICE_TOOLS_OPENAI_KEY - check_for_missing_config() → check_config_version() + get_missing_env_vars() - Adding tools: '2 files' → '3 files' (tool + model_tools.py + toolsets.py) - Venv path: venv/ → .venv/ - Trajectory output path: trajectories/*.jsonl → trajectory_samples.jsonl - process_command() location clarified (HermesCLI in cli.py, not commands.py) - REQUIRED_ENV_VARS noted as intentionally empty - _config_version noted as currently at version 5 New content: - Project structure: added 40+ missing files across agent/, hermes_cli/, tools/, gateway/ - Full gateway/ directory listing with all modules and platforms/ - Added honcho_integration/, scripts/, tests/ directories - Added hermes_constants.py, hermes_time.py, trajectory_compressor.py, utils.py - CLI commands table: added 25+ missing commands (model, login, logout, whatsapp, skills subsystem, tools, insights, gateway start/stop/restart/status/uninstall, sessions export/delete/prune/stats, config path/env-path/show) - Gateway slash commands section with all 20+ commands - Platform toolsets: added hermes-cli, hermes-slack, hermes-homeassistant, hermes-gateway - Gateway: added Home Assistant as supported platform
2026-03-08 17:38:05 -07:00
│ ├── code_execution_tool.py # execute_code sandbox
│ ├── delegate_tool.py # Subagent delegation
│ ├── mcp_tool.py # MCP client (~1050 lines)
│ └── environments/ # Terminal backends (local, docker, ssh, modal, daytona, singularity)
├── gateway/ # Messaging platform gateway
│ ├── run.py # Main loop, slash commands, message dispatch
docs: comprehensive AGENTS.md audit and corrections Major fixes: - Default model: claude-sonnet-4.6 → claude-opus-4.6 - max_iterations default: 60 → 90 (also fixed in config.py OPTIONAL_ENV_VARS description) - chat() signature: chat(user_message, task_id) → chat(message) - Agent loop: _run_agent_loop() doesn't exist, loop is in run_conversation() - Removed async/await references (agent is entirely synchronous) - KawaiiSpinner location: run_agent.py → agent/display.py - NOUS_API_KEY removed (not used by any tool), replaced with VOICE_TOOLS_OPENAI_KEY - OPENAI_API_KEY for Whisper → VOICE_TOOLS_OPENAI_KEY - check_for_missing_config() → check_config_version() + get_missing_env_vars() - Adding tools: '2 files' → '3 files' (tool + model_tools.py + toolsets.py) - Venv path: venv/ → .venv/ - Trajectory output path: trajectories/*.jsonl → trajectory_samples.jsonl - process_command() location clarified (HermesCLI in cli.py, not commands.py) - REQUIRED_ENV_VARS noted as intentionally empty - _config_version noted as currently at version 5 New content: - Project structure: added 40+ missing files across agent/, hermes_cli/, tools/, gateway/ - Full gateway/ directory listing with all modules and platforms/ - Added honcho_integration/, scripts/, tests/ directories - Added hermes_constants.py, hermes_time.py, trajectory_compressor.py, utils.py - CLI commands table: added 25+ missing commands (model, login, logout, whatsapp, skills subsystem, tools, insights, gateway start/stop/restart/status/uninstall, sessions export/delete/prune/stats, config path/env-path/show) - Gateway slash commands section with all 20+ commands - Platform toolsets: added hermes-cli, hermes-slack, hermes-homeassistant, hermes-gateway - Gateway: added Home Assistant as supported platform
2026-03-08 17:38:05 -07:00
│ ├── session.py # SessionStore — conversation persistence
│ └── platforms/ # Adapters: telegram, discord, slack, whatsapp, homeassistant, signal
├── acp_adapter/ # ACP server (VS Code / Zed / JetBrains integration)
├── cron/ # Scheduler (jobs.py, scheduler.py)
├── environments/ # RL training environments (Atropos)
├── tests/ # Pytest suite (~3000 tests)
└── batch_runner.py # Parallel batch processing
```
**User config:** `~/.hermes/config.yaml` (settings), `~/.hermes/.env` (API keys)
## File Dependency Chain
```
tools/registry.py (no deps — imported by all tool files)
tools/*.py (each calls registry.register() at import time)
model_tools.py (imports tools/registry + triggers tool discovery)
run_agent.py, cli.py, batch_runner.py, environments/
```
---
## AIAgent Class (run_agent.py)
```python
class AIAgent:
def __init__(self,
model: str = "anthropic/claude-opus-4.6",
max_iterations: int = 90,
enabled_toolsets: list = None,
disabled_toolsets: list = None,
quiet_mode: bool = False,
docs: comprehensive AGENTS.md audit and corrections Major fixes: - Default model: claude-sonnet-4.6 → claude-opus-4.6 - max_iterations default: 60 → 90 (also fixed in config.py OPTIONAL_ENV_VARS description) - chat() signature: chat(user_message, task_id) → chat(message) - Agent loop: _run_agent_loop() doesn't exist, loop is in run_conversation() - Removed async/await references (agent is entirely synchronous) - KawaiiSpinner location: run_agent.py → agent/display.py - NOUS_API_KEY removed (not used by any tool), replaced with VOICE_TOOLS_OPENAI_KEY - OPENAI_API_KEY for Whisper → VOICE_TOOLS_OPENAI_KEY - check_for_missing_config() → check_config_version() + get_missing_env_vars() - Adding tools: '2 files' → '3 files' (tool + model_tools.py + toolsets.py) - Venv path: venv/ → .venv/ - Trajectory output path: trajectories/*.jsonl → trajectory_samples.jsonl - process_command() location clarified (HermesCLI in cli.py, not commands.py) - REQUIRED_ENV_VARS noted as intentionally empty - _config_version noted as currently at version 5 New content: - Project structure: added 40+ missing files across agent/, hermes_cli/, tools/, gateway/ - Full gateway/ directory listing with all modules and platforms/ - Added honcho_integration/, scripts/, tests/ directories - Added hermes_constants.py, hermes_time.py, trajectory_compressor.py, utils.py - CLI commands table: added 25+ missing commands (model, login, logout, whatsapp, skills subsystem, tools, insights, gateway start/stop/restart/status/uninstall, sessions export/delete/prune/stats, config path/env-path/show) - Gateway slash commands section with all 20+ commands - Platform toolsets: added hermes-cli, hermes-slack, hermes-homeassistant, hermes-gateway - Gateway: added Home Assistant as supported platform
2026-03-08 17:38:05 -07:00
save_trajectories: bool = False,
platform: str = None, # "cli", "telegram", etc.
docs: comprehensive AGENTS.md audit and corrections Major fixes: - Default model: claude-sonnet-4.6 → claude-opus-4.6 - max_iterations default: 60 → 90 (also fixed in config.py OPTIONAL_ENV_VARS description) - chat() signature: chat(user_message, task_id) → chat(message) - Agent loop: _run_agent_loop() doesn't exist, loop is in run_conversation() - Removed async/await references (agent is entirely synchronous) - KawaiiSpinner location: run_agent.py → agent/display.py - NOUS_API_KEY removed (not used by any tool), replaced with VOICE_TOOLS_OPENAI_KEY - OPENAI_API_KEY for Whisper → VOICE_TOOLS_OPENAI_KEY - check_for_missing_config() → check_config_version() + get_missing_env_vars() - Adding tools: '2 files' → '3 files' (tool + model_tools.py + toolsets.py) - Venv path: venv/ → .venv/ - Trajectory output path: trajectories/*.jsonl → trajectory_samples.jsonl - process_command() location clarified (HermesCLI in cli.py, not commands.py) - REQUIRED_ENV_VARS noted as intentionally empty - _config_version noted as currently at version 5 New content: - Project structure: added 40+ missing files across agent/, hermes_cli/, tools/, gateway/ - Full gateway/ directory listing with all modules and platforms/ - Added honcho_integration/, scripts/, tests/ directories - Added hermes_constants.py, hermes_time.py, trajectory_compressor.py, utils.py - CLI commands table: added 25+ missing commands (model, login, logout, whatsapp, skills subsystem, tools, insights, gateway start/stop/restart/status/uninstall, sessions export/delete/prune/stats, config path/env-path/show) - Gateway slash commands section with all 20+ commands - Platform toolsets: added hermes-cli, hermes-slack, hermes-homeassistant, hermes-gateway - Gateway: added Home Assistant as supported platform
2026-03-08 17:38:05 -07:00
session_id: str = None,
skip_context_files: bool = False,
skip_memory: bool = False,
# ... plus provider, api_mode, callbacks, routing params
): ...
docs: comprehensive AGENTS.md audit and corrections Major fixes: - Default model: claude-sonnet-4.6 → claude-opus-4.6 - max_iterations default: 60 → 90 (also fixed in config.py OPTIONAL_ENV_VARS description) - chat() signature: chat(user_message, task_id) → chat(message) - Agent loop: _run_agent_loop() doesn't exist, loop is in run_conversation() - Removed async/await references (agent is entirely synchronous) - KawaiiSpinner location: run_agent.py → agent/display.py - NOUS_API_KEY removed (not used by any tool), replaced with VOICE_TOOLS_OPENAI_KEY - OPENAI_API_KEY for Whisper → VOICE_TOOLS_OPENAI_KEY - check_for_missing_config() → check_config_version() + get_missing_env_vars() - Adding tools: '2 files' → '3 files' (tool + model_tools.py + toolsets.py) - Venv path: venv/ → .venv/ - Trajectory output path: trajectories/*.jsonl → trajectory_samples.jsonl - process_command() location clarified (HermesCLI in cli.py, not commands.py) - REQUIRED_ENV_VARS noted as intentionally empty - _config_version noted as currently at version 5 New content: - Project structure: added 40+ missing files across agent/, hermes_cli/, tools/, gateway/ - Full gateway/ directory listing with all modules and platforms/ - Added honcho_integration/, scripts/, tests/ directories - Added hermes_constants.py, hermes_time.py, trajectory_compressor.py, utils.py - CLI commands table: added 25+ missing commands (model, login, logout, whatsapp, skills subsystem, tools, insights, gateway start/stop/restart/status/uninstall, sessions export/delete/prune/stats, config path/env-path/show) - Gateway slash commands section with all 20+ commands - Platform toolsets: added hermes-cli, hermes-slack, hermes-homeassistant, hermes-gateway - Gateway: added Home Assistant as supported platform
2026-03-08 17:38:05 -07:00
def chat(self, message: str) -> str:
"""Simple interface — returns final response string."""
def run_conversation(self, user_message: str, system_message: str = None,
conversation_history: list = None, task_id: str = None) -> dict:
"""Full interface — returns dict with final_response + messages."""
```
### Agent Loop
The core loop is inside `run_conversation()` — entirely synchronous:
```python
docs: comprehensive AGENTS.md audit and corrections Major fixes: - Default model: claude-sonnet-4.6 → claude-opus-4.6 - max_iterations default: 60 → 90 (also fixed in config.py OPTIONAL_ENV_VARS description) - chat() signature: chat(user_message, task_id) → chat(message) - Agent loop: _run_agent_loop() doesn't exist, loop is in run_conversation() - Removed async/await references (agent is entirely synchronous) - KawaiiSpinner location: run_agent.py → agent/display.py - NOUS_API_KEY removed (not used by any tool), replaced with VOICE_TOOLS_OPENAI_KEY - OPENAI_API_KEY for Whisper → VOICE_TOOLS_OPENAI_KEY - check_for_missing_config() → check_config_version() + get_missing_env_vars() - Adding tools: '2 files' → '3 files' (tool + model_tools.py + toolsets.py) - Venv path: venv/ → .venv/ - Trajectory output path: trajectories/*.jsonl → trajectory_samples.jsonl - process_command() location clarified (HermesCLI in cli.py, not commands.py) - REQUIRED_ENV_VARS noted as intentionally empty - _config_version noted as currently at version 5 New content: - Project structure: added 40+ missing files across agent/, hermes_cli/, tools/, gateway/ - Full gateway/ directory listing with all modules and platforms/ - Added honcho_integration/, scripts/, tests/ directories - Added hermes_constants.py, hermes_time.py, trajectory_compressor.py, utils.py - CLI commands table: added 25+ missing commands (model, login, logout, whatsapp, skills subsystem, tools, insights, gateway start/stop/restart/status/uninstall, sessions export/delete/prune/stats, config path/env-path/show) - Gateway slash commands section with all 20+ commands - Platform toolsets: added hermes-cli, hermes-slack, hermes-homeassistant, hermes-gateway - Gateway: added Home Assistant as supported platform
2026-03-08 17:38:05 -07:00
while api_call_count < self.max_iterations and self.iteration_budget.remaining > 0:
response = client.chat.completions.create(model=model, messages=messages, tools=tool_schemas)
if response.tool_calls:
for tool_call in response.tool_calls:
docs: comprehensive AGENTS.md audit and corrections Major fixes: - Default model: claude-sonnet-4.6 → claude-opus-4.6 - max_iterations default: 60 → 90 (also fixed in config.py OPTIONAL_ENV_VARS description) - chat() signature: chat(user_message, task_id) → chat(message) - Agent loop: _run_agent_loop() doesn't exist, loop is in run_conversation() - Removed async/await references (agent is entirely synchronous) - KawaiiSpinner location: run_agent.py → agent/display.py - NOUS_API_KEY removed (not used by any tool), replaced with VOICE_TOOLS_OPENAI_KEY - OPENAI_API_KEY for Whisper → VOICE_TOOLS_OPENAI_KEY - check_for_missing_config() → check_config_version() + get_missing_env_vars() - Adding tools: '2 files' → '3 files' (tool + model_tools.py + toolsets.py) - Venv path: venv/ → .venv/ - Trajectory output path: trajectories/*.jsonl → trajectory_samples.jsonl - process_command() location clarified (HermesCLI in cli.py, not commands.py) - REQUIRED_ENV_VARS noted as intentionally empty - _config_version noted as currently at version 5 New content: - Project structure: added 40+ missing files across agent/, hermes_cli/, tools/, gateway/ - Full gateway/ directory listing with all modules and platforms/ - Added honcho_integration/, scripts/, tests/ directories - Added hermes_constants.py, hermes_time.py, trajectory_compressor.py, utils.py - CLI commands table: added 25+ missing commands (model, login, logout, whatsapp, skills subsystem, tools, insights, gateway start/stop/restart/status/uninstall, sessions export/delete/prune/stats, config path/env-path/show) - Gateway slash commands section with all 20+ commands - Platform toolsets: added hermes-cli, hermes-slack, hermes-homeassistant, hermes-gateway - Gateway: added Home Assistant as supported platform
2026-03-08 17:38:05 -07:00
result = handle_function_call(tool_call.name, tool_call.args, task_id)
messages.append(tool_result_message(result))
docs: comprehensive AGENTS.md audit and corrections Major fixes: - Default model: claude-sonnet-4.6 → claude-opus-4.6 - max_iterations default: 60 → 90 (also fixed in config.py OPTIONAL_ENV_VARS description) - chat() signature: chat(user_message, task_id) → chat(message) - Agent loop: _run_agent_loop() doesn't exist, loop is in run_conversation() - Removed async/await references (agent is entirely synchronous) - KawaiiSpinner location: run_agent.py → agent/display.py - NOUS_API_KEY removed (not used by any tool), replaced with VOICE_TOOLS_OPENAI_KEY - OPENAI_API_KEY for Whisper → VOICE_TOOLS_OPENAI_KEY - check_for_missing_config() → check_config_version() + get_missing_env_vars() - Adding tools: '2 files' → '3 files' (tool + model_tools.py + toolsets.py) - Venv path: venv/ → .venv/ - Trajectory output path: trajectories/*.jsonl → trajectory_samples.jsonl - process_command() location clarified (HermesCLI in cli.py, not commands.py) - REQUIRED_ENV_VARS noted as intentionally empty - _config_version noted as currently at version 5 New content: - Project structure: added 40+ missing files across agent/, hermes_cli/, tools/, gateway/ - Full gateway/ directory listing with all modules and platforms/ - Added honcho_integration/, scripts/, tests/ directories - Added hermes_constants.py, hermes_time.py, trajectory_compressor.py, utils.py - CLI commands table: added 25+ missing commands (model, login, logout, whatsapp, skills subsystem, tools, insights, gateway start/stop/restart/status/uninstall, sessions export/delete/prune/stats, config path/env-path/show) - Gateway slash commands section with all 20+ commands - Platform toolsets: added hermes-cli, hermes-slack, hermes-homeassistant, hermes-gateway - Gateway: added Home Assistant as supported platform
2026-03-08 17:38:05 -07:00
api_call_count += 1
else:
return response.content
```
Messages follow OpenAI format: `{"role": "system/user/assistant/tool", ...}`. Reasoning content is stored in `assistant_msg["reasoning"]`.
---
## CLI Architecture (cli.py)
- **Rich** for banner/panels, **prompt_toolkit** for input with autocomplete
- **KawaiiSpinner** (`agent/display.py`) — animated faces during API calls, `┊` activity feed for tool results
- `load_cli_config()` in cli.py merges hardcoded defaults + user config YAML
- **Skin engine** (`hermes_cli/skin_engine.py`) — data-driven CLI theming; initialized from `display.skin` config key at startup; skins customize banner colors, spinner faces/verbs/wings, tool prefix, response box, branding text
refactor: centralize slash command registry (#1603) * refactor: centralize slash command registry Replace 7+ scattered command definition sites with a single CommandDef registry in hermes_cli/commands.py. All downstream consumers now derive from this registry: - CLI process_command() resolves aliases via resolve_command() - Gateway _known_commands uses GATEWAY_KNOWN_COMMANDS frozenset - Gateway help text generated by gateway_help_lines() - Telegram BotCommands generated by telegram_bot_commands() - Slack subcommand map generated by slack_subcommand_map() Adding a command or alias is now a one-line change to COMMAND_REGISTRY instead of touching 6+ files. Bugfixes included: - Telegram now registers /rollback, /background (were missing) - Slack now has /voice, /update, /reload-mcp (were missing) - Gateway duplicate 'reasoning' dispatch (dead code) removed - Gateway help text can no longer drift from CLI help Backwards-compatible: COMMANDS and COMMANDS_BY_CATEGORY dicts are rebuilt from the registry, so existing imports work unchanged. * docs: update developer docs for centralized command registry Update AGENTS.md with full 'Slash Command Registry' and 'Adding a Slash Command' sections covering CommandDef fields, registry helpers, and the one-line alias workflow. Also update: - CONTRIBUTING.md: commands.py description - website/docs/reference/slash-commands.md: reference central registry - docs/plans/centralize-command-registry.md: mark COMPLETED - plans/checkpoint-rollback.md: reference new pattern - hermes-agent-dev skill: architecture table * chore: remove stale plan docs
2026-03-16 23:21:03 -07:00
- `process_command()` is a method on `HermesCLI` — dispatches on canonical command name resolved via `resolve_command()` from the central registry
- Skill slash commands: `agent/skill_commands.py` scans `~/.hermes/skills/`, injects as **user message** (not system prompt) to preserve prompt caching
refactor: centralize slash command registry (#1603) * refactor: centralize slash command registry Replace 7+ scattered command definition sites with a single CommandDef registry in hermes_cli/commands.py. All downstream consumers now derive from this registry: - CLI process_command() resolves aliases via resolve_command() - Gateway _known_commands uses GATEWAY_KNOWN_COMMANDS frozenset - Gateway help text generated by gateway_help_lines() - Telegram BotCommands generated by telegram_bot_commands() - Slack subcommand map generated by slack_subcommand_map() Adding a command or alias is now a one-line change to COMMAND_REGISTRY instead of touching 6+ files. Bugfixes included: - Telegram now registers /rollback, /background (were missing) - Slack now has /voice, /update, /reload-mcp (were missing) - Gateway duplicate 'reasoning' dispatch (dead code) removed - Gateway help text can no longer drift from CLI help Backwards-compatible: COMMANDS and COMMANDS_BY_CATEGORY dicts are rebuilt from the registry, so existing imports work unchanged. * docs: update developer docs for centralized command registry Update AGENTS.md with full 'Slash Command Registry' and 'Adding a Slash Command' sections covering CommandDef fields, registry helpers, and the one-line alias workflow. Also update: - CONTRIBUTING.md: commands.py description - website/docs/reference/slash-commands.md: reference central registry - docs/plans/centralize-command-registry.md: mark COMPLETED - plans/checkpoint-rollback.md: reference new pattern - hermes-agent-dev skill: architecture table * chore: remove stale plan docs
2026-03-16 23:21:03 -07:00
### Slash Command Registry (`hermes_cli/commands.py`)
refactor: centralize slash command registry (#1603) * refactor: centralize slash command registry Replace 7+ scattered command definition sites with a single CommandDef registry in hermes_cli/commands.py. All downstream consumers now derive from this registry: - CLI process_command() resolves aliases via resolve_command() - Gateway _known_commands uses GATEWAY_KNOWN_COMMANDS frozenset - Gateway help text generated by gateway_help_lines() - Telegram BotCommands generated by telegram_bot_commands() - Slack subcommand map generated by slack_subcommand_map() Adding a command or alias is now a one-line change to COMMAND_REGISTRY instead of touching 6+ files. Bugfixes included: - Telegram now registers /rollback, /background (were missing) - Slack now has /voice, /update, /reload-mcp (were missing) - Gateway duplicate 'reasoning' dispatch (dead code) removed - Gateway help text can no longer drift from CLI help Backwards-compatible: COMMANDS and COMMANDS_BY_CATEGORY dicts are rebuilt from the registry, so existing imports work unchanged. * docs: update developer docs for centralized command registry Update AGENTS.md with full 'Slash Command Registry' and 'Adding a Slash Command' sections covering CommandDef fields, registry helpers, and the one-line alias workflow. Also update: - CONTRIBUTING.md: commands.py description - website/docs/reference/slash-commands.md: reference central registry - docs/plans/centralize-command-registry.md: mark COMPLETED - plans/checkpoint-rollback.md: reference new pattern - hermes-agent-dev skill: architecture table * chore: remove stale plan docs
2026-03-16 23:21:03 -07:00
All slash commands are defined in a central `COMMAND_REGISTRY` list of `CommandDef` objects. Every downstream consumer derives from this registry automatically:
- **CLI** — `process_command()` resolves aliases via `resolve_command()`, dispatches on canonical name
- **Gateway** — `GATEWAY_KNOWN_COMMANDS` frozenset for hook emission, `resolve_command()` for dispatch
- **Gateway help** — `gateway_help_lines()` generates `/help` output
- **Telegram** — `telegram_bot_commands()` generates the BotCommand menu
- **Slack** — `slack_subcommand_map()` generates `/hermes` subcommand routing
- **Autocomplete** — `COMMANDS` flat dict feeds `SlashCommandCompleter`
- **CLI help** — `COMMANDS_BY_CATEGORY` dict feeds `show_help()`
### Adding a Slash Command
1. Add a `CommandDef` entry to `COMMAND_REGISTRY` in `hermes_cli/commands.py`:
```python
CommandDef("mycommand", "Description of what it does", "Session",
aliases=("mc",), args_hint="[arg]"),
```
2. Add handler in `HermesCLI.process_command()` in `cli.py`:
```python
elif canonical == "mycommand":
self._handle_mycommand(cmd_original)
```
3. If the command is available in the gateway, add a handler in `gateway/run.py`:
```python
if canonical == "mycommand":
return await self._handle_mycommand(event)
```
4. For persistent settings, use `save_config_value()` in `cli.py`
**CommandDef fields:**
- `name` — canonical name without slash (e.g. `"background"`)
- `description` — human-readable description
- `category` — one of `"Session"`, `"Configuration"`, `"Tools & Skills"`, `"Info"`, `"Exit"`
- `aliases` — tuple of alternative names (e.g. `("bg",)`)
- `args_hint` — argument placeholder shown in help (e.g. `"<prompt>"`, `"[name]"`)
- `cli_only` — only available in the interactive CLI
- `gateway_only` — only available in messaging platforms
**Adding an alias** requires only adding it to the `aliases` tuple on the existing `CommandDef`. No other file changes needed — dispatch, help text, Telegram menu, Slack mapping, and autocomplete all update automatically.
Add background process management with process tool, wait, PTY, and stdin support New process registry and tool for managing long-running background processes across all terminal backends (local, Docker, Singularity, Modal, SSH). Process Registry (tools/process_registry.py): - ProcessSession tracking with rolling 200KB output buffer - spawn_local() with optional PTY via ptyprocess for interactive CLIs - spawn_via_env() for non-local backends (runs inside sandbox, never on host) - Background reader threads per process (Popen stdout or PTY) - wait() with timeout clamping, interrupt support, and transparent limit reporting - JSON checkpoint to ~/.hermes/processes.json for gateway crash recovery - Module-level singleton shared across agent loop, gateway, and RL Process Tool (model_tools.py): - 7 actions: list, poll, log, wait, kill, write, submit - Paired with terminal in all toolsets (CLI, messaging, RL) - Timeout clamping with transparent notes in response Terminal Tool Updates (tools/terminal_tool.py): - Replaced nohup background mode with registry spawn (returns session_id) - Added workdir parameter for per-command working directory - Added check_interval parameter for gateway auto-check watchers - Added pty parameter for interactive CLI tools (Codex, Claude Code) - Updated TERMINAL_TOOL_DESCRIPTION with full background workflow docs - Cleanup thread now respects active background processes (won't reap sandbox) Gateway Integration (gateway/run.py, session.py, config.py): - Session reset protection: sessions with active processes exempt from reset - Default idle timeout increased from 2 hours to 24 hours - from_dict fallback aligned to match (was 120, now 1440) - session_key env var propagated to process registry for session mapping - Crash recovery on gateway startup via checkpoint probe - check_interval watcher: asyncio task polls process, delivers updates to platform RL Safety (environments/): - tool_context.py cleanup() kills background processes on episode end - hermes_base_env.py warns when enabled_toolsets is None (loads all tools) - Process tool safe in RL via wait() blocking the agent loop Also: - Added ptyprocess as optional dependency (in pyproject.toml [pty] extra + [all]) - Fixed pre-existing bug: rl_test_inference missing from TOOL_TO_TOOLSET_MAP - Updated AGENTS.md with process management docs and project structure - Updated README.md terminal section with process management overview
2026-02-17 02:51:31 -08:00
---
## Adding New Tools
Requires changes in **3 files**:
**1. Create `tools/your_tool.py`:**
```python
import json, os
from tools.registry import registry
def check_requirements() -> bool:
return bool(os.getenv("EXAMPLE_API_KEY"))
def example_tool(param: str, task_id: str = None) -> str:
return json.dumps({"success": True, "data": "..."})
registry.register(
name="example_tool",
toolset="example",
schema={"name": "example_tool", "description": "...", "parameters": {...}},
handler=lambda args, **kw: example_tool(param=args.get("param", ""), task_id=kw.get("task_id")),
check_fn=check_requirements,
requires_env=["EXAMPLE_API_KEY"],
)
```
**2. Add import** in `model_tools.py` `_discover_tools()` list.
**3. Add to `toolsets.py`** — either `_HERMES_CORE_TOOLS` (all platforms) or a new toolset.
The registry handles schema collection, dispatch, availability checking, and error wrapping. All handlers MUST return a JSON string.
**Agent-level tools** (todo, memory): intercepted by `run_agent.py` before `handle_function_call()`. See `todo_tool.py` for the pattern.
---
## Adding Configuration
### config.yaml options:
1. Add to `DEFAULT_CONFIG` in `hermes_cli/config.py`
2. Bump `_config_version` (currently 5) to trigger migration for existing users
### .env variables:
1. Add to `OPTIONAL_ENV_VARS` in `hermes_cli/config.py` with metadata:
```python
"NEW_API_KEY": {
"description": "What it's for",
"prompt": "Display name",
"url": "https://...",
"password": True,
"category": "tool", # provider, tool, messaging, setting
},
```
### Config loaders (two separate systems):
| Loader | Used by | Location |
|--------|---------|----------|
| `load_cli_config()` | CLI mode | `cli.py` |
| `load_config()` | `hermes tools`, `hermes setup` | `hermes_cli/config.py` |
| Direct YAML load | Gateway | `gateway/run.py` |
---
## Skin/Theme System
The skin engine (`hermes_cli/skin_engine.py`) provides data-driven CLI visual customization. Skins are **pure data** — no code changes needed to add a new skin.
### Architecture
```
hermes_cli/skin_engine.py # SkinConfig dataclass, built-in skins, YAML loader
~/.hermes/skins/*.yaml # User-installed custom skins (drop-in)
```
- `init_skin_from_config()` — called at CLI startup, reads `display.skin` from config
- `get_active_skin()` — returns cached `SkinConfig` for the current skin
- `set_active_skin(name)` — switches skin at runtime (used by `/skin` command)
- `load_skin(name)` — loads from user skins first, then built-ins, then falls back to default
- Missing skin values inherit from the `default` skin automatically
### What skins customize
| Element | Skin Key | Used By |
|---------|----------|---------|
| Banner panel border | `colors.banner_border` | `banner.py` |
| Banner panel title | `colors.banner_title` | `banner.py` |
| Banner section headers | `colors.banner_accent` | `banner.py` |
| Banner dim text | `colors.banner_dim` | `banner.py` |
| Banner body text | `colors.banner_text` | `banner.py` |
| Response box border | `colors.response_border` | `cli.py` |
| Spinner faces (waiting) | `spinner.waiting_faces` | `display.py` |
| Spinner faces (thinking) | `spinner.thinking_faces` | `display.py` |
| Spinner verbs | `spinner.thinking_verbs` | `display.py` |
| Spinner wings (optional) | `spinner.wings` | `display.py` |
| Tool output prefix | `tool_prefix` | `display.py` |
| Per-tool emojis | `tool_emojis` | `display.py``get_tool_emoji()` |
| Agent name | `branding.agent_name` | `banner.py`, `cli.py` |
| Welcome message | `branding.welcome` | `cli.py` |
| Response box label | `branding.response_label` | `cli.py` |
| Prompt symbol | `branding.prompt_symbol` | `cli.py` |
### Built-in skins
- `default` — Classic Hermes gold/kawaii (the current look)
- `ares` — Crimson/bronze war-god theme with custom spinner wings
- `mono` — Clean grayscale monochrome
- `slate` — Cool blue developer-focused theme
### Adding a built-in skin
Add to `_BUILTIN_SKINS` dict in `hermes_cli/skin_engine.py`:
```python
"mytheme": {
"name": "mytheme",
"description": "Short description",
"colors": { ... },
"spinner": { ... },
"branding": { ... },
"tool_prefix": "┊",
},
```
### User skins (YAML)
Users create `~/.hermes/skins/<name>.yaml`:
```yaml
name: cyberpunk
description: Neon-soaked terminal theme
colors:
banner_border: "#FF00FF"
banner_title: "#00FFFF"
banner_accent: "#FF1493"
spinner:
thinking_verbs: ["jacking in", "decrypting", "uploading"]
wings:
- ["⟨⚡", "⚡⟩"]
branding:
agent_name: "Cyber Agent"
response_label: " ⚡ Cyber "
tool_prefix: "▏"
```
Activate with `/skin cyberpunk` or `display.skin: cyberpunk` in config.yaml.
---
## Important Policies
### Prompt Caching Must Not Break
Hermes-Agent ensures caching remains valid throughout a conversation. **Do NOT implement changes that would:**
- Alter past context mid-conversation
- Change toolsets mid-conversation
- Reload memories or rebuild system prompts mid-conversation
Cache-breaking forces dramatically higher costs. The ONLY time we alter context is during context compression.
### Working Directory Behavior
- **CLI**: Uses current directory (`.``os.getcwd()`)
- **Messaging**: Uses `MESSAGING_CWD` env var (default: home directory)
### Background Process Notifications (Gateway)
When `terminal(background=true, check_interval=...)` is used, the gateway runs a watcher that
pushes status updates to the user's chat. Control verbosity with `display.background_process_notifications`
in config.yaml (or `HERMES_BACKGROUND_NOTIFICATIONS` env var):
- `all` — running-output updates + final message (default)
- `result` — only the final completion message
- `error` — only the final message when exit code != 0
- `off` — no watcher messages at all
---
2026-03-07 22:14:21 -08:00
## Known Pitfalls
### DO NOT use `simple_term_menu` for interactive menus
Rendering bugs in tmux/iTerm2 — ghosting on scroll. Use `curses` (stdlib) instead. See `hermes_cli/tools_config.py` for the pattern.
2026-03-07 22:14:21 -08:00
### DO NOT use `\033[K` (ANSI erase-to-EOL) in spinner/display code
Leaks as literal `?[K` text under `prompt_toolkit`'s `patch_stdout`. Use space-padding: `f"\r{line}{' ' * pad}"`.
2026-03-07 22:14:21 -08:00
### `_last_resolved_tool_names` is a process-global in `model_tools.py`
When subagents overwrite this global, `execute_code` calls after delegation may fail with missing tool imports. Known bug.
2026-03-07 22:14:21 -08:00
### Tests must not write to `~/.hermes/`
The `_isolate_hermes_home` autouse fixture in `tests/conftest.py` redirects `HERMES_HOME` to a temp dir. Never hardcode `~/.hermes/` paths in tests.
2026-03-07 22:14:21 -08:00
---
## Testing
```bash
source .venv/bin/activate
python -m pytest tests/ -q # Full suite (~3000 tests, ~3 min)
python -m pytest tests/test_model_tools.py -q # Toolset resolution
python -m pytest tests/test_cli_init.py -q # CLI config loading
python -m pytest tests/gateway/ -q # Gateway tests
python -m pytest tests/tools/ -q # Tool-level tests
```
Always run the full suite before pushing changes.