From a036c50c2596aebb0955f1468df4d471ff083a06 Mon Sep 17 00:00:00 2001 From: mjamiv <142179942+mjamiv@users.noreply.github.com> Date: Tue, 14 Apr 2026 07:49:00 +0000 Subject: [PATCH] fix(dreaming): default storage.mode to "separate" so phase blocks stop polluting daily memory files Closes #66328. Before this change, every dreaming installation that did not explicitly set `plugins.entries.memory-core.config.dreaming.storage.mode` accepted the silent default of `"inline"`, which routes Light Sleep and REM Sleep phase blocks (the structured staged-candidate lists with their `` markers) into `memory/YYYY-MM-DD.md`. On heavy days the inline blocks dominate the daily memory file - the reporter measured 340 lines for the Light phase alone, and a second cross-host check on a v2026.4.12 install observed 475 inline lines from Light plus 14 from REM in a single sweep, leaving the daily file unusable as a "what happened today" record. The `"separate"` storage mode that was already wired up in `writeDailyDreamingPhaseBlock` writes the same content to `memory/dreaming/{phase}/YYYY-MM-DD.md` instead, leaving the daily file untouched. `stripManagedDailyDreamingLines` was already in place so the daily-ingestion scanner does not re-record dream blocks as recall candidates either way, but flipping the default also avoids the incidental round-trip where the dreaming pipeline writes inline output and then reads its own output back as fresh recall material in the next cycle. Operators who want the previous behavior can still opt in explicitly: plugins.entries.memory-core.config.dreaming.storage.mode: "inline" The host helper `normalizeStorageMode` continues to accept all three documented modes (`inline | separate | both`); only the unset-default fallback changes. Test changes: - src/memory-host-sdk/dreaming.test.ts adds two new assertions: one pinning the new default shape and one confirming explicit `"inline"` still round-trips for opt-in callers. - extensions/memory-core/src/dreaming.test.ts updates four default-output assertions in `resolveShortTermPromotionDreamingConfig` to expect `mode: "separate"`. - extensions/memory-core/src/dreaming-phases.test.ts pins `LIGHT_DREAMING_TEST_CONFIG` and the inline-mode harness in "checkpoints daily ingestion and skips unchanged daily files" to `storage.mode: "inline"` with a comment, since those tests rely on inline-mode side effects on `memory/.md` and now need to opt in explicitly. The schema accept-test in extensions/memory-core/src/config.test.ts and the inline-mode write-path tests in extensions/memory-core/src/dreaming-markdown.test.ts intentionally keep asserting `mode: "inline"` because they cover input handling and the inline write path itself, both of which still need to work. --- CHANGELOG.md | 2 ++ .../memory-core/src/dreaming-phases.test.ts | 9 +++++++ extensions/memory-core/src/dreaming.test.ts | 8 +++--- src/memory-host-sdk/dreaming.test.ts | 25 +++++++++++++++++++ src/memory-host-sdk/dreaming.ts | 2 +- 5 files changed, 41 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f23a7f5858f..bcb42da49e5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,8 @@ Docs: https://docs.openclaw.ai - Agents/context + Memory: trim default startup/skills prompt budgets, cap `memory_get` excerpts by default with explicit continuation metadata, and keep QMD reads aligned with the same bounded excerpt contract so long sessions pull less context by default without losing deterministic follow-up reads. - Matrix/commands: skip DM pairing-store reads on room traffic now that room control-command authorization ignores pairing-store entries, keeping the room path narrower without changing room auth behavior. (#67325) Thanks @gumadeiras. - Memory-core/dreaming: skip dreaming narrative transcripts from session-store metadata before bootstrap records land so dream diary prompt/prose lines do not pollute session ingestion. (#67315) thanks @jalehman. +- Agents/local models: clarify low-context preflight hints for self-hosted models, point config-backed caps at the relevant OpenClaw setting, and stop suggesting larger models when `agents.defaults.contextTokens` is the real limit. (#66236) Thanks @ImLukeF. +- Dreaming/memory-core: change the default `dreaming.storage.mode` from `inline` to `separate` so Dreaming phase blocks (`## Light Sleep`, `## REM Sleep`) land in `memory/dreaming/{phase}/YYYY-MM-DD.md` instead of being injected into `memory/YYYY-MM-DD.md`. Daily memory files no longer get dominated by structured candidate output, and the daily-ingestion scanner that already strips dream marker blocks no longer has to compete with hundreds of phase-block lines on every run. Operators who want the previous behavior can opt in by setting `plugins.entries.memory-core.config.dreaming.storage.mode: "inline"`. (#66412) Thanks @mjamiv. ## 2026.4.15-beta.1 diff --git a/extensions/memory-core/src/dreaming-phases.test.ts b/extensions/memory-core/src/dreaming-phases.test.ts index ce5f97ce1cc..8b171c98348 100644 --- a/extensions/memory-core/src/dreaming-phases.test.ts +++ b/extensions/memory-core/src/dreaming-phases.test.ts @@ -27,6 +27,11 @@ const LIGHT_DREAMING_TEST_CONFIG: OpenClawConfig = { dreaming: { enabled: true, timezone: "UTC", + // The existing tests in this file were written when "inline" was the + // default storage mode and assert against `memory/.md` directly. + // Pin the storage mode explicitly so they keep covering inline mode + // after the default flipped to "separate" in #66328. + storage: { mode: "inline", separateReports: false }, phases: { light: { enabled: true, @@ -305,6 +310,10 @@ describe("memory-core dreaming phases", () => { config: { dreaming: { enabled: true, + // This test asserts inline-mode side effects on the daily + // file; pin storage explicitly after the default flipped to + // "separate" in #66328. + storage: { mode: "inline", separateReports: false }, phases: { light: { enabled: true, diff --git a/extensions/memory-core/src/dreaming.test.ts b/extensions/memory-core/src/dreaming.test.ts index dd38bc10f1e..f2386a200cc 100644 --- a/extensions/memory-core/src/dreaming.test.ts +++ b/extensions/memory-core/src/dreaming.test.ts @@ -184,7 +184,7 @@ describe("short-term dreaming config", () => { maxAgeDays: 30, verboseLogging: false, storage: { - mode: "inline", + mode: "separate", separateReports: false, }, }); @@ -223,7 +223,7 @@ describe("short-term dreaming config", () => { maxAgeDays: 30, verboseLogging: true, storage: { - mode: "inline", + mode: "separate", separateReports: false, }, }); @@ -259,7 +259,7 @@ describe("short-term dreaming config", () => { maxAgeDays: 45, verboseLogging: false, storage: { - mode: "inline", + mode: "separate", separateReports: false, }, }); @@ -294,7 +294,7 @@ describe("short-term dreaming config", () => { maxAgeDays: 30, verboseLogging: false, storage: { - mode: "inline", + mode: "separate", separateReports: false, }, }); diff --git a/src/memory-host-sdk/dreaming.test.ts b/src/memory-host-sdk/dreaming.test.ts index bbf51f45471..37410e96885 100644 --- a/src/memory-host-sdk/dreaming.test.ts +++ b/src/memory-host-sdk/dreaming.test.ts @@ -90,6 +90,31 @@ describe("memory dreaming host helpers", () => { }); }); + it("defaults storage mode to separate so phase blocks do not pollute daily memory files", () => { + const resolved = resolveMemoryDreamingConfig({ + pluginConfig: {}, + }); + + expect(resolved.storage).toEqual({ + mode: "separate", + separateReports: false, + }); + }); + + it("preserves explicit inline storage mode for callers that opt in", () => { + const resolved = resolveMemoryDreamingConfig({ + pluginConfig: { + dreaming: { + storage: { + mode: "inline", + }, + }, + }, + }); + + expect(resolved.storage.mode).toBe("inline"); + }); + it("applies top-level dreaming frequency across all phases", () => { const resolved = resolveMemoryDreamingConfig({ pluginConfig: { diff --git a/src/memory-host-sdk/dreaming.ts b/src/memory-host-sdk/dreaming.ts index 177d8540acc..aea9e95f783 100644 --- a/src/memory-host-sdk/dreaming.ts +++ b/src/memory-host-sdk/dreaming.ts @@ -12,7 +12,7 @@ import { export const DEFAULT_MEMORY_DREAMING_ENABLED = false; export const DEFAULT_MEMORY_DREAMING_TIMEZONE = undefined; export const DEFAULT_MEMORY_DREAMING_VERBOSE_LOGGING = false; -export const DEFAULT_MEMORY_DREAMING_STORAGE_MODE = "inline"; +export const DEFAULT_MEMORY_DREAMING_STORAGE_MODE = "separate"; export const DEFAULT_MEMORY_DREAMING_SEPARATE_REPORTS = false; export const DEFAULT_MEMORY_DREAMING_FREQUENCY = "0 3 * * *"; export const DEFAULT_MEMORY_DREAMING_PLUGIN_ID = "memory-core";