@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.
Files changed (46) hide show
  1. package/.env.example +4 -0
  2. package/LICENSE +15 -0
  3. package/README.md +731 -0
  4. package/dist/index.d.ts +36 -0
  5. package/dist/index.d.ts.map +1 -0
  6. package/dist/index.js +506 -0
  7. package/dist/index.js.map +1 -0
  8. package/dist/schemas/validation.d.ts +81 -0
  9. package/dist/schemas/validation.d.ts.map +1 -0
  10. package/dist/schemas/validation.js +27 -0
  11. package/dist/schemas/validation.js.map +1 -0
  12. package/dist/scripts/bootstrap_persona.d.ts +20 -0
  13. package/dist/scripts/bootstrap_persona.d.ts.map +1 -0
  14. package/dist/scripts/bootstrap_persona.js +150 -0
  15. package/dist/scripts/bootstrap_persona.js.map +1 -0
  16. package/dist/scripts/bootstrap_tools.d.ts +2 -0
  17. package/dist/scripts/bootstrap_tools.d.ts.map +1 -0
  18. package/dist/scripts/bootstrap_tools.js +67 -0
  19. package/dist/scripts/bootstrap_tools.js.map +1 -0
  20. package/dist/scripts/setup-db.d.ts +28 -0
  21. package/dist/scripts/setup-db.d.ts.map +1 -0
  22. package/dist/scripts/setup-db.js +539 -0
  23. package/dist/scripts/setup-db.js.map +1 -0
  24. package/dist/scripts/sleep_cycle.d.ts +32 -0
  25. package/dist/scripts/sleep_cycle.d.ts.map +1 -0
  26. package/dist/scripts/sleep_cycle.js +452 -0
  27. package/dist/scripts/sleep_cycle.js.map +1 -0
  28. package/dist/services/db.d.ts +28 -0
  29. package/dist/services/db.d.ts.map +1 -0
  30. package/dist/services/db.js +93 -0
  31. package/dist/services/db.js.map +1 -0
  32. package/dist/services/memoryService.d.ts +75 -0
  33. package/dist/services/memoryService.d.ts.map +1 -0
  34. package/dist/services/memoryService.js +391 -0
  35. package/dist/services/memoryService.js.map +1 -0
  36. package/dist/test-db.d.ts +8 -0
  37. package/dist/test-db.d.ts.map +1 -0
  38. package/dist/test-db.js +94 -0
  39. package/dist/test-db.js.map +1 -0
  40. package/dist/test-memory.d.ts +2 -0
  41. package/dist/test-memory.d.ts.map +1 -0
  42. package/dist/test-memory.js +79 -0
  43. package/dist/test-memory.js.map +1 -0
  44. package/openclaw.plugin.json +37 -0
  45. package/package.json +49 -0
  46. 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