mr-memory 2.9.13 → 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.
Files changed (2) hide show
  1. package/index.ts +76 -5
  2. package/package.json +1 -1
package/index.ts CHANGED
@@ -48,6 +48,63 @@ function stripOldMemory(text: string): string {
48
48
  return text.replace(MEMORY_TAG_RE, "").replace(LEGACY_TAG_RE, "").trim();
49
49
  }
50
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
+
51
108
  // Workspace files OpenClaw loads into the system prompt
52
109
  const WORKSPACE_FILES = [
53
110
  "IDENTITY.md", "USER.md", "MEMORY.md", "HEARTBEAT.md",
@@ -413,11 +470,17 @@ const memoryRouterPlugin = {
413
470
 
414
471
  const toStore: Array<{ role: string; content: string }> = [];
415
472
 
416
- // Add the user message
473
+ // Add the user message (sanitized)
417
474
  if (lastUserIdx >= 0) {
418
475
  const userMsg = msgs[lastUserIdx] as { content?: unknown };
419
- const userText = extractText(userMsg.content);
420
- if (userText) toStore.push({ role: "user", content: stripMediaReferences(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
+ }
421
484
  }
422
485
 
423
486
  // Collect ALL assistant messages after the last user message
@@ -431,10 +494,18 @@ const memoryRouterPlugin = {
431
494
  }
432
495
  if (assistantParts.length > 0) {
433
496
  // Strip media refs so image paths don't get stored and re-injected
434
- toStore.push({ role: "assistant", content: stripMediaReferences(assistantParts.join("\n\n")) });
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
+ }
435
502
  }
436
503
 
437
- if (toStore.length === 0) return;
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
+ }
438
509
 
439
510
  // Await the fetch so OpenClaw's runVoidHook keeps the event loop alive.
440
511
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mr-memory",
3
- "version": "2.9.13",
3
+ "version": "2.10.0",
4
4
  "description": "MemoryRouter persistent memory plugin for OpenClaw — your AI remembers every conversation",
5
5
  "type": "module",
6
6
  "files": [