Agent memory.
- STATUS
- Shipped
- CRATE
- tenzro-agent
- ROOT
- {data_dir}/agent_memory/
- FUSION
- RRF k=60 (Cormack et al.)
Record shape
MemoryRecord {
id: UUID v4
agent_did: String
created_at_ms: i64
kind: Granted | Recalled | SelfNoted | Archived
source: Controller | Tool | Peer | Self_
text: String
embedding: Vec<f32>
metadata: serde_json::Value
da_pointer: Option<DaPointer> // Some when archived
}Two backends, one root
The vector backend (LanceVectorBackend) stores embeddings in a fixed-size float32 column at {root}/agent_memory_vector.lance. The text backend (TantivyTextBackend) provides BM25 search at {root}/agent_memory_text/ with agent_did and kind as STRING+FAST filters, and created_at_ms as I64+FAST for ordering.
Search modes
MemoryManager::recall() supports SearchModes::{VECTOR, TEXT, HYBRID}. HYBRID merges the two ranked lists with Reciprocal Rank Fusion at k=60 (Cormack et al.) and deduplicates by record id. Embeddings come from tenzro_model::TextEmbeddingRuntime — absent a runtime or model id, the call returns MemoryError::NoEmbedder.
Archive to DA
MemoryManager::archive() submits the canonical payload to a tenzro_storage::da::DaBackend. When the iroh resolver is bound at startup, that backend is IrohBlobsDaBackend; otherwise it falls back to InlineFallback. The on-tier rows are then replaced with kind=Archived plus the returned DaPointer so search results still find the row but the bulk content lives off-tier.
Wiring
AgentRuntime::set_memory_manager(Arc<MemoryManager>) -> bool is an idempotent set-once OnceLock. AgentRuntime::memory_manager() -> Option<&Arc<MemoryManager>> is the read path used by RPC, MCP, and A2A handlers.
Cross-surface coverage
# JSON-RPC
tenzro_memoryGrant | tenzro_memoryRecall | tenzro_memoryArchive | tenzro_listMemoryRecords
# MCP tools
memory_grant | memory_recall | memory_archive
# A2A skill
agent-memory
# CLI
tenzro memory grant | recall | archive | list