@yun-zero/claw-memory 0.1.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/.claude/settings.local.json +68 -0
- package/README.md +323 -0
- package/dist/config/llm.d.ts +13 -0
- package/dist/config/llm.d.ts.map +1 -0
- package/dist/config/llm.js +96 -0
- package/dist/config/llm.js.map +1 -0
- package/dist/config/plugin.d.ts +15 -0
- package/dist/config/plugin.d.ts.map +1 -0
- package/dist/config/plugin.js +32 -0
- package/dist/config/plugin.js.map +1 -0
- package/dist/db/entityRepository.d.ts +21 -0
- package/dist/db/entityRepository.d.ts.map +1 -0
- package/dist/db/entityRepository.js +55 -0
- package/dist/db/entityRepository.js.map +1 -0
- package/dist/db/repository.d.ts +22 -0
- package/dist/db/repository.d.ts.map +1 -0
- package/dist/db/repository.js +77 -0
- package/dist/db/repository.js.map +1 -0
- package/dist/db/schema.d.ts +5 -0
- package/dist/db/schema.d.ts.map +1 -0
- package/dist/db/schema.js +112 -0
- package/dist/db/schema.js.map +1 -0
- package/dist/db/todoRepository.d.ts +26 -0
- package/dist/db/todoRepository.d.ts.map +1 -0
- package/dist/db/todoRepository.js +54 -0
- package/dist/db/todoRepository.js.map +1 -0
- package/dist/hooks/bootstrap.d.ts +3 -0
- package/dist/hooks/bootstrap.d.ts.map +1 -0
- package/dist/hooks/bootstrap.js +28 -0
- package/dist/hooks/bootstrap.js.map +1 -0
- package/dist/hooks/message.d.ts +18 -0
- package/dist/hooks/message.d.ts.map +1 -0
- package/dist/hooks/message.js +52 -0
- package/dist/hooks/message.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +46 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp/tools.d.ts +26 -0
- package/dist/mcp/tools.d.ts.map +1 -0
- package/dist/mcp/tools.js +360 -0
- package/dist/mcp/tools.js.map +1 -0
- package/dist/plugin.d.ts +18 -0
- package/dist/plugin.d.ts.map +1 -0
- package/dist/plugin.js +62 -0
- package/dist/plugin.js.map +1 -0
- package/dist/services/entityGraphService.d.ts +87 -0
- package/dist/services/entityGraphService.d.ts.map +1 -0
- package/dist/services/entityGraphService.js +271 -0
- package/dist/services/entityGraphService.js.map +1 -0
- package/dist/services/memory.d.ts +26 -0
- package/dist/services/memory.d.ts.map +1 -0
- package/dist/services/memory.js +281 -0
- package/dist/services/memory.js.map +1 -0
- package/dist/services/memoryIndex.d.ts +34 -0
- package/dist/services/memoryIndex.d.ts.map +1 -0
- package/dist/services/memoryIndex.js +100 -0
- package/dist/services/memoryIndex.js.map +1 -0
- package/dist/services/metadataExtractor.d.ts +16 -0
- package/dist/services/metadataExtractor.d.ts.map +1 -0
- package/dist/services/metadataExtractor.js +75 -0
- package/dist/services/metadataExtractor.js.map +1 -0
- package/dist/services/retrieval.d.ts +24 -0
- package/dist/services/retrieval.d.ts.map +1 -0
- package/dist/services/retrieval.js +40 -0
- package/dist/services/retrieval.js.map +1 -0
- package/dist/services/scheduler.d.ts +122 -0
- package/dist/services/scheduler.d.ts.map +1 -0
- package/dist/services/scheduler.js +434 -0
- package/dist/services/scheduler.js.map +1 -0
- package/dist/services/summarizer.d.ts +43 -0
- package/dist/services/summarizer.d.ts.map +1 -0
- package/dist/services/summarizer.js +252 -0
- package/dist/services/summarizer.js.map +1 -0
- package/dist/services/tagService.d.ts +64 -0
- package/dist/services/tagService.d.ts.map +1 -0
- package/dist/services/tagService.js +281 -0
- package/dist/services/tagService.js.map +1 -0
- package/dist/tools/memory.d.ts +3 -0
- package/dist/tools/memory.d.ts.map +1 -0
- package/dist/tools/memory.js +114 -0
- package/dist/tools/memory.js.map +1 -0
- package/dist/types.d.ts +128 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +6 -0
- package/dist/types.js.map +1 -0
- package/docs/plans/2026-03-02-claw-memory-design.md +445 -0
- package/docs/plans/2026-03-02-incremental-summary-design.md +157 -0
- package/docs/plans/2026-03-02-incremental-summary-implementation.md +468 -0
- package/docs/plans/2026-03-02-memory-index-design.md +163 -0
- package/docs/plans/2026-03-02-memory-index-implementation.md +836 -0
- package/docs/plans/2026-03-02-mvp-implementation.md +1703 -0
- package/docs/plans/2026-03-02-testing-implementation.md +395 -0
- package/docs/plans/2026-03-02-testing-plan.md +93 -0
- package/docs/plans/2026-03-03-claw-memory-openclaw-plugin-design.md +285 -0
- package/docs/plans/2026-03-03-claw-memory-plugin-implementation.md +642 -0
- package/docs/plans/2026-03-03-entity-graph-design.md +121 -0
- package/docs/plans/2026-03-03-entity-graph-implementation.md +687 -0
- package/docs/plans/2026-03-03-llm-generic-config-design.md +43 -0
- package/docs/plans/2026-03-03-llm-generic-config-implementation.md +186 -0
- package/docs/plans/2026-03-03-memory-e2e-stress-test-design.md +110 -0
- package/docs/plans/2026-03-03-memory-e2e-stress-test-implementation.md +464 -0
- package/docs/plans/2026-03-03-minimax-llm-fix.md +156 -0
- package/docs/plans/2026-03-03-scheduler-design.md +165 -0
- package/docs/plans/2026-03-03-scheduler-implementation.md +777 -0
- package/docs/plans/2026-03-03-tags-visualization-design.md +73 -0
- package/docs/plans/2026-03-03-tags-visualization-implementation.md +539 -0
- package/openclaw.plugin.json +11 -0
- package/package.json +41 -0
- package/src/config/llm.ts +129 -0
- package/src/config/plugin.ts +47 -0
- package/src/db/entityRepository.ts +80 -0
- package/src/db/repository.ts +106 -0
- package/src/db/schema.ts +121 -0
- package/src/db/todoRepository.ts +76 -0
- package/src/hooks/bootstrap.ts +36 -0
- package/src/hooks/message.ts +84 -0
- package/src/index.ts +50 -0
- package/src/plugin.ts +85 -0
- package/src/services/entityGraphService.ts +367 -0
- package/src/services/memory.ts +338 -0
- package/src/services/memoryIndex.ts +140 -0
- package/src/services/metadataExtractor.ts +89 -0
- package/src/services/retrieval.ts +71 -0
- package/src/services/scheduler.ts +529 -0
- package/src/services/summarizer.ts +318 -0
- package/src/services/tagService.ts +335 -0
- package/src/tools/memory.ts +137 -0
- package/src/types.ts +139 -0
- package/tsconfig.json +20 -0
- package/vitest.config.ts +16 -0
|
@@ -0,0 +1,642 @@
|
|
|
1
|
+
# Claw-Memory OpenClaw 插件实现计划
|
|
2
|
+
|
|
3
|
+
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
|
|
4
|
+
|
|
5
|
+
**Goal:** 开发 OpenClaw 插件,实现自动保存对话到记忆,会话开始时注入摘要
|
|
6
|
+
|
|
7
|
+
**Architecture:** 完全集成到 OpenClaw 插件系统,复用 claw-memory 现有服务(schema, summarizer, scheduler),使用 OpenClaw 内置 LLM
|
|
8
|
+
|
|
9
|
+
**Tech Stack:** TypeScript, OpenClaw Plugin SDK, better-sqlite3, node-cron
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## 准备工作
|
|
14
|
+
|
|
15
|
+
### Task 1: 创建开发分支
|
|
16
|
+
|
|
17
|
+
**Step 1: 创建并切换到新分支**
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
cd /home/ubuntu/openclaw/claw-memory
|
|
21
|
+
git checkout -b feature/claw-memory-plugin
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
**Step 2: 验证分支**
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
git branch --show-current
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Expected: `feature/claw-memory-plugin`
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## Task 2: 创建插件项目结构
|
|
35
|
+
|
|
36
|
+
**Files:**
|
|
37
|
+
- Create: `plugin/openclaw.plugin.json`
|
|
38
|
+
- Create: `plugin/package.json`
|
|
39
|
+
- Create: `plugin/tsconfig.json`
|
|
40
|
+
|
|
41
|
+
**Step 1: 创建 openclaw.plugin.json**
|
|
42
|
+
|
|
43
|
+
```json
|
|
44
|
+
{
|
|
45
|
+
"id": "claw-memory",
|
|
46
|
+
"name": "ClawMemory",
|
|
47
|
+
"version": "0.1.0",
|
|
48
|
+
"description": "AI memory system - auto-save conversations and inject memory summary",
|
|
49
|
+
"main": "./dist/index.js",
|
|
50
|
+
"dependencies": {},
|
|
51
|
+
"scripts": {
|
|
52
|
+
"build": "tsc"
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
**Step 2: 创建 package.json**
|
|
58
|
+
|
|
59
|
+
```json
|
|
60
|
+
{
|
|
61
|
+
"name": "@openclaw/claw-memory",
|
|
62
|
+
"version": "0.1.0",
|
|
63
|
+
"description": "AI memory system for OpenClaw",
|
|
64
|
+
"main": "dist/index.js",
|
|
65
|
+
"type": "module",
|
|
66
|
+
"scripts": {
|
|
67
|
+
"build": "tsc"
|
|
68
|
+
},
|
|
69
|
+
"dependencies": {
|
|
70
|
+
"better-sqlite3": "^11.0.0",
|
|
71
|
+
"node-cron": "^4.2.1",
|
|
72
|
+
"uuid": "^10.0.0"
|
|
73
|
+
},
|
|
74
|
+
"devDependencies": {
|
|
75
|
+
"@types/better-sqlite3": "^7.6.0",
|
|
76
|
+
"@types/node": "^20.0.0",
|
|
77
|
+
"@types/node-cron": "^3.0.11",
|
|
78
|
+
"@types/uuid": "^10.0.0",
|
|
79
|
+
"typescript": "^5.0.0"
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
**Step 3: 创建 tsconfig.json**
|
|
85
|
+
|
|
86
|
+
```json
|
|
87
|
+
{
|
|
88
|
+
"compilerOptions": {
|
|
89
|
+
"target": "ES2022",
|
|
90
|
+
"module": "ESNext",
|
|
91
|
+
"moduleResolution": "bundler",
|
|
92
|
+
"esModuleInterop": true,
|
|
93
|
+
"strict": true,
|
|
94
|
+
"outDir": "./dist",
|
|
95
|
+
"rootDir": "./src"
|
|
96
|
+
},
|
|
97
|
+
"include": ["src/**/*"]
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
**Step 4: 提交**
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
git add plugin/
|
|
105
|
+
git commit -m "feat: add OpenClaw plugin project structure"
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
---
|
|
109
|
+
|
|
110
|
+
## Task 3: 创建插件入口文件
|
|
111
|
+
|
|
112
|
+
**Files:**
|
|
113
|
+
- Create: `plugin/src/index.ts`
|
|
114
|
+
|
|
115
|
+
**Step 1: 创建插件入口**
|
|
116
|
+
|
|
117
|
+
```typescript
|
|
118
|
+
// plugin/src/index.ts
|
|
119
|
+
import type { OpenClawPlugin } from '@openclaw/plugin-sdk';
|
|
120
|
+
|
|
121
|
+
export default {
|
|
122
|
+
name: 'claw-memory',
|
|
123
|
+
version: '0.1.0',
|
|
124
|
+
|
|
125
|
+
async register(context: any) {
|
|
126
|
+
// 注册 Hooks
|
|
127
|
+
await context.hooks.register('message:sent', async (event: any) => {
|
|
128
|
+
const { message, session } = event;
|
|
129
|
+
// TODO: 实现保存逻辑
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
await context.hooks.register('agent:bootstrap', async (context: any) => {
|
|
133
|
+
// TODO: 实现注入摘要逻辑
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
// 注册 Agent Tools
|
|
137
|
+
context.tools.register({
|
|
138
|
+
name: 'memory_save',
|
|
139
|
+
description: 'Save conversation to memory',
|
|
140
|
+
schema: { /* ... */ },
|
|
141
|
+
handler: async (params: any) => {
|
|
142
|
+
// TODO: 实现保存工具
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
// 初始化数据库
|
|
147
|
+
// TODO: 初始化 SQLite
|
|
148
|
+
|
|
149
|
+
// 启动定时任务
|
|
150
|
+
// TODO: 启动 scheduler
|
|
151
|
+
}
|
|
152
|
+
} satisfies OpenClawPlugin;
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
**Step 2: 提交**
|
|
156
|
+
|
|
157
|
+
```bash
|
|
158
|
+
git add plugin/src/index.ts
|
|
159
|
+
git commit -m "feat: add plugin entry point"
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
---
|
|
163
|
+
|
|
164
|
+
## Task 4: 实现配置管理
|
|
165
|
+
|
|
166
|
+
**Files:**
|
|
167
|
+
- Create: `plugin/src/config.ts`
|
|
168
|
+
|
|
169
|
+
**Step 1: 创建配置管理**
|
|
170
|
+
|
|
171
|
+
```typescript
|
|
172
|
+
// plugin/src/config.ts
|
|
173
|
+
export interface PluginConfig {
|
|
174
|
+
enabled: boolean;
|
|
175
|
+
autoSave: boolean;
|
|
176
|
+
saveMode: 'qa' | 'full';
|
|
177
|
+
dataDir: string;
|
|
178
|
+
scheduler: {
|
|
179
|
+
enabled: boolean;
|
|
180
|
+
deduplicateTime: string;
|
|
181
|
+
dailyTime: string;
|
|
182
|
+
weeklyTime: string;
|
|
183
|
+
monthlyTime: string;
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
export function getConfig(context: any): PluginConfig {
|
|
188
|
+
const defaultConfig: PluginConfig = {
|
|
189
|
+
enabled: true,
|
|
190
|
+
autoSave: true,
|
|
191
|
+
saveMode: 'qa',
|
|
192
|
+
dataDir: '~/.openclaw/claw-memory',
|
|
193
|
+
scheduler: {
|
|
194
|
+
enabled: true,
|
|
195
|
+
deduplicateTime: '01:00',
|
|
196
|
+
dailyTime: '02:00',
|
|
197
|
+
weeklyTime: '03:00',
|
|
198
|
+
monthlyTime: '04:00'
|
|
199
|
+
}
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
const userConfig = context.config.get('plugins.claw-memory') || {};
|
|
203
|
+
return { ...defaultConfig, ...userConfig };
|
|
204
|
+
}
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
**Step 2: 提交**
|
|
208
|
+
|
|
209
|
+
```bash
|
|
210
|
+
git add plugin/src/config.ts
|
|
211
|
+
git commit -m "feat: add config management"
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
---
|
|
215
|
+
|
|
216
|
+
## Task 5: 复用数据库 Schema
|
|
217
|
+
|
|
218
|
+
由于现有 `claw-memory/src/db/schema.ts` 已有完整的数据库 Schema,需要将其复制到插件中。
|
|
219
|
+
|
|
220
|
+
**Files:**
|
|
221
|
+
- Create: `plugin/src/db/schema.ts`
|
|
222
|
+
|
|
223
|
+
**Step 1: 复制 schema**
|
|
224
|
+
|
|
225
|
+
从 `src/db/schema.ts` 复制以下内容到 `plugin/src/db/schema.ts`:
|
|
226
|
+
- `initializeDatabase()` 函数
|
|
227
|
+
- `getDatabase()` 函数
|
|
228
|
+
|
|
229
|
+
**Step 2: 修改 getDatabase 路径**
|
|
230
|
+
|
|
231
|
+
```typescript
|
|
232
|
+
// plugin/src/db/schema.ts
|
|
233
|
+
import Database from 'better-sqlite3';
|
|
234
|
+
import * as path from 'path';
|
|
235
|
+
import * as fs from 'fs';
|
|
236
|
+
import { homedir } from 'os';
|
|
237
|
+
|
|
238
|
+
export function getDatabase(): Database.Database {
|
|
239
|
+
const dataDir = process.env.CLAW_MEMORY_DATA_DIR || path.join(homedir(), '.openclaw', 'claw-memory');
|
|
240
|
+
|
|
241
|
+
// 确保目录存在
|
|
242
|
+
if (!fs.existsSync(dataDir)) {
|
|
243
|
+
fs.mkdirSync(dataDir, { recursive: true });
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
const dbPath = path.join(dataDir, 'memory.db');
|
|
247
|
+
const db = new Database(dbPath);
|
|
248
|
+
|
|
249
|
+
initializeDatabase(db);
|
|
250
|
+
return db;
|
|
251
|
+
}
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
**Step 3: 提交**
|
|
255
|
+
|
|
256
|
+
```bash
|
|
257
|
+
git add plugin/src/db/
|
|
258
|
+
git commit -m "feat: add database schema"
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
---
|
|
262
|
+
|
|
263
|
+
## Task 6: 实现 message:sent Hook
|
|
264
|
+
|
|
265
|
+
**Files:**
|
|
266
|
+
- Create: `plugin/src/hooks/message.ts`
|
|
267
|
+
|
|
268
|
+
**Step 1: 创建 Hook 处理逻辑**
|
|
269
|
+
|
|
270
|
+
```typescript
|
|
271
|
+
// plugin/src/hooks/message.ts
|
|
272
|
+
import type { Database } from 'better-sqlite3';
|
|
273
|
+
import { v4 as uuidv4 } from 'uuid';
|
|
274
|
+
|
|
275
|
+
interface MessageEvent {
|
|
276
|
+
message: {
|
|
277
|
+
role: 'user' | 'assistant';
|
|
278
|
+
content: string;
|
|
279
|
+
};
|
|
280
|
+
session: {
|
|
281
|
+
id: string;
|
|
282
|
+
key: string;
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
export async function handleMessageSent(
|
|
287
|
+
event: MessageEvent,
|
|
288
|
+
db: Database,
|
|
289
|
+
config: any
|
|
290
|
+
): Promise<void> {
|
|
291
|
+
// 只处理 assistant 消息(AI 回复后保存)
|
|
292
|
+
if (event.message.role !== 'assistant') {
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// 获取对应的 user 消息(需要从 session 上下文中获取)
|
|
297
|
+
// TODO: 从 session JSONL 中获取最后一条 user 消息
|
|
298
|
+
|
|
299
|
+
const userMessage = event.message.content; // 临时:需要从上下文获取
|
|
300
|
+
const assistantMessage = event.message.content;
|
|
301
|
+
|
|
302
|
+
// 构建 Q&A
|
|
303
|
+
const qaContent = `Q: ${userMessage}\nA: ${assistantMessage}`;
|
|
304
|
+
|
|
305
|
+
// 保存到数据库
|
|
306
|
+
const memoryId = uuidv4();
|
|
307
|
+
const dataDir = process.env.CLAW_MEMORY_DATA_DIR || '~/.openclaw/claw-memory';
|
|
308
|
+
const contentPath = `${dataDir}/memories/${memoryId}.md`;
|
|
309
|
+
|
|
310
|
+
// 写入文件
|
|
311
|
+
const fs = await import('fs/promises');
|
|
312
|
+
await fs.writeFile(contentPath, qaContent);
|
|
313
|
+
|
|
314
|
+
// 插入数据库
|
|
315
|
+
db.prepare(`
|
|
316
|
+
INSERT INTO memories (id, content_path, summary, created_at)
|
|
317
|
+
VALUES (?, ?, ?, datetime('now'))
|
|
318
|
+
`).run(memoryId, contentPath, assistantMessage.substring(0, 200));
|
|
319
|
+
|
|
320
|
+
console.log(`[ClawMemory] Saved memory: ${memoryId}`);
|
|
321
|
+
}
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
**Step 2: 提交**
|
|
325
|
+
|
|
326
|
+
```bash
|
|
327
|
+
git add plugin/src/hooks/message.ts
|
|
328
|
+
git commit -m "feat: implement message:sent hook"
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
---
|
|
332
|
+
|
|
333
|
+
## Task 7: 实现 agent:bootstrap Hook
|
|
334
|
+
|
|
335
|
+
**Files:**
|
|
336
|
+
- Create: `plugin/src/hooks/bootstrap.ts`
|
|
337
|
+
|
|
338
|
+
**Step 1: 创建 Bootstrap Hook**
|
|
339
|
+
|
|
340
|
+
```typescript
|
|
341
|
+
// plugin/src/hooks/bootstrap.ts
|
|
342
|
+
import type { Database } from 'better-sqlite3';
|
|
343
|
+
|
|
344
|
+
export async function handleAgentBootstrap(
|
|
345
|
+
context: any,
|
|
346
|
+
db: Database
|
|
347
|
+
): Promise<string> {
|
|
348
|
+
// 获取本周记忆摘要
|
|
349
|
+
const today = new Date();
|
|
350
|
+
const weekStart = new Date(today);
|
|
351
|
+
weekStart.setDate(today.getDate() - today.getDay());
|
|
352
|
+
const weekStartStr = weekStart.toISOString().split('T')[0];
|
|
353
|
+
|
|
354
|
+
const weekMemories = db.prepare(`
|
|
355
|
+
SELECT summary, importance
|
|
356
|
+
FROM memories
|
|
357
|
+
WHERE date(created_at) >= date(?)
|
|
358
|
+
ORDER BY importance DESC
|
|
359
|
+
LIMIT 10
|
|
360
|
+
`).all(weekStartStr) as { summary: string; importance: number }[];
|
|
361
|
+
|
|
362
|
+
if (weekMemories.length === 0) {
|
|
363
|
+
return '';
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
// 构建摘要文本
|
|
367
|
+
const lines = ['## 记忆摘要\n'];
|
|
368
|
+
lines.push(`本周共有 ${weekMemories.length} 条记忆记录。\n`);
|
|
369
|
+
lines.push('### 重点内容:\n');
|
|
370
|
+
|
|
371
|
+
for (const m of weekMemories.slice(0, 5)) {
|
|
372
|
+
if (m.summary) {
|
|
373
|
+
lines.push(`- ${m.summary}`);
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
return lines.join('\n');
|
|
378
|
+
}
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
**Step 2: 提交**
|
|
382
|
+
|
|
383
|
+
```bash
|
|
384
|
+
git add plugin/src/hooks/bootstrap.ts
|
|
385
|
+
git commit -m "feat: implement agent:bootstrap hook"
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
---
|
|
389
|
+
|
|
390
|
+
## Task 8: 实现 Agent Tools
|
|
391
|
+
|
|
392
|
+
**Files:**
|
|
393
|
+
- Create: `plugin/src/tools/memory.ts`
|
|
394
|
+
|
|
395
|
+
**Step 1: 创建 memory_save 工具**
|
|
396
|
+
|
|
397
|
+
```typescript
|
|
398
|
+
// plugin/src/tools/memory.ts
|
|
399
|
+
import type { Database } from 'better-sqlite3';
|
|
400
|
+
import { v4 as uuidv4 } from 'uuid';
|
|
401
|
+
|
|
402
|
+
export function registerMemoryTools(context: any, db: Database) {
|
|
403
|
+
context.tools.register({
|
|
404
|
+
name: 'memory_save',
|
|
405
|
+
description: 'Save conversation to memory with metadata',
|
|
406
|
+
schema: {
|
|
407
|
+
type: 'object',
|
|
408
|
+
properties: {
|
|
409
|
+
content: { type: 'string', description: 'Conversation content' },
|
|
410
|
+
metadata: {
|
|
411
|
+
type: 'object',
|
|
412
|
+
properties: {
|
|
413
|
+
tags: { type: 'array', items: { type: 'string' } },
|
|
414
|
+
keywords: { type: 'array', items: { type: 'string' } },
|
|
415
|
+
importance: { type: 'number', minimum: 0, maximum: 1 }
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
},
|
|
419
|
+
required: ['content']
|
|
420
|
+
},
|
|
421
|
+
handler: async (params: any) => {
|
|
422
|
+
const { content, metadata = {} } = params;
|
|
423
|
+
const memoryId = uuidv4();
|
|
424
|
+
|
|
425
|
+
const dataDir = process.env.CLAW_MEMORY_DATA_DIR || '~/.openclaw/claw-memory';
|
|
426
|
+
const contentPath = `${dataDir}/memories/${memoryId}.md`;
|
|
427
|
+
|
|
428
|
+
const fs = await import('fs/promises');
|
|
429
|
+
await fs.writeFile(contentPath, content);
|
|
430
|
+
|
|
431
|
+
db.prepare(`
|
|
432
|
+
INSERT INTO memories (id, content_path, summary, importance, created_at)
|
|
433
|
+
VALUES (?, ?, ?, ?, datetime('now'))
|
|
434
|
+
`).run(memoryId, contentPath, metadata.summary || content.substring(0, 200), metadata.importance || 0.5);
|
|
435
|
+
|
|
436
|
+
return { success: true, memory_id: memoryId };
|
|
437
|
+
}
|
|
438
|
+
});
|
|
439
|
+
|
|
440
|
+
context.tools.register({
|
|
441
|
+
name: 'memory_search',
|
|
442
|
+
description: 'Search memories by query',
|
|
443
|
+
schema: {
|
|
444
|
+
type: 'object',
|
|
445
|
+
properties: {
|
|
446
|
+
query: { type: 'string' },
|
|
447
|
+
limit: { type: 'number', default: 10 }
|
|
448
|
+
},
|
|
449
|
+
required: ['query']
|
|
450
|
+
},
|
|
451
|
+
handler: async (params: any) => {
|
|
452
|
+
const { query, limit = 10 } = params;
|
|
453
|
+
const memories = db.prepare(`
|
|
454
|
+
SELECT id, summary, importance, created_at
|
|
455
|
+
FROM memories
|
|
456
|
+
WHERE summary LIKE ?
|
|
457
|
+
ORDER BY importance DESC
|
|
458
|
+
LIMIT ?
|
|
459
|
+
).all(`%${query}%`, limit);
|
|
460
|
+
|
|
461
|
+
return { memories };
|
|
462
|
+
}
|
|
463
|
+
});
|
|
464
|
+
|
|
465
|
+
context.tools.register({
|
|
466
|
+
name: 'memory_summary',
|
|
467
|
+
description: 'Get memory summary for time period',
|
|
468
|
+
schema: {
|
|
469
|
+
type: 'object',
|
|
470
|
+
properties: {
|
|
471
|
+
period: { type: 'string', enum: ['day', 'week', 'month'] },
|
|
472
|
+
date: { type: 'string' }
|
|
473
|
+
},
|
|
474
|
+
required: ['period']
|
|
475
|
+
},
|
|
476
|
+
handler: async (params: any) => {
|
|
477
|
+
const { period, date } = params;
|
|
478
|
+
// 复用现有 summarizer 逻辑
|
|
479
|
+
// TODO: 实现摘要获取
|
|
480
|
+
return { summary: 'TODO' };
|
|
481
|
+
}
|
|
482
|
+
});
|
|
483
|
+
}
|
|
484
|
+
```
|
|
485
|
+
|
|
486
|
+
**Step 2: 提交**
|
|
487
|
+
|
|
488
|
+
```bash
|
|
489
|
+
git add plugin/src/tools/memory.ts
|
|
490
|
+
git commit -m "feat: register memory tools"
|
|
491
|
+
```
|
|
492
|
+
|
|
493
|
+
---
|
|
494
|
+
|
|
495
|
+
## Task 9: 复用 Scheduler 服务
|
|
496
|
+
|
|
497
|
+
由于现有 `claw-memory/src/services/scheduler.ts` 已有完整的 Scheduler,需要适配到插件中。
|
|
498
|
+
|
|
499
|
+
**Files:**
|
|
500
|
+
- Create: `plugin/src/services/scheduler.ts`
|
|
501
|
+
|
|
502
|
+
**Step 1: 复制并修改 Scheduler**
|
|
503
|
+
|
|
504
|
+
从 `src/services/scheduler.ts` 复制主要内容,并修改:
|
|
505
|
+
- 移除外部依赖(直接从插件上下文获取 db)
|
|
506
|
+
- 适配插件配置
|
|
507
|
+
|
|
508
|
+
**Step 2: 提交**
|
|
509
|
+
|
|
510
|
+
```bash
|
|
511
|
+
git add plugin/src/services/scheduler.ts
|
|
512
|
+
git commit -m "feat: add scheduler service"
|
|
513
|
+
```
|
|
514
|
+
|
|
515
|
+
---
|
|
516
|
+
|
|
517
|
+
## Task 10: 集成所有组件
|
|
518
|
+
|
|
519
|
+
**Files:**
|
|
520
|
+
- Modify: `plugin/src/index.ts`
|
|
521
|
+
|
|
522
|
+
**Step 1: 更新插件入口**
|
|
523
|
+
|
|
524
|
+
```typescript
|
|
525
|
+
// plugin/src/index.ts
|
|
526
|
+
import type { OpenClawPlugin } from '@openclaw/plugin-sdk';
|
|
527
|
+
import { getConfig, PluginConfig } from './config.js';
|
|
528
|
+
import { getDatabase } from './db/schema.js';
|
|
529
|
+
import { handleMessageSent } from './hooks/message.js';
|
|
530
|
+
import { handleAgentBootstrap } from './hooks/bootstrap.js';
|
|
531
|
+
import { registerMemoryTools } from './tools/memory.js';
|
|
532
|
+
|
|
533
|
+
export default {
|
|
534
|
+
name: 'claw-memory',
|
|
535
|
+
version: '0.1.0',
|
|
536
|
+
|
|
537
|
+
async register(context: any) {
|
|
538
|
+
const config = getConfig(context);
|
|
539
|
+
if (!config.enabled) {
|
|
540
|
+
console.log('[ClawMemory] Plugin disabled');
|
|
541
|
+
return;
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
console.log('[ClawMemory] Starting...');
|
|
545
|
+
|
|
546
|
+
// 初始化数据库
|
|
547
|
+
const db = getDatabase();
|
|
548
|
+
|
|
549
|
+
// 注册 Hooks
|
|
550
|
+
await context.hooks.register('message:sent', async (event: any) => {
|
|
551
|
+
if (config.autoSave) {
|
|
552
|
+
try {
|
|
553
|
+
await handleMessageSent(event, db, config);
|
|
554
|
+
} catch (error) {
|
|
555
|
+
console.error('[ClawMemory] Failed to save memory:', error);
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
});
|
|
559
|
+
|
|
560
|
+
await context.hooks.register('agent:bootstrap', async (bootstrapContext: any) => {
|
|
561
|
+
try {
|
|
562
|
+
const summary = await handleAgentBootstrap(bootstrapContext, db);
|
|
563
|
+
if (summary) {
|
|
564
|
+
bootstrapContext.context = bootstrapContext.context || '';
|
|
565
|
+
bootstrapContext.context += '\n\n' + summary;
|
|
566
|
+
}
|
|
567
|
+
} catch (error) {
|
|
568
|
+
console.error('[ClawMemory] Failed to inject summary:', error);
|
|
569
|
+
}
|
|
570
|
+
});
|
|
571
|
+
|
|
572
|
+
// 注册 Tools
|
|
573
|
+
registerMemoryTools(context, db);
|
|
574
|
+
|
|
575
|
+
// 启动 Scheduler
|
|
576
|
+
if (config.scheduler.enabled) {
|
|
577
|
+
// TODO: 启动定时任务
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
console.log('[ClawMemory] Started successfully');
|
|
581
|
+
}
|
|
582
|
+
} satisfies OpenClawPlugin;
|
|
583
|
+
```
|
|
584
|
+
|
|
585
|
+
**Step 2: 测试编译**
|
|
586
|
+
|
|
587
|
+
```bash
|
|
588
|
+
cd plugin
|
|
589
|
+
npm install
|
|
590
|
+
npm run build
|
|
591
|
+
```
|
|
592
|
+
|
|
593
|
+
**Step 3: 提交**
|
|
594
|
+
|
|
595
|
+
```bash
|
|
596
|
+
git add plugin/src/index.ts
|
|
597
|
+
git commit -m "feat: integrate all components"
|
|
598
|
+
```
|
|
599
|
+
|
|
600
|
+
---
|
|
601
|
+
|
|
602
|
+
## Task 11: 最终测试
|
|
603
|
+
|
|
604
|
+
**Step 1: 模拟安装测试**
|
|
605
|
+
|
|
606
|
+
由于需要 OpenClaw 环境,这里先验证代码能编译通过。
|
|
607
|
+
|
|
608
|
+
```bash
|
|
609
|
+
cd plugin
|
|
610
|
+
npm run build
|
|
611
|
+
ls -la dist/
|
|
612
|
+
```
|
|
613
|
+
|
|
614
|
+
Expected: 生成 dist/index.js 等文件
|
|
615
|
+
|
|
616
|
+
---
|
|
617
|
+
|
|
618
|
+
## Task 12: 合并到主分支
|
|
619
|
+
|
|
620
|
+
**Step 1: 切换到主分支**
|
|
621
|
+
|
|
622
|
+
```bash
|
|
623
|
+
git checkout main
|
|
624
|
+
```
|
|
625
|
+
|
|
626
|
+
**Step 2: 合并功能分支**
|
|
627
|
+
|
|
628
|
+
```bash
|
|
629
|
+
git merge feature/claw-memory-plugin
|
|
630
|
+
```
|
|
631
|
+
|
|
632
|
+
**Step 3: 推送到远程**
|
|
633
|
+
|
|
634
|
+
```bash
|
|
635
|
+
git push origin main
|
|
636
|
+
```
|
|
637
|
+
|
|
638
|
+
**Step 4: 删除功能分支(可选)**
|
|
639
|
+
|
|
640
|
+
```bash
|
|
641
|
+
git branch -d feature/claw-memory-plugin
|
|
642
|
+
```
|