Skip to content

feat: add Hermes Agent memory-provider integration#329

Open
jakepu wants to merge 3 commits into
EverMind-AI:mainfrom
jakepu:feat/hermes-integration
Open

feat: add Hermes Agent memory-provider integration#329
jakepu wants to merge 3 commits into
EverMind-AI:mainfrom
jakepu:feat/hermes-integration

Conversation

@jakepu

@jakepu jakepu commented Jul 7, 2026

Copy link
Copy Markdown

Summary

Adds a Hermes Agent memory-provider integration so Hermes can use an EverOS server as its cross-session memory backend. Ships entirely from the EverOS repo — no hermes-agent PR required.

  • A Hermes MemoryProvider plugin bundle at integrations/hermes/ (HTTP client to the EverOS v1 API).
  • An everos integrations install/uninstall hermes Typer sub-group that symlinks the bundle into ~/.hermes/plugins/everos/. Hermes discovers user-installed plugins and loads them via the MemoryProvider subclass-fallback path.
  • Hooks: sync_turn/memory/add (two mixed-role MessageItems, ms timestamps), on_session_end/memory/flush, prefetch/memory/search (two-phase, 1.5s hot-path wait), on_memory_write→mirror target="user" only.
  • Tools: everos_search, everos_list, everos_add, everos_flush.
  • Circuit breaker (5 transient failures → 120s cooldown), fail-open when the server is unreachable.
  • OSS setup wizard writes ~/.everos/everos.toml ([llm]/[embedding]/[rerank]) and seeds ome.toml for the agent track when enabled.

The bundle lives outside src/everos/, so it is unconstrained by the DDD/import-linter contracts and uses stdlib logging (it runs in the Hermes process, not EverOS's).

Area

  • Architecture method
  • Benchmark
  • Use case
  • Documentation
  • Developer experience
  • CI, build, or release

Verification

make ci   # lint + 1626 unit + 82 integration + package — all green on 1.1.1

End-to-end against the real Hermes Agent v0.18.0 binary + a real EverOS server (LLM deepseek/deepseek-v4-flash + embedding qwen/qwen3-embedding-8b via OpenRouter), in an isolated HERMES_HOME/EVEROS_ROOT:

  • hermes memory statuseveros (local) ← active, available ✓ (plugin discovered + loaded via subclass-fallback).
  • hermes everos status → health reachable, correct config/scope.
  • POST /memory/add (the adapter's exact sync_turn payload) → accumulated.
  • POST /memory/flushextracted (LLM ran) → episode markdown written: "hermes-test instructed that they prefer tabular output for data presentations."
  • POST /memory/search + hermes everos search → both return the episode + atomic fact (recall works).
  • Cleanup verified: real ~/.hermes and ~/.everos untouched.

Full LLM extraction+recall requires an accessible LLM model + embedding endpoint in ~/.everos/everos.toml; the adapter's HTTP contract is also covered by 136 hermetic tests with fakes.

Checklist

  • I kept the change scoped to the relevant area.
  • I am opening this from a separate branch, not pushing directly to main.
  • I updated docs, examples, or setup notes when behavior changed. (integrations/hermes/README.md)
  • I added or updated tests when the change affects behavior. (136 hermetic tests)
  • I did not commit secrets, .env files, dependency folders, or generated output.
  • Active relative links in Markdown files resolve.

Notes for Reviewers

  • The bundle is intentionally outside src/everos/ (repo-root integrations/hermes/) so it stays out of the everos import graph and the DDD/import-linter contracts. It uses stdlib logging, not EverOS structlog, because it runs in the Hermes process. make check-datetime/check-cjk are scoped to src/+tests and do not flag the bundle.
  • The plugin uses Hermes's documented subclass-fallback loader path (no register(ctx)); confirmed at hermes-agent/plugins/memory/__init__.py:_load_provider_from_dir.
  • Scope-id charset ^[a-zA-Z0-9_.@+-]+$ matches the server-side PathSafeId in src/everos/entrypoints/api/routes/memorize.py (the docs/api.md §ScopeId list omits @/+; the server is ground truth). Documented in _constants.py.
  • The installer resolves the bundle via EVEROS_HERMES_PLUGIN_SOURCE env → --source flag → repo-root walk-up from everos.__file__ (covers editable/dev installs). PyPI-wheel-only consumers pass --source (vendoring into the wheel is a documented future enhancement).

By submitting this pull request, I agree that my contribution is licensed under the Apache License 2.0.

Ship a Hermes Agent memory-provider plugin at `integrations/hermes/` and an
`everos integrations install/uninstall hermes` installer that symlinks the
bundle into `~/.hermes/plugins/everos/`. Hermes discovers user-installed
plugins and loads them via the MemoryProvider subclass-fallback path, so no
`hermes-agent` PR is required.

The plugin is an HTTP client to the EverOS v1 API:
- sync_turn -> POST /memory/add (two mixed-role MessageItems, ms timestamps)
- on_session_end -> POST /memory/flush
- prefetch -> POST /memory/search (two-phase, 1.5s hot-path wait)
- on_memory_write -> mirror target="user" only
- tools: everos_search / everos_list / everos_add / everos_flush
- circuit breaker (5 transient failures -> 120s cooldown)

OSS setup writes `~/.everos/everos.toml` ([llm]/[embedding]/[rerank]) and
seeds `ome.toml` for the agent track when enabled. The bundle lives outside
`src/everos/`, so it is unconstrained by the DDD/import-linter contracts and
uses stdlib logging (it runs in the Hermes process).

136 hermetic tests cover the client, config, setup, formatting, provider
lifecycle + tools + circuit breaker, the Hermes-side CLI, and the installer.
Verified end-to-end with real models (LLM + embedding via OpenRouter):
extraction writes episode markdown and search recalls the atomic fact.
@jakepu

jakepu commented Jul 7, 2026

Copy link
Copy Markdown
Author

How this integrates with Hermes Agent as a long-term memory provider

A walkthrough for reviewers — what the plugin actually does at runtime.

1. Discovery & loading

The bundle at integrations/hermes/ is installed into Hermes by one command:

everos integrations install hermes   # symlinks the bundle to ~/.hermes/plugins/everos/
hermes config set memory.provider everos

When Hermes starts, its plugin loader scans ~/.hermes/plugins/<name>/, finds everos/, and — because EverosMemoryProvider is a top-level MemoryProvider subclass — instantiates it (the documented subclass-fallback path; no register(ctx) needed). From that point, the Hermes MemoryManager routes memory hooks to our provider. The built-in MEMORY.md/USER.md stays active alongside it.

2. The write path (every turn → durable memory)

After each completed Hermes turn, MemoryManager calls our sync_turn(user_content, assistant_content, *, session_id, messages):

  1. We build two MessageItems — the user turn (sender_id=<user>, role="user") and the assistant turn (sender_id=<agent>, role="assistant") — with Unix-ms timestamps.
  2. On a daemon thread (non-blocking, like mem0/honcho), we POST /api/v1/memory/add to the EverOS server. The server buffers them per session_id.
  3. EverOS's boundary detector decides when a conversation segment is "done"; when it trips, the server's extractor (an LLM) carves a MemCell and writes an Episode (a narrative summary) to a markdown file synchronously — ~/.everos/.../users/<user_id>/episodes/episode-<date>.md.
  4. At session end (on_session_end), we POST /memory/flush to force any pending buffer to be extracted immediately.

The markdown is the source of truth — plain, human-readable, durable. SQLite (state) and LanceDB (vectors + BM25) are derived indexes rebuilt from it. So memory survives across Hermes restarts, machine moves (copy the ~/.everos dir), and even index corruption (delete .index/, it rebuilds).

3. The recall path (every turn → relevant context injected)

Before each turn, MemoryManager calls our prefetch(query):

  1. We fire a background POST /memory/search with include_profile=True and the user's upcoming query.
  2. We wait up to 1.5s; if the result lands, we format it into a markdown block and inject it into the agent's context for that turn. If it's slow, we serve empty now and the result surfaces next turn (mem0-style two-phase).
  3. The injected block has the user profile one-liner + the top episodes (subject + truncated narrative) + nested atomic facts, score-sorted.

So when you ask "what output format do I prefer?", the agent sees the stored episode — "hermes-test instructed that they prefer tabular output for data presentations." — before it answers.

4. What makes it "long-term"

  • Cross-session: episodes persist in markdown; a new Hermes chat recalls facts from prior chats.
  • Cross-platform: a single user_id (resolved as: configured > gateway-native id from Telegram/Discord/Slack > "hermes-user") means the same human merges one memory store across every Hermes gateway.
  • Consolidating, not just appending: EverOS's Offline Memory Engine runs strategies in the background — extract_atomic_facts (single-sentence facts), extract_user_profile (an evolving user.md), and (opt-in, weekly cron) reflect_episodes which merges fragmented episodes about the same topic into one coherent narrative and soft-archives the originals. So memory gets more accurate and compact with use, instead of piling into noise.
  • Editable: the markdown is the truth — you (or Obsidian/Vim) can edit an episode file and the cascade re-indexes just that entry.

5. Explicit tools (agent-driven recall/write)

Beyond passive prefetch, the agent gets four tools:

  • everos_search(query, top_k, method, owner) — semantic+lexical hybrid search.
  • everos_list(memory_type, page, page_size, owner) — paginated browse of episodes/profile.
  • everos_add(content) — buffer a fact + flush (extraction runs).
  • everos_flush() — force extraction of the current session buffer now.

6. Mirroring built-in memory

on_memory_write(action, target, content) mirrors Hermes's own USER.md writes (target="user") into EverOS as a user-track memory, so the two stay in sync without double-echoing ephemeral MEMORY.md entries.

7. Resilience

A circuit breaker trips after 5 consecutive transient failures (server down, LLM/embedding 503) and pauses calls for 120s — so a down EverOS server never crashes Hermes or stalls the conversation; it just degrades to built-in memory until the server recovers.

Two deployment modes

  • platform — point at a vendor-hosted EverOS server (just api_url).
  • oss — run EverOS locally; the setup wizard writes ~/.everos/everos.toml with your LLM / embedding / rerank endpoints (any OpenAI-protocol provider), so the extraction/embedding stack is self-supplied.

In short: Hermes keeps talking to its agent as usual; every turn is quietly written to durable, searchable, self-consolidating markdown memory on the EverOS side, and the relevant bits are injected back before each answer.

jakepu and others added 2 commits July 7, 2026 09:05
Add a dedicated docs page (docs/hermes-integration.md) covering install,
modes, config, the write/recall lifecycle, agent tools, the Hermes-side
CLI, resilience, and troubleshooting. Link it from the docs index
(Tutorials) and document the new `everos integrations` subcommand
(install/uninstall) in cli.md.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant