Skip to content

Latest commit

 

History

History
261 lines (195 loc) · 12.1 KB

File metadata and controls

261 lines (195 loc) · 12.1 KB

recall-echo — Spec v3.0

What It Is

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.

The Problem

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.

Architecture

┌──────────────────────────────────────────────────────────┐
│              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

Dual-Mode Operation

recall-echo operates in two modes:

1. pulse-null Plugin (Primary)

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() and checkpoint::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.

2. Standalone CLI

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

Layer Details

Layer 1 — Curated Memory (MEMORY.md)

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 distill analyzes heavy sections and suggests extracting them to topic files (e.g., memory/debugging.md)

Layer 2 — Short-Term Memory (EPHEMERAL.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

Layer 3 — Long-Term Memory (conversations/)

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

Archive Format

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, Bash

Archives 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)

Summarization

recall-echo supports two summarization strategies:

  1. LLM-enhanced (when an LmProvider is 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.

  2. 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.

Tag Extraction

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.

Plugin Integration

pulse-system-types Dependency

recall-echo depends on pulse-system-types v0.5 for:

  • Plugin trait — lifecycle, health, meta, role, setup prompts, as_any()
  • PluginContext — entity_root, entity_name, Arc<dyn LmProvider>
  • PluginRole::Memory
  • HealthStatus — Healthy, Degraded(reason), Down(reason)
  • Message, ContentBlock — conversation types for archival

Factory Pattern

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.

Health Checks

  • Healthy: memory/, conversations/, and MEMORY.md all exist
  • Degraded: memory/ exists but MEMORY.md or conversations/ missing
  • Down: memory/ directory not found

Configuration

Optional .recall-echo.toml in the memory directory:

[ephemeral]
max_entries = 5

All settings have sensible defaults. Missing file or invalid values fall back silently.

Technology

  • 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

Testing

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.

Resolved Decisions

  1. Entity-root model: All paths relative to entity_root, not hardcoded home dir. Supports multi-entity scenarios.
  2. Markdown archives: Replaced JSONL with markdown + YAML frontmatter. Human-readable, Grep-searchable.
  3. FIFO ephemeral: Rolling window of N entries (default 5) instead of single session summary.
  4. LLM summaries with fallback: Graceful degradation when no provider available.
  5. 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.
  6. Zero external date/YAML/TOML deps: Hand-rolled parsers for minimal bloat.
  7. 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.
  8. Async throughout: Factory and archival functions are async to match pulse-null's architecture.