memory-lancedb-pro 1.0.25 → 1.0.26
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/CHANGELOG.md +15 -0
- package/README.md +14 -1
- package/README_CN.md +14 -1
- package/index.ts +243 -85
- package/openclaw.plugin.json +15 -1
- package/package.json +1 -1
- package/src/access-tracker.ts +330 -0
- package/src/retriever.ts +183 -68
- package/src/store.ts +207 -67
- package/src/tools.ts +339 -87
- package/test/access-tracker.test.mjs +770 -0
- package/test/cli-smoke.mjs +22 -0
package/src/tools.ts
CHANGED
|
@@ -16,7 +16,13 @@ import type { Embedder } from "./embedder.js";
|
|
|
16
16
|
// Types
|
|
17
17
|
// ============================================================================
|
|
18
18
|
|
|
19
|
-
export const MEMORY_CATEGORIES = [
|
|
19
|
+
export const MEMORY_CATEGORIES = [
|
|
20
|
+
"preference",
|
|
21
|
+
"fact",
|
|
22
|
+
"decision",
|
|
23
|
+
"entity",
|
|
24
|
+
"other",
|
|
25
|
+
] as const;
|
|
20
26
|
|
|
21
27
|
interface ToolContext {
|
|
22
28
|
retriever: MemoryRetriever;
|
|
@@ -47,7 +53,7 @@ function clamp01(value: number, fallback = 0.7): number {
|
|
|
47
53
|
}
|
|
48
54
|
|
|
49
55
|
function sanitizeMemoryForSerialization(results: RetrievalResult[]) {
|
|
50
|
-
return results.map(r => ({
|
|
56
|
+
return results.map((r) => ({
|
|
51
57
|
id: r.entry.id,
|
|
52
58
|
text: r.entry.text,
|
|
53
59
|
category: r.entry.category,
|
|
@@ -62,22 +68,41 @@ function sanitizeMemoryForSerialization(results: RetrievalResult[]) {
|
|
|
62
68
|
// Core Tools (Backward Compatible)
|
|
63
69
|
// ============================================================================
|
|
64
70
|
|
|
65
|
-
export function registerMemoryRecallTool(
|
|
71
|
+
export function registerMemoryRecallTool(
|
|
72
|
+
api: OpenClawPluginApi,
|
|
73
|
+
context: ToolContext,
|
|
74
|
+
) {
|
|
66
75
|
api.registerTool(
|
|
67
76
|
(toolCtx) => {
|
|
68
77
|
const agentId = resolveAgentId((toolCtx as any)?.agentId, context.agentId) ?? "main";
|
|
69
78
|
return {
|
|
70
79
|
name: "memory_recall",
|
|
71
80
|
label: "Memory Recall",
|
|
72
|
-
description:
|
|
81
|
+
description:
|
|
82
|
+
"Search through long-term memories using hybrid retrieval (vector + keyword search). Use when you need context about user preferences, past decisions, or previously discussed topics.",
|
|
73
83
|
parameters: Type.Object({
|
|
74
|
-
query: Type.String({
|
|
75
|
-
|
|
76
|
-
|
|
84
|
+
query: Type.String({
|
|
85
|
+
description: "Search query for finding relevant memories",
|
|
86
|
+
}),
|
|
87
|
+
limit: Type.Optional(
|
|
88
|
+
Type.Number({
|
|
89
|
+
description: "Max results to return (default: 5, max: 20)",
|
|
90
|
+
}),
|
|
91
|
+
),
|
|
92
|
+
scope: Type.Optional(
|
|
93
|
+
Type.String({
|
|
94
|
+
description: "Specific memory scope to search in (optional)",
|
|
95
|
+
}),
|
|
96
|
+
),
|
|
77
97
|
category: Type.Optional(stringEnum(MEMORY_CATEGORIES)),
|
|
78
98
|
}),
|
|
79
99
|
async execute(_toolCallId, params) {
|
|
80
|
-
const {
|
|
100
|
+
const {
|
|
101
|
+
query,
|
|
102
|
+
limit = 5,
|
|
103
|
+
scope,
|
|
104
|
+
category,
|
|
105
|
+
} = params as {
|
|
81
106
|
query: string;
|
|
82
107
|
limit?: number;
|
|
83
108
|
scope?: string;
|
|
@@ -94,8 +119,13 @@ export function registerMemoryRecallTool(api: OpenClawPluginApi, context: ToolCo
|
|
|
94
119
|
scopeFilter = [scope];
|
|
95
120
|
} else {
|
|
96
121
|
return {
|
|
97
|
-
content: [
|
|
98
|
-
|
|
122
|
+
content: [
|
|
123
|
+
{ type: "text", text: `Access denied to scope: ${scope}` },
|
|
124
|
+
],
|
|
125
|
+
details: {
|
|
126
|
+
error: "scope_access_denied",
|
|
127
|
+
requestedScope: scope,
|
|
128
|
+
},
|
|
99
129
|
};
|
|
100
130
|
}
|
|
101
131
|
}
|
|
@@ -105,6 +135,7 @@ export function registerMemoryRecallTool(api: OpenClawPluginApi, context: ToolCo
|
|
|
105
135
|
limit: safeLimit,
|
|
106
136
|
scopeFilter,
|
|
107
137
|
category,
|
|
138
|
+
source: "manual",
|
|
108
139
|
});
|
|
109
140
|
|
|
110
141
|
if (results.length === 0) {
|
|
@@ -121,12 +152,17 @@ export function registerMemoryRecallTool(api: OpenClawPluginApi, context: ToolCo
|
|
|
121
152
|
if (r.sources.bm25) sources.push("BM25");
|
|
122
153
|
if (r.sources.reranked) sources.push("reranked");
|
|
123
154
|
|
|
124
|
-
return `${i + 1}. [${r.entry.id}] [${r.entry.category}:${r.entry.scope}] ${r.entry.text} (${(r.score * 100).toFixed(0)}%${sources.length > 0 ? `, ${sources.join(
|
|
155
|
+
return `${i + 1}. [${r.entry.id}] [${r.entry.category}:${r.entry.scope}] ${r.entry.text} (${(r.score * 100).toFixed(0)}%${sources.length > 0 ? `, ${sources.join("+")}` : ""})`;
|
|
125
156
|
})
|
|
126
157
|
.join("\n");
|
|
127
158
|
|
|
128
159
|
return {
|
|
129
|
-
content: [
|
|
160
|
+
content: [
|
|
161
|
+
{
|
|
162
|
+
type: "text",
|
|
163
|
+
text: `Found ${results.length} memories:\n\n${text}`,
|
|
164
|
+
},
|
|
165
|
+
],
|
|
130
166
|
details: {
|
|
131
167
|
count: results.length,
|
|
132
168
|
memories: sanitizeMemoryForSerialization(results),
|
|
@@ -137,30 +173,45 @@ export function registerMemoryRecallTool(api: OpenClawPluginApi, context: ToolCo
|
|
|
137
173
|
};
|
|
138
174
|
} catch (error) {
|
|
139
175
|
return {
|
|
140
|
-
content: [
|
|
176
|
+
content: [
|
|
177
|
+
{
|
|
178
|
+
type: "text",
|
|
179
|
+
text: `Memory recall failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
180
|
+
},
|
|
181
|
+
],
|
|
141
182
|
details: { error: "recall_failed", message: String(error) },
|
|
142
183
|
};
|
|
143
184
|
}
|
|
144
185
|
},
|
|
145
186
|
};
|
|
146
187
|
},
|
|
147
|
-
{ name: "memory_recall" }
|
|
188
|
+
{ name: "memory_recall" },
|
|
148
189
|
);
|
|
149
190
|
}
|
|
150
191
|
|
|
151
|
-
export function registerMemoryStoreTool(
|
|
192
|
+
export function registerMemoryStoreTool(
|
|
193
|
+
api: OpenClawPluginApi,
|
|
194
|
+
context: ToolContext,
|
|
195
|
+
) {
|
|
152
196
|
api.registerTool(
|
|
153
197
|
(toolCtx) => {
|
|
154
198
|
const agentId = resolveAgentId((toolCtx as any)?.agentId, context.agentId) ?? "main";
|
|
155
199
|
return {
|
|
156
200
|
name: "memory_store",
|
|
157
201
|
label: "Memory Store",
|
|
158
|
-
description:
|
|
202
|
+
description:
|
|
203
|
+
"Save important information in long-term memory. Use for preferences, facts, decisions, and other notable information.",
|
|
159
204
|
parameters: Type.Object({
|
|
160
205
|
text: Type.String({ description: "Information to remember" }),
|
|
161
|
-
importance: Type.Optional(
|
|
206
|
+
importance: Type.Optional(
|
|
207
|
+
Type.Number({ description: "Importance score 0-1 (default: 0.7)" }),
|
|
208
|
+
),
|
|
162
209
|
category: Type.Optional(stringEnum(MEMORY_CATEGORIES)),
|
|
163
|
-
scope: Type.Optional(
|
|
210
|
+
scope: Type.Optional(
|
|
211
|
+
Type.String({
|
|
212
|
+
description: "Memory scope (optional, defaults to agent scope)",
|
|
213
|
+
}),
|
|
214
|
+
),
|
|
164
215
|
}),
|
|
165
216
|
async execute(_toolCallId, params) {
|
|
166
217
|
const {
|
|
@@ -182,15 +233,28 @@ export function registerMemoryStoreTool(api: OpenClawPluginApi, context: ToolCon
|
|
|
182
233
|
// Validate scope access
|
|
183
234
|
if (!context.scopeManager.isAccessible(targetScope, agentId)) {
|
|
184
235
|
return {
|
|
185
|
-
content: [
|
|
186
|
-
|
|
236
|
+
content: [
|
|
237
|
+
{
|
|
238
|
+
type: "text",
|
|
239
|
+
text: `Access denied to scope: ${targetScope}`,
|
|
240
|
+
},
|
|
241
|
+
],
|
|
242
|
+
details: {
|
|
243
|
+
error: "scope_access_denied",
|
|
244
|
+
requestedScope: targetScope,
|
|
245
|
+
},
|
|
187
246
|
};
|
|
188
247
|
}
|
|
189
248
|
|
|
190
249
|
// Reject noise before wasting an embedding API call
|
|
191
250
|
if (isNoise(text)) {
|
|
192
251
|
return {
|
|
193
|
-
content: [
|
|
252
|
+
content: [
|
|
253
|
+
{
|
|
254
|
+
type: "text",
|
|
255
|
+
text: `Skipped: text detected as noise (greeting, boilerplate, or meta-question)`,
|
|
256
|
+
},
|
|
257
|
+
],
|
|
194
258
|
details: { action: "noise_filtered", text: text.slice(0, 60) },
|
|
195
259
|
};
|
|
196
260
|
}
|
|
@@ -199,7 +263,9 @@ export function registerMemoryStoreTool(api: OpenClawPluginApi, context: ToolCon
|
|
|
199
263
|
const vector = await context.embedder.embedPassage(text);
|
|
200
264
|
|
|
201
265
|
// Check for duplicates using raw vector similarity (bypasses importance/recency weighting)
|
|
202
|
-
const existing = await context.store.vectorSearch(vector, 1, 0.1, [
|
|
266
|
+
const existing = await context.store.vectorSearch(vector, 1, 0.1, [
|
|
267
|
+
targetScope,
|
|
268
|
+
]);
|
|
203
269
|
|
|
204
270
|
if (existing.length > 0 && existing[0].score > 0.98) {
|
|
205
271
|
return {
|
|
@@ -228,7 +294,12 @@ export function registerMemoryStoreTool(api: OpenClawPluginApi, context: ToolCon
|
|
|
228
294
|
});
|
|
229
295
|
|
|
230
296
|
return {
|
|
231
|
-
content: [
|
|
297
|
+
content: [
|
|
298
|
+
{
|
|
299
|
+
type: "text",
|
|
300
|
+
text: `Stored: "${text.slice(0, 100)}${text.length > 100 ? "..." : ""}" in scope '${targetScope}'`,
|
|
301
|
+
},
|
|
302
|
+
],
|
|
232
303
|
details: {
|
|
233
304
|
action: "created",
|
|
234
305
|
id: entry.id,
|
|
@@ -239,29 +310,46 @@ export function registerMemoryStoreTool(api: OpenClawPluginApi, context: ToolCon
|
|
|
239
310
|
};
|
|
240
311
|
} catch (error) {
|
|
241
312
|
return {
|
|
242
|
-
content: [
|
|
313
|
+
content: [
|
|
314
|
+
{
|
|
315
|
+
type: "text",
|
|
316
|
+
text: `Memory storage failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
317
|
+
},
|
|
318
|
+
],
|
|
243
319
|
details: { error: "store_failed", message: String(error) },
|
|
244
320
|
};
|
|
245
321
|
}
|
|
246
322
|
},
|
|
247
323
|
};
|
|
248
324
|
},
|
|
249
|
-
{ name: "memory_store" }
|
|
325
|
+
{ name: "memory_store" },
|
|
250
326
|
);
|
|
251
327
|
}
|
|
252
328
|
|
|
253
|
-
export function registerMemoryForgetTool(
|
|
329
|
+
export function registerMemoryForgetTool(
|
|
330
|
+
api: OpenClawPluginApi,
|
|
331
|
+
context: ToolContext,
|
|
332
|
+
) {
|
|
254
333
|
api.registerTool(
|
|
255
334
|
(toolCtx) => {
|
|
256
335
|
const agentId = resolveAgentId((toolCtx as any)?.agentId, context.agentId) ?? "main";
|
|
257
336
|
return {
|
|
258
337
|
name: "memory_forget",
|
|
259
338
|
label: "Memory Forget",
|
|
260
|
-
description:
|
|
339
|
+
description:
|
|
340
|
+
"Delete specific memories. Supports both search-based and direct ID-based deletion.",
|
|
261
341
|
parameters: Type.Object({
|
|
262
|
-
query: Type.Optional(
|
|
263
|
-
|
|
264
|
-
|
|
342
|
+
query: Type.Optional(
|
|
343
|
+
Type.String({ description: "Search query to find memory to delete" }),
|
|
344
|
+
),
|
|
345
|
+
memoryId: Type.Optional(
|
|
346
|
+
Type.String({ description: "Specific memory ID to delete" }),
|
|
347
|
+
),
|
|
348
|
+
scope: Type.Optional(
|
|
349
|
+
Type.String({
|
|
350
|
+
description: "Scope to search/delete from (optional)",
|
|
351
|
+
}),
|
|
352
|
+
),
|
|
265
353
|
}),
|
|
266
354
|
async execute(_toolCallId, params) {
|
|
267
355
|
const { query, memoryId, scope } = params as {
|
|
@@ -278,8 +366,13 @@ export function registerMemoryForgetTool(api: OpenClawPluginApi, context: ToolCo
|
|
|
278
366
|
scopeFilter = [scope];
|
|
279
367
|
} else {
|
|
280
368
|
return {
|
|
281
|
-
content: [
|
|
282
|
-
|
|
369
|
+
content: [
|
|
370
|
+
{ type: "text", text: `Access denied to scope: ${scope}` },
|
|
371
|
+
],
|
|
372
|
+
details: {
|
|
373
|
+
error: "scope_access_denied",
|
|
374
|
+
requestedScope: scope,
|
|
375
|
+
},
|
|
283
376
|
};
|
|
284
377
|
}
|
|
285
378
|
}
|
|
@@ -288,12 +381,19 @@ export function registerMemoryForgetTool(api: OpenClawPluginApi, context: ToolCo
|
|
|
288
381
|
const deleted = await context.store.delete(memoryId, scopeFilter);
|
|
289
382
|
if (deleted) {
|
|
290
383
|
return {
|
|
291
|
-
content: [
|
|
384
|
+
content: [
|
|
385
|
+
{ type: "text", text: `Memory ${memoryId} forgotten.` },
|
|
386
|
+
],
|
|
292
387
|
details: { action: "deleted", id: memoryId },
|
|
293
388
|
};
|
|
294
389
|
} else {
|
|
295
390
|
return {
|
|
296
|
-
content: [
|
|
391
|
+
content: [
|
|
392
|
+
{
|
|
393
|
+
type: "text",
|
|
394
|
+
text: `Memory ${memoryId} not found or access denied.`,
|
|
395
|
+
},
|
|
396
|
+
],
|
|
297
397
|
details: { error: "not_found", id: memoryId },
|
|
298
398
|
};
|
|
299
399
|
}
|
|
@@ -308,23 +408,36 @@ export function registerMemoryForgetTool(api: OpenClawPluginApi, context: ToolCo
|
|
|
308
408
|
|
|
309
409
|
if (results.length === 0) {
|
|
310
410
|
return {
|
|
311
|
-
content: [
|
|
411
|
+
content: [
|
|
412
|
+
{ type: "text", text: "No matching memories found." },
|
|
413
|
+
],
|
|
312
414
|
details: { found: 0, query },
|
|
313
415
|
};
|
|
314
416
|
}
|
|
315
417
|
|
|
316
418
|
if (results.length === 1 && results[0].score > 0.9) {
|
|
317
|
-
const deleted = await context.store.delete(
|
|
419
|
+
const deleted = await context.store.delete(
|
|
420
|
+
results[0].entry.id,
|
|
421
|
+
scopeFilter,
|
|
422
|
+
);
|
|
318
423
|
if (deleted) {
|
|
319
424
|
return {
|
|
320
|
-
content: [
|
|
425
|
+
content: [
|
|
426
|
+
{
|
|
427
|
+
type: "text",
|
|
428
|
+
text: `Forgotten: "${results[0].entry.text}"`,
|
|
429
|
+
},
|
|
430
|
+
],
|
|
321
431
|
details: { action: "deleted", id: results[0].entry.id },
|
|
322
432
|
};
|
|
323
433
|
}
|
|
324
434
|
}
|
|
325
435
|
|
|
326
436
|
const list = results
|
|
327
|
-
.map(
|
|
437
|
+
.map(
|
|
438
|
+
(r) =>
|
|
439
|
+
`- [${r.entry.id.slice(0, 8)}] ${r.entry.text.slice(0, 60)}${r.entry.text.length > 60 ? "..." : ""}`,
|
|
440
|
+
)
|
|
328
441
|
.join("\n");
|
|
329
442
|
|
|
330
443
|
return {
|
|
@@ -342,19 +455,29 @@ export function registerMemoryForgetTool(api: OpenClawPluginApi, context: ToolCo
|
|
|
342
455
|
}
|
|
343
456
|
|
|
344
457
|
return {
|
|
345
|
-
content: [
|
|
458
|
+
content: [
|
|
459
|
+
{
|
|
460
|
+
type: "text",
|
|
461
|
+
text: "Provide either 'query' to search for memories or 'memoryId' to delete specific memory.",
|
|
462
|
+
},
|
|
463
|
+
],
|
|
346
464
|
details: { error: "missing_param" },
|
|
347
465
|
};
|
|
348
466
|
} catch (error) {
|
|
349
467
|
return {
|
|
350
|
-
content: [
|
|
468
|
+
content: [
|
|
469
|
+
{
|
|
470
|
+
type: "text",
|
|
471
|
+
text: `Memory deletion failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
472
|
+
},
|
|
473
|
+
],
|
|
351
474
|
details: { error: "delete_failed", message: String(error) },
|
|
352
475
|
};
|
|
353
476
|
}
|
|
354
477
|
},
|
|
355
478
|
};
|
|
356
479
|
},
|
|
357
|
-
{ name: "memory_forget" }
|
|
480
|
+
{ name: "memory_forget" },
|
|
358
481
|
);
|
|
359
482
|
}
|
|
360
483
|
|
|
@@ -362,18 +485,31 @@ export function registerMemoryForgetTool(api: OpenClawPluginApi, context: ToolCo
|
|
|
362
485
|
// Update Tool
|
|
363
486
|
// ============================================================================
|
|
364
487
|
|
|
365
|
-
export function registerMemoryUpdateTool(
|
|
488
|
+
export function registerMemoryUpdateTool(
|
|
489
|
+
api: OpenClawPluginApi,
|
|
490
|
+
context: ToolContext,
|
|
491
|
+
) {
|
|
366
492
|
api.registerTool(
|
|
367
493
|
(toolCtx) => {
|
|
368
494
|
const agentId = resolveAgentId((toolCtx as any)?.agentId, context.agentId) ?? "main";
|
|
369
495
|
return {
|
|
370
496
|
name: "memory_update",
|
|
371
497
|
label: "Memory Update",
|
|
372
|
-
description:
|
|
498
|
+
description:
|
|
499
|
+
"Update an existing memory in-place. Preserves original timestamp. Use when correcting outdated info or adjusting importance/category without losing creation date.",
|
|
373
500
|
parameters: Type.Object({
|
|
374
|
-
memoryId: Type.String({
|
|
375
|
-
|
|
376
|
-
|
|
501
|
+
memoryId: Type.String({
|
|
502
|
+
description:
|
|
503
|
+
"ID of the memory to update (full UUID or 8+ char prefix)",
|
|
504
|
+
}),
|
|
505
|
+
text: Type.Optional(
|
|
506
|
+
Type.String({
|
|
507
|
+
description: "New text content (triggers re-embedding)",
|
|
508
|
+
}),
|
|
509
|
+
),
|
|
510
|
+
importance: Type.Optional(
|
|
511
|
+
Type.Number({ description: "New importance score 0-1" }),
|
|
512
|
+
),
|
|
377
513
|
category: Type.Optional(stringEnum(MEMORY_CATEGORIES)),
|
|
378
514
|
}),
|
|
379
515
|
async execute(_toolCallId, params) {
|
|
@@ -387,7 +523,12 @@ export function registerMemoryUpdateTool(api: OpenClawPluginApi, context: ToolCo
|
|
|
387
523
|
try {
|
|
388
524
|
if (!text && importance === undefined && !category) {
|
|
389
525
|
return {
|
|
390
|
-
content: [
|
|
526
|
+
content: [
|
|
527
|
+
{
|
|
528
|
+
type: "text",
|
|
529
|
+
text: "Nothing to update. Provide at least one of: text, importance, category.",
|
|
530
|
+
},
|
|
531
|
+
],
|
|
391
532
|
details: { error: "no_updates" },
|
|
392
533
|
};
|
|
393
534
|
}
|
|
@@ -407,7 +548,12 @@ export function registerMemoryUpdateTool(api: OpenClawPluginApi, context: ToolCo
|
|
|
407
548
|
});
|
|
408
549
|
if (results.length === 0) {
|
|
409
550
|
return {
|
|
410
|
-
content: [
|
|
551
|
+
content: [
|
|
552
|
+
{
|
|
553
|
+
type: "text",
|
|
554
|
+
text: `No memory found matching "${memoryId}".`,
|
|
555
|
+
},
|
|
556
|
+
],
|
|
411
557
|
details: { error: "not_found", query: memoryId },
|
|
412
558
|
};
|
|
413
559
|
}
|
|
@@ -415,11 +561,22 @@ export function registerMemoryUpdateTool(api: OpenClawPluginApi, context: ToolCo
|
|
|
415
561
|
resolvedId = results[0].entry.id;
|
|
416
562
|
} else {
|
|
417
563
|
const list = results
|
|
418
|
-
.map(
|
|
564
|
+
.map(
|
|
565
|
+
(r) =>
|
|
566
|
+
`- [${r.entry.id.slice(0, 8)}] ${r.entry.text.slice(0, 60)}${r.entry.text.length > 60 ? "..." : ""}`,
|
|
567
|
+
)
|
|
419
568
|
.join("\n");
|
|
420
569
|
return {
|
|
421
|
-
content: [
|
|
422
|
-
|
|
570
|
+
content: [
|
|
571
|
+
{
|
|
572
|
+
type: "text",
|
|
573
|
+
text: `Multiple matches. Specify memoryId:\n${list}`,
|
|
574
|
+
},
|
|
575
|
+
],
|
|
576
|
+
details: {
|
|
577
|
+
action: "candidates",
|
|
578
|
+
candidates: sanitizeMemoryForSerialization(results),
|
|
579
|
+
},
|
|
423
580
|
};
|
|
424
581
|
}
|
|
425
582
|
}
|
|
@@ -429,7 +586,12 @@ export function registerMemoryUpdateTool(api: OpenClawPluginApi, context: ToolCo
|
|
|
429
586
|
if (text) {
|
|
430
587
|
if (isNoise(text)) {
|
|
431
588
|
return {
|
|
432
|
-
content: [
|
|
589
|
+
content: [
|
|
590
|
+
{
|
|
591
|
+
type: "text",
|
|
592
|
+
text: "Skipped: updated text detected as noise",
|
|
593
|
+
},
|
|
594
|
+
],
|
|
433
595
|
details: { action: "noise_filtered" },
|
|
434
596
|
};
|
|
435
597
|
}
|
|
@@ -439,20 +601,35 @@ export function registerMemoryUpdateTool(api: OpenClawPluginApi, context: ToolCo
|
|
|
439
601
|
const updates: Record<string, any> = {};
|
|
440
602
|
if (text) updates.text = text;
|
|
441
603
|
if (newVector) updates.vector = newVector;
|
|
442
|
-
if (importance !== undefined)
|
|
604
|
+
if (importance !== undefined)
|
|
605
|
+
updates.importance = clamp01(importance, 0.7);
|
|
443
606
|
if (category) updates.category = category;
|
|
444
607
|
|
|
445
|
-
const updated = await context.store.update(
|
|
608
|
+
const updated = await context.store.update(
|
|
609
|
+
resolvedId,
|
|
610
|
+
updates,
|
|
611
|
+
scopeFilter,
|
|
612
|
+
);
|
|
446
613
|
|
|
447
614
|
if (!updated) {
|
|
448
615
|
return {
|
|
449
|
-
content: [
|
|
616
|
+
content: [
|
|
617
|
+
{
|
|
618
|
+
type: "text",
|
|
619
|
+
text: `Memory ${resolvedId.slice(0, 8)}... not found or access denied.`,
|
|
620
|
+
},
|
|
621
|
+
],
|
|
450
622
|
details: { error: "not_found", id: resolvedId },
|
|
451
623
|
};
|
|
452
624
|
}
|
|
453
625
|
|
|
454
626
|
return {
|
|
455
|
-
content: [
|
|
627
|
+
content: [
|
|
628
|
+
{
|
|
629
|
+
type: "text",
|
|
630
|
+
text: `Updated memory ${updated.id.slice(0, 8)}...: "${updated.text.slice(0, 80)}${updated.text.length > 80 ? "..." : ""}"`,
|
|
631
|
+
},
|
|
632
|
+
],
|
|
456
633
|
details: {
|
|
457
634
|
action: "updated",
|
|
458
635
|
id: updated.id,
|
|
@@ -464,14 +641,19 @@ export function registerMemoryUpdateTool(api: OpenClawPluginApi, context: ToolCo
|
|
|
464
641
|
};
|
|
465
642
|
} catch (error) {
|
|
466
643
|
return {
|
|
467
|
-
content: [
|
|
644
|
+
content: [
|
|
645
|
+
{
|
|
646
|
+
type: "text",
|
|
647
|
+
text: `Memory update failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
648
|
+
},
|
|
649
|
+
],
|
|
468
650
|
details: { error: "update_failed", message: String(error) },
|
|
469
651
|
};
|
|
470
652
|
}
|
|
471
653
|
},
|
|
472
654
|
};
|
|
473
655
|
},
|
|
474
|
-
{ name: "memory_update" }
|
|
656
|
+
{ name: "memory_update" },
|
|
475
657
|
);
|
|
476
658
|
}
|
|
477
659
|
|
|
@@ -479,7 +661,10 @@ export function registerMemoryUpdateTool(api: OpenClawPluginApi, context: ToolCo
|
|
|
479
661
|
// Management Tools (Optional)
|
|
480
662
|
// ============================================================================
|
|
481
663
|
|
|
482
|
-
export function registerMemoryStatsTool(
|
|
664
|
+
export function registerMemoryStatsTool(
|
|
665
|
+
api: OpenClawPluginApi,
|
|
666
|
+
context: ToolContext,
|
|
667
|
+
) {
|
|
483
668
|
api.registerTool(
|
|
484
669
|
(toolCtx) => {
|
|
485
670
|
const agentId = resolveAgentId((toolCtx as any)?.agentId, context.agentId) ?? "main";
|
|
@@ -488,7 +673,11 @@ export function registerMemoryStatsTool(api: OpenClawPluginApi, context: ToolCon
|
|
|
488
673
|
label: "Memory Statistics",
|
|
489
674
|
description: "Get statistics about memory usage, scopes, and categories.",
|
|
490
675
|
parameters: Type.Object({
|
|
491
|
-
scope: Type.Optional(
|
|
676
|
+
scope: Type.Optional(
|
|
677
|
+
Type.String({
|
|
678
|
+
description: "Specific scope to get stats for (optional)",
|
|
679
|
+
}),
|
|
680
|
+
),
|
|
492
681
|
}),
|
|
493
682
|
async execute(_toolCallId, params) {
|
|
494
683
|
const { scope } = params as { scope?: string };
|
|
@@ -501,8 +690,13 @@ export function registerMemoryStatsTool(api: OpenClawPluginApi, context: ToolCon
|
|
|
501
690
|
scopeFilter = [scope];
|
|
502
691
|
} else {
|
|
503
692
|
return {
|
|
504
|
-
content: [
|
|
505
|
-
|
|
693
|
+
content: [
|
|
694
|
+
{ type: "text", text: `Access denied to scope: ${scope}` },
|
|
695
|
+
],
|
|
696
|
+
details: {
|
|
697
|
+
error: "scope_access_denied",
|
|
698
|
+
requestedScope: scope,
|
|
699
|
+
},
|
|
506
700
|
};
|
|
507
701
|
}
|
|
508
702
|
}
|
|
@@ -516,14 +710,18 @@ export function registerMemoryStatsTool(api: OpenClawPluginApi, context: ToolCon
|
|
|
516
710
|
`• Total memories: ${stats.totalCount}`,
|
|
517
711
|
`• Available scopes: ${scopeManagerStats.totalScopes}`,
|
|
518
712
|
`• Retrieval mode: ${retrievalConfig.mode}`,
|
|
519
|
-
`• FTS support: ${context.store.hasFtsSupport ?
|
|
713
|
+
`• FTS support: ${context.store.hasFtsSupport ? "Yes" : "No"}`,
|
|
520
714
|
``,
|
|
521
715
|
`Memories by scope:`,
|
|
522
|
-
...Object.entries(stats.scopeCounts).map(
|
|
716
|
+
...Object.entries(stats.scopeCounts).map(
|
|
717
|
+
([s, count]) => ` • ${s}: ${count}`,
|
|
718
|
+
),
|
|
523
719
|
``,
|
|
524
720
|
`Memories by category:`,
|
|
525
|
-
...Object.entries(stats.categoryCounts).map(
|
|
526
|
-
|
|
721
|
+
...Object.entries(stats.categoryCounts).map(
|
|
722
|
+
([c, count]) => ` • ${c}: ${count}`,
|
|
723
|
+
),
|
|
724
|
+
].join("\n");
|
|
527
725
|
|
|
528
726
|
return {
|
|
529
727
|
content: [{ type: "text", text }],
|
|
@@ -539,30 +737,49 @@ export function registerMemoryStatsTool(api: OpenClawPluginApi, context: ToolCon
|
|
|
539
737
|
};
|
|
540
738
|
} catch (error) {
|
|
541
739
|
return {
|
|
542
|
-
content: [
|
|
740
|
+
content: [
|
|
741
|
+
{
|
|
742
|
+
type: "text",
|
|
743
|
+
text: `Failed to get memory stats: ${error instanceof Error ? error.message : String(error)}`,
|
|
744
|
+
},
|
|
745
|
+
],
|
|
543
746
|
details: { error: "stats_failed", message: String(error) },
|
|
544
747
|
};
|
|
545
748
|
}
|
|
546
749
|
},
|
|
547
750
|
};
|
|
548
751
|
},
|
|
549
|
-
{ name: "memory_stats" }
|
|
752
|
+
{ name: "memory_stats" },
|
|
550
753
|
);
|
|
551
754
|
}
|
|
552
755
|
|
|
553
|
-
export function registerMemoryListTool(
|
|
756
|
+
export function registerMemoryListTool(
|
|
757
|
+
api: OpenClawPluginApi,
|
|
758
|
+
context: ToolContext,
|
|
759
|
+
) {
|
|
554
760
|
api.registerTool(
|
|
555
761
|
(toolCtx) => {
|
|
556
762
|
const agentId = resolveAgentId((toolCtx as any)?.agentId, context.agentId) ?? "main";
|
|
557
763
|
return {
|
|
558
764
|
name: "memory_list",
|
|
559
765
|
label: "Memory List",
|
|
560
|
-
description:
|
|
766
|
+
description:
|
|
767
|
+
"List recent memories with optional filtering by scope and category.",
|
|
561
768
|
parameters: Type.Object({
|
|
562
|
-
limit: Type.Optional(
|
|
563
|
-
|
|
769
|
+
limit: Type.Optional(
|
|
770
|
+
Type.Number({
|
|
771
|
+
description: "Max memories to list (default: 10, max: 50)",
|
|
772
|
+
}),
|
|
773
|
+
),
|
|
774
|
+
scope: Type.Optional(
|
|
775
|
+
Type.String({ description: "Filter by specific scope (optional)" }),
|
|
776
|
+
),
|
|
564
777
|
category: Type.Optional(stringEnum(MEMORY_CATEGORIES)),
|
|
565
|
-
offset: Type.Optional(
|
|
778
|
+
offset: Type.Optional(
|
|
779
|
+
Type.Number({
|
|
780
|
+
description: "Number of memories to skip (default: 0)",
|
|
781
|
+
}),
|
|
782
|
+
),
|
|
566
783
|
}),
|
|
567
784
|
async execute(_toolCallId, params) {
|
|
568
785
|
const {
|
|
@@ -588,33 +805,58 @@ export function registerMemoryListTool(api: OpenClawPluginApi, context: ToolCont
|
|
|
588
805
|
scopeFilter = [scope];
|
|
589
806
|
} else {
|
|
590
807
|
return {
|
|
591
|
-
content: [
|
|
592
|
-
|
|
808
|
+
content: [
|
|
809
|
+
{ type: "text", text: `Access denied to scope: ${scope}` },
|
|
810
|
+
],
|
|
811
|
+
details: {
|
|
812
|
+
error: "scope_access_denied",
|
|
813
|
+
requestedScope: scope,
|
|
814
|
+
},
|
|
593
815
|
};
|
|
594
816
|
}
|
|
595
817
|
}
|
|
596
818
|
|
|
597
|
-
const entries = await context.store.list(
|
|
819
|
+
const entries = await context.store.list(
|
|
820
|
+
scopeFilter,
|
|
821
|
+
category,
|
|
822
|
+
safeLimit,
|
|
823
|
+
safeOffset,
|
|
824
|
+
);
|
|
598
825
|
|
|
599
826
|
if (entries.length === 0) {
|
|
600
827
|
return {
|
|
601
828
|
content: [{ type: "text", text: "No memories found." }],
|
|
602
|
-
details: {
|
|
829
|
+
details: {
|
|
830
|
+
count: 0,
|
|
831
|
+
filters: {
|
|
832
|
+
scope,
|
|
833
|
+
category,
|
|
834
|
+
limit: safeLimit,
|
|
835
|
+
offset: safeOffset,
|
|
836
|
+
},
|
|
837
|
+
},
|
|
603
838
|
};
|
|
604
839
|
}
|
|
605
840
|
|
|
606
841
|
const text = entries
|
|
607
842
|
.map((entry, i) => {
|
|
608
|
-
const date = new Date(entry.timestamp)
|
|
609
|
-
|
|
843
|
+
const date = new Date(entry.timestamp)
|
|
844
|
+
.toISOString()
|
|
845
|
+
.split("T")[0];
|
|
846
|
+
return `${safeOffset + i + 1}. [${entry.id}] [${entry.category}:${entry.scope}] ${entry.text.slice(0, 100)}${entry.text.length > 100 ? "..." : ""} (${date})`;
|
|
610
847
|
})
|
|
611
|
-
.join(
|
|
848
|
+
.join("\n");
|
|
612
849
|
|
|
613
850
|
return {
|
|
614
|
-
content: [
|
|
851
|
+
content: [
|
|
852
|
+
{
|
|
853
|
+
type: "text",
|
|
854
|
+
text: `Recent memories (showing ${entries.length}):\n\n${text}`,
|
|
855
|
+
},
|
|
856
|
+
],
|
|
615
857
|
details: {
|
|
616
858
|
count: entries.length,
|
|
617
|
-
memories: entries.map(e => ({
|
|
859
|
+
memories: entries.map((e) => ({
|
|
618
860
|
id: e.id,
|
|
619
861
|
text: e.text,
|
|
620
862
|
category: e.category,
|
|
@@ -622,19 +864,29 @@ export function registerMemoryListTool(api: OpenClawPluginApi, context: ToolCont
|
|
|
622
864
|
importance: e.importance,
|
|
623
865
|
timestamp: e.timestamp,
|
|
624
866
|
})),
|
|
625
|
-
filters: {
|
|
867
|
+
filters: {
|
|
868
|
+
scope,
|
|
869
|
+
category,
|
|
870
|
+
limit: safeLimit,
|
|
871
|
+
offset: safeOffset,
|
|
872
|
+
},
|
|
626
873
|
},
|
|
627
874
|
};
|
|
628
875
|
} catch (error) {
|
|
629
876
|
return {
|
|
630
|
-
content: [
|
|
877
|
+
content: [
|
|
878
|
+
{
|
|
879
|
+
type: "text",
|
|
880
|
+
text: `Failed to list memories: ${error instanceof Error ? error.message : String(error)}`,
|
|
881
|
+
},
|
|
882
|
+
],
|
|
631
883
|
details: { error: "list_failed", message: String(error) },
|
|
632
884
|
};
|
|
633
885
|
}
|
|
634
886
|
},
|
|
635
887
|
};
|
|
636
888
|
},
|
|
637
|
-
{ name: "memory_list" }
|
|
889
|
+
{ name: "memory_list" },
|
|
638
890
|
);
|
|
639
891
|
}
|
|
640
892
|
|
|
@@ -647,7 +899,7 @@ export function registerAllMemoryTools(
|
|
|
647
899
|
context: ToolContext,
|
|
648
900
|
options: {
|
|
649
901
|
enableManagementTools?: boolean;
|
|
650
|
-
} = {}
|
|
902
|
+
} = {},
|
|
651
903
|
) {
|
|
652
904
|
// Core tools (always enabled)
|
|
653
905
|
registerMemoryRecallTool(api, context);
|
|
@@ -660,4 +912,4 @@ export function registerAllMemoryTools(
|
|
|
660
912
|
registerMemoryStatsTool(api, context);
|
|
661
913
|
registerMemoryListTool(api, context);
|
|
662
914
|
}
|
|
663
|
-
}
|
|
915
|
+
}
|