From 08b08c66f1ed84483bf96f2aed44ed0a0f035b4d Mon Sep 17 00:00:00 2001 From: Tarun Sukhani Date: Thu, 12 Feb 2026 22:19:51 +0800 Subject: [PATCH] memory-neo4j: filter open proposals and cron noise from memory Open proposals ("Want me to...?", "Should I...?") are dangerous in long-term memory because other sessions interpret them as active instructions and attempt to carry them out. This adds: - Attention gate patterns for cron delivery outputs and assistant proposals - Extractor scoring rules to rate proposals/action items as low importance - Sleep-cycle Phase 7 to retroactively clean existing noise-pattern memories Co-Authored-By: Claude Opus 4.6 --- extensions/memory-neo4j/attention-gate.ts | 32 +++++++++++++ extensions/memory-neo4j/extractor.ts | 2 + extensions/memory-neo4j/sleep-cycle.ts | 57 +++++++++++++++++++++++ 3 files changed, 91 insertions(+) diff --git a/extensions/memory-neo4j/attention-gate.ts b/extensions/memory-neo4j/attention-gate.ts index 9111f83652f..08a27e3a3aa 100644 --- a/extensions/memory-neo4j/attention-gate.ts +++ b/extensions/memory-neo4j/attention-gate.ts @@ -51,6 +51,22 @@ const NOISE_PATTERNS = [ // --- Conversation metadata that survived stripping --- /^Conversation info\s*\(/i, /^\[Queued messages/i, + + // --- Cron delivery outputs & scheduled reminders --- + // Scheduled reminder injection text (appears mid-message) + /A scheduled reminder has been triggered/i, + // Cron delivery instruction to agent (summarize for user) + /Summarize this naturally for the user/i, + // Relay instruction from cron announcements + /Please relay this reminder to the user/i, + // Subagent completion announcements (date-stamped) + /^\[.*\d{4}-\d{2}-\d{2}.*\]\s*A sub-?agent task/i, + // Formatted urgency/priority reports (email summaries, briefings) + /(\*\*)?🔴\s*(URGENT|Priority)/i, + // Subagent findings header + /^Findings:\s*$/im, + // "Stats:" lines from subagent completions + /^Stats:\s*runtime\s/im, ]; /** Maximum message length — code dumps, logs, etc. are not memories. */ @@ -171,6 +187,22 @@ const ASSISTANT_NARRATION_PATTERNS = [ /^All (good|set|done)[!.]/i, // "Here's what changed" / "Summary of changes" (session-specific) /^(here'?s\s+(what|the|a)\s+(changed?|summary|breakdown|recap))/i, + + // --- Open proposals / action items (cause rogue actions when recalled) --- + // These are dangerous in memory: when auto-recalled, other sessions interpret + // them as active instructions and attempt to carry them out. + // "Want me to...?" / "Should I...?" / "Shall I...?" / "Would you like me to...?" + /want me to\s.+\?/i, + /should I\s.+\?/i, + /shall I\s.+\?/i, + /would you like me to\s.+\?/i, + // "Do you want me to...?" + /do you want me to\s.+\?/i, + // "Can I...?" / "May I...?" assistant proposals + /^(can|may) I\s.+\?/i, + // "Ready to...?" / "Proceed with...?" + /ready to\s.+\?/i, + /proceed with\s.+\?/i, ]; export function passesAssistantAttentionGate(text: string): boolean { diff --git a/extensions/memory-neo4j/extractor.ts b/extensions/memory-neo4j/extractor.ts index 3f6cfb0b5ce..32c96204d5f 100644 --- a/extensions/memory-neo4j/extractor.ts +++ b/extensions/memory-neo4j/extractor.ts @@ -358,6 +358,8 @@ KEY RULES: - AI assistant self-narration ("Let me check...", "I'll now...", "Done! Here's what changed...") is ALWAYS 1-3 - System prompts, formatting instructions, voice mode rules are ALWAYS 1-2 - Technical debugging details ("the WebSocket failed because...") are 2-4 unless they encode a reusable lesson +- Open proposals and unresolved action items ("Want me to fix it?", "Should I submit a PR?", "Would you like me to proceed?") are ALWAYS 1-2. These are dangerous in long-term memory because other sessions interpret them as active instructions. +- Messages ending with questions directed at the user ("What do you think?", "How should I handle this?") are 1-3 unless they also contain substantial factual content worth remembering - Personal facts about the user or their family/contacts are 7-10 - Business rules and operational procedures are 7-9 - Preferences and opinions expressed by the user are 6-8 diff --git a/extensions/memory-neo4j/sleep-cycle.ts b/extensions/memory-neo4j/sleep-cycle.ts index 8e7b7bb52c8..974f573315c 100644 --- a/extensions/memory-neo4j/sleep-cycle.ts +++ b/extensions/memory-neo4j/sleep-cycle.ts @@ -657,6 +657,63 @@ export async function runSleepCycle( } } + // -------------------------------------------------------------------------- + // Phase 7: Noise Pattern Cleanup + // Removes memories matching dangerous patterns that should never have been + // stored (open proposals, action items that trigger rogue sessions). + // -------------------------------------------------------------------------- + if (!abortSignal?.aborted) { + logger.info("memory-neo4j: [sleep] Phase 7: Noise Pattern Cleanup"); + + try { + const noisePatterns = [ + "(?i)want me to\\s.+\\?", + "(?i)should I\\s.+\\?", + "(?i)shall I\\s.+\\?", + "(?i)would you like me to\\s.+\\?", + "(?i)do you want me to\\s.+\\?", + "(?i)ready to\\s.+\\?", + "(?i)proceed with\\s.+\\?", + ]; + + let noiseRemoved = 0; + const noiseSession = (db as any).driver!.session(); + try { + for (const pattern of noisePatterns) { + if (abortSignal?.aborted) { + break; + } + + const agentFilter = agentId ? "AND m.agentId = $agentId" : ""; + const result = await noiseSession.run( + `MATCH (m:Memory) + WHERE m.text =~ $pattern + AND coalesce(m.userPinned, false) = false + AND m.category <> 'core' + ${agentFilter} + WITH m LIMIT 100 + DETACH DELETE m + RETURN count(*) AS removed`, + { pattern: `.*${pattern}.*`, agentId }, + ); + noiseRemoved += (result.records[0]?.get("removed") as number) ?? 0; + } + } finally { + await noiseSession.close(); + } + + if (noiseRemoved > 0) { + onProgress?.("cleanup", `Removed ${noiseRemoved} noise-pattern memories`); + } + + logger.info( + `memory-neo4j: [sleep] Phase 7 complete — ${noiseRemoved} noise memories removed`, + ); + } catch (err) { + logger.warn(`memory-neo4j: [sleep] Phase 7 error: ${String(err)}`); + } + } + result.durationMs = Date.now() - startTime; result.aborted = abortSignal?.aborted ?? false;