mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 22:40:43 +00:00
memory: narrow dreaming contamination heuristics
This commit is contained in:
@@ -262,6 +262,36 @@ describe("short-term promotion", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("keeps ordinary snippets that only quote dreaming prompt markers", async () => {
|
||||
await withTempWorkspace(async (workspaceDir) => {
|
||||
await recordShortTermRecalls({
|
||||
workspaceDir,
|
||||
query: "debug note",
|
||||
results: [
|
||||
{
|
||||
path: "memory/2026-04-03.md",
|
||||
source: "memory",
|
||||
startLine: 1,
|
||||
endLine: 1,
|
||||
score: 0.75,
|
||||
snippet:
|
||||
"Debug note: quote Write a dream diary entry from these memory fragments for docs, but do not use dreaming-narrative-like labels in production.",
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const store = JSON.parse(
|
||||
await fs.readFile(resolveShortTermRecallStorePath(workspaceDir), "utf-8"),
|
||||
) as { entries: Record<string, { snippet: string }> };
|
||||
expect(Object.values(store.entries)).toEqual([
|
||||
expect.objectContaining({
|
||||
snippet:
|
||||
"Debug note: quote Write a dream diary entry from these memory fragments for docs, but do not use dreaming-narrative-like labels in production.",
|
||||
}),
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
it("records recalls and ranks candidates with weighted scores", async () => {
|
||||
await withTempWorkspace(async (workspaceDir) => {
|
||||
await recordShortTermRecalls({
|
||||
@@ -1063,6 +1093,22 @@ describe("short-term promotion", () => {
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
it("does not treat ordinary candidate notes with daily-memory evidence as contaminated", () => {
|
||||
expect(
|
||||
__testing.isContaminatedDreamingSnippet(
|
||||
"Candidate: move backups weekly. confidence: 0.76 evidence: memory/2026-04-08.md:1-1",
|
||||
),
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
it("treats transcript-style dreaming prompt echoes as contaminated", () => {
|
||||
expect(
|
||||
__testing.isContaminatedDreamingSnippet(
|
||||
"[main/dreaming-narrative-light.jsonl#L1] Write a dream diary entry from these memory fragments:",
|
||||
),
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
it("skips direct candidates that exceed maxAgeDays during apply", async () => {
|
||||
await withTempWorkspace(async (workspaceDir) => {
|
||||
const applied = await applyShortTermPromotions({
|
||||
|
||||
@@ -32,13 +32,13 @@ const SHORT_TERM_LOCK_RELATIVE_PATH = path.join("memory", ".dreams", "short-term
|
||||
const SHORT_TERM_LOCK_WAIT_TIMEOUT_MS = 10_000;
|
||||
const SHORT_TERM_LOCK_STALE_MS = 60_000;
|
||||
const SHORT_TERM_LOCK_RETRY_DELAY_MS = 40;
|
||||
const DREAMING_NARRATIVE_PROMPT_PREFIX = "Write a dream diary entry from these memory fragments";
|
||||
// Repeated dreaming revisits should be able to clear the default promotion gate
|
||||
// without requiring separate organic recall traffic for the same snippet.
|
||||
const PHASE_SIGNAL_LIGHT_BOOST_MAX = 0.06;
|
||||
const PHASE_SIGNAL_REM_BOOST_MAX = 0.09;
|
||||
const PHASE_SIGNAL_HALF_LIFE_DAYS = 14;
|
||||
const DREAMING_NARRATIVE_RUN_PREFIX = "dreaming-narrative-";
|
||||
const DREAMING_TRANSCRIPT_PROMPT_LINE_RE =
|
||||
/\[[^\]]*dreaming-narrative[^\]]*]\s*Write a dream diary entry from these memory fragments:?/i;
|
||||
const inProcessShortTermLocks = new Map<string, Promise<void>>();
|
||||
const ensuredShortTermDirs = new Map<string, Promise<void>>();
|
||||
|
||||
@@ -277,9 +277,8 @@ function isContaminatedDreamingSnippet(raw: string): boolean {
|
||||
return false;
|
||||
}
|
||||
if (
|
||||
snippet.includes(DREAMING_NARRATIVE_PROMPT_PREFIX) ||
|
||||
snippet.includes(PROMOTION_MARKER_PREFIX) ||
|
||||
snippet.includes(DREAMING_NARRATIVE_RUN_PREFIX)
|
||||
/<!--\s*openclaw-memory-promotion:/i.test(snippet) ||
|
||||
DREAMING_TRANSCRIPT_PROMPT_LINE_RE.test(snippet)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
@@ -291,13 +290,7 @@ function isContaminatedDreamingSnippet(raw: string): boolean {
|
||||
);
|
||||
const hasStatus = /\bstatus:\s*staged\b/i.test(snippet);
|
||||
const hasRecalls = /\brecalls:\s*\d+\b/i.test(snippet);
|
||||
if (hasEvidence && hasNarrativeLead) {
|
||||
return true;
|
||||
}
|
||||
const structuredMarkers = [hasConfidence, hasEvidence, hasStatus, hasRecalls].filter(
|
||||
Boolean,
|
||||
).length;
|
||||
return hasNarrativeLead && structuredMarkers >= 2;
|
||||
return hasNarrativeLead && hasConfidence && hasEvidence && hasStatus && hasRecalls;
|
||||
}
|
||||
|
||||
function normalizeMemoryPath(rawPath: string): string {
|
||||
|
||||
Reference in New Issue
Block a user