@memtensor/memos-cloud-openclaw-plugin 0.1.10-beta.4 → 0.1.11-beta.0
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 +140 -5
- package/README_ZH.md +140 -5
- package/clawdbot.plugin.json +156 -2
- package/index.js +73 -24
- package/lib/memos-cloud-api.js +76 -7
- package/moltbot.plugin.json +156 -2
- package/openclaw.plugin.json +156 -2
- package/package.json +1 -1
- package/test/direct-session-user-id.test.mjs +94 -0
- package/test/query-strip.test.mjs +41 -0
package/index.js
CHANGED
|
@@ -5,6 +5,8 @@ import {
|
|
|
5
5
|
extractResultData,
|
|
6
6
|
extractText,
|
|
7
7
|
formatRecallHookResult,
|
|
8
|
+
isAgentAllowed,
|
|
9
|
+
resolveAgentConfig,
|
|
8
10
|
searchMemory,
|
|
9
11
|
stripOpenClawInjectedPrefix,
|
|
10
12
|
} from "./lib/memos-cloud-api.js";
|
|
@@ -53,6 +55,21 @@ function getEffectiveAgentId(cfg, ctx) {
|
|
|
53
55
|
return agentId === "main" ? undefined : agentId;
|
|
54
56
|
}
|
|
55
57
|
|
|
58
|
+
export function extractDirectSessionUserId(sessionKey) {
|
|
59
|
+
if (!sessionKey || typeof sessionKey !== "string") return "";
|
|
60
|
+
const parts = sessionKey.split(":");
|
|
61
|
+
const directIndex = parts.lastIndexOf("direct");
|
|
62
|
+
if (directIndex === -1) return "";
|
|
63
|
+
return parts[directIndex + 1] || "";
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export function resolveMemosUserId(cfg, ctx) {
|
|
67
|
+
const fallback = cfg?.userId || "openclaw-user";
|
|
68
|
+
if (!cfg?.useDirectSessionUserId) return fallback;
|
|
69
|
+
const directUserId = extractDirectSessionUserId(ctx?.sessionKey);
|
|
70
|
+
return directUserId || fallback;
|
|
71
|
+
}
|
|
72
|
+
|
|
56
73
|
function resolveConversationId(cfg, ctx) {
|
|
57
74
|
if (cfg.conversationId) return cfg.conversationId;
|
|
58
75
|
// TODO: consider binding conversation_id directly to OpenClaw sessionId (prefer ctx.sessionId).
|
|
@@ -65,7 +82,7 @@ function resolveConversationId(cfg, ctx) {
|
|
|
65
82
|
return `${prefix}openclaw-${Date.now()}${dynamicSuffix}${suffix}`;
|
|
66
83
|
}
|
|
67
84
|
|
|
68
|
-
function buildSearchPayload(cfg, prompt, ctx) {
|
|
85
|
+
export function buildSearchPayload(cfg, prompt, ctx) {
|
|
69
86
|
const cleanPrompt = stripOpenClawInjectedPrefix(prompt);
|
|
70
87
|
const queryRaw = `${cfg.queryPrefix || ""}${cleanPrompt}`;
|
|
71
88
|
const query =
|
|
@@ -74,7 +91,7 @@ function buildSearchPayload(cfg, prompt, ctx) {
|
|
|
74
91
|
: queryRaw;
|
|
75
92
|
|
|
76
93
|
const payload = {
|
|
77
|
-
user_id: cfg
|
|
94
|
+
user_id: resolveMemosUserId(cfg, ctx),
|
|
78
95
|
query,
|
|
79
96
|
source: MEMOS_SOURCE,
|
|
80
97
|
};
|
|
@@ -113,9 +130,9 @@ function buildSearchPayload(cfg, prompt, ctx) {
|
|
|
113
130
|
return payload;
|
|
114
131
|
}
|
|
115
132
|
|
|
116
|
-
function buildAddMessagePayload(cfg, messages, ctx) {
|
|
133
|
+
export function buildAddMessagePayload(cfg, messages, ctx) {
|
|
117
134
|
const payload = {
|
|
118
|
-
user_id: cfg
|
|
135
|
+
user_id: resolveMemosUserId(cfg, ctx),
|
|
119
136
|
conversation_id: resolveConversationId(cfg, ctx),
|
|
120
137
|
messages,
|
|
121
138
|
source: MEMOS_SOURCE,
|
|
@@ -326,20 +343,20 @@ async function callRecallFilterModel(cfg, userPrompt, candidatePayload) {
|
|
|
326
343
|
};
|
|
327
344
|
|
|
328
345
|
let lastError;
|
|
329
|
-
const retries = Number.isFinite(cfg.recallFilterRetries) ? Math.max(0, cfg.recallFilterRetries) :
|
|
330
|
-
const timeoutMs = Number.isFinite(cfg.recallFilterTimeoutMs) ? Math.max(1000, cfg.recallFilterTimeoutMs) :
|
|
346
|
+
const retries = Number.isFinite(cfg.recallFilterRetries) ? Math.max(0, cfg.recallFilterRetries) : 1;
|
|
347
|
+
const timeoutMs = Number.isFinite(cfg.recallFilterTimeoutMs) ? Math.max(1000, cfg.recallFilterTimeoutMs) : 30000;
|
|
331
348
|
|
|
332
349
|
for (let attempt = 0; attempt <= retries; attempt += 1) {
|
|
350
|
+
let timeoutId;
|
|
333
351
|
try {
|
|
334
352
|
const controller = new AbortController();
|
|
335
|
-
|
|
353
|
+
timeoutId = setTimeout(() => controller.abort(), timeoutMs);
|
|
336
354
|
const res = await fetch(`${cfg.recallFilterBaseUrl}/chat/completions`, {
|
|
337
355
|
method: "POST",
|
|
338
356
|
headers,
|
|
339
357
|
body: JSON.stringify(body),
|
|
340
358
|
signal: controller.signal,
|
|
341
359
|
});
|
|
342
|
-
clearTimeout(timeoutId);
|
|
343
360
|
if (!res.ok) {
|
|
344
361
|
throw new Error(`HTTP ${res.status}`);
|
|
345
362
|
}
|
|
@@ -351,10 +368,17 @@ async function callRecallFilterModel(cfg, userPrompt, candidatePayload) {
|
|
|
351
368
|
}
|
|
352
369
|
return parsed;
|
|
353
370
|
} catch (err) {
|
|
354
|
-
|
|
371
|
+
const isAbort = err?.name === "AbortError" || /aborted/i.test(String(err?.message ?? err));
|
|
372
|
+
lastError = isAbort
|
|
373
|
+
? new Error(
|
|
374
|
+
`timed out after ${timeoutMs}ms (raise recallFilterTimeoutMs; local LLMs often need 30s+ on cold start)`,
|
|
375
|
+
)
|
|
376
|
+
: err;
|
|
355
377
|
if (attempt < retries) {
|
|
356
378
|
await sleep(120 * (attempt + 1));
|
|
357
379
|
}
|
|
380
|
+
} finally {
|
|
381
|
+
if (timeoutId !== undefined) clearTimeout(timeoutId);
|
|
358
382
|
}
|
|
359
383
|
}
|
|
360
384
|
throw lastError;
|
|
@@ -375,7 +399,13 @@ async function maybeFilterRecallData(cfg, data, userPrompt, log) {
|
|
|
375
399
|
|
|
376
400
|
try {
|
|
377
401
|
const decision = await callRecallFilterModel(cfg, userPrompt, lists.candidatePayload);
|
|
378
|
-
|
|
402
|
+
const filtered = applyRecallDecision(data, decision, lists);
|
|
403
|
+
log.info?.(
|
|
404
|
+
`[memos-cloud] recall filter applied: memory ${lists.memoryList.length}->${filtered.memory_detail_list?.length ?? 0}, ` +
|
|
405
|
+
`preference ${lists.preferenceList.length}->${filtered.preference_detail_list?.length ?? 0}, ` +
|
|
406
|
+
`tool_memory ${lists.toolList.length}->${filtered.tool_memory_detail_list?.length ?? 0}`,
|
|
407
|
+
);
|
|
408
|
+
return filtered;
|
|
379
409
|
} catch (err) {
|
|
380
410
|
log.warn?.(`[memos-cloud] recall filter failed: ${String(err)}`);
|
|
381
411
|
return cfg.recallFilterFailOpen ? data : { ...data, memory_detail_list: [], preference_detail_list: [], tool_memory_detail_list: [] };
|
|
@@ -400,6 +430,15 @@ export default {
|
|
|
400
430
|
log.warn?.(`[memos-cloud] No .env found in ${searchPaths}; falling back to process env or plugin config.`);
|
|
401
431
|
}
|
|
402
432
|
|
|
433
|
+
if (cfg.multiAgentMode && cfg.allowedAgents?.length > 0) {
|
|
434
|
+
log.info?.(`[memos-cloud] Multi-agent mode enabled. Allowed agents: [${cfg.allowedAgents.join(", ")}]`);
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
const overrideAgentIds = Object.keys(cfg._agentOverrides || {});
|
|
438
|
+
if (overrideAgentIds.length > 0) {
|
|
439
|
+
log.info?.(`[memos-cloud] Per-agent overrides configured for: [${overrideAgentIds.join(", ")}]`);
|
|
440
|
+
}
|
|
441
|
+
|
|
403
442
|
if (cfg.conversationSuffixMode === "counter" && cfg.resetOnNew) {
|
|
404
443
|
if (api.config?.hooks?.internal?.enabled !== true) {
|
|
405
444
|
log.warn?.("[memos-cloud] command:new hook requires hooks.internal.enabled = true");
|
|
@@ -419,24 +458,29 @@ export default {
|
|
|
419
458
|
}
|
|
420
459
|
|
|
421
460
|
api.on("before_agent_start", async (event, ctx) => {
|
|
422
|
-
if (!cfg
|
|
461
|
+
if (!isAgentAllowed(cfg, ctx)) {
|
|
462
|
+
log.info?.(`[memos-cloud] recall skipped: agent "${ctx?.agentId}" not in allowedAgents [${cfg.allowedAgents?.join(", ")}]`);
|
|
463
|
+
return;
|
|
464
|
+
}
|
|
465
|
+
const agentCfg = resolveAgentConfig(cfg, ctx?.agentId);
|
|
466
|
+
if (!agentCfg.recallEnabled) return;
|
|
423
467
|
const userPrompt = stripOpenClawInjectedPrefix(event?.prompt || "");
|
|
424
468
|
if (!userPrompt || userPrompt.length < 3) return;
|
|
425
|
-
if (!
|
|
469
|
+
if (!agentCfg.apiKey) {
|
|
426
470
|
warnMissingApiKey(log, "recall");
|
|
427
471
|
return;
|
|
428
472
|
}
|
|
429
473
|
|
|
430
474
|
try {
|
|
431
|
-
const payload = buildSearchPayload(
|
|
432
|
-
const result = await searchMemory(
|
|
475
|
+
const payload = buildSearchPayload(agentCfg, userPrompt, ctx);
|
|
476
|
+
const result = await searchMemory(agentCfg, payload);
|
|
433
477
|
const resultData = extractResultData(result);
|
|
434
478
|
if (!resultData) return;
|
|
435
|
-
const filteredData = await maybeFilterRecallData(
|
|
479
|
+
const filteredData = await maybeFilterRecallData(agentCfg, resultData, userPrompt, log);
|
|
436
480
|
const hookResult = formatRecallHookResult({ data: filteredData }, {
|
|
437
481
|
wrapTagBlocks: true,
|
|
438
482
|
relativity: payload.relativity,
|
|
439
|
-
maxItemChars:
|
|
483
|
+
maxItemChars: agentCfg.maxItemChars,
|
|
440
484
|
});
|
|
441
485
|
if (!hookResult.appendSystemContext && !hookResult.prependContext) return;
|
|
442
486
|
|
|
@@ -447,29 +491,34 @@ export default {
|
|
|
447
491
|
});
|
|
448
492
|
|
|
449
493
|
api.on("agent_end", async (event, ctx) => {
|
|
450
|
-
if (!cfg
|
|
494
|
+
if (!isAgentAllowed(cfg, ctx)) {
|
|
495
|
+
log.info?.(`[memos-cloud] add skipped: agent "${ctx?.agentId}" not in allowedAgents [${cfg.allowedAgents?.join(", ")}]`);
|
|
496
|
+
return;
|
|
497
|
+
}
|
|
498
|
+
const agentCfg = resolveAgentConfig(cfg, ctx?.agentId);
|
|
499
|
+
if (!agentCfg.addEnabled) return;
|
|
451
500
|
if (!event?.success || !event?.messages?.length) return;
|
|
452
|
-
if (!
|
|
501
|
+
if (!agentCfg.apiKey) {
|
|
453
502
|
warnMissingApiKey(log, "add");
|
|
454
503
|
return;
|
|
455
504
|
}
|
|
456
505
|
|
|
457
506
|
const now = Date.now();
|
|
458
|
-
if (
|
|
507
|
+
if (agentCfg.throttleMs && now - lastCaptureTime < agentCfg.throttleMs) {
|
|
459
508
|
return;
|
|
460
509
|
}
|
|
461
510
|
lastCaptureTime = now;
|
|
462
511
|
|
|
463
512
|
try {
|
|
464
513
|
const messages =
|
|
465
|
-
|
|
466
|
-
? pickFullSessionMessages(event.messages,
|
|
467
|
-
: pickLastTurnMessages(event.messages,
|
|
514
|
+
agentCfg.captureStrategy === "full_session"
|
|
515
|
+
? pickFullSessionMessages(event.messages, agentCfg)
|
|
516
|
+
: pickLastTurnMessages(event.messages, agentCfg);
|
|
468
517
|
|
|
469
518
|
if (!messages.length) return;
|
|
470
519
|
|
|
471
|
-
const payload = buildAddMessagePayload(
|
|
472
|
-
await addMessage(
|
|
520
|
+
const payload = buildAddMessagePayload(agentCfg, messages, ctx);
|
|
521
|
+
await addMessage(agentCfg, payload);
|
|
473
522
|
} catch (err) {
|
|
474
523
|
log.warn?.(`[memos-cloud] add failed: ${String(err)}`);
|
|
475
524
|
}
|
package/lib/memos-cloud-api.js
CHANGED
|
@@ -157,6 +157,28 @@ function parseNumber(value, fallback) {
|
|
|
157
157
|
return Number.isFinite(n) ? n : fallback;
|
|
158
158
|
}
|
|
159
159
|
|
|
160
|
+
function parseStringArray(value) {
|
|
161
|
+
if (!value) return [];
|
|
162
|
+
if (Array.isArray(value)) return value.map((v) => String(v).trim()).filter(Boolean);
|
|
163
|
+
return String(value)
|
|
164
|
+
.split(",")
|
|
165
|
+
.map((s) => s.trim().replace(/^["']|["']$/g, ""))
|
|
166
|
+
.filter(Boolean);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function parseJsonObject(value) {
|
|
170
|
+
if (!value || typeof value !== "string") return null;
|
|
171
|
+
try {
|
|
172
|
+
const parsed = JSON.parse(value);
|
|
173
|
+
if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)) {
|
|
174
|
+
return parsed;
|
|
175
|
+
}
|
|
176
|
+
} catch {
|
|
177
|
+
// ignore parse error
|
|
178
|
+
}
|
|
179
|
+
return null;
|
|
180
|
+
}
|
|
181
|
+
|
|
160
182
|
export function buildConfig(pluginConfig = {}) {
|
|
161
183
|
const cfg = pluginConfig ?? {};
|
|
162
184
|
|
|
@@ -184,6 +206,10 @@ export function buildConfig(pluginConfig = {}) {
|
|
|
184
206
|
parseBool(loadEnvVar("MEMOS_MULTI_AGENT_MODE"), false),
|
|
185
207
|
);
|
|
186
208
|
|
|
209
|
+
const allowedAgents = parseStringArray(
|
|
210
|
+
cfg.allowedAgents ?? loadEnvVar("MEMOS_ALLOWED_AGENTS"),
|
|
211
|
+
);
|
|
212
|
+
|
|
187
213
|
const recallFilterEnabled = parseBool(
|
|
188
214
|
cfg.recallFilterEnabled,
|
|
189
215
|
parseBool(loadEnvVar("MEMOS_RECALL_FILTER_ENABLED"), false),
|
|
@@ -200,6 +226,10 @@ export function buildConfig(pluginConfig = {}) {
|
|
|
200
226
|
? parseBool(loadEnvVar("MEMOS_INCLUDE_ASSISTANT"), true)
|
|
201
227
|
: cfg.includeAssistant !== false;
|
|
202
228
|
const maxMessageChars = cfg.maxMessageChars ?? parseNumber(loadEnvVar("MEMOS_MAX_MESSAGE_CHARS"), 20000);
|
|
229
|
+
const useDirectSessionUserId = parseBool(
|
|
230
|
+
cfg.useDirectSessionUserId,
|
|
231
|
+
parseBool(loadEnvVar("MEMOS_USE_DIRECT_SESSION_USER_ID"), false),
|
|
232
|
+
);
|
|
203
233
|
|
|
204
234
|
return {
|
|
205
235
|
baseUrl: baseUrl.replace(/\/+$/, ""),
|
|
@@ -209,6 +239,7 @@ export function buildConfig(pluginConfig = {}) {
|
|
|
209
239
|
conversationIdPrefix,
|
|
210
240
|
conversationIdSuffix,
|
|
211
241
|
conversationSuffixMode,
|
|
242
|
+
useDirectSessionUserId,
|
|
212
243
|
recallGlobal,
|
|
213
244
|
resetOnNew,
|
|
214
245
|
envFileStatus: getEnvFileStatus(),
|
|
@@ -230,15 +261,16 @@ export function buildConfig(pluginConfig = {}) {
|
|
|
230
261
|
return v ? parseFloat(v) : 0.45;
|
|
231
262
|
})()),
|
|
232
263
|
filter: cfg.filter,
|
|
233
|
-
knowledgebaseIds: cfg.knowledgebaseIds ?? [],
|
|
234
|
-
tags: cfg.tags ?? ["openclaw"],
|
|
264
|
+
knowledgebaseIds: cfg.knowledgebaseIds ?? (loadEnvVar("MEMOS_KNOWLEDGEBASE_IDS") ? parseStringArray(loadEnvVar("MEMOS_KNOWLEDGEBASE_IDS")) : []),
|
|
265
|
+
tags: cfg.tags ?? (loadEnvVar("MEMOS_TAGS") ? parseStringArray(loadEnvVar("MEMOS_TAGS")) : ["openclaw"]),
|
|
235
266
|
info: cfg.info ?? {},
|
|
236
267
|
agentId: cfg.agentId,
|
|
237
268
|
appId: cfg.appId,
|
|
238
269
|
allowPublic: cfg.allowPublic ?? false,
|
|
239
|
-
allowKnowledgebaseIds: cfg.allowKnowledgebaseIds ?? [],
|
|
270
|
+
allowKnowledgebaseIds: cfg.allowKnowledgebaseIds ?? (loadEnvVar("MEMOS_ALLOW_KNOWLEDGEBASE_IDS") ? parseStringArray(loadEnvVar("MEMOS_ALLOW_KNOWLEDGEBASE_IDS")) : []),
|
|
240
271
|
asyncMode,
|
|
241
272
|
multiAgentMode,
|
|
273
|
+
allowedAgents,
|
|
242
274
|
recallFilterEnabled,
|
|
243
275
|
recallFilterBaseUrl:
|
|
244
276
|
(cfg.recallFilterBaseUrl ?? loadEnvVar("MEMOS_RECALL_FILTER_BASE_URL") ?? "").replace(/\/+$/, ""),
|
|
@@ -246,9 +278,9 @@ export function buildConfig(pluginConfig = {}) {
|
|
|
246
278
|
recallFilterModel: cfg.recallFilterModel ?? loadEnvVar("MEMOS_RECALL_FILTER_MODEL") ?? "",
|
|
247
279
|
recallFilterTimeoutMs: parseNumber(
|
|
248
280
|
cfg.recallFilterTimeoutMs ?? loadEnvVar("MEMOS_RECALL_FILTER_TIMEOUT_MS"),
|
|
249
|
-
|
|
281
|
+
30000,
|
|
250
282
|
),
|
|
251
|
-
recallFilterRetries: parseNumber(cfg.recallFilterRetries ?? loadEnvVar("MEMOS_RECALL_FILTER_RETRIES"),
|
|
283
|
+
recallFilterRetries: parseNumber(cfg.recallFilterRetries ?? loadEnvVar("MEMOS_RECALL_FILTER_RETRIES"), 1),
|
|
252
284
|
recallFilterCandidateLimit:
|
|
253
285
|
parseNumber(cfg.recallFilterCandidateLimit ?? loadEnvVar("MEMOS_RECALL_FILTER_CANDIDATE_LIMIT"), 30),
|
|
254
286
|
recallFilterMaxItemChars:
|
|
@@ -257,9 +289,35 @@ export function buildConfig(pluginConfig = {}) {
|
|
|
257
289
|
timeoutMs: cfg.timeoutMs ?? 5000,
|
|
258
290
|
retries: cfg.retries ?? 1,
|
|
259
291
|
throttleMs,
|
|
292
|
+
_agentOverrides: cfg.agentOverrides ?? parseJsonObject(loadEnvVar("MEMOS_AGENT_OVERRIDES")) ?? {},
|
|
260
293
|
};
|
|
261
294
|
}
|
|
262
295
|
|
|
296
|
+
const AGENT_OVERRIDABLE_KEYS = [
|
|
297
|
+
"knowledgebaseIds", "memoryLimitNumber", "preferenceLimitNumber",
|
|
298
|
+
"includePreference", "includeToolMemory", "toolMemoryLimitNumber",
|
|
299
|
+
"relativity",
|
|
300
|
+
"recallEnabled", "addEnabled", "captureStrategy", "queryPrefix",
|
|
301
|
+
"maxItemChars", "maxMessageChars", "includeAssistant",
|
|
302
|
+
"recallGlobal", "recallFilterEnabled", "recallFilterModel",
|
|
303
|
+
"recallFilterBaseUrl", "recallFilterApiKey",
|
|
304
|
+
"allowKnowledgebaseIds", "tags", "throttleMs",
|
|
305
|
+
];
|
|
306
|
+
|
|
307
|
+
export function resolveAgentConfig(baseCfg, agentId) {
|
|
308
|
+
if (!agentId || !baseCfg._agentOverrides) return baseCfg;
|
|
309
|
+
const overrides = baseCfg._agentOverrides[agentId];
|
|
310
|
+
if (!overrides || typeof overrides !== "object") return baseCfg;
|
|
311
|
+
|
|
312
|
+
const merged = { ...baseCfg };
|
|
313
|
+
for (const key of AGENT_OVERRIDABLE_KEYS) {
|
|
314
|
+
if (key in overrides) {
|
|
315
|
+
merged[key] = overrides[key];
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
return merged;
|
|
319
|
+
}
|
|
320
|
+
|
|
263
321
|
export async function callApi({ baseUrl, apiKey, timeoutMs = 5000, retries = 1 }, path, body) {
|
|
264
322
|
if (!apiKey) {
|
|
265
323
|
throw new Error("Missing MEMOS API key (Token auth)");
|
|
@@ -317,6 +375,13 @@ function sanitizeAddMessageEntry(entry) {
|
|
|
317
375
|
return { ...entry, content };
|
|
318
376
|
}
|
|
319
377
|
|
|
378
|
+
export function isAgentAllowed(cfg, ctx) {
|
|
379
|
+
if (!cfg.multiAgentMode) return true;
|
|
380
|
+
if (!cfg.allowedAgents || cfg.allowedAgents.length === 0) return true;
|
|
381
|
+
const agentId = ctx?.agentId || cfg.agentId || "main";
|
|
382
|
+
return cfg.allowedAgents.includes(agentId);
|
|
383
|
+
}
|
|
384
|
+
|
|
320
385
|
export async function searchMemory(cfg, payload) {
|
|
321
386
|
return callApi(cfg, "/search/memory", sanitizeSearchPayload(payload));
|
|
322
387
|
}
|
|
@@ -518,12 +583,16 @@ function sanitizeInlineText(text) {
|
|
|
518
583
|
return String(text).replace(/\r?\n+/g, " ").trim();
|
|
519
584
|
}
|
|
520
585
|
|
|
586
|
+
function resolveDisplayTime(item) {
|
|
587
|
+
return item?.update_time ?? item?.create_time;
|
|
588
|
+
}
|
|
589
|
+
|
|
521
590
|
function formatMemoryLine(item, text, options = {}) {
|
|
522
591
|
const cleaned = sanitizeInlineText(text);
|
|
523
592
|
if (!cleaned) return "";
|
|
524
593
|
const maxChars = options.maxItemChars;
|
|
525
594
|
const truncated = truncate(cleaned, maxChars);
|
|
526
|
-
const time = formatTime(item
|
|
595
|
+
const time = formatTime(resolveDisplayTime(item));
|
|
527
596
|
if (time) return ` -[${time}] ${truncated}`;
|
|
528
597
|
return ` - ${truncated}`;
|
|
529
598
|
}
|
|
@@ -533,7 +602,7 @@ function formatPreferenceLine(item, text, options = {}) {
|
|
|
533
602
|
if (!cleaned) return "";
|
|
534
603
|
const maxChars = options.maxItemChars;
|
|
535
604
|
const truncated = truncate(cleaned, maxChars);
|
|
536
|
-
const time = formatTime(item
|
|
605
|
+
const time = formatTime(resolveDisplayTime(item));
|
|
537
606
|
const type = normalizePreferenceType(item?.preference_type);
|
|
538
607
|
const typeLabel = type ? ` [${type}]` : "";
|
|
539
608
|
if (time) return ` -[${time}]${typeLabel} ${truncated}`;
|
package/moltbot.plugin.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"id": "memos-cloud-openclaw-plugin",
|
|
3
3
|
"name": "MemOS Cloud OpenClaw Plugin",
|
|
4
4
|
"description": "MemOS Cloud recall + add memory via lifecycle hooks",
|
|
5
|
-
"version": "0.1.
|
|
5
|
+
"version": "0.1.11-beta.0",
|
|
6
6
|
"kind": "lifecycle",
|
|
7
7
|
"main": "./index.js",
|
|
8
8
|
"configSchema": {
|
|
@@ -41,6 +41,11 @@
|
|
|
41
41
|
],
|
|
42
42
|
"default": "none"
|
|
43
43
|
},
|
|
44
|
+
"useDirectSessionUserId": {
|
|
45
|
+
"type": "boolean",
|
|
46
|
+
"description": "When enabled, direct-session keys like agent:main:<provider>:direct:<id> use the direct id as MemOS user_id instead of the default configured userId.",
|
|
47
|
+
"default": false
|
|
48
|
+
},
|
|
44
49
|
"resetOnNew": {
|
|
45
50
|
"type": "boolean",
|
|
46
51
|
"default": true
|
|
@@ -109,7 +114,47 @@
|
|
|
109
114
|
},
|
|
110
115
|
"filter": {
|
|
111
116
|
"type": "object",
|
|
112
|
-
"description": "MemOS search filter"
|
|
117
|
+
"description": "MemOS search filter",
|
|
118
|
+
"additionalProperties": true
|
|
119
|
+
},
|
|
120
|
+
"relativity": {
|
|
121
|
+
"type": "number",
|
|
122
|
+
"description": "Search relativity threshold",
|
|
123
|
+
"default": 0.45
|
|
124
|
+
},
|
|
125
|
+
"recallFilterEnabled": {
|
|
126
|
+
"type": "boolean",
|
|
127
|
+
"default": false
|
|
128
|
+
},
|
|
129
|
+
"recallFilterBaseUrl": {
|
|
130
|
+
"type": "string",
|
|
131
|
+
"description": "OpenAI-compatible API base URL for recall filtering"
|
|
132
|
+
},
|
|
133
|
+
"recallFilterApiKey": {
|
|
134
|
+
"type": "string"
|
|
135
|
+
},
|
|
136
|
+
"recallFilterModel": {
|
|
137
|
+
"type": "string"
|
|
138
|
+
},
|
|
139
|
+
"recallFilterTimeoutMs": {
|
|
140
|
+
"type": "integer",
|
|
141
|
+
"default": 30000
|
|
142
|
+
},
|
|
143
|
+
"recallFilterRetries": {
|
|
144
|
+
"type": "integer",
|
|
145
|
+
"default": 1
|
|
146
|
+
},
|
|
147
|
+
"recallFilterCandidateLimit": {
|
|
148
|
+
"type": "integer",
|
|
149
|
+
"default": 30
|
|
150
|
+
},
|
|
151
|
+
"recallFilterMaxItemChars": {
|
|
152
|
+
"type": "integer",
|
|
153
|
+
"default": 500
|
|
154
|
+
},
|
|
155
|
+
"recallFilterFailOpen": {
|
|
156
|
+
"type": "boolean",
|
|
157
|
+
"default": true
|
|
113
158
|
},
|
|
114
159
|
"knowledgebaseIds": {
|
|
115
160
|
"type": "array",
|
|
@@ -134,6 +179,13 @@
|
|
|
134
179
|
"type": "boolean",
|
|
135
180
|
"default": false
|
|
136
181
|
},
|
|
182
|
+
"allowedAgents": {
|
|
183
|
+
"type": "array",
|
|
184
|
+
"items": {
|
|
185
|
+
"type": "string"
|
|
186
|
+
},
|
|
187
|
+
"description": "When multiAgentMode is true, only these agent IDs will activate the memory plugin. Comma-separated in env var MEMOS_ALLOWED_AGENTS. Empty list means all agents are allowed."
|
|
188
|
+
},
|
|
137
189
|
"appId": {
|
|
138
190
|
"type": "string"
|
|
139
191
|
},
|
|
@@ -162,6 +214,108 @@
|
|
|
162
214
|
"throttleMs": {
|
|
163
215
|
"type": "integer",
|
|
164
216
|
"default": 0
|
|
217
|
+
},
|
|
218
|
+
"agentOverrides": {
|
|
219
|
+
"type": "object",
|
|
220
|
+
"description": "Per-agent config overrides. Keys are agent IDs, values override global defaults for that agent.",
|
|
221
|
+
"additionalProperties": {
|
|
222
|
+
"type": "object",
|
|
223
|
+
"properties": {
|
|
224
|
+
"knowledgebaseIds": {
|
|
225
|
+
"type": "array",
|
|
226
|
+
"items": {
|
|
227
|
+
"type": "string"
|
|
228
|
+
}
|
|
229
|
+
},
|
|
230
|
+
"memoryLimitNumber": {
|
|
231
|
+
"type": "integer"
|
|
232
|
+
},
|
|
233
|
+
"preferenceLimitNumber": {
|
|
234
|
+
"type": "integer"
|
|
235
|
+
},
|
|
236
|
+
"includePreference": {
|
|
237
|
+
"type": "boolean"
|
|
238
|
+
},
|
|
239
|
+
"includeToolMemory": {
|
|
240
|
+
"type": "boolean"
|
|
241
|
+
},
|
|
242
|
+
"toolMemoryLimitNumber": {
|
|
243
|
+
"type": "integer"
|
|
244
|
+
},
|
|
245
|
+
"includeSkill": {
|
|
246
|
+
"type": "boolean"
|
|
247
|
+
},
|
|
248
|
+
"skillLimitNumber": {
|
|
249
|
+
"type": "integer"
|
|
250
|
+
},
|
|
251
|
+
"relativity": {
|
|
252
|
+
"type": "number"
|
|
253
|
+
},
|
|
254
|
+
"filter": {
|
|
255
|
+
"type": "object",
|
|
256
|
+
"additionalProperties": true
|
|
257
|
+
},
|
|
258
|
+
"recallEnabled": {
|
|
259
|
+
"type": "boolean"
|
|
260
|
+
},
|
|
261
|
+
"addEnabled": {
|
|
262
|
+
"type": "boolean"
|
|
263
|
+
},
|
|
264
|
+
"captureStrategy": {
|
|
265
|
+
"type": "string",
|
|
266
|
+
"enum": [
|
|
267
|
+
"last_turn",
|
|
268
|
+
"full_session"
|
|
269
|
+
]
|
|
270
|
+
},
|
|
271
|
+
"queryPrefix": {
|
|
272
|
+
"type": "string"
|
|
273
|
+
},
|
|
274
|
+
"maxQueryChars": {
|
|
275
|
+
"type": "integer"
|
|
276
|
+
},
|
|
277
|
+
"maxItemChars": {
|
|
278
|
+
"type": "integer"
|
|
279
|
+
},
|
|
280
|
+
"maxMessageChars": {
|
|
281
|
+
"type": "integer"
|
|
282
|
+
},
|
|
283
|
+
"includeAssistant": {
|
|
284
|
+
"type": "boolean"
|
|
285
|
+
},
|
|
286
|
+
"recallGlobal": {
|
|
287
|
+
"type": "boolean"
|
|
288
|
+
},
|
|
289
|
+
"recallFilterEnabled": {
|
|
290
|
+
"type": "boolean"
|
|
291
|
+
},
|
|
292
|
+
"recallFilterModel": {
|
|
293
|
+
"type": "string"
|
|
294
|
+
},
|
|
295
|
+
"recallFilterBaseUrl": {
|
|
296
|
+
"type": "string"
|
|
297
|
+
},
|
|
298
|
+
"recallFilterApiKey": {
|
|
299
|
+
"type": "string"
|
|
300
|
+
},
|
|
301
|
+
"allowKnowledgebaseIds": {
|
|
302
|
+
"type": "array",
|
|
303
|
+
"items": {
|
|
304
|
+
"type": "string"
|
|
305
|
+
}
|
|
306
|
+
},
|
|
307
|
+
"tags": {
|
|
308
|
+
"type": "array",
|
|
309
|
+
"items": {
|
|
310
|
+
"type": "string"
|
|
311
|
+
}
|
|
312
|
+
},
|
|
313
|
+
"throttleMs": {
|
|
314
|
+
"type": "integer"
|
|
315
|
+
}
|
|
316
|
+
},
|
|
317
|
+
"additionalProperties": false
|
|
318
|
+
}
|
|
165
319
|
}
|
|
166
320
|
},
|
|
167
321
|
"additionalProperties": false
|