Stash

2026-05-22 · page · source ↗ · source summary

Open-source persistent memory for AI agents. A single Go binary backed by PostgreSQL+pgvector that gives LLMs durable memory across sessions via MCP, with an 8-stage cognitive consolidation pipeline that transforms raw observations into structured knowledge — facts, relationships, causal links, patterns — and decays stale beliefs over time.


Architecture

Stash is a layered Go application (cmd/cli/main.go) structured as:

CLI (urfave/cli v3) → Brain → Reasoner/Embedder → PostgreSQL + pgvector

The Brain struct (internal/brain/brain.go:78) is the central orchestrator, wrapping a pgxpool, an embedder, a reasoner, and SQL query templates. Every operation flows through the Brain.

Bootstrap (internal/bootstrap/bootstrap.go) wires everything: loads .env config, opens the database (running goose migrations + creating HNSW indexes on episodes.embedding and facts.embedding), wraps the embedder in a SHA-256 hash-based cache, and builds the reasoner.

Data Model

The system defines 16 domain types (internal/models/models.go):

The 8-Stage Consolidation Pipeline

The consolidation pipeline (internal/brain/consolidate.go) runs per-namespace and processes only new data since the last run via checkpoint IDs. The stages execute in this order:

  1. Episodes → Facts — greedy cosine-similarity clustering groups related episodes, then the reasoner synthesizes a structured fact with entity/property/value extraction. Duplicate facts are detected by vector similarity and deduplicated. Also runs contradiction detection inline.

  2. Facts → Relationships — for each new fact with structured fields, the reasoner extracts entity relationship triples.

3.5. Facts → Causal Links — feeds a batch of facts to the reasoner to extract cause-effect pairs (internal/brain/causal.go).

  1. Goal Progress Inference — assesses whether recent facts indicate progress toward, completion of, or contradiction of active goals (internal/brain/consolidate_goal.go).

  2. Failure Pattern Detection — checks if recent episodes repeat past failures, and extracts higher-order failure patterns as new facts (internal/brain/consolidate_failure.go).

  3. Facts + Relationships → Patterns — extracts abstract patterns spanning multiple facts and relationships with coherence scoring.

  4. Hypothesis Evidence Scanning — tests open hypotheses against new facts. Auto-confirms when supporting evidence confidence ≥ 0.9. Auto-rejects when contradicting evidence ≥ 0.9 (internal/brain/consolidate_hypothesis.go).

  5. Confidence Decay — pure SQL: multiplies fact confidence by a decay factor (default 0.95) for facts not updated within the window (default 7 days). Facts below the expiry threshold (default 0.1) are soft-deleted (internal/brain/decay.go).

The pipeline numbering (1,2,3.5,6,7,3,8,5) reflects the actual execution order — causal links come before patterns, and decay runs last.

MCP Integration

The MCP server (cmd/cli/mcp.go) exposes 27 tools via SSE and stdio transports. The prompts template (cmd/cli/mcp_prompts.tmpl, ~1000 lines) is a detailed behavioral contract for AI agents — it defines session protocol (init → recall → work → consolidate → context handoff), namespace rules, a self-model under /self, and per-tool detailed guidance with explicit "cost of not calling" admonitions.

Default namespace scaffold created by the init tool: - /self — agent's self-knowledge root - /self/capabilities — what the agent can do well - /self/limits — what the agent struggles with - /self/preferences — how the agent works best

Key Techniques

Greedy Vector Clustering

Episodes are clustered by cosine similarity using a simple greedy algorithm (consolidate.go:314). Each unclustered episode becomes a seed; all other unclustered episodes within the similarity threshold (default 0.85) join its cluster. O(n²) but negligible vs. LLM API call cost at batch sizes of 100. This is simpler than k-means or DBSCAN and doesn't require pre-specifying the number of clusters.

Defensive LLM Reasoning

The reasoner (internal/reasoner/openai.go) uses aggressive prompt engineering: - Strict system prompt: "You are a strict information extraction engine. Extract ONLY what is explicitly stated. Never guess. Never approximate." - 2-attempt retry loop: On parse failure or validation failure, the system prompt is appended with a retry warning and the specific error - Grounding validation: validateFactGrounding checks the summary isn't a verbatim copy of any source text (tokenizing and checking >80% word overlap triggers retry) - ID validation: Extracted source IDs (fact IDs, relationship IDs) are checked against the actual provided IDs — hallucinated IDs trigger retry

Contradiction Detection with Auto-Resolution

When a new fact is inserted with (entity, property, value), the system queries for existing facts with the same entity+property but different value (internal/brain/contradiction.go:18). Each pair is sent to the reasoner for classification: - Replacement (confidence ≥ 0.9): auto-supersede the old fact by setting valid_until - Replacement (confidence < 0.9): record as unresolved contradiction for human review - Contradiction: record as unresolved - Compatible: both values can coexist, no action

Checkpoint Safety

Progress checkpoints are only advanced if no errors occurred during that stage. If a stage fails, it re-processes the same data on the next run. The final checkpoint save even uses context.Background() if the original context was cancelled — preventing partial-state loss.

Embedding Cache with Request Deduplication

internal/embedder/cache.go uses SHA-256 hash + model name as cache key, backed by the embedding_cache PostgreSQL table. In-flight request deduplication via sync.Map — concurrent requests for the same text wait on a sync.WaitGroup rather than making duplicate API calls.

Hierarchical Namespace Resolution

Paths like /projects/stash match that exact namespace AND all descendants (/projects/stash/backend). Root / matches all namespaces. resolveNamespaceIDWithDescendants (brain.go:200) uses LIKE pattern matching for descendant resolution.

Recursive CTE Causal Tracing

TraceCausalChain (causal.go:116) uses PostgreSQL recursive CTEs to walk cause→effect or effect→cause chains with bounded depth. Supports forward ("what did this cause?") and backward ("what caused this?") traversal.

Design Decisions

Optimized for Correctness, Not Throughput

Every pipeline stage has error accumulation rather than fail-fast. Checkpoints are only advanced on success. Batch sizes are capped at 30-100. This means consolidation is slow but safe — it prioritizes never losing data over processing speed.

Self-Hosted by Default

Unlike Mem0 (hosted API), Stash is a single binary you run yourself. Docker Compose brings up Postgres + pgvector + the Stash binary. The trade-off is operational burden for data sovereignty.

MCP as the Only Interface

There's no REST API, no gRPC, no SDK. MCP is the sole integration surface. This is a strong bet on MCP as the standard agent protocol. The HTTP server only serves metrics and health checks.

Cognitive Metaphor Over Engineering Pragmatism

Stash explicitly models biological memory concepts: episodic memory (raw observations), semantic memory (consolidated facts), consolidation (sleep-like batch processing), decay (forgetting), and contradiction resolution. This is opinionated — it adds complexity over a simple vector store, but provides richer retrieval patterns (causal tracing, contradiction detection, goal tracking).

All Soft-Delete, No Hard-Delete (by default)

Episodes, facts, relationships, patterns, hypotheses, and goals all use deleted_at soft-delete. The forget command is a soft-delete. Only purge episode does a hard delete. This means the system never truly forgets — it only hides. Good for audit, bad for GDPR.

Confidence as a First-Class Concept

Every fact has a confidence score. Confidence is calculated from observation count (Laplace-like smoothing: n/(n+2)) with a boost for structured fields. Confidence decays over time. Low-confidence facts are soft-deleted. This means the system has an opinion about what it knows vs. what it merely suspects — unlike most memory systems where everything is equally "true."

Comparison Notes

Weaknesses

#tool #project #agent-memory


Sources: Stash: Persistent Memory for AI Agents Last updated: 2026-05-22