> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/nullclaw/nullclaw/llms.txt
> Use this file to discover all available pages before exploring further.

# Memory Tools

> Persistent memory with hybrid search and vector retrieval

# Memory Tools

NullClaw provides four memory tools that enable agents to store and retrieve long-term facts:

* `memory_store` — Persist facts to long-term memory
* `memory_recall` — Search memory with hybrid retrieval
* `memory_list` — List all stored memories
* `memory_forget` — Delete memory entries

Memory tools support:

* **Multiple backends** — SQLite, Markdown, None
* **Hybrid search** — BM25 + vector similarity with RRF merge
* **Temporal decay** — Recent memories weighted higher
* **MMR diversification** — Avoid redundant results
* **Vector sync** — Automatic embedding sync to vector store

## Memory Architecture

### Memory Backend

Memory tools operate on a pluggable `Memory` backend:

```zig theme={null}
pub const Memory = struct {
    ptr: *anyopaque,
    vtable: *const VTable,

    pub const VTable = struct {
        store: *const fn (ptr: *anyopaque, key: []const u8, content: []const u8, category: MemoryCategory, metadata: ?[]const u8) anyerror!void,
        recall: *const fn (ptr: *anyopaque, allocator: Allocator, query: []const u8, limit: usize, category: ?MemoryCategory) anyerror![]MemoryEntry,
        list: *const fn (ptr: *anyopaque, allocator: Allocator, category: ?MemoryCategory) anyerror![]MemoryEntry,
        forget: *const fn (ptr: *anyopaque, key: []const u8) anyerror!bool,
    };
};
```

### Memory Categories

Memories are organized into three categories:

```zig theme={null}
pub const MemoryCategory = enum {
    core,         // Stable facts and preferences
    daily,        // Session notes and temporary context
    conversation, // Important conversation snippets
};
```

**Usage guidelines:**

* `core` — User preferences, stable facts (e.g., "User prefers Zig")
* `daily` — Temporary session notes (e.g., "Working on feature X today")
* `conversation` — Key conversation excerpts (use sparingly)

<Warning>
  Do not store routine greetings or every chat message in memory. Only store important facts and decisions.
</Warning>

### Hybrid Retrieval Pipeline

When a `MemoryRuntime` is available, `memory_recall` uses a sophisticated retrieval pipeline:

1. **BM25 full-text search** — Exact keyword matching
2. **Vector similarity search** — Semantic similarity via embeddings
3. **RRF merge** — Reciprocal Rank Fusion combines rankings
4. **Temporal decay** — Recent memories boosted
5. **MMR diversification** — Remove redundant results

**Without MemoryRuntime:**\
Falls back to raw `mem.recall()` (BM25 only)

***

## memory\_store

Persist facts to long-term memory with automatic vector sync.

### Parameters

<ParamField path="key" type="string" required>
  Unique key for this memory (used for updates and deletion)
</ParamField>

<ParamField path="content" type="string" required>
  The information to remember
</ParamField>

<ParamField path="category" type="string" default="core">
  Memory category: `core`, `daily`, or `conversation`
</ParamField>

### Configuration

```zig theme={null}
const mst = try allocator.create(memory_store.MemoryStoreTool);
mst.* = .{
    .memory = memory_backend,  // Memory vtable
    .mem_rt = &memory_runtime, // Optional MemoryRuntime for vector sync
};
```

### Usage

**Store a core fact:**

```json theme={null}
{
  "tool": "memory_store",
  "key": "user_language",
  "content": "User prefers Zig programming language",
  "category": "core"
}
```

**Response:**

```
Stored memory: user_language (core)
```

**Store a daily note:**

```json theme={null}
{
  "tool": "memory_store",
  "key": "daily_2026_03_01",
  "content": "Working on NullClaw documentation today. Focus on tools reference.",
  "category": "daily"
}
```

**Response:**

```
Stored memory: daily_2026_03_01 (daily)
```

### Vector Sync

When `mem_rt` is configured, stored memories are automatically:

1. Embedded via configured embedding provider
2. Upserted to vector store (e.g., Qdrant, Chroma)
3. Available for semantic search in `memory_recall`

Sync is best-effort and does not block on failure.

### Duplicate Keys

Storing with an existing key **overwrites** the previous content:

```json theme={null}
{
  "tool": "memory_store",
  "key": "status",
  "content": "Starting project"
}
```

Later:

```json theme={null}
{
  "tool": "memory_store",
  "key": "status",
  "content": "Project completed"
}
```

`memory_recall` now returns "Project completed" for key "status".

### Source

`src/tools/memory_store.zig:12-61`

***

## memory\_recall

Search long-term memory for relevant facts using hybrid retrieval.

### Parameters

<ParamField path="query" type="string" required>
  Keywords or phrase to search for in memory
</ParamField>

<ParamField path="limit" type="integer" default={5}>
  Maximum results to return (1-100)
</ParamField>

### Configuration

```zig theme={null}
const mrt = try allocator.create(memory_recall.MemoryRecallTool);
mrt.* = .{
    .memory = memory_backend,  // Memory vtable
    .mem_rt = &memory_runtime, // Optional MemoryRuntime for hybrid search
};
```

### Usage

**Search for facts about Zig:**

```json theme={null}
{
  "tool": "memory_recall",
  "query": "Zig programming",
  "limit": 3
}
```

**Response:**

```
Found 2 memories:
1. [user_language] (core): User prefers Zig programming language
2. [daily_2026_03_01] (daily): Working on NullClaw documentation today. Focus on tools reference.
```

**No results:**

```json theme={null}
{
  "tool": "memory_recall",
  "query": "Python"
}
```

**Response:**

```
No memories found matching: Python
```

### Hybrid Search Results

When `mem_rt` is configured, results include hybrid scores:

```
Found 3 memories:
1. [user_language] (bm25 0.85): User prefers Zig programming language
2. [project_notes] (vector 0.72): Working on a Zig-based AI agent
3. [learning_log] (bm25 0.68): Started learning Zig last week
```

Scores indicate:

* `bm25` — Exact keyword match
* `vector` — Semantic similarity
* Higher score = better match

### Internal Memory Filtering

Memories with keys starting with `__bootstrap.` are filtered from results:

```zig theme={null}
// Stored internally during bootstrap:
"__bootstrap.prompt.SOUL.md" = "internal system prompt"

// User memory:
"user_pref" = "loves Zig"
```

`memory_recall("Zig")` returns only `user_pref`, not bootstrap internals.

### Source

`src/tools/memory_recall.zig:14-163`

***

## memory\_list

List all stored memories (optionally filtered by category).

### Parameters

<ParamField path="category" type="string">
  Filter by category: `core`, `daily`, or `conversation`
</ParamField>

### Configuration

```zig theme={null}
const mlt = try allocator.create(memory_list.MemoryListTool);
mlt.* = .{
    .memory = memory_backend,
};
```

### Usage

**List all memories:**

```json theme={null}
{
  "tool": "memory_list"
}
```

**Response:**

```
Found 5 memories:
1. [user_language] (core): User prefers Zig programming language
2. [daily_2026_03_01] (daily): Working on NullClaw documentation today
3. [status] (core): Project completed
4. [meeting_notes] (conversation): Discussed feature roadmap
5. [timezone] (core): User timezone is UTC-8
```

**List core memories only:**

```json theme={null}
{
  "tool": "memory_list",
  "category": "core"
}
```

**Response:**

```
Found 3 memories:
1. [user_language] (core): User prefers Zig programming language
2. [status] (core): Project completed
3. [timezone] (core): User timezone is UTC-8
```

### Source

`src/tools/memory_list.zig`

***

## memory\_forget

Delete a memory by key with automatic vector store cleanup.

### Parameters

<ParamField path="key" type="string" required>
  The key of the memory to forget
</ParamField>

### Configuration

```zig theme={null}
const mft = try allocator.create(memory_forget.MemoryForgetTool);
mft.* = .{
    .memory = memory_backend,
    .mem_rt = &memory_runtime, // Optional MemoryRuntime for vector cleanup
};
```

### Usage

**Delete a memory:**

```json theme={null}
{
  "tool": "memory_forget",
  "key": "outdated_fact"
}
```

**Response:**

```
Forgot memory: outdated_fact
```

**Key not found:**

```json theme={null}
{
  "tool": "memory_forget",
  "key": "nonexistent"
}
```

**Response:**

```
No memory found with key: nonexistent
```

### Vector Store Cleanup

When `mem_rt` is configured, `memory_forget` automatically:

1. Deletes the memory from the primary backend (SQLite/Markdown)
2. Deletes the corresponding vector embedding from the vector store

Cleanup is best-effort and does not block on failure.

### Source

`src/tools/memory_forget.zig:11-57`

***

## Memory Backends

### SQLite Backend

```zig theme={null}
var sqlite_mem = try mem_root.SqliteMemory.init(allocator, "memory.db");
defer sqlite_mem.deinit();
const mem = sqlite_mem.memory();
```

**Features:**

* BM25 full-text search via SQLite FTS5
* Category filtering
* Metadata support
* Persistent across restarts

### Markdown Backend

```zig theme={null}
var md_mem = try mem_root.MarkdownMemory.init(allocator, "memory.md");
defer md_mem.deinit();
const mem = md_mem.memory();
```

**Features:**

* Human-readable markdown file
* Simple key-value structure
* Substring search (not BM25)
* Great for debugging

### None Backend (No-Op)

```zig theme={null}
var none_mem = mem_root.NoneMemory.init();
defer none_mem.deinit();
const mem = none_mem.memory();
```

**Features:**

* All operations are no-ops
* Useful for testing without persistence
* Store/recall return empty results

***

## Memory Runtime

For hybrid search with vector embeddings, configure a `MemoryRuntime`:

```zig theme={null}
const mem_rt = try mem_root.MemoryRuntime.init(allocator, .{
    .memory = memory_backend,
    .embedding_provider = embedding_provider,
    .vector_store = vector_store,
    .k = 5,  // Top-K results per method
    .bm25_weight = 0.6,
    .vector_weight = 0.4,
    .temporal_decay_days = 30,
    .mmr_lambda = 0.7,
});
defer mem_rt.deinit();
```

Bind to tools:

```zig theme={null}
root.bindMemoryRuntime(tools, &mem_rt);
```

### Retrieval Parameters

* `k` — Number of candidates from each method (BM25, vector)
* `bm25_weight` — Weight for BM25 scores in RRF merge (default: 0.6)
* `vector_weight` — Weight for vector scores in RRF merge (default: 0.4)
* `temporal_decay_days` — Days after which memory score decays by 50% (default: 30)
* `mmr_lambda` — MMR diversification parameter (0 = max diversity, 1 = max relevance)

***

## Use Cases

### User Preferences

```json theme={null}
{
  "tool": "memory_store",
  "key": "user_timezone",
  "content": "User is in UTC-8 (Pacific Time)",
  "category": "core"
}
```

### Project Context

```json theme={null}
{
  "tool": "memory_store",
  "key": "project_status",
  "content": "Building NullClaw tools documentation. Completed file operations and shell pages.",
  "category": "daily"
}
```

### Conversation Highlights

```json theme={null}
{
  "tool": "memory_store",
  "key": "feature_request_2026_03_01",
  "content": "User requested async tool execution with callback support",
  "category": "conversation"
}
```

### Recall During Task

```json theme={null}
{
  "tool": "memory_recall",
  "query": "user timezone"
}
```

**Response:**

```
Found 1 memory:
1. [user_timezone] (core): User is in UTC-8 (Pacific Time)
```

***

## Testing

Run memory tool tests:

```bash theme={null}
zig build test --summary all
```

Tests cover:

* Store/recall/list/forget operations
* Category filtering
* Hybrid search with MemoryRuntime
* Internal memory filtering
* No backend (NoneMemory) behavior
