@@ -21,6 +21,7 @@ import {
2121 formatNarrativeDate ,
2222 formatBackfillDiaryDate ,
2323 generateAndAppendDreamNarrative ,
24+ readRecentDreamDiaryEntries ,
2425 removeBackfillDiaryEntries ,
2526 runDetachedDreamNarrative ,
2627 type NarrativePhaseData ,
@@ -133,6 +134,19 @@ describe("buildNarrativePrompt", () => {
133134 expect ( prompt ) . toContain ( "snippet-11" ) ;
134135 expect ( prompt ) . not . toContain ( "snippet-12" ) ;
135136 } ) ;
137+
138+ it ( "includes current sweep and recent diary context" , ( ) => {
139+ const prompt = buildNarrativePrompt ( {
140+ phase : "light" ,
141+ snippets : [ "Later workspace routing notes surfaced." ] ,
142+ currentDate : "April 6, 2026, 9:00 AM UTC" ,
143+ recentDiaryEntries : [ "The first meeting memory already filled the page." ] ,
144+ } ) ;
145+ expect ( prompt ) . toContain ( "Diary continuity context" ) ;
146+ expect ( prompt ) . toContain ( "Current sweep: April 6, 2026, 9:00 AM UTC" ) ;
147+ expect ( prompt ) . toContain ( "The first meeting memory already filled the page." ) ;
148+ expect ( prompt ) . toContain ( "do not replay the same first-day framing" ) ;
149+ } ) ;
136150} ) ;
137151
138152describe ( "extractNarrativeText" , ( ) => {
@@ -388,6 +402,77 @@ describe("appendNarrativeEntry", () => {
388402 expect ( secondIdx ) . toBeLessThan ( end ) ;
389403 } ) ;
390404
405+ it ( "reads recent diary entries without timestamps or markers" , async ( ) => {
406+ const workspaceDir = await createTempWorkspace ( "openclaw-dreaming-narrative-" ) ;
407+ await appendNarrativeEntry ( {
408+ workspaceDir,
409+ narrative : "The first meeting memory already filled the page." ,
410+ nowMs : Date . parse ( "2026-04-04T03:00:00Z" ) ,
411+ timezone : "UTC" ,
412+ } ) ;
413+ await appendNarrativeEntry ( {
414+ workspaceDir,
415+ narrative : "A later routing note flickered in the margins." ,
416+ nowMs : Date . parse ( "2026-04-05T03:00:00Z" ) ,
417+ timezone : "UTC" ,
418+ } ) ;
419+
420+ await expect ( readRecentDreamDiaryEntries ( { workspaceDir, limit : 1 } ) ) . resolves . toEqual ( [
421+ "A later routing note flickered in the margins." ,
422+ ] ) ;
423+ } ) ;
424+
425+ it ( "skips symlinked DREAMS.md when building recent diary context" , async ( ) => {
426+ const workspaceDir = await createTempWorkspace ( "openclaw-dreaming-narrative-" ) ;
427+ const targetPath = path . join ( workspaceDir , "target-dreams.md" ) ;
428+ const dreamsPath = path . join ( workspaceDir , "DREAMS.md" ) ;
429+ const symlinkTargetDiary = "Symlink target diary text must not enter the prompt." ;
430+ await fs . writeFile (
431+ targetPath ,
432+ [
433+ "# Dream Diary" ,
434+ "" ,
435+ "<!-- openclaw:dreaming:diary:start -->" ,
436+ "---" ,
437+ "" ,
438+ "*April 5, 2026, 3:00 AM UTC*" ,
439+ "" ,
440+ symlinkTargetDiary ,
441+ "" ,
442+ "<!-- openclaw:dreaming:diary:end -->" ,
443+ "" ,
444+ ] . join ( "\n" ) ,
445+ "utf-8" ,
446+ ) ;
447+ await fs . symlink ( targetPath , dreamsPath ) ;
448+
449+ const entries = await readRecentDreamDiaryEntries ( { workspaceDir, limit : 3 } ) ;
450+ expect ( entries ) . toEqual ( [ ] ) ;
451+ const prompt = buildNarrativePrompt ( {
452+ phase : "light" ,
453+ snippets : [ "A fresh routing memory arrived." ] ,
454+ recentDiaryEntries : entries ,
455+ } ) ;
456+ expect ( prompt ) . not . toContain ( symlinkTargetDiary ) ;
457+ } ) ;
458+
459+ it ( "skips non-file DREAMS.md when reading recent diary context" , async ( ) => {
460+ const workspaceDir = await createTempWorkspace ( "openclaw-dreaming-narrative-" ) ;
461+ await fs . mkdir ( path . join ( workspaceDir , "DREAMS.md" ) ) ;
462+
463+ await expect ( readRecentDreamDiaryEntries ( { workspaceDir, limit : 3 } ) ) . resolves . toEqual ( [ ] ) ;
464+ } ) ;
465+
466+ it ( "treats unreadable DREAMS.md as empty recent diary context" , async ( ) => {
467+ const workspaceDir = await createTempWorkspace ( "openclaw-dreaming-narrative-" ) ;
468+ await fs . writeFile ( path . join ( workspaceDir , "DREAMS.md" ) , "unreadable" , "utf-8" ) ;
469+ vi . spyOn ( fs , "access" ) . mockRejectedValueOnce (
470+ Object . assign ( new Error ( "permission denied" ) , { code : "EACCES" } ) ,
471+ ) ;
472+
473+ await expect ( readRecentDreamDiaryEntries ( { workspaceDir, limit : 3 } ) ) . resolves . toEqual ( [ ] ) ;
474+ } ) ;
475+
391476 it ( "prepends diary before existing managed blocks" , async ( ) => {
392477 const workspaceDir = await createTempWorkspace ( "openclaw-dreaming-narrative-" ) ;
393478 const dreamsPath = path . join ( workspaceDir , "DREAMS.md" ) ;
0 commit comments