@@ -567,6 +567,97 @@ describe("MESSAGES_SNAPSHOT preserves activity messages", () => {
567567 expect ( msgs . map ( ( m ) => m . id ) ) . toEqual ( [ "m1" , "act-1" , "m2" , "m3" ] ) ;
568568 } ) ;
569569
570+ it ( "preserves reasoning messages after MESSAGES_SNAPSHOT" , async ( ) => {
571+ const initial : Message [ ] = [
572+ { id : "m1" , role : "user" , content : "hello" } ,
573+ { id : "r1" , role : "reasoning" , content : "Let me think about this..." } ,
574+ { id : "m2" , role : "assistant" , content : "hi there" } ,
575+ ] as Message [ ] ;
576+
577+ const events$ = new Subject < BaseEvent > ( ) ;
578+ const agent = createAgent ( initial ) ;
579+ const result$ = defaultApplyEvents ( makeInput ( initial ) , events$ , agent , [ ] ) ;
580+ const updatesPromise = firstValueFrom ( result$ . pipe ( toArray ( ) ) ) ;
581+
582+ // Snapshot does NOT contain the reasoning message (backends don't include them)
583+ events$ . next ( {
584+ type : EventType . MESSAGES_SNAPSHOT ,
585+ messages : [
586+ { id : "m1" , role : "user" , content : "hello" } ,
587+ { id : "m2" , role : "assistant" , content : "hi there" } ,
588+ ] ,
589+ } as MessagesSnapshotEvent ) ;
590+
591+ events$ . complete ( ) ;
592+ const updates = await updatesPromise ;
593+
594+ const msgs = updates [ 0 ] ?. messages ! ;
595+ expect ( msgs . length ) . toBe ( 3 ) ;
596+ expect ( msgs . map ( ( m ) => m . id ) ) . toEqual ( [ "m1" , "r1" , "m2" ] ) ;
597+ expect ( msgs [ 1 ] . role ) . toBe ( "reasoning" ) ;
598+ expect ( msgs [ 1 ] . content ) . toBe ( "Let me think about this..." ) ;
599+ } ) ;
600+
601+ it ( "preserves both activity and reasoning messages after MESSAGES_SNAPSHOT" , async ( ) => {
602+ const initial : Message [ ] = [
603+ { id : "m1" , role : "user" , content : "explain this" } ,
604+ { id : "act-1" , role : "activity" , activityType : "PLAN" , content : { tasks : [ "research" ] } } ,
605+ { id : "r1" , role : "reasoning" , content : "The user wants an explanation..." } ,
606+ { id : "m2" , role : "assistant" , content : "Here is the explanation" } ,
607+ ] as Message [ ] ;
608+
609+ const events$ = new Subject < BaseEvent > ( ) ;
610+ const agent = createAgent ( initial ) ;
611+ const result$ = defaultApplyEvents ( makeInput ( initial ) , events$ , agent , [ ] ) ;
612+ const updatesPromise = firstValueFrom ( result$ . pipe ( toArray ( ) ) ) ;
613+
614+ events$ . next ( {
615+ type : EventType . MESSAGES_SNAPSHOT ,
616+ messages : [
617+ { id : "m1" , role : "user" , content : "explain this" } ,
618+ { id : "m2" , role : "assistant" , content : "Here is the explanation" } ,
619+ ] ,
620+ } as MessagesSnapshotEvent ) ;
621+
622+ events$ . complete ( ) ;
623+ const updates = await updatesPromise ;
624+
625+ const msgs = updates [ 0 ] ?. messages ! ;
626+ expect ( msgs . length ) . toBe ( 4 ) ;
627+ expect ( msgs . map ( ( m ) => m . id ) ) . toEqual ( [ "m1" , "act-1" , "r1" , "m2" ] ) ;
628+ expect ( msgs [ 1 ] . role ) . toBe ( "activity" ) ;
629+ expect ( msgs [ 2 ] . role ) . toBe ( "reasoning" ) ;
630+ } ) ;
631+
632+ it ( "reasoning messages are not replaced by snapshot data" , async ( ) => {
633+ const initial : Message [ ] = [
634+ { id : "m1" , role : "user" , content : "hello" } ,
635+ { id : "r1" , role : "reasoning" , content : "original reasoning" } ,
636+ { id : "m2" , role : "assistant" , content : "response" } ,
637+ ] as Message [ ] ;
638+
639+ const events$ = new Subject < BaseEvent > ( ) ;
640+ const agent = createAgent ( initial ) ;
641+ const result$ = defaultApplyEvents ( makeInput ( initial ) , events$ , agent , [ ] ) ;
642+ const updatesPromise = firstValueFrom ( result$ . pipe ( toArray ( ) ) ) ;
643+
644+ events$ . next ( {
645+ type : EventType . MESSAGES_SNAPSHOT ,
646+ messages : [
647+ { id : "m1" , role : "user" , content : "hello" } ,
648+ { id : "m2" , role : "assistant" , content : "response" } ,
649+ ] ,
650+ } as MessagesSnapshotEvent ) ;
651+
652+ events$ . complete ( ) ;
653+ const updates = await updatesPromise ;
654+
655+ const msgs = updates [ 0 ] ?. messages ! ;
656+ const reasoning = msgs . find ( ( m ) => m . id === "r1" ) ! ;
657+ expect ( reasoning . role ) . toBe ( "reasoning" ) ;
658+ expect ( reasoning . content ) . toBe ( "original reasoning" ) ;
659+ } ) ;
660+
570661 it ( "preserves activity position when a message ID changes in snapshot" , async ( ) => {
571662 // Simulates the real-world scenario: streaming creates a tool message with ID "tool-stream",
572663 // but MESSAGES_SNAPSHOT has the same tool message with a different canonical ID "tool-canon".
0 commit comments