跳到主要内容

The Third-Party Memory Layer: How claude-mem Actually Works

高级
What you'll learn
  • Understand what 'memory' means in this class of tool: not a saved transcript, but observations extracted by a second model that watches your tool calls
  • Learn the six-hook lifecycle and the exit-code contract that decides whether a broken memory layer degrades quietly or blocks your coding session
  • See the detail almost nobody checks: the observer is a separate model on a provider you choose, and claude-mem's own cost dataset maps its hosted observer to a DeepSeek model — your session content does not necessarily go to Claude
  • Follow the three-layer recall that makes the token arithmetic work — a ~50-100-token index hit before a ~500-1,000-token detail fetch — and why that ordering is the whole design
  • Know where the data lands: SQLite with FTS5, a Chroma vector store that indexes every extracted fact separately, and a worker on port 37700 + (uid % 100)
  • Work out who pays for the compression, and why a $30/month hosted tier exists at all
  • Avoid five documented failure modes, starting with the one where the observer's reasoning tokens ate the session's output budget
  • Apply an eight-point evaluation checklist to any agent memory layer before you let it read every file your agent touches

Every coding-agent user hits the same wall. You spend an hour establishing context — which module owns what, why the previous approach failed, the one gotcha in the build — and then the session ends and all of it is gone. Tomorrow you explain it again.

claude-mem is the most popular answer to that problem by a very wide margin: 103,252 stars on GitHub as of 26 October 2026, Apache-2.0, and shipping fast enough that it went from v13.38 to v15.2.1 in the twelve days before this page was written. It works with Claude Code, Cursor, Codex, OpenCode, Antigravity, Claude Desktop and several others.

This page is not an install guide — the README does that well. It is the thing the README does not do: explain what the tool actually is architecturally, what it costs, what reads your code, and what breaks. Most of what follows comes from the repository's own docs/ folder, which is detailed, honest, and almost certainly unread by the large majority of those 103,000 stargazers.

What it stores is not your transcript​

The first mental model to discard is "it saves my chats". It does not. The unit of storage is an observation: a structured note with a type, a title and a narrative, extracted by a model from what your agent did.

The flow, from the repo's own data-flow diagram:

  1. You submit a prompt. The session-init handler registers the session and asks the worker for semantically relevant context.
  2. Your agent uses a tool. The PostToolUse hook posts that tool usage to the worker, which enqueues it.
  3. The worker's SDKAgent — a model session, running in a background daemon — reads the queue and produces observations and summaries.
  4. Those land in SQLite, get synced into a Chroma vector store, and get broadcast to a local UI over server-sent events.

So the thing watching your work is a second model, and the thing it reads is every tool call: file reads, file writes, command output, search results. That is the price of admission for this entire category of tool, and it is worth saying plainly before any of the rest.

What you'll learn
  • A memory layer is not a storage feature. It is a second model with a standing subscription to everything your first model touches. Evaluate it the way you would evaluate adding any model to your pipeline — what it reads, where it sends it, who pays for its tokens, and what happens when it fails.

The six hooks, and the one contract that matters operationally​

claude-mem installs as a set of lifecycle hooks, each invoked as a single shell-free command of the form claude-mem hook claude-code <event>. Per docs/architecture-overview.md:

EventHandlerWhat it doesTimeout
Setupversion-check.jsSub-100 ms version-marker check; prints a hint to run npx claude-mem repair on mismatch60s
SessionStartworker start + contextStarts the worker service and injects prior context60s
UserPromptSubmitsession-initRegisters the session, starts the SDK agent, semantic injection60s
PostToolUseobservationCaptures tool usage and enqueues it in the worker120s
SummarysummarizeRequests a session summary from the SDK agent120s
SessionEndsession-completeEnds the session and drains pending messages30s

Now the part that will matter to you at 2 a.m. The hook orchestrator implements an explicit degradation contract:

  • Transport errors — ECONNREFUSED, timeouts, 5xx from the worker — exit 0. Non-blocking. The worker being down never blocks your coding session.
  • Client bugs — 4xx, TypeError, ReferenceError — exit 2. Blocking, on the stated reasoning that these indicate a bug needing a fix.

That is a defensible design, and it has a consequence worth internalising: "the memory daemon is dead" is harmless, but "the memory daemon is answering 400" is not. A malformed request, a schema drift after a partial upgrade, or an expired key that surfaces as a 4xx is in the category the tool deliberately chose to make loud. This is also why the Setup hook nags about npx claude-mem repair after an external plugin upgrade — a version-skewed install is exactly how you reach the blocking branch.

Watch out
  • If a coding session starts failing immediately after you updated a plugin, check your memory layer before you debug your agent. The published contract here is that transport failures degrade silently and client-side failures block — so the symptom of a half-upgraded install is a session that will not start, not a session without memory.

The three-layer recall is the actual idea​

Any memory system can store things. The hard part is retrieval that does not cost more context than it saves — dump twenty remembered notes into every session start and you have made the problem worse.

claude-mem's answer, shipped as the default in v15.0.0 on 20 October 2026 under the name Smart memory search, is a three-step search the agent reasons through rather than a single retrieval:

Guided walkthrough1 of 3
  1. A full-text query (SQLite FTS5, optionally hybridised with Chroma vectors) returns a compact list of hits with observation IDs. The README puts these at roughly 50-100 tokens per result — enough to judge relevance, not enough to be useful on its own.

The three steps are exposed to the agent as MCP tools — search(query, type, date, project, limit), timeline(observation_id, query) and get_observations(ids[]) — which is why the agent can reason between them instead of the retrieval being a fixed pipeline.

The transferable lesson here has nothing to do with claude-mem. Filter on cheap representations, then pay for detail on the survivors. It is the same pattern as reading a search-results page before opening a tab, the same pattern as a database index before a row fetch, and it is the correct shape for any retrieval you bolt onto an agent. If you are designing your own, see context engineering for the general framing and how agent memory actually works for the taxonomy this tool sits inside.

Where your data actually lives​

Everything local lands under ~/.claude-mem/. Two stores, with distinct jobs:

SQLite (claude-mem.db), with FTS5 for full-text search:

TablePurpose
sdk_sessionsSession lifecycle, and the mapping between the two session IDs
observationsThe extracted notes: type, title, narrative, content hash
session_summariesPer-session request / learned / completed
user_promptsYour prompt history
pending_messagesThe per-session work queue
observation_feedbackUsage signals per observation

ChromaDB (chroma.sqlite3) for vector embeddings, accessed through chroma-mcp over stdio. The detail worth noticing: each observation produces several documents, not one — obs_{id}_narrative for the main text plus obs_{id}_fact_0, obs_{id}_fact_1 and so on for each extracted fact. Facts are embedded individually, which is why a query about one specific detail can retrieve an observation whose overall summary is about something else.

The worker is an Express daemon on a per-user port: 37700 + (uid % 100). If you have seen localhost:37777 in the project's own release notes and docs, that is one uid's port, not a constant — which is a small thing that will cost you ten minutes exactly once.

What you'll learn
  • The worker binds a local HTTP API and serves a UI from it. docs/security.md is explicit that the server beta defaults to API-key auth, that keys use a cmem_ prefix and are shown once with only a SHA-256 hash stored, and that the loopback development bypass requires two environment variables set together (CLAUDE_MEM_AUTH_MODE=local-dev and CLAUDE_MEM_ALLOW_LOCAL_DEV_BYPASS=1) and must not be used behind a reverse proxy or on a publicly reachable bind address. Treat that as a hard rule: this daemon holds a searchable index of everything your agents read.

Who pays for the compression — and which model reads your code​

Here is the fact that most surprised me, and the reason this page exists.

The observer is a separate model call, and you choose its provider at install time: npx claude-mem install --provider [host|openrouter|gemini|anthropic]. Four options, four different answers to "where does my session content go".

The repository's own cost-accounting dataset, in docs/expense-pricing/README.md, documents how to price observer traffic — and in doing so documents what the hosted observer is. Its join recipe says to resolve the model id cmem-observer to deepseek/deepseek-v4-flash on or after 2026-08-08, and to deepseek/deepseek-chat before that.

There is an apparent contradiction here worth resolving, because you will hit it if you read the repo yourself: docs/architecture-overview.md labels the observer component SDKAgent (Claude Agent SDK), which sounds like it must be Claude. Both things are true at once. The Agent SDK is the harness — the loop that manages the session and parses responses — and it can be pointed at a non-Anthropic endpoint, which is exactly what the v15.0.2 release notes imply when they describe observer requests going to "OpenRouter, the cmem.ai gateway, and OpenAI-compatible endpoints". The SDK is the client; the provider flag decides who answers.

Read the DeepSeek mapping carefully too, because it is easy to over- or under-state. It does not mean the project is hiding anything — the provider is a documented install flag and the dataset is published in the open repo. It does mean that a tool named claude-mem, on its hosted path, has been pricing its observer as a DeepSeek model, and that if your assumption was "this runs on Claude because of the name", the assumption was wrong. If your code cannot go to a third-party inference provider, --provider anthropic or a self-hosted OpenAI-compatible endpoint is the choice you have to make deliberately.

The same dataset also tells you the shape of that traffic: the project's own notes treat observer usage as roughly 98% input tokens, which is exactly what you would expect from a process whose job is reading tool output and emitting short notes. Prompt-heavy, output-light.

And that is the whole economics of the hosted tier. The paid product, CMEM Pro, is described on cmem.ai as hosted observation and cloud sync at $30/month after a 30-day free trial, with the pitch that hosted observation moves memory generation off your primary agent's subscription. In other words: by default, something has to pay for a second model to read everything your first model does. Either that is your own provider account and plan limits, or it is $30/month to someone else. The site is also careful to say the effect on token use depends on your workload and provider, and that Pro does not change the price or limits of another subscription.

What you'll learn
  • Any memory layer has a hidden second bill: the cost of the model that writes the memories. Before you install one, answer three questions. Which model does the extraction? Whose account pays for it? And is the content it reads allowed to go there? For claude-mem the answers are configurable, documented, and not what the name implies by default.

The licensing split is also published, in docs/ip-boundary.md, and it is a clean open-core boundary worth knowing if you are planning to build on it: Apache-2.0 covers the memory engine, server, CLI, SDKs, REST schemas, MCP tools, the Claude Code and generic agent adapters and the storage adapters. Reserved commercial areas include hosted Magic Recall, team/org memory sync, the admin dashboard, SSO/SAML/SCIM, enterprise RBAC and audit UI, the DLP/policy engine, managed evals and private connectors. So the engine is genuinely yours to embed; the team and enterprise surface is not.

Five documented failure modes​

These are all from the project's own release notes and architecture docs, which is the best possible source for a gotcha list.

1. The observer's reasoning tokens ate the session's output budget. Fixed in v15.0.2 (23 October 2026): observer requests to OpenRouter, the cmem.ai gateway and OpenAI-compatible endpoints now disable reasoning by default, because reasoning tokens were filling the output budget and leaving a session with nothing left to emit. This is the single most instructive bug in the list — it is a pure consequence of putting a reasoning model in a budget-constrained extraction role, and it will recur in anything you build that does the same.

2. Two session IDs that are not interchangeable. contentSessionId comes from the host and is invariant for the session; memorySessionId comes from the SDK agent and changes on every worker restart. The architecture doc calls the conversion between them critical for foreign-key constraints. If you query the SQLite store directly, this is the first thing that will confuse you.

3. Deduplication has a 30-second window. The content hash is SHA256(memory_session_id + title + narrative) truncated to 16 hex characters, and a repeat within 30 seconds returns the existing ID instead of inserting. Useful, and also a reason identical-looking observations can legitimately coexist if they arrived 31 seconds apart.

4. Generator crashes retry three times, then stop. Backoff is 1s, 2s, 4s; after more than three consecutive restarts the worker stops retrying and lets the iterator end. The counter resets when the generator completes work naturally. Pending messages survive restarts and are cleared by the parser path on the next valid response — so a persistently crashing observer means a silently growing queue, not lost input.

5. The install footprint is larger than npm install. npx claude-mem install sets up Bun and uv globally, runs bun install in the plugin cache, and writes a version marker. The README is explicit that npm install -g installs the SDK only and that you must use npx claude-mem install. Two package-manager runtimes installed globally by a memory tool is a reasonable engineering choice and also a supply-chain surface you should know you accepted — vetting agent skills is the general discipline.

Watch out
  • Release velocity cuts both ways. Between 13 and 25 October 2026 this project shipped v13.38 through v15.2.1 — two major version bumps in twelve days, with features renamed across them (the viewer, the recall mode, the work views). A tool this active fixes bugs fast and also breaks your notes fast. Pin a version if you are documenting it for a team, and read the release notes before upgrading rather than after.

What leaves your machine, and the one control to know​

docs/security.md and the README together give a usable picture:

  • The local engine stores memory on your machine, under ~/.claude-mem/.
  • Cloud sync is opt-in; if you enable it, synced data is also stored by CMEM. The site's own guidance is to read the privacy policy before connecting.
  • CLAUDE_MEM_ONLINE_OPTIN=false skips the sign-in flow.
  • Content wrapped in <private> tags is excluded from storage entirely — not written to the observation store, and not written to profile or project memory.

That last one is the control most worth remembering, because it is the only in-band one. Everything else is a configuration decision you make once at install; <private> is the thing you can reach for mid-session when a secret, a customer name or a production hostname is about to pass through a tool call.

It is also, by construction, a control you have to remember to use. For the broader question of what an agent ships off your machine in the normal course of doing its job, see what your agent uploads.

Inspect what a memory layer has actually recorded about you

# Where the local stores live, and how big they have got
du -sh ~/.claude-mem/ 2>/dev/null
ls -la ~/.claude-mem/

# Which worker port is yours (the formula, not the number in the docs)
python3 -c "import os; print(37700 + (os.getuid() % 100))"

# Read the observation store directly — it is plain SQLite
sqlite3 ~/.claude-mem/claude-mem.db ".tables"
sqlite3 ~/.claude-mem/claude-mem.db \
"SELECT type, title, substr(narrative,1,90) FROM observations
 ORDER BY rowid DESC LIMIT 15;"

# Which model actually wrote your memories (the field the cost docs join on)
sqlite3 ~/.claude-mem/claude-mem.db \
"SELECT generated_by_model, COUNT(*) FROM observations
 GROUP BY 1 ORDER BY 2 DESC;"

# And your own prompt history, which is stored separately
sqlite3 ~/.claude-mem/claude-mem.db \
"SELECT COUNT(*) FROM user_prompts;"
What you'll learn
  • Column names come from the schema table in docs/architecture-overview.md and the join recipe in docs/expense-pricing/README.md, which names generated_by_model and created_at on observation rows. A project shipping two majors in twelve days may rename them — if a query errors, run .schema observations and adjust. The point of the exercise is not the exact SQL; it is that you can and should read what a memory layer wrote about you, because it is a local SQLite file and nothing stops you.

Evaluating any memory layer, not just this one​

This category is going to keep growing, and the questions generalise. Run these before you install the next one.

Guided walkthrough1 of 8
  1. A raw transcript, a model-extracted note, or a compiled knowledge artefact? Extraction means a second model's judgement sits between what happened and what you remember — which is usually what you want, and is also a place for errors to become permanent.
Key takeaways
  • claude-mem is not a transcript store. It is a second model in a background daemon that reads every tool call and writes structured observations — which is the real cost and the real capability of this entire tool class.
  • The observer is a separate model on a provider you select at install time (host, OpenRouter, Gemini, or Anthropic). The project's own cost dataset maps its hosted cmem-observer id to deepseek/deepseek-v4-flash from 2026-08-08, so the name does not tell you where your code goes.
  • Someone has to pay for the extraction. By default that is your own provider account; CMEM Pro's $30/month pitch is explicitly that hosted observation moves memory generation off your primary agent's subscription.
  • Recall is three steps, not one: a ~50-100-token index hit, then timeline context, then a ~500-1,000-token detail fetch for selected IDs only. 'Filter cheap, fetch expensive' is the transferable pattern and the reason the token math works at all.
  • The degradation contract is published and asymmetric: transport failures exit 0 and never block your session, client-side failures exit 2 and do. A half-upgraded install is therefore a session that will not start.
  • Observer traffic is roughly 98% input tokens by the project's own accounting — a reading process, not a writing one. Price it that way.
  • Each observation becomes multiple Chroma documents, one per extracted fact plus the narrative, which is why a query about a small detail can surface a note that is nominally about something else.
  • The v15.0.2 fix is the most instructive bug in the project: a reasoning model in an extraction role spent the output budget on reasoning and left sessions with nothing to emit. Disable reasoning on any model you use as a summariser.
  • Two package-manager runtimes (Bun and uv) get installed globally, a daemon binds 37700 + (uid % 100), and an MCP server joins your agent. All defensible, all worth counting as surface area before you accept them.
  • The one in-band control is the <private> tag, which excludes content from storage entirely. Everything else is decided at install time.
Twelve terms from the memory-layer stack
按 Enter 或空格键翻转卡片。使用左右方向键在卡片之间切换。已显示术语。
1 / 12

Check yourself

0/6
  1. Your coding session starts failing to launch immediately after a plugin update. Given claude-mem's published degradation contract, what is the most likely relationship?
  2. Which statement about the model that writes claude-mem's observations is accurate?
  3. Why does the three-step recall save tokens rather than spend them?
  4. The v15.0.2 release disabled reasoning by default for observer requests. What was the failure it fixed, and what is the general lesson?
  5. You need to stop one specific thing — a production hostname — from being recorded, mid-session, without reconfiguring anything. What does claude-mem give you?
  6. The docs say the worker listens on port 37700 + (uid % 100), while release notes mention localhost:37777. What should you conclude?

Sources & further reading​

Next​

  • How agent memory actually works — the taxonomy this tool sits inside: what counts as memory, which layer each approach operates on, and why most of them fail at retrieval rather than storage
  • Hindsight: agent memory that learns — the other end of the design space, where memory is updated by outcome rather than extracted from transcripts
  • CLAUDE.md & memory files — the first-party, hand-written baseline every one of these tools is implicitly competing with, and the one you should get right before adding a daemon
  • What your agent uploads — the broader version of the question this page asks about the observer: what leaves your machine while an agent does ordinary work