langstage-hermes
A faithful reproduction of Nous Research's Hermes Agent on top of LangGraph + deepagents + langstage-core.
Status: live on PyPI (renamed from deepagent-hermes — the old name now just installs this one, and the deepagent-hermes command still works). Spec at SPEC.md. Release notes in CHANGELOG.md. The runtime is verified end-to-end against a real Anthropic model — both the memory loop and the skill-creation loop close autonomously; see examples/dogfood.py and examples/dogfood_procedural.py for the traces.
What it is
A deepagents-built agent with a closed reflection→skill-creation loop:
- After ~10 tool-using iterations, a review subagent runs in the background, writes/patches a
SKILL.mdcapturing the pattern it just exercised, and ships it to a skill library. - Next session, the agent reads the library at startup, sees the new skill's description in its system prompt, and can
skill_view(name)to load the full body on demand (progressive disclosure per the agentskills.io spec). - A weekly curator consolidates skills into umbrellas and archives stale ones.
- A frozen-snapshot memory (
MEMORY.md+USER.md) preserves prefix-cache hits for the entire session. - FTS5 session search indexes every past conversation in a local SQLite DB.
- Bundled MarkdownProvider that keyword-searches
<HERMES_HOME>/memories/notes/*.md— drop hand-authored long-form context there and the agent surfaces relevant sections on demand. Zero external dependencies.
Designed to be loaded into the LangStage host family without UI changes — set LANGSTAGE_AGENT_SPEC=langstage_hermes.agent:graph in any of them.
Every stage for your LangGraph agent
langstage-hermes is the reference agent of the LangStage family: write your agent once — any LangGraph CompiledGraph — and run it on every stage with the same spec string (module:attr or path/to/file.py:attr), the same langstage.toml config file, and the same LANGSTAGE_* environment variables.
| Stage | Package | Try it |
|---|---|---|
| Web app | langstage | langstage run --agent langstage_hermes.agent:graph |
| JupyterLab | langstage-jupyter | pip install langstage-jupyter, then the chat sidebar in jupyter lab |
| Terminal | langstage-cli | langstage-cli -a langstage_hermes.agent:graph |
| VS Code | langstage-vscode | chat participant + stdio sidecar |
| Reference agent | langstage-hermes | you are here |
| Shared core | langstage-core | typed events + config resolver behind every stage |
Serve over AG-UI
This surface's agent — any LangGraph CompiledGraph — can also be served over the AG-UI protocol as a standalone HTTP endpoint. Install the extra and point the bundled console script at your agent spec:
pip install "langstage-core[agui]"
langstage-agui --agent langstage_hermes.agent:graph
📖 Full documentation: https://dkedar7.github.io/langstage-docs/
Installation
pip install langstage-hermes
Or with uv (recommended):
uv venv .venv
. .venv/Scripts/activate # Windows
. .venv/bin/activate # macOS / Linux
uv pip install langstage-hermes
Optional extras
pip install "langstage-hermes[openai]" # OpenAI / OpenRouter / any OpenAI-wire provider
pip install "langstage-hermes[daytona]" # Daytona sandbox terminal backend
pip install "langstage-hermes[modal]" # Modal sandbox terminal backend
pip install "langstage-hermes[ssh]" # paramiko-backed SSH terminal backend
pip install "langstage-hermes[dev]" # tests + lint (contributors only)
Picking a model
By default the agent uses anthropic:claude-sonnet-4-6 and needs ANTHROPIC_API_KEY set. Swap the model via --model on the CLI or model.default in langstage-hermes.toml — any init_chat_model string works.
OpenAI / OpenRouter
pip install "langstage-hermes[openai]"
export OPENAI_API_KEY=sk-… # or: OPENROUTER_API_KEY=sk-or-v1-…
export OPENAI_BASE_URL=https://openrouter.ai/api/v1 # only for OpenRouter
langstage-hermes chat --model openai:openai/gpt-4o-mini
For OpenRouter specifically you usually also want:
export LANGSTAGE_HERMES_MODEL_DEFAULT="openai:openai/gpt-4o-mini"
export LANGSTAGE_HERMES_MODEL_AUX="openai:openai/gpt-4o-mini"
so the reflection subagent uses the same cheap model.
Verify your setup
langstage-hermes verify
does one live round-trip against the configured model and confirms the prompts, bundled skills, and FTS5 store all wire up correctly. Run this first on any fresh install — if it passes, chat will work. The offline checks (bundled prompts and skills, a writable HERMES_HOME, and that the FTS5 store opens) run before the key check, so even a keyless run tells you the install itself is sound.
Both verify and doctor accept --json for scripting/CI — a top-level ok plus a per-check list, with .ok equal to exit code == 0 (so langstage-hermes doctor --json | jq -e .ok is a one-liner readiness gate). verify --json reports the live round-trip as skipped when no key is set, so a keyless CI check never triggers a paid call.
Quick start
# show resolved config + sources
langstage-hermes --show-config
# interactive chat
langstage-hermes chat
# chat against a different agent (same spec format as every LangStage
# stage; overrides LANGSTAGE_AGENT_SPEC)
langstage-hermes chat -a my_agent.py:graph
# from inside chat:
# /skills list available skills
# /model anthropic:claude-haiku-4-5-20251001 switch models
# /memory dump current memory snapshot
# /compress force context compression
# /quit
Search your session history
Every conversation is indexed in a local SQLite FTS5 store at <HERMES_HOME>/state.db. Query it from the terminal — keyless and offline, no model call — in the three documented modes:
# DISCOVERY — BM25 top-N with a highlighted snippet (prints session_id + message_id)
langstage-hermes search "profile slow python"
langstage-hermes search "profile slow python" --limit 10 --json
# SCROLL — a ±window view centred on a message (window clamped 1–20)
langstage-hermes search --session sess-1a2b3c --around 8 --window 5
# BROWSE — recent sessions, newest first (also the default with no query)
langstage-hermes search --browse --limit 20
--json emits structured output for scripting/CI. The same flag is honored by skills list, skills audit, audit log, memory show / memory notes, the readiness checks doctor / verify, --show-config, and the cron subcommands (list, create, run-due, delete, pause, resume) — so the skill inventory, mutation log, setup diagnostics, and scheduler are all scriptable too (each prints one JSON object with stable keys). FTS5 syntax works: multi-word queries default to AND, and OR, quoted "phrases", and prefix wildcards* are all honored.
Want a store to try it against, keyless? Point HERMES_HOME at a directory and run langstage-hermes demo — with HERMES_HOME set the demo copies its session (and only its session) into that same <HERMES_HOME>/state.db, so search reads it straight back:
export HERMES_HOME=~/.langstage-hermes # any stable directory
langstage-hermes demo # populates <HERMES_HOME>/state.db
langstage-hermes search "python" # finds the demo session
(The loop itself always runs in a throwaway home that is cleaned up afterwards — pass --keep-workspace to inspect it — so the demo's generated skill and scripted user-memory note never land in your real skill library or USER.md. A bare langstage-hermes demo with no HERMES_HOME set writes nothing outside that throwaway home.)
Preview your notes
Drop hand-authored long-form context in <HERMES_HOME>/memories/notes/*.md and the bundled MarkdownProvider surfaces relevant sections to the agent on demand (enable it with memory.provider = "markdown"). Preview exactly what it will recall from the terminal — keyless and offline, no model call — so note authors get a feedback loop without a chat turn:
langstage-hermes memory notes "rollback" # prints the source file + matching section
langstage-hermes memory notes "rollback" --json # {"query", "count", "results":[{file, section, snippet}]}
Recall is the same dumb-but-robust keyword overlap the agent uses (query tokens ≥ 3 chars, ranked by distinct hits). --limit N caps the sections returned; a missing/empty notes dir or a no-match query prints a clear message, never a traceback.
Read your memory
The frozen-snapshot memory (MEMORY.md + USER.md) is the layer the agent grows about you and the session, but inside a chat only the /memory slash command could read it — and chat needs an API key. Dump the current snapshot straight from the terminal — keyless and offline, no model call — to answer "what has the agent learned about me?" without spending a turn:
langstage-hermes memory show # both USER.md + MEMORY.md, with char count vs budget
langstage-hermes memory show --user # just USER.md
langstage-hermes memory show --session # just MEMORY.md
langstage-hermes memory show --json # {"user": {...}, "memory": {...}} for scripting / CI
Each layer prints its char count against the configured char budget (memory_char_limit = 2200, memory_user_char_limit = 1375) so you can see when a snapshot is near or over budget. Nothing is truncated: an over-budget layer (e.g. a hand-edited USER.md) is still injected in full every turn, and the agent's memory tool rejects new entries for it until it is trimmed back under budget. A missing/empty layer prints a clear one-line message, never a traceback (memory dump is an alias for memory show).
Load into an existing host
Any LangStage host can run this agent:
# langstage-cli
LANGSTAGE_AGENT_SPEC="langstage_hermes.agent:graph" langstage-cli
# langstage-jupyter — set the same in langstage.toml under [agent]
printf '[agent]\nspec = "langstage_hermes.agent:graph"\n' >> langstage.toml
langstage-jupyter
Configuration
langstage-hermes.toml (project) or $HERMES_HOME/config.toml (global — default ~/.langstage-hermes/config.toml, and it moves with a custom HERMES_HOME). Layered resolution: defaults < TOML < LANGSTAGE_HERMES_* env < CLI overrides. See SPEC §2 for every field; langstage-hermes --show-config prints the resolved value + source of each, and langstage-hermes --show-config --json prints the same thing as one JSON object ({"config": {<field>: {value, source, env, legacy_env, toml}}, "toml": {...}, "issues": [...]}) for CI. The legacy project filename deepagent-hermes.toml is still read, with a one-time deprecation note on stderr (silence it with LANGSTAGE_SUPPRESS_LEGACY_NOTICE=1).
Architecture
See SPEC.md for the full 21-section requirements doc. Top-level layout:
src/langstage_hermes/agent.py— the compiled graph (entry point for hosts)src/langstage_hermes/config.py—HermesConfig(HostConfig)resolversrc/langstage_hermes/state.py—HermesState(extendsAgentState)src/langstage_hermes/reflection.py— closed-loop middleware + review subagentsrc/langstage_hermes/skills/— SkillLibrary, loader, toolssrc/langstage_hermes/memory/— frozen-snapshot memory + provider ABCsrc/langstage_hermes/store/sqlite_fts.py—BaseStorewith FTS5src/langstage_hermes/search/session_search.py—session_searchtoolsrc/langstage_hermes/compression.py—HermesCompressionMiddlewaresrc/langstage_hermes/caching.py—AnthropicCachingS3Middlewaresrc/langstage_hermes/budget.py—IterationBudgetMiddlewaresrc/langstage_hermes/tools/— registry + 33 toolsets + 6 terminal envssrc/langstage_hermes/cron/— daemon +cronjobtoolsrc/langstage_hermes/plugins/— discovery + lifecycle hookssrc/langstage_hermes/cli.py—langstage-hermesentry pointprompts/— verbatim/paraphrased system-prompt building blocks
Status by subsystem
| Subsystem | Status |
|---|---|
| Config + state + agent factory | ✅ working |
| Reflection loop (10-iter / 10-turn triggers, subagent review) | ✅ working — verified live |
| Skill library + agentskills.io validator | ✅ working |
| Skill loader (system-prompt injection + progressive disclosure) | ✅ working |
skill_view / skill_manage / skills_list tools |
✅ working |
| Frozen-snapshot memory (MEMORY.md / USER.md) | ✅ working — verified live (702 bytes written autonomously); also a keyless langstage-hermes memory show CLI (--user / --session / --json) |
SQLite FTS5 store + session_search (3 modes) |
✅ working — also a keyless langstage-hermes search CLI (DISCOVERY / SCROLL / BROWSE, --json) |
MarkdownProvider (bundled, opt-in via memory.provider="markdown"; the default is no-op) |
✅ keyword search over <HERMES_HOME>/memories/notes/*.md — zero deps; also a keyless langstage-hermes memory notes CLI (--json) |
| Iteration budget middleware | ✅ working |
| Compression middleware (13-section template) | ✅ working |
Anthropic system_and_3 caching strategy |
✅ working |
| Tool registry + 33-toolset enum | ✅ working |
LocalEnvironment terminal backend |
✅ working (Git Bash on Windows) |
DockerEnvironment |
✅ working (gated on docker info reachability) |
SshEnvironment |
✅ working (paramiko-backed, behind [ssh] extra) |
SingularityEnvironment |
✅ working (auto-detects singularity / apptainer) |
DaytonaEnvironment / ModalEnvironment |
✅ lazy SDK with defensive attribute probing (extras-gated) |
Cron daemon + cronjob tool |
✅ working (deliverers: local, stdout, agentmail) |
| Plugin loader (4 discovery sources) | ✅ working (13 of 17 lifecycle hooks wired) |
| CLI + v1-essentials slash commands | ✅ working |
| Curator (skill lifecycle) | ✅ basic |
| Bundled skills | ✅ 26 from nousresearch/hermes-agent (MIT, attributed) |
| Self-evolution integration | 📄 docs only (separate offline repo) |
License
MIT. See LICENSE. This project is a faithful reproduction of the design ideas in Nous Research's Hermes Agent — see NOTICE for attribution.
Release files for langstage-hermes 0.4.34
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| langstage_hermes-0.4.34.tar.gz | 1.5 MB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| langstage_hermes-0.4.34-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 2.9 MB
Release files / langstage_hermes-0.4.34.tar.gz
| Download URL | langstage_hermes-0.4.34.tar.gz |
|---|---|
| Size | 1.5 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
dea11fd45d76093dca4b4b117c3327830b3068674fd892a99457533ae5de2144
|
|
BLAKE2b-256 checksum How to use checksums |
aa4646a67acdb31d28eb36b9889f0d4365ff8244776818a7b40b6c99235bdbe3
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.11.15 {"installer":{"name":"uv","version":"0.11.15","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Release files / langstage_hermes-0.4.34-py3-none-any.whl
| Download URL | langstage_hermes-0.4.34-py3-none-any.whl |
|---|---|
| Size | 1.4 MB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
6bf14412a2a99498747ab3383c2f95c48386bece630b63dd06e00de44999b5fa
|
|
BLAKE2b-256 checksum How to use checksums |
e0b57578e6acdca32b1d8413d6eb97a9e1006fbe0c60a819f67fb605704fb42e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.11.15 {"installer":{"name":"uv","version":"0.11.15","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|