@@ -5,21 +5,22 @@ import {
55 type MemoryDreamingPhaseName ,
66 type MemoryDreamingStorageConfig ,
77} from "openclaw/plugin-sdk/memory-core-host-status" ;
8+ import { appendMemoryHostEvent } from "openclaw/plugin-sdk/memory-host-events" ;
9+ import {
10+ replaceManagedMarkdownBlock ,
11+ withTrailingNewline ,
12+ } from "openclaw/plugin-sdk/memory-host-markdown" ;
813
914const DAILY_PHASE_HEADINGS : Record < Exclude < MemoryDreamingPhaseName , "deep" > , string > = {
1015 light : "## Light Sleep" ,
1116 rem : "## REM Sleep" ,
1217} ;
13- const DEEP_PHASE_HEADING = "## Deep Sleep" ;
1418
1519const DAILY_PHASE_LABELS : Record < Exclude < MemoryDreamingPhaseName , "deep" > , string > = {
1620 light : "light" ,
1721 rem : "rem" ,
1822} ;
1923
20- const PRIMARY_DREAMS_FILENAME = "DREAMS.md" ;
21- const DREAMS_FILENAME_ALIASES = [ PRIMARY_DREAMS_FILENAME , "dreams.md" ] as const ;
22-
2324function resolvePhaseMarkers ( phase : Exclude < MemoryDreamingPhaseName , "deep" > ) : {
2425 start : string ;
2526 end : string ;
@@ -31,72 +32,9 @@ function resolvePhaseMarkers(phase: Exclude<MemoryDreamingPhaseName, "deep">): {
3132 } ;
3233}
3334
34- function withTrailingNewline ( content : string ) : string {
35- return content . endsWith ( "\n" ) ? content : `${ content } \n` ;
36- }
37-
38- function replaceManagedBlock ( params : {
39- original : string ;
40- heading : string ;
41- startMarker : string ;
42- endMarker : string ;
43- body : string ;
44- } ) : string {
45- const managedBlock = `${ params . heading } \n${ params . startMarker } \n${ params . body } \n${ params . endMarker } ` ;
46- const existingPattern = new RegExp (
47- `${ escapeRegex ( params . heading ) } \\n${ escapeRegex ( params . startMarker ) } [\\s\\S]*?${ escapeRegex ( params . endMarker ) } ` ,
48- "m" ,
49- ) ;
50- if ( existingPattern . test ( params . original ) ) {
51- return params . original . replace ( existingPattern , managedBlock ) ;
52- }
53- const trimmed = params . original . trimEnd ( ) ;
54- if ( trimmed . length === 0 ) {
55- return `${ managedBlock } \n` ;
56- }
57- return `${ trimmed } \n\n${ managedBlock } \n` ;
58- }
59-
60- function escapeRegex ( value : string ) : string {
61- return value . replace ( / [ . * + ? ^ $ { } ( ) | [ \] \\ ] / g, "\\$&" ) ;
62- }
63-
64- async function resolveDreamsPath ( workspaceDir : string ) : Promise < string > {
65- for ( const candidate of DREAMS_FILENAME_ALIASES ) {
66- const target = path . join ( workspaceDir , candidate ) ;
67- try {
68- await fs . access ( target ) ;
69- return target ;
70- } catch ( err ) {
71- if ( ( err as NodeJS . ErrnoException ) ?. code !== "ENOENT" ) {
72- throw err ;
73- }
74- }
75- }
76- return path . join ( workspaceDir , PRIMARY_DREAMS_FILENAME ) ;
77- }
78-
79- async function writeInlineDeepDreamingBlock ( params : {
80- workspaceDir : string ;
81- body : string ;
82- } ) : Promise < string > {
83- const inlinePath = await resolveDreamsPath ( params . workspaceDir ) ;
84- await fs . mkdir ( path . dirname ( inlinePath ) , { recursive : true } ) ;
85- const original = await fs . readFile ( inlinePath , "utf-8" ) . catch ( ( err : unknown ) => {
86- if ( ( err as NodeJS . ErrnoException ) ?. code === "ENOENT" ) {
87- return "" ;
88- }
89- throw err ;
90- } ) ;
91- const updated = replaceManagedBlock ( {
92- original,
93- heading : DEEP_PHASE_HEADING ,
94- startMarker : "<!-- openclaw:dreaming:deep:start -->" ,
95- endMarker : "<!-- openclaw:dreaming:deep:end -->" ,
96- body : params . body ,
97- } ) ;
98- await fs . writeFile ( inlinePath , withTrailingNewline ( updated ) , "utf-8" ) ;
99- return inlinePath ;
35+ function resolveDailyMemoryPath ( workspaceDir : string , epochMs : number , timezone ?: string ) : string {
36+ const isoDay = formatMemoryDreamingDay ( epochMs , timezone ) ;
37+ return path . join ( workspaceDir , "memory" , `${ isoDay } .md` ) ;
10038}
10139
10240function resolveSeparateReportPath (
@@ -131,7 +69,7 @@ export async function writeDailyDreamingPhaseBlock(params: {
13169 let reportPath : string | undefined ;
13270
13371 if ( shouldWriteInline ( params . storage ) ) {
134- inlinePath = await resolveDreamsPath ( params . workspaceDir ) ;
72+ inlinePath = resolveDailyMemoryPath ( params . workspaceDir , nowMs , params . timezone ) ;
13573 await fs . mkdir ( path . dirname ( inlinePath ) , { recursive : true } ) ;
13674 const original = await fs . readFile ( inlinePath , "utf-8" ) . catch ( ( err : unknown ) => {
13775 if ( ( err as NodeJS . ErrnoException ) ?. code === "ENOENT" ) {
@@ -140,7 +78,7 @@ export async function writeDailyDreamingPhaseBlock(params: {
14078 throw err ;
14179 } ) ;
14280 const markers = resolvePhaseMarkers ( params . phase ) ;
143- const updated = replaceManagedBlock ( {
81+ const updated = replaceManagedMarkdownBlock ( {
14482 original,
14583 heading : DAILY_PHASE_HEADINGS [ params . phase ] ,
14684 startMarker : markers . start ,
@@ -167,6 +105,16 @@ export async function writeDailyDreamingPhaseBlock(params: {
167105 await fs . writeFile ( reportPath , report , "utf-8" ) ;
168106 }
169107
108+ await appendMemoryHostEvent ( params . workspaceDir , {
109+ type : "memory.dream.completed" ,
110+ timestamp : new Date ( nowMs ) . toISOString ( ) ,
111+ phase : params . phase ,
112+ ...( inlinePath ? { inlinePath } : { } ) ,
113+ ...( reportPath ? { reportPath } : { } ) ,
114+ lineCount : params . bodyLines . length ,
115+ storageMode : params . storage . mode ,
116+ } ) ;
117+
170118 return {
171119 ...( inlinePath ? { inlinePath } : { } ) ,
172120 ...( reportPath ? { reportPath } : { } ) ,
@@ -180,19 +128,21 @@ export async function writeDeepDreamingReport(params: {
180128 timezone ?: string ;
181129 storage : MemoryDreamingStorageConfig ;
182130} ) : Promise < string | undefined > {
183- const nowMs = Number . isFinite ( params . nowMs ) ? ( params . nowMs as number ) : Date . now ( ) ;
184- const body = params . bodyLines . length > 0 ? params . bodyLines . join ( "\n" ) : "- No durable changes." ;
185- await writeInlineDeepDreamingBlock ( {
186- workspaceDir : params . workspaceDir ,
187- body,
188- } ) ;
189-
190131 if ( ! shouldWriteSeparate ( params . storage ) ) {
191132 return undefined ;
192133 }
193-
134+ const nowMs = Number . isFinite ( params . nowMs ) ? ( params . nowMs as number ) : Date . now ( ) ;
194135 const reportPath = resolveSeparateReportPath ( params . workspaceDir , "deep" , nowMs , params . timezone ) ;
195136 await fs . mkdir ( path . dirname ( reportPath ) , { recursive : true } ) ;
137+ const body = params . bodyLines . length > 0 ? params . bodyLines . join ( "\n" ) : "- No durable changes." ;
196138 await fs . writeFile ( reportPath , `# Deep Sleep\n\n${ body } \n` , "utf-8" ) ;
139+ await appendMemoryHostEvent ( params . workspaceDir , {
140+ type : "memory.dream.completed" ,
141+ timestamp : new Date ( nowMs ) . toISOString ( ) ,
142+ phase : "deep" ,
143+ reportPath,
144+ lineCount : params . bodyLines . length ,
145+ storageMode : params . storage . mode ,
146+ } ) ;
197147 return reportPath ;
198148}
0 commit comments