recall-echo is a persistent four-layer memory system for pulse-null entities. It gives AI agents long-term recall across sessions — a knowledge graph with Bayesian confidence, curated facts, recent session context, and searchable conversation archives. Designed as a native pulse-null plugin (Memory role) with standalone CLI support for administration.
Inspired by MemGPT (arxiv:2310.08560) — event-driven memory management for LLMs.
LLM coding agents start every session from zero. Built-in memory systems are typically a single flat file with no concept of short-term vs long-term memory, no session summaries, and no searchable history. Memory management that depends on the agent choosing to save things is circular — it forgets to remember.
recall-echo makes the entire memory lifecycle mechanical. When integrated with pulse-null, archival and checkpointing happen automatically via the plugin system. The agent writes to MEMORY.md during sessions. Everything else is enforced by the system.
┌──────────────────────────────────────────────────────────┐
│ MEMORY ARCHITECTURE │
│ │
│ Layer 0: KNOWLEDGE GRAPH (structured, semantic) │
│ ┌──────────────────────────────────────────────────┐ │
│ │ SurrealDB + FastEmbed │ │
│ │ Entities, relationships, episodes │ │
│ │ Bayesian confidence · Semantic search (HNSW) │ │
│ │ LLM-powered extraction + deduplication │ │
│ └──────────────────────────────────────────────────┘ │
│ │
│ Layer 1: CURATED (always in context) │
│ ┌───────────┐ │
│ │ MEMORY.md │ Facts, preferences, patterns │
│ └───────────┘ Distilled & maintained by the agent │
│ │
│ Layer 2: SHORT-TERM (FIFO rolling window) │
│ ┌───────────────┐ │
│ │ EPHEMERAL.md │ Last N session summaries │
│ └───────────────┘ Appended on archive, auto-trimmed │
│ │
│ Layer 3: LONG-TERM (searched on demand) │
│ ┌─────────────┐ ┌────────────────────────────┐ │
│ │ ARCHIVE.md │───→│ conversations/ │ │
│ └─────────────┘ │ conversation-001.md │ │
│ │ conversation-002.md │ │
│ │ ... │ │
│ └────────────────────────────┘ │
│ YAML frontmatter + markdown │
│ LLM-summarized or algorithmic │
└──────────────────────────────────────────────────────────┘
All paths are relative to an entity root directory:
{entity_root}/memory/
├── MEMORY.md # Layer 1 — curated facts
├── EPHEMERAL.md # Layer 2 — rolling session window
├── ARCHIVE.md # Layer 3 — conversation index
├── conversations/ # Layer 3 — full archives
│ ├── conversation-001.md
│ ├── conversation-002.md
│ └── ...
├── graph/ # Layer 0 — knowledge graph
│ ├── surreal/ # SurrealDB embedded data
│ └── models/ # FastEmbed cached models
└── .recall-echo.toml # Optional configuration
recall-echo operates in two modes:
As a native pulse-null plugin implementing the Plugin trait from pulse-system-types:
- Role:
PluginRole::Memory(required — exactly one per entity) - Factory:
create(config, ctx) -> Box<dyn Plugin> - Health: Reports memory directory state (Healthy / Degraded / Down)
- Setup: Prompts for entity_root during pulse-null init wizard
- Lifecycle: pulse-null calls
archive::archive_session()andcheckpoint::create_checkpoint()automatically
The plugin does not contribute tools, scheduled tasks, or HTTP routes. It is a data layer — pulse-null orchestrates when archival and checkpointing happen.
For administration and use outside pulse-null:
recall-echo init [entity_root] # Create memory directory structure
recall-echo status [entity_root] # Health check with dashboard
recall-echo dashboard [entity_root] # Full dashboard with stats and health
recall-echo search <query> # Line-level archive search
recall-echo search <query> --ranked # File-ranked search with relevance scoring
recall-echo distill [entity_root] # Analyze MEMORY.md, suggest cleanup
recall-echo consume [entity_root] # Output EPHEMERAL.md content
recall-echo archive-session # Archive Claude Code session from JSONL
recall-echo archive --all-unarchived # Batch archive all missed sessions
recall-echo checkpoint # Save checkpoint before context compression
recall-echo config # View or modify configuration
recall-echo graph <subcommand> # Knowledge graph operations
The source of truth. Distilled facts, user preferences, patterns, key decisions. Always loaded into agent context at session start.
- Lives at:
{entity_root}/memory/MEMORY.md - Size discipline: Keep under 200 lines
- Updated: During conversations when stable facts are confirmed
- Distillation:
recall-echo distillanalyzes heavy sections and suggests extracting them to topic files (e.g.,memory/debugging.md)
A FIFO rolling window of recent session summaries. Gives the agent immediate context about recent work.
- Lives at:
{entity_root}/memory/EPHEMERAL.md - Max entries: Configurable (default 5, range 1–50)
- Format: Separator-delimited entries with session ID, date, duration, message count, summary, and archive pointer
- Updated: Automatically when a session is archived
Full conversation archives with structured metadata. Not loaded into context — searched on demand via Grep.
- Index:
{entity_root}/memory/ARCHIVE.md(markdown table) - Archives:
{entity_root}/memory/conversations/conversation-NNN.md - Format: YAML frontmatter + markdown sections
Each conversation archive includes structured metadata and content:
---
log: 5
date: "2026-03-06T10:30:00Z"
session_id: "abc123"
message_count: 34
duration: "30m"
source: "session"
topics: ["auth", "jwt", "middleware"]
---
## Summary
LLM-generated summary of the conversation.
**Decisions**: Key decisions made during the session.
**Action Items**: Follow-up tasks identified.
### User
(message content)
### Assistant
(message content)
## Tags
**Decisions**: decided to use JWT for auth
**Files**: src/auth.rs, src/middleware.rs
**Tools**: Read, Edit, BashArchives are created with two source types:
session— end-of-session archive (appends to EPHEMERAL.md)checkpoint— pre-compaction checkpoint (does not append to EPHEMERAL.md)
recall-echo supports two summarization strategies:
-
LLM-enhanced (when an
LmProvideris available via pulse-null): Structured JSON output with summary, topics, decisions, and action items. Conversations are condensed to ~4000 characters before sending to the model. -
Algorithmic fallback (standalone or when LLM is unavailable): Keyword extraction with stop-word filtering and tool-target boosting. First user message as summary. No decisions or action items extracted.
Fallback is silent — if the LLM call fails, algorithmic extraction runs automatically.
Archives include heuristic-based structured tags:
- Decisions: Detected via linguistic markers ("decided to", "let's use", "chose to")
- Action items: Detected via markers ("todo:", "need to", "follow up")
- Files touched: Extracted from tool inputs (paths containing
/or.) - Tools used: Collected from tool use entries in the conversation
Max: 5 decisions, 5 action items, 10 files per archive.
recall-echo depends on pulse-system-types v0.5 for:
Plugintrait — lifecycle, health, meta, role, setup prompts,as_any()PluginContext— entity_root, entity_name,Arc<dyn LmProvider>PluginRole::MemoryHealthStatus— Healthy, Degraded(reason), Down(reason)Message,ContentBlock— conversation types for archival
pub async fn create(
config: &serde_json::Value,
ctx: &PluginContext,
) -> Result<Box<dyn Plugin>, Box<dyn Error + Send + Sync>>Config accepts entity_root (string). Falls back to ctx.entity_root if not specified. Returns a fully initialized RecallEcho instance — no two-phase init.
- Healthy: memory/, conversations/, and MEMORY.md all exist
- Degraded: memory/ exists but MEMORY.md or conversations/ missing
- Down: memory/ directory not found
Optional .recall-echo.toml in the memory directory:
[ephemeral]
max_entries = 5All settings have sensible defaults. Missing file or invalid values fall back silently.
- Language: Rust
- License: AGPL-3.0
- Dependencies: pulse-system-types (v0.5), clap, serde, serde_json, dirs, surrealdb, fastembed, tokio, chrono, thiserror, futures, regex
- Graph: surrealdb (embedded, kv-surrealkv), fastembed (BGE-Small-EN-v1.5, 384 dimensions)
- Zero external deps for YAML and date handling (hand-rolled parsers)
- Dev dependencies: tempfile, tokio
80 tests covering:
- Archive operations (creation, indexing, numbering)
- Checkpoint functionality
- Conversation processing (flattening, markdown rendering, topic extraction)
- Ephemeral FIFO window (append, trim, parse)
- Dashboard rendering and health assessment
- Search (line-level and ranked)
- Summarization (algorithmic)
- Status reporting
- Tags and frontmatter parsing
All tests run in isolated temporary directories — no production memory touched.
- Entity-root model: All paths relative to entity_root, not hardcoded home dir. Supports multi-entity scenarios.
- Markdown archives: Replaced JSONL with markdown + YAML frontmatter. Human-readable, Grep-searchable.
- FIFO ephemeral: Rolling window of N entries (default 5) instead of single session summary.
- LLM summaries with fallback: Graceful degradation when no provider available.
- Lifecycle hooks: As a standalone CLI, recall-echo installs PreToolUse (consume), PreCompact (checkpoint), and SessionEnd (archive-session) hooks. As a pulse-null plugin, pulse-null manages lifecycle directly.
- Zero external date/YAML/TOML deps: Hand-rolled parsers for minimal bloat.
- Plugin role: Memory (required, exactly one). No scheduled tasks or HTTP routes contributed. Graph query tool is contributed via pulse-null's GraphQueryTool, not the plugin itself.
- Async throughout: Factory and archival functions are async to match pulse-null's architecture.