← Back to Plugins
Tools

Mnemosyne Openclaw

smfworks By smfworks 👁 169 views ▲ 0 votes

100% offline, local SQLite memory plugin for OpenClaw โ€” FTS5 full-text search, auto-capture, zero network dependency

GitHub

Install

npm install
npm

Configuration Example

{
  "plugins": {
    "slots": { "memory": "mnemosyne" },
    "entries": {
      "mnemosyne": {
        "enabled": true,
        "config": {
          "dbPath": "~/.openclaw/memory/mnemosyne.db",
          "ownerObserveOthers": true,
          "noisePatterns": [],
          "maxMessagesPerSession": 10000,
          "maxMemoriesPerSession": 1000,
          "enableFts": true
        }
      }
    },
    "allow": ["mnemosyne", "memory-core"]
  }
}

README

# Mnemosyne โ€” Offline Memory Plugin for OpenClaw

> **v1.1.0** โ€” FTS5 full-text search, crash resilience, and proper session-scoped tools.

## What It Is

**Mnemosyne** is a **100% offline, local-only** memory plugin for OpenClaw agents. It replaces cloud-dependent memory systems (e.g. Honcho) with a synchronous SQLite backend that lives inside the gateway process.

**Zero network. Zero API keys. Zero cloud. Native to OpenClaw.**

## What's New in v1.1

| Change | Before | After |
|--------|--------|-------|
| **Session context** | Tools wrote to ghost `"default_session"` โ€” agents never saw results | Tools receive real `sessionKey`/`agentId` from OpenClaw runtime |
| **Full-text search** | No cross-session search | `mnemosyne_search` with FTS5 + Porter stemming |
| **Crash resilience** | No crash recovery | `wal_checkpoint(TRUNCATE)` on every startup |
| **Error guards** | DB errors propagated raw | Retry on `SQLITE_BUSY` with exponential backoff |
| **Startup speed** | FTS rebuilt every restart | Guarded rebuild โ€” only runs once on first migration |
| **Code quality** | Duplicated `ToolRuntimeContext` in two files | Shared in `src/types/runtime.ts` |

## Architecture at a Glance

```
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                 OpenClaw Gateway                      โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”   โ”‚
โ”‚  โ”‚        Mnemosyne Plugin (kind: memory)        โ”‚   โ”‚
โ”‚  โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”‚   โ”‚
โ”‚  โ”‚  โ”‚ hooks/ โ”‚  โ”‚  tools/  โ”‚  โ”‚  database   โ”‚  โ”‚   โ”‚
โ”‚  โ”‚  โ”‚capture โ”‚  โ”‚ remember โ”‚  โ”‚  SQLite WAL โ”‚  โ”‚   โ”‚
โ”‚  โ”‚  โ”‚ agent_ โ”‚  โ”‚ recall   โ”‚  โ”‚  FTS5 index โ”‚  โ”‚   โ”‚
โ”‚  โ”‚  โ”‚ end    โ”‚  โ”‚ search โ—€ โ”‚  โ”‚  auto-prune โ”‚  โ”‚   โ”‚
โ”‚  โ”‚  โ”‚        โ”‚  โ”‚ list     โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ”‚   โ”‚
โ”‚  โ”‚  โ”‚        โ”‚  โ”‚ forget   โ”‚                   โ”‚   โ”‚
โ”‚  โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜                   โ”‚   โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜   โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”   โ”‚
โ”‚  โ”‚        QMD Plugin (untouched)                 โ”‚   โ”‚
โ”‚  โ”‚  memory_search, memory_get                    โ”‚   โ”‚
โ”‚  โ”‚  Indexes ~/.openclaw/workspace/               โ”‚   โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜   โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
```

## Core Features

| Feature | How It Works |
|---------|-------------|
| **Auto-capture** | `agent_end` hook fires after every successful turn โ†’ messages saved to SQLite |
| **Full-text search** | `mnemosyne_search` โ€” FTS5 with Porter stemming across ALL sessions |
| **Explicit memory** | `mnemosyne_remember` โ€” store key-value facts scoped to real session |
| **Cross-session recall** | `mnemosyne_recall(cross_session=true)` โ€” search memories from any session |
| **List** | `mnemosyne_list` โ€” enumerate all stored memories |
| **Forget** | `mnemosyne_forget` โ€” delete a memory by key |
| **Slash command** | `/mnemosyne stats` โ€” shows counts + FTS status |
| **Auto-pruning** | FIFO deletion per session when limits exceeded |
| **WAL mode** | Crash-safe, concurrent-friendly SQLite |
| **Crash recovery** | `wal_checkpoint(TRUNCATE)` flushes pending WAL on startup |

## Data Model

### `messages` โ€” Auto-captured conversation turns
- `session_key`, `agent_id`, `role` (user/assistant/system), `content`, `timestamp`

### `memories` โ€” Explicit key-value stores
- `session_key + key` โ€” unique composite
- `value`, `timestamp`, `updated_at`

### `sessions` โ€” Metadata ledger
- `message_count`, `memory_count`, `updated_at`

### `messages_fts` / `memories_fts` โ€” FTS5 virtual tables
- Porter stemmer, Unicode61 normalizer, diacritic removal
- Keep-fresh triggers on INSERT/UPDATE/DELETE

## Coexistence with QMD (Zero Conflict)

| Layer | System | What It Does | Data Format |
|-------|--------|-------------|-------------|
| **Long-term Document Memory** | **QMD** (unchanged) | Indexes Markdown files โ€” MEMORY.md, DREAMS.md | Files on disk |
| **Session / Structured Memory** | **Mnemosyne** | Captures conversation turns + explicit key-value remembers + FTS5 search | SQLite (`~/.openclaw/memory/mnemosyne.db`) |

QMD tools stay exactly as they are:
- `memory_search` โ†’ searches Markdown files โœ“
- `memory_get` โ†’ reads Markdown files โœ“

Mnemosyne adds new tools alongside them:
- `mnemosyne_remember`, `mnemosyne_recall`, `mnemosyne_search`, `mnemosyne_list`, `mnemosyne_forget`

## Installation (Any OpenClaw, Even Offline)

### Prerequisites
- Node.js 22+ (tested on v24.14.0)
- OpenClaw >= 2026.4.27
- `python3` and `make` (for better-sqlite3 native compilation)

### Step 1: Clone & Build
```bash
git clone https://github.com/smfworks/mnemosyne-openclaw.git
cd mnemosyne-openclaw
npm install
npm run build
```

### Step 2: Load Plugin
```bash
openclaw plugin load /full/path/to/mnemosyne-openclaw
```

### Step 3: Configure
```json
{
  "plugins": {
    "slots": { "memory": "mnemosyne" },
    "entries": {
      "mnemosyne": {
        "enabled": true,
        "config": {
          "dbPath": "~/.openclaw/memory/mnemosyne.db",
          "ownerObserveOthers": true,
          "noisePatterns": [],
          "maxMessagesPerSession": 10000,
          "maxMemoriesPerSession": 1000,
          "enableFts": true
        }
      }
    },
    "allow": ["mnemosyne", "memory-core"]
  }
}
```

### Step 4: Restart Gateway
```bash
openclaw gateway restart
```

### Step 5: Verify
```
/mnemosyne stats
```
Expected:
```
Mnemosyne Stats:
- Messages: 0
- Memories: 0
- Sessions: 0
- DB: /home/.../.openclaw/memory/mnemosyne.db
- FTS: enabled
```

## Configuration Reference

| Field | Type | Default | Description |
|-------|------|---------|-------------|
| `dbPath` | string | `~/.openclaw/memory/mnemosyne.db` | Path to SQLite file |
| `ownerObserveOthers` | boolean | `true` | Capture owner messages alongside agent messages |
| `noisePatterns` | string[] | Built-in + user-defined | Substrings that cause a message to be skipped |
| `maxMessagesPerSession` | integer | 10000 | Auto-pruning: keep N most recent messages per session |
| `maxMemoriesPerSession` | integer | 1000 | Auto-pruning: keep N most recent explicit memories per session |
| `enableFts` | boolean | `true` | Enable FTS5 full-text search indexes (set to `false` to save disk on resource-constrained deployments) |

## Built-in Noise Patterns
- `HEARTBEAT_OK`
- `cron reminder`
- `scheduled run`
- `[heartbeat]`, `[system]`

## Tool Usage (from Agent Prompt)

### Remember
```
mnemosyne_remember(key="user_name", value="Michael", scope="session")
```

### Recall by key
```
mnemosyne_recall(key="user_name")
```

### Recall by query
```
mnemosyne_recall(query="timezone", limit=5)
```

### Cross-session recall
```
mnemosyne_recall(query="plugin", cross_session=true, limit=10)
```

### Full-text search (FTS5)
```
mnemosyne_search(query="Italian wine candles", source="all", limit=10)

# Source filter: "messages" | "memories" | "all"
# Stemming: "remembering" matches "remember", "conversations" matches "conversation"
```

### List all
```
mnemosyne_list(limit=20)
```

### Forget
```
mnemosyne_forget(key="old_fact")
```

## Reliability Guarantees

| Failure Mode | Mitigation |
|-------------|-----------|
| Gateway crash mid-write | SQLite WAL + `wal_checkpoint(TRUNCATE)` on restart |
| Disk full | Graceful degradation; oldest entries pruned |
| Config corruption | Schema validation on every boot; auto-create DB on first run |
| QMD conflict | Completely separate namespaces; QMD tools unaffected |
| Plugin crash on load | Isolated to plugin; gateway stays up; error logged |
| SQLITE_BUSY (WAL contention) | Retry with exponential backoff (3 attempts, max 500ms) |
| Large DB (100K+ messages) | Guarded FTS rebuild โ€” near-zero startup after first migration |

## Why No HTTP Server?

- **Synchronous** โ€” No async/await complexity in hooks; `agent_end` blocks until write completes
- **Single process** โ€” No separate process to monitor, restart, or debug
- **Zero network** โ€” No `localhost:3001` to fail; direct file I/O
- **Transaction safety** โ€” SQLite WAL mode handles crashes gracefully
- **Proven stack** โ€” better-sqlite3 is a production backend used by millions

## File Structure
```
mnemosyne-plugin/
โ”œโ”€โ”€ dist/                      โ† Compiled JS (tsc output)
โ”œโ”€โ”€ src/
โ”‚   โ”œโ”€โ”€ index.ts               โ† Plugin entry point
โ”‚   โ”œโ”€โ”€ config.ts              โ† Schema + validator
โ”‚   โ”œโ”€โ”€ database.ts            โ† SQLite singleton + FTS5 + WAL checkpoint
โ”‚   โ”œโ”€โ”€ state.ts               โ† Shared plugin state (DI pattern)
โ”‚   โ”œโ”€โ”€ helpers.ts             โ† Session keys, noise filter
โ”‚   โ”œโ”€โ”€ hooks/
โ”‚   โ”‚   โ””โ”€โ”€ capture.ts         โ† agent_end handler
โ”‚   โ”œโ”€โ”€ tools/
โ”‚   โ”‚   โ””โ”€โ”€ index.ts           โ† 5 tools: remember/recall/search/list/forget
โ”‚   โ””โ”€โ”€ types/
โ”‚       โ”œโ”€โ”€ runtime.ts         โ† ToolRuntimeContext (shared)
โ”‚       โ”œโ”€โ”€ better-sqlite3.d.tsโ† Type declarations
โ”‚       โ””โ”€โ”€ openclaw-sdk.d.ts  โ† Plugin API types
โ”œโ”€โ”€ tests/
โ”‚   โ””โ”€โ”€ database.test.js       โ† Node built-in test runner
โ”œโ”€โ”€ workspace_md/
โ”‚   โ”œโ”€โ”€ SOUL.md                โ† Plugin philosophy
โ”‚   โ”œโ”€โ”€ AGENTS.md              โ† Agent contract
โ”‚   โ””โ”€โ”€ BOOTSTRAP.md           โ† Quick start
โ”œโ”€โ”€ openclaw.plugin.json       โ† Plugin manifest
โ”œโ”€โ”€ package.json               โ† NPM manifest
โ””โ”€โ”€ tsconfig.json              โ† TypeScript config
```

## Non-Goals (What This Plugin Does NOT Do)

These features are intentionally excluded to keep Mnemosyne small, safe, and zero-dependency:

| Feature | Status | Rationale |
|---------|--------|-----------|
| **Vector embeddings / semantic search** | โŒ Not planned | Requires ML runtime (ONNX/transformers.js) โ€” violates "zero heavy deps" design goal. FTS5 + stemming provides excellent keyword recall without the complexity |
| **Knowledge graph / entity-relationship DB** | โŒ Not planned | Requires external services (Qdrant, FalkorDB, Neo4j) โ€” violates single-process design. Use a dedicated graph plugin if needed |
| **LLM-based memory curation** | โŒ Not planned here | Memory consolidation should happen in OpenClaw's dreaming pipeline, n

... (truncated)
tools

Comments

Sign in to leave a comment

Loading comments...