Extracted data is not written directly to memory. A reconciliation pass compares new extractions against what is already stored: matching entities, detecting conflicts, and deciding whether a new value replaces an old one or coexists alongside it.
Reconciliation in brief
Entity matching
SurrealDB Agent Memory attaches a new extraction to an existing entity by exact composite key. An entity's record id is [normalised_type, normalised_name], and matching is a direct lookup on that id: either the record exists and the new attributes and relations land on it, or a new entity is created. There is no fuzzy, phonetic, or embedding-based matching on the write path - a name that normalises differently becomes a separate entity.
Keeping references together therefore depends on extraction emitting a consistent name. To support that, the extraction prompt is given the entities the Context already holds and instructed to reuse their exact names. Seeding the entities you care about before a bulk ingest measurably improves the odds that later mentions land on them.
Type is half the identity - the same name under two types is two entities that nothing merges. Supplied types are normalised into a closed vocabulary (person, organisation, project, location, topic, product, policy, concept, event, agent, service, other). Near-synonyms fold into it - company, org, and organization all become organisation - and a type outside the set becomes other rather than minting a new type. When you write entities directly, pick the type once and keep it stable, or a later write lands on a different record.
Nicknames and alternative names - because matching is exact, a referent named two ways produces two entities. Matt and Matthew Cauldwell, IBM and International Business Machines, a person referred to by first name in one document and in full in another: each pair is two records, each with its own attributes and its own supersession chains.
There is no alias field and no automatic merge. Three things help:
Seed canonical names first. Create the entity under the name you want before ingesting, so it appears in the extraction prompt's known-entity list.
Normalise at the edge. Map the variants you know about to one canonical name in your own pipeline, before the write.
Audit with
POST /fsck. Theduplicatescheck reports entity pairs above a cosine-similarity floor (default0.95) in the same scope. It reports candidates for you to act on; it does not merge them.
A same_as relation is an ordinary edge label, not an instruction to the reconciler. It records that two entities denote the same referent - useful when that is a fact the reader or agent learns at a particular moment, and gate-able with asOf - but the two entities stay separate, keep their own attributes, and are returned separately. Merging them is a decision for the layer above.
Cross-language mentions - the same concept in different languages (for example skill_math from an English conversation and skill_matte from a Swedish conversation) are usually stored as separate relation labels unless extraction or reconciliation maps them to the same entity and key. That is expected: labels carry language-specific semantics. Background elaboration may later link related entities. Labels cannot be constrained up front, but extraction is shown the labels already in use and told to reuse them where the meaning matches - see Extraction vocabulary. SurrealDB Agent Memory does not automatically mint a meta-edge between every cross-language synonym.
Attribute conflicts
When the same entity and key already have a value in the same scope and the new value differs, SurrealDB Agent Memory treats it as a conflict. What happens depends on where the existing value came from:
Experiential-only conflict - the new value supersedes the old. The previous value is closed with a
valid_untiltimestamp and remains in history.Authoritative conflict - curated knowledge (documents, operator upserts) is not modified. The user’s assertion is stored in experiential memory with full provenance, and the clash is surfaced (for example via
uncertaintyrecords). See Authority when pillars meet.
SurrealDB Agent Memory does not use last-write-wins. Concurrent updates reconcile in one ACID transaction per write; cross-provenance clashes become uncertainty, same-stream updates supersede when confidence allows, and writes below the confidence floor cannot silently overwrite stronger beliefs.
Confidence floor
Each Context configures config.reconciliation.confidence_floor (default 0.7). On a same-provenance conflict, SurrealDB Agent Memory supersedes the prior value only when the new extraction’s confidence is at or above the floor and at or above the existing row’s confidence. Otherwise both values stay live and an uncertainty is recorded - a low-confidence extraction cannot erase an established high-confidence fact.
Scope-aware coexistence
Different scopes may legitimately hold different values for the same key - an org default and a user override, or the same person in two organisations. Conflict detection runs within one scope. Cross-scope differences are independent records, not supersessions.
Invalidation
When a user explicitly negates a fact (“I no longer work there”, “forget my old address”), reconciliation closes matching active attributes with valid_until rather than deleting them. History and audit trails stay intact.
The supersession chain
When a fact changes over time, SurrealDB Agent Memory keeps a chain of values - each link records what it replaced and what replaced it. Only the current head of the chain is returned by default queries; earlier values remain available for history and point-in-time reads.
Example: Alice’s role moves from COO → CEO → CTO. Each step opens a new value with valid_from set to the correction time and closes the previous one with valid_until. Nothing is erased.
Querying current and historical state
Use the public API rather than ad hoc database queries:
Current beliefs -
POST /queryorGET /profilefor a scope; entity GETs return active attributes.History for one key -
GET /api/v1/{context_id}/entities/{type}/{name}/history/{key}returns the supersession chain.Point in time - entity reads accept
asOf,atInstant,validFrom, andvalidUntilquery parameters. See Tri-temporal model and the REST API.
Correction versus coexistence
Correction - same scope, new value replaces old reality (role change, corrected name). Supersession applies.
Coexistence - same key, different scopes, both valid in their contexts. Neither record supersedes the other.
Unresolved relation objects
When reconciliation cannot resolve a relation’s object entity, it materialises a placeholder typed unknown so the stated fact is not dropped from recall. These placeholders may not auto-merge with a later properly typed extraction until a maintenance sweep reconciles them.
History by default - and forget when you need removal
For corrections and supersession, SurrealDB Agent Memory keeps a full chain of past values. Nothing is erased: earlier beliefs stay in the store so you can audit, recover from a bad correction, or answer “what did we believe in January?”
That default does not mean users can never remove data. When someone asks to forget something, SurrealDB Agent Memory supports two distinct levels:
| Intent | What happens | Typical use |
|---|---|---|
| Stop using this in the agent (default) | Matching memories are expired - they no longer appear in recall, query, profile, or chat context, but remain stored for audit and point-in-time review. | “Forget my old job”, entity delete, routine privacy tidying. |
| Remove it for compliance (explicit opt-in) | The same match is permanently erased, including correction history for the affected entities. | Right-to-erasure requests, retention policy enforcement. |
| Erase a whole scope subtree | Everything tagged under a scope path is hard-deleted across memory and knowledge. | GDPR-style deletion for a user or team branch. |
Default forget and entity delete are soft: the agent behaves as if the information is gone, while operators can still inspect what was retired. Compliance paths require an explicit purge flag on POST /forget, or POST /scopes/forget for subtree erasure - both need the memory:forget grant on the relevant scope. Audit traces may still record that a forget operation ran, without retaining the forgotten content itself.
See Forgetting memories for request shapes and CLI equivalents.