Memory
Agents remember across runs through a memory special page: a running list of facts, episodes, and procedures, each an ordinary node you can read, link, and search. Agents propose items during runs, a nightly pass consolidates duplicates without ever deleting history, and every new run retrieves the relevant items into its system prompt. Because memory items are just nodes, they surface as backlinks on the entities they mention and get vector coverage for free.

Memory items carry provenance and [[links]], are filtered by kind, and are consolidated nightly.
The item model
Section titled “The item model”Memory items are ordinary nodes on the memory special page (ensured on boot), each
carrying a props.memory block:
kind 'semantic' | 'episodic' | 'procedural'
semantic is a stable fact, episodic is a dated event, procedural is a how-to.
source string
Where the item came from, for example agent:<runId>.
ts timestamp
When the item was recorded.
supersededBy nodeId?
Set by consolidation when a newer item replaces this one. The old node and its history are kept, never deleted.
[[links]] in an item’s text are plain link rows, so an item that mentions [[Rachel Kim]]
shows up as a backlink on that CRM page and is retrievable by
search like any other node. There is no separate memory index.
Agents propose, they do not curate
Section titled “Agents propose, they do not curate”During a run an agent calls memory.propose {kind, text}, which files a new item under
props.memory = {kind, source: 'agent:<runId>', ts}. Proposing is deliberately the only
thing a run does to memory: curation (dedup, supersession) belongs to the consolidation
pass, so a chatty run cannot quietly rewrite what you already know. See
tools & approvals for the tool surface.
Nightly consolidation
Section titled “Nightly consolidation”Consolidation is deterministic TypeScript, no LLM required. Walking active items in timestamp order:
- An exact normalized-text duplicate merges: the newer copy is stamped superseded by the older.
- A same-kind near-duplicate with differing content (character-bigram Dice similarity at or above 0.82) supersedes: the older item is stamped superseded by the newer, so memory stays current.
An optional LLM-assist seam refines borderline pairs (Dice between 0.6 and 0.82); under
PROVIDERS=fake it is a no-op, so tests stay deterministic. Each run commits one
transaction: all the supersededBy stamps plus a consolidation frame node appended to
the memory page (recording how many items merged and how many were superseded). Re-runs are
idempotent.
The pass runs as a normal agent run: the seeded memory-consolidate
schedule (0 3 * * *) targets the seeded agents/memory-consolidation
definition, so nightly consolidation appears in the
task queue Scheduled section and streams in the run tree like any other
run.
Retrieval into every run
Section titled “Retrieval into every run”retrieveMemory {q?, pages?, limit=8} is a hybrid search filtered to the memory corpus plus
any items [[linked]] to the given pages, deduped and active-only. It is wired into the
runner: at every run start, Subspace retrieves on the instruction text and the invoking
page, and when items come back, appends a Relevant memory: block to the run’s system
message (the instruction stays verbatim; a retrieval failure is swallowed, never blocking
the run). So an agent starts each run already knowing the relevant facts, without you pasting
them in.
The memory page
Section titled “The memory page”Open /p/memory to browse and curate:
- Filter tabs: all / semantic / episodic / procedural, each with an active count, plus a show-superseded toggle.
- Item rows: a kind chip, the text with
[[links]]rendered as navigable links, and asource · tsmeta line. Superseded items are struck through with asupersededchip. - The consolidation frame: shows the nightly schedule and the last run’s merged and
superseded counts, with a consolidate now button (backed by
memory.consolidate).
The memory.view {kind?, includeSuperseded?} endpoint returns the items, active counts per
kind, and the last frame; memory.consolidate {} runs the pass on demand.