Metadata-Version: 2.4
Name: async-hermes-agent
Version: 0.20.0.5
Summary: A native-async AI agent harness for tools, MCP, skills, memory, and trajectory generation
Author: Nous Research
License-Expression: MIT
Project-URL: Documentation, https://ykoh42.github.io/async-hermes-agent/
Project-URL: Issues, https://github.com/ykoh42/async-hermes-agent/issues
Project-URL: Repository, https://github.com/ykoh42/async-hermes-agent
Requires-Python: <3.14,>=3.11
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: openai==2.24.0
Requires-Dist: aiosqlite==0.22.1
Requires-Dist: aiofiles==24.1.0
Requires-Dist: python-dotenv==1.2.2
Requires-Dist: httpx[socks]==0.28.1
Requires-Dist: certifi==2026.5.20
Requires-Dist: cryptography==48.0.1
Requires-Dist: aiohttp==3.14.3
Requires-Dist: tiktoken==0.13.0
Requires-Dist: tokenizers==0.23.1
Requires-Dist: sentencepiece==0.2.2
Requires-Dist: protobuf==6.33.6
Requires-Dist: websockets==15.0.1
Requires-Dist: rich==14.3.3
Requires-Dist: pyyaml==6.0.3
Requires-Dist: pydantic==2.13.4
Requires-Dist: mcp==1.28.1
Requires-Dist: starlette==1.3.1
Requires-Dist: tzdata==2025.3; sys_platform == "win32"
Requires-Dist: psutil==7.2.2
Requires-Dist: Pillow==12.3.0
Requires-Dist: concurrent-log-handler==0.9.29; sys_platform == "win32"
Provides-Extra: anthropic
Requires-Dist: anthropic==0.87.0; extra == "anthropic"
Provides-Extra: vertex
Requires-Dist: google-auth[aiohttp]==2.56.2; extra == "vertex"
Provides-Extra: azure-identity
Requires-Dist: azure-identity==1.25.3; extra == "azure-identity"
Requires-Dist: aiohttp==3.14.3; extra == "azure-identity"
Provides-Extra: parallel-web
Requires-Dist: parallel-web==0.4.2; extra == "parallel-web"
Provides-Extra: exa
Provides-Extra: firecrawl
Provides-Extra: fal
Requires-Dist: fal-client==0.13.1; extra == "fal"
Provides-Extra: edge-tts
Requires-Dist: edge-tts==7.2.7; extra == "edge-tts"
Provides-Extra: tts-premium
Requires-Dist: elevenlabs==1.59.0; extra == "tts-premium"
Provides-Extra: mistral
Requires-Dist: mistralai==2.4.8; extra == "mistral"
Provides-Extra: piper-tts
Requires-Dist: piper-tts==1.6.0; extra == "piper-tts"
Provides-Extra: modal
Requires-Dist: modal==1.3.4; extra == "modal"
Provides-Extra: daytona
Requires-Dist: daytona==0.155.0; extra == "daytona"
Provides-Extra: vercel
Requires-Dist: vercel==0.7.2; extra == "vercel"
Provides-Extra: homeassistant
Provides-Extra: computer-use
Provides-Extra: vision
Provides-Extra: dev
Requires-Dist: pytest==9.1.1; extra == "dev"
Requires-Dist: pytest-asyncio==1.3.0; extra == "dev"
Requires-Dist: blockbuster==1.5.26; extra == "dev"
Requires-Dist: flake8-async==26.6.1; extra == "dev"
Requires-Dist: pyleak==0.2.0; extra == "dev"
Requires-Dist: ty==0.0.21; extra == "dev"
Requires-Dist: ruff==0.15.10; extra == "dev"
Requires-Dist: setuptools==83.0.0; extra == "dev"
Provides-Extra: mcp
Provides-Extra: bedrock
Requires-Dist: aiobotocore==3.8.0; extra == "bedrock"
Requires-Dist: anthropic[bedrock]==0.87.0; extra == "bedrock"
Provides-Extra: mem0
Requires-Dist: mem0ai==2.0.10; extra == "mem0"
Requires-Dist: ollama==0.6.2; extra == "mem0"
Requires-Dist: psycopg[binary,pool]==3.3.4; extra == "mem0"
Provides-Extra: supermemory
Requires-Dist: supermemory==3.50.0; extra == "supermemory"
Provides-Extra: hindsight
Requires-Dist: hindsight-client==0.6.1; extra == "hindsight"
Requires-Dist: packaging==26.0; extra == "hindsight"
Provides-Extra: honcho
Requires-Dist: honcho-ai==2.2.0; extra == "honcho"
Dynamic: license-file

# Async Hermes Agent

Native-async, library-focused distribution of
[NousResearch/hermes-agent](https://github.com/NousResearch/hermes-agent), based
on upstream tag `v2026.8.3` (Python package version `0.20.0`).

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`:

```python
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:

```python
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.

```bash
uv pip install "async-hermes-agent==0.20.0.5"
```

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.0.5` means upstream
Python version `0.20.0` plus async-distribution revision `5`. 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 and
sorts after `0.20.0.5` under Python version ordering. If it was installed from
that Git tag, migrate explicitly once:

```bash
uv pip install --reinstall "async-hermes-agent==0.20.0.5"
```

For development:

```bash
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:

```bash
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](https://ykoh42.github.io/async-hermes-agent/getting-started/installation)
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](https://github.com/ykoh42/async-hermes-agent/blob/v0.20.0.5/plugins/memory/honcho/README.md).

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](https://github.com/ykoh42/async-hermes-agent/blob/v0.20.0.5/plugins/memory/openviking/README.md).

## 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:

```python
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:

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

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

```markdown
---
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`:

```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`:

```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:

```python
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.

```python
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:

```python
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

```bash
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](https://github.com/ykoh42/async-hermes-agent/blob/v0.20.0.5/CONTRIBUTING.md)
before submitting changes and
[SECURITY.md](https://github.com/ykoh42/async-hermes-agent/blob/v0.20.0.5/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](https://nousresearch.com). This
distribution retains the upstream MIT license; see
[LICENSE](https://github.com/ykoh42/async-hermes-agent/blob/v0.20.0.5/LICENSE).
