Skip to content

Case study

CSM - Cross-Session Memory

A TypeScript plugin that gives AI coding agents persistent, verifiable memory across sessions.

production v1.0.0 TypeScript ESM PostgreSQL · SQLite
RoleSole builder
Span2026-06-12 - present (30 days)
Tests969 tests · ~244 files
SourceGitHub repo ↗

01 · Definition

CSM is a cross-session memory plugin for AI coding agents. It hooks into the agent’s tool lifecycle, extracts structured memories from every tool call - errors, file edits, decisions, milestones - stores them with graph, vector, and text indices, and re-injects relevant context at the start of each new session.

It runs as a TypeScript ESM plugin. It supports both PostgreSQL (for production scale) and SQLite (for local-first operation). It is not a model wrapper, not a SaaS, and not a prompt template library - it is infrastructure.

What it is not:

  • Not a chatbot. It has no user interface. It runs inside the agent’s process.
  • Not a cloud product. It runs on your machine, against your database.
  • Not a prompt engineering tool. It does not write prompts. It manages the memory that prompts reference.

02 · The problem

Most AI coding agents still lack durable, auditable continuity. Some cache threads or read project rules, but preferences, decisions, mistakes, tool usage patterns, and file history don’t truly persist. The next session starts partially blind.

This produces three compounding failures:

  1. Context loss. The agent re-asks questions you already answered. It makes the same mistakes. It re-discovers the same architectural constraints. You spend the first 20 minutes of every session re-explaining your project.
  2. Unverifiable claims. When an agent says “it works,” there is nothing to audit. No log of what was tried, what failed, what was learned. No way to replay a session. No telemetry on recall quality. The system is a black box that produces output.
  3. Context bloat. To compensate for no memory, agents are fed ever-larger context windows. More tokens, more cost, more latency, more hallucination surface. The solution to forgetting is not more context - it is better recall.

03 · Architecture

CSM architecture DATA PATH Agent activity tool calls · errors · milestones Extract entities · tags · chunks Quality + dedup score · merge · prune Recall graph · vector · text Context assembly re-entry block · compaction Re-entry context in continuity: next session inherits SUPPORTING SUBSYSTEMS Work LedgerSHA-256 line lineage Living Statebelief + self-model Self-Model8 capability scores Belief Promotion111 entries · governance Recall Telemetry590K events logged Context Compaction2.0B tokens saved SQLite + PG adaptersdual-backend parity
Data flows left-to-right, feedback closes the loop. Seven supporting subsystems beneath make the loop verifiable.

The data path is a loop, not a pipeline:

  • Agent activity - every tool call, error, and milestone is an event that becomes an experience packet
  • Extraction - entities, tags, and chunks are derived from each event
  • Quality + dedup - memories are scored, merged, and pruned before storage
  • Recall - graph traversal (memory_links), vector similarity (memory_chunks), and text search combine to find relevant context
  • Context assembly - recalled memories are compiled into a re-entry block with compaction to fit budget
  • Re-entry - the next session inherits the context. The loop closes.

Seven supporting subsystems make the loop verifiable rather than magical:

SubsystemRoleEvidence
Work LedgerSHA-256 line lineage, run attribution, work-survival recomputation121 changes tracked
Living StateRuns the advisory pipeline (scan → update → consolidate) before each session28,043 packets processed
Self-ModelTracks 8 capability scores with confidence, uncertainty, and drift warnings8 capabilities
Belief PromotionPromotes recurring patterns into durable beliefs under governance111 entries
Recall TelemetryLogs every recall event for quality auditing647,074 events
Context CompactionDistills tool-call outputs into references to fit context budget2.015B tokens saved
SQLite + PG adaptersDual-backend parity - same code, two storage layers59,834 rows on PG

04 · Demonstration

CSM database schema memories memory_links memory_chunks sessions experience_packets compaction_metrics memory_recall_events retrieval_logs belief_knowledge_store self_model_capabilities work_ledger_changes
11 tables · two backends (SQLite + PostgreSQL) · parity-tested. This is the picture screenshots can't give you.

CSM is a plugin, not a GUI application - there are no screenshots to show. Instead, the schema diagram above is the proof of the system’s shape. 11 tables, each with a defined role:

  • memories - 59,834 rows (52,455 live, 7,379 archived). Episodic (33,678), conversation (16,639), procedural (9,127), lesson (133), workspace (129), repo (107), preference (21).
  • memory_links - 40,538 edges in the semantic graph.
  • memory_chunks - 59,745 chunks for vector recall.
  • experience_packets - 28,043 captured tool events with derived internal state (cognitive load, frustration, energy, dominant emotion, stance, urgency).
  • compaction_metrics - 10,663 events with before/after token counts. Total tokens_saved: 2,015,470,627. Before: 2,220,425,444. After: 204,954,817. Reduction: 90.8%.
  • memory_recall_events - 647,074 events across 4 recall sources: context_recall (509,611), search (124,385), empty_result (11,733), list (1,345). Spanning 235 distinct sessions.
  • retrieval_logs - 30 retrieval audit rows.
  • belief_knowledge_store - 111 promoted beliefs (preference, opinion, worldview).
  • self_model_capabilities - 8 tracked capabilities with confidence and drift detection.
  • work_ledger_changes - 121 file changes with SHA-256 line lineage.
  • sessions - 602. Data span: 2026-06-12T17:40 → 2026-07-11T03:28 (30 days).

05 · Evidence

Evidence · frozen snapshot

verified 2026-07-11
59,715 Total memories 52,336 live · 7,379 archived
35,635 Memory links (graph) semantic + entity edges
58,317 Memory chunks for vector recall
22,749 Experience packets captured tool events
10,663 Compaction events context distillations
2.015B Tokens saved 90.8% reduction
642,650 Recall events 232 sessions with telemetry · 4 sources
584 Total sessions 214 with telemetry · 370 pre-rollout
111 Belief knowledge promoted entries
8 Self-model caps tracked capabilities
121 Work-ledger changes SHA-256 attributed
969 Tests passing across ~244 files

Every number above was obtained via read-only SELECT against the production CSM PostgreSQL database on 2026-07-11 (localhost:5432). Not estimates, not projections. The 370 sessions without recall logs pre-date the telemetry rollout (Phase 6); they used CSM's memory features but didn't emit recall events to the logging table. See the verification SQL query for the exact statements.

Every number in the evidence panel above was obtained via read-only SELECT queries against the production CSM PostgreSQL database on 2026-07-11. The connection string is postgresql://opencode_memory@localhost:5432/opencode_memory.

Notable findings:

  • Embeddings: 8,983 memories have non-null embeddings (no embeddings_768 or embeddings_1536 partition rows were populated - embeddings are stored inline in the memories table). Vector recall thus operates over a subset, with text/graph recall covering the remainder.
  • Recall quality distribution: the empty_result source (11,733 events) represents a 1.8% no-result rate - recall attempts that returned nothing because no memory matched the query. This is distinct from precision (did the agent get the right memory?) or recall against ground truth; empty_result means the store had nothing to offer, not that it offered the wrong thing. Both failure modes are logged internally; only the no-result rate is surfaced here because precision against ground truth requires human-labelled reference queries that don’t yet exist at scale. Not zero, and worth investigating, but well within acceptable range for a 60K-row memory store.
  • Compaction ratio: 90.8% is consistent across the 30-day span. Compaction is not asymptotic - it keeps working as the memory store grows.
  • Session telemetry coverage: 235 of 602 sessions have recall telemetry. The 367 sessions without recall logs pre-date the Phase 6 telemetry rollout - they used CSM’s memory features (write, recall, graph) but didn’t emit recall events to the logging table because that instrumentation didn’t exist yet. The memory store itself covers all 602 sessions; only the fine-grained recall-audit telemetry is partial.

06 · Technical decisions

Why a plugin, not a standalone service?

A service requires the agent to call out over a network boundary. Latency, failure modes, and trust boundaries all degrade. A plugin runs in-process, has direct access to tool-call events, and can inject context synchronously. CSM is a TypeScript ESM plugin loaded by the agent at startup.

Why dual backends?

SQLite for local-first (no server, zero config, file-backed). PostgreSQL for production (scale, concurrency, rich queries). The same plugin code works against both. Parity is tested - 969 tests run against both backends.

Why graph + vector + text?

No single recall path is sufficient. Vector similarity finds semantically related memories but misses exact entity matches. Graph traversal finds related-by-link memories but misses semantic neighbors. Text search finds exact string matches but misses paraphrases. CSM combines all three and logs which path found what (memory_recall_events.source).

Why experience packets with internal-state classification?

A memory of “edit succeeded” is less useful than a memory of “edit failed three times, frustration rose, then succeeded after switching approach.” Experience packets capture a classification of the agent’s operational state alongside each event - cognitive load, frustration level, energy, dominant emotion, stance, urgency. These are classification signals derived from observable tool behavior (retry counts, error frequency, session length), not simulated emotions. A high “frustration” score means the agent retried the same operation multiple times, not that it felt anything. The distinction matters because it keeps the system grounded in measurable signals rather than anthropomorphic projection.

Why context compaction?

The agent’s context window is finite. Without compaction, it fills with raw tool-call output - large file reads, long command outputs - that the agent no longer needs. CSM distills these into structured references, freeing budget for new work. 90.8% reduction is not a theoretical number - it is the measured average across 10,663 real compaction events.

07 · Failures & lessons

This section is honest. CSM was not built right the first time.

The CRLF edge case

Early versions of the memory extractor assumed Unix line endings. When a Windows tool produced CRLF output, the extractor split memories incorrectly, producing duplicate or truncated entries. The fix was to normalize line endings before extraction - but the deeper lesson was that memory extraction must be encoding-agnostic. Tool output is not text; it is bytes that happen to look like text.

Provider-failure corruption

When an LLM provider call failed mid-extraction (rate limit, timeout, 500), the partial memory was written anyway. This produced “garbage memories” - entries with valid metadata but empty or truncated content. The fix was a transactional wrapper that rolled back on provider failure. The lesson: memory writes must be atomic. A partial memory is worse than no memory.

SQLite vs PostgreSQL parity gaps

Several PostgreSQL features (array types, JSONB operators, partial indexes) have no SQLite equivalent. Early code used PG-specific syntax that silently degraded on SQLite. The fix was a compatibility layer with explicit feature detection. The lesson: dual-backend parity is a testing problem, not a code problem. You only find the gaps by running the same test suite against both.

Belief promotion governance

The initial belief promotion pipeline was too aggressive - it promoted single-occurrence patterns as beliefs, producing noise and drift warnings that were never reviewed. The fix was governance: promotion requires multiple confirmations, a minimum confidence threshold, and a review queue. The lesson: promotion without governance is just a different kind of noise.

08 · Current status

CSM is at v1.0.0 in production. It has been running continuously for 30 days across 602 sessions. All 969 tests pass. No data loss. No unrecoverable corruption. The recall no-result rate is 1.8%.

Build log

CSM - how it was built

Each phase was an engineering milestone with tests, docs, and failures that didn't make the headlines.

  1. PHASE 1

    Memory extraction ←→ context injection

    First memory write/recall cycle. Plugin pattern established.

    Shipped
  2. PHASE 2

    Vector similarity search

    Embedding generation, cosine similarity recall, chunking.

    Shipped
  3. PHASE 3

    Graph relationships

    memory_links table, semantic graph traversal, linked recall.

    Shipped
  4. PHASE 4

    Experience packets

    Tool-call event capture, internal-state derivation (load, frustration, energy, stance - classification signals, not simulated emotion).

    Shipped
  5. PHASE 5

    Self-model & belief promotion

    Capability tracking, belief candidates, promotion governance.

    Shipped
  6. PHASE 6

    Recall telemetry & quality audit

    Live recall event logging, recall quality reports, governance buckets.

    Shipped
  7. PHASE 7

    Context compaction

    2.0B tokens saved across 10,663 compaction events. 90.8% reduction.

    Shipped
  8. PHASE 8

    Work ledger & surviving changes

    SHA-256 line lineage, run attribution, work-survival recomputation.

    Shipped
  9. Current

    v1.0.0 · production

    969 tests passing · 59,715 memories · 599 sessions · steady-state operation.

    Live

Known limitations

  • No public UI. CSM is a plugin. The DB-schema diagram and evidence panel on this page are the visual proof. A future observability dashboard is planned but not built.
  • No multi-process concurrency. The SQLite adapter assumes single-process access. PostgreSQL handles concurrency natively.
  • Embeddings coverage is partial. 8,881 of 59,715 memories have embeddings. Backfill is available (csm_memory_backfill_embeddings) but not yet run to completion.
  • Numbers are frozen at 2026-07-11. A build-time query mechanism to refresh them is planned but not implemented in v1.

Next research layer

CSM currently persists memory and structured reentry context. Persistent Thinking is the proposed next layer: preserving unresolved reasoning topology, evidence dependencies, and branch state across sessions - not as a replacement for CSM memory, but as the reasoning-continuity layer above it.

09 · Source & documentation

  • Source code: github.com/NovasPlace/CSM (public)
  • License: MIT
  • Language: TypeScript ESM
  • Runtime: Bun / Node.js
  • Backends: PostgreSQL, SQLite
  • Test suite: 969 cases across ~244 files
  • Documentation: Phase docs (docs/PHASE*.md), agent guidance (AGENTS.md), debug notes (DEBUG_NOTES.md), CodeQL findings (docs/CODEQL*.md)
  • Build log: Each PHASE*.md documents a real milestone, with the decisions and failures that got us there.