@@ -17,6 +17,8 @@ import {
1717 loadSessionEntry ,
1818 loadTranscriptEvents ,
1919 patchSessionEntry ,
20+ persistSessionResetLifecycle ,
21+ persistSessionRolloverLifecycle ,
2022 persistSessionTranscriptTurn ,
2123 purgeDeletedAgentSessionEntries ,
2224 publishTranscriptUpdate ,
@@ -597,6 +599,118 @@ describe("session accessor file-backed seam", () => {
597599 expect ( fs . readdirSync ( siblingDir ) ) . toEqual ( [ "sibling-lifecycle.jsonl" ] ) ;
598600 } ) ;
599601
602+ it ( "persists reset lifecycle entry changes with transcript replay and cleanup" , async ( ) => {
603+ const now = Date . now ( ) ;
604+ const sessionKey = "agent:main:main" ;
605+ const previousTranscript = path . join ( tempDir , "previous-session.jsonl" ) ;
606+ const nextTranscript = path . join ( tempDir , "next-session.jsonl" ) ;
607+ const previousEntry : SessionEntry = {
608+ sessionFile : previousTranscript ,
609+ sessionId : "previous-session" ,
610+ updatedAt : now ,
611+ } ;
612+ const nextEntry : SessionEntry = {
613+ sessionFile : nextTranscript ,
614+ sessionId : "next-session" ,
615+ updatedAt : now + 1 ,
616+ } ;
617+ fs . writeFileSync (
618+ previousTranscript ,
619+ [
620+ JSON . stringify ( { type : "session" , id : "previous-session" } ) ,
621+ JSON . stringify ( {
622+ id : "msg-user" ,
623+ message : { role : "user" , content : "hello" } ,
624+ parentId : null ,
625+ timestamp : "2026-06-16T00:00:00.000Z" ,
626+ type : "message" ,
627+ } ) ,
628+ JSON . stringify ( {
629+ id : "msg-assistant" ,
630+ message : { role : "assistant" , content : "hi" } ,
631+ parentId : "msg-user" ,
632+ timestamp : "2026-06-16T00:00:01.000Z" ,
633+ type : "message" ,
634+ } ) ,
635+ ] . join ( "\n" ) + "\n" ,
636+ "utf-8" ,
637+ ) ;
638+ await upsertSessionEntry ( { sessionKey, storePath } , previousEntry ) ;
639+
640+ const result = await persistSessionResetLifecycle ( {
641+ agentId : "main" ,
642+ cleanupPreviousTranscript : true ,
643+ nextEntry,
644+ nextSessionFile : nextTranscript ,
645+ previousEntry,
646+ previousSessionId : previousEntry . sessionId ,
647+ sessionKey,
648+ storePath,
649+ } ) ;
650+
651+ expect ( result . replayedMessages ) . toBe ( 2 ) ;
652+ expect ( loadSessionEntry ( { sessionKey, storePath } ) ) . toMatchObject ( nextEntry ) ;
653+ expect ( fs . existsSync ( previousTranscript ) ) . toBe ( false ) ;
654+ expect ( fs . readFileSync ( nextTranscript , "utf-8" ) ) . toContain ( '"content":"hello"' ) ;
655+ } ) ;
656+
657+ it ( "persists rollover entries and returns archived previous transcript info" , async ( ) => {
658+ const now = Date . now ( ) ;
659+ const sessionKey = "agent:main:telegram:dm:user" ;
660+ const retiredKey = "agent:main:main" ;
661+ const previousTranscript = path . join ( tempDir , "previous-rollover.jsonl" ) ;
662+ const previousEntry : SessionEntry = {
663+ sessionFile : previousTranscript ,
664+ sessionId : "previous-rollover" ,
665+ updatedAt : now ,
666+ } ;
667+ const nextEntry : SessionEntry = {
668+ sessionFile : path . join ( tempDir , "next-rollover.jsonl" ) ,
669+ sessionId : "next-rollover" ,
670+ updatedAt : now + 1 ,
671+ } ;
672+ fs . writeFileSync ( previousTranscript , '{"type":"session","id":"previous-rollover"}\n' , "utf-8" ) ;
673+ await upsertSessionEntry ( { sessionKey, storePath } , previousEntry ) ;
674+ await upsertSessionEntry (
675+ { sessionKey : retiredKey , storePath } ,
676+ {
677+ lastChannel : "telegram" ,
678+ lastTo : "user" ,
679+ sessionId : "legacy-main" ,
680+ updatedAt : now ,
681+ } ,
682+ ) ;
683+
684+ const result = await persistSessionRolloverLifecycle ( {
685+ activeSessionKey : sessionKey ,
686+ agentId : "main" ,
687+ previousEntry,
688+ retiredEntry : {
689+ key : retiredKey ,
690+ entry : {
691+ sessionId : "legacy-main" ,
692+ updatedAt : now ,
693+ } ,
694+ } ,
695+ sessionEntry : nextEntry ,
696+ sessionKey,
697+ storePath,
698+ } ) ;
699+
700+ expect ( result . sessionEntry ) . toMatchObject ( nextEntry ) ;
701+ expect ( result . previousSessionTranscript . transcriptArchived ) . toBe ( true ) ;
702+ expect ( result . previousSessionTranscript . sessionFile ) . toContain (
703+ "previous-rollover.jsonl.reset." ,
704+ ) ;
705+ expect ( loadSessionEntry ( { sessionKey, storePath } ) ) . toMatchObject ( nextEntry ) ;
706+ expect ( loadSessionEntry ( { sessionKey : retiredKey , storePath } ) ) . toEqual ( {
707+ sessionId : "legacy-main" ,
708+ updatedAt : expect . any ( Number ) ,
709+ } ) ;
710+ expect ( fs . existsSync ( previousTranscript ) ) . toBe ( false ) ;
711+ expect ( fs . existsSync ( result . previousSessionTranscript . sessionFile ?? "" ) ) . toBe ( true ) ;
712+ } ) ;
713+
600714 it ( "loads and appends transcript events through a session scope" , async ( ) => {
601715 const scope = {
602716 sessionFile : transcriptPath ,
0 commit comments