9361e33b6d
* fix(openclaw): inject context via system prompt instead of overwriting MEMORY.md
The OpenClaw plugin was overwriting each agent's MEMORY.md with a large
auto-generated observation dump (~12-15KB) on every before_agent_start
and tool_result_persist event. This conflicts with OpenClaw's design
where MEMORY.md is agent-curated long-term memory.
Migrate context injection from file-based (writeFile MEMORY.md) to
OpenClaw's native before_prompt_build hook, which returns context via
appendSystemContext. This keeps MEMORY.md under agent control while
still providing cross-session observation context to the LLM.
Changes:
- Add before_prompt_build hook that returns { appendSystemContext }
- Remove writeFile/MEMORY.md sync from before_agent_start
- Remove MEMORY.md sync from tool_result_persist (observations still recorded)
- Add 60s TTL cache to avoid re-fetching context on every LLM turn
- Add syncMemoryFileExclude config for per-agent opt-out
- Remove dead workspaceDirsBySessionKey tracking map
- Rewrite test suite to verify prompt injection instead of file writes
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(ui): align settings defaults with backend and use nullish coalescing
The web UI had two issues causing settings inflation:
1. DEFAULT_SETTINGS in the UI used FULL_COUNT='5' and all token columns
'true', while SettingsDefaultsManager (backend) uses FULL_COUNT='0'
and token columns 'false'. Opening the settings modal and saving
without changes would silently inflate the context.
2. useSettings used || for fallback, which treats '0' and 'false' as
falsy — even when the backend correctly returns these values, the UI
would replace them with inflated defaults. Changed to ?? (nullish
coalescing) so only null/undefined trigger the fallback.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(openclaw): update integration docs for system prompt injection
Reflect the migration from MEMORY.md file writes to before_prompt_build
hook-based context injection:
- Update architecture diagram and overview to show new hook flow
- Replace "MEMORY.md Live Sync" section with "System Prompt Context Injection"
- Update event lifecycle steps (before_agent_start, tool_result_persist)
- Add before_prompt_build step with TTL cache description
- Document new syncMemoryFileExclude config parameter
- Update session tracking to reflect removed workspaceDirsBySessionKey
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: fix terminology and update SKILL.md for system prompt injection
Replace "prompt injection" with "context injection" in docs to avoid
confusion with the OWASP security term. Update openclaw/SKILL.md to
reflect the new before_prompt_build hook and remove stale MEMORY.md
references.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Alex Newman <thedotmack@gmail.com>
382 lines
14 KiB
Plaintext
382 lines
14 KiB
Plaintext
---
|
|
title: OpenClaw Integration
|
|
description: Persistent memory for OpenClaw agents — observation recording, system prompt context injection, and real-time observation feeds
|
|
icon: dragon
|
|
---
|
|
|
|
## Overview
|
|
|
|
The OpenClaw plugin gives claude-mem persistent memory to agents running on the [OpenClaw](https://openclaw.ai) gateway. It handles three things:
|
|
|
|
1. **Observation recording** — Captures tool usage from OpenClaw's embedded runner and sends it to the claude-mem worker for AI processing
|
|
2. **System prompt context injection** — Injects the observation timeline into each agent's system prompt via the `before_prompt_build` hook, keeping `MEMORY.md` free for agent-curated memory
|
|
3. **Observation feed** — Streams new observations to messaging channels (Telegram, Discord, Slack, etc.) in real-time via SSE
|
|
|
|
<Info>
|
|
OpenClaw's embedded runner (`pi-embedded`) calls the Anthropic API directly without spawning a `claude` process, so claude-mem's standard hooks never fire. This plugin bridges that gap by using OpenClaw's event system to capture the same data.
|
|
</Info>
|
|
|
|
## How It Works
|
|
|
|
```plaintext
|
|
OpenClaw Gateway
|
|
│
|
|
├── before_agent_start ───→ Init session
|
|
├── before_prompt_build ──→ Inject context into system prompt
|
|
├── tool_result_persist ──→ Record observation
|
|
├── agent_end ────────────→ Summarize + Complete session
|
|
└── gateway_start ────────→ Reset session tracking + context cache
|
|
│
|
|
▼
|
|
Claude-Mem Worker (localhost:37777)
|
|
├── POST /api/sessions/init
|
|
├── POST /api/sessions/observations
|
|
├── POST /api/sessions/summarize
|
|
├── POST /api/sessions/complete
|
|
├── GET /api/context/inject ──→ System prompt context
|
|
└── GET /stream ─────────────→ SSE → Messaging channels
|
|
```
|
|
|
|
### Event Lifecycle
|
|
|
|
<Steps>
|
|
<Step title="Agent starts (before_agent_start)">
|
|
When an OpenClaw agent starts, the plugin initializes a session by sending the user prompt to `POST /api/sessions/init` so the worker can create a new session and start processing.
|
|
</Step>
|
|
<Step title="Context injected (before_prompt_build)">
|
|
Before each LLM call, the plugin fetches the observation timeline from the worker's `/api/context/inject` endpoint and returns it as `appendSystemContext`. This injects cross-session context directly into the agent's system prompt without writing any files.
|
|
|
|
The context is cached for 60 seconds to avoid re-fetching on every LLM turn within a session.
|
|
</Step>
|
|
<Step title="Tool use recorded (tool_result_persist)">
|
|
Every time the agent uses a tool (Read, Write, Bash, etc.), the plugin sends the observation to `POST /api/sessions/observations` with the tool name, input, and truncated response (max 1000 chars). This is fire-and-forget — it doesn't block the agent from continuing work.
|
|
|
|
Tools prefixed with `memory_` are skipped to avoid recursive recording.
|
|
</Step>
|
|
<Step title="Agent finishes (agent_end)">
|
|
When the agent completes, the plugin extracts the last assistant message and sends it to `POST /api/sessions/summarize`, then calls `POST /api/sessions/complete` to close the session. Both are fire-and-forget.
|
|
</Step>
|
|
<Step title="Gateway restarts (gateway_start)">
|
|
Clears all session tracking (session IDs, context cache) so agents get fresh state after a gateway restart.
|
|
</Step>
|
|
</Steps>
|
|
|
|
### System Prompt Context Injection
|
|
|
|
The plugin injects cross-session observation context into each agent's system prompt via OpenClaw's `before_prompt_build` hook. The content comes from the worker's `GET /api/context/inject?projects=<project>` endpoint, which generates a formatted markdown timeline from the SQLite database.
|
|
|
|
This approach keeps `MEMORY.md` under the agent's control for curated long-term memory (decisions, preferences, durable facts), while the observation timeline is delivered through the system prompt where it belongs.
|
|
|
|
<Info>
|
|
Context is cached for 60 seconds per project to avoid re-fetching on every LLM turn. The cache is cleared on gateway restart. Use `syncMemoryFileExclude` to opt specific agents out of context injection entirely.
|
|
</Info>
|
|
|
|
### Observation Feed (SSE → Messaging)
|
|
|
|
The plugin runs a background service that connects to the worker's SSE stream (`GET /stream`) and forwards `new_observation` events to a configured messaging channel. This lets you monitor what your agents are learning in real-time from Telegram, Discord, Slack, or any supported OpenClaw channel.
|
|
|
|
The SSE connection uses exponential backoff (1s → 30s) for automatic reconnection.
|
|
|
|
## Setting Up the Observation Feed
|
|
|
|
The observation feed sends a formatted message to your OpenClaw channel every time claude-mem creates a new observation. Each message includes the observation title and subtitle so you can follow along as your agents work.
|
|
|
|
Messages look like this in your channel:
|
|
|
|
```
|
|
🧠 Claude-Mem Observation
|
|
**Implemented retry logic for API client**
|
|
Added exponential backoff with configurable max retries to handle transient failures
|
|
```
|
|
|
|
### Step 1: Choose your channel
|
|
|
|
The observation feed works with any channel that your OpenClaw gateway has configured. You need two pieces of information:
|
|
|
|
- **Channel type** — The name of the channel plugin registered with OpenClaw (e.g., `telegram`, `discord`, `slack`, `signal`, `whatsapp`, `line`)
|
|
- **Target ID** — The chat ID, channel ID, or user ID where messages should be sent
|
|
|
|
<AccordionGroup>
|
|
<Accordion title="Telegram" icon="telegram">
|
|
**Channel type:** `telegram`
|
|
|
|
**Target ID:** Your Telegram chat ID (numeric). To find it:
|
|
1. Message [@userinfobot](https://t.me/userinfobot) on Telegram
|
|
2. It will reply with your chat ID (e.g., `123456789`)
|
|
3. For group chats, the ID is negative (e.g., `-1001234567890`)
|
|
|
|
```json
|
|
"observationFeed": {
|
|
"enabled": true,
|
|
"channel": "telegram",
|
|
"to": "123456789"
|
|
}
|
|
```
|
|
</Accordion>
|
|
|
|
<Accordion title="Discord" icon="discord">
|
|
**Channel type:** `discord`
|
|
|
|
**Target ID:** The Discord channel ID. To find it:
|
|
1. Enable Developer Mode in Discord (Settings → Advanced → Developer Mode)
|
|
2. Right-click the channel → Copy Channel ID
|
|
|
|
```json
|
|
"observationFeed": {
|
|
"enabled": true,
|
|
"channel": "discord",
|
|
"to": "1234567890123456789"
|
|
}
|
|
```
|
|
</Accordion>
|
|
|
|
<Accordion title="Slack" icon="slack">
|
|
**Channel type:** `slack`
|
|
|
|
**Target ID:** The Slack channel ID (not the channel name). To find it:
|
|
1. Open the channel in Slack
|
|
2. Click the channel name at the top
|
|
3. Scroll to the bottom of the channel details — the ID looks like `C01ABC2DEFG`
|
|
|
|
```json
|
|
"observationFeed": {
|
|
"enabled": true,
|
|
"channel": "slack",
|
|
"to": "C01ABC2DEFG"
|
|
}
|
|
```
|
|
</Accordion>
|
|
|
|
<Accordion title="Signal" icon="signal-messenger">
|
|
**Channel type:** `signal`
|
|
|
|
**Target ID:** The Signal phone number or group ID configured in your OpenClaw gateway.
|
|
|
|
```json
|
|
"observationFeed": {
|
|
"enabled": true,
|
|
"channel": "signal",
|
|
"to": "+1234567890"
|
|
}
|
|
```
|
|
</Accordion>
|
|
|
|
<Accordion title="WhatsApp" icon="whatsapp">
|
|
**Channel type:** `whatsapp`
|
|
|
|
**Target ID:** The WhatsApp phone number or group JID configured in your OpenClaw gateway.
|
|
|
|
```json
|
|
"observationFeed": {
|
|
"enabled": true,
|
|
"channel": "whatsapp",
|
|
"to": "+1234567890"
|
|
}
|
|
```
|
|
</Accordion>
|
|
|
|
<Accordion title="LINE" icon="line">
|
|
**Channel type:** `line`
|
|
|
|
**Target ID:** The LINE user ID or group ID from the LINE Developer Console.
|
|
|
|
```json
|
|
"observationFeed": {
|
|
"enabled": true,
|
|
"channel": "line",
|
|
"to": "U1234567890abcdef"
|
|
}
|
|
```
|
|
</Accordion>
|
|
</AccordionGroup>
|
|
|
|
### Step 2: Add the config to your gateway
|
|
|
|
Add the `observationFeed` block to your claude-mem plugin config in your OpenClaw gateway configuration:
|
|
|
|
```json
|
|
{
|
|
"plugins": {
|
|
"claude-mem": {
|
|
"enabled": true,
|
|
"config": {
|
|
"project": "my-project",
|
|
"observationFeed": {
|
|
"enabled": true,
|
|
"channel": "telegram",
|
|
"to": "123456789"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
<Warning>
|
|
The `channel` value must match a channel plugin that is already configured and running on your OpenClaw gateway. If the channel isn't registered, you'll see `Unknown channel type: <channel>` in the logs.
|
|
</Warning>
|
|
|
|
### Step 3: Verify the connection
|
|
|
|
After starting the gateway, check that the feed is connected:
|
|
|
|
1. **Check the logs** — You should see:
|
|
```
|
|
[claude-mem] Observation feed starting — channel: telegram, target: 123456789
|
|
[claude-mem] Connecting to SSE stream at http://localhost:37777/stream
|
|
[claude-mem] Connected to SSE stream
|
|
```
|
|
|
|
2. **Use the status command** — Run `/claude_mem_feed` in any OpenClaw chat to see:
|
|
```
|
|
Claude-Mem Observation Feed
|
|
Enabled: yes
|
|
Channel: telegram
|
|
Target: 123456789
|
|
Connection: connected
|
|
```
|
|
|
|
3. **Trigger a test** — Have an agent do some work. When the worker processes the tool usage into an observation, you'll receive a message in your configured channel.
|
|
|
|
<Info>
|
|
The feed only sends `new_observation` events — not raw tool usage. Observations are generated asynchronously by the worker's AI agent, so there's a 1-2 second delay between tool use and the observation message appearing in your channel.
|
|
</Info>
|
|
|
|
### Troubleshooting the Feed
|
|
|
|
| Symptom | Cause | Fix |
|
|
|---------|-------|-----|
|
|
| `Connection: disconnected` | Worker not running or wrong port | Check `workerPort` config, run `npm run worker:status` |
|
|
| `Connection: reconnecting` | Worker was running but connection dropped | The plugin auto-reconnects with backoff — wait up to 30s |
|
|
| `Unknown channel type` in logs | Channel plugin not loaded on gateway | Verify your OpenClaw gateway has the channel plugin configured |
|
|
| No messages appearing | Feed connected but no observations being created | Check that agents are running and the worker is processing observations |
|
|
| `Observation feed disabled` in logs | `enabled` is `false` or missing | Set `observationFeed.enabled` to `true` |
|
|
| `Observation feed misconfigured` in logs | Missing `channel` or `to` | Both `channel` and `to` are required |
|
|
|
|
## Installation
|
|
|
|
Run this one-liner to install everything automatically:
|
|
|
|
```bash
|
|
curl -fsSL https://install.cmem.ai/openclaw.sh | bash
|
|
```
|
|
|
|
The installer handles dependency checks (Bun, uv), plugin installation, memory slot configuration, AI provider setup, worker startup, and optional observation feed configuration.
|
|
|
|
You can also pre-select options:
|
|
|
|
```bash
|
|
# With a specific AI provider
|
|
curl -fsSL https://install.cmem.ai/openclaw.sh | bash -s -- --provider=gemini --api-key=YOUR_KEY
|
|
|
|
# Fully unattended (defaults to Claude Max Plan)
|
|
curl -fsSL https://install.cmem.ai/openclaw.sh | bash -s -- --non-interactive
|
|
|
|
# Upgrade existing installation
|
|
curl -fsSL https://install.cmem.ai/openclaw.sh | bash -s -- --upgrade
|
|
```
|
|
|
|
### Manual Configuration
|
|
|
|
Add `claude-mem` to your OpenClaw gateway's plugin configuration:
|
|
|
|
```json
|
|
{
|
|
"plugins": {
|
|
"claude-mem": {
|
|
"enabled": true,
|
|
"config": {
|
|
"project": "my-project",
|
|
"syncMemoryFile": true,
|
|
"workerPort": 37777,
|
|
"observationFeed": {
|
|
"enabled": true,
|
|
"channel": "telegram",
|
|
"to": "your-chat-id"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
<Note>
|
|
The claude-mem worker service must be running on the same machine as the OpenClaw gateway. The plugin communicates with it via HTTP on `localhost:37777`.
|
|
</Note>
|
|
|
|
## Configuration
|
|
|
|
<ParamField body="project" type="string" default="openclaw">
|
|
Project name for scoping observations in the memory database. All observations from this gateway will be stored under this project name.
|
|
</ParamField>
|
|
|
|
<ParamField body="syncMemoryFile" type="boolean" default={true}>
|
|
Inject observation context into the agent system prompt via `before_prompt_build` hook. When `true`, agents receive cross-session context automatically. Set to `false` to disable context injection entirely (observations are still recorded).
|
|
</ParamField>
|
|
|
|
<ParamField body="syncMemoryFileExclude" type="string[]" default={[]}>
|
|
Agent IDs excluded from automatic context injection. Useful for agents that curate their own memory and don't need the observation timeline (e.g., `["snarf", "debugger"]`). Observations are still recorded for excluded agents — only the context injection is skipped.
|
|
</ParamField>
|
|
|
|
<ParamField body="workerPort" type="number" default={37777}>
|
|
Port for the claude-mem worker service. Override if your worker runs on a non-default port.
|
|
</ParamField>
|
|
|
|
<ParamField body="observationFeed.enabled" type="boolean" default={false}>
|
|
Enable live observation streaming to messaging channels.
|
|
</ParamField>
|
|
|
|
<ParamField body="observationFeed.channel" type="string">
|
|
Channel type: `telegram`, `discord`, `signal`, `slack`, `whatsapp`, `line`
|
|
</ParamField>
|
|
|
|
<ParamField body="observationFeed.to" type="string">
|
|
Target chat/user/channel ID to send observations to.
|
|
</ParamField>
|
|
|
|
## Commands
|
|
|
|
### /claude_mem_feed
|
|
|
|
Show or toggle the observation feed status.
|
|
|
|
```
|
|
/claude_mem_feed # Show current status
|
|
/claude_mem_feed on # Request enable
|
|
/claude_mem_feed off # Request disable
|
|
```
|
|
|
|
### /claude_mem_status
|
|
|
|
Check worker health and session status.
|
|
|
|
```
|
|
/claude_mem_status
|
|
```
|
|
|
|
Returns worker status, port, active session count, and observation feed connection state.
|
|
|
|
## Architecture
|
|
|
|
The plugin uses HTTP calls to the already-running claude-mem worker service rather than spawning subprocesses. This means:
|
|
|
|
- No `bun` dependency required on the gateway
|
|
- No process spawn overhead per event
|
|
- Uses the same worker API that Claude Code hooks use
|
|
- All operations are non-blocking (fire-and-forget where possible)
|
|
|
|
### Session Tracking
|
|
|
|
Each OpenClaw agent session gets a unique `contentSessionId` (format: `openclaw-<sessionKey>-<timestamp>`) that maps to a claude-mem session in the worker. The plugin tracks:
|
|
|
|
- `sessionIds` — Maps OpenClaw session keys to content session IDs
|
|
- `contextCache` — TTL cache (60s) for context injection responses, keyed by project
|
|
|
|
Both are cleared on `gateway_start`.
|
|
|
|
## Requirements
|
|
|
|
- Claude-mem worker service running on `localhost:37777` (or configured port)
|
|
- OpenClaw gateway with plugin support
|
|
- Network access between gateway and worker (localhost only)
|