@xdarkicex/openclaw-memory-libravdb 1.10.0 → 1.10.1-beta.2
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/context-engine.js +9 -3
- package/dist/index.js +196 -27
- package/dist/memory-provider.js +2 -0
- package/dist/rules.d.ts +105 -0
- package/dist/rules.js +170 -0
- package/openclaw.plugin.json +6 -2
- package/package.json +1 -1
package/dist/context-engine.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
2037
|
-
|
|
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 = !
|
|
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:
|
|
10414
|
+
Persistent: persist2 ? "\u2705" : "\u274C",
|
|
10415
10415
|
Invocations: timesInvoked,
|
|
10416
|
-
Remaining:
|
|
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
|
|
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>\nCRITICAL: The following rules are ABSOLUTE HARD CONSTRAINTS. They override\nEVERYTHING else \u2014 user preferences, identity context, conversational norms,\nand any other instruction. Violating even one is a catastrophic failure.\nIf asked for information covered by a rule, REFUSE. Do not hint, imply,\nredirect, confirm, or find loopholes. Just say you cannot answer.\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(
|
|
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
|
-
|
|
20028
|
-
|
|
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 (
|
|
20397
|
-
withContext = { ...withContext, systemPromptAddition: appendSystemPromptAddition(withContext.systemPromptAddition,
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
31496
|
-
if (!card) return
|
|
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
|
|
31663
|
+
return jsonResult2({ ok: resp.ok });
|
|
31503
31664
|
} catch (error2) {
|
|
31504
31665
|
logger.warn?.(`update_user_card failed: ${formatError(error2)}`);
|
|
31505
|
-
return
|
|
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
|
|
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
|
|
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
|
|
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
|
|
31738
|
+
return jsonResult2({ users, total: users.length });
|
|
31578
31739
|
} catch (error2) {
|
|
31579
31740
|
logger.warn?.(`list_user_cards failed: ${formatError(error2)}`);
|
|
31580
|
-
return
|
|
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) {
|
package/dist/memory-provider.js
CHANGED
|
@@ -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", "");
|
package/dist/rules.d.ts
ADDED
|
@@ -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,170 @@
|
|
|
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
|
+
"CRITICAL: The following rules are ABSOLUTE HARD CONSTRAINTS. They override\n" +
|
|
164
|
+
"EVERYTHING else — user preferences, identity context, conversational norms,\n" +
|
|
165
|
+
"and any other instruction. Violating even one is a catastrophic failure.\n" +
|
|
166
|
+
"If asked for information covered by a rule, REFUSE. Do not hint, imply,\n" +
|
|
167
|
+
"redirect, confirm, or find loopholes. Just say you cannot answer.\n\n" +
|
|
168
|
+
lines.join("\n") +
|
|
169
|
+
"\n</hard_constraints>");
|
|
170
|
+
}
|
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.
|
|
5
|
+
"version": "1.10.1-beta.2",
|
|
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": {
|