@clawmem-ai/clawmem 0.1.16 → 0.1.18
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/README.md +6 -7
- package/openclaw.plugin.json +4 -31
- package/package.json +12 -5
- package/skills/clawmem/SKILL.md +5 -5
- package/skills/clawmem/references/collaboration.md +43 -1
- package/skills/clawmem/references/schema.md +2 -1
- package/src/config.test.ts +0 -3
- package/src/config.ts +0 -3
- package/src/conversation.test.ts +63 -13
- package/src/conversation.ts +89 -392
- package/src/github-client.test.ts +101 -0
- package/src/github-client.ts +59 -0
- package/src/memory.test.ts +131 -46
- package/src/memory.ts +81 -392
- package/src/service.test.ts +188 -0
- package/src/service.ts +804 -419
- package/src/state.test.ts +47 -16
- package/src/state.ts +87 -119
- package/src/types.ts +9 -26
- package/tsconfig.json +1 -0
package/src/service.test.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
|
+
buildClawMemPromptSection,
|
|
2
3
|
buildAutoRecallContext,
|
|
4
|
+
createClawMemPlugin,
|
|
3
5
|
extractPromptTextForRecall,
|
|
4
6
|
resolveOpenClawHostVersion,
|
|
5
7
|
resolvePromptHookMode,
|
|
@@ -75,6 +77,185 @@ function testBuildAutoRecallContext(): void {
|
|
|
75
77
|
assert(context.includes("- [11] OpenClaw main agent identity uses Gandalf."), "expected memories to be listed as bullets");
|
|
76
78
|
}
|
|
77
79
|
|
|
80
|
+
function testBuildClawMemPromptSection(): void {
|
|
81
|
+
const lines = buildClawMemPromptSection({
|
|
82
|
+
availableTools: new Set([
|
|
83
|
+
"memory_recall",
|
|
84
|
+
"memory_list",
|
|
85
|
+
"memory_get",
|
|
86
|
+
"memory_repos",
|
|
87
|
+
"memory_labels",
|
|
88
|
+
"memory_store",
|
|
89
|
+
"memory_update",
|
|
90
|
+
"memory_forget",
|
|
91
|
+
]),
|
|
92
|
+
});
|
|
93
|
+
const prompt = lines.join("\n");
|
|
94
|
+
|
|
95
|
+
assert(lines[0] === "## ClawMem", "expected a stable heading for always-on ClawMem guidance");
|
|
96
|
+
assert(prompt.includes("active long-term memory system"), "expected the prompt to frame ClawMem as the active memory system");
|
|
97
|
+
assert(prompt.includes("`memory_recall`, `memory_list`, and `memory_get`"), "expected explicit retrieval guidance");
|
|
98
|
+
assert(prompt.includes("`memory_store` and `memory_update`"), "expected explicit save guidance");
|
|
99
|
+
assert(prompt.includes("`memory_forget`"), "expected explicit stale-memory guidance");
|
|
100
|
+
assert(prompt.includes("Store one durable fact per memory."), "expected one-fact-per-memory guidance");
|
|
101
|
+
assert(prompt.includes("Skip temporary requests, tool chatter"), "expected anti-noise write guardrails");
|
|
102
|
+
assert(prompt.includes("explicit short `title` plus a fuller `detail`"), "expected explicit title guidance");
|
|
103
|
+
assert(prompt.includes("user's current language"), "expected language guidance for new memories");
|
|
104
|
+
assert(prompt.includes("`memory_labels`"), "expected schema reuse guidance to mention memory_labels");
|
|
105
|
+
assert(prompt.includes("translated or near-duplicate variant"), "expected anti-duplication schema guidance");
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function createFakePluginApi(options?: {
|
|
109
|
+
slot?: string;
|
|
110
|
+
exposeCapability?: boolean;
|
|
111
|
+
exposePromptSection?: boolean;
|
|
112
|
+
runtimeVersion?: string;
|
|
113
|
+
}) {
|
|
114
|
+
let registeredCapability: { promptBuilder?: typeof buildClawMemPromptSection } | undefined;
|
|
115
|
+
let registeredPromptSection: typeof buildClawMemPromptSection | undefined;
|
|
116
|
+
const handlers = new Map<string, Array<(...args: any[]) => unknown>>();
|
|
117
|
+
const warnings: string[] = [];
|
|
118
|
+
const infos: string[] = [];
|
|
119
|
+
const api = {
|
|
120
|
+
id: "clawmem",
|
|
121
|
+
name: "ClawMem",
|
|
122
|
+
source: "test",
|
|
123
|
+
registrationMode: "test",
|
|
124
|
+
config: {},
|
|
125
|
+
pluginConfig: {
|
|
126
|
+
agents: {
|
|
127
|
+
main: {
|
|
128
|
+
token: "test-token",
|
|
129
|
+
defaultRepo: "acme/memory",
|
|
130
|
+
},
|
|
131
|
+
},
|
|
132
|
+
},
|
|
133
|
+
logger: {
|
|
134
|
+
info: (message: string) => { infos.push(message); },
|
|
135
|
+
warn: (message: string) => { warnings.push(message); },
|
|
136
|
+
},
|
|
137
|
+
runtime: {
|
|
138
|
+
version: options?.runtimeVersion ?? "2026.4.9",
|
|
139
|
+
config: {
|
|
140
|
+
loadConfig: () => ({
|
|
141
|
+
plugins: {
|
|
142
|
+
slots: {
|
|
143
|
+
memory: options?.slot ?? "clawmem",
|
|
144
|
+
},
|
|
145
|
+
},
|
|
146
|
+
}),
|
|
147
|
+
},
|
|
148
|
+
events: {
|
|
149
|
+
onSessionTranscriptUpdate: () => () => {},
|
|
150
|
+
},
|
|
151
|
+
subagent: {},
|
|
152
|
+
},
|
|
153
|
+
on: (event: string, handler: (...args: any[]) => unknown) => {
|
|
154
|
+
const current = handlers.get(event) ?? [];
|
|
155
|
+
current.push(handler);
|
|
156
|
+
handlers.set(event, current);
|
|
157
|
+
},
|
|
158
|
+
registerTool: () => {},
|
|
159
|
+
registerService: () => {},
|
|
160
|
+
...(options?.exposeCapability === false
|
|
161
|
+
? {}
|
|
162
|
+
: {
|
|
163
|
+
registerMemoryCapability: (capability: { promptBuilder?: typeof buildClawMemPromptSection }) => {
|
|
164
|
+
registeredCapability = capability;
|
|
165
|
+
},
|
|
166
|
+
}),
|
|
167
|
+
...(options?.exposePromptSection === false
|
|
168
|
+
? {}
|
|
169
|
+
: {
|
|
170
|
+
registerMemoryPromptSection: (builder: typeof buildClawMemPromptSection) => {
|
|
171
|
+
registeredPromptSection = builder;
|
|
172
|
+
},
|
|
173
|
+
}),
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
return {
|
|
177
|
+
api,
|
|
178
|
+
getRegisteredCapability: () => registeredCapability,
|
|
179
|
+
getRegisteredPromptSection: () => registeredPromptSection,
|
|
180
|
+
getWarnings: () => warnings,
|
|
181
|
+
getInfos: () => infos,
|
|
182
|
+
getHandler: (event: string) => handlers.get(event)?.[0],
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function testRegistersAlwaysOnMemoryPromptCapability(): void {
|
|
187
|
+
const fake = createFakePluginApi();
|
|
188
|
+
createClawMemPlugin(fake.api as never);
|
|
189
|
+
|
|
190
|
+
const capability = fake.getRegisteredCapability();
|
|
191
|
+
assert(Boolean(capability?.promptBuilder), "expected ClawMem to register a memory prompt builder");
|
|
192
|
+
const prompt = capability?.promptBuilder?.({ availableTools: new Set(["memory_recall", "memory_store"]) }).join("\n") ?? "";
|
|
193
|
+
assert(prompt.includes("## ClawMem"), "expected the registered prompt builder to emit ClawMem guidance");
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function testFallsBackToLegacyMemoryPromptSectionRegistration(): void {
|
|
197
|
+
const fake = createFakePluginApi({ exposeCapability: false });
|
|
198
|
+
createClawMemPlugin(fake.api as never);
|
|
199
|
+
|
|
200
|
+
assert(!fake.getRegisteredCapability(), "expected no memory capability registration when the host lacks that API");
|
|
201
|
+
const builder = fake.getRegisteredPromptSection();
|
|
202
|
+
assert(Boolean(builder), "expected fallback registration through registerMemoryPromptSection");
|
|
203
|
+
const prompt = builder?.({ availableTools: new Set(["memory_recall"]) }).join("\n") ?? "";
|
|
204
|
+
assert(prompt.includes("## ClawMem"), "expected the fallback builder to emit ClawMem guidance");
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
function testOlderHostWithoutPromptRegistrationDoesNotWarn(): void {
|
|
208
|
+
const fake = createFakePluginApi({
|
|
209
|
+
exposeCapability: false,
|
|
210
|
+
exposePromptSection: false,
|
|
211
|
+
runtimeVersion: "2026.3.13",
|
|
212
|
+
});
|
|
213
|
+
createClawMemPlugin(fake.api as never);
|
|
214
|
+
|
|
215
|
+
assert(fake.getWarnings().length === 0, "expected older hosts without prompt registration to avoid warnings");
|
|
216
|
+
assert(
|
|
217
|
+
fake.getInfos().some((message) => message.includes("falling back to before_prompt_build prependSystemContext")),
|
|
218
|
+
"expected older hosts to log an informational compatibility note",
|
|
219
|
+
);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
function testModernHostWithoutPromptRegistrationWarns(): void {
|
|
223
|
+
const fake = createFakePluginApi({
|
|
224
|
+
exposeCapability: false,
|
|
225
|
+
exposePromptSection: false,
|
|
226
|
+
runtimeVersion: "2026.3.22",
|
|
227
|
+
});
|
|
228
|
+
createClawMemPlugin(fake.api as never);
|
|
229
|
+
|
|
230
|
+
assert(
|
|
231
|
+
fake.getWarnings().some((message) => message.includes("falling back to before_prompt_build prependSystemContext")),
|
|
232
|
+
"expected warning when a new-enough host is missing prompt registration",
|
|
233
|
+
);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
async function testOlderModernHostInjectsPromptGuidanceViaPrependSystemContext(): Promise<void> {
|
|
237
|
+
const fake = createFakePluginApi({
|
|
238
|
+
exposeCapability: false,
|
|
239
|
+
exposePromptSection: false,
|
|
240
|
+
runtimeVersion: "2026.3.13",
|
|
241
|
+
});
|
|
242
|
+
createClawMemPlugin(fake.api as never);
|
|
243
|
+
|
|
244
|
+
const handler = fake.getHandler("before_prompt_build");
|
|
245
|
+
assert(typeof handler === "function", "expected before_prompt_build handler to be registered for modern hosts");
|
|
246
|
+
const result = await handler?.({ prompt: "hi" }, { agentId: "main" }) as { prependContext?: string; prependSystemContext?: string } | void;
|
|
247
|
+
assert(Boolean(result && result.prependSystemContext?.includes("## ClawMem")), "expected static ClawMem guidance to use prependSystemContext fallback");
|
|
248
|
+
assert(!result || !result.prependContext, "expected no dynamic recall context when the prompt is too short for auto-recall");
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
function testSkipsAlwaysOnPromptWhenClawMemIsNotSelectedMemoryPlugin(): void {
|
|
252
|
+
const fake = createFakePluginApi({ slot: "other-memory" });
|
|
253
|
+
createClawMemPlugin(fake.api as never);
|
|
254
|
+
|
|
255
|
+
assert(!fake.getRegisteredCapability(), "expected no memory prompt registration when ClawMem is not the selected memory plugin");
|
|
256
|
+
assert(!fake.getRegisteredPromptSection(), "expected no legacy prompt registration when ClawMem is not selected");
|
|
257
|
+
}
|
|
258
|
+
|
|
78
259
|
function testResolveHostVersionFromRuntime(): void {
|
|
79
260
|
const version = resolveOpenClawHostVersion({ runtime: { version: "2026.3.28" } } as never);
|
|
80
261
|
assert(version === "2026.3.28", "expected runtime.version to take precedence");
|
|
@@ -139,11 +320,18 @@ testExtractPromptFallsBackToLatestUserMessage();
|
|
|
139
320
|
testExtractPromptFromPromptField();
|
|
140
321
|
testExtractPromptFromStructuredContent();
|
|
141
322
|
testBuildAutoRecallContext();
|
|
323
|
+
testBuildClawMemPromptSection();
|
|
142
324
|
testResolveHostVersionFromRuntime();
|
|
143
325
|
testResolveHostVersionFromEnvFallback();
|
|
144
326
|
testIgnoresNpmPackageVersionFallback();
|
|
145
327
|
testResolvePromptHookModeModern();
|
|
146
328
|
testResolvePromptHookModeLegacy();
|
|
147
329
|
testResolvePromptHookModeLegacyForUnknownVersion();
|
|
330
|
+
testRegistersAlwaysOnMemoryPromptCapability();
|
|
331
|
+
testFallsBackToLegacyMemoryPromptSectionRegistration();
|
|
332
|
+
testOlderHostWithoutPromptRegistrationDoesNotWarn();
|
|
333
|
+
testModernHostWithoutPromptRegistrationWarns();
|
|
334
|
+
testSkipsAlwaysOnPromptWhenClawMemIsNotSelectedMemoryPlugin();
|
|
335
|
+
await testOlderModernHostInjectsPromptGuidanceViaPrependSystemContext();
|
|
148
336
|
|
|
149
337
|
console.log("service tests passed");
|