Tools
Swapmemory
Auto-store conversations and retrieve context for OpenClaw agents — semantic retrieval, wiki cold storage, multi-agent, smart TTL
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