@@ -616,6 +616,120 @@ describe('SessionSelector', () => {
616616 expect ( sessions . length ) . toBe ( 1 ) ;
617617 expect ( sessions [ 0 ] . id ) . toBe ( mainSessionId ) ;
618618 } ) ;
619+
620+ it ( 'should list legacy session JSON without timestamps (regression #18593)' , async ( ) => {
621+ const sessionId = randomUUID ( ) ;
622+
623+ const chatsDir = path . join ( tmpDir , 'chats' ) ;
624+ await fs . mkdir ( chatsDir , { recursive : true } ) ;
625+
626+ const session = {
627+ sessionId,
628+ projectHash : 'test-hash' ,
629+ messages : [
630+ {
631+ type : 'user' ,
632+ content : 'Legacy session message' ,
633+ id : 'msg1' ,
634+ timestamp : '2024-01-01T10:00:00.000Z' ,
635+ } ,
636+ ] ,
637+ } ;
638+
639+ const filePath = path . join (
640+ chatsDir ,
641+ `${ SESSION_FILE_PREFIX } 2024-01-01T10-00-${ sessionId . slice ( 0 , 8 ) } .json` ,
642+ ) ;
643+ await fs . writeFile ( filePath , JSON . stringify ( session , null , 2 ) ) ;
644+ const fallbackTimestamp = new Date ( '2024-01-01T10:30:00.000Z' ) ;
645+ await fs . utimes ( filePath , fallbackTimestamp , fallbackTimestamp ) ;
646+
647+ const sessionSelector = new SessionSelector ( storage ) ;
648+ const sessions = await sessionSelector . listSessions ( ) ;
649+
650+ expect ( sessions . length ) . toBe ( 1 ) ;
651+ expect ( sessions [ 0 ] . id ) . toBe ( sessionId ) ;
652+ expect ( sessions [ 0 ] . startTime ) . toBe ( fallbackTimestamp . toISOString ( ) ) ;
653+ expect ( sessions [ 0 ] . lastUpdated ) . toBe ( fallbackTimestamp . toISOString ( ) ) ;
654+ } ) ;
655+
656+ it ( 'should resolve legacy session JSON without timestamps by UUID (regression #18593)' , async ( ) => {
657+ const sessionId = randomUUID ( ) ;
658+
659+ const chatsDir = path . join ( tmpDir , 'chats' ) ;
660+ await fs . mkdir ( chatsDir , { recursive : true } ) ;
661+
662+ const session = {
663+ sessionId,
664+ projectHash : 'test-hash' ,
665+ messages : [
666+ {
667+ type : 'user' ,
668+ content : 'Legacy session message' ,
669+ id : 'msg1' ,
670+ timestamp : '2024-01-01T10:00:00.000Z' ,
671+ } ,
672+ ] ,
673+ } ;
674+
675+ const filePath = path . join (
676+ chatsDir ,
677+ `${ SESSION_FILE_PREFIX } 2024-01-01T10-00-${ sessionId . slice ( 0 , 8 ) } .json` ,
678+ ) ;
679+ await fs . writeFile ( filePath , JSON . stringify ( session , null , 2 ) ) ;
680+ const fallbackTimestamp = new Date ( '2024-01-01T10:30:00.000Z' ) ;
681+ await fs . utimes ( filePath , fallbackTimestamp , fallbackTimestamp ) ;
682+
683+ const sessionSelector = new SessionSelector ( storage ) ;
684+ const result = await sessionSelector . resolveSession ( sessionId ) ;
685+
686+ expect ( result . sessionData . sessionId ) . toBe ( sessionId ) ;
687+ expect ( result . sessionData . startTime ) . toBe ( fallbackTimestamp . toISOString ( ) ) ;
688+ expect ( result . sessionData . lastUpdated ) . toBe (
689+ fallbackTimestamp . toISOString ( ) ,
690+ ) ;
691+ } ) ;
692+
693+ it ( 'should throw INVALID_SESSION_IDENTIFIER for a UUID that does not exist on disk at all' , async ( ) => {
694+ const existingSessionId = randomUUID ( ) ;
695+ const nonExistentId = randomUUID ( ) ;
696+
697+ const chatsDir = path . join ( tmpDir , 'chats' ) ;
698+ await fs . mkdir ( chatsDir , { recursive : true } ) ;
699+
700+ const session = {
701+ sessionId : existingSessionId ,
702+ projectHash : 'test-hash' ,
703+ startTime : '2024-01-01T10:00:00.000Z' ,
704+ lastUpdated : '2024-01-01T10:30:00.000Z' ,
705+ messages : [
706+ {
707+ type : 'user' ,
708+ content : 'Hello' ,
709+ id : 'msg1' ,
710+ timestamp : '2024-01-01T10:00:00.000Z' ,
711+ } ,
712+ ] ,
713+ } ;
714+
715+ await fs . writeFile (
716+ path . join (
717+ chatsDir ,
718+ `${ SESSION_FILE_PREFIX } 2024-01-01T10-00-${ existingSessionId . slice ( 0 , 8 ) } .json` ,
719+ ) ,
720+ JSON . stringify ( session , null , 2 ) ,
721+ ) ;
722+
723+ const sessionSelector = new SessionSelector ( storage ) ;
724+
725+ await expect ( sessionSelector . findSession ( nonExistentId ) ) . rejects . toSatisfy (
726+ ( error ) => {
727+ expect ( error ) . toBeInstanceOf ( SessionError ) ;
728+ expect ( ( error as SessionError ) . code ) . toBe ( 'INVALID_SESSION_IDENTIFIER' ) ;
729+ return true ;
730+ } ,
731+ ) ;
732+ } ) ;
619733} ) ;
620734
621735describe ( 'extractFirstUserMessage' , ( ) => {
0 commit comments