@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 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 across all sessions. Rules are injected at session start as non-negotiable instructions. Use this when the user wants you to never do something, always do something, or set a permanent behavioral boundary. Higher priority rules appear first.",
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 across all sessions. " +
76
- "Rules are injected at session start as non-negotiable instructions. " +
77
- "Use this when the user wants you to never do something, always do something, " +
78
- "or set a permanent behavioral boundary. Higher priority rules appear first.",
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
  };
@@ -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.3",
5
+ "version": "1.10.1-beta.6",
6
6
  "kind": [
7
7
  "memory",
8
8
  "context-engine"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xdarkicex/openclaw-memory-libravdb",
3
- "version": "1.10.1-beta.3",
3
+ "version": "1.10.1-beta.6",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",