@bamdra/bamdra-openclaw-memory 0.3.13 → 0.3.14
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 +132 -27
- package/dist/openclaw.plugin.json +1 -1
- package/dist/package.json +1 -1
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -36,22 +36,30 @@ var ContextAssembler = class {
|
|
|
36
36
|
}
|
|
37
37
|
assemble(input) {
|
|
38
38
|
const sections = [];
|
|
39
|
+
const maxFactValueChars = this.config.contextAssembly?.maxFactValueChars ?? 280;
|
|
40
|
+
const recentMessageMaxChars = this.config.contextAssembly?.recentMessageMaxChars ?? 1200;
|
|
39
41
|
if (input.topic) {
|
|
40
42
|
sections.push({
|
|
41
43
|
kind: "topic",
|
|
42
|
-
content:
|
|
43
|
-
|
|
44
|
+
content: trimToLength(
|
|
45
|
+
`Topic: ${input.topic.title}
|
|
46
|
+
Labels: ${joinList(input.topic.labels)}`,
|
|
47
|
+
240
|
|
48
|
+
)
|
|
44
49
|
});
|
|
45
50
|
if (this.config.contextAssembly?.includeTopicShortSummary !== false) {
|
|
46
51
|
sections.push({
|
|
47
52
|
kind: "summary",
|
|
48
|
-
content: input.topic.summaryShort || "(no short summary yet)"
|
|
53
|
+
content: trimToLength(input.topic.summaryShort || "(no short summary yet)", 600)
|
|
49
54
|
});
|
|
50
55
|
}
|
|
51
56
|
if (this.config.contextAssembly?.includeOpenLoops !== false && input.topic.openLoops.length > 0) {
|
|
52
57
|
sections.push({
|
|
53
58
|
kind: "open_loops",
|
|
54
|
-
content:
|
|
59
|
+
content: trimToLength(
|
|
60
|
+
input.topic.openLoops.map((item) => `- ${trimToLength(item, 120)}`).join("\n"),
|
|
61
|
+
600
|
|
62
|
+
)
|
|
55
63
|
});
|
|
56
64
|
}
|
|
57
65
|
}
|
|
@@ -70,22 +78,30 @@ Labels: ${joinList(input.topic.labels)}`
|
|
|
70
78
|
kind: "facts",
|
|
71
79
|
content: facts.map((fact) => {
|
|
72
80
|
const prefix = fact.sensitivity === "secret_ref" ? "[secret-ref]" : `[${fact.category}]`;
|
|
73
|
-
return `${prefix} ${fact.key}: ${fact.value}`;
|
|
81
|
+
return `${prefix} ${trimToLength(fact.key, 80)}: ${trimToLength(fact.value, maxFactValueChars)}`;
|
|
74
82
|
}).join("\n")
|
|
75
83
|
});
|
|
76
84
|
}
|
|
77
85
|
if (input.recentMessages.length > 0) {
|
|
78
86
|
sections.push({
|
|
79
87
|
kind: "recent_messages",
|
|
80
|
-
content:
|
|
88
|
+
content: trimToLength(
|
|
89
|
+
input.recentMessages.map(({ message }) => `${message.role}: ${trimToLength(normalizeWhitespace(message.text), 220)}`).join("\n"),
|
|
90
|
+
recentMessageMaxChars
|
|
91
|
+
)
|
|
81
92
|
});
|
|
82
93
|
}
|
|
94
|
+
const maxChars = this.config.contextAssembly?.maxChars ?? 4e3;
|
|
95
|
+
const text = trimToLength(
|
|
96
|
+
sections.filter((section) => section.content.trim().length > 0).map((section) => `[${section.kind}]
|
|
97
|
+
${section.content}`).join("\n\n"),
|
|
98
|
+
maxChars
|
|
99
|
+
);
|
|
83
100
|
return {
|
|
84
101
|
sessionId: input.sessionId,
|
|
85
102
|
topicId: input.topic?.id ?? null,
|
|
86
|
-
text
|
|
87
|
-
|
|
88
|
-
sections
|
|
103
|
+
text,
|
|
104
|
+
sections: sections.filter((section) => section.content.trim().length > 0)
|
|
89
105
|
};
|
|
90
106
|
}
|
|
91
107
|
};
|
|
@@ -95,6 +111,22 @@ function joinList(values) {
|
|
|
95
111
|
function limitFacts(values, limit) {
|
|
96
112
|
return values.slice(0, Math.max(0, limit));
|
|
97
113
|
}
|
|
114
|
+
function trimToLength(value, maxChars) {
|
|
115
|
+
if (maxChars <= 0) {
|
|
116
|
+
return "";
|
|
117
|
+
}
|
|
118
|
+
const normalized = normalizeWhitespace(value);
|
|
119
|
+
if (normalized.length <= maxChars) {
|
|
120
|
+
return normalized;
|
|
121
|
+
}
|
|
122
|
+
if (maxChars <= 3) {
|
|
123
|
+
return normalized.slice(0, maxChars);
|
|
124
|
+
}
|
|
125
|
+
return `${normalized.slice(0, maxChars - 3).trimEnd()}...`;
|
|
126
|
+
}
|
|
127
|
+
function normalizeWhitespace(value) {
|
|
128
|
+
return value.replace(/\s+/g, " ").trim();
|
|
129
|
+
}
|
|
98
130
|
|
|
99
131
|
// ../../packages/fact-extractor/src/index.ts
|
|
100
132
|
var FactExtractor = class {
|
|
@@ -1337,9 +1369,11 @@ function createContextEngineMemoryV2Plugin(inputConfig, api) {
|
|
|
1337
1369
|
const sessionId = getSessionIdFromHookContext(hookContext);
|
|
1338
1370
|
await backfillResolvedIdentity(store, sessionId);
|
|
1339
1371
|
const text = getTextFromHookContext(event);
|
|
1372
|
+
const hasMultimodalPayload = containsNonTextualPayload(event) || containsNonTextualPayload(hookContext);
|
|
1340
1373
|
logMemoryEvent("hook-assemble-received", {
|
|
1341
1374
|
hasSessionId: Boolean(sessionId),
|
|
1342
|
-
hasText: Boolean(text)
|
|
1375
|
+
hasText: Boolean(text),
|
|
1376
|
+
hasMultimodalPayload
|
|
1343
1377
|
});
|
|
1344
1378
|
if (!sessionId) {
|
|
1345
1379
|
return;
|
|
@@ -1360,14 +1394,21 @@ function createContextEngineMemoryV2Plugin(inputConfig, api) {
|
|
|
1360
1394
|
query: text,
|
|
1361
1395
|
limit: 3
|
|
1362
1396
|
}) : null;
|
|
1363
|
-
const assembledWithRecall = knowledgeRecall != null ? mergeLocalKnowledgeRecall(
|
|
1397
|
+
const assembledWithRecall = knowledgeRecall != null ? mergeLocalKnowledgeRecall(
|
|
1398
|
+
assembled,
|
|
1399
|
+
knowledgeRecall,
|
|
1400
|
+
config.contextAssembly?.recallMaxChars ?? 900
|
|
1401
|
+
) : assembled;
|
|
1364
1402
|
logMemoryEvent("hook-assemble-complete", {
|
|
1365
1403
|
sessionId,
|
|
1366
1404
|
topicId: assembledWithRecall.topicId,
|
|
1367
1405
|
sections: assembledWithRecall.sections.length,
|
|
1368
1406
|
localKnowledgeHits: knowledgeRecall == null ? 0 : knowledgeRecall.vectors.length + knowledgeRecall.facts.length + knowledgeRecall.topics.length
|
|
1369
1407
|
});
|
|
1370
|
-
return buildBeforePromptBuildResult(
|
|
1408
|
+
return buildBeforePromptBuildResult(
|
|
1409
|
+
assembledWithRecall,
|
|
1410
|
+
hasMultimodalPayload ? config.contextAssembly?.maxCharsWhenMultimodal ?? 1200 : config.contextAssembly?.maxChars ?? 4e3
|
|
1411
|
+
);
|
|
1371
1412
|
});
|
|
1372
1413
|
} else {
|
|
1373
1414
|
logMemoryEvent("register-typed-hooks-skipped", { reason: "typed hook registrar unavailable" });
|
|
@@ -1933,7 +1974,12 @@ function normalizeMemoryConfig(inputConfig) {
|
|
|
1933
1974
|
includeTopicShortSummary: inputConfig?.contextAssembly?.includeTopicShortSummary ?? true,
|
|
1934
1975
|
includeOpenLoops: inputConfig?.contextAssembly?.includeOpenLoops ?? true,
|
|
1935
1976
|
alwaysFactLimit: inputConfig?.contextAssembly?.alwaysFactLimit ?? 12,
|
|
1936
|
-
topicFactLimit: inputConfig?.contextAssembly?.topicFactLimit ?? 16
|
|
1977
|
+
topicFactLimit: inputConfig?.contextAssembly?.topicFactLimit ?? 16,
|
|
1978
|
+
maxChars: inputConfig?.contextAssembly?.maxChars ?? 4e3,
|
|
1979
|
+
maxCharsWhenMultimodal: inputConfig?.contextAssembly?.maxCharsWhenMultimodal ?? 1200,
|
|
1980
|
+
recentMessageMaxChars: inputConfig?.contextAssembly?.recentMessageMaxChars ?? 1200,
|
|
1981
|
+
maxFactValueChars: inputConfig?.contextAssembly?.maxFactValueChars ?? 280,
|
|
1982
|
+
recallMaxChars: inputConfig?.contextAssembly?.recallMaxChars ?? 900
|
|
1937
1983
|
}
|
|
1938
1984
|
};
|
|
1939
1985
|
}
|
|
@@ -2007,14 +2053,11 @@ function extractTextFromInput(input) {
|
|
|
2007
2053
|
return normalizeHookText(lastUserMessage?.text ?? lastUserMessage?.content);
|
|
2008
2054
|
}
|
|
2009
2055
|
function normalizeHookText(value) {
|
|
2010
|
-
|
|
2011
|
-
return null;
|
|
2012
|
-
}
|
|
2013
|
-
const normalized = value.trim();
|
|
2056
|
+
const normalized = extractTextFromStructuredValue(value).trim();
|
|
2014
2057
|
return normalized ? normalized : null;
|
|
2015
2058
|
}
|
|
2016
|
-
function buildBeforePromptBuildResult(assembled) {
|
|
2017
|
-
const text = assembled.text.trim();
|
|
2059
|
+
function buildBeforePromptBuildResult(assembled, maxChars) {
|
|
2060
|
+
const text = trimToLength2(assembled.text.trim(), maxChars);
|
|
2018
2061
|
if (!text) {
|
|
2019
2062
|
return void 0;
|
|
2020
2063
|
}
|
|
@@ -2047,30 +2090,92 @@ function shouldPreferLocalKnowledgeFirst(text) {
|
|
|
2047
2090
|
];
|
|
2048
2091
|
return signals.some((signal) => normalized.includes(signal));
|
|
2049
2092
|
}
|
|
2050
|
-
function mergeLocalKnowledgeRecall(assembled, search) {
|
|
2051
|
-
const vectorLines = search.vectors.slice(0, 3).map((item) => `- [vector] ${item.title} (${item.sourcePath}): ${item.text
|
|
2052
|
-
const factLines = search.facts.slice(0, 2).map((item) => `- [fact] ${item.fact.key}: ${item.fact.value}`);
|
|
2053
|
-
const topicLines = search.topics.slice(0, 2).map((item) => `- [topic] ${item.topic.title}: ${item.topic.summaryShort}`);
|
|
2093
|
+
function mergeLocalKnowledgeRecall(assembled, search, maxChars) {
|
|
2094
|
+
const vectorLines = search.vectors.slice(0, 3).map((item) => `- [vector] ${trimToLength2(item.title, 80)} (${trimToLength2(item.sourcePath, 120)}): ${trimToLength2(item.text, 180)}`);
|
|
2095
|
+
const factLines = search.facts.slice(0, 2).map((item) => `- [fact] ${trimToLength2(item.fact.key, 80)}: ${trimToLength2(item.fact.value, 140)}`);
|
|
2096
|
+
const topicLines = search.topics.slice(0, 2).map((item) => `- [topic] ${trimToLength2(item.topic.title, 80)}: ${trimToLength2(item.topic.summaryShort, 140)}`);
|
|
2054
2097
|
const lines = [...vectorLines, ...factLines, ...topicLines];
|
|
2055
2098
|
if (lines.length === 0) {
|
|
2056
2099
|
return assembled;
|
|
2057
2100
|
}
|
|
2058
|
-
const content = [
|
|
2101
|
+
const content = trimToLength2([
|
|
2059
2102
|
"Prefer answering from local memory and knowledge before using web search.",
|
|
2060
2103
|
...lines
|
|
2061
|
-
].join("\n");
|
|
2104
|
+
].join("\n"), maxChars);
|
|
2062
2105
|
return {
|
|
2063
2106
|
...assembled,
|
|
2064
|
-
text: `[facts]
|
|
2107
|
+
text: trimToLength2(`[facts]
|
|
2065
2108
|
${content}
|
|
2066
2109
|
|
|
2067
|
-
${assembled.text}`.trim(),
|
|
2110
|
+
${assembled.text}`.trim(), assembled.text.length + maxChars + 32),
|
|
2068
2111
|
sections: [
|
|
2069
2112
|
{ kind: "facts", content },
|
|
2070
2113
|
...assembled.sections
|
|
2071
2114
|
]
|
|
2072
2115
|
};
|
|
2073
2116
|
}
|
|
2117
|
+
function extractTextFromStructuredValue(value, depth = 0) {
|
|
2118
|
+
if (depth > 4 || value == null) {
|
|
2119
|
+
return "";
|
|
2120
|
+
}
|
|
2121
|
+
if (typeof value === "string") {
|
|
2122
|
+
return value;
|
|
2123
|
+
}
|
|
2124
|
+
if (Array.isArray(value)) {
|
|
2125
|
+
return value.map((item) => extractTextFromStructuredValue(item, depth + 1)).filter(Boolean).join("\n");
|
|
2126
|
+
}
|
|
2127
|
+
if (typeof value !== "object") {
|
|
2128
|
+
return "";
|
|
2129
|
+
}
|
|
2130
|
+
const candidate = value;
|
|
2131
|
+
const direct = [
|
|
2132
|
+
candidate.text,
|
|
2133
|
+
candidate.input_text,
|
|
2134
|
+
candidate.caption
|
|
2135
|
+
].map((item) => extractTextFromStructuredValue(item, depth + 1)).filter(Boolean);
|
|
2136
|
+
if (direct.length > 0) {
|
|
2137
|
+
return direct.join("\n");
|
|
2138
|
+
}
|
|
2139
|
+
return [
|
|
2140
|
+
extractTextFromStructuredValue(candidate.content, depth + 1),
|
|
2141
|
+
extractTextFromStructuredValue(candidate.message, depth + 1),
|
|
2142
|
+
extractTextFromStructuredValue(candidate.input, depth + 1),
|
|
2143
|
+
extractTextFromStructuredValue(candidate.messages, depth + 1)
|
|
2144
|
+
].filter(Boolean).join("\n");
|
|
2145
|
+
}
|
|
2146
|
+
function containsNonTextualPayload(value, depth = 0) {
|
|
2147
|
+
if (depth > 4 || value == null) {
|
|
2148
|
+
return false;
|
|
2149
|
+
}
|
|
2150
|
+
if (Array.isArray(value)) {
|
|
2151
|
+
return value.some((item) => containsNonTextualPayload(item, depth + 1));
|
|
2152
|
+
}
|
|
2153
|
+
if (typeof value !== "object") {
|
|
2154
|
+
return false;
|
|
2155
|
+
}
|
|
2156
|
+
const candidate = value;
|
|
2157
|
+
const typeValue = typeof candidate.type === "string" ? candidate.type.toLowerCase() : "";
|
|
2158
|
+
if (typeValue.includes("image") || typeValue.includes("audio") || typeValue.includes("video") || "image_url" in candidate || "input_image" in candidate) {
|
|
2159
|
+
return true;
|
|
2160
|
+
}
|
|
2161
|
+
if (typeof candidate.mimeType === "string" && candidate.mimeType.startsWith("image/")) {
|
|
2162
|
+
return true;
|
|
2163
|
+
}
|
|
2164
|
+
return Object.values(candidate).some((item) => containsNonTextualPayload(item, depth + 1));
|
|
2165
|
+
}
|
|
2166
|
+
function trimToLength2(value, maxChars) {
|
|
2167
|
+
if (maxChars <= 0) {
|
|
2168
|
+
return "";
|
|
2169
|
+
}
|
|
2170
|
+
const normalized = value.replace(/\s+/g, " ").trim();
|
|
2171
|
+
if (normalized.length <= maxChars) {
|
|
2172
|
+
return normalized;
|
|
2173
|
+
}
|
|
2174
|
+
if (maxChars <= 3) {
|
|
2175
|
+
return normalized.slice(0, maxChars);
|
|
2176
|
+
}
|
|
2177
|
+
return `${normalized.slice(0, maxChars - 3).trimEnd()}...`;
|
|
2178
|
+
}
|
|
2074
2179
|
|
|
2075
2180
|
// src/index.ts
|
|
2076
2181
|
var import_node_fs2 = require("node:fs");
|
package/dist/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bamdra/bamdra-openclaw-memory",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.14",
|
|
4
4
|
"description": "Unified topic-aware memory plugin for OpenClaw with durable SQLite recall and bounded context growth.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"homepage": "https://www.bamdra.com",
|
package/openclaw.plugin.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bamdra/bamdra-openclaw-memory",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.14",
|
|
4
4
|
"description": "Unified topic-aware memory plugin for OpenClaw with durable SQLite recall and bounded context growth.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"homepage": "https://www.bamdra.com",
|