From 981cf6057faea194c51b028f826b4ff2169860aa Mon Sep 17 00:00:00 2001 From: Anton Evseev <78427278+slayoffer@users.noreply.github.com> Date: Mon, 9 Feb 2026 19:13:19 +1000 Subject: [PATCH] fix(openclaw): prevent memory wipe on every session (#323) Use unique document_id per conversation (sessionKey + timestamp) instead of static sessionKey. The backend CASCADE-deletes old memories when the same document_id is reused, causing all prior facts to be lost. Also: - Universal envelope stripping for all channels (was Telegram-only) - Prefer rawMessage over prompt for cleaner recall queries - Increase recall max_tokens from 512 to 2048 Co-authored-by: Claude Opus 4.6 --- hindsight-integrations/openclaw/src/index.ts | 42 +++++++++++++++----- 1 file changed, 33 insertions(+), 9 deletions(-) diff --git a/hindsight-integrations/openclaw/src/index.ts b/hindsight-integrations/openclaw/src/index.ts index feafb5fc..a05a0730 100644 --- a/hindsight-integrations/openclaw/src/index.ts +++ b/hindsight-integrations/openclaw/src/index.ts @@ -554,17 +554,40 @@ export default function (api: MoltbotPluginAPI) { console.log(`[Hindsight] before_agent_start - bank: ${bankId}, channel: ${ctx?.messageProvider}/${ctx?.channelId}`); // Get the user's latest message for recall - let prompt = event.prompt; + // Prefer rawMessage (clean user text) over prompt (envelope-formatted) + let prompt = event.rawMessage ?? event.prompt; if (!prompt || typeof prompt !== 'string' || prompt.length < 5) { return; // Skip very short messages } - // Extract actual message from Telegram format: [Telegram ... GMT+1] actual message - const telegramMatch = prompt.match(/\[Telegram[^\]]+\]\s*(.+)$/); - if (telegramMatch) { - prompt = telegramMatch[1].trim(); + // Strip envelope-formatted prompts from any channel + // The prompt may contain: System: lines, abort hints, [Channel ...] header, [from: ...] suffix + let cleaned = prompt; + + // Remove leading "System: ..." lines (from prependSystemEvents) + cleaned = cleaned.replace(/^(?:System:.*\n)+\n?/, ''); + + // Remove session abort hint + cleaned = cleaned.replace( + /^Note: The previous agent run was aborted[^\n]*\n\n/, + '', + ); + + // Extract message after [ChannelName ...] envelope header + // Handles any channel: Telegram, Slack, Discord, WhatsApp, Signal, etc. + // Uses [\s\S]+ instead of .+ to support multiline messages + const envelopeMatch = cleaned.match( + /\[[A-Z][A-Za-z]*(?:\s[^\]]+)?\]\s*([\s\S]+)$/, + ); + if (envelopeMatch) { + cleaned = envelopeMatch[1]; } + // Remove trailing [from: SenderName] metadata (group chats) + cleaned = cleaned.replace(/\n\[from:[^\]]*\]\s*$/, ''); + + prompt = cleaned.trim() || prompt; + if (prompt.length < 5) { return; // Skip very short messages after extraction } @@ -587,10 +610,10 @@ export default function (api: MoltbotPluginAPI) { console.log(`[Hindsight] Auto-recall for bank ${bankId}, prompt: ${prompt.substring(0, 50)}`); - // Recall relevant memories (up to 512 tokens) + // Recall relevant memories const response = await client.recall({ query: prompt, - max_tokens: 512, + max_tokens: 2048, }); if (!response.results || response.results.length === 0) { @@ -675,8 +698,9 @@ User message: ${prompt} return; } - // Use session key as document ID (prefer context over captured value) - const documentId = effectiveCtx?.sessionKey || currentSessionKey || 'default-session'; + // Use unique document ID per conversation (sessionKey + timestamp) + // Static sessionKey (e.g. "agent:main:main") causes CASCADE delete of old memories + const documentId = `${effectiveCtx?.sessionKey || currentSessionKey || 'session'}-${Date.now()}`; // Retain to Hindsight await client.retain({