Memory change feed
Available in 0.8.0. Use this feed to refresh scoped caches, reconnect agents and
observe committed memory mutations. It shares the existing durable memory store
and does not require a messaging service, model provider or background worker.
Check the deployed version with /health.
Read committed changes#
Call POST /api/v1/memory/changes with Authorization: Bearer <credential> and
memory_read for the exact scope. Tenant and private subject come from the current
credential, including on continuations. A cursor grants no additional access.
{
"contract_version": 1,
"scope": {"project_id": "project", "mission_id": null, "agent_id": "agent", "visibility": "shared"},
"query": {"after": null, "through": null, "limit": 100}
}
The page response contains:
| Field | Contract |
|---|---|
changes |
At most limit journal entries in ascending sequence order |
next_cursor |
Exclusive starting point for the next request, or null before journal activation |
high_watermark |
Inclusive fence used by this request, or null before activation |
complete |
This request reached its fence; later writes may still arrive |
Each cursor has an opaque journal_id and unsigned 64-bit sequence. Ordering is
local to one authorized namespace. There is no global order across tenants,
projects, missions, agents or private subjects. limit must be 1–256; the standard
64 KiB request limit and Cache-Control: no-store apply.
Each entry preserves schema_version, cursor, kind, record_id,
record_revision, authenticated author, committed_at_millis and an opaque
change_digest. Kinds include created, derived, updated, deleted and
embedding_attached, plus reviewed for review decisions. An embedding entry identifies the bound memory revision;
it does not increment that revision. Memory content, vectors and provider secrets
are never copied to the journal. Refetch an authorized record when needed.
A memory mutation, its idempotency receipt, integrity index and journal entry commit in one synchronous WAL batch. A new embedding binding and its event also commit together. An identical command retry creates no second event. Repeating an identical existing embedding under a new idempotency key records its normal receipt but adds no change event because the binding did not change. Conflicted or rejected commands do not advance the journal.
Catch up and then poll#
- Read a page with
through: nulland your last durableaftercursor. - Retain that page's
high_watermark. Pass it asthroughon subsequent pages and advanceafterto each returnednext_cursor. - Apply entries idempotently using
(journal_id, sequence)as the delivery ID. Save progress only after your local effects are durable. - When
completeis true, retainnext_cursor. For a new polling cycle, clearthroughso the next request can observe later commits.
The fence prevents a busy writer from moving the end of a catch-up cycle. The
service retains no pagination session; persisted cursors survive restart.
An empty page with complete: true means caught up to that fence, not that the
stream is permanently closed. A disconnected client can resume without replaying
already acknowledged sequence numbers. Retrying a read may redeliver entries;
this is not a guarantee of exactly-once external effects.
Fetch current record state when processing an entry; the record may have advanced
beyond record_revision or become unavailable. Never replace newer cached state
with an older event's inferred state. A deletion event remains readable as
metadata after its payload is unavailable. Treat any scoped change as invalidating
a cached ranking whose dependency set is unknown.
Initial reconciliation and upgrades#
The journal covers mutations committed by the platform API after this feature is active. Existing records and historical idempotency receipts are preserved, but the upgrade does not invent earlier journal entries. An old command retry still returns its original receipt without creating synthetic history.
For a new consumer, first capture the current journal watermark, then enumerate
current authorized records with the memory query API.
After that scan, consume changes after the captured watermark and refetch current
state for affected IDs. If the initial watermark is null, start the change feed
with after: null. Reconcile repeated IDs by their revision and availability.
This protocol supports eventual cache reconciliation under concurrent writes; the
ordinary query scan is not a globally frozen database snapshot.
Expiry is clock-driven and creates no journal entry. Respect each record's
valid_until_millis and revalidate current access before using cached content.
Credential revocation is enforced on requests; it does not erase copies already
held by a client. The feed is not a content-erasure acknowledgement.
Errors and retention#
Malformed bounds or an after sequence beyond through return 400. A cursor from
another namespace/journal or ahead of restored state returns 409; do not silently
reset it to zero. A missing/invalid credential returns 401, missing capability or
scope grant returns 403, and inconsistent journal state or a missing expected
entry returns 500. No partial page is returned on a detected inconsistency.
Version 0.8.0 performs no automatic journal pruning and declares no cursor expiry. Journal metadata therefore grows with mutations. Operators must retain its data with the memory volume. Restoring an older backup may make a client cursor ahead of the restored journal; reconcile that recovery explicitly. Configurable retention, replication and cross-region ordering are separate capabilities.
Save consumer progress#
Durable consumer checkpoints store subject-owned cursors
with monotonic updates and revision checks. They require memory_checkpoint in
addition to memory_read and do not generate feed events. Save progress only
after external effects are durable; retries may redeliver events.
Detect divergent restore histories#
The version 2 feed, available in 0.9.0, adds prefix-anchored cursors. Version 1 remains sequence-only: after a restore and divergent writes, reaching the same sequence does not prove the same history. Use version 2 when consumers require that distinction.