Two surfaces on one store
This is the first thing to get straight, because the same store ID serves both:
Document Memory has
alpha / hosted source posture; confirm its executable state in the live capability status. The versioned surface has a qualified durable Postgres adapter for production and local development, while the in-memory adapter remains its test and conformance oracle. Hosted alpha still leaves MEMORY_VERSIONED_SURFACE disabled until an operator-approved target completes the D7 enablement sequence; versioned operations and Dream creation currently fail closed there with 503 memory.backend_unavailable. Existing Dream metadata remains readable for recovery and audit. The distinction matters because the surfaces answer different questions: reach for the document surface when you just need a file at a path, and the versioned surface when you need history, review, or a per-Session branch.
The model
Store
One ownership, history, and visibility boundary. Its owner decides lifecycle and the default PermissionAssignments minted at creation, never who may access it. Access is always PermissionAssignments.
Document
Content at a normalized absolute path. Capped at 100 KB each.
Revision
An immutable snapshot of the store’s whole tree, not one document’s version. Content-hashed so every storage backend produces byte-identical values.
Ref
A named pointer at an exact revision. Refs are the only mutable part; they advance by compare-and-swap, never last-write-wins.
canonical is the shared truth. A Session gets its own working ref so it can write without disturbing anyone else, and a checkpoint seals that Session’s accumulated changes at a turn boundary: at most one revision per changed store per turn. A turn that changed nothing produces nothing.
Ownership is one of workspace, principal, agent_definition, session, or surface_scope. At creation, every owner kind except workspace mints one PermissionAssignment giving the owner read and write. A Workspace-owned store mints none, so access has to be granted deliberately.
POST /v1/memory-stores/{id}/fork creates a new store from one exact source revision. You choose the target name, owner, and optional description; Checkfu copies that immutable tree, proves the target canonical tree_hash matches, and then the two stores evolve independently. The endpoint requires an Idempotency-Key: an exact retry returns the same fork, while reusing the key with different input is a conflict. Forking retained content is unavailable in a zero-data-retention Workspace and remains behind the same versioned-surface rollout fence.
Portable exports
Two export routes exist, and they are different shapes rather than two sizes of the same one.GET /v1/memory-stores/{id}/export returns the whole store as a single complete aggregate and
is kept unchanged for the dated v1 response, so a large store arrives as one unbounded body.
GET /v1/memory-stores/{id}/export-pages is the additive bounded route and the one to reach
for: a normalized, resumable graph stream of revision, path_fence, document_version,
revision_entry, and ref items in canonical order. Read a page, then pass its next_page
value back as the page query parameter until next_page is null. That cursor is
authenticated and opaque, and it pins every page to one Workspace, source store, container
generation, scope, retention instant, and the earliest included body expiry. A concurrent
mutation or an elapsed expiry returns memory.conflict instead of mixing snapshots or
extending custody. A page carries at most 200 items (ask for fewer with limit) and 1 MiB of
canonical item payload bytes; the manifest and response envelope sit outside that byte count,
and an importer should recompute both the count and the canonical ordering rather than trust
the page’s declaration.
Structural history survives erasure: every revision/path pair remains a revision_entry,
while its referenced document version carries either an available body with content and its
immutable expiry ceiling or an explicit erased body. Erasure replaces raw provenance with its
structural digest, retaining tree hashes and document counts without containing or
reconstructing shredded bytes or source identifiers.
How canonical actually moves
Canonical advances differently depending on who owns the store, and that is what keeps shared memory safe:- A personal store (principal- or session-owned) advances canonical directly. When such a store is mounted
read_write, the agent’s file changes seal into its durable documents at turn settlement (see Writeback at settlement). - A shared store (environment-, agent-definition-, or surface-scope-owned) advances canonical only through an approved Proposal. A direct canonical write returns
403 memory.read_only, and a shared store never opens aread_writemount in the first place: the clamp is structural, so a straywritePermissionAssignment cannot turn a sandbox into a live writer. There is therefore no shared writeback to defer. A settlement capture naming a shared store matches no admitted writable mount and is refused outright, mutating nothing and producing no skip event.
Writeback at settlement
When a personal store is mountedread_write, the agent works its memory as an on-disk file tree, and at turn settlement Checkfu seals what changed: the runner diffs its live sandbox tree against the frozen staged manifest and the Session seals a bounded, idempotent pass into the store’s durable document surface (putDocument / removeDocument, content-addressed per document). One memory.change_checkpointed records the changed paths and the base/new tree digests; one memory.accessed records the read. The seal fails closed: on any write error, an unreadable store, or ZDR denial it writes nothing partial. It is idempotent on replay. Because a personal store advances directly, a second Session the same principal opens reads back what the first one wrote. (The versioned revision graph of revisions, diffs, and working refs remains locally implemented but preview / rollout_fenced behind MEMORY_VERSIONED_SURFACE; the settlement seal advances the independently modeled durable document surface, so the digests above are content addresses, not graph revision IDs. See capability status.)
Writeback needs the execution provider to enumerate the mounted tree, because a delete is inferred from a path that is no longer there — a partial listing would read as deletions. Checkfu therefore enumerates through a platform-owned reader, never a command run inside the sandbox image, whose binaries the agent controls. A provider that cannot enumerate does not silently drop the edits: a writable memory mount is refused at admission with runtime.harness_incompatible, so the Run fails visibly rather than settling a wrong revision. A read_only memory mount needs no enumeration and is unaffected.
The seal is also single-store: one turn may checkpoint at most one MemoryStore. A store’s aggregate, its whole document tree, and its replay receipt commit in one durable transaction, and there is no cross-store coordinator that could make two independently owned stores one transaction. So a capture naming two stores is refused before any mutation rather than committing a prefix, and a terminal turn that changed two writable stores cannot settle. Admit one writeback-capable store per Run and mount the rest read_only.
Proposals
A Proposal freezes itsbase_revision, head_revision, and diff hash at creation. Approving it compare-and-swaps canonical from that frozen base, so a proposal can never silently rebase onto work it was not reviewed against.
You respond with approve or reject. superseded is not something a reviewer chooses: it is what an approval becomes when canonical moved on beneath it. Handle it as “re-propose against the new base,” not as a rejection.
The stored status values (open, approved, rejected, superseded) differ from the imperative verbs you send (approve, reject). An empty proposal is unrepresentable: base and head must differ.
Mounting memory into a Session
A Session declares its Memory mounts at creation, requestingread_only or read_write plus optional agent-facing instructions. Admission then clamps the request against the acting Principal’s PermissionAssignments:
Two structural write caps then apply on top of the PermissionAssignment clamp:
- A shared-owner store (Workspace, AgentDefinition, or SurfaceScope) never resolves to
read_write, however the PermissionAssignments read. Shared canonical state advances through governed proposals, not a live sandbox writer. - A principal-owned personal store is
read_onlyif explicitly mounted into a shared public/private place. A personal/DM place is not shared, so the store remainsread_write-eligible there.
write PermissionAssignment cannot bypass any of them.
The degradation is recorded, not silent: memory.mount_resolved carries both the requested and the effective access, and each Run repeats the effective access in session.mounts_resolved. An archived store stays readable through an existing read-only mount but rejects a read-write plan.
Retrieval
Search is lexical, deterministic, and revision-pinned. Omittingrevision resolves canonical once and then searches that exact revision, never a moving target, so results within one search are internally consistent.
Reads are recorded as memory.accessed, batched once per store per turn. Under zero-data-retention the record downgrades from listing document paths to an aggregate count before it is persisted.
Memory events carry IDs, paths, hashes, and counts, never document content. The event log tells you what was touched, not what it said.
Erasure
POST /v1/memory-stores/{id}/redactions takes a list of paths and erases them across every surface the platform owns for that store, including the search index. It reports counts only (revisions, paths, and hashes affected) and never returns erased content. Redaction requires an administrator key and is itself recorded as a revision, so erasure appears in history rather than quietly rewriting it.
Deleting a store fences writers first, erases its complete versioned container and projections, and only then records the terminal tombstone. A delayed fork or change-set retry cannot recreate the store or replay paths from a stale receipt.
Curated memory (Dreams)
A Dream is a batch job that distills what agents learned across many Sessions into a clean MemoryStore. One input store and 1–100 Sessions go in; one new Workspace-owned output store comes out. The inputs are never mutated: curation is copy-on-write.Dreams are
preview / rollout_fenced; see capability status. The example below runs against local development with Postgres and MEMORY_VERSIONED_SURFACE=true; the in-memory adapter is only the test and conformance oracle. Hosted requests still fail closed until an operator-approved rollout enables the versioned Memory prerequisite.- Omit
model_routing_profile_keyand curation is a deterministic dedupe pass: no inference, zero usage. - Supply
model_routing_profile_keyand one schema-constrained model call, steered byinstructions, does the curation. review: trueleaves the proposalopenfor a human decision instead of auto-approving it (safe to auto-approve by default, because the fresh output store has no other writers).
status moves pending → running → completed (or failed/canceled), and a completed Dream names its output_memory_store. The create call returns as soon as the Dream is pending: execution runs on the platform’s own work queue, so you poll (or cancel) a Dream that is genuinely still going rather than one that already finished inside your request. Cancel therefore lands mid-pipeline on a pending or running Dream, and a Dream can never strand pending with nothing to execute it — a failed enqueue settles the row failed at create time. To run curation on a schedule, point a schedule-triggered Automation at a dream target; each firing curates only the Sessions it has not seen before.
One trap worth knowing
Memory uses three different concurrency mechanisms, and picking the wrong one is the most common integration mistake:
All three fail with
409 memory.conflict when the precondition misses. None of them fall back to last-write-wins.
Next steps
Sessions and runs
How mounts resolve at admission and per Run.
Tenancy and governance
The PermissionAssignments that decide what a mount is clamped to.