Skip to main content

Async Hermes Agent

Native-async, library-focused distribution of NousResearch/hermes-agent, based on upstream tag v2026.8.13 (Python package version 0.20.1).

This repository keeps the Hermes agent loop, model providers, tool execution, MCP, skills, persistent memory and sessions, trajectory generation, runner, and batch runner. The CLI/TUI, messaging bridges, scheduler, dashboard, and FastAPI application are intentionally outside this package.

The public core API keeps the upstream names and module locations. Existing library integrations normally only need to add await:

import asyncio
import os

from run_agent import AIAgent


async def main():
    async with AIAgent(
        provider="openrouter",
        model="openrouter/auto",
        api_key=os.environ["OPENROUTER_API_KEY"],
    ) as agent:
        result = await agent.run_conversation("Investigate this repository")
        print(result["final_response"])


asyncio.run(main())

Inside an async function, the compact string-returning interface and explicit lifecycle are:

async def chat_once():
    agent = AIAgent(provider="openrouter", model="openrouter/auto")
    try:
        return await agent.chat("Summarize the result")
    finally:
        await agent.close()

AIAgent.__init__() performs state-only construction. Configuration, provider clients, session storage, and MCP connections initialize lazily at the first awaited boundary. Turns on one AIAgent instance are serialized; separate instances can run concurrently.

Install

Python 3.11 through 3.13 is supported.

uv pip install "async-hermes-agent==0.20.1.1"

Versioned packages are published to PyPI through GitHub OIDC Trusted Publishing. The same verified wheel, source distribution, and checksums are attached to the corresponding GitHub Release.

The package version has four numeric segments: 0.20.1.1 means upstream Python version 0.20.1 plus async-distribution revision 1. Fork-only releases increment the fourth segment. When a new upstream version is ported, the first three segments change to match it and the async revision restarts at 1.

The earlier 0.20.4 GitHub release used the old independent version scheme. If it was installed from that Git tag, migrate explicitly once:

uv pip install --reinstall "async-hermes-agent==0.20.1.1"

For development:

git clone https://github.com/ykoh42/async-hermes-agent.git
cd async-hermes-agent
uv sync --extra dev

Provider-specific dependencies remain opt-in, for example:

uv sync --extra anthropic
uv sync --extra vertex
uv sync --extra azure-identity
uv sync --extra supermemory
uv sync --extra hindsight
uv sync --extra honcho

The installation guide lists every current extra, including retained media, execution-backend, and memory providers.

The Hindsight extra covers cloud and local-external modes. Its local_embedded mode additionally requires the upstream hindsight-all runtime.

The Honcho extra pins the native-async SDK version validated by this package. Select memory.provider: honcho in config.yaml; connection, identity, cadence, and session settings are documented in the Honcho provider guide.

OpenViking uses the core native-async HTTP transport and needs no Python extra. Server setup, provider configuration, async lifecycle, recall, and tool behavior are documented in the OpenViking provider guide.

Sessions

SessionDB keeps the upstream export and import names under the original hermes_state.py path. SQLite reads, writes, lineage reconstruction, and resource cleanup are awaited directly:

import asyncio

from hermes_state import SessionDB


async def copy_sessions():
    source = SessionDB("state.db")
    restored = SessionDB("restored-state.db")
    try:
        exported = await source.export_all()
        return await restored.import_sessions(exported)
    finally:
        await source.close()
        await restored.close()


asyncio.run(copy_sessions())

export_all(), export_session(), and import_sessions() preserve the upstream dictionaries and validation limits. Import restores conversation history but deliberately clears stale live-activity fields.

Skills, MCP, and memory

Skills follow the existing Hermes layout. HERMES_HOME defaults to ~/.hermes; put each active skill at:

$HERMES_HOME/skills/<skill-name>/SKILL.md

Each SKILL.md is a normal Hermes skill document with YAML frontmatter:

---
name: code-review
description: Review a code change before it is merged.
---

# Code review

Read the change, run its tests, and report correctness issues first.

Upstream Hermes seeds its source-bundled skills through the product installer. This library does not include that installer, so Git/wheel users add skill directories explicitly or point at shared directories in config.yaml:

skills:
  external_dirs:
    - ~/.agents/skills
    - /shared/team-skills

The skills_list and skill_view tools discover both the local and configured external directories. Skill content remains outside the model-tool schema until the model selects and reads it.

MCP servers are configured under mcp_servers in $HERMES_HOME/config.yaml:

mcp_servers:
  filesystem:
    command: npx
    args: ["-y", "@modelcontextprotocol/server-filesystem", "/workspace"]

The first awaited agent boundary discovers configured servers and registers their tools under the server's toolset. MCP subprocesses and client sessions are closed by await agent.close() or the async context manager.

The file-backed memory and user profile surfaces also retain the normal Hermes home under ~/.hermes. Enable the memory toolset and the corresponding memory settings in config.yaml when constructing a memory-enabled agent.

Training and trajectories

Set save_trajectories=True on AIAgent for individual conversations. The saved sequence preserves reasoning, tool calls, observations, and the final answer for interleaved-thinking fine-tuning. Completed samples append to trajectory_samples.jsonl in the process working directory.

The upstream single-task training runner is retained at the same mini_swe_runner.py import path. Its provider, terminal execution, cleanup, and JSONL batch methods are native coroutines; the trajectory conversion and return shapes remain unchanged:

import asyncio

from mini_swe_runner import MiniSWERunner


async def run_one_task():
    runner = MiniSWERunner(
        model="openai/gpt-oss-20b:free",
        env_type="local",
        cwd="/workspace",
    )
    return await runner.run_task("Inspect and repair the project")


result = asyncio.run(run_one_task())

For datasets, use BatchRunner from the unchanged batch_runner.py module and await its existing run() method. It retains bounded concurrency, checkpoints, resume support, and JSONL output. trajectory_compressor.py remains available for post-processing generated trajectories.

import asyncio
import os

from batch_runner import BatchRunner


async def main():
    runner = BatchRunner(
        dataset_file="prompts.jsonl",
        batch_size=8,
        run_name="tool-training",
        distribution="terminal_only",
        base_url="https://openrouter.ai/api/v1",
        api_key=os.environ["OPENROUTER_API_KEY"],
        model="openai/gpt-oss-20b:free",
        num_workers=4,
        reasoning_config={"enabled": True, "effort": "low"},
    )
    await runner.run(resume=True)


asyncio.run(main())

Each input line must be JSON with a prompt field. Outputs are written under data/<run_name>/: per-batch JSONL shards, merged trajectories.jsonl, checkpoint.json, and statistics.json.

Service integration

No web framework is bundled. A service should own its HTTP lifecycle and await the library directly:

from fastapi import FastAPI
from run_agent import AIAgent

app = FastAPI()

@app.post("/chat")
async def chat(message: str):
    # One AIAgent is one mutable conversation. A real host should keep one
    # instance per conversation ID; this short-lived example isolates calls.
    async with AIAgent(provider="openrouter", model="openrouter/auto") as agent:
        return await agent.run_conversation(message)

Provider, network, MCP, and subprocess paths use coroutine transports, and optional providers without one fail explicitly. The filesystem layer uses aiofiles, whose regular-file operations delegate to an executor, while aiosqlite serializes SQLite calls on a connection worker thread. Eliminating those portable Python limitations is outside the package's native-async contract: public I/O remains directly awaitable and does not block the host event loop, but the project does not claim zero-thread, OS-native regular-file or embedded-SQLite I/O.

Verification

uv run pytest -q
uv run ruff check agent tools hermes_cli plugins providers \
  run_agent.py model_tools.py mini_swe_runner.py batch_runner.py hermes_state.py \
  hermes_state_portability.py \
  hermes_state_schema.py \
  trajectory_compressor.py
uv build

Contributing and security

Read CONTRIBUTING.md before submitting changes and SECURITY.md for private vulnerability reporting.

Upstream relationship

The repository preserves original core file and function names to keep future upstream imports reviewable. It is a divergent async distribution, not a claim that these changes are drop-in mergeable to the synchronous upstream product.

Hermes Agent is built by Nous Research. This distribution retains the upstream MIT license; see LICENSE.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

async_hermes_agent-0.20.1.1.tar.gz (3.7 MB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

async_hermes_agent-0.20.1.1-py3-none-any.whl (3.8 MB view details)

Uploaded Python 3

File details

Details for the file async_hermes_agent-0.20.1.1.tar.gz.

File metadata

  • Download URL: async_hermes_agent-0.20.1.1.tar.gz
  • Upload date:
  • Size: 3.7 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.13

File hashes

Hashes for async_hermes_agent-0.20.1.1.tar.gz
Algorithm Hash digest
SHA256 420a60adab4637099dcd43e2cf021511be5a9840ad697631ace888a9d0a5d5ba
MD5 e53a3c7dfbd4fdf43e3755f7293e443a
BLAKE2b-256 ac571221eafc5653e9394b2b710d6ce51cef3ca3df13366ca5b7e953abef9f1c

See more details on using hashes here.

Provenance

The following attestation bundles were made for async_hermes_agent-0.20.1.1.tar.gz:

Publisher: release.yml on ykoh42/async-hermes-agent

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file async_hermes_agent-0.20.1.1-py3-none-any.whl.

File metadata

File hashes

Hashes for async_hermes_agent-0.20.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 6eb2dd04d9bef72534e26c4c803f4c352d7273e928befaf81cf4d34c99b7aa83
MD5 03d7170c7fb49625195a4b5176877b23
BLAKE2b-256 abbc8b613a5b3baf36f08bf2e8d97a6462b81a678f581ae75c34b8f593fd269c

See more details on using hashes here.

Provenance

The following attestation bundles were made for async_hermes_agent-0.20.1.1-py3-none-any.whl:

Publisher: release.yml on ykoh42/async-hermes-agent

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page