1. Executive Summary
Mem0Sharp is a reimplementation rather than a
client. Apache-2.0, 2,333 lines of C# across Application,
Infrastructure, Intelligence,
Transports and Telemetry, it builds the Mem0 architecture — LLM extraction, LLM conflict
resolution, vector storage, graph relationships — on .NET, with a
Postgres/pgvector store, an in-memory store, an MCP server, and a
decorator that wraps the service in telemetry.
That makes it useful to this atlas in a way a port usually is not: the same design, read twice, in two languages, by two teams. And the second reading has something the first did not.
It earns audit_log, which the atlas's Mem0
report does not. PostgresMemoryStore creates a
{table}_history table beside the memory table, indexed on
(memory_id, created_at), and writes rows through a single
INSERT INTO {historyTable} (id, memory_id, event, old_memory, new_memory, created_at).
There is no UPDATE and no targeted DELETE
against it anywhere in the file — the only statement that removes
history is a TRUNCATE of both tables together, which is a
reset rather than an edit. So every mutation leaves a durable record of
what the text was and what it became, keyed on the memory, in the
system's own store.
The audit is inherited, not added. Mem0's mem0/mem0/memory/storage.py
carries the same append-only history at its pinned commit — written only
by add_history and batch_add_history, with no
UPDATE or DELETE against it — and it is the
richer of the two, since the Python table records actor_id
and role where the C# table does not. Both hold
audit_log. What the port contributes is not the mechanism
but a second reading of it: the same design expressed in another
language, which is the cheapest way to find out whether a mark was
earned by the design or by the implementation.
Scope is the other mark and it is enforced at the schema:
user_id text NOT NULL with agent_id,
run_id and scope beside it, composed into a
WHERE clause that travels into the get, list and
vector-search queries alike. A NOT NULL user column is a stronger
commitment than most of this corpus manages — it makes an unscoped row
unwritable rather than merely discouraged.
2. Mental Model
Inherited from Mem0, and the atlas's criticism of the original transfers intact: facts are LLM-extracted and stored as plain text with no representation of uncertainty. There is no status, no confidence and no provenance chain on a row. What decides whether an existing memory is kept, updated or removed is an LLM conflict resolver run at write time, and its verdict is applied rather than recorded — the history table captures that a change happened and what the text was, not why the model thought it should.
The one thing the history table changes is that the previous text survives. In a design whose central risk is an extractor overwriting a good memory with a worse paraphrase, being able to read what was there before is a meaningful partial answer, and it is available by query rather than by log-grepping.
flowchart TB
In["Conversation"] --> Ex["LlmMemoryExtractor"]
Ex --> Cand["Candidate memories"]
Cand --> Res["LLM conflict resolver<br/>vs existing rows"]
Res --> Apply[("memories<br/>user_id NOT NULL · agent_id · run_id · scope")]
Apply --> Hist[("{table}_history<br/>INSERT only<br/>event · old_memory · new_memory")]
Q["Search"] -->|"WHERE built from the scope columns"| Apply
Apply -->|"cosine ORDER BY distance"| Hits["Hits → optional LlmReranker"]
Hist -.->|"the record of what was removed exists<br/>and the write path never reads it"| Gap["no tombstone"]
3. Architecture
Postgres with pgvector for real deployments, an 80-line in-memory
store for tests, and a PostgresRelationshipStores (250
lines) for the graph side. The layering is conventional .NET and clean:
Application/MemoryService.cs (417 lines) holds the
orchestration, Infrastructure/ the stores,
Intelligence/ the four LLM-facing pieces —
LlmMemoryIntelligence (82), LlmReranker (41),
LlmGraphMemoryExtractor (26),
LlmMemoryExtractor (24) — and
Transports/Mcp/MemoryMcpServer.cs (205) exposes it over
MCP.
TelemetryMemoryService (74 lines) is a decorator around
the service rather than instrumentation threaded through it, which is
the same discipline MateClaw applies to retry
and metrics and which most systems here skip.
4. Essential Implementation Paths
src/Mem0Sharp/Application/MemoryService.cs(417) — add, search, update, delete, and the conflict path.src/Mem0Sharp/Infrastructure/Postgres/PostgresMemoryStore.cs(326) — the schema, the scope predicate, the history writes.src/Mem0Sharp/Infrastructure/Postgres/PostgresRelationshipStores.cs(250).src/Mem0Sharp/Transports/Mcp/MemoryMcpServer.cs(205).src/Mem0Sharp/Intelligence/— extraction, graph extraction, reranking.tests/Mem0Sharp.Tests/—MemoryServiceTests,LlmMemoryConflictResolverTests,MemoryMcpServerTests.
5. Memory Data Model
id, text_value, user_id (NOT
NULL), agent_id, run_id, scope,
metadata, embedding, created_at,
updated_at, expires_at,
hash_value.
Four scope-ish columns is more than most, and they are hierarchical
in intent — a user owns agents, an agent has runs — though nothing in
the schema enforces the hierarchy. hash_value is the dedup
key and expires_at is a TTL. Both timestamps are record
time, so no bi-temporality.
The history row is id, memory_id,
event, old_memory, new_memory,
created_at — the event vocabulary plus the before and
after, which is exactly what an audit of a text-overwriting system
needs.
6. Retrieval Mechanics
SELECT ... 1 - (m.embedding <=> $1::vector) AS score ... ORDER BY m.embedding <=> $1::vector LIMIT $topK,
with the scope predicate ANDed in and a guard that rows without
embeddings are excluded. Cosine nearest neighbour, nothing fused, with
an optional LlmReranker above it — one model call to
reorder, which is the expensive way to buy relevance and the only one on
offer here.
Scope is applied on every read path traced: get-by-id, list, and
vector search each build from the same where.
7. Write Mechanics
Writes block. Extraction produces candidates, the conflict resolver decides what to do about existing rows, the store applies it, and each application writes its history row. Deletion exists in two forms — by id, and by the scope predicate, which makes "forget everything for this user" a single statement. That is more than Mem0's reviewed contract offers and considerably more than ADK or AutoGen can express.
Nothing runs in the background; there is no consolidation sweep and no decay.
8. Agent Integration
An MCP server and a .NET library. For the .NET ecosystem specifically this is the gap it fills — the memory systems in this atlas are overwhelmingly Python and TypeScript, and a C# agent wanting Mem0's shape previously had a REST client or nothing.
9. Reliability, Safety, and Trust
Two marks, as above.
No tombstone. hash_value is a dedup
key, not a rejection record: a deleted memory's hash is deleted with it,
so the same text re-extracted inserts cleanly. The history table knows
the text was removed and nothing consults the history table on the write
path. That is the closest near-miss in this report — the durable record
of a removed value exists, and the extraction path does not read it.
No trust state, so the conflict resolver's decisions
are irreversible in kind: a memory it chose to overwrite is overwritten,
and the recovery path is a human reading _history and
re-inserting by hand.
No bi-temporality and no human review surface.
10. Tests, Evals, and Benchmarks
Three test classes were found — MemoryServiceTests,
LlmMemoryConflictResolverTests,
MemoryMcpServerTests — and none were run. That there is a
dedicated test class for the conflict resolver is the right instinct,
since its precision is the product.
No benchmark, no retrieval-quality measurement and no published numbers, which also means none of the Mem0 figures this atlas has previously had to treat as claims are restated here. A port that does not inherit the original's benchmark claims is a port making no claims, which is the correct default.
11. For Your Own Build
Steal
- Write an
event, old_memory, new_memoryrow on every mutation. Six columns, one insert, and it is the difference between an overwrite you can investigate and one you can only regret. In a design where an LLM decides what to overwrite, this is not optional. - Make the owner column
NOT NULL. A scope enforced by the schema cannot be forgotten by a caller, which is the failure mode this atlas keeps finding. - Support delete-by-scope, not just delete-by-id. "Forget this user" is the request that arrives, and it should be one statement.
- Wrap the service in a telemetry decorator. Instrumentation threaded through business logic rots; a decorator does not.
Avoid
- Keeping a history nobody reads on the write path. The record of what was deleted is right there, in the same database, indexed by memory. Consulting it before re-inserting the same text is the whole tombstone mechanism.
- Porting an architecture without porting an evaluation. The extraction and conflict-resolution quality are the product, and neither the original nor this reimplementation measures them.
Fit
If you are building .NET agents and want Mem0's shape, this is the only implementation in the atlas that gives it to you natively, and it is better instrumented and better audited than a thin REST client would be. The Postgres schema is sound and the MCP surface makes it reachable from non-.NET agents too.
Do not take it expecting the epistemics the original also lacks. It stores LLM-extracted text as fact, resolves conflicts with another LLM call, and offers no way to mark a memory doubtful — the history table is a forensic tool, not a trust model.
12. Open Questions
Does the Python Mem0 earnRe-checked on 2026-07-30: yes, and its history table is the richer of the two. Mem0's mark was corrected.audit_logtoo?- How good is the conflict resolver? There is a test class for it and no measurement of its precision, which is the same gap the atlas records for Memanto's conflict detection.
- Is the scope hierarchy enforced anywhere?
user_id,agent_idandrun_idimply containment that the schema does not express.
Appendix: File Index
| Path | Lines | What it holds |
|---|---|---|
src/Mem0Sharp/Application/MemoryService.cs |
417 | Orchestration, conflict path |
src/Mem0Sharp/Infrastructure/Postgres/PostgresMemoryStore.cs |
326 | Schema, scope predicate, history writes |
src/Mem0Sharp/Infrastructure/Postgres/PostgresRelationshipStores.cs |
250 | Graph side |
src/Mem0Sharp/Transports/Mcp/MemoryMcpServer.cs |
205 | MCP surface |
src/Mem0Sharp/Intelligence/LlmMemoryIntelligence.cs |
82 | Extraction and conflict orchestration |
src/Mem0Sharp/Infrastructure/InMemory/InMemoryStore.cs |
80 | Test backend |
src/Mem0Sharp/Telemetry/TelemetryMemoryService.cs |
74 | Decorator |