What happens while you're not looking
Dreaming
Every day you work, the ship's log gets longer. Dreaming is the pass that reads what piled up, folds what belongs together into one theme, and leaves the originals archived underneath. It runs on your machine — and it folds memories from every assistant you use, not one at a time.
Shipping · consolidation runs on its own · latest release ·dream --dry-run stays a read-only explorer
What ships today
Four passes, all on by default, all reversible, none deleting.
Two of the passes run hourly. Dedup folds near-duplicate observations where title similarity
and an embedding confirm both hold. Supersede demotes an older version-fact by 0.5x when a newer
one exists, a demotion you can undo rather than an archive. The other two are idle-time passes,
and captain-memo stats lists all four under Housekeeping.
The idle-time passes run while your machine is idle: no work queued, no assistant mid-session,
and nothing written or recalled for thirty minutes. A busy week simply defers it. A pass
that found nothing to change is skipped until an observation is written or surfaced or a
vector is written; captain-memo consolidate always runs one, and a restart
runs each pass once.
Folding collapses restatements written inside a single session — one event the summarizer described twice, in different words. The survivor keeps the counts; the restatement is archived into it. Folding calls no model.
Every fold passes a guard on the titles first. It vetoes a pair when exactly one title states an
absence or a failure, when both name dotted versions and not the same ones (v1.30 and
1.30 are the same, v1.30 and v1.32 are not), when both carry
identifiers such as paths or letters-and-digits codes and share none, or when each states a number
the other lacks. A clock time such as 14:56 is not read as a number, a date is, and a
rewording that keeps the same figures still folds. The guard is a heuristic on titles alone: two codes
such as p95 and p99 still fold when the titles share another identifier. The
worker picks up a changed guard at its next restart.
Themes handle the other case: the same standing fact learned again in a different session, weeks later. Folding those would erase the evidence that the knowledge failed to stick, so instead one written theme states the durable fact and the originals are archived beneath it, listed and restorable. Writing the theme is the one step that calls a model: for each candidate group (a bounded number per pass) the pass makes one call to the summarizer you chose, carrying the dates, types and titles of that group's observations, and the summarizer may decline. With no summarizer configured, no themes are written.
A theme only forms where both signals agree: the observations read alike, and you have actually pulled them up together. Similarity alone would group everything that shares a vocabulary, which on a real corpus is most of a project. The recall evidence is what separates one topic from one word-list — and it comes from the audit log your own searches have been writing all along.
Everything stays inside one project and branch, nothing you drilled into or anchored is
ever touched, and nothing is deleted. captain-memo theme undo <id> puts a
theme's members back, and captain-memo dedup --undo reverses every fold. When a sweep
got a few wrong, name them: --member ID..., --run N (an id from the
read-only captain-memo dedup --runs), or --since with an optional
--job dedup|semantic; selectors combine. A selector prints what it will reverse and
then does it, with no prompt, and repeating one is a no-op. Needs Captain Memo 0.57.9 or later.
An undone fold stays undone. Every undo, plain --undo included, marks the pair
it reversed, and no later pass folds that pair again until
captain-memo dedup --allow-refold lifts the mark
(captain-memo dedup --refold-blocks lists the marks, read-only). A pair is marked, not
a row: a member that is also a near-duplicate of a third row can still fold. Needs Captain Memo
0.57.10 or later; --refold-blocks needs 0.57.11 or later.
The back catalogue. Consolidation normally works on what actually reaches you — an
observation that has never once been recalled is not crowding your context, so it is left
where it is. On a corpus with years behind it that turns out to be most of the collection,
and the repetition in there is just as real. --backlog is a one-off sweep that
drops the gate and takes the whole corpus in one pass; afterwards new repetition only
accrues as fast as you make it:
captain-memo consolidate --semantic --backlog --for 30m, and the same with
--themes.
The problem
A log that only grows stops being a log.
Memory systems are graded on what they remember. They should be graded on what they can still find.
Month one
You fix a webhook bug. Three observations get written. You search "webhook" and get exactly those three. The system looks perfect.
Month nine
You've fixed webhooks eleven times. You search "webhook" and get forty rows — eight of which are the same conclusion, written eight different ways, by four different assistants, none aware of the others. The answer is in there. So is everything else.
The obvious answer — expire old memories, cap the corpus, drop the least-used rows — throws away the thing you kept them for. The observation you touch once a year is often the most expensive one to re-derive. Dreaming does the opposite: it makes a summary the thing search hits first, and moves the detail out of the way without destroying it.
The originals do not vanish when a theme forms. They are pressed into the record underneath it — dimmed, out of the way of search, and still there when you need the detail back.
The mechanic
Fold the group, archive the parts.
One pass, four steps: read the co-retrieval evidence, find which observations keep getting recalled together, collapse each group into a theme, archive the members inside it.
--min-pts, deliberately left alone.
The signal
Grouped by what you recall together — not by what shares words.
This is the part that makes dreaming worth doing, and it is the part most systems get wrong. Cluster on text similarity and you get groups that use the same vocabulary. That is not the same as groups that are about the same thing.
Worth being precise about, because it sounds like an omission and is actually the point: the dream --dry-run explorer scores pairs on co-retrieval and recency only. The explorer leaves the semantic layer out, so what it shows you is the co-retrieval signal on its own. A pass that never looks at the words cannot produce a group that merely shares vocabulary. The shipped consolidation pass adds a cosine confirm on top, so a theme forms only where the observations read alike and you have pulled them up together.
Worked cases
Three groups a similarity search would not have made.
Illustrative examples, in the shape the dry-run reports them: the members that came in, the theme that would come out, and why the grouping happened.
CASE 01 The same bug in four vocabularies
Over nine days you hit one root cause — a payment webhook rejecting live traffic — from four directions, in three different tools. No two observations share a distinctive word. Every time you searched, they came back together.
- webhook returns 400 on live keys only
- signature check reads the parsed body
- express.json() runs before the route
- test mode passes because secret differs
Webhook signature verification must run on the raw request body — mount the raw parser before the JSON one, or live keys fail while test keys pass.
Why it grouped: co-retrieval 0.60 (surfaced together 9 of 15 combined surfacings), created within 3 days. Word overlap between members: essentially zero. A similarity clusterer had no way to see this group.
CASE 02 Two assistants, one conclusion
You debugged a deploy problem in Claude Code on Monday and re-hit it in Codex on Thursday, in a different editor, with no shared session. Both wrote observations. Neither knew the other existed — but your searches kept pulling both.
- CSS deployed but browser shows old file
- @import version pin inside components file
- curl returns new, browser returns stale
A CSS deploy needs three version bumps, not one — outer link tag, inner @import, and the global deploy version. curl hitting the inner URL proves nothing about what the browser fetched.
Why it matters: the theme is built from evidence no single assistant had. Codex contributed the middle observation; the retrievals that bound it to the other two came from Claude Code sessions. Neither tool could have written this theme alone.
CASE 03 The group that was correctly left alone
Three observations from the same afternoon, all about the same file, all sharing vocabulary. A similarity clusterer folds them instantly. Dreaming does not — and it is right not to.
- invoice total rounds at line 412
- invoice PDF margin is 14mm
- invoice list query needs an index
Reported as noise. Three facts that share a filename and a Tuesday, and nothing else. Folding them would produce a summary no one could ever use.
Why it didn't group: they were never recalled together. Same file, same day, same words — but in nine months of retrievals, no query ever wanted two of them at once. That absence is a signal, and only a co-retrieval pass can read it.
Under the hood
Two numbers, one threshold.
No magic, and nothing you can't check by hand. Every pair of observations gets a distance; DBSCAN groups anything closer than --eps.
Co-retrieval similarity
How often two observations surface together, normalised against how often each surfaces at all — so a popular row can't drag everything into its orbit.
Surfaced 100 times each, never together → 0. Surfaced 5 times each, always together → 1.
Temporal similarity
Smooth exponential decay on the gap between when the two were written. Default τ is 7 days.
Same day → ≈1. A week apart → 0.37. A month apart → 0.01.
Worked pair — clusters.
surfaced 12 and 12 times, together 9 → coRet = 9 / (12+12−9) = 0.600created 2 days apart → temp = e^(−2/7) = 0.751
similarity = 0.625·0.600 + 0.375·0.751 = 0.657 → distance = 0.343
0.343 < eps 0.35 → same cluster ✓
Same two observations, never co-retrieved.
coRet = 0, temp unchanged at 0.751similarity = 0.625·0 + 0.375·0.751 = 0.282 → distance = 0.718
0.718 > eps 0.35 → left alone ✗
Written on the same afternoon, about the same file — and still not grouped. Proximity in time is not enough on its own. Co-retrieval is doing the work.
Tunables
--eps 0.35 distance threshold · --min-pts 3 smallest group · --tau-days 7 decay constant · --since 14d look-back window. Loosen with --eps 0.45 --min-pts 2 if a run reports no clusters.
Raise τ when you widen the window
The two interact, and not obviously. At τ=7d and eps=0.35, even a perfect co-retrieval score of 1.0 can only bridge a gap of about 19 days — so a --since 30d run cannot cluster across its own window edges however strong the evidence, unless you raise --tau-days to match. The dry-run prints that ceiling on every run rather than leaving you to work out why the extra fortnight produced nothing.
The pooled corpus
One dream, every assistant.
Twelve assistants write into one local corpus through one MCP server. The dream pass runs over all of it at once — so a theme can be built from evidence that no single assistant ever held.
origin_agent tag so you can see who wrote what — the dream pass deliberately ignores it.The retrievals pool too
The co-retrieval log records what surfaced together, not who asked. So when Cursor pulls two observations in one search, that pair strengthens — and the theme it eventually produces is one Claude Code will read tomorrow. The assistants shape each other's memory without ever talking to each other.
Cross-vendor is the default, not a mode
There is no flag to scope a dream to one assistant, because the corpus was never split by assistant in the first place. The only partition is --project. That falls out of the architecture rather than being bolted onto it.
Pooled on one captain, shared between captains
Pooled means the AI tools on one captain, one machine. Dreaming never folds another captain's memories into yours. With Captain Fleet, captains share what they know but never merge it, the way colleagues ask each other without copying each other's memory. When your captain recalls something ALPHA wrote, the result is tagged with ALPHA and the time, and the answer names its source: "As per ALPHA, the apples are 11", not "the apples are 11". A newer note from ALPHA only says when it was written, not that it is true, and it never overrides what your captain saw itself. ALPHA's apples may be different apples.
Where the name comes from
We took the name from Anthropic. And moved the idea onto your machine.
Credit where it is due: Dreams shipped in Claude Managed Agents in May 2026 as a research preview — the first time the word named an actual API resource, though Letta and Supermemory use "dreaming" for their own background passes too, so the metaphor is by now a crowded one. A dream reads an agent's memory store alongside its past session transcripts and produces a new, reorganised store — duplicates merged, stale or contradicted entries replaced, fresh insights surfaced — leaving the input untouched. Their framing is the clean one: memory captures what an agent learns as it works; dreaming refines it between sessions.
| Anthropic Dreams | Captain Memo Dreaming | |
|---|---|---|
| Runs | Anthropic's cloud, via Managed Agents | Your machine. Reads local SQLite files directly |
| Reads | One memory store + 1–100 of that agent's session transcripts | Your whole corpus + the local co-retrieval log |
| Scope | One agent at a time | Every assistant that writes to your local corpus, pooled |
| Grouping signal | A model re-reads the transcripts and synthesises | DBSCAN over co-retrieval + recency. The text is never re-read |
| Output | A new memory store; keep it or discard it | Themes in place, members archived, not deleted |
| Cost | API tokens per dream, scaling with transcript volume | No dream service to pay for. Grouping is local arithmetic; the only model call decides whether a group is one fact and names its theme, and goes to the summarizer you chose |
| Status | Research preview — request access | Shipping — runs by itself, reversible; dream --dry-run is the read-only explorer |
The two are not competitors so much as the same idea at different altitudes. Anthropic's version has the enormous advantage of a model actually reading your transcripts, and it will surface insights a distance metric never could. Ours has the advantage of seeing every tool you use, with no dream service to pay for, and grouping on your own disk without sending your corpus anywhere. If you use one assistant and live in the cloud, theirs fits. If you use six and want the corpus on your own disk, that is the gap this fills.
Where this actually sits
Local, cross-assistant consolidation is not rare. The signal is.
It would be easy to claim more here than is true, so here is the honest version. Several projects already run a consolidation pass locally over a corpus shared by many assistants — mcp-memory-service, SuperLocalMemory, shodh-memory, and ByteRover V4 (which also calls its pass "Dream"). That combination is not the differentiator.
What we have not found in any shipped system is a pass that decides what belongs together from which memories actually surface together in real use. Every one we could read the source or docs of groups by re-reading the text — with a model, with embeddings, or with token overlap. (Closed products publish no mechanism, so treat this as "we looked hard and found none", not a proof.)
And the technique itself is old — it is only new here. Grouping things by how often they are fetched together is standard practice elsewhere: co-citation analysis in bibliometrics since 1973, item-to-item collaborative filtering since Amazon's 2003 paper, co-click clustering over search logs since 2000. None of that is our invention. Applying it to an agent's memory is the part nobody had shipped.
| System | Runs | Pooled across assistants | What decides the grouping |
|---|---|---|---|
| Anthropic Dreams | cloud | no — one agent | a model re-reads the transcripts |
| mcp-memory-service | local | yes | DBSCAN / agglomerative on embeddings |
| SuperLocalMemory | local | yes | entity overlap + temporal proximity |
| ByteRover V4 "Dream" | local | yes | Jaccard token overlap of title + body |
| shodh-memory | local | yes | Jaccard over stemmed tokens |
| MemoryOS | local | partial | write-time content similarity |
| Mem0 · Zep · Cognee · CORE | mixed | partial | an LLM reconciles extracted facts |
| HeLa-Mem (paper) | partial | no — one conversation | co-retrieval, as a Hebbian graph |
| Captain Memo | local | yes | co-retrieval + recency |
The closest anyone gets
Two systems come genuinely close, and both deserve naming. SuperLocalMemory already records pairwise co-retrieval on every recall — the telemetry is there — but its consolidation pass never reads it, clustering on entity overlap instead. And HeLa-Mem, a 2026 paper, builds exactly this: edges that strengthen only when two memories appear in the same retrieval result. It is single-conversation and research-stage, not a cross-assistant tool. The idea is not ours alone; the shipped implementation is.
The signal is collected everywhere. It's just thrown away.
This is the genuinely odd part. Three of the systems above already record co-retrieval on every recall — SuperLocalMemory increments pairwise edges, shodh-memory has a CoRetrieved relation type, mcp-memory-service persists the result set of each query. Then none of them lets it decide a grouping. In one case the consumer is dead code that fails on a missing column; in another it is deliberately restricted to strengthening edges that content similarity already created. The data is sitting there, unused, in three separate projects.
What it reads today
Three files, no network.
The Dream block in captain-memo stats shows the inputs the pass would read — not its output. If the audit log is off, dreaming has no co-retrieval signal to work with and falls back to recency alone.
observations.db
The corpus. Non-archived rows inside the look-back window, optionally filtered to one project.
recall-audit.jsonl
One line per retrieval, listing what surfaced together. The co-retrieval signal is built from this. On by default; CAPTAIN_MEMO_RECALL_AUDIT=0 switches it off. Bounded at 32 MB, one rotation kept, and it never leaves the machine.
meta.sqlite3
The index that joins audit entries back to observation ids, so a surfacing event can be attributed to a row.
--dry-run is required for this command. Consolidation itself ships and runs by itself, but dream stays a read-only explorer: it is how you look at the clustering signal without waiting for a pass. It never contacts the worker, never writes to the database, and never calls the summarizer, so it is safe to run at any time, including while the worker is busy.
That is a deliberate order of operations: a pass that rewrites your memory should be one you have read the output of, several times, before it is allowed to touch anything.
DREAM — DRY RUN ───────────────────────────────────────── cluster preview — no writes, no Haiku Observations 10103 Without co-recall 9445 (93.5% of input — high % means audit log is thin) eps / minPts / τ 0.35 / 3 / 7d Signal weights semantic=0.000 temporal=0.375 coRet=0.625 Max pair age gap 19.0d (pairs further apart cannot cluster at any co-retrieval) RESULT ─────────────────────────────────────────────────── Clusters discovered 87 Would-archive total 454 Noise (singletons) 9649 Cluster 1 · 18 members · coOcc=98 Cluster 2 · 20 members · coOcc=91 Cluster 3 · 15 members · coOcc=73 …
A real run over one busy week. Note the shape: 95% of observations are noise, and the pass proposes archiving 454 of 10,103. Dreaming is meant to fold the few groups that genuinely earned it, not to reorganise everything it can reach. The 93.5% without co-recall is the honest cost of a young audit log — those rows simply have no signal yet, and the pass says so rather than guessing.
Questions
The ones worth asking before you let anything rewrite your memory.
Does dreaming delete my observations?
No. Members of a folded cluster are archived — flagged and linked to the theme that replaced them, still on disk, still readable. The design treats deletion as the one operation a memory system must never perform silently, so it is not performed at all: captain-memo theme undo <id> or dedup --undo puts the members back. dedup --undo can also reverse just some folds (--member, --run, --since), and an undone fold is remembered: no pass folds that pair again until dedup --allow-refold lifts the mark.
What if I switched the audit log off?
Then there is no co-retrieval signal and clustering falls back to recency alone, which is a weak grouping on its own — you'll see most observations reported as noise. It is on by default for exactly that reason; set CAPTAIN_MEMO_RECALL_AUDIT=0 in worker.env only if you want it gone. The log stays on your disk: nothing indexes it into the corpus and nothing relays it anywhere.
Does anything leave my machine?
Only to the summarizer and embedder you chose. Grouping is local arithmetic. The consolidation pass that runs on idle makes one call to the summarizer you chose for each candidate group, carrying the dates, types and titles of that group's observations, and a theme it writes is embedded by your embedder like any other memory. The dream --dry-run explorer sends nothing: it opens three local files read-only and does its own arithmetic, with no network call, no worker contact and no model invocation, not even to your own local model. It runs identically on a laptop with the network cable pulled out.
Isn't someone else already doing this?
Partly, and it's worth being straight about which part. Local consolidation over a corpus shared by several assistants is already done by mcp-memory-service, SuperLocalMemory, shodh-memory and ByteRover V4. What none of them does is group by co-retrieval — they cluster on embeddings, entity overlap or token overlap. The closest anyone comes is SuperLocalMemory, which records pairwise co-retrieval on every recall but never feeds it to its consolidator, and HeLa-Mem, a research paper that builds precisely this signal as a Hebbian graph for a single conversation. See the comparison.
Why not just use embedding similarity? It's simpler.
Because it answers a different question. Embedding similarity tells you which memories are worded alike; that reliably groups three unrelated notes that all happen to say "cache", and reliably misses the two that describe one bug in different words. Co-retrieval tells you which memories your actual work treats as one topic. The dry-run explorer scores co-retrieval and recency only; the shipped consolidation pass adds a cosine confirm on top, so a theme forms only where the observations read alike and you have pulled them up together.
Can I dream over just one assistant's memories?
No, and that's deliberate. The corpus isn't partitioned by assistant, so there's nothing to scope to — the only filter is --project. Pooling is the feature: a theme built from what Codex saw and what Claude Code saw is worth more than either tool's half of it. Pooling stops at your machine, though: another captain's memories are never folded into yours. See shared, never merged.
How is this different from Anthropic's Dreams?
Same name, same intent, different altitude. Theirs runs in the cloud, reads one agent's transcripts with a model, and bills per dream. Ours runs on your disk, groups the whole multi-assistant corpus with a distance metric, and has no dream service to pay for. Its only model call decides whether a group is one fact and names its theme, and it goes to the summarizer you chose. See the comparison above; they're complementary more than competing.