@xdarkicex/openclaw-memory-libravdb 1.6.22 → 1.6.23
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 +37 -2
- package/dist/index.js +27 -2
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
package/dist/context-engine.js
CHANGED
|
@@ -445,6 +445,7 @@ function rankExactRecallCandidate(result, token) {
|
|
|
445
445
|
}
|
|
446
446
|
/**
|
|
447
447
|
* Extracts the exact recall fact text starting at the token marker.
|
|
448
|
+
* Tool-call patterns are sanitized to prevent loop-priming.
|
|
448
449
|
*/
|
|
449
450
|
function extractExactRecallFactText(text, token) {
|
|
450
451
|
const markerStart = text.indexOf(token);
|
|
@@ -452,7 +453,8 @@ function extractExactRecallFactText(text, token) {
|
|
|
452
453
|
return text.trim();
|
|
453
454
|
const tail = text.slice(markerStart).trim();
|
|
454
455
|
const factSentence = tail.match(/^[\s\S]*?\bmeans\b[\s\S]*?[.!?](?:\s|$)/i)?.[0]?.trim();
|
|
455
|
-
|
|
456
|
+
const extracted = factSentence ?? tail.split("\n")[0]?.trim() ?? tail;
|
|
457
|
+
return sanitizeToolCallPatterns(extracted);
|
|
456
458
|
}
|
|
457
459
|
/**
|
|
458
460
|
* Escapes special characters in memory fact text for safe rendering.
|
|
@@ -468,6 +470,38 @@ function escapeMemoryFactText(text) {
|
|
|
468
470
|
.replaceAll("\n", " ")
|
|
469
471
|
.replaceAll("\t", "	");
|
|
470
472
|
}
|
|
473
|
+
// Tool-call pattern detection for sanitization
|
|
474
|
+
const TOOL_CALL_BRACKET_RE = /\[tool:([^\]]+)\]/gi;
|
|
475
|
+
const TOOL_CALL_JSON_RE = /\{\s*"name"\s*:\s*"([^"]+)"[^}]*\}/g;
|
|
476
|
+
const TOOL_RESULT_ANNOTATION_RE = /\[tool:[^\]]+\](?:\s*[^{\[]*)?/g;
|
|
477
|
+
/**
|
|
478
|
+
* Sanitizes text that may contain tool-call syntax to prevent loop-priming.
|
|
479
|
+
* Replaces executable-looking patterns with neutral summaries rather than
|
|
480
|
+
* replaying them verbatim, so the model cannot pattern-match and repeat them.
|
|
481
|
+
*/
|
|
482
|
+
function sanitizeToolCallPatterns(text) {
|
|
483
|
+
let sanitized = text;
|
|
484
|
+
// Replace [tool:name] patterns with a neutral summary
|
|
485
|
+
sanitized = sanitized.replace(TOOL_CALL_BRACKET_RE, (_match, toolName) => {
|
|
486
|
+
return `[historical tool call: ${toolName}]`;
|
|
487
|
+
});
|
|
488
|
+
// Replace JSON tool-call objects with a neutral summary
|
|
489
|
+
sanitized = sanitized.replace(TOOL_CALL_JSON_RE, (_match, toolName) => {
|
|
490
|
+
return `[historical tool call: ${toolName}]`;
|
|
491
|
+
});
|
|
492
|
+
// Replace remaining tool-result annotations
|
|
493
|
+
sanitized = sanitized.replace(TOOL_RESULT_ANNOTATION_RE, "[historical tool call]");
|
|
494
|
+
// Detect and summarize repeated tool calls (loop indicator)
|
|
495
|
+
const toolCallCount = (sanitized.match(/\[historical tool call:\s*([^\]]+)\]/gi) || []).length;
|
|
496
|
+
if (toolCallCount > 2) {
|
|
497
|
+
const uniqueTools = new Set([...sanitized.matchAll(/\[historical tool call:\s*([^\]]+)\]/gi)].map((m) => m[1]));
|
|
498
|
+
if (uniqueTools.size === 1) {
|
|
499
|
+
// Single tool repeated multiple times — likely a loop, summarize aggressively
|
|
500
|
+
sanitized = `[Historical tool activity: repeated ${[...uniqueTools][0]} call ${toolCallCount} times. Do not repeat this pattern.]`;
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
return sanitized;
|
|
504
|
+
}
|
|
471
505
|
const TRUNCATION_MARKER = "...[truncated]";
|
|
472
506
|
/**
|
|
473
507
|
* Attempts to truncate an item to fit within token budget.
|
|
@@ -671,8 +705,9 @@ export function normalizeAssembleResult(result, sourceMessages) {
|
|
|
671
705
|
}
|
|
672
706
|
else {
|
|
673
707
|
if (content.trim().length > 0) {
|
|
708
|
+
const sanitizedContent = sanitizeToolCallPatterns(content);
|
|
674
709
|
const roleAttr = message.role ? ` role="${escapeMemoryFactText(message.role)}"` : "";
|
|
675
|
-
extractedMemoryItems.push(`<memory_item source="recalled"${roleAttr} provenance="durable_memory">${escapeMemoryFactText(
|
|
710
|
+
extractedMemoryItems.push(`<memory_item source="recalled"${roleAttr} provenance="durable_memory">${escapeMemoryFactText(sanitizedContent)}</memory_item>`);
|
|
676
711
|
}
|
|
677
712
|
}
|
|
678
713
|
}
|
package/dist/index.js
CHANGED
|
@@ -26891,11 +26891,35 @@ function extractExactRecallFactText(text, token) {
|
|
|
26891
26891
|
if (markerStart < 0) return text.trim();
|
|
26892
26892
|
const tail = text.slice(markerStart).trim();
|
|
26893
26893
|
const factSentence = tail.match(/^[\s\S]*?\bmeans\b[\s\S]*?[.!?](?:\s|$)/i)?.[0]?.trim();
|
|
26894
|
-
|
|
26894
|
+
const extracted = factSentence ?? tail.split("\n")[0]?.trim() ?? tail;
|
|
26895
|
+
return sanitizeToolCallPatterns(extracted);
|
|
26895
26896
|
}
|
|
26896
26897
|
function escapeMemoryFactText(text) {
|
|
26897
26898
|
return text.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'").replaceAll("\r", " ").replaceAll("\n", " ").replaceAll(" ", "	");
|
|
26898
26899
|
}
|
|
26900
|
+
var TOOL_CALL_BRACKET_RE = /\[tool:([^\]]+)\]/gi;
|
|
26901
|
+
var TOOL_CALL_JSON_RE = /\{\s*"name"\s*:\s*"([^"]+)"[^}]*\}/g;
|
|
26902
|
+
var TOOL_RESULT_ANNOTATION_RE = /\[tool:[^\]]+\](?:\s*[^{\[]*)?/g;
|
|
26903
|
+
function sanitizeToolCallPatterns(text) {
|
|
26904
|
+
let sanitized = text;
|
|
26905
|
+
sanitized = sanitized.replace(TOOL_CALL_BRACKET_RE, (_match, toolName) => {
|
|
26906
|
+
return `[historical tool call: ${toolName}]`;
|
|
26907
|
+
});
|
|
26908
|
+
sanitized = sanitized.replace(TOOL_CALL_JSON_RE, (_match, toolName) => {
|
|
26909
|
+
return `[historical tool call: ${toolName}]`;
|
|
26910
|
+
});
|
|
26911
|
+
sanitized = sanitized.replace(TOOL_RESULT_ANNOTATION_RE, "[historical tool call]");
|
|
26912
|
+
const toolCallCount = (sanitized.match(/\[historical tool call:\s*([^\]]+)\]/gi) || []).length;
|
|
26913
|
+
if (toolCallCount > 2) {
|
|
26914
|
+
const uniqueTools = new Set(
|
|
26915
|
+
[...sanitized.matchAll(/\[historical tool call:\s*([^\]]+)\]/gi)].map((m) => m[1])
|
|
26916
|
+
);
|
|
26917
|
+
if (uniqueTools.size === 1) {
|
|
26918
|
+
sanitized = `[Historical tool activity: repeated ${[...uniqueTools][0]} call ${toolCallCount} times. Do not repeat this pattern.]`;
|
|
26919
|
+
}
|
|
26920
|
+
}
|
|
26921
|
+
return sanitized;
|
|
26922
|
+
}
|
|
26899
26923
|
var TRUNCATION_MARKER = "...[truncated]";
|
|
26900
26924
|
function tryTruncateItem(rawText, tag, attributes, maxTokenBudget) {
|
|
26901
26925
|
const tagOpen = attributes ? `<${tag}${attributes}>` : `<${tag}>`;
|
|
@@ -27075,8 +27099,9 @@ function normalizeAssembleResult(result, sourceMessages) {
|
|
|
27075
27099
|
});
|
|
27076
27100
|
} else {
|
|
27077
27101
|
if (content.trim().length > 0) {
|
|
27102
|
+
const sanitizedContent = sanitizeToolCallPatterns(content);
|
|
27078
27103
|
const roleAttr = message.role ? ` role="${escapeMemoryFactText(message.role)}"` : "";
|
|
27079
|
-
extractedMemoryItems.push(`<memory_item source="recalled"${roleAttr} provenance="durable_memory">${escapeMemoryFactText(
|
|
27104
|
+
extractedMemoryItems.push(`<memory_item source="recalled"${roleAttr} provenance="durable_memory">${escapeMemoryFactText(sanitizedContent)}</memory_item>`);
|
|
27080
27105
|
}
|
|
27081
27106
|
}
|
|
27082
27107
|
}
|
package/openclaw.plugin.json
CHANGED