How ai-memory Provides Persistent Cross-Session Memory for AI Coding Agents
ai-memory enables persistent, cross-session memory by treating a directory-local markdown wiki as the single source of truth and building a derived SQLite index that agents query at runtime.
AI coding agents traditionally lose all context when a terminal session ends. The akitaonrails/ai-memory project solves this with a Git-backed wiki architecture where every observation gets written atomically to markdown and indexed for fast retrieval. This article explains the seven-stage workflow—from lifecycle hooks to retention policies—that makes cross-session memory possible.
The Core Architecture: Wiki-First, Index-Second
Unlike systems that store everything in an opaque database, ai-memory uses a human-readable markdown wiki as the authoritative source. The SQLite database exists only as a performance-optimized derivative.
This design delivers three advantages for AI coding agents:
- Durability – The wiki is a standard Git repository with full version history
- Portability – Any tool can read the markdown without special drivers
- Inspectability – Developers can browse, search, and edit memory with standard tools
The trade-off is a single-writer bottleneck that serializes all mutations through a dedicated actor.
Stage 1: Lifecycle Hook Emission
Every agent action triggers a JSON hook to the /hook endpoint. In crates/ai-memory-hooks/src/lib.rs, the router sanitizes payloads and assigns an ObservationKind:
| Kind | Trigger |
|---|---|
session-start |
New CLI invocation or IDE window opened |
user-prompt |
Agent receives a coding request |
tool-execution |
Agent runs a command or edits a file |
session-end |
Process termination or explicit cleanup |
Each observation becomes a WriteCmd enqueued to the single-writer SQLite actor defined in crates/ai-memory-store/src/writer.rs.
# Example: Start a managed workstream that emits session-start
ai-memory run
Stage 2: Atomic Markdown Persistence
The writer actor persists observations by appending to markdown pages under <data_dir>/wiki/. In crates/ai-memory-wiki/src/atomic.rs, writes use a temp-file + rename + fsync sequence that guarantees atomicity even on power loss:
// From ai-memory-wiki/src/atomic.rs
pub fn atomic_write(path: &Path, content: &[u8]) -> io::Result<()> {
let temp = NamedTempFile::new_in(path.parent().unwrap())?;
temp.as_file().write_all(content)?;
temp.as_file().sync_all()?; // fsync before rename
temp.persist(path)?; // atomic rename
Ok(())
}
This ensures the wiki never contains partially-written pages. Common page paths include:
log.md– Chronological event streamnotes/*.md– Developer-captured insightssessions/<id>.md– Auto-generated session summaries
Stage 3: Derived SQLite Indexing
While the wiki remains the source of truth, crates/ai-memory-store/src/lib.rs maintains a derived database at <data_dir>/db/memory.sqlite with tables for:
- pages – Markdown file metadata with access counters
- observations – Individual hook payloads with timestamps
- links – Cross-references between pages
- page_embeddings – Optional vector representations for semantic search
The schema enables FTS5 full-text search and vector RRF retrieval when embeddings are configured. Updates happen synchronously with markdown writes to maintain consistency.
Stage 4: Session Synthesis and Handoffs
When a session-end hook arrives, the server performs two critical operations:
- Generate session summary – Creates
sessions/<id>.mdwith condensed context - Create handoff row – Inserts
memory_handoff_beginpointing to the next expected agent
This memory handoff eliminates manual context copying. A developer running Claude Code at 9 AM can resume with Codex at 2 PM with zero friction.
# End session; creates summary and handoff automatically
ai-memory finalize-session
Stage 5: Runtime Query Surface
Agents retrieve memory through MCP tools implemented in crates/ai-memory-store/src/fts_query.rs. The three primary tools are:
memory_query– FTS5 + entity-match + optional vector RRFmemory_read_page– Direct markdown retrieval by pathmemory_briefing– Aggregated context for session startup
Each query bumps pages.access_count, enabling the retention system to identify hot versus cold knowledge.
# Search across all remembered pages with relevance scoring
ai-memory query "how to configure auto-improve"
For programmatic access from custom agents:
use ai_memory_client::MemoryClient;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let client = MemoryClient::new("http://127.0.0.1:49374")?;
let resp = client.memory_query()
.query("embedding provider")
.limit(5)
.send()
.await?;
for hit in resp.hits {
println!("▶ {} ({:.2})", hit.path, hit.score);
}
Ok(())
}
Stage 6: Optional LLM Consolidation
When AI_MEMORY_LLM_PROVIDER is set, the memory_consolidate tool in crates/ai-memory-consolidate/src/lib.rs rewrites episodic logs into semantic knowledge. This transforms raw event streams into organized documentation that survives long-term.
The consolidation pipeline:
- Identifies related observations and pages
- Generates LLM summaries with source attribution
- Proposes structural improvements (merging, splitting, linking)
- Applies changes atomically through the standard writer path
Without consolidation, memory remains searchable but fragmented. With it, the wiki becomes a curated knowledge base.
Stage 7: Retention and Decay
A periodic forget-sweep prevents unbounded growth:
- Evicts pages below an access threshold
- Respects explicit TTLs on observations
- Purges tombstoned records after grace period
- Preserves pinned knowledge regardless of temperature
This keeps query latency bounded while protecting critical context. The sweep runs as a background task in the writer actor, never blocking agent operations.
Key Source Files
| Component | Path | Purpose |
|---|---|---|
| Hook router | crates/ai-memory-hooks/src/lib.rs |
Validates JSON, assigns ObservationKind, enqueues writes |
| Wiki API | crates/ai-memory-wiki/src/lib.rs |
High-level markdown page operations |
| Atomic writes | crates/ai-memory-wiki/src/atomic.rs |
Crash-safe file persistence |
| Writer actor | crates/ai-memory-store/src/writer.rs |
Serialized DB mutations and FTS5 updates |
| Query engine | crates/ai-memory-store/src/fts_query.rs |
FTS5 + entity + vector RRF search |
| CLI entry | crates/ai-memory-cli/src/main.rs |
Subcommand parsing and execution |
| Consolidation | crates/ai-memory-consolidate/src/lib.rs |
LLM-driven page rewriting |
Summary
- Wiki-first design makes memory human-readable and Git-versioned
- Atomic writes via temp-file + rename + fsync guarantee durability
- Single-writer actor serializes mutations while allowing concurrent reads
- Session handoffs enable seamless context transfer between agents and sessions
- FTS5 + optional vector search delivers fast, relevant retrieval
- LLM consolidation transforms raw logs into structured knowledge
- Forget-sweep retention bounds storage without losing important context
Frequently Asked Questions
What makes ai-memory different from a simple log file?
Traditional logs are write-only streams that grow indefinitely. ai-memory maintains a bidirectional relationship between markdown content and a queryable SQLite index. Every write updates FTS5 and optional embedding tables, while every query feeds back into access statistics that drive retention. The atomic write protocol in crates/ai-memory-wiki/src/atomic.rs also ensures crash safety that append-only logs lack.
How does cross-session persistence actually work?
When an agent starts, it loads the memory_handoff_begin row created by the previous session's finalize-session command. This points to the summary page and relevant context pages. Because the wiki is directory-local and Git-backed, any subsequent agent invocation in the same repository sees identical state. No network sync or explicit export is required.
Can I use ai-memory without an LLM provider?
Yes. The core functionality—hook ingestion, atomic markdown writes, FTS5 search, and session handoffs—works entirely offline. LLM consolidation via memory_consolidate is optional and only activates when AI_MEMORY_LLM_PROVIDER is configured. Without it, you'll have searchable but unconsolidated episodic memory.
What happens if the SQLite database is corrupted?
The database is strictly a performance derivative. Delete <data_dir>/db/memory.sqlite and restart the server; it will rebuild the entire index from the markdown wiki in crates/ai-memory-store/src/lib.rs. This reconstruction preserves all content because the wiki is the authoritative source.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →