Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ dist/
.arch/
lib/*.ts
!lib/*.d.ts
temp/

*.tsbuildinfo
.DS_Store
Expand Down
142 changes: 89 additions & 53 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
# Supermemory Plugin for OpenClaw (previously Clawdbot)

<img width="1200" height="628" alt="Announcement-3 (2)" src="https://github.com/user-attachments/assets/caa5acaa-8246-4172-af3a-9cfed2a452c1" />
# OpenClaw Supermemory Plugin

<img width="2048" height="512" alt="Untitled_Artwork 3" src="https://github.com/user-attachments/assets/e68fe07d-bc1f-49a1-a40c-3560f1a079b2" />


Long-term memory for OpenClaw. Automatically remembers conversations, recalls relevant context, and builds a persistent user profile — all powered by [Supermemory](https://supermemory.ai) cloud. No local infrastructure required.

> **Requires [Supermemory Pro or above](https://console.supermemory.ai/billing)** - Unlock the state of the art memory for your OpenClaw bot.
> **Requires [Supermemory Pro or above](https://console.supermemory.ai/billing)** - Unlock the state of the art memory for your OpenClaw bot.

## Install

Expand All @@ -16,76 +15,113 @@ openclaw plugins install @supermemory/openclaw-supermemory

Restart OpenClaw after installing.

## Configuration

The only required value is your Supermemory API key. Get one at [console.supermemory.ai](https://console.supermemory.ai).

Set it as an environment variable:
## Setup

```bash
export SUPERMEMORY_OPENCLAW_API_KEY="sm_..."
openclaw supermemory setup
```

Or configure it directly in `openclaw.json`:
Enter your API key from [console.supermemory.ai](https://console.supermemory.ai). That's it.

```json5
{
"plugins": {
"entries": {
"openclaw-supermemory": {
"enabled": true,
"config": {
"apiKey": "${SUPERMEMORY_OPENCLAW_API_KEY}"
}
}
}
}
}
```
### Advanced Setup

### Advanced options
```bash
openclaw supermemory setup-advanced
```

| Key | Type | Default | Description |
|-----|------|---------|-------------|
| `containerTag` | `string` | `openclaw_{hostname}` | Memory namespace. All channels share this tag. |
| `autoRecall` | `boolean` | `true` | Inject relevant memories before every AI turn. |
| `autoCapture` | `boolean` | `true` | Automatically store conversation content after every turn. |
| `maxRecallResults` | `number` | `10` | Max memories injected into context per turn. |
| `profileFrequency` | `number` | `50` | Inject full user profile every N turns. Search results are injected every turn. |
| `captureMode` | `string` | `"all"` | `"all"` filters short texts and injected context. `"everything"` captures all messages. |
| `debug` | `boolean` | `false` | Verbose debug logs for API calls and responses. |
Configure all options interactively: container tag, auto-recall, auto-capture, capture mode, custom container tags, and more.

## How it works

Once installed, the plugin works automatically with zero interaction:
Once installed, the plugin works automatically:

- **Auto-Recall** — Before every AI turn, the plugin queries Supermemory for relevant memories and injects them as context. The AI sees your user profile (preferences, facts) and semantically similar past conversations.
- **Auto-Capture** — After every AI turn, the last user/assistant exchange is sent to Supermemory for extraction and long-term storage.
- **Auto-Recall** — Before every AI turn, queries Supermemory for relevant memories and injects them as context. The AI sees your user profile and semantically similar past conversations.
- **Auto-Capture** — After every AI turn, the conversation is sent to Supermemory for extraction and long-term storage.
- **Custom Container Tags** — Define custom memory containers (e.g., `work`, `personal`, `bookmarks`). The AI automatically picks the right container based on your instructions when using memory tools.

Everything runs in the cloud. Supermemory handles extraction, deduplication, and profile building on its end.
Everything runs in the cloud. Supermemory handles extraction, deduplication, and profile building.

## Slash Commands

| Command | Description |
|---------|-------------|
| `/remember <text>` | Manually save something to memory. |
| `/recall <query>` | Search your memories and see results with similarity scores. |
| Command | Description |
| ------------------ | --------------------------------------- |
| `/remember <text>` | Manually save something to memory. |
| `/recall <query>` | Search memories with similarity scores. |

## AI Tools

The AI can use these tools autonomously during conversations:
The AI uses these tools autonomously. With custom container tags enabled, all tools support a `containerTag` parameter for routing to specific containers.

| Tool | Description |
|------|-------------|
| `supermemory_store` | Save information to long-term memory. |
| `supermemory_search` | Search memories by query. |
| `supermemory_forget` | Delete a memory by query. |
| `supermemory_profile` | View the user profile (persistent facts + recent context). |
| Tool | Description |
| --------------------- | ------------------------------------------------------ |
| `supermemory_store` | Save information to memory. |
| `supermemory_search` | Search memories by query. |
| `supermemory_forget` | Delete a memory by query or ID. |
| `supermemory_profile` | View user profile (persistent facts + recent context). |

## CLI Commands

```bash
openclaw supermemory search <query> # Search memories
openclaw supermemory profile # View user profile
openclaw supermemory wipe # Delete all memories (destructive, requires confirmation)
openclaw supermemory setup # Configure API key
openclaw supermemory setup-advanced # Configure all options
openclaw supermemory status # View current configuration
openclaw supermemory search <query> # Search memories
openclaw supermemory profile # View user profile
openclaw supermemory wipe # Delete all memories (requires confirmation)
```

## Configuration

Set API key via environment variable:

```bash
export SUPERMEMORY_OPENCLAW_API_KEY="sm_..."
```

Or configure in `~/.openclaw/openclaw.json`:

### Options

| Key | Type | Default | Description |
| ----------------------------- | --------- | --------------------- | --------------------------------------------------------- |
| `apiKey` | `string` | — | Supermemory API key. |
| `containerTag` | `string` | `openclaw_{hostname}` | Root memory namespace. |
| `autoRecall` | `boolean` | `true` | Inject relevant memories before every AI turn. |
| `autoCapture` | `boolean` | `true` | Store conversations after every turn. |
| `maxRecallResults` | `number` | `10` | Max memories injected per turn. |
| `profileFrequency` | `number` | `50` | Inject full profile every N turns. |
| `captureMode` | `string` | `"all"` | `"all"` filters short texts, `"everything"` captures all. |
| `debug` | `boolean` | `false` | Verbose debug logs. |
| `enableCustomContainerTags` | `boolean` | `false` | Enable custom container routing. |
| `customContainers` | `array` | `[]` | Custom containers with `tag` and `description`. |
| `customContainerInstructions` | `string` | `""` | Instructions for AI on container routing. |

### Full Example

```json
{
"plugins": {
"entries": {
"openclaw-supermemory": {
"enabled": true,
"config": {
"apiKey": "${SUPERMEMORY_OPENCLAW_API_KEY}",
"containerTag": "my_memory",
"autoRecall": true,
"autoCapture": true,
"maxRecallResults": 10,
"profileFrequency": 50,
"captureMode": "all",
"debug": false,
"enableCustomContainerTags": true,
"customContainers": [
{ "tag": "work", "description": "Work-related memories" },
{ "tag": "personal", "description": "Personal notes" }
],
"customContainerInstructions": "Store work tasks in 'work', personal stuff in 'personal'"
}
}
}
}
}
```
46 changes: 33 additions & 13 deletions client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,18 +61,21 @@ export class SupermemoryClient {
content: string,
metadata?: Record<string, string | number | boolean>,
customId?: string,
containerTag?: string,
): Promise<{ id: string }> {
const cleaned = sanitizeContent(content)
const tag = containerTag ?? this.containerTag

log.debugRequest("add", {
contentLength: cleaned.length,
customId,
metadata,
containerTag: tag,
})

const result = await this.client.add({
content: cleaned,
containerTag: this.containerTag,
containerTag: tag,
...(metadata && { metadata }),
...(customId && { customId }),
})
Expand All @@ -81,16 +84,22 @@ export class SupermemoryClient {
return { id: result.id }
}

async search(query: string, limit = 5): Promise<SearchResult[]> {
async search(
query: string,
limit = 5,
containerTag?: string,
): Promise<SearchResult[]> {
const tag = containerTag ?? this.containerTag

log.debugRequest("search.memories", {
query,
limit,
containerTag: this.containerTag,
containerTag: tag,
})

const response = await this.client.search.memories({
q: query,
containerTag: this.containerTag,
containerTag: tag,
limit,
})

Expand All @@ -106,11 +115,16 @@ export class SupermemoryClient {
return results
}

async getProfile(query?: string): Promise<ProfileResult> {
log.debugRequest("profile", { containerTag: this.containerTag, query })
async getProfile(
query?: string,
containerTag?: string,
): Promise<ProfileResult> {
const tag = containerTag ?? this.containerTag

log.debugRequest("profile", { containerTag: tag, query })

const response = await this.client.profile({
containerTag: this.containerTag,
containerTag: tag,
...(query && { q: query }),
})

Expand All @@ -131,13 +145,18 @@ export class SupermemoryClient {
return result
}

async deleteMemory(id: string): Promise<{ id: string; forgotten: boolean }> {
async deleteMemory(
id: string,
containerTag?: string,
): Promise<{ id: string; forgotten: boolean }> {
const tag = containerTag ?? this.containerTag

log.debugRequest("memories.delete", {
id,
containerTag: this.containerTag,
containerTag: tag,
})
const result = await this.client.memories.forget({
containerTag: this.containerTag,
containerTag: tag,
id,
})
log.debugResponse("memories.delete", result)
Expand All @@ -146,16 +165,17 @@ export class SupermemoryClient {

async forgetByQuery(
query: string,
containerTag?: string,
): Promise<{ success: boolean; message: string }> {
log.debugRequest("forgetByQuery", { query })
log.debugRequest("forgetByQuery", { query, containerTag })

const results = await this.search(query, 5)
const results = await this.search(query, 5, containerTag)
if (results.length === 0) {
return { success: false, message: "No matching memory found to forget." }
}

const target = results[0]
await this.deleteMemory(target.id)
await this.deleteMemory(target.id, containerTag)

const preview = limitText(target.content || target.memory || "", 100)
return { success: true, message: `Forgot: "${preview}"` }
Expand Down
Loading