The memory systems tour: from MemGPT to your window
TL;DR. Every memory system in the ecosystem answers three questions: what gets extracted from a conversation, where it lives, and what lands in your window per turn, and only the third question costs money. This chapter opens up the well-known open-source systems along that axis: Letta (the MemGPT lineage: self-editing core blocks, archival search, memory pressure), mem0 (the extract-and-update pipeline, its famous paper, and its 2026 pivot to add-only), the knowledge-graph stores (Graphiti, cognee, and the tiny MCP reference server), LangMem's memory taxonomy, and Anthropic's own pair (the client-side memory tool and context editing). A from-scratch lab reproduces the MemGPT loop in 150 lines: append, replace on contradiction, evict under pressure, retrieve on demand, quiz 4/4 from a fresh window at 78 tokens versus 292 for transcript replay. Each system ends with the practical wiring for Claude Code.
Contents
- The three questions, and the two camps
- The lab: the MemGPT loop from scratch
- Letta: the operating-system view, productized
- mem0: the pipeline, the paper, and the pivot
- The graph stores: Graphiti, cognee, and the MCP reference server
- LangMem: the taxonomy
- Anthropic's native pair: the memory tool and context editing
- Choosing for Claude Code
- Further reading
- Takeaways
Chapter 9 built the four operations (extract, store, retrieve, invalidate) and named the big three systems. Chapter 40 priced the loading rules. This chapter goes inside the real projects: how each one actually works, what each one puts in your window, and how each one bolts onto Claude Code. Facts below were checked against the projects' repositories, docs, and papers in July 2026; version numbers and star counts are that snapshot.
The three questions, and the two camps
Strip the marketing from any memory system and three design questions remain:
- Extraction. What becomes a memory: raw turns, LLM-extracted atomic facts, graph entities and relations, or files the agent writes deliberately?
- Storage. Where it lives: a vector store, a relational table, a knowledge graph with timestamps, or markdown on disk?
- Window contract. What lands in the context per turn, and who decides? This is the only question with a per-turn bill attached, and the one this book cares about most.
The third question splits the field into two camps:
- Pipeline memory. Your code (or the vendor's) runs extraction and retrieval around the model. The model never knows memory exists; it just receives a few injected facts. mem0, Graphiti, and LangMem's background mode work this way. Predictable cost, no agent effort, but the retrieval rule is fixed by the developer.
- Agentic memory. The model gets tools: read this, write that, search the store. The agent decides what to remember and what to recall, turn by turn. Letta, Anthropic's memory tool, the MCP memory servers, and Serena's memories work this way. Adaptive, and the agent can manage its own working set, but you are trusting the model to page well, and every memory op costs a tool round-trip.
Don't be confused. Around Claude specifically, three different things are all called "memory," and they are different products at different layers. Auto memory is a Claude Code feature: Claude writes markdown under
~/.claude/projects/<project>/memory/and loads the index each session (Chapter 18). The memory tool is an Anthropic API feature: a tool type (memory_20250818) your own application handles, with the files stored on your side. Memory MCP servers (OpenMemory, the reference server, Graphiti's) are third-party stores any MCP client can mount, Claude Code included. Same word, three mechanisms, three owners.
The lab: the MemGPT loop from scratch
The MemGPT paper (Packer et al., 2023, "MemGPT: Towards LLMs as Operating Systems") supplied the field's central metaphor: the window is RAM, external storage is disk, and the agent pages data between them itself, prompted by "memory pressure" interrupts as the window fills. The lab reduces that to its mechanics: a small always-in-window core the agent edits in place, an unbounded archival store reached by search, a recall log of past turns, and eviction when the core budget overflows.
"""A MemGPT-style memory hierarchy from scratch: core, archival, recall.
The idea from the MemGPT paper (Packer et al., 2023), reduced to its mechanics:
treat the context window like RAM and the store like disk, and give the agent
TOOLS to move facts between them. Three tiers:
core memory small, ALWAYS in the window, edited in place by the agent
(core_append / core_replace), evicted to archival on overflow
archival memory unbounded store outside the window, searched on demand
recall memory the raw transcript of past turns, searched on demand
This lab simulates three work sessions. Facts arrive, a contradiction forces an
in-place edit, the core budget forces evictions, and a final quiz has to be
answered from a FRESH window: core is simply there, everything else must be
retrieved. A transcript-replay baseline runs alongside for the token
comparison. Deterministic, standard library only.
Run: python3 memgpt_core.py
"""
def toks(s):
return len(s) // 4
def words(s):
return set("".join(c if c.isalnum() else " " for c in s.lower()).split())
# ---------------------------------------------------------------------------
# The three tiers.
# ---------------------------------------------------------------------------
class Memory:
CORE_BUDGET = 62 # tokens the core block may occupy, total
def __init__(self):
self.core = {"user": [], "project": []}
self.archival = [] # facts evicted from core or filed directly
self.recall = [] # every raw turn ever seen
self.trace = []
# -- the self-editing tools the agent calls ----------------------------
def core_append(self, section, fact, priority=1):
self.core[section].append((fact, priority))
self.trace.append(f"[core_append ] {section}: {fact}")
self._evict_if_over()
def core_replace(self, section, old_key, fact):
kept, replaced = [], None
for f, pr in self.core[section]:
if old_key in f and replaced is None:
replaced = f
kept.append((fact, pr))
else:
kept.append((f, pr))
self.core[section] = kept
self.trace.append(f"[core_replace ] {section}: '{replaced}' -> '{fact}'")
def archival_search(self, query, k=1):
q = words(query)
scored = sorted(self.archival, key=lambda f: -len(words(f) & q))
return [f for f in scored[:k] if words(f) & q]
def conversation_search(self, query, k=1):
q = words(query)
scored = sorted(self.recall, key=lambda t: -len(words(t) & q))
return [t for t in scored[:k] if words(t) & q]
# -- memory pressure: overflow spills the lowest-priority fact ---------
def _evict_if_over(self):
while toks(self.render_core()) > self.CORE_BUDGET:
section, idx = min(
((s, i) for s in self.core for i in range(len(self.core[s]))),
key=lambda si: self.core[si[0]][si[1]][1])
fact, _ = self.core[section].pop(idx)
self.archival.append(fact)
self.trace.append(f"[evict->archival] {fact} (core over budget)")
def render_core(self):
out = []
for section, facts in self.core.items():
out.append(f"<{section}>")
out += [f"- {f}" for f, _ in facts]
return "\n".join(out)
# ---------------------------------------------------------------------------
# Three sessions of facts arriving. Priority 2 = belongs in core long-term.
# ---------------------------------------------------------------------------
SESSIONS = [
[ # session 1: onboarding
("user", "name is Sol, timezone America/Toronto", 2),
("user", "prefers pip for Python installs", 2),
("project", "site deploys to Cloudflare Pages on push to main", 2),
("project", "books build with mdBook, navy theme", 2),
],
[ # session 2: work continues, one fact changes, minor facts arrive
("project", "CI runs on GitHub Actions with a 15 min budget", 1),
("user", "SWITCH: now prefers uv over pip", 2),
("project", "the 2026-03 incident was a bad symlink in build.sh", 1),
("project", "mathjax.js must be copied verbatim between books", 1),
],
[ # session 3: fresh window, the quiz
],
]
mem = Memory()
baseline_transcript = []
for n, session in enumerate(SESSIONS, 1):
for section, fact, pr in session:
# A real transcript carries both sides of the exchange, not bare facts.
baseline_transcript.append(f"user: by the way, {fact}")
baseline_transcript.append(f"assistant: got it, I will keep in mind that {fact}.")
mem.recall.append(f"session {n}: {fact}")
if fact.startswith("SWITCH:"):
mem.core_replace(section, "pip", fact.removeprefix("SWITCH: now "))
else:
mem.core_append(section, fact, pr)
print("=== The agent's memory ops, in order ===")
for line in mem.trace:
print(line)
print("\n=== Core memory block as session 3 opens (always in the window) ===")
core = mem.render_core()
print(core)
print(f"[{toks(core)} tokens, budget {Memory.CORE_BUDGET}]")
# ---------------------------------------------------------------------------
# The quiz, answered from a FRESH session-3 window.
# ---------------------------------------------------------------------------
print("\n=== Session 3 quiz: fresh window, core + retrieval only ===")
QUIZ = [
("Which installer does the user prefer, pip or uv?", "uv"),
("Where does the site deploy?", "cloudflare"),
("What caused the 2026-03 incident in build.sh?", "symlink"),
("What runs the CI, and what is its budget?", "github"),
]
core_facts = [f for sec in mem.core.values() for f, _ in sec]
retrieved, score = [], 0
for q, needle in QUIZ:
src, pool = "core ", [f for f in core_facts if words(f) & words(q)]
if not any(needle in f.lower() for f in pool):
pool = mem.archival_search(q)
retrieved += pool
src = "archival"
hit = next((f for f in pool if needle in f.lower()), None)
score += hit is not None
print(f" Q: {q}")
print(f" -> {src} {'HIT ' if hit else 'MISS'} {hit or '(nothing relevant)'}")
print(f"Score: {score}/{len(QUIZ)}")
# ---------------------------------------------------------------------------
# What did session 3's window carry, versus replaying the transcript?
# ---------------------------------------------------------------------------
replay = toks("\n".join(baseline_transcript))
carried = toks(core) + toks("\n".join(retrieved))
print(f"\n=== Session 3 window cost, facts only ===")
print(f"transcript replay : {replay:>4} tokens after 8 exchanges, grows every session forever")
print(f"core + retrieved : {carried:>4} tokens, bounded by budget + the slices this quiz used")
print(f"""
Lesson: the core block buys 'always known' for a fixed rent ({Memory.CORE_BUDGET} tokens),
eviction keeps that rent capped as facts accumulate, and everything evicted
stays reachable through search. The agent, not the developer, runs the moves:
append, replace-in-place on contradiction, evict under pressure, retrieve on
demand. That loop IS MemGPT, and every system in this chapter is a variation
on which tier holds what and who decides.""")
The verified output:
=== The agent's memory ops, in order ===
[core_append ] user: name is Sol, timezone America/Toronto
[core_append ] user: prefers pip for Python installs
[core_append ] project: site deploys to Cloudflare Pages on push to main
[core_append ] project: books build with mdBook, navy theme
[core_append ] project: CI runs on GitHub Actions with a 15 min budget
[core_replace ] user: 'prefers pip for Python installs' -> 'prefers uv over pip'
[core_append ] project: the 2026-03 incident was a bad symlink in build.sh
[evict->archival] CI runs on GitHub Actions with a 15 min budget (core over budget)
[core_append ] project: mathjax.js must be copied verbatim between books
[evict->archival] the 2026-03 incident was a bad symlink in build.sh (core over budget)
=== Core memory block as session 3 opens (always in the window) ===
<user>
- name is Sol, timezone America/Toronto
- prefers uv over pip
<project>
- site deploys to Cloudflare Pages on push to main
- books build with mdBook, navy theme
- mathjax.js must be copied verbatim between books
[54 tokens, budget 62]
=== Session 3 quiz: fresh window, core + retrieval only ===
Q: Which installer does the user prefer, pip or uv?
-> core HIT prefers uv over pip
Q: Where does the site deploy?
-> core HIT site deploys to Cloudflare Pages on push to main
Q: What caused the 2026-03 incident in build.sh?
-> archival HIT the 2026-03 incident was a bad symlink in build.sh
Q: What runs the CI, and what is its budget?
-> archival HIT CI runs on GitHub Actions with a 15 min budget
Score: 4/4
=== Session 3 window cost, facts only ===
transcript replay : 292 tokens after 8 exchanges, grows every session forever
core + retrieved : 78 tokens, bounded by budget + the slices this quiz used
Lesson: the core block buys 'always known' for a fixed rent (62 tokens),
eviction keeps that rent capped as facts accumulate, and everything evicted
stays reachable through search. The agent, not the developer, runs the moves:
append, replace-in-place on contradiction, evict under pressure, retrieve on
demand. That loop IS MemGPT, and every system in this chapter is a variation
on which tier holds what and who decides.
Every move in that trace has a production counterpart in the systems below. Watch for them.
Letta: the operating-system view, productized
What it is. Letta (github.com/letta-ai/letta, Apache-2.0, roughly 24k stars, actively
released through 2026) is the MemGPT paper turned into an agent platform: agents run as
services with their memory managed server-side, persisting across conversations by
construction.
How it works inside. The in-window unit is the memory block: a labeled string with a
description, a value, and a limit in characters, rendered into the prompt in an XML-like
format on every turn. Default agents ship two blocks, persona and human (20,000 characters
each by default; general blocks allow 100,000), and blocks can be attached to several agents at
once, which gives you shared memory between agents for free. The agent edits its own blocks
with tools whose names you will recognize from the lab: core_memory_append and
core_memory_replace, alongside archival_memory_insert, archival_memory_search (a vector
store of passages, unlimited, retrieved on demand), and conversation_search over past
messages. The paper's memory-pressure design survives in production as a summarization pass:
when a step's usage passes 90% of the window, older messages are trimmed into a recursive
summary, after a warning message urges the agent to save what matters to memory first, exactly
the eviction the lab forced with a 62-token budget.
Two 2025-2026 additions are worth knowing. Sleep-time agents (from the "Sleep-time Compute"
paper, Lin et al., 2025) attach a second agent that shares the primary's memory blocks and
reorganizes them between conversations with dedicated tools (memory_replace,
memory_insert, memory_rethink, memory_finish_edits): memory maintenance moved off the
hot path, the paper reporting about 5x less test-time compute for comparable accuracy on its
benchmarks. And Agent File (.af) is an open format that serializes an entire stateful
agent (blocks, message history, tool code, config) into one importable file, which is what
checkpointing an agent's memory looks like when memory is server-side state.
The window contract. Blocks are always resident (bounded by their limits); archival and conversation search land as tool results only when called; the recursive summary replaces evicted history. Rent is fixed and known in advance, which is the whole point.
Applied to Claude Code. Letta is a platform you run, not a plugin, so the practical
integrations are: point Letta at Anthropic models for its agents (it is model-agnostic), or
mount a Letta agent's capabilities into Claude Code via MCP (Letta speaks MCP as a host; the
community also ships Letta MCP servers). The more direct lesson for a Claude Code user is
architectural: Claude Code's own auto memory (MEMORY.md index always loaded, topic files on
demand) is the same core-plus-archival split with files standing in for blocks, and its
sibling letta-code CLI is that comparison made explicit by the Letta team themselves.
mem0: the pipeline, the paper, and the pivot
What it is. mem0 (github.com/mem0ai/mem0, Apache-2.0, roughly 61k stars, the most-starred
project in this space) is pipeline memory in its purest form: a layer that watches
conversations, extracts facts, and serves back a relevant slice per query.
How the paper's design works. The mem0 paper (Chhikara et al., 2025) describes the two-phase pipeline Chapter 9 taught. Extraction: an LLM reads the new exchange plus a running conversation summary and recent messages, and proposes candidate facts. Update: for each candidate, the top-10 semantically similar stored memories are fetched and a second LLM call picks one of four operations, verbatim from the paper: ADD (no equivalent exists), UPDATE (augment an existing memory), DELETE (the new fact contradicts a stored one), NOOP. That update phase is the invalidation step every append-only store lacks, and it is what made the design the reference pipeline. The paper's headline numbers on the LoCoMo benchmark: 26% relative improvement over OpenAI's built-in memory (LLM-as-judge score), 91% lower p95 latency and over 90% token savings versus stuffing the full conversation into context, with a graph variant (Mem0-g) about 2% better again.
The pivot. In April 2026 mem0 changed the default algorithm to single-pass, add-only
extraction: one LLM call, no UPDATE or DELETE; "memories accumulate; nothing is overwritten,"
with retrieval and reranking doing the work of surfacing the current fact. The old two-phase
behavior is gone from current versions (the version switch is ignored), and mem0 self-reports
large benchmark gains from the change (LoCoMo 92.5 versus the old algorithm's 71.4). Two
readings coexist: reranking may genuinely beat eager reconciliation, and add-only is certainly
cheaper and faster to write; but the burden of resolving contradictions has moved from write
time to read time, and Chapter 42 shows exactly the staleness
probe that tells you whether read-time resolution is working on your data.
The benchmark fight, and the lesson. Zep published a detailed critique alleging the mem0 paper misconfigured Zep in its comparisons (wrong message roles, timestamps embedded in text instead of the dedicated field, sequential searches inflating latency), reporting a corrected Zep score about 10% above mem0's best, and noting that in mem0's own paper a plain full-context baseline outscored mem0's pipeline. mem0 published counter-corrections of Zep's numbers in turn. The dispute is unresolved and probably unresolvable from the outside, which is the durable lesson: vendor benchmark tables are adversarial documents. The response is not to pick a side; it is Chapter 42's harness, run on your own sessions.
The window contract. Nothing resident. Per turn, search() returns the top-k facts and
your code injects them: a few hundred tokens, flat, regardless of how much is stored, which is
rung 3 of Chapter 40's ladder implemented as a service.
Applied to Claude Code. The route is OpenMemory, mem0's local-first MCP server: a
Docker Compose stack that runs on your machine (server on localhost:8765, dashboard on
:3000) and exposes exactly five tools to any MCP client: add_memories, search_memory,
list_memories, delete_memories, delete_all_memories. Registered with claude mcp add,
those tools appear in every session, and one CLAUDE.md line ("check search_memory before
asking me to restate preferences") turns it into cross-session, cross-client memory: the
same store serves Claude Code, Claude Desktop, and Cursor at once, which per-project
CLAUDE.md files cannot do. Note how much steering lives in the tool descriptions themselves:
OpenMemory's search_memory description says to call it for every user question, an
instruction the model reads every session; Chapter 28 explained
why that channel works.
The graph stores: Graphiti, cognee, and the MCP reference server
Graphiti (github.com/getzep/graphiti, Apache-2.0, roughly 29k stars) is the engine
Chapter 10 covered in depth: a bi-temporal knowledge
graph where facts are edges with validity intervals, superseded facts are closed rather than
deleted, raw inputs are preserved as episodes, and retrieval is a hybrid of embeddings, BM25,
and graph traversal. Its repo ships an MCP server, so the practical Claude Code wiring is the
same shape as OpenMemory's: mount it, and the agent gets "what was true in March" as a tool.
Choose it when the history of facts is itself the question.
cognee (github.com/topoteretes/cognee, Apache-2.0, roughly 28k stars) targets documents
more than conversations: ECL pipelines (Extract, Cognify, Load) that turn ingested files into
a knowledge graph plus embeddings, queried through both relationship and similarity lenses,
also mountable into Claude Code over MCP. Think of it as Chapter 31's
retrieval pipeline with a graph bolted on, sold as memory.
The MCP reference memory server (@modelcontextprotocol/server-memory, from the official
modelcontextprotocol/servers repo) is the smallest real system in the chapter and the best
one to study. It is a knowledge graph in a single JSONL file (memory.jsonl, path set by
MEMORY_FILE_PATH): entities (name, type, a list of atomic observation strings),
relations (directed, active voice), and nine tools that are the whole API:
create_entities, create_relations, add_observations, delete_entities,
delete_observations, delete_relations, read_graph, search_nodes, open_nodes. No
embeddings, no LLM in the loop: extraction is whatever the agent chooses to write, retrieval is
string search over names and observations. One claude mcp add memory -- npx -y @modelcontextprotocol/server-memory and Claude Code has durable, greppable, agent-curated
memory whose entire mechanism you can read in one file. For a personal repos ledger like
Chapter 40's, this is honestly hard to beat.
LangMem: the taxonomy
LangMem (github.com/langchain-ai/langmem, MIT, roughly 1.6k stars, a much smaller project
than the others here) matters less for its code than for its vocabulary, which the field has
broadly adopted. It classifies agent memory as semantic (facts and knowledge: preferences,
triplets; stored as a strict-schema profile or a searchable collection), episodic (past
experiences kept as learning examples: the situation, the reasoning, why it worked), and
procedural (how to behave: the system prompt and rules, evolved through feedback). It also
names the two formation modes: hot path (update memory during the conversation, paying
latency now) versus background (reflect after the conversation goes quiet), the same split
Letta's sleep-time agents implement.
Mechanically it is a thin, honest layer: create_manage_memory_tool() and
create_search_memory_tool() give any agent write and search tools over a LangGraph store
(JSON documents in namespace tuples like (user_id, "preferences"), with put, get, and
semantic search), plus a background manager that extracts and consolidates on a schedule.
The taxonomy maps straight onto this book: semantic memory is Chapter 9,
episodic is the transcript and Chapter 12's failure examples,
procedural is CLAUDE.md and Chapter 12's learned rules. When
a vendor pitches "memory," asking which of the three it stores, and which formation mode
it uses, is the fastest way to place it.
Anthropic's native pair: the memory tool and context editing
Anthropic ships the two halves of MemGPT's loop as separate API features, and the split is instructive: one feature is the durable store, the other is the eviction.
The memory tool (now generally available on the Messages API, no beta header) is declared
as {"type": "memory_20250818", "name": "memory"}. It is entirely client-side: Claude
issues file commands and your code executes them against storage you control, under a
/memories path prefix. The commands are view, create, str_replace, insert, delete,
and rename, and if the shape sounds familiar, it should: it is a text editor pointed at a
memory directory, the same design as Claude Code's auto memory and Serena's .serena/memories/,
generalized to any application. Declaring the tool auto-injects a system instruction that tells
Claude to check its memory directory before doing anything else and to "assume interruption"
(the context window might reset at any moment), which is the memory-pressure discipline from
the lab, imposed by prompt. Follow-along shape, output illustrative since the SDK is not
installed on this box:
# Follow-along: requires the anthropic SDK and an API key.
import anthropic
client = anthropic.Anthropic()
response = client.messages.create(
model="claude-opus-4-8",
max_tokens=1024,
tools=[{"type": "memory_20250818", "name": "memory"}],
messages=[{"role": "user", "content": "Pick up the migration where we left off."}],
)
# Claude's first move, before touching the task:
# tool_use: {"command": "view", "path": "/memories"}
# Your handler executes it against ./memory_store/ (never trust the path
# blindly: reject anything that escapes /memories) and returns the listing.
The security note is not optional: the docs warn explicitly about path traversal
(/memories/../../secrets.env), and Chapter 32 explains why a
memory an attacker can write into is an injection channel with persistence.
Context editing (beta, header context-management-2025-06-27) is the eviction half:
server-side strategies that prune the conversation as it grows. clear_tool_uses_20250919
clears old tool results past a trigger (default 100,000 input tokens), keeping the newest few
(default 3, tunable, with exclude_tools to protect specific tools); clear_thinking_20251015
prunes old thinking blocks. Newer still is server-side compaction (beta
compact-2026-01-12), which summarizes instead of clearing;
Chapter 42 covers it where it belongs, next to the eval that
tells you what a summary dropped. Pair the memory tool with context editing and you have the
lab's exact architecture at API scale: durable facts written out before old turns are cleared
away.
Choosing for Claude Code
The decision, compressed to the axes that matter:
| You want | Reach for | Window contract | Wiring |
|---|---|---|---|
| Zero setup, per-project facts | Auto memory (Ch 18) | index resident, topics on demand | on by default |
| Rules and conventions | CLAUDE.md hierarchy | resident, in full | you write it |
| Code knowledge | Serena / LSP (Ch 38) | retrieved slices | MCP |
| Cross-client personal memory | OpenMemory (mem0) | top-k slice per query | MCP, Docker |
| Inspectable, tiny, greppable | @modelcontextprotocol/server-memory | retrieved nodes | one claude mcp add |
| Fact history over time | Graphiti (Ch 10) | retrieved subgraph | MCP, graph DB |
| Memory inside your own app | Anthropic memory tool | files the model views | SDK, your handler |
| Agents as a managed service | Letta | blocks resident + search | separate platform |
Remember. Judge every system by its window contract, because that is the column you pay for on every turn. Resident memory (blocks,
CLAUDE.md, an index) buys "always known" at a fixed rent; retrieved memory buys "findable" at per-query cost; and the systems that win in practice keep the resident part small and push everything else behind search, which is exactly what the 78-versus-292-token quiz in the lab measured.
Further reading
- Packer et al., "MemGPT: Towards LLMs as Operating Systems" (2023, arXiv 2310.08560): the paper behind the lab; the memory-pressure and recursive-summary designs are sections worth reading in full.
- Chhikara et al., "Mem0: Building Production-Ready AI Agents with Scalable Long-Term Memory" (2025, arXiv 2504.19413): the two-phase pipeline, and the numbers the benchmark fight is about; read alongside Zep's critique ("Is Mem0 Really SOTA in Agent Memory?", blog.getzep.com) as a case study in adversarial benchmarking.
- Lin et al., "Sleep-time Compute: Beyond Inference Scaling at Test-time" (2025, arXiv 2504.13171): memory maintenance off the hot path, measured.
- Anthropic docs: the memory tool (
platform.claude.com/docs/en/agents-and-tools/tool-use/memory-tool) and context editing (.../build-with-claude/context-editing); the memory tool page includes the full command reference and the path-traversal warning. - Chapter 9, Chapter 10, Chapter 18, Chapter 38: the foundations this tour builds on.
Takeaways
- Three questions sort every memory system: what is extracted, where it lives, what lands in the window per turn. Only the third has a per-turn bill, and it splits the field into pipeline memory (code decides) and agentic memory (the model decides, via tools).
- The MemGPT loop (append, replace on contradiction, evict under pressure, retrieve on demand)
reproduces in 150 lines and answers a fresh-window quiz 4/4 at 78 tokens versus 292 for
replay; Letta is that loop productized, with blocks, sleep-time maintenance, and
.afcheckpoints. - mem0's paper design added the update-or-delete reconciliation step; its April 2026 pivot to add-only moved contradiction handling from write time to read time, and the mem0-versus-Zep benchmark dispute is the standing argument for running your own eval.
- The MCP reference memory server (nine tools, one JSONL file) is the most inspectable real memory system available to Claude Code, and often enough; OpenMemory adds cross-client reach; Graphiti adds time.
- Anthropic's memory tool is a client-side text editor over a
/memoriesdirectory (the same file-based design as auto memory and Serena), and context editing is the matching eviction; together they are the lab's architecture at API scale.
👉 Every system in this tour ships a benchmark table, and two of them are publicly at war over
one. The next chapter builds the thing that settles it for you: an eval harness for memory
and context, before and after a change, across sessions, across compactions, and across model
swaps, with a from-scratch lab and the exact claude -p recipes. Continue to
The memory eval harness.