How it works
The extract → classify → store pipeline and search-time ranking.
Every memory.add() runs a pipeline before anything is written.
Pipeline overview
messages
→ extractFacts (LLM, latest turn)
→ embed
→ resolveEntity (align entity label — optional similarity + LLM)
→ ensureEntityInRegistry (optional, useEntityRegistry)
→ Pass 1: own history (ADD | UPDATE | REPLACE | NOOP)
→ if ADD: Pass 2 — label other agents' claims, then recency or authority on contradictions
→ apply op (write claim row + sync vector index)1. Extract
extractFacts() sends the latest user/assistant turn to an LLM. Output is validated with Zod:
{ "facts": [{ "entity": "user.diet", "text": "The user is vegetarian", "type": "semantic" }] }With useEntityRegistry: true, the prompt includes known keys from the entities table and the LLM must pick an existing key or use new:user.<domain>.
2. Resolve entity
resolveEntity() aligns the extracted label with existing active claims when fact text is semantically similar. Prevents user.timezone vs user.location drift from fragmenting memory.
| Similarity | Behavior |
|---|---|
| ≥ 0.85 | Auto-align to incumbent entity |
| 0.70 – 0.85 | Optional LLM disambiguation |
| < 0.70 | Keep extracted entity |
See Claims and entities for registry and config options.
3. Pass 1 — same agent
Vector search finds similar active claims from the same sourceAgent. The LLM classifies:
| Op | Meaning |
|---|---|
ADD | New information |
UPDATE | Refines without contradicting |
REPLACE | Contradicts / supersedes own prior claim |
NOOP | Restates existing claim — nothing written |
If pass 1 is not ADD, pass 2 is skipped.
4. Pass 2 — other agents
When pass 1 returns ADD, Vault loads every active claim on the same entity from other agents (from SQL, not similarity search). One LLM call labels each claim:
| Label | Result |
|---|---|
equivalent | Write nothing, unless another claim contradicts |
compatible | Store the new claim active; leave the others active |
contradicts | Apply recency or authority to those claims only |
Search
memory.search(query) embeds the query, hits the vector store, hydrates rows from SQL, and applies recency-weighted scoring at query time. Stored confidence is not mutated.
Cross-agent search uses the exported search(db, vectorStore, embed, query, opts) with no sourceAgent filter.
Each agent process holds its own FAISS index. Call vectorStore.rebuild(db) before reads/writes when another process may have written claims — see Vector store.