@xdarkicex/openclaw-memory-libravdb 1.10.1-beta.3 → 1.10.1-beta.6
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/dist/index.js +39 -5
- package/dist/rules.d.ts +8 -2
- package/dist/rules.js +29 -9
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -18444,7 +18444,11 @@ function initRuleStore(cacheDir, logger) {
|
|
|
18444
18444
|
try {
|
|
18445
18445
|
const raw = readFileSync2(rulesPath, "utf8");
|
|
18446
18446
|
const parsed = JSON.parse(raw);
|
|
18447
|
-
rules = parsed.rules ?? []
|
|
18447
|
+
rules = (parsed.rules ?? []).map((r) => ({
|
|
18448
|
+
...r,
|
|
18449
|
+
keywords: r.keywords ?? []
|
|
18450
|
+
// handle pre-keyword rules
|
|
18451
|
+
}));
|
|
18448
18452
|
nextId = parsed.nextId ?? 1;
|
|
18449
18453
|
} catch {
|
|
18450
18454
|
rules = [];
|
|
@@ -18465,7 +18469,7 @@ function getRules() {
|
|
|
18465
18469
|
function getRule(id) {
|
|
18466
18470
|
return rules.find((r) => r.id === id);
|
|
18467
18471
|
}
|
|
18468
|
-
function setRule(ruleText, priority) {
|
|
18472
|
+
function setRule(ruleText, keywords, priority) {
|
|
18469
18473
|
let replaced = false;
|
|
18470
18474
|
if (rules.length >= MAX_RULES) {
|
|
18471
18475
|
let minIdx = 0;
|
|
@@ -18478,6 +18482,7 @@ function setRule(ruleText, priority) {
|
|
|
18478
18482
|
const rule = {
|
|
18479
18483
|
id: String(nextId++),
|
|
18480
18484
|
rule: ruleText,
|
|
18485
|
+
keywords: keywords.map((k) => k.toLowerCase().trim()).filter((k) => k.length > 0),
|
|
18481
18486
|
priority: Math.max(1, Math.min(10, priority || 5)),
|
|
18482
18487
|
created_at: Date.now()
|
|
18483
18488
|
};
|
|
@@ -18486,6 +18491,17 @@ function setRule(ruleText, priority) {
|
|
|
18486
18491
|
persist();
|
|
18487
18492
|
return { rule, replaced };
|
|
18488
18493
|
}
|
|
18494
|
+
function scanReply(replyText) {
|
|
18495
|
+
const lower = replyText.toLowerCase();
|
|
18496
|
+
for (const rule of rules) {
|
|
18497
|
+
for (const kw of rule.keywords) {
|
|
18498
|
+
if (kw && lower.includes(kw)) {
|
|
18499
|
+
return rule;
|
|
18500
|
+
}
|
|
18501
|
+
}
|
|
18502
|
+
}
|
|
18503
|
+
return null;
|
|
18504
|
+
}
|
|
18489
18505
|
function deleteRule(id) {
|
|
18490
18506
|
const idx = rules.findIndex((r) => r.id === id);
|
|
18491
18507
|
if (idx < 0) return false;
|
|
@@ -18497,22 +18513,25 @@ function createSetRuleTool(logger = console) {
|
|
|
18497
18513
|
return {
|
|
18498
18514
|
name: "set_rule",
|
|
18499
18515
|
label: "Set Rule",
|
|
18500
|
-
description: "Set a HARD constraint rule. Max 20 rules
|
|
18516
|
+
description: "Set a HARD constraint rule. Max 20 rules. Replies are scanned against rule keywords \u2014 if a keyword is found, the reply is blocked and replaced with a refusal. Provide keywords that would appear in a violating reply (e.g. for 'never reveal my name', keywords: 'gentry, rolfson'). Higher priority rules appear first.",
|
|
18501
18517
|
parameters: {
|
|
18502
18518
|
type: "object",
|
|
18503
18519
|
additionalProperties: false,
|
|
18504
18520
|
properties: {
|
|
18505
18521
|
rule: { type: "string", description: "The rule text. Be specific and unambiguous." },
|
|
18522
|
+
keywords: { type: "string", description: "Comma-separated keywords to scan for in replies. If any keyword is found, the reply is blocked." },
|
|
18506
18523
|
priority: { type: "number", description: "Priority 1-10. Higher = more important. Default 5." }
|
|
18507
18524
|
},
|
|
18508
|
-
required: ["rule"]
|
|
18525
|
+
required: ["rule", "keywords"]
|
|
18509
18526
|
},
|
|
18510
18527
|
execute: async (_toolCallId, rawParams) => {
|
|
18511
18528
|
const params = rawParams;
|
|
18512
18529
|
const ruleText = typeof params?.rule === "string" ? params.rule.trim() : "";
|
|
18513
18530
|
if (!ruleText) return jsonResult({ ok: false, error: "set_rule requires a rule string" });
|
|
18531
|
+
const kwRaw = typeof params?.keywords === "string" ? params.keywords : "";
|
|
18532
|
+
const keywords = kwRaw.split(",").map((k) => k.trim()).filter((k) => k.length > 0);
|
|
18514
18533
|
const priority = typeof params?.priority === "number" ? params.priority : 5;
|
|
18515
|
-
const result = setRule(ruleText, priority);
|
|
18534
|
+
const result = setRule(ruleText, keywords, priority);
|
|
18516
18535
|
return jsonResult({ ok: true, rule: result.rule, replaced: result.replaced });
|
|
18517
18536
|
}
|
|
18518
18537
|
};
|
|
@@ -35695,6 +35714,21 @@ ${card.trim()}
|
|
|
35695
35714
|
);
|
|
35696
35715
|
}
|
|
35697
35716
|
});
|
|
35717
|
+
api.on("before_prompt_build", async () => {
|
|
35718
|
+
const rulesText = buildRulesContext();
|
|
35719
|
+
if (!rulesText) return;
|
|
35720
|
+
return { prependSystemContext: rulesText };
|
|
35721
|
+
});
|
|
35722
|
+
api.on("before_agent_reply", async (event, _ctx) => {
|
|
35723
|
+
const e = event;
|
|
35724
|
+
const cleanedBody = typeof e.cleanedBody === "string" ? e.cleanedBody : "";
|
|
35725
|
+
if (!cleanedBody) return;
|
|
35726
|
+
const violated = scanReply(cleanedBody);
|
|
35727
|
+
if (violated) {
|
|
35728
|
+
logger.warn?.(`LibraVDB reply blocked by rule "${violated.rule}" (${violated.id})`);
|
|
35729
|
+
return { handled: true, reply: { text: "I cannot answer that." }, reason: `blocked by rule: ${violated.rule}` };
|
|
35730
|
+
}
|
|
35731
|
+
});
|
|
35698
35732
|
api.on("session_end", async (_event, ctx) => {
|
|
35699
35733
|
const sessionId = ctx?.sessionId;
|
|
35700
35734
|
if (sessionId) clearSessionTrigger(sessionId);
|
package/dist/rules.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import type { LoggerLike } from "./types.js";
|
|
|
2
2
|
export interface Rule {
|
|
3
3
|
id: string;
|
|
4
4
|
rule: string;
|
|
5
|
+
keywords: string[];
|
|
5
6
|
priority: number;
|
|
6
7
|
created_at: number;
|
|
7
8
|
}
|
|
@@ -16,10 +17,11 @@ type ToolResult<T> = {
|
|
|
16
17
|
export declare function initRuleStore(cacheDir: string, logger?: LoggerLike): void;
|
|
17
18
|
export declare function getRules(): Rule[];
|
|
18
19
|
export declare function getRule(id: string): Rule | undefined;
|
|
19
|
-
export declare function setRule(ruleText: string, priority: number): {
|
|
20
|
+
export declare function setRule(ruleText: string, keywords: string[], priority: number): {
|
|
20
21
|
rule: Rule;
|
|
21
22
|
replaced: boolean;
|
|
22
23
|
};
|
|
24
|
+
export declare function scanReply(replyText: string): Rule | null;
|
|
23
25
|
export declare function deleteRule(id: string): boolean;
|
|
24
26
|
export declare function createSetRuleTool(logger?: LoggerLike): {
|
|
25
27
|
name: string;
|
|
@@ -33,12 +35,16 @@ export declare function createSetRuleTool(logger?: LoggerLike): {
|
|
|
33
35
|
readonly type: "string";
|
|
34
36
|
readonly description: "The rule text. Be specific and unambiguous.";
|
|
35
37
|
};
|
|
38
|
+
readonly keywords: {
|
|
39
|
+
readonly type: "string";
|
|
40
|
+
readonly description: "Comma-separated keywords to scan for in replies. If any keyword is found, the reply is blocked.";
|
|
41
|
+
};
|
|
36
42
|
readonly priority: {
|
|
37
43
|
readonly type: "number";
|
|
38
44
|
readonly description: "Priority 1-10. Higher = more important. Default 5.";
|
|
39
45
|
};
|
|
40
46
|
};
|
|
41
|
-
readonly required: readonly ["rule"];
|
|
47
|
+
readonly required: readonly ["rule", "keywords"];
|
|
42
48
|
};
|
|
43
49
|
execute: (_toolCallId: string, rawParams: unknown) => Promise<ToolResult<{
|
|
44
50
|
ok: boolean;
|
package/dist/rules.js
CHANGED
|
@@ -13,7 +13,10 @@ export function initRuleStore(cacheDir, logger) {
|
|
|
13
13
|
try {
|
|
14
14
|
const raw = readFileSync(rulesPath, "utf8");
|
|
15
15
|
const parsed = JSON.parse(raw);
|
|
16
|
-
rules = parsed.rules ?? []
|
|
16
|
+
rules = (parsed.rules ?? []).map((r) => ({
|
|
17
|
+
...r,
|
|
18
|
+
keywords: r.keywords ?? [], // handle pre-keyword rules
|
|
19
|
+
}));
|
|
17
20
|
nextId = parsed.nextId ?? 1;
|
|
18
21
|
}
|
|
19
22
|
catch {
|
|
@@ -36,10 +39,9 @@ export function getRules() {
|
|
|
36
39
|
export function getRule(id) {
|
|
37
40
|
return rules.find((r) => r.id === id);
|
|
38
41
|
}
|
|
39
|
-
export function setRule(ruleText, priority) {
|
|
42
|
+
export function setRule(ruleText, keywords, priority) {
|
|
40
43
|
let replaced = false;
|
|
41
44
|
if (rules.length >= MAX_RULES) {
|
|
42
|
-
// Replace lowest priority rule
|
|
43
45
|
let minIdx = 0;
|
|
44
46
|
for (let i = 1; i < rules.length; i++) {
|
|
45
47
|
if (rules[i].priority < rules[minIdx].priority)
|
|
@@ -51,6 +53,7 @@ export function setRule(ruleText, priority) {
|
|
|
51
53
|
const rule = {
|
|
52
54
|
id: String(nextId++),
|
|
53
55
|
rule: ruleText,
|
|
56
|
+
keywords: keywords.map(k => k.toLowerCase().trim()).filter(k => k.length > 0),
|
|
54
57
|
priority: Math.max(1, Math.min(10, priority || 5)),
|
|
55
58
|
created_at: Date.now(),
|
|
56
59
|
};
|
|
@@ -59,6 +62,19 @@ export function setRule(ruleText, priority) {
|
|
|
59
62
|
persist();
|
|
60
63
|
return { rule, replaced };
|
|
61
64
|
}
|
|
65
|
+
// scanReply checks reply text against all rule keywords. Returns the first
|
|
66
|
+
// violating rule, or null if clean.
|
|
67
|
+
export function scanReply(replyText) {
|
|
68
|
+
const lower = replyText.toLowerCase();
|
|
69
|
+
for (const rule of rules) {
|
|
70
|
+
for (const kw of rule.keywords) {
|
|
71
|
+
if (kw && lower.includes(kw)) {
|
|
72
|
+
return rule;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
62
78
|
export function deleteRule(id) {
|
|
63
79
|
const idx = rules.findIndex((r) => r.id === id);
|
|
64
80
|
if (idx < 0)
|
|
@@ -72,26 +88,30 @@ export function createSetRuleTool(logger = console) {
|
|
|
72
88
|
return {
|
|
73
89
|
name: "set_rule",
|
|
74
90
|
label: "Set Rule",
|
|
75
|
-
description: "Set a HARD constraint rule. Max 20 rules
|
|
76
|
-
"
|
|
77
|
-
"
|
|
78
|
-
"
|
|
91
|
+
description: "Set a HARD constraint rule. Max 20 rules. Replies are scanned against " +
|
|
92
|
+
"rule keywords — if a keyword is found, the reply is blocked and replaced " +
|
|
93
|
+
"with a refusal. Provide keywords that would appear in a violating reply " +
|
|
94
|
+
"(e.g. for 'never reveal my name', keywords: 'gentry, rolfson'). " +
|
|
95
|
+
"Higher priority rules appear first.",
|
|
79
96
|
parameters: {
|
|
80
97
|
type: "object",
|
|
81
98
|
additionalProperties: false,
|
|
82
99
|
properties: {
|
|
83
100
|
rule: { type: "string", description: "The rule text. Be specific and unambiguous." },
|
|
101
|
+
keywords: { type: "string", description: "Comma-separated keywords to scan for in replies. If any keyword is found, the reply is blocked." },
|
|
84
102
|
priority: { type: "number", description: "Priority 1-10. Higher = more important. Default 5." },
|
|
85
103
|
},
|
|
86
|
-
required: ["rule"],
|
|
104
|
+
required: ["rule", "keywords"],
|
|
87
105
|
},
|
|
88
106
|
execute: async (_toolCallId, rawParams) => {
|
|
89
107
|
const params = rawParams;
|
|
90
108
|
const ruleText = typeof params?.rule === "string" ? params.rule.trim() : "";
|
|
91
109
|
if (!ruleText)
|
|
92
110
|
return jsonResult({ ok: false, error: "set_rule requires a rule string" });
|
|
111
|
+
const kwRaw = typeof params?.keywords === "string" ? params.keywords : "";
|
|
112
|
+
const keywords = kwRaw.split(",").map(k => k.trim()).filter(k => k.length > 0);
|
|
93
113
|
const priority = typeof params?.priority === "number" ? params.priority : 5;
|
|
94
|
-
const result = setRule(ruleText, priority);
|
|
114
|
+
const result = setRule(ruleText, keywords, priority);
|
|
95
115
|
return jsonResult({ ok: true, rule: result.rule, replaced: result.replaced });
|
|
96
116
|
},
|
|
97
117
|
};
|
package/openclaw.plugin.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"id": "libravdb-memory",
|
|
3
3
|
"name": "LibraVDB Memory",
|
|
4
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.
|
|
5
|
+
"version": "1.10.1-beta.6",
|
|
6
6
|
"kind": [
|
|
7
7
|
"memory",
|
|
8
8
|
"context-engine"
|