Deterministic memory observability for long-lived AI agents.
Project description
notmemory
Deterministic memory observability for long-lived AI agents.
What is notmemory?
notmemory is an open-source Python SDK that gives AI agents auditable, reversible, GDPR-compliant memory.
Every memory write is cryptographically hash-chained — like Git commits for agent state. Every mistake can be rolled back. Every deletion leaves a forensic trail. Every read is logged.
Built for production AI systems where trust, compliance, and debuggability are non-negotiable.
Why notmemory?
| Problem | notmemory solution |
|---|---|
| Agent hallucinates and corrupts memory | rollback(transaction_id) undoes it |
| GDPR deletion request | forget(bank_id) tombstones with audit trail |
| Can't debug what agent remembered | get_audit_trail(cycle_id) forensic replay |
| Memory grows stale over time | Confidence decay c = c₀ · 2^(-t/30) |
| Need semantic search too | Mem0 / SuperMemory sync-layer adapters |
| Want Claude/Cursor to use agent memory | MCP server adapter |
Features
- Cryptographic audit trail — SHA-256 hash chaining on every write
- Git-like rollback — undo any transaction by ID
- Conflict detection — duplicate detection with health score 0–100
- GDPR tombstoning — compliant deletion with forensic trail
- Full-text search — SQLite FTS5 keyword search
- Confidence decay — exponential half-life
c = c₀ · 2^(-t/30) - LangGraph checkpointer — drop-in replacement for
MemorySaver - LangChain chat history —
BaseChatMessageHistoryimplementation - Mem0 sidecar — semantic search via Mem0
- SuperMemory sidecar — semantic search via SuperMemory
- MCP server — Claude Desktop / Cursor / Windsurf integration
Install
# Core SDK
pip install notmemory
# With LangChain / LangGraph
pip install "notmemory[langchain]"
# With Mem0 semantic search
pip install "notmemory[mem0]" mem0ai
# With MCP server
pip install "notmemory[mcp]"
# Everything
pip install "notmemory[langchain,mcp]" mem0ai
Quickstart
import asyncio
from notmemory import AgentMemory, MemoryConfig
async def main():
async with AgentMemory() as memory:
# Store a memory
entry = await memory.retain(
bank_id="facts",
content={"fact": "The Eiffel Tower is in Paris"},
source="user",
)
print(f"Stored: {entry.id}")
# Search memory
result = await memory.recall(
bank_id="facts",
query="Eiffel Tower",
)
for e in result.entries:
print(e.content)
# Rollback a bad write
await memory.rollback(entry.transaction_id)
# GDPR delete entire bank
await memory.forget("facts")
asyncio.run(main())
Core API
AgentMemory
from notmemory import AgentMemory, MemoryConfig
config = MemoryConfig(db_url="sqlite+aiosqlite:///./myagent.db")
async with AgentMemory(config=config) as memory:
...
| Method | Returns | Description |
|---|---|---|
retain(*, bank_id, content, ...) |
MemoryEntry |
Store with hash chain |
recall(*, bank_id, query, ...) |
RecallResult |
Keyword / temporal search |
rollback(transaction_id) |
RollbackResult |
Tombstone a transaction |
detect_conflicts(*, bank_id) |
ConflictReport |
Find duplicates, health score |
log_cycle_event(*, cycle_id, ...) |
CycleEvent |
DAG audit event |
get_audit_trail(*, cycle_id) |
AuditTrail |
Forensic reconstruction |
verify_integrity(bank_id) |
bool |
Verify SHA-256 hash chain |
forget(bank_id, *, entry_ids) |
int |
GDPR tombstone |
Confidence Decay
Memories lose confidence over time using exponential half-life decay: c(t) = c₀ · 2^(-t / half_life_days)
| Age | Confidence (from 1.0) |
|---|---|
| 0 days | 1.000 |
| 30 days | 0.500 |
| 60 days | 0.250 |
| 90 days | 0.125 |
from notmemory.memory.decay import decayed_confidence, filter_fresh
# Current decayed score
score = decayed_confidence(entry, half_life_days=30.0)
# Filter stale entries out, sort fresh ones by score
fresh = filter_fresh(entries, half_life_days=30.0, deprecation_threshold=0.05)
Adapters
LangChain / LangGraph
from notmemory.adapters.langchain import (
NotMemoryCheckpointer,
NotMemoryChatHistory,
)
# Drop-in for MemorySaver — adds hash-chained audit trail
checkpointer = NotMemoryCheckpointer()
graph = builder.compile(checkpointer=checkpointer)
async with checkpointer:
result = await graph.ainvoke(inputs, config)
# Chat history with GDPR tombstoning
history = NotMemoryChatHistory(session_id="user-42")
async with history:
await history.aadd_messages(messages)
msgs = await history.aget_messages()
Mem0
from notmemory.adapters.mem0 import NotMemoryMem0Adapter
import os
adapter = NotMemoryMem0Adapter(
mem0_api_key=os.environ["MEM0_API_KEY"],
user_id="agent-1",
)
async with adapter:
# Writes to SQLite + mirrors to Mem0
await adapter.retain(bank_id="facts", content={"fact": "..."})
# Keyword search (SQLite)
result = await adapter.recall(bank_id="facts", query="Paris")
# Semantic search (Mem0)
results = await adapter.semantic_recall("European capitals")
SuperMemory
from notmemory.adapters.supermemory import NotMemorySuperMemoryAdapter
import os
adapter = NotMemorySuperMemoryAdapter(
api_key=os.environ["SUPERMEMORY_API_KEY"],
user_id="agent-1",
)
async with adapter:
await adapter.retain(bank_id="facts", content={"fact": "..."})
results = await adapter.semantic_recall("European capitals")
MCP Server (Claude Desktop / Cursor / Windsurf)
Add to claude_desktop_config.json:
{
"mcpServers": {
"notmemory": {
"command": "python",
"args": ["-m", "notmemory.adapters.mcp"],
"env": {
"NOTMEMORY_DB_URL": "sqlite+aiosqlite:///./notmemory.db"
}
}
}
}
Available tools: notmemory_retain, notmemory_recall,
notmemory_rollback, notmemory_forget, notmemory_health
Configuration
from notmemory import MemoryConfig
from notmemory.core.config import HashChainConfig, RetentionConfig
config = MemoryConfig(
storage="sqlite",
db_url="sqlite+aiosqlite:///./agent.db",
hash_chain=HashChainConfig(
enabled=True,
algorithm="sha256",
),
retention=RetentionConfig(
confidence_half_life_days=30.0,
deprecation_threshold=0.05,
),
)
Project Structure
notmemory/ ├── core/ │ ├── agent_memory.py # AgentMemory — main interface │ ├── config.py # MemoryConfig (Pydantic v2) │ └── exceptions.py # exception hierarchy ├── memory/ │ ├── models.py # domain models │ └── decay.py # confidence decay ├── storage/ │ └── backends/ │ ├── base.py # abstract storage interface │ └── sqlite.py # SQLite + FTS5 + hash chaining └── adapters/ ├── base.py # BaseAdapter abstract class ├── langchain.py # LangChain / LangGraph ├── mem0.py # Mem0 sync-layer ├── supermemory.py # SuperMemory sync-layer └── mcp.py # MCP server
Development
git clone https://github.com/notmemory/notmemory
cd notmemory
python -m venv .venv
# Windows:
.venv\Scripts\Activate.ps1
# macOS/Linux:
source .venv/bin/activate
pip install -e ".[dev,langchain,mcp]" mem0ai
# Before every commit
ruff check . --fix
ruff format .
pytest --tb=short
Contributing
- Fork the repo
- Branch from
develop:git checkout -b feature/your-feature - Make changes, run
ruff+pytest - Open PR targeting
develop - CI must be green on Python 3.11, 3.12, 3.13
License
MIT — see LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file notmemory-0.1.0.tar.gz.
File metadata
- Download URL: notmemory-0.1.0.tar.gz
- Upload date:
- Size: 34.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3f31d96291ba60fe8411293edf1d96af33eb9417c91084696b1d36e00a32a672
|
|
| MD5 |
e1b0e1088be96b6c17ee162333fdeceb
|
|
| BLAKE2b-256 |
8a8b5ea629b44cbc865f794d897aba53bff33727c7a39b227b90cb53449c5c11
|
Provenance
The following attestation bundles were made for notmemory-0.1.0.tar.gz:
Publisher:
release.yml on notmemory/notmemory
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
notmemory-0.1.0.tar.gz -
Subject digest:
3f31d96291ba60fe8411293edf1d96af33eb9417c91084696b1d36e00a32a672 - Sigstore transparency entry: 1778624513
- Sigstore integration time:
-
Permalink:
notmemory/notmemory@4702c23df6bf905bc2fd424fed4460599eaaed8a -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/notmemory
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@4702c23df6bf905bc2fd424fed4460599eaaed8a -
Trigger Event:
push
-
Statement type:
File details
Details for the file notmemory-0.1.0-py3-none-any.whl.
File metadata
- Download URL: notmemory-0.1.0-py3-none-any.whl
- Upload date:
- Size: 34.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
833d7fc859ce2017aecb4aebdf54913e1787afc2426ad347337924064a028ee0
|
|
| MD5 |
9edd1225cd36b9d7016166c4323d6b3e
|
|
| BLAKE2b-256 |
cacc945bfbb97cac352706084ef3a95d48c137a12159fcce5241625b42dc48bd
|
Provenance
The following attestation bundles were made for notmemory-0.1.0-py3-none-any.whl:
Publisher:
release.yml on notmemory/notmemory
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
notmemory-0.1.0-py3-none-any.whl -
Subject digest:
833d7fc859ce2017aecb4aebdf54913e1787afc2426ad347337924064a028ee0 - Sigstore transparency entry: 1778624628
- Sigstore integration time:
-
Permalink:
notmemory/notmemory@4702c23df6bf905bc2fd424fed4460599eaaed8a -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/notmemory
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@4702c23df6bf905bc2fd424fed4460599eaaed8a -
Trigger Event:
push
-
Statement type: