Skip to Content
AgentMem is in active development. Star us on GitHub.
FeaturesHistory & lineage

History & lineage

When timestamp_wins resolution fires, AgentMem soft-deletes the old conflicting memories and back-links them to the new one via a superseded_by column. The /history endpoint surfaces that lineage for auditing and debugging.

Reading lineage

const history = await mem.getHistory(memoryId) console.log({ current: history.memory, // the memory you asked about replacedBy: history.supersededBy, // Memory | null — the row that replaced it thisReplaced: history.supersedes, // Memory[] — rows this one replaced })

supersededBy is one level forward — the memory that directly replaced memory. If that one was itself replaced later, you’ll find its replacement by calling getHistory again with history.supersededBy.id.

supersedes is one level back. If memory was created by a timestamp_wins resolution that consolidated three older memories, supersedes.length === 3.

When superseded_by is set

Only one code path writes this column: the conflict resolution flow when the policy is timestamp_wins. The sequence is:

  1. Conflict check finds N existing memories that contradict the new write.
  2. Those N rows get deleted_at = now() (they vanish from search).
  3. The new memory is inserted.
  4. Those same N rows get superseded_by = <new memory id> set.

If a memory is plain-deleted via DELETE /v1/memory/:id or expires via TTL, superseded_by stays null. Lineage is specifically about replacement, not just deletion.

Including vector clocks

Every history response and every search hit now includes vector_clock — the per-agent counter snapshot at write time. Useful when you need to reason about causality:

{ "memory": { "id": "uuid-B", "content": "deadline is Monday", "vector_clock": { "planner-bot": 5, "executor-bot": 12 }, "superseded_by": null }, "supersededBy": null, "supersedes": [ { "id": "uuid-A", "content": "deadline is Friday", "vector_clock": { "planner-bot": 4 }, "deleted_at": "2026-05-14T10:00:00Z" } ] }

The clocks let you answer “did the new write know about the old one before contradicting it?” — if clock(B) ≥ clock(A) per agent, B was causally aware of A.

REST equivalent

curl -X GET https://lwbwcuuzoituanwhekyo.supabase.co/functions/v1/api/v1/memory/<id>/history \ -H "Authorization: Bearer $AGENTMEM_API_KEY"

Returns { memory, supersededBy, supersedes }.

Why this isn’t a single graph endpoint

We deliberately return one level of lineage rather than the full chain. A “supersession graph” endpoint sounds clean but encourages clients to render the whole tree even when they only need the latest version, which is the common case. Walking forward one step at a time keeps the API predictable and stops it from accidentally returning 100 rows on a hot path. If you need the full chain, call getHistory recursively until supersededBy === null.

Last updated on