@mem9/mem9 0.3.5 → 0.3.7-rc1
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/hooks.ts +5 -2
- package/index.ts +374 -16
- package/package.json +1 -1
- package/server-backend.ts +1 -0
- package/types.ts +3 -0
package/hooks.ts
CHANGED
|
@@ -130,11 +130,14 @@ function formatMemoriesBlock(memories: Memory[]): string {
|
|
|
130
130
|
let idx = 1;
|
|
131
131
|
|
|
132
132
|
const formatMem = (m: Memory): string => {
|
|
133
|
-
const
|
|
133
|
+
const tagStr = m.tags?.length ? `[${m.tags.join(", ")}]` : "";
|
|
134
|
+
const age = m.relative_age ? `(${m.relative_age})` : "";
|
|
135
|
+
const middle = [tagStr, age].filter(Boolean).join(" ");
|
|
136
|
+
const sep = middle ? " " + middle + " " : " ";
|
|
134
137
|
const content = m.content.length > MAX_CONTENT_LEN
|
|
135
138
|
? m.content.slice(0, MAX_CONTENT_LEN) + "..."
|
|
136
139
|
: m.content;
|
|
137
|
-
return `${idx++}.${
|
|
140
|
+
return `${idx++}.${sep}${escapeForPrompt(content)}`;
|
|
138
141
|
};
|
|
139
142
|
|
|
140
143
|
if (pinned.length > 0) {
|
package/index.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { ServerBackend } from "./server-backend.js";
|
|
|
3
3
|
import { registerHooks } from "./hooks.js";
|
|
4
4
|
import type {
|
|
5
5
|
PluginConfig,
|
|
6
|
+
Memory,
|
|
6
7
|
CreateMemoryInput,
|
|
7
8
|
UpdateMemoryInput,
|
|
8
9
|
SearchInput,
|
|
@@ -11,6 +12,8 @@ import type {
|
|
|
11
12
|
} from "./types.js";
|
|
12
13
|
|
|
13
14
|
const DEFAULT_API_URL = "https://api.mem9.ai";
|
|
15
|
+
const MEM9_MEMORY_PATH_PREFIX = "mem9/";
|
|
16
|
+
const MEM9_MEMORY_PROVIDER = "mem9";
|
|
14
17
|
|
|
15
18
|
function jsonResult(data: unknown) {
|
|
16
19
|
// Older OpenClaw versions may assume tool results have a normalized
|
|
@@ -26,6 +29,141 @@ function jsonResult(data: unknown) {
|
|
|
26
29
|
}
|
|
27
30
|
}
|
|
28
31
|
|
|
32
|
+
function buildMemoryPath(id: string): string {
|
|
33
|
+
return `${MEM9_MEMORY_PATH_PREFIX}${id}`;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function normalizeMemoryLookup(value: string): string {
|
|
37
|
+
const trimmed = value.trim();
|
|
38
|
+
return trimmed.startsWith(MEM9_MEMORY_PATH_PREFIX)
|
|
39
|
+
? trimmed.slice(MEM9_MEMORY_PATH_PREFIX.length)
|
|
40
|
+
: trimmed;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function normalizeSnippet(text: string, maxLength = 240): string {
|
|
44
|
+
const flattened = text.replace(/\s+/g, " ").trim();
|
|
45
|
+
if (flattened.length <= maxLength) {
|
|
46
|
+
return flattened;
|
|
47
|
+
}
|
|
48
|
+
return `${flattened.slice(0, maxLength - 3)}...`;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function countLines(text: string): number {
|
|
52
|
+
if (!text) {
|
|
53
|
+
return 1;
|
|
54
|
+
}
|
|
55
|
+
return text.split(/\r?\n/).length;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function buildPromptSection(params: {
|
|
59
|
+
availableTools: Set<string>;
|
|
60
|
+
citationsMode?: string;
|
|
61
|
+
}): string[] {
|
|
62
|
+
const hasMemorySearch = params.availableTools.has("memory_search");
|
|
63
|
+
const hasMemoryGet = params.availableTools.has("memory_get");
|
|
64
|
+
|
|
65
|
+
if (!hasMemorySearch && !hasMemoryGet) {
|
|
66
|
+
return [];
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
let toolGuidance: string;
|
|
70
|
+
if (hasMemorySearch && hasMemoryGet) {
|
|
71
|
+
toolGuidance =
|
|
72
|
+
"Before answering anything about prior work, decisions, dates, people, preferences, or todos: run `memory_search` first, then use `memory_get` to pull only the needed memory. If low confidence after search, say you checked.";
|
|
73
|
+
} else if (hasMemorySearch) {
|
|
74
|
+
toolGuidance =
|
|
75
|
+
"Before answering anything about prior work, decisions, dates, people, preferences, or todos: run `memory_search` first and answer from the matching results. If low confidence after search, say you checked.";
|
|
76
|
+
} else {
|
|
77
|
+
toolGuidance =
|
|
78
|
+
"Before answering anything about prior work, decisions, dates, people, preferences, or todos that already point to a specific memory: run `memory_get` to pull only the needed memory. If low confidence after reading it, say you checked.";
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const lines = ["## Memory Recall", toolGuidance];
|
|
82
|
+
if (params.citationsMode === "off") {
|
|
83
|
+
lines.push(
|
|
84
|
+
"Citations are disabled: do not mention memory identifiers unless the user explicitly asks.",
|
|
85
|
+
);
|
|
86
|
+
} else {
|
|
87
|
+
lines.push(
|
|
88
|
+
"Citations: include the memory identifier only when it helps the user verify recalled memory.",
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
lines.push("");
|
|
92
|
+
return lines;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
interface MemoryCapability {
|
|
96
|
+
search: (query: string, opts?: { limit?: number }) => Promise<{ data: Memory[]; total: number }>;
|
|
97
|
+
store: (content: string, opts?: { tags?: string[]; source?: string }) => Promise<unknown>;
|
|
98
|
+
get: (id: string) => Promise<Memory | null>;
|
|
99
|
+
remove: (id: string) => Promise<boolean>;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
interface MemoryPromptSectionBuilder {
|
|
103
|
+
(params: { availableTools: Set<string>; citationsMode?: string }): string[];
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
interface MemorySearchResult {
|
|
107
|
+
path: string;
|
|
108
|
+
startLine: number;
|
|
109
|
+
endLine: number;
|
|
110
|
+
score: number;
|
|
111
|
+
snippet: string;
|
|
112
|
+
source: "memory";
|
|
113
|
+
citation?: string;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
interface MemoryProviderStatus {
|
|
117
|
+
backend: "builtin" | "qmd";
|
|
118
|
+
provider: string;
|
|
119
|
+
requestedProvider?: string;
|
|
120
|
+
custom?: Record<string, unknown>;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
interface MemorySearchManager {
|
|
124
|
+
search: (
|
|
125
|
+
query: string,
|
|
126
|
+
opts?: { maxResults?: number; minScore?: number; sessionKey?: string },
|
|
127
|
+
) => Promise<MemorySearchResult[]>;
|
|
128
|
+
readFile: (params: {
|
|
129
|
+
relPath: string;
|
|
130
|
+
from?: number;
|
|
131
|
+
lines?: number;
|
|
132
|
+
}) => Promise<{ text: string; path: string }>;
|
|
133
|
+
status: () => MemoryProviderStatus;
|
|
134
|
+
sync?: (params?: {
|
|
135
|
+
reason?: string;
|
|
136
|
+
force?: boolean;
|
|
137
|
+
sessionFiles?: string[];
|
|
138
|
+
progress?: (update: { completed: number; total: number; label?: string }) => void;
|
|
139
|
+
}) => Promise<void>;
|
|
140
|
+
probeEmbeddingAvailability: () => Promise<{ ok: boolean; error?: string }>;
|
|
141
|
+
probeVectorAvailability: () => Promise<boolean>;
|
|
142
|
+
close?: () => Promise<void>;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
interface MemoryRuntime {
|
|
146
|
+
getMemorySearchManager: (params: {
|
|
147
|
+
cfg?: unknown;
|
|
148
|
+
agentId: string;
|
|
149
|
+
purpose?: "default" | "status";
|
|
150
|
+
}) => Promise<{ manager: MemorySearchManager | null; error?: string }>;
|
|
151
|
+
resolveMemoryBackendConfig: (params: { cfg?: unknown; agentId: string }) => {
|
|
152
|
+
backend: "builtin" | "qmd";
|
|
153
|
+
provider: string;
|
|
154
|
+
requestedProvider?: string;
|
|
155
|
+
custom?: Record<string, unknown>;
|
|
156
|
+
};
|
|
157
|
+
closeAllMemorySearchManagers?: () => Promise<void>;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
interface MemorySlotCapability {
|
|
161
|
+
promptBuilder?: MemoryPromptSectionBuilder;
|
|
162
|
+
runtime?: MemoryRuntime;
|
|
163
|
+
flushPlanResolver?: unknown;
|
|
164
|
+
publicArtifacts?: unknown;
|
|
165
|
+
}
|
|
166
|
+
|
|
29
167
|
interface OpenClawPluginApi {
|
|
30
168
|
pluginConfig?: unknown;
|
|
31
169
|
logger: {
|
|
@@ -36,6 +174,10 @@ interface OpenClawPluginApi {
|
|
|
36
174
|
factory: ToolFactory | (() => AnyAgentTool[]),
|
|
37
175
|
opts: { names: string[] }
|
|
38
176
|
) => void;
|
|
177
|
+
registerMemoryCapability?: (capability: MemorySlotCapability) => void;
|
|
178
|
+
registerMemoryPromptSection?: (builder: MemoryPromptSectionBuilder) => void;
|
|
179
|
+
registerMemoryRuntime?: (runtime: MemoryRuntime) => void;
|
|
180
|
+
registerCapability?: (slot: string, capability: MemoryCapability) => void;
|
|
39
181
|
on: (hookName: string, handler: (...args: unknown[]) => unknown, opts?: { priority?: number }) => void;
|
|
40
182
|
}
|
|
41
183
|
|
|
@@ -60,6 +202,146 @@ interface AnyAgentTool {
|
|
|
60
202
|
execute: (_id: string, params: unknown) => Promise<unknown>;
|
|
61
203
|
}
|
|
62
204
|
|
|
205
|
+
class Mem9MemorySearchManager implements MemorySearchManager {
|
|
206
|
+
constructor(
|
|
207
|
+
private backend: MemoryBackend,
|
|
208
|
+
private apiUrl: string,
|
|
209
|
+
) {}
|
|
210
|
+
|
|
211
|
+
async search(
|
|
212
|
+
query: string,
|
|
213
|
+
opts?: { maxResults?: number; minScore?: number; sessionKey?: string },
|
|
214
|
+
): Promise<MemorySearchResult[]> {
|
|
215
|
+
void opts?.sessionKey;
|
|
216
|
+
const result = await this.backend.search({
|
|
217
|
+
q: query,
|
|
218
|
+
limit: opts?.maxResults,
|
|
219
|
+
});
|
|
220
|
+
return (result.data ?? [])
|
|
221
|
+
.map((memory, index) => {
|
|
222
|
+
const startLine = 1;
|
|
223
|
+
const endLine = countLines(memory.content);
|
|
224
|
+
const score =
|
|
225
|
+
typeof memory.score === "number"
|
|
226
|
+
? memory.score
|
|
227
|
+
: Math.max(0, 1 - index / Math.max(result.data.length, 1));
|
|
228
|
+
return {
|
|
229
|
+
path: buildMemoryPath(memory.id),
|
|
230
|
+
startLine,
|
|
231
|
+
endLine,
|
|
232
|
+
score,
|
|
233
|
+
snippet: normalizeSnippet(memory.content),
|
|
234
|
+
source: "memory" as const,
|
|
235
|
+
citation: `${buildMemoryPath(memory.id)}#L${startLine}`,
|
|
236
|
+
};
|
|
237
|
+
})
|
|
238
|
+
.filter((entry) => opts?.minScore == null || entry.score >= opts.minScore);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
async readFile(params: {
|
|
242
|
+
relPath: string;
|
|
243
|
+
from?: number;
|
|
244
|
+
lines?: number;
|
|
245
|
+
}): Promise<{ text: string; path: string }> {
|
|
246
|
+
const lookup = normalizeMemoryLookup(params.relPath);
|
|
247
|
+
const memory = await this.backend.get(lookup);
|
|
248
|
+
if (!memory) {
|
|
249
|
+
return { text: "", path: params.relPath };
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
const contentLines = memory.content.split(/\r?\n/);
|
|
253
|
+
const fromLine = Math.max(1, params.from ?? 1);
|
|
254
|
+
const lineCount = params.lines == null ? contentLines.length : Math.max(0, params.lines);
|
|
255
|
+
const sliced =
|
|
256
|
+
params.lines == null
|
|
257
|
+
? contentLines.slice(fromLine - 1)
|
|
258
|
+
: contentLines.slice(fromLine - 1, fromLine - 1 + lineCount);
|
|
259
|
+
|
|
260
|
+
return {
|
|
261
|
+
text: sliced.join("\n"),
|
|
262
|
+
path: buildMemoryPath(memory.id),
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
status(): MemoryProviderStatus {
|
|
267
|
+
return {
|
|
268
|
+
backend: "builtin",
|
|
269
|
+
provider: MEM9_MEMORY_PROVIDER,
|
|
270
|
+
requestedProvider: MEM9_MEMORY_PROVIDER,
|
|
271
|
+
custom: {
|
|
272
|
+
mode: "remote",
|
|
273
|
+
apiUrl: this.apiUrl,
|
|
274
|
+
},
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
async sync(): Promise<void> {}
|
|
279
|
+
|
|
280
|
+
async probeEmbeddingAvailability(): Promise<{ ok: boolean; error?: string }> {
|
|
281
|
+
try {
|
|
282
|
+
await this.backend.search({ q: "mem9-healthcheck", limit: 1 });
|
|
283
|
+
return { ok: true };
|
|
284
|
+
} catch (err) {
|
|
285
|
+
return {
|
|
286
|
+
ok: false,
|
|
287
|
+
error: err instanceof Error ? err.message : String(err),
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
async probeVectorAvailability(): Promise<boolean> {
|
|
293
|
+
const probe = await this.probeEmbeddingAvailability();
|
|
294
|
+
return probe.ok;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
async close(): Promise<void> {}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
function createMemoryRuntime(params: {
|
|
301
|
+
apiUrl: string;
|
|
302
|
+
fallbackAgentId: string;
|
|
303
|
+
createBackend: (agentId: string) => MemoryBackend;
|
|
304
|
+
}): MemoryRuntime {
|
|
305
|
+
const managers = new Map<string, Mem9MemorySearchManager>();
|
|
306
|
+
|
|
307
|
+
const getOrCreateManager = (agentId: string): Mem9MemorySearchManager => {
|
|
308
|
+
const resolvedAgentId = agentId.trim() || params.fallbackAgentId;
|
|
309
|
+
const existing = managers.get(resolvedAgentId);
|
|
310
|
+
if (existing) {
|
|
311
|
+
return existing;
|
|
312
|
+
}
|
|
313
|
+
const next = new Mem9MemorySearchManager(
|
|
314
|
+
params.createBackend(resolvedAgentId),
|
|
315
|
+
params.apiUrl,
|
|
316
|
+
);
|
|
317
|
+
managers.set(resolvedAgentId, next);
|
|
318
|
+
return next;
|
|
319
|
+
};
|
|
320
|
+
|
|
321
|
+
return {
|
|
322
|
+
async getMemorySearchManager({ agentId }) {
|
|
323
|
+
return { manager: getOrCreateManager(agentId) };
|
|
324
|
+
},
|
|
325
|
+
resolveMemoryBackendConfig() {
|
|
326
|
+
return {
|
|
327
|
+
backend: "builtin",
|
|
328
|
+
provider: MEM9_MEMORY_PROVIDER,
|
|
329
|
+
requestedProvider: MEM9_MEMORY_PROVIDER,
|
|
330
|
+
custom: {
|
|
331
|
+
mode: "remote",
|
|
332
|
+
apiUrl: params.apiUrl,
|
|
333
|
+
},
|
|
334
|
+
};
|
|
335
|
+
},
|
|
336
|
+
async closeAllMemorySearchManagers() {
|
|
337
|
+
for (const manager of managers.values()) {
|
|
338
|
+
await manager.close?.();
|
|
339
|
+
}
|
|
340
|
+
managers.clear();
|
|
341
|
+
},
|
|
342
|
+
};
|
|
343
|
+
}
|
|
344
|
+
|
|
63
345
|
function buildTools(backend: MemoryBackend): AnyAgentTool[] {
|
|
64
346
|
return [
|
|
65
347
|
{
|
|
@@ -113,6 +395,10 @@ function buildTools(backend: MemoryBackend): AnyAgentTool[] {
|
|
|
113
395
|
type: "object",
|
|
114
396
|
properties: {
|
|
115
397
|
q: { type: "string", description: "Search query" },
|
|
398
|
+
query: {
|
|
399
|
+
type: "string",
|
|
400
|
+
description: "Search query alias for hosts that expect `query` instead of `q`",
|
|
401
|
+
},
|
|
116
402
|
tags: {
|
|
117
403
|
type: "string",
|
|
118
404
|
description: "Comma-separated tags to filter by (AND)",
|
|
@@ -123,14 +409,28 @@ function buildTools(backend: MemoryBackend): AnyAgentTool[] {
|
|
|
123
409
|
description: "Max results (default 20, max 200)",
|
|
124
410
|
},
|
|
125
411
|
offset: { type: "number", description: "Pagination offset" },
|
|
412
|
+
memory_type: {
|
|
413
|
+
type: "string",
|
|
414
|
+
description: "Comma-separated memory types to filter by (e.g. insight,pinned)",
|
|
415
|
+
},
|
|
126
416
|
},
|
|
127
417
|
required: [],
|
|
128
418
|
},
|
|
129
419
|
async execute(_id: string, params: unknown) {
|
|
130
420
|
try {
|
|
131
|
-
const input = (params ?? {}) as SearchInput;
|
|
421
|
+
const input = { ...((params ?? {}) as SearchInput) };
|
|
422
|
+
if (!input.q && typeof (params as { query?: unknown } | null)?.query === "string") {
|
|
423
|
+
input.q = (params as { query: string }).query;
|
|
424
|
+
}
|
|
132
425
|
const result = await backend.search(input);
|
|
133
|
-
return jsonResult({
|
|
426
|
+
return jsonResult({
|
|
427
|
+
ok: true,
|
|
428
|
+
...result,
|
|
429
|
+
data: (result.data ?? []).map((memory) => ({
|
|
430
|
+
...memory,
|
|
431
|
+
path: buildMemoryPath(memory.id),
|
|
432
|
+
})),
|
|
433
|
+
});
|
|
134
434
|
} catch (err) {
|
|
135
435
|
return jsonResult({
|
|
136
436
|
ok: false,
|
|
@@ -148,16 +448,31 @@ function buildTools(backend: MemoryBackend): AnyAgentTool[] {
|
|
|
148
448
|
type: "object",
|
|
149
449
|
properties: {
|
|
150
450
|
id: { type: "string", description: "Memory id (UUID)" },
|
|
451
|
+
path: {
|
|
452
|
+
type: "string",
|
|
453
|
+
description: "Memory lookup path alias, e.g. mem9/<id>",
|
|
454
|
+
},
|
|
151
455
|
},
|
|
152
|
-
required: [
|
|
456
|
+
required: [],
|
|
153
457
|
},
|
|
154
458
|
async execute(_id: string, params: unknown) {
|
|
155
459
|
try {
|
|
156
|
-
const
|
|
460
|
+
const raw = params as { id?: string; path?: string };
|
|
461
|
+
const lookup = typeof raw.id === "string" ? raw.id : raw.path;
|
|
462
|
+
if (!lookup) {
|
|
463
|
+
return jsonResult({ ok: false, error: "memory id or path is required" });
|
|
464
|
+
}
|
|
465
|
+
const id = normalizeMemoryLookup(lookup);
|
|
157
466
|
const result = await backend.get(id);
|
|
158
467
|
if (!result)
|
|
159
468
|
return jsonResult({ ok: false, error: "memory not found" });
|
|
160
|
-
return jsonResult({
|
|
469
|
+
return jsonResult({
|
|
470
|
+
ok: true,
|
|
471
|
+
data: {
|
|
472
|
+
...result,
|
|
473
|
+
path: buildMemoryPath(result.id),
|
|
474
|
+
},
|
|
475
|
+
});
|
|
161
476
|
} catch (err) {
|
|
162
477
|
return jsonResult({
|
|
163
478
|
ok: false,
|
|
@@ -237,8 +552,10 @@ const mnemoPlugin = {
|
|
|
237
552
|
name: "Mnemo Memory",
|
|
238
553
|
description:
|
|
239
554
|
"AI agent memory — server mode (mnemo-server) with hybrid vector + keyword search.",
|
|
555
|
+
// Static hint for hosts that inspect entry metadata before runtime registration.
|
|
556
|
+
capabilities: ["memory"],
|
|
240
557
|
|
|
241
|
-
|
|
558
|
+
register(api: OpenClawPluginApi) {
|
|
242
559
|
const cfg = (api.pluginConfig ?? {}) as PluginConfig;
|
|
243
560
|
const effectiveApiUrl = cfg.apiUrl ?? DEFAULT_API_URL;
|
|
244
561
|
if (!cfg.apiUrl) {
|
|
@@ -272,25 +589,66 @@ const mnemoPlugin = {
|
|
|
272
589
|
|
|
273
590
|
const hookAgentId = cfg.agentName ?? "agent";
|
|
274
591
|
|
|
275
|
-
const
|
|
276
|
-
|
|
277
|
-
const backend = new LazyServerBackend(
|
|
592
|
+
const createBackend = (agentId: string): MemoryBackend =>
|
|
593
|
+
new LazyServerBackend(
|
|
278
594
|
effectiveApiUrl,
|
|
279
595
|
() => resolveAPIKey(agentId),
|
|
280
596
|
agentId,
|
|
281
597
|
);
|
|
598
|
+
|
|
599
|
+
const factory: ToolFactory = (ctx: ToolContext) => {
|
|
600
|
+
const agentId = ctx.agentId ?? cfg.agentName ?? "agent";
|
|
601
|
+
const backend = createBackend(agentId);
|
|
282
602
|
return buildTools(backend);
|
|
283
603
|
};
|
|
284
604
|
|
|
285
605
|
api.registerTool(factory, { names: toolNames });
|
|
286
606
|
|
|
287
|
-
//
|
|
288
|
-
|
|
289
|
-
const
|
|
290
|
-
effectiveApiUrl,
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
);
|
|
607
|
+
// Shared lazy backend for hooks and capability registration.
|
|
608
|
+
const hookBackend = createBackend(hookAgentId);
|
|
609
|
+
const memoryRuntime = createMemoryRuntime({
|
|
610
|
+
apiUrl: effectiveApiUrl,
|
|
611
|
+
fallbackAgentId: hookAgentId,
|
|
612
|
+
createBackend,
|
|
613
|
+
});
|
|
614
|
+
|
|
615
|
+
// OpenClaw's memory slot API has evolved across versions:
|
|
616
|
+
// - current hosts prefer registerMemoryCapability({ promptBuilder, runtime })
|
|
617
|
+
// - 2026.4.2 uses registerMemoryPromptSection/registerMemoryRuntime
|
|
618
|
+
// - older compatibility shims may still expose registerCapability("memory", ...)
|
|
619
|
+
if (typeof api.registerMemoryCapability === "function") {
|
|
620
|
+
api.registerMemoryCapability({
|
|
621
|
+
promptBuilder: buildPromptSection,
|
|
622
|
+
runtime: memoryRuntime,
|
|
623
|
+
});
|
|
624
|
+
} else {
|
|
625
|
+
if (typeof api.registerMemoryPromptSection === "function") {
|
|
626
|
+
api.registerMemoryPromptSection(buildPromptSection);
|
|
627
|
+
}
|
|
628
|
+
if (typeof api.registerMemoryRuntime === "function") {
|
|
629
|
+
api.registerMemoryRuntime(memoryRuntime);
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
if (
|
|
634
|
+
typeof api.registerMemoryCapability !== "function" &&
|
|
635
|
+
typeof api.registerMemoryRuntime !== "function" &&
|
|
636
|
+
typeof api.registerCapability === "function"
|
|
637
|
+
) {
|
|
638
|
+
api.registerCapability("memory", {
|
|
639
|
+
search: async (query, opts) => {
|
|
640
|
+
const result = await hookBackend.search({ q: query, limit: opts?.limit });
|
|
641
|
+
return { data: result.data, total: result.total };
|
|
642
|
+
},
|
|
643
|
+
store: async (content, opts) => {
|
|
644
|
+
return hookBackend.store({ content, tags: opts?.tags, source: opts?.source });
|
|
645
|
+
},
|
|
646
|
+
get: (id) => hookBackend.get(id),
|
|
647
|
+
remove: (id) => hookBackend.remove(id),
|
|
648
|
+
});
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
// Register hooks with lazy backend for lifecycle memory management.
|
|
294
652
|
registerHooks(api, hookBackend, api.logger, {
|
|
295
653
|
maxIngestBytes: cfg.maxIngestBytes,
|
|
296
654
|
fallbackAgentId: hookAgentId,
|
package/package.json
CHANGED
package/server-backend.ts
CHANGED
|
@@ -67,6 +67,7 @@ export class ServerBackend implements MemoryBackend {
|
|
|
67
67
|
if (input.source) params.set("source", input.source);
|
|
68
68
|
if (input.limit != null) params.set("limit", String(input.limit));
|
|
69
69
|
if (input.offset != null) params.set("offset", String(input.offset));
|
|
70
|
+
if (input.memory_type) params.set("memory_type", input.memory_type);
|
|
70
71
|
|
|
71
72
|
const qs = params.toString();
|
|
72
73
|
const raw = await this.request<{
|
package/types.ts
CHANGED
|
@@ -31,6 +31,8 @@ export interface Memory {
|
|
|
31
31
|
state?: string;
|
|
32
32
|
agent_id?: string;
|
|
33
33
|
session_id?: string;
|
|
34
|
+
|
|
35
|
+
relative_age?: string;
|
|
34
36
|
}
|
|
35
37
|
|
|
36
38
|
export interface SearchResult {
|
|
@@ -60,6 +62,7 @@ export interface SearchInput {
|
|
|
60
62
|
source?: string;
|
|
61
63
|
limit?: number;
|
|
62
64
|
offset?: number;
|
|
65
|
+
memory_type?: string;
|
|
63
66
|
}
|
|
64
67
|
|
|
65
68
|
export interface IngestMessage {
|