@christopherlittle51/postclaw 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +4 -0
- package/LICENSE +15 -0
- package/README.md +731 -0
- package/dist/index.d.ts +36 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +506 -0
- package/dist/index.js.map +1 -0
- package/dist/schemas/validation.d.ts +81 -0
- package/dist/schemas/validation.d.ts.map +1 -0
- package/dist/schemas/validation.js +27 -0
- package/dist/schemas/validation.js.map +1 -0
- package/dist/scripts/bootstrap_persona.d.ts +20 -0
- package/dist/scripts/bootstrap_persona.d.ts.map +1 -0
- package/dist/scripts/bootstrap_persona.js +150 -0
- package/dist/scripts/bootstrap_persona.js.map +1 -0
- package/dist/scripts/bootstrap_tools.d.ts +2 -0
- package/dist/scripts/bootstrap_tools.d.ts.map +1 -0
- package/dist/scripts/bootstrap_tools.js +67 -0
- package/dist/scripts/bootstrap_tools.js.map +1 -0
- package/dist/scripts/setup-db.d.ts +28 -0
- package/dist/scripts/setup-db.d.ts.map +1 -0
- package/dist/scripts/setup-db.js +539 -0
- package/dist/scripts/setup-db.js.map +1 -0
- package/dist/scripts/sleep_cycle.d.ts +32 -0
- package/dist/scripts/sleep_cycle.d.ts.map +1 -0
- package/dist/scripts/sleep_cycle.js +452 -0
- package/dist/scripts/sleep_cycle.js.map +1 -0
- package/dist/services/db.d.ts +28 -0
- package/dist/services/db.d.ts.map +1 -0
- package/dist/services/db.js +93 -0
- package/dist/services/db.js.map +1 -0
- package/dist/services/memoryService.d.ts +75 -0
- package/dist/services/memoryService.d.ts.map +1 -0
- package/dist/services/memoryService.js +391 -0
- package/dist/services/memoryService.js.map +1 -0
- package/dist/test-db.d.ts +8 -0
- package/dist/test-db.d.ts.map +1 -0
- package/dist/test-db.js +94 -0
- package/dist/test-db.js.map +1 -0
- package/dist/test-memory.d.ts +2 -0
- package/dist/test-memory.d.ts.map +1 -0
- package/dist/test-memory.js +79 -0
- package/dist/test-memory.js.map +1 -0
- package/openclaw.plugin.json +37 -0
- package/package.json +49 -0
- package/schemas/db.sql +371 -0
package/README.md
ADDED
|
@@ -0,0 +1,731 @@
|
|
|
1
|
+
# PostClaw — PostgreSQL Memory Plugin for OpenClaw
|
|
2
|
+
|
|
3
|
+
<p align="center">
|
|
4
|
+
<img src="https://github.com/user-attachments/assets/2a7798c6-e711-40fa-bcf2-7416180651cc"
|
|
5
|
+
alt="PostClaw Logo - Postmodern Artwork of a giant lobster interspersed with technology and classical designs."
|
|
6
|
+
width="800"
|
|
7
|
+
height="600"
|
|
8
|
+
style="display: block; margin: 0 auto" />
|
|
9
|
+
</p>
|
|
10
|
+
|
|
11
|
+
**PostClaw** replaces OpenClaw's default memory system with a PostgreSQL backend powered by `pgvector`. It provides semantic search, episodic memory, knowledge graph linking, dynamic persona injection, and autonomous memory management for multi-agent swarms.
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Table of Contents
|
|
16
|
+
|
|
17
|
+
- [Features](#features)
|
|
18
|
+
- [Prerequisites](#prerequisites)
|
|
19
|
+
- [Quick Start](#quick-start)
|
|
20
|
+
- [Installation](#installation)
|
|
21
|
+
- [Database Setup](#database-setup)
|
|
22
|
+
- [Configuration](#configuration)
|
|
23
|
+
- [Plugin Hooks](#plugin-hooks)
|
|
24
|
+
- [Agent Tools](#agent-tools)
|
|
25
|
+
- [CLI Commands](#cli-commands)
|
|
26
|
+
- [Background Services](#background-services)
|
|
27
|
+
- [Database Schema](#database-schema)
|
|
28
|
+
- [Row-Level Security](#row-level-security)
|
|
29
|
+
- [Persona Bootstrap](#persona-bootstrap)
|
|
30
|
+
- [Sleep Cycle](#sleep-cycle)
|
|
31
|
+
- [Testing](#testing)
|
|
32
|
+
- [Troubleshooting](#troubleshooting)
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## Features
|
|
37
|
+
|
|
38
|
+
| Feature | Description |
|
|
39
|
+
|---------|-------------|
|
|
40
|
+
| **Semantic Memory (RAG)** | Stores memories as vector embeddings in PostgreSQL via `pgvector` for contextual retrieval |
|
|
41
|
+
| **Episodic Memory** | Automatically logs user prompts and tool calls as short-term memory events |
|
|
42
|
+
| **Knowledge Graph** | Links related memories with typed, directed edges for graph-augmented retrieval |
|
|
43
|
+
| **Dynamic Persona Injection** | Injects agent-specific persona rules and RAG context into every prompt |
|
|
44
|
+
| **Duplicate Detection** | Identifies and merges near-duplicate memories during background maintenance |
|
|
45
|
+
| **Multi-Agent Isolation** | Row-Level Security (RLS) ensures agents can only read/write their own data |
|
|
46
|
+
| **Autonomous Management** | Agents silently store, update, link, and search memories without user prompts |
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
## Prerequisites
|
|
51
|
+
|
|
52
|
+
1. **OpenClaw** — installed and configured
|
|
53
|
+
2. **PostgreSQL** (v14+) — installed and running. The `pgvector` extension is installed automatically by the setup command.
|
|
54
|
+
3. **Node.js** (v18+) and **npm**
|
|
55
|
+
4. **Embedding Provider** — LM Studio, Ollama, or any OpenAI-compatible endpoint serving an embedding model (e.g. `nomic-embed-text-v2-moe`)
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## Quick Start
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
# 1. Install the plugin
|
|
63
|
+
openclaw plugins install @postclaw/postclaw # from npm
|
|
64
|
+
# OR from a local path:
|
|
65
|
+
openclaw plugins install /path/to/PostClaw
|
|
66
|
+
|
|
67
|
+
# 2. Set up the database (creates DB, user, schema, everything)
|
|
68
|
+
openclaw postclaw setup
|
|
69
|
+
|
|
70
|
+
# 3. Restart the gateway
|
|
71
|
+
openclaw restart
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Done. PostClaw will automatically:
|
|
75
|
+
- Connect to the database using the credential it just generated
|
|
76
|
+
- Start injecting RAG context and persona rules into every prompt
|
|
77
|
+
- Begin logging episodic memory after each agent turn
|
|
78
|
+
- Run the sleep cycle in the background every 6 hours
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
## Installation
|
|
83
|
+
|
|
84
|
+
### From npm (recommended)
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
openclaw plugins install @postclaw/postclaw
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### From local path
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
# Copy into ~/.openclaw/extensions/postclaw/
|
|
94
|
+
openclaw plugins install /path/to/PostClaw
|
|
95
|
+
|
|
96
|
+
# Or link for development (live edits, no copy)
|
|
97
|
+
openclaw plugins install -l /path/to/PostClaw
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### Manual
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
git clone <repository-url> PostClaw
|
|
104
|
+
cd PostClaw
|
|
105
|
+
npm install
|
|
106
|
+
npm run build
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Then add to `~/.openclaw/openclaw.json`:
|
|
110
|
+
|
|
111
|
+
```json
|
|
112
|
+
{
|
|
113
|
+
"plugins": {
|
|
114
|
+
"load": { "paths": ["/absolute/path/to/PostClaw"] },
|
|
115
|
+
"slots": { "memory": "postclaw" },
|
|
116
|
+
"entries": {
|
|
117
|
+
"postclaw": { "enabled": true }
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
---
|
|
124
|
+
|
|
125
|
+
## Database Setup
|
|
126
|
+
|
|
127
|
+
### Automated Setup
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
openclaw postclaw setup [options]
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
This command:
|
|
134
|
+
1. Connects to PostgreSQL as a superuser
|
|
135
|
+
2. Creates the `memorydb` database (if it doesn't exist)
|
|
136
|
+
3. Installs the `vector` and `pgcrypto` extensions
|
|
137
|
+
4. Creates the `openclaw` app user with a **random password**
|
|
138
|
+
5. Creates the `system_admin` role with RLS bypass
|
|
139
|
+
6. Builds all tables, triggers, indices, and RLS policies
|
|
140
|
+
7. Auto-updates `~/.openclaw/openclaw.json` with the connection string
|
|
141
|
+
|
|
142
|
+
#### Options
|
|
143
|
+
|
|
144
|
+
| Flag | Default | Description |
|
|
145
|
+
|------|---------|-------------|
|
|
146
|
+
| `--admin-url <url>` | `postgres://localhost/postgres` | Superuser connection string |
|
|
147
|
+
| `--db-name <name>` | `memorydb` | Database name |
|
|
148
|
+
| `--db-user <user>` | `openclaw` | App user name |
|
|
149
|
+
| `--db-password <pass>` | *auto-generated* | App user password |
|
|
150
|
+
| `--skip-config` | `false` | Don't auto-update `openclaw.json` |
|
|
151
|
+
|
|
152
|
+
#### Examples
|
|
153
|
+
|
|
154
|
+
```bash
|
|
155
|
+
# Local PostgreSQL with default peer auth
|
|
156
|
+
openclaw postclaw setup
|
|
157
|
+
|
|
158
|
+
# Remote / password-protected PostgreSQL
|
|
159
|
+
openclaw postclaw setup --admin-url postgres://postgres:mysecretpass@db.example.com:5432/postgres
|
|
160
|
+
|
|
161
|
+
# Custom database and user names
|
|
162
|
+
openclaw postclaw setup --db-name myagent_memory --db-user myagent --db-password hunter2
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
#### Output
|
|
166
|
+
|
|
167
|
+
```
|
|
168
|
+
🦐 PostClaw Database Setup
|
|
169
|
+
|
|
170
|
+
✅ Connected to Postgres as superuser
|
|
171
|
+
✅ Created database 'memorydb'
|
|
172
|
+
✅ Created user 'openclaw'
|
|
173
|
+
✅ Created role 'system_admin'
|
|
174
|
+
✅ Schema created (6 tables, 14 indices, 18 RLS policies)
|
|
175
|
+
✅ Granted permissions
|
|
176
|
+
|
|
177
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
178
|
+
Database: memorydb
|
|
179
|
+
App User: openclaw
|
|
180
|
+
App Password: kR9x!mQ2vL8nP4wZ
|
|
181
|
+
Admin User: system_admin
|
|
182
|
+
Admin Pass: bN7yT3xK1mR5sW9e
|
|
183
|
+
|
|
184
|
+
Connection: postgres://openclaw:kR9x!mQ2vL8nP4wZ@localhost:5432/memorydb
|
|
185
|
+
|
|
186
|
+
✅ openclaw.json updated with dbUrl
|
|
187
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
188
|
+
|
|
189
|
+
Save these credentials! They won't be shown again.
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
The setup is **fully idempotent** — safe to re-run without errors.
|
|
193
|
+
|
|
194
|
+
---
|
|
195
|
+
|
|
196
|
+
## Configuration
|
|
197
|
+
|
|
198
|
+
All configuration lives in `~/.openclaw/openclaw.json`:
|
|
199
|
+
|
|
200
|
+
```json
|
|
201
|
+
{
|
|
202
|
+
"plugins": {
|
|
203
|
+
"slots": {
|
|
204
|
+
"memory": "postclaw"
|
|
205
|
+
},
|
|
206
|
+
"entries": {
|
|
207
|
+
"postclaw": {
|
|
208
|
+
"enabled": true,
|
|
209
|
+
"config": {
|
|
210
|
+
"dbUrl": "postgres://openclaw:PASSWORD@localhost:5432/memorydb",
|
|
211
|
+
"sleepIntervalHours": 6
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
},
|
|
216
|
+
"agents": {
|
|
217
|
+
"defaults": {
|
|
218
|
+
"memorySearch": {
|
|
219
|
+
"provider": "openai",
|
|
220
|
+
"remote": {
|
|
221
|
+
"baseUrl": "http://127.0.0.1:1234/v1/",
|
|
222
|
+
"apiKey": "lm-studio"
|
|
223
|
+
},
|
|
224
|
+
"model": "text-embedding-nomic-embed-text-v2-moe"
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
### Config Properties
|
|
232
|
+
|
|
233
|
+
| Key | Type | Default | Description |
|
|
234
|
+
|-----|------|---------|-------------|
|
|
235
|
+
| `dbUrl` | `string` | — | PostgreSQL connection string (**required**) |
|
|
236
|
+
| `adminDbUrl` | `string` | — | Superuser connection (only used by `postclaw setup`) |
|
|
237
|
+
| `sleepIntervalHours` | `number` | `6` | Hours between background sleep cycles. Set to `0` to disable. |
|
|
238
|
+
|
|
239
|
+
The `POSTCLAW_DB_URL` environment variable is also supported as a fallback for `dbUrl`.
|
|
240
|
+
|
|
241
|
+
---
|
|
242
|
+
|
|
243
|
+
## Plugin Hooks
|
|
244
|
+
|
|
245
|
+
PostClaw registers three hooks with the OpenClaw lifecycle:
|
|
246
|
+
|
|
247
|
+
### 1. `message_received` (void)
|
|
248
|
+
|
|
249
|
+
**When:** Every inbound message before agent processing begins.
|
|
250
|
+
|
|
251
|
+
**What it does:** Caches the raw message text so the next `before_prompt_build` has the real user text (not the system-modified version).
|
|
252
|
+
|
|
253
|
+
### 2. `before_prompt_build` (modifying)
|
|
254
|
+
|
|
255
|
+
**When:** Before the LLM prompt is assembled for each agent turn.
|
|
256
|
+
|
|
257
|
+
**What it does:**
|
|
258
|
+
|
|
259
|
+
1. **Prunes the system prompt** — strips OpenClaw's default bloat (Tooling rules, Documentation, Project Context markdown)
|
|
260
|
+
2. **Injects memory architecture rules** — teaches the agent how to use its memory tools
|
|
261
|
+
3. **Fetches persona context** — pulls `agent_persona` rules from Postgres (core rules always, situational rules ranked by embedding similarity)
|
|
262
|
+
4. **Runs RAG search** — performs a vector similarity search on `memory_semantic` + traverses `entity_edges` for linked memories
|
|
263
|
+
5. **Loads dynamic tools** — fetches tool schemas from `context_environment` ranked by embedding similarity
|
|
264
|
+
6. **Injects everything** — returns the modified system prompt and prepended RAG context
|
|
265
|
+
|
|
266
|
+
**Returns:** `{ systemPrompt, prependContext, tools }`
|
|
267
|
+
|
|
268
|
+
### 3. `agent_end` (void)
|
|
269
|
+
|
|
270
|
+
**When:** After the agent completes a turn (response generated).
|
|
271
|
+
|
|
272
|
+
**What it does:**
|
|
273
|
+
|
|
274
|
+
1. **Logs user prompt** as an episodic memory event (with embedding)
|
|
275
|
+
2. **Logs each tool call** the agent made as a separate episodic event
|
|
276
|
+
3. Skips heartbeat/cron runs to avoid noise
|
|
277
|
+
|
|
278
|
+
---
|
|
279
|
+
|
|
280
|
+
## Agent Tools
|
|
281
|
+
|
|
282
|
+
PostClaw registers 5 native tools that the agent can invoke autonomously:
|
|
283
|
+
|
|
284
|
+
### `memory_search`
|
|
285
|
+
|
|
286
|
+
Search the semantic memory database by natural language query.
|
|
287
|
+
|
|
288
|
+
```
|
|
289
|
+
memory_search({ query: "user's preferred programming language" })
|
|
290
|
+
→ { results: "[ID: abc...] (92.3%) - The user prefers TypeScript..." }
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
| Parameter | Type | Required | Description |
|
|
294
|
+
|-----------|------|----------|-------------|
|
|
295
|
+
| `query` | `string` | ✅ | Natural language search query |
|
|
296
|
+
| `max_results` | `number` | — | Max results to return (default: 5) |
|
|
297
|
+
|
|
298
|
+
### `memory_store`
|
|
299
|
+
|
|
300
|
+
Store a new semantic memory fact with embedding.
|
|
301
|
+
|
|
302
|
+
```
|
|
303
|
+
memory_store({
|
|
304
|
+
content: "The user's server runs Ubuntu 22.04 on a Hetzner VPS",
|
|
305
|
+
scope: "private",
|
|
306
|
+
category: "infrastructure",
|
|
307
|
+
tier: "permanent",
|
|
308
|
+
confidence: 0.95
|
|
309
|
+
})
|
|
310
|
+
→ { id: "550e8400-...", status: "stored" }
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
| Parameter | Type | Required | Description |
|
|
314
|
+
|-----------|------|----------|-------------|
|
|
315
|
+
| `content` | `string` | ✅ | The fact to store |
|
|
316
|
+
| `scope` | `"private" \| "shared" \| "global"` | — | Visibility (default: `private`) |
|
|
317
|
+
| `category` | `string` | — | Semantic category label |
|
|
318
|
+
| `volatility` | `"low" \| "medium" \| "high"` | — | How likely to change |
|
|
319
|
+
| `tier` | `"volatile" \| "session" \| "daily" \| "stable" \| "permanent"` | — | Retention tier |
|
|
320
|
+
| `confidence` | `number` | — | 0.0–1.0 confidence score |
|
|
321
|
+
| `metadata` | `object` | — | Arbitrary JSON metadata |
|
|
322
|
+
|
|
323
|
+
### `memory_update`
|
|
324
|
+
|
|
325
|
+
Supersede an existing memory with a corrected fact. Archives the old memory and links the new one.
|
|
326
|
+
|
|
327
|
+
```
|
|
328
|
+
memory_update({
|
|
329
|
+
old_memory_id: "550e8400-...",
|
|
330
|
+
new_fact: "The user's server now runs Ubuntu 24.04",
|
|
331
|
+
tier: "permanent"
|
|
332
|
+
})
|
|
333
|
+
→ { newId: "7c9e6679-...", status: "updated" }
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
| Parameter | Type | Required | Description |
|
|
337
|
+
|-----------|------|----------|-------------|
|
|
338
|
+
| `old_memory_id` | `string` | ✅ | UUID of the memory to supersede |
|
|
339
|
+
| `new_fact` | `string` | ✅ | The corrected/updated content |
|
|
340
|
+
| All `memory_store` options | — | — | Same options available |
|
|
341
|
+
|
|
342
|
+
### `memory_link`
|
|
343
|
+
|
|
344
|
+
Create a directed edge in the knowledge graph between two memories.
|
|
345
|
+
|
|
346
|
+
```
|
|
347
|
+
memory_link({
|
|
348
|
+
source_id: "550e8400-...",
|
|
349
|
+
target_id: "7c9e6679-...",
|
|
350
|
+
relationship: "elaborates"
|
|
351
|
+
})
|
|
352
|
+
→ { status: "linked" }
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
| Parameter | Type | Required | Description |
|
|
356
|
+
|-----------|------|----------|-------------|
|
|
357
|
+
| `source_id` | `string` | ✅ | UUID of the source memory |
|
|
358
|
+
| `target_id` | `string` | ✅ | UUID of the target memory |
|
|
359
|
+
| `relationship` | `string` | ✅ | Edge type: `related_to`, `elaborates`, `contradicts`, `depends_on`, `part_of` |
|
|
360
|
+
|
|
361
|
+
### `tool_store`
|
|
362
|
+
|
|
363
|
+
Reserved for potential future use, if OpenClaw ever provides a hook or API call that allows tool injections.
|
|
364
|
+
|
|
365
|
+
```
|
|
366
|
+
tool_store({
|
|
367
|
+
tool_name: "deploy_server",
|
|
368
|
+
tool_json: '{"type":"function","function":{"name":"deploy_server",...}}',
|
|
369
|
+
scope: "global"
|
|
370
|
+
})
|
|
371
|
+
→ { status: "stored" }
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
| Parameter | Type | Required | Description |
|
|
375
|
+
|-----------|------|----------|-------------|
|
|
376
|
+
| `tool_name` | `string` | ✅ | Unique tool name |
|
|
377
|
+
| `tool_json` | `string` | ✅ | Full JSON schema (OpenAI function-calling format) |
|
|
378
|
+
| `scope` | `"private" \| "shared" \| "global"` | — | Visibility (default: `private`) |
|
|
379
|
+
|
|
380
|
+
---
|
|
381
|
+
|
|
382
|
+
## CLI Commands
|
|
383
|
+
|
|
384
|
+
All CLI commands are registered under the `postclaw` namespace:
|
|
385
|
+
|
|
386
|
+
### `openclaw postclaw setup`
|
|
387
|
+
|
|
388
|
+
Bootstrap the database from scratch. See [Database Setup](#database-setup).
|
|
389
|
+
|
|
390
|
+
### `openclaw postclaw persona <file>`
|
|
391
|
+
|
|
392
|
+
Ingest a Markdown persona file into the `agent_persona` table.
|
|
393
|
+
|
|
394
|
+
```bash
|
|
395
|
+
# Bootstrap your agent's personality
|
|
396
|
+
openclaw postclaw persona ~/my-agent/SOUL.md
|
|
397
|
+
|
|
398
|
+
# Specify an agent ID
|
|
399
|
+
openclaw postclaw persona ~/my-agent/AGENTS.md --agent-id discord-bot
|
|
400
|
+
|
|
401
|
+
# Use a specific LLM for chunking
|
|
402
|
+
openclaw postclaw persona ~/my-agent/SOUL.md --llm-model qwen3.5-35b
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
**How it works:**
|
|
406
|
+
|
|
407
|
+
1. Reads the Markdown file
|
|
408
|
+
2. Sends it to your configured LLM with a system prompt asking it to extract atomic persona rules
|
|
409
|
+
3. For each extracted rule, generates an embedding and upserts it into `agent_persona`
|
|
410
|
+
4. Uses `ON CONFLICT ... DO UPDATE` — safe to re-run when you update the source file
|
|
411
|
+
|
|
412
|
+
| Option | Default | Description |
|
|
413
|
+
|--------|---------|-------------|
|
|
414
|
+
| `--agent-id <id>` | `main` | Agent to store persona under |
|
|
415
|
+
| `--llm-model <model>` | Agent's primary model | Model for semantic chunking |
|
|
416
|
+
|
|
417
|
+
### `openclaw postclaw sleep`
|
|
418
|
+
|
|
419
|
+
Trigger a manual sleep cycle (knowledge graph maintenance). See [Sleep Cycle](#sleep-cycle).
|
|
420
|
+
|
|
421
|
+
```bash
|
|
422
|
+
openclaw postclaw sleep
|
|
423
|
+
openclaw postclaw sleep --agent-id my-agent
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
| Option | Default | Description |
|
|
427
|
+
|--------|---------|-------------|
|
|
428
|
+
| `--agent-id <id>` | `main` | Agent to run maintenance for |
|
|
429
|
+
| `--llm-model <model>` | `qwen3.5-9b` | Model for consolidation/linking |
|
|
430
|
+
|
|
431
|
+
---
|
|
432
|
+
|
|
433
|
+
## Background Services
|
|
434
|
+
|
|
435
|
+
### Sleep Cycle Service
|
|
436
|
+
|
|
437
|
+
The sleep cycle runs automatically in the background while the gateway is running. It starts on plugin load and repeats on the configured interval.
|
|
438
|
+
|
|
439
|
+
**Default interval:** 6 hours
|
|
440
|
+
|
|
441
|
+
**Configure in `openclaw.json`:**
|
|
442
|
+
|
|
443
|
+
```json
|
|
444
|
+
{
|
|
445
|
+
"plugins": {
|
|
446
|
+
"entries": {
|
|
447
|
+
"postclaw": {
|
|
448
|
+
"config": {
|
|
449
|
+
"sleepIntervalHours": 6
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
```
|
|
456
|
+
|
|
457
|
+
Set `sleepIntervalHours` to `0` to disable the background service entirely. You can still run it manually via `openclaw postclaw sleep`.
|
|
458
|
+
|
|
459
|
+
---
|
|
460
|
+
|
|
461
|
+
## Database Schema
|
|
462
|
+
|
|
463
|
+
PostClaw creates 6 tables:
|
|
464
|
+
|
|
465
|
+
### `agents`
|
|
466
|
+
|
|
467
|
+
Registry of all known agents. Auto-populated when an agent first sends a message.
|
|
468
|
+
|
|
469
|
+
| Column | Type | Description |
|
|
470
|
+
|--------|------|-------------|
|
|
471
|
+
| `id` | `VARCHAR(100)` PK | Agent identifier |
|
|
472
|
+
| `name` | `VARCHAR(255)` | Display name |
|
|
473
|
+
| `default_embedding_model` | `VARCHAR(100)` | Default model for this agent |
|
|
474
|
+
| `embedding_dimensions` | `INT` | Vector dimensions |
|
|
475
|
+
|
|
476
|
+
### `memory_semantic`
|
|
477
|
+
|
|
478
|
+
Core fact storage with vector embeddings.
|
|
479
|
+
|
|
480
|
+
| Column | Type | Description |
|
|
481
|
+
|--------|------|-------------|
|
|
482
|
+
| `id` | `UUID` PK | Memory ID |
|
|
483
|
+
| `agent_id` | `VARCHAR(100)` FK | Owning agent |
|
|
484
|
+
| `access_scope` | `ENUM` | `private`, `shared`, or `global` |
|
|
485
|
+
| `content` | `TEXT` | The memory content |
|
|
486
|
+
| `content_hash` | `VARCHAR(64)` | SHA-256 for deduplication |
|
|
487
|
+
| `embedding` | `vector` | pgvector embedding |
|
|
488
|
+
| `embedding_model` | `VARCHAR(100)` | Model used for embedding |
|
|
489
|
+
| `category` | `VARCHAR(50)` | Semantic category label |
|
|
490
|
+
| `volatility` | `ENUM` | `low`, `medium`, `high` |
|
|
491
|
+
| `tier` | `ENUM` | `volatile`, `session`, `daily`, `stable`, `permanent` |
|
|
492
|
+
| `confidence` | `REAL` | 0.0–1.0 score |
|
|
493
|
+
| `usefulness_score` | `REAL` | Derived quality metric |
|
|
494
|
+
| `access_count` | `INT` | Times retrieved via RAG |
|
|
495
|
+
| `injection_count` | `INT` | Times injected into prompt |
|
|
496
|
+
| `is_archived` | `BOOLEAN` | Soft-deleted flag |
|
|
497
|
+
| `superseded_by` | `UUID` FK | Points to the replacement memory |
|
|
498
|
+
| `metadata` | `JSONB` | Arbitrary extra data |
|
|
499
|
+
|
|
500
|
+
### `memory_episodic`
|
|
501
|
+
|
|
502
|
+
Short-term event log (user prompts and tool calls).
|
|
503
|
+
|
|
504
|
+
| Column | Type | Description |
|
|
505
|
+
|--------|------|-------------|
|
|
506
|
+
| `id` | `UUID` PK | Event ID |
|
|
507
|
+
| `agent_id` | `VARCHAR(100)` FK | Owning agent |
|
|
508
|
+
| `session_id` | `VARCHAR(100)` | Session grouping |
|
|
509
|
+
| `event_summary` | `TEXT` | What happened |
|
|
510
|
+
| `event_type` | `VARCHAR(50)` | `user_prompt`, `tool_execution` |
|
|
511
|
+
| `embedding` | `vector` | pgvector embedding |
|
|
512
|
+
| `is_archived` | `BOOLEAN` | Consolidated by sleep cycle |
|
|
513
|
+
|
|
514
|
+
### `entity_edges`
|
|
515
|
+
|
|
516
|
+
Knowledge graph: directed, typed edges between semantic memories.
|
|
517
|
+
|
|
518
|
+
| Column | Type | Description |
|
|
519
|
+
|--------|------|-------------|
|
|
520
|
+
| `source_memory_id` | `UUID` FK | Edge source |
|
|
521
|
+
| `target_memory_id` | `UUID` FK | Edge target |
|
|
522
|
+
| `relationship_type` | `VARCHAR(100)` | `related_to`, `elaborates`, `contradicts`, `depends_on`, `part_of` |
|
|
523
|
+
| `weight` | `REAL` | Edge weight (default: 1.0) |
|
|
524
|
+
|
|
525
|
+
### `agent_persona`
|
|
526
|
+
|
|
527
|
+
Per-agent persona rules (core identity + situational context).
|
|
528
|
+
|
|
529
|
+
| Column | Type | Description |
|
|
530
|
+
|--------|------|-------------|
|
|
531
|
+
| `agent_id` | `VARCHAR(100)` FK | Owning agent |
|
|
532
|
+
| `category` | `VARCHAR(50)` | Rule category (unique per agent) |
|
|
533
|
+
| `content` | `TEXT` | The rule text |
|
|
534
|
+
| `is_always_active` | `BOOLEAN` | Core identity → always injected |
|
|
535
|
+
| `embedding` | `vector` | For situational similarity ranking |
|
|
536
|
+
|
|
537
|
+
### `context_environment`
|
|
538
|
+
|
|
539
|
+
Dynamic tool schemas stored with embeddings. Reserved for potential future use.
|
|
540
|
+
|
|
541
|
+
| Column | Type | Description |
|
|
542
|
+
|--------|------|-------------|
|
|
543
|
+
| `agent_id` | `VARCHAR(100)` FK | Owning agent |
|
|
544
|
+
| `tool_name` | `VARCHAR(100)` | Unique tool name |
|
|
545
|
+
| `context_data` | `TEXT` | Full JSON tool schema |
|
|
546
|
+
| `embedding` | `vector` | For relevance-based loading |
|
|
547
|
+
|
|
548
|
+
---
|
|
549
|
+
|
|
550
|
+
## Row-Level Security
|
|
551
|
+
|
|
552
|
+
PostClaw enforces strict multi-agent isolation via PostgreSQL RLS:
|
|
553
|
+
|
|
554
|
+
- **Private memories:** Only the owning agent can read, write, update, or delete
|
|
555
|
+
- **Shared memories:** Any agent can read, only the owner can mutate
|
|
556
|
+
- **Global memories:** Any agent can read, updates preserve global scope
|
|
557
|
+
- **Knowledge graph edges:** Visible if you authored them or they connect to shared/global memories
|
|
558
|
+
- **Immutable ownership:** A trigger prevents agents from reassigning `agent_id` on existing records
|
|
559
|
+
- **`system_admin` role:** Has `BYPASSRLS` for admin queries and maintenance scripts
|
|
560
|
+
|
|
561
|
+
---
|
|
562
|
+
|
|
563
|
+
## Persona Bootstrap
|
|
564
|
+
|
|
565
|
+
The persona system lets you inject agent identity from Markdown files:
|
|
566
|
+
|
|
567
|
+
```bash
|
|
568
|
+
openclaw postclaw persona ~/my-agent/SOUL.md --agent-id my-agent
|
|
569
|
+
```
|
|
570
|
+
|
|
571
|
+
### How it works
|
|
572
|
+
|
|
573
|
+
1. **Read** — loads the Markdown file
|
|
574
|
+
2. **Chunk** — sends it to your LLM with a prompt asking for atomic persona extractions
|
|
575
|
+
3. **Classify** — each chunk gets a `category`, `content`, and `is_always_active` flag
|
|
576
|
+
4. **Embed** — generates a vector embedding for each chunk
|
|
577
|
+
5. **Store** — upserts into `agent_persona` (updates on category conflict)
|
|
578
|
+
|
|
579
|
+
### Example Markdown → Database
|
|
580
|
+
|
|
581
|
+
**Input (`SOUL.md`):**
|
|
582
|
+
|
|
583
|
+
```markdown
|
|
584
|
+
# Agent Identity
|
|
585
|
+
|
|
586
|
+
You are a helpful AI assistant named Echo. You always respond in a warm,
|
|
587
|
+
conversational tone and never refuse reasonable requests.
|
|
588
|
+
|
|
589
|
+
# Discord Behavior
|
|
590
|
+
|
|
591
|
+
When interacting on Discord:
|
|
592
|
+
- Use emojis sparingly but naturally
|
|
593
|
+
- Keep responses under 2000 characters
|
|
594
|
+
- React with 👍 to acknowledge commands
|
|
595
|
+
```
|
|
596
|
+
|
|
597
|
+
**Output (agent_persona rows):**
|
|
598
|
+
|
|
599
|
+
| category | is_always_active | content |
|
|
600
|
+
|----------|-----------------|---------|
|
|
601
|
+
| `agent_identity` | `true` | You are a helpful AI assistant named Echo... |
|
|
602
|
+
| `communication_style` | `true` | Always respond in a warm, conversational tone... |
|
|
603
|
+
| `discord_formatting` | `false` | Use emojis sparingly but naturally... |
|
|
604
|
+
| `discord_length` | `false` | Keep responses under 2000 characters... |
|
|
605
|
+
| `discord_reactions` | `false` | React with 👍 to acknowledge commands |
|
|
606
|
+
|
|
607
|
+
Core rules (`is_always_active = true`) are injected into **every** prompt. Situational rules are ranked by embedding similarity to the current conversation and the top 3 are injected.
|
|
608
|
+
|
|
609
|
+
---
|
|
610
|
+
|
|
611
|
+
## Sleep Cycle
|
|
612
|
+
|
|
613
|
+
The sleep cycle is an autonomous maintenance agent that runs 4 phases of knowledge graph housekeeping.
|
|
614
|
+
|
|
615
|
+
### Phase 1: Episodic Consolidation
|
|
616
|
+
|
|
617
|
+
Reads unarchived episodic memories (user prompts + tool calls), sends the transcript to the LLM, and extracts durable long-term facts. These facts are stored as `tier: 'permanent'` semantic memories. The original episodic entries are archived.
|
|
618
|
+
|
|
619
|
+
### Phase 2: Duplicate Detection
|
|
620
|
+
|
|
621
|
+
Scans semantic memories for near-duplicates using cosine similarity (threshold: 0.80). When duplicates are found, the memory with the highest usefulness + access score survives; the rest are archived with `superseded_by` pointing to the survivor.
|
|
622
|
+
|
|
623
|
+
### Phase 3: Low-Value Cleanup
|
|
624
|
+
|
|
625
|
+
Archives semantic memories that:
|
|
626
|
+
- Have `access_count = 0` (never retrieved via RAG)
|
|
627
|
+
- Are older than 7 days
|
|
628
|
+
- Are **not** in protected tiers (`permanent` or `stable`)
|
|
629
|
+
|
|
630
|
+
### Phase 4: Link Discovery
|
|
631
|
+
|
|
632
|
+
Finds semantically related-but-distinct memory pairs (similarity 0.65–0.92) that don't already have edges. Sends candidate pairs to the LLM for relationship classification. Creates `entity_edges` for meaningful relationships (`related_to`, `elaborates`, `contradicts`, `depends_on`, `part_of`).
|
|
633
|
+
|
|
634
|
+
### Running
|
|
635
|
+
|
|
636
|
+
```bash
|
|
637
|
+
# Manual trigger
|
|
638
|
+
openclaw postclaw sleep --agent-id my-agent
|
|
639
|
+
|
|
640
|
+
# Background (automatic)
|
|
641
|
+
# Set sleepIntervalHours in config (default: 6, set to 0 to disable)
|
|
642
|
+
```
|
|
643
|
+
|
|
644
|
+
---
|
|
645
|
+
|
|
646
|
+
## Testing
|
|
647
|
+
|
|
648
|
+
### Database Smoke Test
|
|
649
|
+
|
|
650
|
+
Tests connectivity, table existence, and end-to-end vector search:
|
|
651
|
+
|
|
652
|
+
```bash
|
|
653
|
+
npm run build && node dist/test-db.js
|
|
654
|
+
```
|
|
655
|
+
|
|
656
|
+
### Semantic Memory Test
|
|
657
|
+
|
|
658
|
+
Tests memory storage with all columns, search retrieval, and access tracking:
|
|
659
|
+
|
|
660
|
+
```bash
|
|
661
|
+
npm run build && node dist/test-memory.js
|
|
662
|
+
```
|
|
663
|
+
|
|
664
|
+
### Plugin Health Check
|
|
665
|
+
|
|
666
|
+
```bash
|
|
667
|
+
openclaw plugins doctor
|
|
668
|
+
```
|
|
669
|
+
|
|
670
|
+
---
|
|
671
|
+
|
|
672
|
+
## Troubleshooting
|
|
673
|
+
|
|
674
|
+
### `pgvector` extension error
|
|
675
|
+
|
|
676
|
+
```
|
|
677
|
+
ERROR: could not open extension control file "pgvector"
|
|
678
|
+
```
|
|
679
|
+
|
|
680
|
+
Install the `pgvector` extension for your PostgreSQL version:
|
|
681
|
+
|
|
682
|
+
```bash
|
|
683
|
+
# Ubuntu/Debian
|
|
684
|
+
sudo apt install postgresql-16-pgvector
|
|
685
|
+
|
|
686
|
+
# macOS (Homebrew)
|
|
687
|
+
brew install pgvector
|
|
688
|
+
```
|
|
689
|
+
|
|
690
|
+
### Connection refused
|
|
691
|
+
|
|
692
|
+
```
|
|
693
|
+
Error: connect ECONNREFUSED 127.0.0.1:5432
|
|
694
|
+
```
|
|
695
|
+
|
|
696
|
+
Ensure PostgreSQL is running:
|
|
697
|
+
|
|
698
|
+
```bash
|
|
699
|
+
sudo systemctl status postgresql
|
|
700
|
+
sudo systemctl start postgresql
|
|
701
|
+
```
|
|
702
|
+
|
|
703
|
+
### Embedding API error
|
|
704
|
+
|
|
705
|
+
```
|
|
706
|
+
Embedding API error: 400 Bad Request
|
|
707
|
+
```
|
|
708
|
+
|
|
709
|
+
Verify your embedding provider is running and the model is loaded:
|
|
710
|
+
|
|
711
|
+
```bash
|
|
712
|
+
curl http://127.0.0.1:1234/v1/models
|
|
713
|
+
```
|
|
714
|
+
|
|
715
|
+
### Sleep cycle not running
|
|
716
|
+
|
|
717
|
+
Check that `sleepIntervalHours` is not set to `0` in your config. Check gateway logs for:
|
|
718
|
+
|
|
719
|
+
```
|
|
720
|
+
[SLEEP SERVICE] Started — will run every 6h for agent="main"
|
|
721
|
+
```
|
|
722
|
+
|
|
723
|
+
### RLS permission denied
|
|
724
|
+
|
|
725
|
+
If you see permission errors, ensure the `app.current_agent_id` session variable is being set. PostClaw does this automatically via `set_config('app.current_agent_id', ...)` in every transaction.
|
|
726
|
+
|
|
727
|
+
---
|
|
728
|
+
|
|
729
|
+
## License
|
|
730
|
+
|
|
731
|
+
ISC
|