Agent Memory
Concepts

Updating and forgetting

Correct a memory in place, mark it superseded, delete it with its derived copies, and track the erasure until every copy is gone.

Memories change. A user corrects the agent, a decision is reversed, or a person asks to be forgotten. Agent Memory gives you four tools, from lightest to heaviest:

Want to…Use
Fix the wording of a memoryUpdate its content in place
Record that a newer memory replaces an older oneSupersede
Remove a memory and everything derived from itDelete, then check the erasure status
Expire memories by ageWorkspace retention

Update in place

PUT /v1/memory/{id} replaces a memory's content without creating a new memory. The new content is trimmed, screened like an append and re-embedded; an UPDATE history row and an update change-feed row are written.

Updates use optimistic concurrency. Every memory has a version, returned when you read it (GET /v1/memory/{id}):

  • Send If-Match: "<version>" to make the update conditional. If the memory changed since you read it, the server answers 412 with code precondition_failed and the currentVersion, and writes nothing.
  • Without If-Match (or with *), the last writer wins.
  • A successful update answers 200 { id, version } with an ETag header carrying the new version.
curl -s -X PUT "$AGENT_MEMORY_BASE_URL/v1/memory/<id>" \
  -H "Authorization: Bearer $AGENT_MEMORY_API_KEY" \
  -H "Content-Type: application/json" \
  -H 'If-Match: "7"' \
  -d '{"workspaceId":"'"$AGENT_MEMORY_WORKSPACE_ID"'","content":"Alex is vegan (not just vegetarian)"}'

Over MCP, update_memory(memoryId, content, version?) does the same; get_memory reports the version.

Supersede

Superseding keeps both memories but marks the old one as no longer current: it stops being canonical, its valid time closes, a REPLACES relationship is recorded, and memories derived from it are flagged for review. Superseded memories are hidden from recall by default (see Retrieval) and explain shows what replaced them.

There are three ways a memory becomes superseded:

  • When writing the replacement — the MCP tool add_memory(..., supersedes=<id>) stores the new memory and supersedes the old one in one step. If the target is not visible or already superseded, nothing is written and the reply says what replaced it.
  • Linking two existing memories — the MCP tool supersede_memory(memoryId, supersededBy), for example when two corrections disagree and the user says which one is current. Needs the write capability.
  • During consolidation — of two correction memories on the same topic, one at least 24 hours newer supersedes the older. Closer pairs are not guessed at: they are recorded as an open contradiction (visible in explain_memory) for you to resolve with supersede_memory.

MCP tools that take a memory ID accept either the full GUID or the short form (the first 8 or more characters) that search and explain print, as long as it is unambiguous in the workspace.

Delete

DELETE /v1/memory/{id}?workspaceId=… needs the delete capability. The memory's text is destroyed in the delete's own transaction, then an erasure cascade removes every copy derived from it: session entries, episode events, procedure and abstraction references, history.

StatusResultMeaning
204deletedThe memory and all derived copies are gone.
202pendingThe memory is erased; some derived copies are still being removed. Body: {"cleanup":"pending"}.
409blockedLive memories derive from it. Code deletion_blocked_by_active_dependents, with blockingDependents. Nothing was written.
404not_foundUnknown, or not visible to the caller.
403forbiddenpolicy_denied (visible but not deletable by this principal) or tenant-scope content that a workspace credential cannot delete.

The SDKs return these as { status } values.

Dependents and cascade

A delete is refused while live, not-yet-stale memories derive from the target — through DERIVED_FROM, BUILDS_ON or DEPENDS_ON relationships, or tenant conventions promoted from its lineage. You decide:

  • Retry with cascade=true to flag those dependents for review (reviewState: "stale") and proceed. Dependents are never deleted for you.
  • A leaf memory, or one whose dependents are already stale, needs no cascade.

Deletes never cascade unless you ask — on REST, MCP and every SDK. After a delete, GET /v1/memory/changesets?memoryId=<id>&action=delete shows which dependents were flagged.

Erasure status

When a delete answers pending, poll the erasure status:

curl -s "$AGENT_MEMORY_BASE_URL/v1/memory/<id>/erasure?workspaceId=$AGENT_MEMORY_WORKSPACE_ID" \
  -H "Authorization: Bearer $AGENT_MEMORY_API_KEY"
# {"memoryId":"…","status":"complete","attempts":1,"erasedAt":"…"}
statusMeaning
noneNo erasure on record for this memory.
pendingRedacted; copies still owed. A background worker retries.
completeEvery copy erased.
deadOut of retries; copies may survive and an operator must act.

The response contains IDs and state only, never content.

Retention

Workspaces can age memories out automatically with the setting retention.memoryDays (1–3650). A daily sweep deletes memories older than that with the same cascade as an explicit delete. Age-based retention only runs when the setting is overridden for the workspace or configured by the operator; by default nothing is aged out.

Memories can also carry an expiry time; expired memories are removed by the same daily sweep. Session memory expires on its own 48 hours after a run's last write. See Workspace settings.

Quick reference

SurfaceUpdateSupersedeDeleteErasure status
RESTPUT /v1/memory/{id}—DELETE /v1/memory/{id}GET /v1/memory/{id}/erasure
MCPupdate_memoryadd_memory(supersedes), supersede_memorydelete_memoryerasure_status
Node / Pythonupdate()—delete()erasure()
.NETUpdateContentAsync—DeleteMemoryAsyncGetErasureStatusAsync

On this page