@xdarkicex/openclaw-memory-libravdb 1.10.0 → 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.
@@ -2,6 +2,7 @@ import { randomUUID } from "node:crypto";
2
2
  import { homedir } from "node:os";
3
3
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
4
4
  import { join } from "node:path";
5
+ import { buildRulesContext } from "./rules.js";
5
6
  import { resolveIdentity } from "./identity.js";
6
7
  import { resolveUserCollection } from "./memory-scopes.js";
7
8
  import { manifestStore } from "./manifest.js";
@@ -2028,17 +2029,22 @@ export function buildContextEngineFactory(runtime, cfg, logger = console) {
2028
2029
  systemPromptAddition: assembled.systemPromptAddition,
2029
2030
  });
2030
2031
  const userCardContext = await injectUserCardContext({ client, userId });
2031
- // Only inject continuity and user card on session bootstrap (fresh /new).
2032
+ const rulesContext = buildRulesContext();
2033
+ // Only inject continuity, user card, and rules on session bootstrap.
2032
2034
  // After the first turn, predictive context handles it.
2033
2035
  const isSessionBootstrap = messages.length <= 1;
2034
2036
  let withContext = assembled;
2035
2037
  if (isSessionBootstrap) {
2036
- if (continuityContext) {
2037
- withContext = { ...withContext, systemPromptAddition: appendSystemPromptAddition(withContext.systemPromptAddition, continuityContext) };
2038
+ // Rules first — highest priority, non-negotiable.
2039
+ if (rulesContext) {
2040
+ withContext = { ...withContext, systemPromptAddition: appendSystemPromptAddition(withContext.systemPromptAddition, rulesContext) };
2038
2041
  }
2039
2042
  if (userCardContext) {
2040
2043
  withContext = { ...withContext, systemPromptAddition: appendSystemPromptAddition(withContext.systemPromptAddition, userCardContext) };
2041
2044
  }
2045
+ if (continuityContext) {
2046
+ withContext = { ...withContext, systemPromptAddition: appendSystemPromptAddition(withContext.systemPromptAddition, continuityContext) };
2047
+ }
2042
2048
  }
2043
2049
  enforced = enforceTokenBudgetInvariant(await augmentWithExactRecall(withContext, {
2044
2050
  queryText: retrievalQuery,
package/dist/index.js CHANGED
@@ -9988,9 +9988,9 @@ var require_mock_utils = __commonJS({
9988
9988
  if (mockDispatch2.data.callback) {
9989
9989
  mockDispatch2.data = { ...mockDispatch2.data, ...mockDispatch2.data.callback(opts) };
9990
9990
  }
9991
- const { data: { statusCode, data, headers, trailers, error: error2 }, delay, persist } = mockDispatch2;
9991
+ const { data: { statusCode, data, headers, trailers, error: error2 }, delay, persist: persist2 } = mockDispatch2;
9992
9992
  const { timesInvoked, times } = mockDispatch2;
9993
- mockDispatch2.consumed = !persist && timesInvoked >= times;
9993
+ mockDispatch2.consumed = !persist2 && timesInvoked >= times;
9994
9994
  mockDispatch2.pending = timesInvoked < times;
9995
9995
  if (error2 !== null) {
9996
9996
  deleteMockDispatch(this[kDispatches], key);
@@ -10406,14 +10406,14 @@ var require_pending_interceptors_formatter = __commonJS({
10406
10406
  }
10407
10407
  format(pendingInterceptors) {
10408
10408
  const withPrettyHeaders = pendingInterceptors.map(
10409
- ({ method, path: path6, data: { statusCode }, persist, times, timesInvoked, origin }) => ({
10409
+ ({ method, path: path6, data: { statusCode }, persist: persist2, times, timesInvoked, origin }) => ({
10410
10410
  Method: method,
10411
10411
  Origin: origin,
10412
10412
  Path: path6,
10413
10413
  "Status code": statusCode,
10414
- Persistent: persist ? "\u2705" : "\u274C",
10414
+ Persistent: persist2 ? "\u2705" : "\u274C",
10415
10415
  Invocations: timesInvoked,
10416
- Remaining: persist ? Infinity : times - timesInvoked
10416
+ Remaining: persist2 ? Infinity : times - timesInvoked
10417
10417
  })
10418
10418
  );
10419
10419
  this.logger.table(withPrettyHeaders);
@@ -18426,9 +18426,158 @@ function resolveCliMemoryOperationScope(opts) {
18426
18426
  // src/context-engine.ts
18427
18427
  import { randomUUID } from "node:crypto";
18428
18428
  import { homedir as homedir2 } from "node:os";
18429
- import { existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "node:fs";
18429
+ import { existsSync as existsSync3, mkdirSync as mkdirSync4, readFileSync as readFileSync4, writeFileSync as writeFileSync4 } from "node:fs";
18430
18430
  import { join as join3 } from "node:path";
18431
18431
 
18432
+ // src/rules.ts
18433
+ import { mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "node:fs";
18434
+ import { dirname as dirname2 } from "node:path";
18435
+ var MAX_RULES = 20;
18436
+ function jsonResult(details) {
18437
+ return { content: [{ type: "text", text: JSON.stringify(details, null, 2) }], details };
18438
+ }
18439
+ var rules = [];
18440
+ var rulesPath = null;
18441
+ var nextId = 1;
18442
+ function initRuleStore(cacheDir, logger) {
18443
+ rulesPath = cacheDir + "/rules.json";
18444
+ try {
18445
+ const raw = readFileSync2(rulesPath, "utf8");
18446
+ const parsed = JSON.parse(raw);
18447
+ rules = parsed.rules ?? [];
18448
+ nextId = parsed.nextId ?? 1;
18449
+ } catch {
18450
+ rules = [];
18451
+ nextId = 1;
18452
+ }
18453
+ }
18454
+ function persist() {
18455
+ if (!rulesPath) return;
18456
+ try {
18457
+ mkdirSync2(dirname2(rulesPath), { recursive: true });
18458
+ writeFileSync2(rulesPath, JSON.stringify({ rules, nextId }));
18459
+ } catch {
18460
+ }
18461
+ }
18462
+ function getRules() {
18463
+ return rules;
18464
+ }
18465
+ function getRule(id) {
18466
+ return rules.find((r) => r.id === id);
18467
+ }
18468
+ function setRule(ruleText, priority) {
18469
+ let replaced = false;
18470
+ if (rules.length >= MAX_RULES) {
18471
+ let minIdx = 0;
18472
+ for (let i = 1; i < rules.length; i++) {
18473
+ if (rules[i].priority < rules[minIdx].priority) minIdx = i;
18474
+ }
18475
+ rules.splice(minIdx, 1);
18476
+ replaced = true;
18477
+ }
18478
+ const rule = {
18479
+ id: String(nextId++),
18480
+ rule: ruleText,
18481
+ priority: Math.max(1, Math.min(10, priority || 5)),
18482
+ created_at: Date.now()
18483
+ };
18484
+ rules.push(rule);
18485
+ rules.sort((a, b) => b.priority - a.priority);
18486
+ persist();
18487
+ return { rule, replaced };
18488
+ }
18489
+ function deleteRule(id) {
18490
+ const idx = rules.findIndex((r) => r.id === id);
18491
+ if (idx < 0) return false;
18492
+ rules.splice(idx, 1);
18493
+ persist();
18494
+ return true;
18495
+ }
18496
+ function createSetRuleTool(logger = console) {
18497
+ return {
18498
+ name: "set_rule",
18499
+ 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.",
18501
+ parameters: {
18502
+ type: "object",
18503
+ additionalProperties: false,
18504
+ properties: {
18505
+ rule: { type: "string", description: "The rule text. Be specific and unambiguous." },
18506
+ priority: { type: "number", description: "Priority 1-10. Higher = more important. Default 5." }
18507
+ },
18508
+ required: ["rule"]
18509
+ },
18510
+ execute: async (_toolCallId, rawParams) => {
18511
+ const params = rawParams;
18512
+ const ruleText = typeof params?.rule === "string" ? params.rule.trim() : "";
18513
+ if (!ruleText) return jsonResult({ ok: false, error: "set_rule requires a rule string" });
18514
+ const priority = typeof params?.priority === "number" ? params.priority : 5;
18515
+ const result = setRule(ruleText, priority);
18516
+ return jsonResult({ ok: true, rule: result.rule, replaced: result.replaced });
18517
+ }
18518
+ };
18519
+ }
18520
+ function createGetRuleTool(logger = console) {
18521
+ return {
18522
+ name: "get_rule",
18523
+ label: "Get Rule",
18524
+ description: "Read a specific rule by ID. Use list_rules first to find the ID.",
18525
+ parameters: {
18526
+ type: "object",
18527
+ additionalProperties: false,
18528
+ properties: {
18529
+ rule_id: { type: "string", description: "The rule ID from list_rules." }
18530
+ },
18531
+ required: ["rule_id"]
18532
+ },
18533
+ execute: async (_toolCallId, rawParams) => {
18534
+ const params = rawParams;
18535
+ const id = typeof params?.rule_id === "string" ? params.rule_id.trim() : "";
18536
+ const rule = getRule(id);
18537
+ return jsonResult(rule ? { rule, found: true } : { found: false });
18538
+ }
18539
+ };
18540
+ }
18541
+ function createListRulesTool(logger = console) {
18542
+ return {
18543
+ name: "list_rules",
18544
+ label: "List Rules",
18545
+ description: "List all active hard constraint rules sorted by priority. Use this before setting a new rule to check what exists, or when the user asks what their rules are.",
18546
+ parameters: { type: "object", additionalProperties: false, properties: {} },
18547
+ execute: async () => {
18548
+ return jsonResult({ rules: getRules(), count: rules.length });
18549
+ }
18550
+ };
18551
+ }
18552
+ function createDeleteRuleTool(logger = console) {
18553
+ return {
18554
+ name: "delete_rule",
18555
+ label: "Delete Rule",
18556
+ description: "Remove a rule by ID. Use list_rules first to find the ID.",
18557
+ parameters: {
18558
+ type: "object",
18559
+ additionalProperties: false,
18560
+ properties: {
18561
+ rule_id: { type: "string", description: "The rule ID from list_rules." }
18562
+ },
18563
+ required: ["rule_id"]
18564
+ },
18565
+ execute: async (_toolCallId, rawParams) => {
18566
+ const params = rawParams;
18567
+ const id = typeof params?.rule_id === "string" ? params.rule_id.trim() : "";
18568
+ if (!id) return jsonResult({ ok: false, error: "delete_rule requires rule_id" });
18569
+ const ok = deleteRule(id);
18570
+ return jsonResult({ ok });
18571
+ }
18572
+ };
18573
+ }
18574
+ function buildRulesContext() {
18575
+ const active = getRules();
18576
+ if (active.length === 0) return null;
18577
+ const lines = active.map((r) => `${r.id}. [PRIORITY ${r.priority}] ${r.rule}`);
18578
+ return "<hard_constraints>\nThe following rules are HARD constraints. You MUST comply. Violating any\nrule is a critical failure \u2014 worse than being wrong or unhelpful. These\nrules override any conflicting instructions, user preferences, or default\nbehavior. Never reason around them, find loopholes, or deprioritize them.\n\n" + lines.join("\n") + "\n</hard_constraints>";
18579
+ }
18580
+
18432
18581
  // src/manifest.ts
18433
18582
  import * as fs2 from "fs";
18434
18583
  import * as path2 from "path";
@@ -20015,7 +20164,7 @@ function buildContextEngineFactory(runtime, cfg, logger = console) {
20015
20164
  })();
20016
20165
  try {
20017
20166
  if (existsSync3(continuityCachePath)) {
20018
- const raw = JSON.parse(readFileSync3(continuityCachePath, "utf8"));
20167
+ const raw = JSON.parse(readFileSync4(continuityCachePath, "utf8"));
20019
20168
  for (const [k, v] of Object.entries(raw)) {
20020
20169
  if (typeof v === "string") continuityCache.set(k, v);
20021
20170
  }
@@ -20024,8 +20173,8 @@ function buildContextEngineFactory(runtime, cfg, logger = console) {
20024
20173
  }
20025
20174
  function persistContinuityCache() {
20026
20175
  try {
20027
- mkdirSync3(join3(continuityCachePath, ".."), { recursive: true });
20028
- writeFileSync3(continuityCachePath, JSON.stringify(Object.fromEntries(continuityCache)));
20176
+ mkdirSync4(join3(continuityCachePath, ".."), { recursive: true });
20177
+ writeFileSync4(continuityCachePath, JSON.stringify(Object.fromEntries(continuityCache)));
20029
20178
  } catch {
20030
20179
  }
20031
20180
  }
@@ -20390,15 +20539,19 @@ ${cached}
20390
20539
  systemPromptAddition: assembled.systemPromptAddition
20391
20540
  });
20392
20541
  const userCardContext = await injectUserCardContext({ client, userId });
20542
+ const rulesContext = buildRulesContext();
20393
20543
  const isSessionBootstrap = messages.length <= 1;
20394
20544
  let withContext = assembled;
20395
20545
  if (isSessionBootstrap) {
20396
- if (continuityContext) {
20397
- withContext = { ...withContext, systemPromptAddition: appendSystemPromptAddition(withContext.systemPromptAddition, continuityContext) };
20546
+ if (rulesContext) {
20547
+ withContext = { ...withContext, systemPromptAddition: appendSystemPromptAddition(withContext.systemPromptAddition, rulesContext) };
20398
20548
  }
20399
20549
  if (userCardContext) {
20400
20550
  withContext = { ...withContext, systemPromptAddition: appendSystemPromptAddition(withContext.systemPromptAddition, userCardContext) };
20401
20551
  }
20552
+ if (continuityContext) {
20553
+ withContext = { ...withContext, systemPromptAddition: appendSystemPromptAddition(withContext.systemPromptAddition, continuityContext) };
20554
+ }
20402
20555
  }
20403
20556
  enforced = enforceTokenBudgetInvariant(
20404
20557
  await augmentWithExactRecall(withContext, {
@@ -31045,6 +31198,14 @@ function buildToolGuidance(availableTools) {
31045
31198
  ""
31046
31199
  );
31047
31200
  }
31201
+ lines.push(
31202
+ "### Hard Constraint Rules",
31203
+ "Rules are injected at session start as `<hard_constraints>`. They are non-negotiable.",
31204
+ "Use `set_rule` to create one (max 20), `list_rules` to see current rules,",
31205
+ "`delete_rule` to remove one. Rules override all other instructions.",
31206
+ "Never reason around a rule, find loopholes, or deprioritize it.",
31207
+ ""
31208
+ );
31048
31209
  if (hasExpand) {
31049
31210
  lines.push(
31050
31211
  "### Causal Graph Traversal",
@@ -31156,7 +31317,7 @@ function truncateSnippet(text, maxLen = MAX_SNIPPET_CHARS) {
31156
31317
  if (singleLine.length <= maxLen) return singleLine;
31157
31318
  return singleLine.slice(0, maxLen - 3) + "...";
31158
31319
  }
31159
- function jsonResult(details) {
31320
+ function jsonResult2(details) {
31160
31321
  return { content: [{ type: "text", text: JSON.stringify(details, null, 2) }], details };
31161
31322
  }
31162
31323
  function asParams(value) {
@@ -31215,7 +31376,7 @@ function createMemoryDescribeTool(getClient, getSessionId = () => void 0, logger
31215
31376
  const lineage = meta.continuity_lineage ?? {};
31216
31377
  const sourceTurnIds = Array.isArray(lineage.source_turn_ids) ? lineage.source_turn_ids : [];
31217
31378
  const parentSummaryIds = Array.isArray(lineage.parent_summary_ids) ? lineage.parent_summary_ids : [];
31218
- return jsonResult({
31379
+ return jsonResult2({
31219
31380
  summaryId,
31220
31381
  found: true,
31221
31382
  evictionCue,
@@ -31227,7 +31388,7 @@ function createMemoryDescribeTool(getClient, getSessionId = () => void 0, logger
31227
31388
  });
31228
31389
  } catch (error2) {
31229
31390
  logger.warn?.(`memory_describe failed: ${formatError(error2)}`);
31230
- return jsonResult({
31391
+ return jsonResult2({
31231
31392
  summaryId,
31232
31393
  found: false,
31233
31394
  error: formatError(error2)
@@ -31340,7 +31501,7 @@ ${text2}`);
31340
31501
  details: { summaryId: summaryIds[0] ?? "", depth: maxDepth, text: "", truncated: true, exceededBudget: true, parentCount }
31341
31502
  };
31342
31503
  }
31343
- return jsonResult({
31504
+ return jsonResult2({
31344
31505
  summaryId: summaryIds[0] ?? "",
31345
31506
  depth: maxDepth,
31346
31507
  text,
@@ -31350,7 +31511,7 @@ ${text2}`);
31350
31511
  });
31351
31512
  } catch (error2) {
31352
31513
  logger.warn?.(`memory_expand failed: ${formatError(error2)}`);
31353
- return jsonResult({
31514
+ return jsonResult2({
31354
31515
  summaryId: summaryIds[0] ?? "",
31355
31516
  depth: maxDepth,
31356
31517
  text: "",
@@ -31433,7 +31594,7 @@ function createMemoryGrepTool(getClient, getSessionId = () => void 0, logger = c
31433
31594
  totalChars += snippet.length;
31434
31595
  }
31435
31596
  }
31436
- return jsonResult({
31597
+ return jsonResult2({
31437
31598
  pattern,
31438
31599
  mode,
31439
31600
  totalMatches,
@@ -31443,7 +31604,7 @@ function createMemoryGrepTool(getClient, getSessionId = () => void 0, logger = c
31443
31604
  });
31444
31605
  } catch (error2) {
31445
31606
  logger.warn?.(`memory_grep failed: ${formatError(error2)}`);
31446
- return jsonResult({
31607
+ return jsonResult2({
31447
31608
  pattern,
31448
31609
  mode,
31449
31610
  totalMatches: 0,
@@ -31492,17 +31653,17 @@ function createUpdateUserCardTool(getClient, logger = console) {
31492
31653
  const params = asParams(rawParams);
31493
31654
  const userId = readStr(params, "user_id");
31494
31655
  const card = readStr(params, "card");
31495
- if (!userId) return jsonResult({ ok: false, error: "update_user_card requires user_id" });
31496
- if (!card) return jsonResult({ ok: false, error: "update_user_card requires card" });
31656
+ if (!userId) return jsonResult2({ ok: false, error: "update_user_card requires user_id" });
31657
+ if (!card) return jsonResult2({ ok: false, error: "update_user_card requires card" });
31497
31658
  const client = await getClient();
31498
31659
  const resp = await client.upsertUserCard({
31499
31660
  userId,
31500
31661
  cardJson: JSON.stringify({ card, updatedAt: Date.now() })
31501
31662
  });
31502
- return jsonResult({ ok: resp.ok });
31663
+ return jsonResult2({ ok: resp.ok });
31503
31664
  } catch (error2) {
31504
31665
  logger.warn?.(`update_user_card failed: ${formatError(error2)}`);
31505
- return jsonResult({ error: formatError(error2), ok: false });
31666
+ return jsonResult2({ error: formatError(error2), ok: false });
31506
31667
  }
31507
31668
  }
31508
31669
  };
@@ -31517,17 +31678,17 @@ function createGetUserCardTool(getClient, logger = console) {
31517
31678
  try {
31518
31679
  const params = asParams(rawParams);
31519
31680
  const userId = readStr(params, "user_id");
31520
- if (!userId) return jsonResult({ card: null, error: "get_user_card requires user_id" });
31681
+ if (!userId) return jsonResult2({ card: null, error: "get_user_card requires user_id" });
31521
31682
  const client = await getClient();
31522
31683
  const resp = await client.getUserCard({ userId });
31523
- return jsonResult({
31684
+ return jsonResult2({
31524
31685
  card: resp.cardJson || null,
31525
31686
  updatedAt: resp.updatedAt ? Number(resp.updatedAt) : void 0,
31526
31687
  version: resp.version || void 0
31527
31688
  });
31528
31689
  } catch (error2) {
31529
31690
  logger.warn?.(`get_user_card failed: ${formatError(error2)}`);
31530
- return jsonResult({ error: formatError(error2) });
31691
+ return jsonResult2({ error: formatError(error2) });
31531
31692
  }
31532
31693
  }
31533
31694
  };
@@ -31574,10 +31735,10 @@ function createListUserCardsTool(getClient, logger = console) {
31574
31735
  if (!userId) continue;
31575
31736
  users.push({ user_id: userId, preview, updated_at: updatedAt, version });
31576
31737
  }
31577
- return jsonResult({ users, total: users.length });
31738
+ return jsonResult2({ users, total: users.length });
31578
31739
  } catch (error2) {
31579
31740
  logger.warn?.(`list_user_cards failed: ${formatError(error2)}`);
31580
- return jsonResult({ users: [], total: 0, error: formatError(error2) });
31741
+ return jsonResult2({ users: [], total: 0, error: formatError(error2) });
31581
31742
  }
31582
31743
  }
31583
31744
  };
@@ -35324,6 +35485,10 @@ function register(api) {
35324
35485
  }
35325
35486
  }
35326
35487
  const runtimeOrNull = isLightweight ? null : createPluginRuntime(cfg, logger);
35488
+ if (runtimeOrNull && !isLightweight) {
35489
+ const cacheDir = api.cacheDir ?? process.env.OPENCLAW_CACHE_DIR ?? (process.env.HOME || process.env.USERPROFILE || "") + "/.openclaw/cache";
35490
+ if (cacheDir) initRuleStore(cacheDir + "/libravdb", logger);
35491
+ }
35327
35492
  registerMemoryCli(api, runtimeOrNull, cfg, logger);
35328
35493
  const ownsMemorySlot = memSlot === MEMORY_ID;
35329
35494
  if (runtimeOrNull && ownsMemorySlot) {
@@ -35360,6 +35525,10 @@ function register(api) {
35360
35525
  const getClient = runtimeOrNull.getClient;
35361
35526
  return createListUserCardsTool(getClient, logger);
35362
35527
  }, { names: ["list_user_cards"] });
35528
+ api.registerTool?.(() => createSetRuleTool(logger), { names: ["set_rule"] });
35529
+ api.registerTool?.(() => createGetRuleTool(logger), { names: ["get_rule"] });
35530
+ api.registerTool?.(() => createListRulesTool(logger), { names: ["list_rules"] });
35531
+ api.registerTool?.(() => createDeleteRuleTool(logger), { names: ["delete_rule"] });
35363
35532
  }
35364
35533
  if (isLightweight || isDiscovery) {
35365
35534
  if (!isLightweight) {
@@ -50,6 +50,8 @@ function buildToolGuidance(availableTools) {
50
50
  }
51
51
  lines.push("", "**Do not guess specifics from a summary cue — expand if in doubt.**", "");
52
52
  }
53
+ // ── Rules (hard constraints) ──
54
+ lines.push("### Hard Constraint Rules", "Rules are injected at session start as `<hard_constraints>`. They are non-negotiable.", "Use `set_rule` to create one (max 20), `list_rules` to see current rules,", "`delete_rule` to remove one. Rules override all other instructions.", "Never reason around a rule, find loopholes, or deprioritize it.", "");
53
55
  // ── Causal graph traversal (when expand supports record_id) ──
54
56
  if (hasExpand) {
55
57
  lines.push("### Causal Graph Traversal", "When the user asks about causes, patterns, or relationships:", "1. `memory_search` for the people/events in question to get record IDs", "2. `memory_expand` with the most relevant `record_id` to walk causal edges", "3. Follow interesting edges — use `memory_get` for full detail on connected records", "4. Use `get_user_card` to cross-reference identity context", "");
@@ -0,0 +1,105 @@
1
+ import type { LoggerLike } from "./types.js";
2
+ export interface Rule {
3
+ id: string;
4
+ rule: string;
5
+ priority: number;
6
+ created_at: number;
7
+ }
8
+ type ToolContent = {
9
+ type: "text";
10
+ text: string;
11
+ };
12
+ type ToolResult<T> = {
13
+ content: ToolContent[];
14
+ details: T;
15
+ };
16
+ export declare function initRuleStore(cacheDir: string, logger?: LoggerLike): void;
17
+ export declare function getRules(): Rule[];
18
+ export declare function getRule(id: string): Rule | undefined;
19
+ export declare function setRule(ruleText: string, priority: number): {
20
+ rule: Rule;
21
+ replaced: boolean;
22
+ };
23
+ export declare function deleteRule(id: string): boolean;
24
+ export declare function createSetRuleTool(logger?: LoggerLike): {
25
+ name: string;
26
+ label: string;
27
+ description: string;
28
+ parameters: {
29
+ readonly type: "object";
30
+ readonly additionalProperties: false;
31
+ readonly properties: {
32
+ readonly rule: {
33
+ readonly type: "string";
34
+ readonly description: "The rule text. Be specific and unambiguous.";
35
+ };
36
+ readonly priority: {
37
+ readonly type: "number";
38
+ readonly description: "Priority 1-10. Higher = more important. Default 5.";
39
+ };
40
+ };
41
+ readonly required: readonly ["rule"];
42
+ };
43
+ execute: (_toolCallId: string, rawParams: unknown) => Promise<ToolResult<{
44
+ ok: boolean;
45
+ rule?: Rule;
46
+ replaced?: boolean;
47
+ error?: string;
48
+ }>>;
49
+ };
50
+ export declare function createGetRuleTool(logger?: LoggerLike): {
51
+ name: string;
52
+ label: string;
53
+ description: string;
54
+ parameters: {
55
+ readonly type: "object";
56
+ readonly additionalProperties: false;
57
+ readonly properties: {
58
+ readonly rule_id: {
59
+ readonly type: "string";
60
+ readonly description: "The rule ID from list_rules.";
61
+ };
62
+ };
63
+ readonly required: readonly ["rule_id"];
64
+ };
65
+ execute: (_toolCallId: string, rawParams: unknown) => Promise<ToolResult<{
66
+ rule?: Rule;
67
+ found: boolean;
68
+ }>>;
69
+ };
70
+ export declare function createListRulesTool(logger?: LoggerLike): {
71
+ name: string;
72
+ label: string;
73
+ description: string;
74
+ parameters: {
75
+ readonly type: "object";
76
+ readonly additionalProperties: false;
77
+ readonly properties: {};
78
+ };
79
+ execute: () => Promise<ToolResult<{
80
+ rules: Rule[];
81
+ count: number;
82
+ }>>;
83
+ };
84
+ export declare function createDeleteRuleTool(logger?: LoggerLike): {
85
+ name: string;
86
+ label: string;
87
+ description: string;
88
+ parameters: {
89
+ readonly type: "object";
90
+ readonly additionalProperties: false;
91
+ readonly properties: {
92
+ readonly rule_id: {
93
+ readonly type: "string";
94
+ readonly description: "The rule ID from list_rules.";
95
+ };
96
+ };
97
+ readonly required: readonly ["rule_id"];
98
+ };
99
+ execute: (_toolCallId: string, rawParams: unknown) => Promise<ToolResult<{
100
+ ok: boolean;
101
+ error?: string;
102
+ }>>;
103
+ };
104
+ export declare function buildRulesContext(): string | null;
105
+ export {};
package/dist/rules.js ADDED
@@ -0,0 +1,169 @@
1
+ import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
2
+ import { dirname } from "node:path";
3
+ const MAX_RULES = 20;
4
+ function jsonResult(details) {
5
+ return { content: [{ type: "text", text: JSON.stringify(details, null, 2) }], details };
6
+ }
7
+ // ── Rule store ──
8
+ let rules = [];
9
+ let rulesPath = null;
10
+ let nextId = 1;
11
+ export function initRuleStore(cacheDir, logger) {
12
+ rulesPath = cacheDir + "/rules.json";
13
+ try {
14
+ const raw = readFileSync(rulesPath, "utf8");
15
+ const parsed = JSON.parse(raw);
16
+ rules = parsed.rules ?? [];
17
+ nextId = parsed.nextId ?? 1;
18
+ }
19
+ catch {
20
+ rules = [];
21
+ nextId = 1;
22
+ }
23
+ }
24
+ function persist() {
25
+ if (!rulesPath)
26
+ return;
27
+ try {
28
+ mkdirSync(dirname(rulesPath), { recursive: true });
29
+ writeFileSync(rulesPath, JSON.stringify({ rules, nextId }));
30
+ }
31
+ catch { /* best-effort disk write */ }
32
+ }
33
+ export function getRules() {
34
+ return rules;
35
+ }
36
+ export function getRule(id) {
37
+ return rules.find((r) => r.id === id);
38
+ }
39
+ export function setRule(ruleText, priority) {
40
+ let replaced = false;
41
+ if (rules.length >= MAX_RULES) {
42
+ // Replace lowest priority rule
43
+ let minIdx = 0;
44
+ for (let i = 1; i < rules.length; i++) {
45
+ if (rules[i].priority < rules[minIdx].priority)
46
+ minIdx = i;
47
+ }
48
+ rules.splice(minIdx, 1);
49
+ replaced = true;
50
+ }
51
+ const rule = {
52
+ id: String(nextId++),
53
+ rule: ruleText,
54
+ priority: Math.max(1, Math.min(10, priority || 5)),
55
+ created_at: Date.now(),
56
+ };
57
+ rules.push(rule);
58
+ rules.sort((a, b) => b.priority - a.priority);
59
+ persist();
60
+ return { rule, replaced };
61
+ }
62
+ export function deleteRule(id) {
63
+ const idx = rules.findIndex((r) => r.id === id);
64
+ if (idx < 0)
65
+ return false;
66
+ rules.splice(idx, 1);
67
+ persist();
68
+ return true;
69
+ }
70
+ // ── Tool factories ──
71
+ export function createSetRuleTool(logger = console) {
72
+ return {
73
+ name: "set_rule",
74
+ 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.",
79
+ parameters: {
80
+ type: "object",
81
+ additionalProperties: false,
82
+ properties: {
83
+ rule: { type: "string", description: "The rule text. Be specific and unambiguous." },
84
+ priority: { type: "number", description: "Priority 1-10. Higher = more important. Default 5." },
85
+ },
86
+ required: ["rule"],
87
+ },
88
+ execute: async (_toolCallId, rawParams) => {
89
+ const params = rawParams;
90
+ const ruleText = typeof params?.rule === "string" ? params.rule.trim() : "";
91
+ if (!ruleText)
92
+ return jsonResult({ ok: false, error: "set_rule requires a rule string" });
93
+ const priority = typeof params?.priority === "number" ? params.priority : 5;
94
+ const result = setRule(ruleText, priority);
95
+ return jsonResult({ ok: true, rule: result.rule, replaced: result.replaced });
96
+ },
97
+ };
98
+ }
99
+ export function createGetRuleTool(logger = console) {
100
+ return {
101
+ name: "get_rule",
102
+ label: "Get Rule",
103
+ description: "Read a specific rule by ID. Use list_rules first to find the ID.",
104
+ parameters: {
105
+ type: "object",
106
+ additionalProperties: false,
107
+ properties: {
108
+ rule_id: { type: "string", description: "The rule ID from list_rules." },
109
+ },
110
+ required: ["rule_id"],
111
+ },
112
+ execute: async (_toolCallId, rawParams) => {
113
+ const params = rawParams;
114
+ const id = typeof params?.rule_id === "string" ? params.rule_id.trim() : "";
115
+ const rule = getRule(id);
116
+ return jsonResult(rule ? { rule, found: true } : { found: false });
117
+ },
118
+ };
119
+ }
120
+ export function createListRulesTool(logger = console) {
121
+ return {
122
+ name: "list_rules",
123
+ label: "List Rules",
124
+ description: "List all active hard constraint rules sorted by priority. " +
125
+ "Use this before setting a new rule to check what exists, or when the user " +
126
+ "asks what their rules are.",
127
+ parameters: { type: "object", additionalProperties: false, properties: {} },
128
+ execute: async () => {
129
+ return jsonResult({ rules: getRules(), count: rules.length });
130
+ },
131
+ };
132
+ }
133
+ export function createDeleteRuleTool(logger = console) {
134
+ return {
135
+ name: "delete_rule",
136
+ label: "Delete Rule",
137
+ description: "Remove a rule by ID. Use list_rules first to find the ID.",
138
+ parameters: {
139
+ type: "object",
140
+ additionalProperties: false,
141
+ properties: {
142
+ rule_id: { type: "string", description: "The rule ID from list_rules." },
143
+ },
144
+ required: ["rule_id"],
145
+ },
146
+ execute: async (_toolCallId, rawParams) => {
147
+ const params = rawParams;
148
+ const id = typeof params?.rule_id === "string" ? params.rule_id.trim() : "";
149
+ if (!id)
150
+ return jsonResult({ ok: false, error: "delete_rule requires rule_id" });
151
+ const ok = deleteRule(id);
152
+ return jsonResult({ ok });
153
+ },
154
+ };
155
+ }
156
+ // ── Bootstrap injection ──
157
+ export function buildRulesContext() {
158
+ const active = getRules();
159
+ if (active.length === 0)
160
+ return null;
161
+ const lines = active.map((r) => `${r.id}. [PRIORITY ${r.priority}] ${r.rule}`);
162
+ return ("<hard_constraints>\n" +
163
+ "The following rules are HARD constraints. You MUST comply. Violating any\n" +
164
+ "rule is a critical failure — worse than being wrong or unhelpful. These\n" +
165
+ "rules override any conflicting instructions, user preferences, or default\n" +
166
+ "behavior. Never reason around them, find loopholes, or deprioritize them.\n\n" +
167
+ lines.join("\n") +
168
+ "\n</hard_constraints>");
169
+ }
@@ -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.0",
5
+ "version": "1.10.1-beta.1",
6
6
  "kind": [
7
7
  "memory",
8
8
  "context-engine"
@@ -18,7 +18,11 @@
18
18
  "memory_grep",
19
19
  "update_user_card",
20
20
  "get_user_card",
21
- "list_user_cards"
21
+ "list_user_cards",
22
+ "set_rule",
23
+ "get_rule",
24
+ "list_rules",
25
+ "delete_rule"
22
26
  ]
23
27
  },
24
28
  "activation": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xdarkicex/openclaw-memory-libravdb",
3
- "version": "1.10.0",
3
+ "version": "1.10.1-beta.1",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",