docs: refresh README, crate READMEs and reference docs; fact-check every claim #22

Merged
osobh merged 21 commits from docs/readme-refresh into main 2026-09-28 17:24:26 +00:00
Showing only changes of commit 4d1a43fd7a - Show all commits
+496
View File
@@ -0,0 +1,496 @@
# Agent memory (`clawhdf5-agent`)
`clawhdf5-agent` is a persistent, searchable memory store for AI agents,
built on clawhdf5's HDF5 writer: records (text, embedding, source channel,
timestamp, session, tags), sessions and a knowledge graph in one `.h5` file,
with a write-ahead log beside it. This page is the long form of the agent
part of the [README](../README.md); every number on it comes from
[BENCHMARKS.md](../BENCHMARKS.md), where the commands and machines are.
- [Quick start](#quick-start) · [Search](#search) · [Signed checkpoints](#signed-checkpoints)
- [Architecture](#architecture) · [Modules](#modules) · [Library components](#library-components)
- [Performance](#performance) · [LongMemEval](#longmemeval-retrieval-recall) · [Footprint](#memory-footprint)
- [Feature flags and settings](#feature-flags-and-settings) · [File schema](#file-schema)
- [CLI](#cli) · [Migrating from SQLite](#migrating-from-sqlite) · [Research foundation](#research-foundation)
Integration status: ClawBrainHub's CLI uses this crate's `bm25::BM25Index`;
no agent framework uses the store. clawhdf5 is **not** an OpenClaw memory
plugin ([openclaw.md](openclaw.md)), and ZeroClaw does not use it.
## Quick start
```toml
[dependencies]
clawhdf5-agent = { git = "https://git.redclaw.dev/quantumclaw/clawhdf5" } # not on crates.io yet
```
```rust
use clawhdf5_agent::{AgentMemory, HDF5Memory, MemoryConfig, MemoryEntry, SearchOptions};
// A new store: 384-dim embeddings (float16 on disk and an int8 HNSW index by default).
let mut memory = HDF5Memory::create(MemoryConfig::new("agent.h5".into(), "my-agent", 384))?;
memory.save(MemoryEntry {
chunk: "User prefers dark mode and vim keybindings.".into(),
embedding: embed("User prefers dark mode and vim keybindings."), // your embedder
source_channel: "chat".into(),
timestamp: now,
session_id: "session-001".into(),
tags: "preference".into(),
})?;
// Hybrid search: HNSW vector + BM25 keyword, fused 0.4 / 0.6 (the measured default).
let query = embed("what editor does the user like?");
for r in memory.search(&query, "editor preferences", &SearchOptions::new(5)) {
println!("[{:.3}] {}", r.score, r.chunk);
}
memory.flush_wal()?; // checkpoint the WAL into agent.h5
```
clawhdf5 stores embeddings; it does not compute them. Any dimension works,
fixed when the store is created. `HDF5Memory::open(path)` reopens a store
(holding its single-writer lock); `HDF5Memory::open_read_only(path)` gives a
lock-free point-in-time view.
## Search
`HDF5Memory::search(query_emb, text, &SearchOptions)` is the full search
path; `hybrid_search(query_emb, text, vector_weight, keyword_weight, k)` and
`hybrid_search_with` are thin wrappers over it.
```rust
use clawhdf5_agent::confidence::ConfidenceConfig;
use clawhdf5_agent::reranker::ReRankConfig;
// Only memories from these source channels; still a full page of k results.
let work = memory.search(&query, "deadline", &SearchOptions::new(5).with_sources(["slack", "email"]));
// Re-rank (relevance, recency, source authority, activation), then drop
// low-confidence results: the pipeline ClawhdfBackend runs.
let careful = memory.search(
&query,
"user preferences",
&SearchOptions::new(5)
.with_rerank(ReRankConfig::default())
.with_confidence(ConfidenceConfig::default()),
);
```
The source-channel filter is applied before ranking: an exact scan of the
allowed records whenever that is cheaper than the index would be, and as
the fallback when the index returns a short pool. Hebbian activation boosts
are persisted by the next checkpoint (or on drop), not per query; search
never writes the store.
## Signed checkpoints
```rust
use clawhdf5_agent::signing;
let key = signing::generate_key(); // keep the secret key; publish the public one
let public = key.verifying_key();
memory.set_signing_key(key); // never written to disk
memory.flush_wal()?; // this checkpoint is signed
let report = HDF5Memory::verify(std::path::Path::new("agent.h5"), &public)?;
assert!(report.is_valid()); // report.changed_records names edited records
```
The Ed25519 signature covers every record (text, embedding as stored,
channel, timestamp, session, tags, deleted flag, activation) through a
SHA-256 Merkle tree, plus the store's settings, sessions and knowledge
graph, so a change made with any tool is caught and located. It covers
checkpoints, not saves still in the WAL (`report.wal_entries_unsigned`
counts those). A signed store refuses to checkpoint without the key
(`MemoryError::SigningKeyRequired`). CLI: `clawhdf5 keygen`,
`--signing-key <file>` on writing commands, and `verify --public-key`.
Signing adds about 20% to a checkpoint and 32 bytes per record to the file
([BENCHMARKS.md § Signed checkpoints](../BENCHMARKS.md#signed-checkpoints)).
## Architecture
```
┌─────────────────┐
│ Agent Query │
└────────┬────────┘
│
┌─────────────────▼──────────────────┐
│ HDF5Memory::search │
│ optional source-channel filter │
│ HNSW vector + BM25 keyword │
│ weighted fusion (0.4 / 0.6) │
│ × √(Hebbian activation) │
└─────────────────┬──────────────────┘
│ opt-in (SearchOptions);
│ ClawhdfBackend turns both on
┌─────────────────▼──────────────────┐
│ Multi-factor re-ranking │
│ relevance · recency · authority · │
│ activation │
├────────────────────────────────────┤
│ Confidence rejection │
│ (suppress bad matches) │
└─────────────────┬──────────────────┘
│
┌────────────────────────────▼────────────────────────────┐
│ In memory │
│ cache (embeddings) · BM25 index · HNSW index │
│ provenance ledger + anomaly alerts (session-scoped) │
└────────────────────────────┬────────────────────────────┘
│ WAL append; checkpoint
┌────────────────────────────▼────────────────────────────┐
│ agent_memory.h5 /meta · /memory · /sessions · │
│ /knowledge_graph │
│ agent_memory.h5.wal chained-CRC write-ahead log │
│ agent_memory.h5.ann HNSW graph (derived, rebuildable) │
│ agent_memory.h5.lock single-writer lock │
└─────────────────────────────────────────────────────────┘
```
**Durability.** Every WAL entry carries a CRC32 chained to the previous
entry's, so a corrupted, reordered, duplicated or spliced entry stops replay
instead of loading bad data. Each checkpoint records a WAL mark in `/meta`,
so a crash between a checkpoint and the WAL truncate never applies an entry
twice. Checkpoints and snapshots are made durable as a unit (temp file
synced, renamed, directory synced). **Individual WAL appends are not
fsynced** (a latency trade-off): saves since the last checkpoint can be lost
on power failure or a kernel panic, not on a process crash. An unreadable
WAL is quarantined to `<store>.h5.wal.corrupt-<ts>` rather than blocking
`open()`.
**Single writer.** `create`/`open` take an exclusive advisory lock on
`<store>.h5.lock`; a second opener gets `MemoryError::Locked`.
**Write bookkeeping.** `save`/`save_batch`/`save_or_update` run each write
through an in-memory (session-scoped, not persisted) provenance ledger — an
unkeyed content hash per record, for detecting accidental corruption, not
tampering — and a write-anomaly detector (rate limits, injection patterns,
source distribution). Alerts never block a save; drain them with
`take_anomaly_alerts`. The source classification is inferred from the
caller's `source_channel` string, a heuristic, not an authenticated trust
boundary.
## Modules
| Module | What it does |
|--------|-------------|
| `hybrid` | Vector + BM25 fusion: min-max-normalised weighted sum, vector 0.4 / keyword 0.6 by default (`hybrid::DEFAULT_FUSION`, tuned on LongMemEval); RRF via `Fusion::Rrf` / `hybrid_search_with` (measured worse) |
| `reranker` | Re-ranking by retrieval relevance (leads, weight 1.0), recency, source authority, activation. Opt-in via `SearchOptions::with_rerank`; on in `ClawhdfBackend` |
| `confidence` | Low-confidence rejection. Opt-in via `SearchOptions::with_confidence`; on in `ClawhdfBackend` |
| `bm25` | Incremental Okapi BM25 index kept for the life of the store; optional stemming |
| `signing` | Ed25519-signed checkpoints (above) |
| `wal` | Write-ahead log, format v4, chained CRC32 per entry; reads v2 and v3 (v1 only through the one-time migration in `open`) |
| `knowledge` | Entity/relation graph: BFS, spreading activation, fuzzy (Levenshtein) entity resolution |
| `consolidation` | Three tiers (Working → Episodic → Semantic): importance, novelty, time decay |
| `temporal` | Sorted timestamp index, session DAG, entity timeline |
| `multimodal` | Cross-modal search over text/image/audio/video embeddings (exact scan) |
| `provenance`, `anomaly` | Session-scoped write bookkeeping (above) |
| `openclaw` | `ClawhdfBackend`, a Markdown-oriented backend (below). Named for OpenClaw, but **not an OpenClaw plugin** ([openclaw.md](openclaw.md)) |
| `vector_search` | Flat cosine search paths: pre-normed, SIMD, BLAS, GPU, parallel |
| `ivf` / `pq` | Standalone IVF and IVF-PQ indexes; not used by `HDF5Memory`, whose index is HNSW |
| `query_expand`, `entity_extract` | Synonym/acronym/temporal query expansion; rule-based entity extraction into the graph |
| `memory_strategy`, `decision_gate` | When to save: save-every, semantic shift, user correction; trivial/substantive classification |
| `ephemeral` | In-memory TTL/LFU working tier |
| `async_memory` | Tokio wrapper over the store (`async` feature) |
## Library components
The consolidation tiers, the graph algorithms and the temporal and
multi-modal indexes are components you drive directly; the store persists
the records, sessions and graph they work over.
```rust
use clawhdf5_agent::knowledge::KnowledgeCache;
let mut kg = KnowledgeCache::new();
let alice = kg.add_entity("Alice", "person", -1);
let bob = kg.add_entity("Bob", "person", -1);
let acme = kg.add_entity("Acme Corp", "company", -1);
kg.add_relation(alice, acme, "works_at", 1.0);
kg.add_relation(alice, bob, "manages", 0.8);
let neighbors = kg.bfs_neighbors(alice, 2); // 2-hop neighbourhood
let activated = kg.spreading_activation(&[alice], 0.5, 0.01, 5); // related entities
let (id, created) = kg.resolve_or_create("alice", "person", -1, 2); // fuzzy (Levenshtein <= 2)
assert_eq!((id, created), (alice, false));
```
```rust
use clawhdf5_agent::consolidation::{ConsolidationConfig, ConsolidationEngine, UntrustedSource};
let mut engine = ConsolidationEngine::new(ConsolidationConfig {
working_capacity: 100,
..Default::default()
});
let id = engine.add_memory("User prefers dark mode".into(), embed("dark mode"), UntrustedSource::User, now);
engine.access_memory(id, now + 60.0); // reactivates it
engine.consolidate(now + 3600.0); // promote (Working -> Episodic -> Semantic) and evict
let stats = engine.get_stats();
println!("working {} episodic {} semantic {}", stats.working_count, stats.episodic_count, stats.semantic_count);
```
System and correction sources get elevated importance and go through a
separate entry point, `add_trusted_memory(.., TrustedSource::System, ..)`,
so untrusted content cannot claim them.
```rust
use clawhdf5_agent::temporal::TemporalIndex;
let mut index = TemporalIndex::new();
index.insert(1, 1_700_000_000.0);
index.insert(2, 1_700_003_600.0); // an hour later
let in_range = index.range_query(1_700_000_000.0, 1_700_010_800.0);
let recent = index.latest(10);
```
### Markdown backend
`ClawhdfBackend` ingests Markdown by section and searches it with the full
pipeline. It is a library API, not an OpenClaw plugin.
```rust
use clawhdf5_agent::openclaw::{ClawhdfBackend, MemoryBackend};
let mut backend = ClawhdfBackend::create(std::path::Path::new("memory.h5"), 384)?;
let md = std::fs::read_to_string("MEMORY.md")?;
let sections = backend.ingest_markdown("MEMORY.md", &md)?; // one record per heading
for r in backend.search("dark mode", &embed("dark mode"), 5) {
println!("[{:.3}] {} ({})", r.score, r.text, r.path);
}
let exported = backend.export_markdown("MEMORY.md")?;
```
Limits: ingested sections carry no embedding, so their search is
keyword-only unless you save records with vectors through `save_entry`;
ingesting a file again adds its sections again; `export_markdown` writes
every heading as `##`, so it is not a lossless round trip.
## Performance
Unless marked otherwise, measured 2026-09-24 on tank (AMD Ryzen 7 7800X3D,
8C/16T), commit 5c8323c, 384-dim embeddings; commands in
[BENCHMARKS.md](../BENCHMARKS.md).
**HNSW (the default vector stage)** — `search_harness`, clustered data,
N = 100K, M = 16, ef_construction = 64, ef = 64, recall against an exact scan
([§ Quantising the index copy](../BENCHMARKS.md#quantising-the-index-copy-quantized_index)):
| index | recall@10 | QPS | build |
|---|---:|---:|---:|
| `f32` | 0.9945 | 13 399 | 3.2 s |
| `i8` + exact re-score (**default for new stores**) | 0.9940 | **21 848** | **1.8 s** |
A paired comparison (medians of alternating runs, same binary), not re-run
on 2026-09-24: a single `f32` run that day measured recall 0.9945, 19 001
QPS and a 2.7 s build, so the 1.63x ratio has not been re-checked. On a
Raspberry Pi 5 (NEON `SDOT`) the int8 index is 1.18x the `f32` QPS at equal
recall. Before the v2.4.0 neighbour-selection fix, recall@10 at 100K was
0.31.
**Operations:**
| Operation | Latency | Scale |
|-----------|---------|-------|
| `hybrid_search` p50 | 0.07 ms / 0.49 ms / 4.69 ms | 1K / 10K / 100K records |
| BM25 keyword search | 20.4 µs | 1K records |
| Knowledge graph BFS | 23.1 µs | 1K entities |
| Spreading activation | 10.1 µs | 100 entities |
| Temporal range query | 622 ns | 10K timestamps |
| Consolidation cycle | 115.2 µs | 1K records |
| Cross-modal search (exact scan, 2 embeddings per record) | 842.0 µs / 8.44 ms | 1K / 10K records |
| Memory write (WAL append) | 26.1 µs | per record |
`float16` stores (the default) add about 2 µs per write for rounding
([§ Write Path](../BENCHMARKS.md#write-path)).
**Brute-force and IVF** (Criterion; not used by `HDF5Memory`):
| Scale | Flat | IVF (nprobe=10) | IVF-PQ |
|-------|------|-----------------|--------|
| 1K | 47.4 µs | — | — |
| 10K | 500.5 µs | 24.8 µs | — |
| 100K | 6.58 ms | 592 µs | 869 µs |
No comparison with MemX is made: its published figure is end-to-end and
ours is one component ([BENCHMARKS.md](../BENCHMARKS.md#comparison-to-memx-arxiv260316171)).
**Consolidation** — 1,000 records (10 signal + 990 noise),
`working_capacity = 100`: the store goes from 1,000 to 100 records with
Hit@1 on the signal records staying at 100%, and search from 2.22 ms to
0.24 ms ([§ Consolidation Efficiency](../BENCHMARKS.md#consolidation-efficiency)).
## LongMemEval retrieval recall
Full `longmemeval_s` haystack, all 500 questions (47.7 sessions and 493.5
turns each; 4.0% of sessions are evidence), real `all-MiniLM-L6-v2`
embeddings, k = 10. Re-run 2026-09-27 on tank; the headline reproduced
exactly ([§ LongMemEval Results](../BENCHMARKS.md#longmemeval-results)):
| Mode | Turn-level Hit@5 | Session-level Hit@5 |
|------|------------------|---------------------|
| BM25 only | 75.0% | 93.6% |
| Vector only (MiniLM) | 71.8% | 94.2% |
| Hybrid 0.4 / 0.6 (default) | **81.4%** | **96.8%** |
This is **retrieval recall** (did a gold turn appear in the top k), not the
official LongMemEval QA accuracy; the two are not comparable. A weight sweep
found the old 0.7 / 0.3 default strictly dominated by 0.4 / 0.6, the default
since v2.5.0; use 0.3 / 0.7 if rank-1 precision matters most. Earlier
session-level figures of 100% and a claimed win over MemX were retracted
([BENCHMARKS.md](../BENCHMARKS.md#retracted-session-level-recall-and-the-memx-comparison)).
The benchmark's vector stage needs `clawhdf5-bench`'s `embeddings` feature.
## Memory footprint
**On disk** — `float16` embeddings (the default), 200-character synthetic
text, `footprint_bench`: 810.4 KB at 1K records, 7.8 MB at 10K, 76.7 MB at
100K (803–829 bytes per record). The synthetic text is far more repetitive
than real text (40 distinct strings, deflated), so real records will be
larger; the embeddings alone are 768 B per record. On the same data, 100K ×
384 takes 80.8 MiB as `float16` and 154.0 MiB as `f32`
([§ Memory Footprint](../BENCHMARKS.md#memory-footprint-1)).
**In memory** — a store reopened from disk, counting allocator
([§ Memory footprint](../BENCHMARKS.md#memory-footprint)):
| Records | Raw vectors | `f32` index | `i8` index (default) |
|---------|-------------|-------------|----------------------|
| 1K | 1 MiB | 4 MiB (2.40x) | 2 MiB (1.64x) |
| 10K | 15 MiB | 44 MiB (3.03x) | 27 MiB (1.81x) |
| 100K | 146 MiB | 399 MiB (2.72x) | 256 MiB (1.74x) |
The `f32` column was re-measured on 2026-09-24; the `i8` column was not.
## Feature flags and settings
| `clawhdf5-agent` flag | Default | Description |
|------|---------|-------------|
| `float16` | **yes** | Half-precision cosine kernel. Half-precision *storage* is the `MemoryConfig::float16` setting, not this feature |
| `hnsw` | **yes** | HNSW index for the vector stage (`clawhdf5-ann`); without it, an exact linear scan |
| `parallel` | **yes** | Parallel HNSW bulk build (identical graph) and Rayon search strategies |
| `zstd` | no | Zstd instead of deflate for embeddings when `MemoryConfig::compression` is on (links libzstd) |
| `fast-math` / `openblas` / `accelerate` | no | BLAS matrix-vector multiply (generic / OpenBLAS / Apple Accelerate) |
| `gpu` | no | GPU distance computation via wgpu (`clawhdf5-gpu`) |
| `async` | no | Tokio async wrapper with background flush |
For an exact linear scan: `--no-default-features --features float16`.
Settings stored in the file (`MemoryConfig`):
- `float16` (**on** for new stores): embeddings on disk as IEEE half
precision, rounded as they enter the cache so memory and file agree;
values must lie within ±65504. On LongMemEval with real MiniLM embeddings
every retrieval metric matches `f32`. Opt out with `float16 = false` or
`clawhdf5 create --f32`. Existing stores keep their setting.
- `quantized_index` (**on** for new stores): the HNSW index's copy of the
embeddings as `i8`, re-scored against the exact embeddings; see the table
above. Opt out with `quantized_index = false` or `create --f32-index`.
- `hnsw_m`, `hnsw_ef_construction`, `hnsw_ef_search`: 16 / 64 / scaled with
`k` by default.
- `compression` (off): deflate (or Zstd) for embeddings; text of 4 KiB or
more is always deflated.
- `wal_enabled` (on), `wal_max_entries`, `hebbian_boost`, `decay_factor`.
## File schema
```
agent_memory.h5
├── /meta (attributes)
│ ├── schema_version, edgehdf5_version (writer tag, kept for compatibility)
│ ├── agent_id, embedder, embedding_dim, chunk_size, overlap, created_at
│ ├── float16, compression, compression_level, compact_threshold,
│ │ hebbian_boost, decay_factor, wal_enabled, wal_max_entries
│ ├── quantized_index, hnsw_m, hnsw_ef_construction, hnsw_ef_search
│ ├── wal_applied_len, wal_applied_crc (WAL mark of the last checkpoint)
│ └── ann_generation (ties the .ann sidecar to this checkpoint)
├── /memory
│ ├── chunks: string[N]
│ ├── embeddings: f32[N × D], or f16 for a `float16` store (chunked)
│ ├── source_channel, session_ids, tags: string[N]
│ ├── timestamps: f64[N]
│ ├── tombstones: u8[N]
│ ├── norms: f32[N] (pre-computed L2)
│ └── activation_weights: f32[N] (Hebbian)
├── /sessions
│ ├── ids, channels, summaries: string[S]
│ ├── start_idxs, end_idxs: i64[S]
│ └── timestamps: f64[S]
├── /knowledge_graph
│ ├── entity_ids, entity_emb_idxs: i64[E]; entity_names, entity_types: string[E]
│ ├── relation_srcs, relation_tgts: i64[R]; relation_types: string[R]
│ ├── relation_weights: f32[R]; relation_ts: f64[R]
│ └── alias_strings: string[A]; alias_entity_ids: i64[A] (when aliases exist)
└── /integrity (signed stores: per-record hashes and the signed manifest)
```
A store is an ordinary HDF5 file: h5py, h5dump and `h5rs` read it (the
agent's `h5py_interop` test checks a whole store). Beside it:
`<store>.h5.wal`, `<store>.h5.ann` (HNSW graph; derived, safe to delete)
and `<store>.h5.lock`.
## CLI
`clawhdf5-cli` installs a binary named `clawhdf5`:
```bash
cargo install --path crates/clawhdf5-cli
clawhdf5 --path agent.h5 create --agent-id my-agent --dim 384 --wal
echo '{"chunk":"User prefers dark mode","embedding":[0.1, ...],"source_channel":"chat","timestamp":1700000000.0,"session_id":"s1","tags":"pref"}' \
| clawhdf5 --path agent.h5 save
clawhdf5 --path agent.h5 search --embedding '[0.1, ...]' --query 'dark mode preferences' \
--top-k 5 --vector-weight 0.4 --keyword-weight 0.6
clawhdf5 --path agent.h5 stats # also: recall <index>, export, agents-md, flush-wal
clawhdf5 --path agent.h5 snapshot backup.h5
clawhdf5 keygen --out signing.key # then --signing-key signing.key; verify --public-key <hex>
```
Output is JSON. The CLI's `search` defaults to weights 0.7 / 0.3, not the
library's 0.4 / 0.6, so pass them. `recall`, `stats`, `agents-md` and
`export` open the store read-only.
## Migrating from SQLite
```bash
cargo install --path crates/clawhdf5-migrate
clawhdf5-migrate --sqlite old.db --hdf5 memory.h5 --agent-id my-agent --embedder minilm
```
The output is an ordinary agent store, written through the agent's API. The
source must use the `memory_chunks` / `sessions` / `entities` / `relations`
layout (names configurable with `--*-table`); this is not ZeroClaw's schema,
and ZeroClaw does not use clawhdf5. What carries over:
| SQLite | Agent store |
|--------|-------------|
| `memory_chunks` | records (text, embedding, source channel, timestamp, session id, tags); rows with `deleted = 1` become deleted records, or are left out with `--skip-deleted` |
| `sessions` | sessions (id, start/end index, channel, summary, timestamp) |
| `entities`, `relations` | knowledge-graph entities and relations; entities get new ids and relations are re-pointed |
Records are written in `id` order and numbered from 0. Embeddings are
stored as float16 like any new store; `--f32` keeps full precision (and is
required for values beyond ±65504). The dimension is detected from the
first row unless `--embedding-dim` is given, and a row of another length is
an error, never truncated or padded; a source with no records needs
`--embedding-dim`. Every row is checked before the output is created.
`--incremental` adds only rows the store does not hold (records already in
it take the source's deleted flag). The tool reads the result back
read-only, compares it with the source (every row with `--validate-full`)
and checks that a migrated record is found by search; `--dry-run` only
counts rows. `clawhdf5-migrate` bundles SQLite, so it compiles C.
Older crate names: `rustyhdf5*` is now `clawhdf5*`, `edgehdf5-memory` is
`clawhdf5-agent`, and the `edgehdf5` CLI is `clawhdf5-cli`.
## Research foundation
The design draws on recent papers on agent memory:
| Paper | Idea | Module |
|-------|------|--------|
| MemX (2026) | Hybrid fusion + multi-factor re-ranking | `hybrid`, `reranker` |
| Graph-Native Cognitive Memory (2026) | Weighted, timestamped relations; entity timelines | `knowledge`, `temporal` |
| CraniMem (2026) | Bounded hippocampal memory | `consolidation` |
| D-MEM (2026) | Surprise-gated storage (as a novelty score) | `consolidation` |
| SYNAPSE (2025) | Spreading activation for recall | `knowledge` |
| RAGdb (2025) | Zero-dependency edge RAG | architecture |
| MemoryGraft (2025) | Memory poisoning attacks | `anomaly`, `provenance` |
| MemoryArena (2026) | Multi-session benchmark | `temporal` |
| AI Hippocampus (2026) | Memory taxonomy survey | overall design |