Hermes Agent Persistent Memory Integration Guide
Connects Hermes Agent to its built-in persistent memory system for cross-session recall.

This integration connects Hermes Agent's built-in persistent memory system to your AI agent runtime, allowing the agent to remember user preferences, environment facts, project conventions, and learned lessons across sessions. With this wired up, the agent can automatically curate two files (MEMORY.md and USER.md) stored locally in ~/.hermes/memories/, inject them into the system prompt at session start, and manage entries via a dedicated memory tool without requiring manual intervention.
What It Does
- Persists agent memory across sessions: The agent remembers your preferences, projects, environment, and things it has learned between conversations.
- Manages two separate memory stores: One for the agent's personal notes (MEMORY.md) and one for the user profile (USER.md), each with distinct character limits.
- Injects memory into the system prompt: At the start of every session, memory entries are loaded from disk and rendered as a frozen block in the system prompt, giving the agent immediate context.
- Provides a
memorytool for self-management: The agent can add, replace, or remove entries using substring matching, without needing to read the full memory content. - Prevents duplicates automatically: Exact duplicate entries are rejected with a success message indicating no duplicate was added.
- Scans for security threats: Memory entries are checked for injection and exfiltration patterns, including invisible Unicode characters, before being accepted.
- Supports session search: Beyond persistent memory, the agent can search past conversations using the
session_searchtool, which queries an SQLite database with FTS5 full-text search. - Offers write approval gating: You can require approval before any memory writes take effect, giving you control over what gets saved.
- Provides background review notifications: After each turn, the agent may quietly save memory or update skills; you can control how chatty these notifications are.
- Allows running background review on a cheaper model: You can configure the self-improvement review to run on a different, less expensive model to reduce costs.
- Supports external memory providers: Eight plugins (Honcho, OpenViking, Mem0, Hindsight, Holographic, RetainDB, ByteRover, Supermemory) add capabilities like knowledge graphs, semantic search, and automatic fact extraction.
Hermes Agent's persistent memory is not a simple key-value store. It is a bounded, curated system designed to keep the agent focused and efficient. The two files that make up the memory are MEMORY.md (for the agent's personal notes) and USER.md (for the user profile). MEMORY.md has a character limit of 2,200 characters (approximately 800 tokens) and typically holds 8-15 entries. USER.md has a limit of 1,375 characters (approximately 500 tokens) and typically holds 5-10 entries. Both files are stored in the ~/.hermes/memories/ directory and are injected into the system prompt as a frozen snapshot at the start of each session. The agent manages its own memory via the memory tool, which supports add, replace, and remove actions. There is no read action because the memory content is automatically injected into the system prompt at session start. The agent sees its memories as part of its conversation context.
Setup
Installation and Prerequisites
Hermes Agent's persistent memory is built into the agent runtime. No separate installation is required. The memory system is enabled by default. To verify that the memory directory exists, check for ~/.hermes/memories/. If it does not exist, it will be created automatically on first use.
Configuration File
The memory system is configured in ~/.hermes/config.yaml. The following block shows the default configuration:
memory:
memory_enabled: true
user_profile_enabled: true
memory_char_limit: 2200 # ~800 tokens
user_char_limit: 1375 # ~500 tokens
write_approval: false # false = write freely (default) | true = require approval
To turn memory off entirely, set memory_enabled: false. To gate writes so they require approval, set write_approval: true. To control background review notifications, add a display section:
display:
memory_notifications: on # off | on (default) | verbose
To run the background review on a cheaper model, add an auxiliary section:
auxiliary:
background_review:
provider: openrouter
model: google/gemini-3-flash-preview # auto (default) = main chat model
Authentication
No authentication is required for the built-in memory system. It operates entirely on local files. For external memory providers, authentication may be required; see the Memory Providers guide for details.
Verifying Setup
To check the status of memory, run:
hermes memory status
This will show what is active, including which external providers are configured if any.
To list past sessions (which are stored separately in SQLite for session search):
hermes sessions list
Configuration Reference
Memory Configuration Options
| Option | Description | Default |
|---|---|---|
memory_enabled | Enables or disables the entire memory system. When false, no memory is loaded or written. | true |
user_profile_enabled | Enables or disables the user profile store (USER.md). When false, only MEMORY.md is active. | true |
memory_char_limit | Maximum characters for MEMORY.md. Approximately 800 tokens. | 2200 |
user_char_limit | Maximum characters for USER.md. Approximately 500 tokens. | 1375 |
write_approval | When true, all memory writes require explicit approval before being saved. When false, writes happen freely. | false |
Display Configuration Options
| Option | Description | Default |
|---|---|---|
display.memory_notifications | Controls chat notifications for background memory updates. off shows nothing, on shows a generic line, verbose shows a compact preview of what changed. | on |
Auxiliary Configuration Options
| Option | Description | Default |
|---|---|---|
auxiliary.background_review.provider | The provider for the background review model. Set to a different provider (e.g., openrouter) to run the review on a cheaper model. | auto (uses main chat model) |
auxiliary.background_review.model | The model for the background review. When different from the main model, the review replays a compact digest of the conversation. | auto (uses main chat model) |
Skills Write Approval Configuration
| Option | Description | Default |
|---|---|---|
skills.write_approval | When true, all skill writes (create, edit, patch, write_file, delete) require approval before being saved. | false |
How It Works

Memory Files and Storage
Two files make up the agent's memory:
- MEMORY.md: Agent's personal notes. Stores environment facts, conventions, things learned. Character limit: 2,200 chars (~800 tokens). Typical entries: 8-15.
- USER.md: User profile. Stores your preferences, communication style, expectations. Character limit: 1,375 chars (~500 tokens). Typical entries: 5-10.
Both are stored in ~/.hermes/memories/ and are injected into the system prompt as a frozen snapshot at session start. The agent manages its own memory via the memory tool.
Memory Injection into System Prompt
At the start of every session, memory entries are loaded from disk and rendered into the system prompt as a frozen block. The format includes:
- A header showing which store (MEMORY or USER PROFILE)
- Usage percentage and character counts so the agent knows capacity
- Individual entries separated by
§(section sign) delimiters - Entries can be multiline
Example of how memory appears in the system prompt:
══════════════════════════════════════════════
MEMORY (your personal notes) [67%, 1,474/2,200 chars]
══════════════════════════════════════════════
User's project is a Rust web service at ~/code/myapi using Axum + SQLx
§
This machine runs Ubuntu 22.04, has Docker and Podman installed
§
User prefers concise responses, dislikes verbose explanations
This injection is a frozen snapshot captured once at session start and never changes mid-session. This is intentional because it preserves the LLM's prefix cache for performance. When the agent adds or removes memory entries during a session, the changes are persisted to disk immediately but will not appear in the system prompt until the next session starts. Tool responses always show the live state.
Memory Tool Actions
The agent uses the memory tool with these actions:
- add: Add a new memory entry.
- replace: Replace an existing entry with updated content. Uses substring matching via
old_text. - remove: Remove an entry that is no longer relevant. Uses substring matching via
old_text.
There is no read action because memory content is automatically injected into the system prompt at session start.
Substring Matching
The replace and remove actions use short unique substring matching. You do not need the full entry text. The old_text parameter just needs to be a unique substring that identifies exactly one entry. For example:
# If memory contains "User prefers dark mode in all editors"
memory(
action="replace",
target="memory",
old_text="dark mode",
content="User prefers light mode in VS Code, dark mode in terminal"
)
If the substring matches multiple entries, an error is returned asking for a more specific match.
Two Targets Explained
memory, Agent's Personal Notes
For information the agent needs to remember about the environment, workflows, and lessons learned:
- Environment facts (OS, tools, project structure)
- Project conventions and configuration
- Tool quirks and workarounds discovered
- Completed task diary entries
- Skills and techniques that worked
user, User Profile
For information about the user's identity, preferences, and communication style:
- Name, role, timezone
- Communication preferences (concise vs detailed, format preferences)
- Pet peeves and things to avoid
- Workflow habits
- Technical skill level
What to Save vs Skip
Save These (Proactively)
The agent saves automatically. You do not need to ask. It saves when it learns:
- User preferences: "I prefer TypeScript over JavaScript" → save to
user - Environment facts: "This server runs Debian 12 with PostgreSQL 16" → save to
memory - Corrections: "Don't use
sudofor Docker commands, user is in docker group" → save tomemory - Conventions: "Project uses tabs, 120-char line width, Google-style docstrings" → save to
memory - Completed work: "Migrated database from MySQL to PostgreSQL on 2026-01-15" → save to
memory - Explicit requests: "Remember that my API key rotation happens monthly" → save to
memory
Skip These
- Trivial/obvious info: "User asked about Python", too vague to be useful
- Easily re-discovered facts: "Python 3.12 supports f-string nesting", can web search this
- Raw data dumps: Large code blocks, log files, data tables, too big for memory
- Session-specific ephemera: Temporary file paths, one-off debugging context
- Information already in context files: SOUL.md and AGENTS.md content
Capacity Management
Memory has strict character limits to keep system prompts bounded:
| Store | Limit | Typical entries |
|---|---|---|
| memory | 2,200 chars | 8-15 entries |
| user | 1,375 chars | 5-10 entries |
What Happens When Memory is Full
When you try to add an entry that would exceed the limit, the tool returns an error:
{
"success": false,
"error": "Memory at 2,100/2,200 chars. Adding this entry (250 chars) would exceed the limit. Consolidate now: use 'replace' to merge overlapping entries into shorter ones or 'remove' stale or less important entries (see current_entries below), then retry this add, all in this turn.",
"current_entries": ["..."],
"usage": "2,100/2,200"
}
The agent should then:
- Read the current entries (shown in the error response).
- Identify entries that can be removed or consolidated.
- Use
replaceto merge related entries into shorter versions. - Then
addthe new entry.
Best practice: When memory is above 80% capacity (visible in the system prompt header), consolidate entries before adding new ones. For example, merge three separate "project uses X" entries into one comprehensive project description entry.
Practical Examples of Good Memory Entries
Compact, information-dense entries work best:
# Good: Packs multiple related facts
User runs macOS 14 Sonoma, uses Homebrew, has Docker Desktop and Podman. Shell: zsh with oh-my-zsh. Editor: VS Code with Vim keybindings.
# Good: Specific, actionable convention
Project ~/code/api uses Go 1.22, sqlc for DB queries, chi router. Run tests with 'make test'. CI via GitHub Actions.
# Good: Lesson learned with context
The staging server (10.0.1.50) needs SSH port 2222, not 22. Key is at ~/.ssh/staging_ed25519.
# Bad: Too vague
User has a project.
# Bad: Too verbose
On January 5th, 2026, the user asked me to look at their project which is located at ~/code/api. I discovered it uses Go version 1.22 and...
Duplicate Prevention
The memory system automatically rejects exact duplicate entries. If you try to add content that already exists, it returns success with a "no duplicate added" message.
Security Scanning
Memory entries are scanned for injection and exfiltration patterns before being accepted, since they are injected into the system prompt. Content matching threat patterns (prompt injection, credential exfiltration, SSH backdoors) or containing invisible Unicode characters is blocked.
Session Search
Beyond MEMORY.md and USER.md, the agent can search its past conversations using the session_search tool:
- All CLI and messaging sessions are stored in SQLite (
~/.hermes/state.db) with FTS5 full-text search. - Search queries return actual messages from the DB, no LLM summarization, no truncation.
- The agent can find things it discussed weeks ago, even if they are not in its active memory.
- The agent can also scroll forward/backward inside any session it finds.
To browse past sessions from the CLI:
hermes sessions list
session_search vs memory
| Feature | Persistent Memory | Session Search |
|---|---|---|
| Capacity | ~1,300 tokens total | Unlimited (all sessions) |
| Speed | Instant (in system prompt) | ~20ms FTS5 query, ~1ms scroll |
| Cost | Token cost in every prompt | Free, no LLM calls |
| Use case | Key facts always available | Finding specific past conversations |
| Management | Manually curated by agent | Automatic, all sessions stored |
| Token cost | Fixed per session (~1,300 tokens) | On-demand (searched when needed) |
Memory is for critical facts that should always be in context. Session search is for "did we discuss X last week?" queries where the agent needs to recall specifics from past conversations.
Controlling Memory Writes (write_approval)
By default the agent saves memory freely, including from the background self-improvement review that runs after a turn. If you would rather approve saves first, set memory.write_approval: true. It is a simple on/off gate applied to both foreground turns and the background review:
write_approval | Behaviour |
|---|---|
false (default) | Write freely, the gate is off (the pre-gate behaviour). |
true | Require approval before anything is saved. In the interactive CLI, foreground writes prompt you inline (entries are small enough to read in full). Everywhere else, messaging platforms, scripts, and the background self-improvement review, writes are staged for review with /memory pending. |
To turn memory off entirely (not just gate it), set memory_enabled: false.
Review staged writes from the CLI or any messaging platform:
/memory pending # list staged memory writes (auto ones tagged [auto])
/memory approve # apply one (or 'all')
/memory reject # drop one (or 'all')
/memory approval on # turn the gate on (or 'off') and persist it
This is the answer to "the agent saved a wrong assumption about me": set write_approval: true, and every save, especially the unprompted background ones, waits for your yes/no before it ever enters your profile.
Background Review Notifications (display.memory_notifications)
After a turn, the background self-improvement review may quietly save a memory or update a skill. This is Hermes' consent-aware learning loop: repeated corrections and durable workflow lessons become compact memory entries or procedural skills, while write_approval can stage those writes for review before they affect future sessions. By default it surfaces a short 💾 Memory updated line in chat so you know it happened. Control how chatty that is:
display:
memory_notifications: on # off | on (default) | verbose
| Value | Behaviour |
|---|---|
off | No chat notification. The review still runs and still writes, you just don't see a line for it. |
on (default) | Generic line, e.g. 💾 Memory updated, 💾 Skill 'foo' patched. |
verbose | Includes a compact preview of what changed, e.g. 💾 Memory ➕ User prefers terse replies or a "old" → "new" skill diff snippet. |
This only governs the gateway chat notification. The review itself, and writes to your memory/skill stores, are unaffected by this setting. Set it per-platform via display.platforms.<platform>.memory_notifications.
Running the Review on a Cheaper Model (auxiliary.background_review)
The review runs on your main chat model by default, replaying the conversation, which is already warm in the prompt cache, so it is cheap cache reads. On an expensive main model you can run the review on a cheaper model instead:
auxiliary:
background_review:
provider: openrouter
model: google/gemini-3-flash-preview # auto (default) = main chat model
When you point it at a model different from your main one, the review runs there for substantially lower cost (~3-5x in benchmarks). Because a different model cannot reuse your main model's prompt cache anyway, the fork automatically replays a compact digest of the conversation (recent turns verbatim + a summary of older ones) rather than the full transcript, minimizing what it writes to the new cache. Capture holds: in testing, memory capture was identical and skill capture near-identical to the main-model review.
Leave it at auto (or set it to your main model) and nothing changes, the review keeps running on the main model with the full warm-cache replay.
Controlling Skill Writes (skills.write_approval)
Skills use the same on/off gate, but the review UX differs because a SKILL.md is far too large to read in a chat bubble:
skills:
write_approval: false # false = write freely (default) | true = require approval
When write_approval: true, skill writes (create / edit / patch / write_file / delete) always stage regardless of origin. You review the one-line gist inline, but the full diff stays out-of-band:
/skills pending # list staged skill writes + a one-line gist each
/skills diff # full unified diff (best viewed in CLI or dashboard)
/skills approve # apply it (or 'all')
/skills reject # drop it (or 'all')
/skills approval on # turn the gate on (or 'off') and persist it
On a messaging platform, approve a skill from its gist + metadata, or open /skills diff on the CLI / dashboard / the staged file under ~/.hermes/pending/skills/<id>.json when you want to read the whole change.
External Memory Providers
For deeper, persistent memory that goes beyond MEMORY.md and USER.md, Hermes ships with 8 external memory provider plugins, including Honcho, OpenViking, Mem0, Hindsight, Holographic, RetainDB, ByteRover, and Supermemory.
External providers run alongside built-in memory (never replacing it) and add capabilities like knowledge graphs, semantic search, automatic fact extraction, and cross-session user modeling.
To set up an external provider:
hermes memory setup # pick a provider and configure it
hermes memory status # check what's active
See the Memory Providers guide for full details on each provider, setup instructions, and comparison.
Requirements and Limitations
Prerequisites
- Hermes Agent must be installed and configured.
- The memory system is built-in and requires no additional dependencies.
- For external memory providers, additional setup may be required.
Supported Platforms
- The memory system works on any platform where Hermes Agent runs (Linux, macOS, Windows via WSL).
- Session search requires SQLite with FTS5 support, which is included with the agent.
Version Constraints
- No specific version constraints are mentioned in the sources.
Known Gaps and Limitations
- Memory does not auto-compact. When a write would exceed the limit, the
memorytool returns an error instead of silently dropping entries. The agent must then make room itself by consolidating or removing entries in the same turn before retrying. - The
replaceaction is also bound by the character limit. Swapping an entry for a longer one can still overflow, so the new content must be shortened (or another entry removed) to fit. - Memory injection is a frozen snapshot captured once at session start. Changes made during a session are persisted to disk immediately but will not appear in the system prompt until the next session starts.
- Substring matching for
replaceandremoverequires a unique substring. If the substring matches multiple entries, an error is returned asking for a more specific match. - External memory providers run alongside built-in memory but never replace it. They add capabilities but do not remove the built-in system.
- The background review runs on the main chat model by default. Running it on a cheaper model may result in near-identical but not guaranteed identical skill capture.
Troubleshooting
Memory Write Exceeds Limit
Problem: The agent tries to add an entry that would exceed the character limit, and the tool returns an error.
Fix: The agent should read the current entries from the error response, identify entries that can be removed or consolidated, use replace to merge related entries into shorter versions, and then retry the add. As a best practice, consolidate when memory is above 80% capacity.
Substring Match Ambiguous
Problem: The replace or remove action fails because the old_text substring matches multiple entries.
Fix: Provide a more specific substring that uniquely identifies the target entry. The error message will indicate the ambiguity.
Agent Saves Wrong Assumption
Problem: The agent saves an incorrect or unwanted memory entry.
Fix: Set memory.write_approval: true in the configuration. This gates all writes, including those from the background self-improvement review, requiring your approval before they take effect. Use /memory pending, /memory approve, and /memory reject to manage staged writes.
Background Review Notifications Too Chatty
Problem: The agent displays too many memory update notifications in chat.
Fix: Set display.memory_notifications: off to suppress all notifications, or display.memory_notifications: on for a generic line only. The review still runs and writes regardless.
External Memory Provider Not Working
Problem: An external memory provider fails to initialize or respond.
Fix: Run hermes memory status to check what is active. Run hermes memory setup to reconfigure the provider. Consult the Memory Providers guide for provider-specific troubleshooting.
Session Search Returns No Results
Problem: The session_search tool returns no results for a query.
Fix: Ensure that past sessions exist by running hermes sessions list. The search uses FTS5 full-text search on the SQLite database at ~/.hermes/state.db. If the database is missing or corrupted, the agent may need to be restarted.
Sources & References
This page was researched from 1 independent source, combined and verified for completeness.
- 1.Persistent MemoryOfficial documentation · primary source
The #1 AI Newsletter
The most important ai updates, guides, and fixes — one weekly email.
No spam, unsubscribe anytime. Privacy policy