@xdarkicex/openclaw-memory-libravdb 1.9.10-beta.9 → 1.10.1-beta.1
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/README.md +42 -24
- package/dist/context-engine.d.ts +5 -0
- package/dist/context-engine.js +173 -13
- package/dist/index.js +22875 -36057
- package/dist/libravdb-client.d.ts +4 -1
- package/dist/libravdb-client.js +13 -0
- package/dist/memory-provider.js +19 -0
- package/dist/memory-tools.js +1 -1
- package/dist/rules.d.ts +105 -0
- package/dist/rules.js +169 -0
- package/dist/tools/memory-recall.d.ts +75 -3
- package/dist/tools/memory-recall.js +187 -9
- package/openclaw.plugin.json +10 -3
- package/package.json +3 -8
|
@@ -31,11 +31,15 @@ const MEMORY_EXPAND_SCHEMA = {
|
|
|
31
31
|
items: { type: "string" },
|
|
32
32
|
description: "Summary IDs (sum_xxx format) to expand. Use results from memory_search or memory_describe.",
|
|
33
33
|
},
|
|
34
|
+
record_id: {
|
|
35
|
+
type: "string",
|
|
36
|
+
description: "Record ID for causal graph traversal. Use exact IDs from memory_search or memory_get results.",
|
|
37
|
+
},
|
|
34
38
|
maxDepth: {
|
|
35
39
|
type: "number",
|
|
36
40
|
minimum: 0,
|
|
37
41
|
maximum: 5,
|
|
38
|
-
description: "Max tree traversal depth
|
|
42
|
+
description: "Max tree/graph traversal depth (default: 1). 0 returns only edge metadata.",
|
|
39
43
|
},
|
|
40
44
|
maxTokens: {
|
|
41
45
|
type: "number",
|
|
@@ -45,10 +49,9 @@ const MEMORY_EXPAND_SCHEMA = {
|
|
|
45
49
|
},
|
|
46
50
|
sessionId: {
|
|
47
51
|
type: "string",
|
|
48
|
-
description: "Session ID
|
|
52
|
+
description: "Session ID. If omitted, uses the current session.",
|
|
49
53
|
},
|
|
50
54
|
},
|
|
51
|
-
required: ["summaryIds"],
|
|
52
55
|
};
|
|
53
56
|
const MEMORY_GREP_SCHEMA = {
|
|
54
57
|
type: "object",
|
|
@@ -191,20 +194,49 @@ export function createMemoryExpandTool(getClient, getSessionKey, logger = consol
|
|
|
191
194
|
return {
|
|
192
195
|
name: "memory_expand",
|
|
193
196
|
label: "Memory Expand",
|
|
194
|
-
description: "Expand compacted summaries
|
|
195
|
-
"
|
|
196
|
-
"
|
|
197
|
-
"
|
|
197
|
+
description: "Expand compacted summaries OR walk causal graph edges from ANY record. " +
|
|
198
|
+
"Summary mode (summaryIds): walk the summary tree up to maxDepth levels. " +
|
|
199
|
+
"Graph mode (record_id): walk causal edges (why_ids/how_ids/hop_targets) " +
|
|
200
|
+
"from a record ID. Use exact IDs from memory_search or memory_get results — " +
|
|
201
|
+
"any ingested turn, memory, or summary has graph edges. " +
|
|
202
|
+
"After get_user_card for context, search for related people/events then " +
|
|
203
|
+
"expand the most relevant hit. " +
|
|
204
|
+
"For large expansions, spawns a sub-agent. " +
|
|
205
|
+
"Use memory_describe first to check if expansion is warranted.",
|
|
198
206
|
parameters: MEMORY_EXPAND_SCHEMA,
|
|
199
207
|
execute: async (_toolCallId, rawParams) => {
|
|
200
208
|
const params = asParams(rawParams);
|
|
209
|
+
const recordId = readStr(params, "record_id");
|
|
201
210
|
const rawIds = params.summaryIds;
|
|
202
211
|
const summaryIds = Array.isArray(rawIds) ? rawIds.filter((v) => typeof v === "string" && v.trim().length > 0) : [];
|
|
203
|
-
if (summaryIds.length === 0)
|
|
204
|
-
throw new Error("memory_expand requires at least one summaryId");
|
|
205
212
|
const maxDepth = readNum(params, "maxDepth", { integer: true, min: 0 }) ?? 1;
|
|
206
213
|
let maxTokens = readNum(params, "maxTokens", { integer: true }) ?? MAX_EXPAND_TOKENS;
|
|
207
214
|
const sessionId = readStr(params, "sessionId") ?? getSessionId() ?? "";
|
|
215
|
+
// Graph mode: walk causal edges from a record ID.
|
|
216
|
+
if (recordId) {
|
|
217
|
+
try {
|
|
218
|
+
const client = await getClient();
|
|
219
|
+
const resp = await client.expandSummary({ recordId, maxDepth });
|
|
220
|
+
let text = resp.text ?? "";
|
|
221
|
+
const connected = resp.connected;
|
|
222
|
+
if (connected && connected.length > 0) {
|
|
223
|
+
text = connected.map((c) => `[depth=${c.depth}] ${c.recordId}: ${c.text || ""}`).join("\n\n");
|
|
224
|
+
}
|
|
225
|
+
if (!text && resp.whyIds?.length) {
|
|
226
|
+
text = `why_ids: ${resp.whyIds.join(", ")}\nhow_ids: ${resp.howIds?.join(", ") ?? "none"}\nhop_targets: ${resp.hopTargets?.join(", ") ?? "none"}`;
|
|
227
|
+
}
|
|
228
|
+
return {
|
|
229
|
+
content: [{ type: "text", text: text || "(no graph edges found)" }],
|
|
230
|
+
details: { summaryId: recordId, depth: maxDepth, text: text || "", truncated: false, exceededBudget: false, parentCount: connected?.length ?? 0 },
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
catch (error) {
|
|
234
|
+
logger.warn?.(`memory_expand graph mode failed: ${formatError(error)}`);
|
|
235
|
+
return { content: [{ type: "text", text: `Graph expansion failed: ${formatError(error)}` }], details: { summaryId: recordId, depth: maxDepth, text: "", truncated: false, exceededBudget: false, parentCount: 0 } };
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
if (summaryIds.length === 0)
|
|
239
|
+
throw new Error("memory_expand requires at least one summaryId or record_id");
|
|
208
240
|
// Subagent budget gate: if this is a subagent, check remaining expansion budget.
|
|
209
241
|
const sessionKey = getSessionKey();
|
|
210
242
|
if (sessionKey) {
|
|
@@ -422,3 +454,149 @@ const RECALL_GUIDANCE = [
|
|
|
422
454
|
export function memoryRecallPromptSection() {
|
|
423
455
|
return [...RECALL_GUIDANCE];
|
|
424
456
|
}
|
|
457
|
+
// ── User Card tool schemas ──
|
|
458
|
+
const UPDATE_USER_CARD_SCHEMA = {
|
|
459
|
+
type: "object",
|
|
460
|
+
additionalProperties: false,
|
|
461
|
+
properties: {
|
|
462
|
+
user_id: {
|
|
463
|
+
type: "string",
|
|
464
|
+
description: "The user ID to update the card for.",
|
|
465
|
+
},
|
|
466
|
+
card: {
|
|
467
|
+
type: "string",
|
|
468
|
+
description: "Prose description of what you've learned about this person. Write like describing a friend. Include identity, values, history, communication style, triggers, and what matters to them. Merge with previous understanding — don't replace entirely unless the user explicitly contradicts the record. Max ~1200 characters.",
|
|
469
|
+
},
|
|
470
|
+
},
|
|
471
|
+
required: ["user_id", "card"],
|
|
472
|
+
};
|
|
473
|
+
const GET_USER_CARD_SCHEMA = {
|
|
474
|
+
type: "object",
|
|
475
|
+
additionalProperties: false,
|
|
476
|
+
properties: {
|
|
477
|
+
user_id: {
|
|
478
|
+
type: "string",
|
|
479
|
+
description: "The user ID to retrieve the card for.",
|
|
480
|
+
},
|
|
481
|
+
},
|
|
482
|
+
required: ["user_id"],
|
|
483
|
+
};
|
|
484
|
+
// ── User Card tool factories ──
|
|
485
|
+
export function createUpdateUserCardTool(getClient, logger = console) {
|
|
486
|
+
return {
|
|
487
|
+
name: "update_user_card",
|
|
488
|
+
label: "Update User Card",
|
|
489
|
+
description: "Write what you've learned about a speaker. Prose format — write like you're describing a friend. " +
|
|
490
|
+
"Include identity, values, history, communication style, triggers, and what matters to them. " +
|
|
491
|
+
"This is the canonical record of who they are. If something changes, update it. " +
|
|
492
|
+
"Merge with previous understanding, don't replace entirely unless the user explicitly contradicts the record.",
|
|
493
|
+
parameters: UPDATE_USER_CARD_SCHEMA,
|
|
494
|
+
execute: async (_toolCallId, rawParams) => {
|
|
495
|
+
try {
|
|
496
|
+
const params = asParams(rawParams);
|
|
497
|
+
const userId = readStr(params, "user_id");
|
|
498
|
+
const card = readStr(params, "card");
|
|
499
|
+
if (!userId)
|
|
500
|
+
return jsonResult({ ok: false, error: "update_user_card requires user_id" });
|
|
501
|
+
if (!card)
|
|
502
|
+
return jsonResult({ ok: false, error: "update_user_card requires card" });
|
|
503
|
+
const client = await getClient();
|
|
504
|
+
const resp = await client.upsertUserCard({
|
|
505
|
+
userId,
|
|
506
|
+
cardJson: JSON.stringify({ card, updatedAt: Date.now() }),
|
|
507
|
+
});
|
|
508
|
+
return jsonResult({ ok: resp.ok });
|
|
509
|
+
}
|
|
510
|
+
catch (error) {
|
|
511
|
+
logger.warn?.(`update_user_card failed: ${formatError(error)}`);
|
|
512
|
+
return jsonResult({ error: formatError(error), ok: false });
|
|
513
|
+
}
|
|
514
|
+
},
|
|
515
|
+
};
|
|
516
|
+
}
|
|
517
|
+
export function createGetUserCardTool(getClient, logger = console) {
|
|
518
|
+
return {
|
|
519
|
+
name: "get_user_card",
|
|
520
|
+
label: "Get User Card",
|
|
521
|
+
description: "PRIMARY identity lookup. Call this FIRST whenever someone asks about a person " +
|
|
522
|
+
"('who is X', 'tell me about X', 'what do you know about X'). " +
|
|
523
|
+
"Returns the full prose identity card with metadata. " +
|
|
524
|
+
"Only use memory_search AFTER get_user_card if the card lacks detail.",
|
|
525
|
+
parameters: GET_USER_CARD_SCHEMA,
|
|
526
|
+
execute: async (_toolCallId, rawParams) => {
|
|
527
|
+
try {
|
|
528
|
+
const params = asParams(rawParams);
|
|
529
|
+
const userId = readStr(params, "user_id");
|
|
530
|
+
if (!userId)
|
|
531
|
+
return jsonResult({ card: null, error: "get_user_card requires user_id" });
|
|
532
|
+
const client = await getClient();
|
|
533
|
+
const resp = await client.getUserCard({ userId });
|
|
534
|
+
return jsonResult({
|
|
535
|
+
card: resp.cardJson || null,
|
|
536
|
+
updatedAt: resp.updatedAt ? Number(resp.updatedAt) : undefined,
|
|
537
|
+
version: resp.version || undefined,
|
|
538
|
+
});
|
|
539
|
+
}
|
|
540
|
+
catch (error) {
|
|
541
|
+
logger.warn?.(`get_user_card failed: ${formatError(error)}`);
|
|
542
|
+
return jsonResult({ error: formatError(error) });
|
|
543
|
+
}
|
|
544
|
+
},
|
|
545
|
+
};
|
|
546
|
+
}
|
|
547
|
+
export function createListUserCardsTool(getClient, logger = console) {
|
|
548
|
+
return {
|
|
549
|
+
name: "list_user_cards",
|
|
550
|
+
label: "List User Cards",
|
|
551
|
+
description: "List every person you have a user card for. Returns user IDs and card previews. " +
|
|
552
|
+
"Use this FIRST when asked about multiple people, 'who do you know?', 'who is everyone?', " +
|
|
553
|
+
"or when you're unsure which people have cards. Then use get_user_card on specific IDs " +
|
|
554
|
+
"for full detail. Use memory_search only to supplement gaps.",
|
|
555
|
+
parameters: { type: "object", additionalProperties: false, properties: {} },
|
|
556
|
+
execute: async (_toolCallId, _rawParams) => {
|
|
557
|
+
try {
|
|
558
|
+
const client = await getClient();
|
|
559
|
+
const resp = await client.listByMeta({
|
|
560
|
+
collection: "",
|
|
561
|
+
key: "type",
|
|
562
|
+
value: "user_card",
|
|
563
|
+
});
|
|
564
|
+
const users = [];
|
|
565
|
+
for (const result of resp.results) {
|
|
566
|
+
let userId = "";
|
|
567
|
+
let preview = "";
|
|
568
|
+
let updatedAt;
|
|
569
|
+
let version;
|
|
570
|
+
if (result.metadataJson && result.metadataJson.length > 0) {
|
|
571
|
+
try {
|
|
572
|
+
const decoder = new TextDecoder();
|
|
573
|
+
const meta = JSON.parse(decoder.decode(result.metadataJson));
|
|
574
|
+
userId = typeof meta._user_id === "string" ? meta._user_id : "";
|
|
575
|
+
const cardJson = typeof meta.card_json === "string" ? meta.card_json : null;
|
|
576
|
+
if (cardJson) {
|
|
577
|
+
try {
|
|
578
|
+
preview = JSON.parse(cardJson).card ?? cardJson;
|
|
579
|
+
}
|
|
580
|
+
catch {
|
|
581
|
+
preview = cardJson;
|
|
582
|
+
}
|
|
583
|
+
preview = preview.slice(0, 200);
|
|
584
|
+
}
|
|
585
|
+
updatedAt = typeof meta.updated_at === "number" ? meta.updated_at : undefined;
|
|
586
|
+
version = typeof meta.version === "number" ? meta.version : undefined;
|
|
587
|
+
}
|
|
588
|
+
catch { /* best-effort metadata parse */ }
|
|
589
|
+
}
|
|
590
|
+
if (!userId)
|
|
591
|
+
continue;
|
|
592
|
+
users.push({ user_id: userId, preview, updated_at: updatedAt, version });
|
|
593
|
+
}
|
|
594
|
+
return jsonResult({ users, total: users.length });
|
|
595
|
+
}
|
|
596
|
+
catch (error) {
|
|
597
|
+
logger.warn?.(`list_user_cards failed: ${formatError(error)}`);
|
|
598
|
+
return jsonResult({ users: [], total: 0, error: formatError(error) });
|
|
599
|
+
}
|
|
600
|
+
},
|
|
601
|
+
};
|
|
602
|
+
}
|
package/openclaw.plugin.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "libravdb-memory",
|
|
3
3
|
"name": "LibraVDB Memory",
|
|
4
|
-
"description": "
|
|
5
|
-
"version": "1.
|
|
4
|
+
"description": "Cognitive memory engine — causal graph reasoning, predictive context, identity tracking, and hybrid vector recall with back-door adjustment scoring",
|
|
5
|
+
"version": "1.10.1-beta.1",
|
|
6
6
|
"kind": [
|
|
7
7
|
"memory",
|
|
8
8
|
"context-engine"
|
|
@@ -15,7 +15,14 @@
|
|
|
15
15
|
"memory_get",
|
|
16
16
|
"memory_describe",
|
|
17
17
|
"memory_expand",
|
|
18
|
-
"memory_grep"
|
|
18
|
+
"memory_grep",
|
|
19
|
+
"update_user_card",
|
|
20
|
+
"get_user_card",
|
|
21
|
+
"list_user_cards",
|
|
22
|
+
"set_rule",
|
|
23
|
+
"get_rule",
|
|
24
|
+
"list_rules",
|
|
25
|
+
"delete_rule"
|
|
19
26
|
]
|
|
20
27
|
},
|
|
21
28
|
"activation": {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xdarkicex/openclaw-memory-libravdb",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.10.1-beta.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -65,7 +65,7 @@
|
|
|
65
65
|
"build:daemon": "bash scripts/build-daemon.sh"
|
|
66
66
|
},
|
|
67
67
|
"devDependencies": {
|
|
68
|
-
"@bufbuild/protobuf": "1.
|
|
68
|
+
"@bufbuild/protobuf": "1.10.1",
|
|
69
69
|
"@grpc/grpc-js": "^1.14.3",
|
|
70
70
|
"@grpc/proto-loader": "^0.8.0",
|
|
71
71
|
"@openclaw/plugin-inspector": "0.3.7",
|
|
@@ -80,11 +80,6 @@
|
|
|
80
80
|
"dependencies": {
|
|
81
81
|
"@connectrpc/connect": "^1.7.0",
|
|
82
82
|
"@connectrpc/connect-node": "^1.7.0",
|
|
83
|
-
"@xdarkicex/libravdb-contracts": "^2.0.
|
|
84
|
-
},
|
|
85
|
-
"pnpm": {
|
|
86
|
-
"overrides": {
|
|
87
|
-
"undici": "8.3.0"
|
|
88
|
-
}
|
|
83
|
+
"@xdarkicex/libravdb-contracts": "^2.0.27"
|
|
89
84
|
}
|
|
90
85
|
}
|