|
Some checks failed
Nix / nix (macos-latest) (pull_request) Waiting to run
Contributor Attribution Check / check-attribution (pull_request) Failing after 43s
Supply Chain Audit / Scan PR for critical supply chain risks (pull_request) Successful in 38s
Tests / e2e (pull_request) Failing after 1m12s
Tests / test (pull_request) Failing after 1m31s
Nix / nix (ubuntu-latest) (pull_request) Failing after 31m1s
Two distinct sub-shapes converged in the seven systemd test failures tracked under hermes-agent#9. Both are addressed here without changing production code; the production contract is correct on both axes. ## Sub-shape 1 — TimeoutStopSec=210 vs 90 (2 tests) `TestGeneratedSystemdUnits::test_user_unit_avoids_recursive_execstop_and_uses_extended_stop_timeout` and its system-scope sibling pin `TimeoutStopSec=90`. The unit generator computes `restart_timeout = max(60, drain_timeout) + 30s` of post-interrupt cleanup headroom (gateway.py L1635). PR #18761 (2026-05-02) intentionally raised the default `agent.restart_drain_timeout` from 60s to 180s after a /restart on 2026-05-02 force-interrupted three mid-API-call agents inside the old 60s budget. The new arithmetic is therefore `max(60, 180) + 30 = 210s`, and both unit generators produce `TimeoutStopSec=210`. The tests are updated to derive the expected value from `DEFAULT_GATEWAY_RESTART_DRAIN_TIMEOUT` (already imported at the top of the file) so future drain-timeout changes flag the contract drift in one place. An explicit `expected_timeout == 210` assert + commit reference keeps the rationale visible at the failure site. ## Sub-shape 2 — D-Bus / loginctl mocks (5 tests) `systemd_start()` and `systemd_restart()` (user scope) gained a `_preflight_user_systemd()` call that probes `/run/user/$UID/{bus, systemd/private}` and, if both are missing, runs `loginctl enable-linger $USER` to bring the user@.service socket up. None of that is reachable from a unit-test fixture: the test runner has no real user D-Bus session, the linger probe touches the live filesystem, and `loginctl enable-linger runner` falls outside the `fake_subprocess_run` whitelist these tests pin. Five tests were therefore failing not on production behavior but on their fixtures missing a setup that the production code path can't have in a test: - TestSystemdServiceRefresh::test_systemd_start_refreshes_outdated_unit - TestSystemdServiceRefresh::test_systemd_restart_refreshes_outdated_unit - TestGatewaySystemServiceRouting::test_systemd_restart_self_requests_graceful_restart_and_waits - TestGatewaySystemServiceRouting::test_systemd_restart_recovers_failed_planned_restart - TestGatewayServiceDetection::test_supports_systemd_services_returns_true_when_systemctl_present (different shape — see below) - TestSupportsSystemdServicesWSL::test_native_linux (same shape as the detection test) Each of the four restart/start tests now stubs `_preflight_user_systemd` with a no-op. The preflight contract itself is exercised end-to-end by the existing `TestPreflightUserSystemd` suite (six tests), so this stub doesn't paper over any real bug — it only narrows the assertion to what the test was actually meant to check (refresh + systemctl call shape). For the two `supports_systemd_services()` tests, the missing fixture was `is_container()`. CI runners frequently expose `/.dockerenv`, which makes `is_container()` return True at the gateway layer and sends the call into the `_container_systemd_operational()` branch (real systemctl probe). Both tests model a native-Linux host, so they pin `is_container=False`; the WSL one also pins `shutil.which("systemctl")` to a non-None value so the early binary-presence check doesn't short-circuit. ## Verification - 7/7 named tests in the brief now pass on a clean macOS Python 3.13 venv (no real systemd present). - Set-diff of `pytest tests/hermes_cli/ -q` failure list before vs after the change: 7 fewer failures, 0 added (residual non-systemd failures match between baseline and branch — same set, just reordered by xdist). - `pytest tests/gateway/ -q`: same 14 pre-existing failures as on main; no new regressions. The post-fix contract: - User and system units write `TimeoutStopSec=210` while `restart_drain_timeout=180s` (default). The formula is pinned in test as `max(60, drain_timeout) + 30` so future drain bumps fail loudly in one place. - `_preflight_user_systemd()` probe sequence (still owned by `TestPreflightUserSystemd`): D-Bus / private-socket exists → short-circuit; else linger-on → wait 3s; else `loginctl enable-linger $USER` → wait 5s; else raise `UserSystemdUnavailableError` with remediation hint. |
||
|---|---|---|
| .github | ||
| .plans | ||
| acp_adapter | ||
| acp_registry | ||
| agent | ||
| assets | ||
| cron | ||
| datagen-config-examples | ||
| docker | ||
| docs | ||
| environments | ||
| gateway | ||
| hermes_cli | ||
| nix | ||
| optional-skills | ||
| packaging/homebrew | ||
| plans | ||
| plugins | ||
| scripts | ||
| skills | ||
| tests | ||
| tinker-atropos@65f084ee80 | ||
| tools | ||
| tui_gateway | ||
| ui-tui | ||
| web | ||
| website | ||
| .dockerignore | ||
| .env.example | ||
| .envrc | ||
| .gitattributes | ||
| .gitignore | ||
| .gitmodules | ||
| .mailmap | ||
| AGENTS.md | ||
| batch_runner.py | ||
| cli-config.yaml.example | ||
| cli.py | ||
| constraints-termux.txt | ||
| CONTRIBUTING.md | ||
| docker-compose.yml | ||
| Dockerfile | ||
| flake.lock | ||
| flake.nix | ||
| hermes | ||
| hermes_constants.py | ||
| hermes_logging.py | ||
| hermes_state.py | ||
| hermes_time.py | ||
| hermes-already-has-routines.md | ||
| LICENSE | ||
| MANIFEST.in | ||
| mcp_serve.py | ||
| mini_swe_runner.py | ||
| model_tools.py | ||
| package-lock.json | ||
| package.json | ||
| pyproject.toml | ||
| README.md | ||
| RELEASE_v0.2.0.md | ||
| RELEASE_v0.3.0.md | ||
| RELEASE_v0.4.0.md | ||
| RELEASE_v0.5.0.md | ||
| RELEASE_v0.6.0.md | ||
| RELEASE_v0.7.0.md | ||
| RELEASE_v0.8.0.md | ||
| RELEASE_v0.9.0.md | ||
| RELEASE_v0.10.0.md | ||
| RELEASE_v0.11.0.md | ||
| RELEASE_v0.12.0.md | ||
| rl_cli.py | ||
| run_agent.py | ||
| SECURITY.md | ||
| setup-hermes.sh | ||
| toolset_distributions.py | ||
| toolsets.py | ||
| trajectory_compressor.py | ||
| utils.py | ||
| uv.lock | ||
Hermes Agent ☤
The self-improving AI agent built by Nous Research. It's the only agent with a built-in learning loop — it creates skills from experience, improves them during use, nudges itself to persist knowledge, searches its own past conversations, and builds a deepening model of who you are across sessions. Run it on a $5 VPS, a GPU cluster, or serverless infrastructure that costs nearly nothing when idle. It's not tied to your laptop — talk to it from Telegram while it works on a cloud VM.
Use any model you want — Nous Portal, OpenRouter (200+ models), NVIDIA NIM (Nemotron), Xiaomi MiMo, z.ai/GLM, Kimi/Moonshot, MiniMax, Hugging Face, OpenAI, or your own endpoint. Switch with hermes model — no code changes, no lock-in.
| A real terminal interface | Full TUI with multiline editing, slash-command autocomplete, conversation history, interrupt-and-redirect, and streaming tool output. |
| Lives where you do | Telegram, Discord, Slack, WhatsApp, Signal, and CLI — all from a single gateway process. Voice memo transcription, cross-platform conversation continuity. |
| A closed learning loop | Agent-curated memory with periodic nudges. Autonomous skill creation after complex tasks. Skills self-improve during use. FTS5 session search with LLM summarization for cross-session recall. Honcho dialectic user modeling. Compatible with the agentskills.io open standard. |
| Scheduled automations | Built-in cron scheduler with delivery to any platform. Daily reports, nightly backups, weekly audits — all in natural language, running unattended. |
| Delegates and parallelizes | Spawn isolated subagents for parallel workstreams. Write Python scripts that call tools via RPC, collapsing multi-step pipelines into zero-context-cost turns. |
| Runs anywhere, not just your laptop | Six terminal backends — local, Docker, SSH, Daytona, Singularity, and Modal. Daytona and Modal offer serverless persistence — your agent's environment hibernates when idle and wakes on demand, costing nearly nothing between sessions. Run it on a $5 VPS or a GPU cluster. |
| Research-ready | Batch trajectory generation, Atropos RL environments, trajectory compression for training the next generation of tool-calling models. |
Quick Install
curl -fsSL https://raw.githubusercontent.com/NousResearch/hermes-agent/main/scripts/install.sh | bash
Works on Linux, macOS, WSL2, and Android via Termux. The installer handles the platform-specific setup for you.
Android / Termux: The tested manual path is documented in the Termux guide. On Termux, Hermes installs a curated
.[termux]extra because the full.[all]extra currently pulls Android-incompatible voice dependencies.Windows: Native Windows is not supported. Please install WSL2 and run the command above.
After installation:
source ~/.bashrc # reload shell (or: source ~/.zshrc)
hermes # start chatting!
Getting Started
hermes # Interactive CLI — start a conversation
hermes model # Choose your LLM provider and model
hermes tools # Configure which tools are enabled
hermes config set # Set individual config values
hermes gateway # Start the messaging gateway (Telegram, Discord, etc.)
hermes setup # Run the full setup wizard (configures everything at once)
hermes claw migrate # Migrate from OpenClaw (if coming from OpenClaw)
hermes update # Update to the latest version
hermes doctor # Diagnose any issues
CLI vs Messaging Quick Reference
Hermes has two entry points: start the terminal UI with hermes, or run the gateway and talk to it from Telegram, Discord, Slack, WhatsApp, Signal, or Email. Once you're in a conversation, many slash commands are shared across both interfaces.
| Action | CLI | Messaging platforms |
|---|---|---|
| Start chatting | hermes |
Run hermes gateway setup + hermes gateway start, then send the bot a message |
| Start fresh conversation | /new or /reset |
/new or /reset |
| Change model | /model [provider:model] |
/model [provider:model] |
| Set a personality | /personality [name] |
/personality [name] |
| Retry or undo the last turn | /retry, /undo |
/retry, /undo |
| Compress context / check usage | /compress, /usage, /insights [--days N] |
/compress, /usage, /insights [days] |
| Browse skills | /skills or /<skill-name> |
/<skill-name> |
| Interrupt current work | Ctrl+C or send a new message |
/stop or send a new message |
| Platform-specific status | /platforms |
/status, /sethome |
For the full command lists, see the CLI guide and the Messaging Gateway guide.
Documentation
All documentation lives at hermes-agent.nousresearch.com/docs:
| Section | What's Covered |
|---|---|
| Quickstart | Install → setup → first conversation in 2 minutes |
| CLI Usage | Commands, keybindings, personalities, sessions |
| Configuration | Config file, providers, models, all options |
| Messaging Gateway | Telegram, Discord, Slack, WhatsApp, Signal, Home Assistant |
| Security | Command approval, DM pairing, container isolation |
| Tools & Toolsets | 40+ tools, toolset system, terminal backends |
| Skills System | Procedural memory, Skills Hub, creating skills |
| Memory | Persistent memory, user profiles, best practices |
| MCP Integration | Connect any MCP server for extended capabilities |
| Cron Scheduling | Scheduled tasks with platform delivery |
| Context Files | Project context that shapes every conversation |
| Architecture | Project structure, agent loop, key classes |
| Contributing | Development setup, PR process, code style |
| CLI Reference | All commands and flags |
| Environment Variables | Complete env var reference |
Migrating from OpenClaw
If you're coming from OpenClaw, Hermes can automatically import your settings, memories, skills, and API keys.
During first-time setup: The setup wizard (hermes setup) automatically detects ~/.openclaw and offers to migrate before configuration begins.
Anytime after install:
hermes claw migrate # Interactive migration (full preset)
hermes claw migrate --dry-run # Preview what would be migrated
hermes claw migrate --preset user-data # Migrate without secrets
hermes claw migrate --overwrite # Overwrite existing conflicts
What gets imported:
- SOUL.md — persona file
- Memories — MEMORY.md and USER.md entries
- Skills — user-created skills →
~/.hermes/skills/openclaw-imports/ - Command allowlist — approval patterns
- Messaging settings — platform configs, allowed users, working directory
- API keys — allowlisted secrets (Telegram, OpenRouter, OpenAI, Anthropic, ElevenLabs)
- TTS assets — workspace audio files
- Workspace instructions — AGENTS.md (with
--workspace-target)
See hermes claw migrate --help for all options, or use the openclaw-migration skill for an interactive agent-guided migration with dry-run previews.
Contributing
We welcome contributions! See the Contributing Guide for development setup, code style, and PR process.
Quick start for contributors — clone and go with setup-hermes.sh:
git clone https://github.com/NousResearch/hermes-agent.git
cd hermes-agent
./setup-hermes.sh # installs uv, creates venv, installs .[all], symlinks ~/.local/bin/hermes
./hermes # auto-detects the venv, no need to `source` first
Manual path (equivalent to the above):
curl -LsSf https://astral.sh/uv/install.sh | sh
uv venv venv --python 3.11
source venv/bin/activate
uv pip install -e ".[all,dev]"
scripts/run_tests.sh
RL Training (optional): The RL/Atropos integration (
environments/) ships via theatroposlibandtinkerdependencies pulled in by.[all,dev]— no submodule setup required.
Community
- 💬 Discord
- 📚 Skills Hub
- 🐛 Issues
- 🔌 HermesClaw — Community WeChat bridge: Run Hermes Agent and OpenClaw on the same WeChat account.
License
MIT — see LICENSE.
Built by Nous Research.
