supaclaw 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/LICENSE +21 -0
- package/README.md +871 -0
- package/SCHEMA.md +215 -0
- package/dist/clawdbot-integration.d.ts +171 -0
- package/dist/clawdbot-integration.js +339 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +1815 -0
- package/dist/context-manager.d.ts +143 -0
- package/dist/context-manager.js +360 -0
- package/dist/error-handling.d.ts +100 -0
- package/dist/error-handling.js +301 -0
- package/dist/index.d.ts +735 -0
- package/dist/index.js +2256 -0
- package/dist/parsers.d.ts +115 -0
- package/dist/parsers.js +406 -0
- package/migrations/001_initial.sql +153 -0
- package/migrations/002_vector_search.sql +219 -0
- package/migrations/003_entity_relationships.sql +143 -0
- package/package.json +66 -0
package/README.md
ADDED
|
@@ -0,0 +1,871 @@
|
|
|
1
|
+
# 🧠 Supaclaw
|
|
2
|
+
|
|
3
|
+
**Persistent memory for AI agents using Supabase.**
|
|
4
|
+
|
|
5
|
+
Stop losing context. Stop re-reading massive markdown files. Give your agent a real memory.
|
|
6
|
+
|
|
7
|
+
[](https://www.npmjs.com/package/supaclaw)
|
|
8
|
+
[](https://opensource.org/licenses/MIT)
|
|
9
|
+
|
|
10
|
+
## The Problem
|
|
11
|
+
|
|
12
|
+
AI agents using file-based memory (MEMORY.md, daily logs) face:
|
|
13
|
+
- **Context window bloat** - Files grow unbounded, eating your token budget
|
|
14
|
+
- **Forgetting** - Context resets wipe session memory
|
|
15
|
+
- **No search** - Linear scan through text to find relevant info
|
|
16
|
+
- **Unstructured** - Can't query "what did we discuss about X?"
|
|
17
|
+
|
|
18
|
+
## The Solution
|
|
19
|
+
|
|
20
|
+
Supaclaw uses **Supabase (Postgres)** to give your agent:
|
|
21
|
+
- ✅ **Session tracking** - Every conversation logged with metadata
|
|
22
|
+
- ✅ **Semantic search** - Find relevant memories via vector similarity (pgvector)
|
|
23
|
+
- ✅ **Smart context** - Only load what's relevant, not everything
|
|
24
|
+
- ✅ **Context window management** - Token budgeting, smart selection, lost-in-middle mitigation
|
|
25
|
+
- ✅ **Entity relationships** - Knowledge graph with multi-hop traversal, confidence scoring
|
|
26
|
+
- ✅ **Multi-agent** - Share memories across agents
|
|
27
|
+
- ✅ **Structured data** - SQL queries, relationships, types
|
|
28
|
+
|
|
29
|
+
## Quick Start
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
npm install supaclaw
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
```typescript
|
|
36
|
+
import { OpenClawMemory } from 'supaclaw';
|
|
37
|
+
|
|
38
|
+
const memory = new OpenClawMemory({
|
|
39
|
+
supabaseUrl: process.env.SUPABASE_URL,
|
|
40
|
+
supabaseKey: process.env.SUPABASE_KEY,
|
|
41
|
+
agentId: 'my-agent',
|
|
42
|
+
// Optional: Enable semantic search with OpenAI embeddings
|
|
43
|
+
embeddingProvider: 'openai',
|
|
44
|
+
openaiApiKey: process.env.OPENAI_API_KEY
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
// Initialize tables (first run only)
|
|
48
|
+
await memory.initialize();
|
|
49
|
+
|
|
50
|
+
// Start a conversation session
|
|
51
|
+
const session = await memory.startSession({
|
|
52
|
+
userId: 'user-123',
|
|
53
|
+
channel: 'telegram'
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
// Log messages
|
|
57
|
+
await memory.addMessage(session.id, {
|
|
58
|
+
role: 'user',
|
|
59
|
+
content: 'Remember that I prefer TypeScript over JavaScript'
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
// Create a persistent memory
|
|
63
|
+
await memory.remember({
|
|
64
|
+
content: 'User prefers TypeScript over JavaScript',
|
|
65
|
+
category: 'preference',
|
|
66
|
+
importance: 0.9
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
// Later: recall relevant memories (semantic search if embeddings enabled)
|
|
70
|
+
const memories = await memory.recall('programming language preferences', {
|
|
71
|
+
minSimilarity: 0.7, // Cosine similarity threshold
|
|
72
|
+
limit: 10
|
|
73
|
+
});
|
|
74
|
+
// Returns: [{ content: 'User prefers TypeScript...', importance: 0.9, similarity: 0.85, ... }]
|
|
75
|
+
|
|
76
|
+
// Or use hybrid search (combines semantic + keyword matching)
|
|
77
|
+
const hybrid = await memory.hybridRecall('coding tips', {
|
|
78
|
+
vectorWeight: 0.7, // Weight for semantic similarity
|
|
79
|
+
keywordWeight: 0.3, // Weight for keyword matching
|
|
80
|
+
limit: 10
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
// Find memories similar to an existing one
|
|
84
|
+
const similar = await memory.findSimilarMemories(memoryId, {
|
|
85
|
+
minSimilarity: 0.8,
|
|
86
|
+
limit: 5
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
// End session with AI-generated summary
|
|
90
|
+
await memory.endSession(session.id, { autoSummarize: true });
|
|
91
|
+
|
|
92
|
+
// Resume interrupted session
|
|
93
|
+
const { session, messages, context } = await memory.resumeSession(sessionId);
|
|
94
|
+
|
|
95
|
+
// Extract entities from conversation
|
|
96
|
+
const entities = await memory.extractEntities('I love using Claude for coding');
|
|
97
|
+
// Returns: [{ entity_type: 'product', name: 'Claude', description: '...' }]
|
|
98
|
+
|
|
99
|
+
// Extract entities AND relationships in one call
|
|
100
|
+
const { entities, relationships } = await memory.extractEntitiesWithRelationships(
|
|
101
|
+
'Alice works at TechCorp in San Francisco'
|
|
102
|
+
);
|
|
103
|
+
// Returns entities: Han (person), TechCorp (organization), San Francisco (place)
|
|
104
|
+
// Returns relationships: Han → works_at → TechCorp, TechCorp → located_in → San Francisco
|
|
105
|
+
|
|
106
|
+
// Find related entities through graph traversal (multi-hop)
|
|
107
|
+
const related = await memory.findRelatedEntities(hanId, { maxDepth: 2 });
|
|
108
|
+
// Returns entities connected within 2 hops with relationship paths and confidence scores
|
|
109
|
+
|
|
110
|
+
// Create tasks with hierarchy
|
|
111
|
+
const project = await memory.createTask({ title: 'Build feature X' });
|
|
112
|
+
await memory.createTask({
|
|
113
|
+
title: 'Design UI',
|
|
114
|
+
parentTaskId: project.id
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
// Get upcoming tasks (due in next 24h)
|
|
118
|
+
const upcoming = await memory.getUpcomingTasks({ hoursAhead: 24 });
|
|
119
|
+
|
|
120
|
+
// Record learnings for future context
|
|
121
|
+
await memory.learn({
|
|
122
|
+
category: 'error',
|
|
123
|
+
trigger: 'Database migration failed',
|
|
124
|
+
lesson: 'Always backup before schema changes',
|
|
125
|
+
severity: 'critical'
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
// Search learnings when facing similar issues
|
|
129
|
+
const relevantLearnings = await memory.searchLearnings('database');
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
## Database Schema
|
|
133
|
+
|
|
134
|
+
### Sessions
|
|
135
|
+
```sql
|
|
136
|
+
sessions (id, agent_id, user_id, channel, started_at, ended_at, summary, metadata)
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### Messages
|
|
140
|
+
```sql
|
|
141
|
+
messages (id, session_id, role, content, created_at, token_count, metadata)
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### Memories
|
|
145
|
+
```sql
|
|
146
|
+
memories (id, agent_id, user_id, category, content, importance, embedding, expires_at, ...)
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### Entities
|
|
150
|
+
```sql
|
|
151
|
+
entities (id, agent_id, entity_type, name, aliases, properties, embedding, ...)
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### Entity Relationships
|
|
155
|
+
```sql
|
|
156
|
+
entity_relationships (id, agent_id, source_entity_id, target_entity_id, relationship_type,
|
|
157
|
+
confidence, mention_count, properties, ...)
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
### Tasks
|
|
161
|
+
```sql
|
|
162
|
+
tasks (id, agent_id, title, status, priority, due_at, ...)
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### Learnings
|
|
166
|
+
```sql
|
|
167
|
+
learnings (id, agent_id, category, trigger, lesson, action, severity, ...)
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
See [SCHEMA.md](./SCHEMA.md) for full details.
|
|
171
|
+
|
|
172
|
+
## Search Modes
|
|
173
|
+
|
|
174
|
+
Supaclaw supports three search strategies:
|
|
175
|
+
|
|
176
|
+
### 📝 Keyword Search (Default)
|
|
177
|
+
Traditional text matching - fast, no API keys needed.
|
|
178
|
+
|
|
179
|
+
```typescript
|
|
180
|
+
const results = await memory.recall('TypeScript', { limit: 10 });
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
### 🧠 Semantic Search
|
|
184
|
+
Uses OpenAI embeddings for meaning-based search. Understands that "coding tips" and "programming best practices" are related.
|
|
185
|
+
|
|
186
|
+
```typescript
|
|
187
|
+
const results = await memory.recall('machine learning', {
|
|
188
|
+
minSimilarity: 0.75, // Cosine similarity threshold (0-1)
|
|
189
|
+
limit: 10
|
|
190
|
+
});
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
**Requirements:**
|
|
194
|
+
- `embeddingProvider: 'openai'` in config
|
|
195
|
+
- `OPENAI_API_KEY` environment variable
|
|
196
|
+
- Run migration `002_vector_search.sql`
|
|
197
|
+
|
|
198
|
+
### ⚡ Hybrid Search (Best Results)
|
|
199
|
+
Combines semantic understanding with keyword matching.
|
|
200
|
+
|
|
201
|
+
```typescript
|
|
202
|
+
const results = await memory.hybridRecall('AI agents', {
|
|
203
|
+
vectorWeight: 0.7, // 70% semantic similarity
|
|
204
|
+
keywordWeight: 0.3, // 30% keyword matching
|
|
205
|
+
limit: 10
|
|
206
|
+
});
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
**When to use each:**
|
|
210
|
+
- **Keyword** - Fast lookups, exact term matching
|
|
211
|
+
- **Semantic** - Conceptual search, understanding context
|
|
212
|
+
- **Hybrid** - Best overall results, balances both strategies
|
|
213
|
+
|
|
214
|
+
## 🎯 Context Window Management
|
|
215
|
+
|
|
216
|
+
Advanced token budgeting and smart context selection to optimize LLM performance.
|
|
217
|
+
|
|
218
|
+
### Smart Context Generation
|
|
219
|
+
|
|
220
|
+
```typescript
|
|
221
|
+
// Simple: Get optimized context for a query
|
|
222
|
+
const context = await memory.getSmartContext('What did we discuss about the project?', {
|
|
223
|
+
sessionId: 'current-session',
|
|
224
|
+
model: 'claude-3.5-sonnet' // Auto-configures for 200k context
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
// Advanced: Full control over budget and selection
|
|
228
|
+
const result = await memory.buildOptimizedContext({
|
|
229
|
+
query: 'Project updates',
|
|
230
|
+
sessionId: 'session-123',
|
|
231
|
+
model: 'claude-3.5-sonnet',
|
|
232
|
+
useLostInMiddleFix: true, // Place important items at edges
|
|
233
|
+
importanceWeight: 0.8, // 80% importance, 20% recency
|
|
234
|
+
recencyWeight: 0.2
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
console.log('Context:', result.formatted);
|
|
238
|
+
console.log('Stats:', result.stats);
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
### Custom Budget Allocation
|
|
242
|
+
|
|
243
|
+
```typescript
|
|
244
|
+
import { createContextBudget } from 'supaclaw';
|
|
245
|
+
|
|
246
|
+
const budget = createContextBudget({
|
|
247
|
+
modelContextSize: 200000, // 200k tokens for Claude
|
|
248
|
+
recentMessagesPct: 0.5, // 50% for messages
|
|
249
|
+
memoriesPct: 0.3, // 30% for memories
|
|
250
|
+
learningsPct: 0.15, // 15% for learnings
|
|
251
|
+
entitiesPct: 0.05 // 5% for entities
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
const result = await memory.buildOptimizedContext({
|
|
255
|
+
query: 'Tell me everything',
|
|
256
|
+
customBudget: budget
|
|
257
|
+
});
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
### Adaptive Budgeting
|
|
261
|
+
|
|
262
|
+
Automatically adjusts allocation based on available content:
|
|
263
|
+
|
|
264
|
+
```typescript
|
|
265
|
+
import { createAdaptiveBudget } from 'supaclaw';
|
|
266
|
+
|
|
267
|
+
const budget = createAdaptiveBudget({
|
|
268
|
+
messageCount: 100, // Lots of messages
|
|
269
|
+
memoryCount: 20, // Few memories
|
|
270
|
+
learningCount: 10,
|
|
271
|
+
entityCount: 5
|
|
272
|
+
});
|
|
273
|
+
// Result: More budget allocated to messages
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
### Lost-in-Middle Mitigation
|
|
277
|
+
|
|
278
|
+
Research shows LLMs pay less attention to content in the middle of long contexts. OpenClaw automatically places high-importance items at the beginning and end:
|
|
279
|
+
|
|
280
|
+
```typescript
|
|
281
|
+
const result = await memory.buildOptimizedContext({
|
|
282
|
+
query: 'Important details',
|
|
283
|
+
useLostInMiddleFix: true // ✓ High-importance at edges
|
|
284
|
+
});
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
See [CONTEXT_WINDOW_GUIDE.md](./CONTEXT_WINDOW_GUIDE.md) for detailed examples and best practices.
|
|
288
|
+
|
|
289
|
+
## CLI Usage
|
|
290
|
+
|
|
291
|
+
```bash
|
|
292
|
+
# Initialize config
|
|
293
|
+
npx supaclaw init
|
|
294
|
+
|
|
295
|
+
# Run migrations
|
|
296
|
+
npx supaclaw migrate
|
|
297
|
+
|
|
298
|
+
# Test connection
|
|
299
|
+
npx supaclaw test
|
|
300
|
+
|
|
301
|
+
# Check database status
|
|
302
|
+
npx supaclaw status
|
|
303
|
+
|
|
304
|
+
# Search memories (keyword mode)
|
|
305
|
+
npx supaclaw search "TypeScript"
|
|
306
|
+
|
|
307
|
+
# Semantic search (requires OPENAI_API_KEY)
|
|
308
|
+
npx supaclaw search "coding best practices" --mode semantic
|
|
309
|
+
|
|
310
|
+
# Hybrid search
|
|
311
|
+
npx supaclaw search "AI patterns" --mode hybrid --limit 15
|
|
312
|
+
|
|
313
|
+
# List sessions
|
|
314
|
+
npx supaclaw sessions --limit 20 --active
|
|
315
|
+
|
|
316
|
+
# Export memories
|
|
317
|
+
npx supaclaw export memories.md
|
|
318
|
+
|
|
319
|
+
# Import memories
|
|
320
|
+
npx supaclaw import MEMORY.md
|
|
321
|
+
|
|
322
|
+
# Import from Clawdbot workspace
|
|
323
|
+
npx supaclaw import-memory-md ~/clawd/MEMORY.md
|
|
324
|
+
npx supaclaw import-daily-logs ~/clawd/memory
|
|
325
|
+
npx supaclaw import-todo-md ~/clawd/TODO.md
|
|
326
|
+
npx supaclaw import-learnings-md ~/clawd/LEARNINGS.md
|
|
327
|
+
|
|
328
|
+
# Import everything at once
|
|
329
|
+
npx supaclaw import-all ~/clawd --user-id han
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
## 🤖 Clawdbot Integration
|
|
333
|
+
|
|
334
|
+
Supaclaw provides seamless integration with [Clawdbot](https://github.com/clawdbot/clawdbot) to replace file-based memory systems.
|
|
335
|
+
|
|
336
|
+
### Installation as Clawdbot Skill
|
|
337
|
+
|
|
338
|
+
```bash
|
|
339
|
+
# Install via clawdhub
|
|
340
|
+
clawdhub install supaclaw
|
|
341
|
+
|
|
342
|
+
# Or install globally via npm
|
|
343
|
+
npm install -g supaclaw
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
### Quick Setup
|
|
347
|
+
|
|
348
|
+
```typescript
|
|
349
|
+
import { createClawdbotIntegration } from 'supaclaw';
|
|
350
|
+
|
|
351
|
+
const integration = createClawdbotIntegration({
|
|
352
|
+
supabaseUrl: process.env.SUPABASE_URL!,
|
|
353
|
+
supabaseKey: process.env.SUPABASE_KEY!,
|
|
354
|
+
agentId: 'hans-assistant',
|
|
355
|
+
userId: 'han',
|
|
356
|
+
embeddingProvider: 'openai',
|
|
357
|
+
openaiApiKey: process.env.OPENAI_API_KEY,
|
|
358
|
+
autoLog: true, // Auto-log all messages
|
|
359
|
+
sessionTimeout: 30 * 60 * 1000 // 30 min inactivity
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
await integration.initialize();
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
### Auto-Logging Messages
|
|
366
|
+
|
|
367
|
+
```typescript
|
|
368
|
+
// User message
|
|
369
|
+
await integration.logUserMessage(chatId, 'What is TypeScript?', {
|
|
370
|
+
channel: 'telegram',
|
|
371
|
+
messageId: msg.id
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
// Assistant response
|
|
375
|
+
await integration.logAssistantMessage(chatId, 'TypeScript is...', {
|
|
376
|
+
model: 'claude-sonnet-4-5'
|
|
377
|
+
});
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
### Replacing memory_search Tool
|
|
381
|
+
|
|
382
|
+
**Before (file-based):**
|
|
383
|
+
```typescript
|
|
384
|
+
function memory_search(query: string) {
|
|
385
|
+
const content = fs.readFileSync('MEMORY.md', 'utf-8');
|
|
386
|
+
return content.split('\n').filter(line =>
|
|
387
|
+
line.toLowerCase().includes(query.toLowerCase())
|
|
388
|
+
);
|
|
389
|
+
}
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
**After (semantic search):**
|
|
393
|
+
```typescript
|
|
394
|
+
async function memory_search(query: string) {
|
|
395
|
+
return await integration.memorySearch(query, {
|
|
396
|
+
userId: 'han',
|
|
397
|
+
limit: 5,
|
|
398
|
+
minImportance: 0.5
|
|
399
|
+
});
|
|
400
|
+
}
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
**Benefits:**
|
|
404
|
+
- 🎯 Semantic understanding (finds "code style" when you search "programming preferences")
|
|
405
|
+
- 📉 95% token reduction (5 memories vs entire file)
|
|
406
|
+
- ⚡ Faster (database query vs file I/O)
|
|
407
|
+
- 🎚️ Importance filtering (only relevant memories)
|
|
408
|
+
|
|
409
|
+
### Auto-Injecting Context into System Prompts
|
|
410
|
+
|
|
411
|
+
```typescript
|
|
412
|
+
async function buildSystemPrompt(userQuery: string) {
|
|
413
|
+
// Get relevant memories, learnings, and recent messages
|
|
414
|
+
const context = await integration.buildContext(userQuery, {
|
|
415
|
+
includeMemories: true,
|
|
416
|
+
includeLearnings: true,
|
|
417
|
+
includeRecentMessages: true,
|
|
418
|
+
chatId: 'telegram-123',
|
|
419
|
+
maxMemories: 5,
|
|
420
|
+
maxLearnings: 3
|
|
421
|
+
});
|
|
422
|
+
|
|
423
|
+
return BASE_SYSTEM_PROMPT + '\n\n' + context;
|
|
424
|
+
}
|
|
425
|
+
```
|
|
426
|
+
|
|
427
|
+
**Generated Context:**
|
|
428
|
+
```
|
|
429
|
+
## Relevant Context
|
|
430
|
+
|
|
431
|
+
- [preferences] User prefers TypeScript over JavaScript
|
|
432
|
+
- [projects] Working on stock trading challenge
|
|
433
|
+
- [context] User is actively trading TSLA
|
|
434
|
+
|
|
435
|
+
## Past Learnings
|
|
436
|
+
|
|
437
|
+
- [correction] User prefers Rust for performance-critical code
|
|
438
|
+
Action: Suggest Rust for system-level tasks
|
|
439
|
+
|
|
440
|
+
## Recent Conversation
|
|
441
|
+
|
|
442
|
+
- user: What's the stock price of TSLA?
|
|
443
|
+
- assistant: Tesla is currently trading at $245.
|
|
444
|
+
```
|
|
445
|
+
|
|
446
|
+
### Session Lifecycle Hooks
|
|
447
|
+
|
|
448
|
+
```typescript
|
|
449
|
+
// Session auto-created on first message
|
|
450
|
+
const sessionId = await integration.getOrCreateSession('telegram-123');
|
|
451
|
+
|
|
452
|
+
// End session with auto-summary
|
|
453
|
+
await integration.endSession('telegram-123', {
|
|
454
|
+
autoSummarize: true,
|
|
455
|
+
extractMemories: true
|
|
456
|
+
});
|
|
457
|
+
|
|
458
|
+
// Heartbeat check (call every 30 min)
|
|
459
|
+
const { upcomingTasks, inactiveSessions } = await integration.heartbeat();
|
|
460
|
+
|
|
461
|
+
// Send task reminders
|
|
462
|
+
for (const task of upcomingTasks) {
|
|
463
|
+
const reminder = integration.getMemory().formatTaskReminder(task, task.timeUntilDue);
|
|
464
|
+
await sendNotification(reminder);
|
|
465
|
+
}
|
|
466
|
+
```
|
|
467
|
+
|
|
468
|
+
### Token & Cost Savings
|
|
469
|
+
|
|
470
|
+
**Before (MEMORY.md):**
|
|
471
|
+
- File size: 50 KB
|
|
472
|
+
- Tokens per turn: ~12,500
|
|
473
|
+
- Cost per 1M turns: ~$37.50
|
|
474
|
+
|
|
475
|
+
**After (Supaclaw):**
|
|
476
|
+
- Memories retrieved: 5
|
|
477
|
+
- Tokens per turn: ~500
|
|
478
|
+
- Cost per 1M turns: ~$1.50
|
|
479
|
+
- **💰 Savings: 96% reduction, $36/M turns**
|
|
480
|
+
|
|
481
|
+
### Complete Integration Example
|
|
482
|
+
|
|
483
|
+
See [`skill/example-integration.ts`](skill/example-integration.ts) for a full working example showing:
|
|
484
|
+
- Message handler with auto-logging
|
|
485
|
+
- Tool replacement (memory_search, memory_get)
|
|
486
|
+
- Context injection
|
|
487
|
+
- Session lifecycle
|
|
488
|
+
- Heartbeat monitoring
|
|
489
|
+
|
|
490
|
+
### Migrating from Clawdbot Memory Files
|
|
491
|
+
|
|
492
|
+
If you're using traditional Clawdbot memory files, Supaclaw can import them:
|
|
493
|
+
|
|
494
|
+
**Supported formats:**
|
|
495
|
+
- `MEMORY.md` → memories table (with importance and category tags)
|
|
496
|
+
- `memory/*.md` → sessions + messages (daily logs)
|
|
497
|
+
- `TODO.md` → tasks table (with status and due dates)
|
|
498
|
+
- `LEARNINGS.md` → learnings table (with triggers and lessons)
|
|
499
|
+
|
|
500
|
+
**Import examples:**
|
|
501
|
+
|
|
502
|
+
```bash
|
|
503
|
+
# Import MEMORY.md
|
|
504
|
+
npx supaclaw import-memory-md ~/clawd/MEMORY.md
|
|
505
|
+
|
|
506
|
+
# Import daily logs from memory/
|
|
507
|
+
npx supaclaw import-daily-logs ~/clawd/memory --user-id han
|
|
508
|
+
|
|
509
|
+
# Import TODO.md
|
|
510
|
+
npx supaclaw import-todo-md ~/clawd/TODO.md
|
|
511
|
+
|
|
512
|
+
# Import LEARNINGS.md
|
|
513
|
+
npx supaclaw import-learnings-md ~/clawd/LEARNINGS.md
|
|
514
|
+
|
|
515
|
+
# Import everything at once
|
|
516
|
+
npx supaclaw import-all ~/clawd --user-id han
|
|
517
|
+
```
|
|
518
|
+
|
|
519
|
+
**What gets imported:**
|
|
520
|
+
- MEMORY.md: Parses sections (##) as categories, list items and paragraphs as memories
|
|
521
|
+
- Daily logs: Extracts sessions and User/Assistant message exchanges
|
|
522
|
+
- TODO.md: Imports tasks with status (pending/completed/cancelled), priority, and due dates
|
|
523
|
+
- LEARNINGS.md: Imports learnings with category, trigger, lesson, and importance
|
|
524
|
+
|
|
525
|
+
**Tag support:**
|
|
526
|
+
- `[importance: 0.9]` - Set memory importance (0.0-1.0)
|
|
527
|
+
- `[2024-01-28]` - Set creation date
|
|
528
|
+
- `[due: 2024-02-15]` - Set task due date
|
|
529
|
+
|
|
530
|
+
```
|
|
531
|
+
|
|
532
|
+
## Setup Supabase
|
|
533
|
+
|
|
534
|
+
1. Create a [Supabase](https://supabase.com) project
|
|
535
|
+
2. Enable the `vector` extension:
|
|
536
|
+
```sql
|
|
537
|
+
CREATE EXTENSION IF NOT EXISTS vector;
|
|
538
|
+
```
|
|
539
|
+
3. Run the migrations:
|
|
540
|
+
```bash
|
|
541
|
+
npx supaclaw migrate
|
|
542
|
+
```
|
|
543
|
+
4. Set environment variables:
|
|
544
|
+
```bash
|
|
545
|
+
SUPABASE_URL=https://your-project.supabase.co
|
|
546
|
+
SUPABASE_KEY=your-anon-key
|
|
547
|
+
```
|
|
548
|
+
|
|
549
|
+
## API Reference
|
|
550
|
+
|
|
551
|
+
### `new OpenClawMemory(config)`
|
|
552
|
+
Create a memory instance.
|
|
553
|
+
|
|
554
|
+
**Config options:**
|
|
555
|
+
- `supabaseUrl` - Supabase project URL
|
|
556
|
+
- `supabaseKey` - Supabase anon or service key
|
|
557
|
+
- `agentId` - Unique identifier for this agent
|
|
558
|
+
- `embeddingProvider` - Optional: 'openai', 'voyage', or 'none'
|
|
559
|
+
- `openaiApiKey` - Required if using OpenAI embeddings
|
|
560
|
+
- `embeddingModel` - Optional: OpenAI model name (default: 'text-embedding-3-small')
|
|
561
|
+
|
|
562
|
+
### `memory.initialize()`
|
|
563
|
+
Create database tables if they don't exist.
|
|
564
|
+
|
|
565
|
+
### `memory.startSession(opts)`
|
|
566
|
+
Start a new conversation session.
|
|
567
|
+
|
|
568
|
+
### `memory.addMessage(sessionId, message)`
|
|
569
|
+
Log a message to a session.
|
|
570
|
+
|
|
571
|
+
### `memory.endSession(sessionId, opts?)`
|
|
572
|
+
End a session, optionally with a summary.
|
|
573
|
+
|
|
574
|
+
**Options:**
|
|
575
|
+
- `summary` - Manual summary text
|
|
576
|
+
- `autoSummarize` - Auto-generate summary using AI (requires OpenAI)
|
|
577
|
+
|
|
578
|
+
### `memory.generateSessionSummary(sessionId)`
|
|
579
|
+
Generate an AI summary of a session (2-3 sentences).
|
|
580
|
+
|
|
581
|
+
### `memory.resumeSession(sessionId)`
|
|
582
|
+
Resume an interrupted session with context.
|
|
583
|
+
|
|
584
|
+
**Returns:** `{ session, messages, context }`
|
|
585
|
+
|
|
586
|
+
### `memory.searchSessions(opts?)`
|
|
587
|
+
Search sessions by date range and metadata.
|
|
588
|
+
|
|
589
|
+
**Options:**
|
|
590
|
+
- `userId`, `channel`, `startDate`, `endDate`
|
|
591
|
+
- `limit`, `offset`
|
|
592
|
+
|
|
593
|
+
### `memory.exportSessionToMarkdown(sessionId)`
|
|
594
|
+
Export a session to markdown format.
|
|
595
|
+
|
|
596
|
+
### `memory.importSessionFromMarkdown(markdown, opts?)`
|
|
597
|
+
Import a session from markdown.
|
|
598
|
+
|
|
599
|
+
### `memory.extractMemoriesFromSession(sessionId, opts?)`
|
|
600
|
+
Extract key memories from a session using AI.
|
|
601
|
+
|
|
602
|
+
**Options:**
|
|
603
|
+
- `minImportance` - Minimum importance threshold (default: 0.5)
|
|
604
|
+
- `autoExtract` - Use AI to extract (requires OpenAI)
|
|
605
|
+
|
|
606
|
+
### `memory.countSessionTokens(sessionId)`
|
|
607
|
+
Count total tokens used in a session.
|
|
608
|
+
|
|
609
|
+
**Returns:** `{ totalTokens, messageCount, averageTokensPerMessage }`
|
|
610
|
+
|
|
611
|
+
### `memory.remember(memory)`
|
|
612
|
+
Store a long-term memory. Automatically generates embeddings if provider configured.
|
|
613
|
+
|
|
614
|
+
### `memory.recall(query, opts?)`
|
|
615
|
+
Search for relevant memories using semantic similarity (if embeddings enabled) or keyword matching.
|
|
616
|
+
|
|
617
|
+
**Options:**
|
|
618
|
+
- `userId` - Filter by user
|
|
619
|
+
- `category` - Filter by category
|
|
620
|
+
- `limit` - Maximum results (default: 10)
|
|
621
|
+
- `minImportance` - Minimum importance score
|
|
622
|
+
- `minSimilarity` - Minimum cosine similarity (0-1, default: 0.7)
|
|
623
|
+
|
|
624
|
+
### `memory.hybridRecall(query, opts?)`
|
|
625
|
+
Hybrid search combining vector similarity and keyword matching.
|
|
626
|
+
|
|
627
|
+
**Options:**
|
|
628
|
+
- All options from `recall()` plus:
|
|
629
|
+
- `vectorWeight` - Weight for semantic similarity (default: 0.7)
|
|
630
|
+
- `keywordWeight` - Weight for keyword matching (default: 0.3)
|
|
631
|
+
|
|
632
|
+
### `memory.findSimilarMemories(memoryId, opts?)`
|
|
633
|
+
Find memories similar to an existing memory.
|
|
634
|
+
|
|
635
|
+
**Options:**
|
|
636
|
+
- `minSimilarity` - Minimum similarity threshold (default: 0.8)
|
|
637
|
+
- `limit` - Maximum results (default: 5)
|
|
638
|
+
|
|
639
|
+
### `memory.forget(memoryId)`
|
|
640
|
+
Delete a memory.
|
|
641
|
+
|
|
642
|
+
### `memory.getContext(query, opts?)`
|
|
643
|
+
Get relevant context for the current query (memories + recent messages).
|
|
644
|
+
|
|
645
|
+
### `memory.createTask(task)`
|
|
646
|
+
Create a task with optional hierarchy.
|
|
647
|
+
|
|
648
|
+
**Options:** `title`, `description`, `priority`, `dueAt`, `parentTaskId`
|
|
649
|
+
|
|
650
|
+
### `memory.updateTask(taskId, updates)`
|
|
651
|
+
Update task properties.
|
|
652
|
+
|
|
653
|
+
### `memory.deleteTask(taskId)`
|
|
654
|
+
Delete a task.
|
|
655
|
+
|
|
656
|
+
### `memory.getTasks(opts?)`
|
|
657
|
+
Get tasks with filters.
|
|
658
|
+
|
|
659
|
+
**Options:** `status`, `userId`, `limit`
|
|
660
|
+
|
|
661
|
+
### `memory.getSubtasks(parentTaskId)`
|
|
662
|
+
Get all subtasks of a parent task.
|
|
663
|
+
|
|
664
|
+
### `memory.getTaskWithSubtasks(taskId)`
|
|
665
|
+
Get a task with all its subtasks (hierarchical view).
|
|
666
|
+
|
|
667
|
+
### `memory.getUpcomingTasks(opts?)`
|
|
668
|
+
Get tasks due soon (default: next 24 hours).
|
|
669
|
+
|
|
670
|
+
### `memory.learn(learning)`
|
|
671
|
+
Record a learning for future reference.
|
|
672
|
+
|
|
673
|
+
**Options:** `category`, `trigger`, `lesson`, `action`, `severity`
|
|
674
|
+
|
|
675
|
+
### `memory.getLearnings(opts?)`
|
|
676
|
+
Get recorded learnings.
|
|
677
|
+
|
|
678
|
+
**Options:** `category`, `severity`, `limit`
|
|
679
|
+
|
|
680
|
+
### `memory.searchLearnings(query, opts?)`
|
|
681
|
+
Search learnings by topic.
|
|
682
|
+
|
|
683
|
+
### `memory.applyLearning(learningId)`
|
|
684
|
+
Mark a learning as applied (increments usage count).
|
|
685
|
+
|
|
686
|
+
### `memory.extractEntities(text, opts?)`
|
|
687
|
+
Extract named entities from text using AI.
|
|
688
|
+
|
|
689
|
+
**Returns:** Array of entities (person, place, organization, product, concept)
|
|
690
|
+
|
|
691
|
+
### `memory.createEntity(entity)`
|
|
692
|
+
Manually create an entity.
|
|
693
|
+
|
|
694
|
+
### `memory.updateEntity(entityId, updates)`
|
|
695
|
+
Update an entity (increments mention count).
|
|
696
|
+
|
|
697
|
+
### `memory.findEntity(nameOrAlias)`
|
|
698
|
+
Find an entity by name or alias (case-insensitive).
|
|
699
|
+
|
|
700
|
+
### `memory.searchEntities(opts?)`
|
|
701
|
+
Search entities with filters.
|
|
702
|
+
|
|
703
|
+
**Options:** `query`, `entityType`, `limit`
|
|
704
|
+
|
|
705
|
+
### `memory.mergeEntities(primaryId, duplicateId)`
|
|
706
|
+
Merge duplicate entities (deduplication).
|
|
707
|
+
|
|
708
|
+
### `memory.createEntityRelationship(rel)`
|
|
709
|
+
Create or update a relationship between entities.
|
|
710
|
+
|
|
711
|
+
**Parameters:**
|
|
712
|
+
- `sourceEntityId` - Source entity UUID
|
|
713
|
+
- `targetEntityId` - Target entity UUID
|
|
714
|
+
- `relationshipType` - Type: `works_at`, `knows`, `created`, `located_in`, etc.
|
|
715
|
+
- `properties` - Optional additional context
|
|
716
|
+
- `confidence` - Confidence score 0-1 (default: 0.5)
|
|
717
|
+
- `sessionId` - Optional source session
|
|
718
|
+
|
|
719
|
+
**Returns:** EntityRelationship
|
|
720
|
+
|
|
721
|
+
### `memory.getEntityRelationships(entityId, opts?)`
|
|
722
|
+
Get relationships for an entity.
|
|
723
|
+
|
|
724
|
+
**Options:**
|
|
725
|
+
- `direction` - `'outgoing'` | `'incoming'` | `'both'` (default: `'both'`)
|
|
726
|
+
- `relationshipType` - Filter by type
|
|
727
|
+
- `minConfidence` - Minimum confidence threshold (default: 0.3)
|
|
728
|
+
- `limit` - Maximum results (default: 50)
|
|
729
|
+
|
|
730
|
+
**Returns:** Array of `{ relationship, relatedEntity, direction }`
|
|
731
|
+
|
|
732
|
+
### `memory.findRelatedEntities(entityId, opts?)`
|
|
733
|
+
Find entities connected through multi-hop relationships (graph traversal).
|
|
734
|
+
|
|
735
|
+
**Options:**
|
|
736
|
+
- `maxDepth` - Maximum hops (default: 2)
|
|
737
|
+
- `minConfidence` - Minimum confidence threshold (default: 0.5)
|
|
738
|
+
|
|
739
|
+
**Returns:** Array of `{ entityId, entityName, entityType, relationshipPath, totalConfidence, depth }`
|
|
740
|
+
|
|
741
|
+
### `memory.getEntityNetworkStats()`
|
|
742
|
+
Get entity network statistics.
|
|
743
|
+
|
|
744
|
+
**Returns:** `{ totalEntities, totalRelationships, avgConnectionsPerEntity, mostConnectedEntity }`
|
|
745
|
+
|
|
746
|
+
### `memory.extractEntitiesWithRelationships(text, opts?)`
|
|
747
|
+
Extract entities AND relationships from text using AI (one call).
|
|
748
|
+
|
|
749
|
+
**Returns:** `{ entities: Entity[], relationships: EntityRelationship[] }`
|
|
750
|
+
|
|
751
|
+
### `memory.searchRelationships(opts?)`
|
|
752
|
+
Search relationships with filters.
|
|
753
|
+
|
|
754
|
+
**Options:** `relationshipType`, `minConfidence`, `limit`
|
|
755
|
+
|
|
756
|
+
### `memory.deleteEntityRelationship(relationshipId)`
|
|
757
|
+
Delete a relationship.
|
|
758
|
+
|
|
759
|
+
### `memory.decayMemoryImportance(opts?)`
|
|
760
|
+
Apply importance decay to old memories (lifecycle management).
|
|
761
|
+
|
|
762
|
+
**Options:**
|
|
763
|
+
- `userId` - Filter by user
|
|
764
|
+
- `decayRate` - Decay rate 0-1 (default: 0.1)
|
|
765
|
+
- `minImportance` - Minimum importance threshold (default: 0.1)
|
|
766
|
+
- `olderThanDays` - Only decay memories older than X days (default: 7)
|
|
767
|
+
|
|
768
|
+
**Returns:** `{ updated: number, avgDecay: number }`
|
|
769
|
+
|
|
770
|
+
### `memory.consolidateMemories(opts?)`
|
|
771
|
+
Merge similar/duplicate memories to reduce clutter.
|
|
772
|
+
|
|
773
|
+
**Options:**
|
|
774
|
+
- `userId` - Filter by user
|
|
775
|
+
- `similarityThreshold` - Similarity threshold 0-1 (default: 0.9)
|
|
776
|
+
- `category` - Filter by category
|
|
777
|
+
- `limit` - Max pairs to check (default: 100)
|
|
778
|
+
|
|
779
|
+
**Returns:** `{ merged: number, kept: number }`
|
|
780
|
+
|
|
781
|
+
### `memory.versionMemory(memoryId)`
|
|
782
|
+
Create a version snapshot of a memory.
|
|
783
|
+
|
|
784
|
+
**Returns:** `{ memory, versionId }`
|
|
785
|
+
|
|
786
|
+
### `memory.getMemoryVersions(memoryId)`
|
|
787
|
+
Get version history for a memory.
|
|
788
|
+
|
|
789
|
+
**Returns:** Array of `{ version, timestamp, content, importance }`
|
|
790
|
+
|
|
791
|
+
### `memory.tagMemory(memoryId, tags)`
|
|
792
|
+
Add tags to a memory for organization.
|
|
793
|
+
|
|
794
|
+
### `memory.untagMemory(memoryId, tags)`
|
|
795
|
+
Remove tags from a memory.
|
|
796
|
+
|
|
797
|
+
### `memory.searchMemoriesByTags(tags, opts?)`
|
|
798
|
+
Search memories by tags.
|
|
799
|
+
|
|
800
|
+
**Options:**
|
|
801
|
+
- `matchAll` - If true, must match ALL tags; if false, match ANY (default: false)
|
|
802
|
+
- `limit` - Maximum results (default: 50)
|
|
803
|
+
|
|
804
|
+
### `memory.cleanupOldSessions(opts?)`
|
|
805
|
+
Archive or delete old sessions (maintenance).
|
|
806
|
+
|
|
807
|
+
**Options:**
|
|
808
|
+
- `olderThanDays` - Archive sessions older than X days (default: 90)
|
|
809
|
+
- `action` - 'archive' or 'delete' (default: 'archive')
|
|
810
|
+
- `keepSummaries` - Keep sessions with summaries (default: true)
|
|
811
|
+
- `userId` - Filter by user
|
|
812
|
+
|
|
813
|
+
**Returns:** `{ archived?: number, deleted?: number }`
|
|
814
|
+
|
|
815
|
+
### `memory.getCleanupStats()`
|
|
816
|
+
Get cleanup statistics for monitoring.
|
|
817
|
+
|
|
818
|
+
**Returns:** `{ totalSessions, archivedSessions, oldSessions, totalMessages, orphanedMessages }`
|
|
819
|
+
|
|
820
|
+
## Integration with OpenClaw/Clawdbot
|
|
821
|
+
|
|
822
|
+
This package is designed to integrate with [Clawdbot](https://github.com/clawdbot/clawdbot):
|
|
823
|
+
|
|
824
|
+
```typescript
|
|
825
|
+
// In your agent's AGENTS.md equivalent
|
|
826
|
+
// Instead of reading MEMORY.md, use:
|
|
827
|
+
const context = await memory.getContext(userMessage);
|
|
828
|
+
```
|
|
829
|
+
|
|
830
|
+
## Roadmap
|
|
831
|
+
|
|
832
|
+
### ✅ Completed
|
|
833
|
+
- [x] CLI for memory management
|
|
834
|
+
- [x] Markdown import/export
|
|
835
|
+
- [x] Semantic search (OpenAI embeddings)
|
|
836
|
+
- [x] Hybrid search (vector + keyword)
|
|
837
|
+
- [x] Vector similarity functions
|
|
838
|
+
- [x] Automatic session summarization
|
|
839
|
+
- [x] Entity extraction from conversations
|
|
840
|
+
- [x] Session export/import (markdown)
|
|
841
|
+
- [x] Memory extraction from sessions
|
|
842
|
+
- [x] Task hierarchy (subtasks)
|
|
843
|
+
- [x] Learning application tracking
|
|
844
|
+
- [x] Context window budgeting & management
|
|
845
|
+
- [x] **Memory importance decay over time**
|
|
846
|
+
- [x] **Memory consolidation (merge similar)**
|
|
847
|
+
- [x] **Memory versioning (historical snapshots)**
|
|
848
|
+
- [x] **Memory tagging system**
|
|
849
|
+
- [x] **Auto-cleanup old sessions**
|
|
850
|
+
- [x] **Cleanup statistics & monitoring**
|
|
851
|
+
|
|
852
|
+
### 🚧 In Progress
|
|
853
|
+
- [ ] Voyage AI embedding provider
|
|
854
|
+
- [ ] Local embeddings (transformers.js)
|
|
855
|
+
- [ ] Clawdbot skill integration
|
|
856
|
+
|
|
857
|
+
### 📋 Planned
|
|
858
|
+
- [ ] Multi-agent memory sharing
|
|
859
|
+
- [ ] Entity relationship tracking table
|
|
860
|
+
- [ ] Memory migration tools (MEMORY.md → DB)
|
|
861
|
+
- [ ] Real-time subscriptions
|
|
862
|
+
- [ ] Memory access logging
|
|
863
|
+
- [ ] Memory reactions/ratings
|
|
864
|
+
|
|
865
|
+
## Contributing
|
|
866
|
+
|
|
867
|
+
PRs welcome! See [CONTRIBUTING.md](./CONTRIBUTING.md).
|
|
868
|
+
|
|
869
|
+
## License
|
|
870
|
+
|
|
871
|
+
MIT
|