mr-memory 2.9.12 → 2.10.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/index.ts +90 -9
- package/package.json +1 -1
package/index.ts
CHANGED
|
@@ -15,11 +15,21 @@ import type { OpenClawPluginApi } from "openclaw/plugin-sdk";
|
|
|
15
15
|
|
|
16
16
|
const DEFAULT_ENDPOINT = "https://api.memoryrouter.ai";
|
|
17
17
|
|
|
18
|
-
/** Strip media-attached references
|
|
19
|
-
*
|
|
20
|
-
const MEDIA_ATTACHED_RE = /\[media attached
|
|
18
|
+
/** Strip media-attached references and OpenClaw media instruction text so
|
|
19
|
+
* old screenshots/photos/audio don't get stored and re-injected forever. */
|
|
20
|
+
const MEDIA_ATTACHED_RE = /\[media attached[^\]]*\]/gi;
|
|
21
|
+
const MEDIA_TAGS_RE = /<media:(?:audio|image|video|document)>/gi;
|
|
22
|
+
const MEDIA_INSTRUCTION_RE = /To send an image back,[\s\S]*?Keep caption in the text body\./gi;
|
|
23
|
+
const MEDIA_INBOUND_PATH_RE = /\/Users\/[^\s]*\/media\/inbound\/[^\s)\]"]*/gi;
|
|
24
|
+
const IMAGE_DATA_RE = /\[image data removed[^\]]*\]/gi;
|
|
21
25
|
function stripMediaReferences(text: string): string {
|
|
22
|
-
return text
|
|
26
|
+
return text
|
|
27
|
+
.replace(MEDIA_INSTRUCTION_RE, "")
|
|
28
|
+
.replace(MEDIA_ATTACHED_RE, "[media reference removed]")
|
|
29
|
+
.replace(MEDIA_TAGS_RE, "")
|
|
30
|
+
.replace(MEDIA_INBOUND_PATH_RE, "[media reference removed]")
|
|
31
|
+
.replace(IMAGE_DATA_RE, "")
|
|
32
|
+
.replace(/\n{3,}/g, "\n\n");
|
|
23
33
|
}
|
|
24
34
|
|
|
25
35
|
/** Wrap raw memory context in XML tags with a strong instruction */
|
|
@@ -38,6 +48,63 @@ function stripOldMemory(text: string): string {
|
|
|
38
48
|
return text.replace(MEMORY_TAG_RE, "").replace(LEGACY_TAG_RE, "").trim();
|
|
39
49
|
}
|
|
40
50
|
|
|
51
|
+
// ── Ingest sanitization: strip system noise so only real conversations get stored
|
|
52
|
+
// Patterns that indicate system-generated messages (not user conversations)
|
|
53
|
+
const SYSTEM_NOISE_PATTERNS = [
|
|
54
|
+
// Heartbeat prompts and responses
|
|
55
|
+
/^Read HEARTBEAT\.md if it exists/,
|
|
56
|
+
/^HEARTBEAT_OK\s*$/,
|
|
57
|
+
// Pre-compaction flush prompts
|
|
58
|
+
/^Pre-compaction memory flush\./,
|
|
59
|
+
// Post-compaction audit warnings
|
|
60
|
+
/⚠️ Post-Compaction Audit:/,
|
|
61
|
+
// Silent reply ack
|
|
62
|
+
/^NO_REPLY\s*$/,
|
|
63
|
+
// Agent abort notices
|
|
64
|
+
/^Note: The previous agent run was aborted/,
|
|
65
|
+
// Queued message headers
|
|
66
|
+
/^\[Queued messages while agent was busy\]/,
|
|
67
|
+
// Media reference placeholders
|
|
68
|
+
/^\[media reference removed\]\s*$/,
|
|
69
|
+
// System event timestamps (cron fires, exec failures, etc.)
|
|
70
|
+
/^System: \[\d{4}-\d{2}-\d{2}/,
|
|
71
|
+
/^\[\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2} [A-Z]{2,4}\] ⚠️/,
|
|
72
|
+
// Memory flush with date pattern
|
|
73
|
+
/^Store durable memories now/,
|
|
74
|
+
// Current time system lines (injected by OpenClaw into system events)
|
|
75
|
+
/^Current time: .+\(America\//,
|
|
76
|
+
];
|
|
77
|
+
|
|
78
|
+
/** Strip OpenClaw envelope metadata from user messages */
|
|
79
|
+
const ENVELOPE_METADATA_RE = /Conversation info \(untrusted metadata\):\s*```json\s*\{[^}]*\}\s*```\s*/g;
|
|
80
|
+
const SENDER_METADATA_RE = /Sender \(untrusted metadata\):\s*```json\s*\{[^}]*\}\s*```\s*/g;
|
|
81
|
+
const REPLY_CONTEXT_RE = /Replied message \(untrusted, for context\):\s*```json\s*\{[^}]*\}\s*```\s*/g;
|
|
82
|
+
const MEMORY_INSTRUCTION_RE = /The above are retrieved memories from past conversations[^\n]*\n*/g;
|
|
83
|
+
const IMPORTANT_MEMORY_RE = /IMPORTANT: The above (?:are retrieved memories|block contains retrieved memories)[^\n]*\n*/g;
|
|
84
|
+
|
|
85
|
+
/** Sanitize text before storing in memory — removes system noise and envelope metadata */
|
|
86
|
+
function sanitizeForIngest(text: string): string {
|
|
87
|
+
// 1. Strip our own memory injection wrapper (already exists as stripOldMemory)
|
|
88
|
+
let cleaned = stripOldMemory(text);
|
|
89
|
+
// 2. Strip envelope metadata blocks
|
|
90
|
+
cleaned = cleaned
|
|
91
|
+
.replace(ENVELOPE_METADATA_RE, "")
|
|
92
|
+
.replace(SENDER_METADATA_RE, "")
|
|
93
|
+
.replace(REPLY_CONTEXT_RE, "")
|
|
94
|
+
.replace(MEMORY_INSTRUCTION_RE, "")
|
|
95
|
+
.replace(IMPORTANT_MEMORY_RE, "");
|
|
96
|
+
// 3. Collapse excessive whitespace from removals
|
|
97
|
+
cleaned = cleaned.replace(/\n{3,}/g, "\n\n").trim();
|
|
98
|
+
return cleaned;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/** Check if a message is system noise that should not be stored */
|
|
102
|
+
function isSystemNoise(text: string): boolean {
|
|
103
|
+
const trimmed = text.trim();
|
|
104
|
+
if (!trimmed) return true;
|
|
105
|
+
return SYSTEM_NOISE_PATTERNS.some(pattern => pattern.test(trimmed));
|
|
106
|
+
}
|
|
107
|
+
|
|
41
108
|
// Workspace files OpenClaw loads into the system prompt
|
|
42
109
|
const WORKSPACE_FILES = [
|
|
43
110
|
"IDENTITY.md", "USER.md", "MEMORY.md", "HEARTBEAT.md",
|
|
@@ -403,11 +470,17 @@ const memoryRouterPlugin = {
|
|
|
403
470
|
|
|
404
471
|
const toStore: Array<{ role: string; content: string }> = [];
|
|
405
472
|
|
|
406
|
-
// Add the user message
|
|
473
|
+
// Add the user message (sanitized)
|
|
407
474
|
if (lastUserIdx >= 0) {
|
|
408
475
|
const userMsg = msgs[lastUserIdx] as { content?: unknown };
|
|
409
|
-
|
|
410
|
-
if (userText)
|
|
476
|
+
let userText = extractText(userMsg.content);
|
|
477
|
+
if (userText) {
|
|
478
|
+
// Sanitize: strip memory injections, envelope metadata, and system noise
|
|
479
|
+
userText = sanitizeForIngest(stripMediaReferences(userText));
|
|
480
|
+
if (userText && !isSystemNoise(userText)) {
|
|
481
|
+
toStore.push({ role: "user", content: userText });
|
|
482
|
+
}
|
|
483
|
+
}
|
|
411
484
|
}
|
|
412
485
|
|
|
413
486
|
// Collect ALL assistant messages after the last user message
|
|
@@ -421,10 +494,18 @@ const memoryRouterPlugin = {
|
|
|
421
494
|
}
|
|
422
495
|
if (assistantParts.length > 0) {
|
|
423
496
|
// Strip media refs so image paths don't get stored and re-injected
|
|
424
|
-
|
|
497
|
+
let assistantText = stripMediaReferences(assistantParts.join("\n\n"));
|
|
498
|
+
// Skip storing system noise responses (HEARTBEAT_OK, NO_REPLY, etc.)
|
|
499
|
+
if (!isSystemNoise(assistantText)) {
|
|
500
|
+
toStore.push({ role: "assistant", content: assistantText });
|
|
501
|
+
}
|
|
425
502
|
}
|
|
426
503
|
|
|
427
|
-
if (
|
|
504
|
+
// Skip if nothing meaningful to store (both messages were system noise)
|
|
505
|
+
if (toStore.length === 0) {
|
|
506
|
+
log("memoryrouter: skipped ingest — system noise filtered out");
|
|
507
|
+
return;
|
|
508
|
+
}
|
|
428
509
|
|
|
429
510
|
// Await the fetch so OpenClaw's runVoidHook keeps the event loop alive.
|
|
430
511
|
try {
|