← Back to Plugins
Tools

Swapmemory

mikefaierberg-byte By mikefaierberg-byte 👁 48 views ▲ 0 votes

Auto-store conversations and retrieve context for OpenClaw agents — semantic retrieval, wiki cold storage, multi-agent, smart TTL

GitHub

Install

npm install —

Configuration Example

{
  "plugins": {
    "entries": {
      "swap-memory": {
        "source": "workspace/plugins/swap-memory/src/index.js",
        "enabled": true,
        "config": {
          "enabled": true,
          "defaultAgent": "main",
          "crossSearchEnabled": false
        }
      }
    }
  }
}

README

# Swap Memory Plugin 🐾

**Auto-store conversations and retrieve context for OpenClaw agents.**

Three-layer memory: **hot** (6h chunks) → **warm** (wiki session logs) → **cold** (wiki lessons).  
No cron jobs, no external dependencies. Nothing is ever lost.

---

## Dependencies

The plugin requires these OpenClaw features to be **enabled** (all default ON):

| Feature | Required for | Config path |
|---------|-------------|------------|
| **Memory core** | Storing chunks, embedding, retrieval | `memory.enabled: true` |
| **Wiki** | Cold storage offload, session logs | `wiki.enabled: true` |
| **Embedding model** | Semantic search (e.g., `text-embedding-3-small`) | `providers.openrouter.embeddingModel` |
| **Plugin system** | Loading `.js` plugin files | `plugins.enabled: true` |
| **OpenClaw ≥ 2026.5** | `registerTool`, `contracts.tools` support | — |

If any of these are off, the plugin will skip the affected functionality
(embeddings fall back to null, cold storage is skipped, etc.) without crashing.

---

## Quick Start

### Option A — Agent install (recommended)

Tell your main agent:

> Install the swap-memory plugin from `workspace/plugins/swap-memory/`

The agent will register, enable, and configure it automatically.

### Option B — Manual install

**1. Copy**

Copy the `plugins/swap-memory/` folder into your workspace.

**Or** tell your agent:

> Install swap-memory

**2. Enable in openclaw.json**

```json
{
  "plugins": {
    "entries": {
      "swap-memory": {
        "source": "workspace/plugins/swap-memory/src/index.js",
        "enabled": true,
        "config": {
          "enabled": true,
          "defaultAgent": "main",
          "crossSearchEnabled": false
        }
      }
    }
  }
}
```

**3. Restart**

```bash
openclaw gateway restart
```

> ✅ Plugin auto-detects your agents. Each gets its own isolated swap file.

### 🎯 Try it now

After install, just say in any chat:

> **«Show dashboard»**

You'll see visual bars: chunks per agent, embed ratio, pinned count.

> **«Swap status»**

Detailed stats: stores, retrievals, prunes, archives, syntheses.

---

## Tools & Commands

Full reference: [`COMMANDS.md`](./COMMANDS.md)

| Tool / Slash | What it does |
|-------------|-------------|
| `swap_status` / `/swap-status` | Stats: chunks, pins, retrievals |
| `swap_dashboard` / `/swap-dashboard` | Visual progress bars per agent |
| `swap_promote` / `/swap-promote <keyword>` | Pin chunks by keyword or session |
| `swap_cross` / `/swap-cross on\|off` | Toggle cross-agent search (OFF by default) |

All tools work as both **natural-language intents** ("show me the dashboard") and **slash commands**.

---

## Tool Availability

Swap tools register at the gateway level via the plugin SDK (`registerTool`).

| Agent type | Sees swap tools? |
|-----------|-----------------|
| **Default profile** (no custom `tools.profile`) | ✅ Automatically |
| **Custom profile** (`coding`, `messaging`, etc.) | ✅ Add to `alsoAllow` (see below) |

### Enable for custom-profile agents

Add these four tool names to the agent's `tools.alsoAllow` in `openclaw.json`:

```json
{
  "agents": {
    "list": [
      {
        "id": "my-agent",
        "tools": {
          "profile": "coding",
          "alsoAllow": [
            "swap_status",
            "swap_dashboard",
            "swap_promote",
            "swap_cross"
          ]
        }
      }
    ]
  }
}
```

Then restart. The agent will now see and use swap tools.

### Multi-instance

Plugin must be installed on **each instance** separately.  
Agents on instance A cannot see tools installed only on instance B.

---

## Architecture

```
User Message
    │
    ▼
before_prompt_build hook
    │
    ├─ Phase 1: Semantic Retrieval
    │   └─ embed(query) → cosine similarity → score > minScore
    │   └─ Dynamic count: all qualifying chunks within token budget
    │   └─ Minimum 2 chunks if available
    │
    ├─ Phase 2: Recency Fallback
    │   └─ if Phase 1 returns 0 → last 2 chunks chronologically
    │
    ▼
prependSystemContext → inject (model treats as authoritative)
    │
    ▼
LLM responds
    │
    ▼
agent_end hook → chunk + embed + append to swap.jsonl
    │
    ▼
Volume Offload Check
    └─ if swap > 500 chunks → offload oldest 100 to wiki cold storage
```

### Why prependSystemContext?

| Field | Priority | Model treats as |
|-------|----------|----------------|
| `prependContext` (user prompt) | Low | User chatter, easy to ignore |
| `prependSystemContext` (system) | **High** | Authoritative knowledge |

The agent reads injected chunks alongside its identity and rules. **No tool calls needed** for recall — context is naturally available.

---

## Key Features

### Multi-Agent (v3.2+)

One plugin, all agents. Each gets an isolated swap file:

```
agents/{agentId}/swap/
  swap.jsonl       — JSONL chunk storage
  config.json      — Per-agent config (TTL, max chunks)
```

Agent detection is dynamic — reads `openclaw.json` → `agents.list`. No hardcoded lists.

### Smart TTL (v3.3)

| Parameter | Value |
|----------|-------|
| Base TTL | 6 hours |
| Bump per retrieval | +2 hours |
| Maximum TTL | 48 hours |
| Pinned chunks | ∞ (never pruned) |

### Wiki Cold Storage (v3.2)

When swap exceeds 500 chunks, oldest chunks auto-offload to wiki:

```
wiki/{agent}/sources/swap/{short-id}.md
```

Full session logs are also written to:

```
wiki/{agent}/sources/session-logs/session-{agent}-{date}.md
```

Every chunk has a `logRef` linking to its source session.

### Auto-Embedding Repair

On startup, `repairEmbeddings()` scans all agents and re-embeds chunks with `null`
embeddings. No manual maintenance needed.

### Compaction Safety Net

| Hook | Purpose |
|------|---------|
| `before_compaction` | Saves chunks without embedding (fast) |
| `after_compaction` | Sets flag for broader next retrieval |

### Noise Filter

Auto-strips before storing:
- Cron trigger messages (`[cron:...]`)
- Heartbeat polls (`[OpenClaw heartbeat poll]`)
- Inter-session routing, runtime events
- Internal monologue (single-line "Let me...")
- Messages under 10 chars

---

## Retrieval Logic

### Phase 1 — Semantic (primary)
- Embed expanded query via `text-embedding-3-small`
- Cosine similarity against all chunks in swap.jsonl
- Filter: `minScore >= 0.4`
- Token-budget: include all qualifying chunks up to budget
- Dedup: embeddings similarity > 0.9

### Phase 2 — Recency Fallback (safety net)
- Triggered when Phase 1 returns 0 (e.g., first message in new session)
- Returns last 1-2 chunks by timestamp
- Filters out chunks under 20 chars
- Score set to `-1` (LLM can distinguish fallback vs semantic)

### Token Budget

```
available = contextWindow - currentTokens - safetyMargin (12k)
budget = max(2000, floor(available * 0.2))
```

- `contextWindow`: 200,000 (model default)
- Each chunk: `chars/4 + 30` tokens

---

## Storage Format

### swap.jsonl

```
~/.openclaw/agents/{agent}/swap/swap.jsonl
```

```json
{
  "id": "uuid",
  "ts": "ISO-8601",
  "role": "user|assistant",
  "content": "text",
  "embedding": [1536 floats],
  "embeddedAt": "ISO-8601",
  "sessionKey": "agent:main:xxx",
  "contentLength": 1234,
  "sourcePlugin": "swap-memory"
}
```

### Wiki cold storage

```yaml
---
role: user
session: agent:main:direct:xxx
ts: 2026-05-10T12:00:00Z
---
```text
Conversation chunk content here
```
```

---

## Plugin Config

Add under `plugins.entries.swap-memory.config`:

| Property | Type | Default | Description |
|----------|------|---------|-------------|
| `enabled` | boolean | true | Master toggle |
| `defaultAgent` | string | "main" | Agent ID for `/swap-*` commands |
| `crossSearchEnabled` | boolean | false | Cross-agent search ON by default |
| `storeOnly` | boolean | false | Store only, never inject |
| `minScore` | number | 0.4 | Minimum cosine similarity |
| `pruneThreshold` | number | 500 | Max chunks before wiki offload |
| `pruneKeep` | number | 400 | Newest chunks to keep after offload |

---

## Portability

Zero hardcoded paths. Everything derives from `os.homedir()` → `.openclaw/`.

Works in Docker (`HOME=/home/node`), production, or any environment.  
No npm install — just Node.js built-ins (fs, path, crypto).

---

## File Map

| File | Purpose |
|------|---------|
| `src/index.js` | Hooks, commands, tool registration |
| `src/store.js` | Chunking, embedding, storage, offload |
| `src/retrieve.js` | Semantic search, token budget |
| `src/archive.js` | Session archiving |
| `src/synthesize.js` | Lesson extraction |
| `src/search.js` | Cross-agent search |
| `src/ttl.js` | Smart TTL logic |
| `COMMANDS.md` | Full tool/command reference |
| `CHANGELOG.md` | Version history |

---

## Gotchas

### `sessions_send` (nested sub-agent)

When agent A sends a message to agent B via `sessions_send`, `agent_end` fires
for **agent A** (the parent), **not** agent B. Only direct conversations
(Telegram, webchat, etc.) trigger `agent_end` for the receiving agent.

This is by design — nested calls process inline within the parent session.
Hooks for agent B will fire normally when it receives direct user messages.
tools

Comments

Sign in to leave a comment

Loading comments...