@@ -67,8 +67,15 @@ describe("runMemoryFlushIfNeeded", () => {
6767 } ;
6868 if ( typeof params . newSessionId === "string" && params . newSessionId ) {
6969 nextEntry . sessionId = params . newSessionId ;
70- const storePath = typeof params . storePath === "string" ? params . storePath : rootDir ;
71- nextEntry . sessionFile = path . join ( path . dirname ( storePath ) , `${ params . newSessionId } .jsonl` ) ;
70+ if ( typeof params . newSessionFile === "string" && params . newSessionFile ) {
71+ nextEntry . sessionFile = params . newSessionFile ;
72+ } else {
73+ const storePath = typeof params . storePath === "string" ? params . storePath : rootDir ;
74+ nextEntry . sessionFile = path . join (
75+ path . dirname ( storePath ) ,
76+ `${ params . newSessionId } .jsonl` ,
77+ ) ;
78+ }
7279 }
7380 params . sessionStore [ sessionKey ] = nextEntry ;
7481 if ( typeof params . storePath === "string" ) {
@@ -287,6 +294,76 @@ describe("runMemoryFlushIfNeeded", () => {
287294 ) ;
288295 } ) ;
289296
297+ it ( "updates the active preflight run after transcript rotation" , async ( ) => {
298+ const sessionFile = path . join ( rootDir , "session.jsonl" ) ;
299+ const successorFile = path . join ( rootDir , "session-rotated.jsonl" ) ;
300+ await fs . writeFile (
301+ sessionFile ,
302+ `${ JSON . stringify ( { message : { role : "user" , content : "x" . repeat ( 5_000 ) } } ) } \n` ,
303+ "utf8" ,
304+ ) ;
305+ registerMemoryFlushPlanResolver ( ( ) => ( {
306+ softThresholdTokens : 1 ,
307+ forceFlushTranscriptBytes : 1_000_000_000 ,
308+ reserveTokensFloor : 0 ,
309+ prompt : "Pre-compaction memory flush.\nNO_REPLY" ,
310+ systemPrompt : "Write memory to memory/YYYY-MM-DD.md." ,
311+ relativePath : "memory/2023-11-14.md" ,
312+ } ) ) ;
313+ compactEmbeddedPiSessionMock . mockResolvedValueOnce ( {
314+ ok : true ,
315+ compacted : true ,
316+ result : {
317+ tokensAfter : 42 ,
318+ sessionId : "session-rotated" ,
319+ sessionFile : successorFile ,
320+ } ,
321+ } ) ;
322+ const sessionEntry : SessionEntry = {
323+ sessionId : "session" ,
324+ sessionFile,
325+ updatedAt : Date . now ( ) ,
326+ totalTokensFresh : false ,
327+ } ;
328+ const sessionStore = { "agent:main:main" : sessionEntry } ;
329+ const followupRun = createTestFollowupRun ( {
330+ sessionId : "session" ,
331+ sessionFile,
332+ sessionKey : "agent:main:main" ,
333+ } ) ;
334+ const updateSessionId = vi . fn ( ) ;
335+ const replyOperation = {
336+ abortSignal : new AbortController ( ) . signal ,
337+ setPhase : vi . fn ( ) ,
338+ updateSessionId,
339+ } as never ;
340+
341+ const entry = await runPreflightCompactionIfNeeded ( {
342+ cfg : { agents : { defaults : { compaction : { memoryFlush : { } } } } } ,
343+ followupRun,
344+ defaultModel : "anthropic/claude-opus-4-6" ,
345+ agentCfgContextTokens : 100 ,
346+ sessionEntry,
347+ sessionStore,
348+ sessionKey : "agent:main:main" ,
349+ storePath : path . join ( rootDir , "sessions.json" ) ,
350+ isHeartbeat : false ,
351+ replyOperation,
352+ } ) ;
353+
354+ expect ( entry ?. sessionId ) . toBe ( "session-rotated" ) ;
355+ expect ( entry ?. sessionFile ) . toBe ( successorFile ) ;
356+ expect ( followupRun . run . sessionId ) . toBe ( "session-rotated" ) ;
357+ expect ( followupRun . run . sessionFile ) . toBe ( successorFile ) ;
358+ expect ( updateSessionId ) . toHaveBeenCalledWith ( "session-rotated" ) ;
359+ expect ( refreshQueuedFollowupSessionMock ) . toHaveBeenCalledWith ( {
360+ key : "agent:main:main" ,
361+ previousSessionId : "session" ,
362+ nextSessionId : "session-rotated" ,
363+ nextSessionFile : successorFile ,
364+ } ) ;
365+ } ) ;
366+
290367 it ( "uses configured prompts and stored bootstrap warning signatures" , async ( ) => {
291368 const sessionEntry : SessionEntry = {
292369 sessionId : "session" ,
0 commit comments