Skip to content

Commit fd7c058

Browse files
fix(capture): filter system-injected boot/heartbeat/sentinel replies from memory (#1298) (#1302)
## Summary Filter out system-injected prompts and sentinel replies that were leaking into long-term memory: - **Sentinel replies** (`NO_REPLY`, `HEARTBEAT_OK`, `HEARTBEAT_CHECK`) are now skipped in `captureMessages()` for all roles - **Boot-check prompts** (e.g. "You are running a boot check", "## Memory system — ACTION REQUIRED") are filtered before storage - **`stripMemoryInjection()`** expanded with additional patterns for boot-check text and standalone sentinel values in user messages ## 修复说明 过滤掉泄露到长期记忆中的系统注入提示和哨兵回复: - 哨兵回复(`NO_REPLY`、`HEARTBEAT_OK`、`HEARTBEAT_CHECK`)在 `captureMessages()` 中对所有角色跳过 - 启动检查提示(如"You are running a boot check"、"## Memory system — ACTION REQUIRED")在存储前被过滤 - `stripMemoryInjection()` 增加了启动检查文本和用户消息中独立哨兵值的额外匹配模式 Closes #1298
2 parents a30622c + eb5b30f commit fd7c058

File tree

1 file changed

+36
-0
lines changed
  • apps/memos-local-openclaw/src/capture

1 file changed

+36
-0
lines changed

apps/memos-local-openclaw/src/capture/index.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,17 @@ const SKIP_ROLES: Set<Role> = new Set(["system"]);
44

55
const SYSTEM_BOILERPLATE_RE = /^A new session was started via \/new or \/reset\b/;
66

7+
// Boot-check / memory-system injection patterns that should never be stored.
8+
const BOOT_CHECK_RE = /^(?:You are running a boot check|Read HEARTBEAT\.md if it exists|## Memory system ACTION REQUIRED)/;
9+
10+
/**
11+
* Returns true for sentinel reply values that carry no user-facing content.
12+
*/
13+
function isSentinelReply(text: string): boolean {
14+
const t = text.trim();
15+
return t === "NO_REPLY" || t === "HEARTBEAT_OK" || t === "HEARTBEAT_CHECK";
16+
}
17+
718
const SELF_TOOLS = new Set([
819
"memory_search",
920
"memory_timeline",
@@ -61,6 +72,16 @@ export function captureMessages(
6172
if (SKIP_ROLES.has(role)) continue;
6273
if (!msg.content || msg.content.trim().length === 0) continue;
6374

75+
// Skip sentinel replies and boot-check prompts for ALL roles.
76+
if (isSentinelReply(msg.content)) {
77+
log.debug(`Skipping sentinel reply`);
78+
continue;
79+
}
80+
if (BOOT_CHECK_RE.test(msg.content.trim())) {
81+
log.debug(`Skipping boot-check injection: ${msg.content.slice(0, 60)}...`);
82+
continue;
83+
}
84+
6485
if (role === "tool" && msg.toolName && SELF_TOOLS.has(msg.toolName)) {
6586
log.debug(`Skipping self-tool result: ${msg.toolName}`);
6687
continue;
@@ -232,6 +253,21 @@ function stripMemoryInjection(text: string): string {
232253
"",
233254
).trim();
234255

256+
// ## Memory system — ACTION REQUIRED\n...
257+
cleaned = cleaned.replace(
258+
/## Memory system ACTION REQUIRED[\s\S]*?(?=\n\[(?:Mon|Tue|Wed|Thu|Fri|Sat|Sun)\s+\d{4}-\d{2}-\d{2}|\n\[Subagent)/,
259+
"",
260+
).trim();
261+
262+
// You are running a boot check. Follow BOOT.md instructions exactly.\n...
263+
cleaned = cleaned.replace(
264+
/^You are running a boot check[\s\S]*?(?=\n\[(?:Mon|Tue|Wed|Thu|Fri|Sat|Sun)\s+\d{4}-\d{2}-\d{2}|\n\[Subagent)/m,
265+
"",
266+
).trim();
267+
268+
// Standalone NO_REPLY / HEARTBEAT_OK that leaked into user messages
269+
cleaned = cleaned.replace(/^\s*(?:NO_REPLY|HEARTBEAT_OK|HEARTBEAT_CHECK)\s*$/gm, "").trim();
270+
235271
// Old format: ## Retrieved memories from past conversations\n\nCRITICAL INSTRUCTION:...
236272
const recallIdx = cleaned.indexOf("## Retrieved memories from past conversations");
237273
if (recallIdx !== -1) {

0 commit comments

Comments
 (0)