@@ -773,6 +773,40 @@ describe("agent event handler", () => {
773773 nowSpy . mockRestore ( ) ;
774774 } ) ;
775775
776+ it ( "does not emit a delta when a repeated assistant snapshot is unchanged" , ( ) => {
777+ let now = 11_250 ;
778+ const nowSpy = vi . spyOn ( Date , "now" ) . mockImplementation ( ( ) => now ) ;
779+ const { broadcast, nodeSendToSession, chatRunState, handler } = createHarness ( ) ;
780+ chatRunState . registry . add ( "run-unchanged-snapshot" , {
781+ sessionKey : "session-unchanged-snapshot" ,
782+ clientRunId : "client-unchanged-snapshot" ,
783+ } ) ;
784+
785+ handler ( {
786+ runId : "run-unchanged-snapshot" ,
787+ seq : 1 ,
788+ stream : "assistant" ,
789+ ts : Date . now ( ) ,
790+ data : { text : "Hello world" } ,
791+ } ) ;
792+
793+ now = 11_450 ;
794+ handler ( {
795+ runId : "run-unchanged-snapshot" ,
796+ seq : 2 ,
797+ stream : "assistant" ,
798+ ts : Date . now ( ) ,
799+ data : { text : "Hello world" } ,
800+ } ) ;
801+
802+ const chatCalls = chatBroadcastCalls ( broadcast ) ;
803+ expect ( chatCalls ) . toHaveLength ( 1 ) ;
804+ const payload = chatCalls [ 0 ] ?. [ 1 ] as { deltaText ?: string } ;
805+ expect ( payload . deltaText ) . toBe ( "Hello world" ) ;
806+ expect ( sessionChatCalls ( nodeSendToSession ) ) . toHaveLength ( 1 ) ;
807+ nowSpy . mockRestore ( ) ;
808+ } ) ;
809+
776810 it ( "marks non-prefix replacement deltas explicitly" , ( ) => {
777811 let now = 11_300 ;
778812 const nowSpy = vi . spyOn ( Date , "now" ) . mockImplementation ( ( ) => now ) ;
@@ -815,6 +849,51 @@ describe("agent event handler", () => {
815849 nowSpy . mockRestore ( ) ;
816850 } ) ;
817851
852+ it ( "flushes throttled shorter replacement deltas before final" , ( ) => {
853+ let now = 11_700 ;
854+ const nowSpy = vi . spyOn ( Date , "now" ) . mockImplementation ( ( ) => now ) ;
855+ const { broadcast, nodeSendToSession, chatRunState, handler } = createHarness ( ) ;
856+ chatRunState . registry . add ( "run-short-replacement-flush" , {
857+ sessionKey : "session-short-replacement-flush" ,
858+ clientRunId : "client-short-replacement-flush" ,
859+ } ) ;
860+
861+ handler ( {
862+ runId : "run-short-replacement-flush" ,
863+ seq : 1 ,
864+ stream : "assistant" ,
865+ ts : Date . now ( ) ,
866+ data : { text : "Hello world" } ,
867+ } ) ;
868+
869+ now = 11_760 ;
870+ handler ( {
871+ runId : "run-short-replacement-flush" ,
872+ seq : 2 ,
873+ stream : "assistant" ,
874+ ts : Date . now ( ) ,
875+ data : { text : "Hi" } ,
876+ } ) ;
877+
878+ emitLifecycleEnd ( handler , "run-short-replacement-flush" , 3 ) ;
879+
880+ const chatCalls = chatBroadcastCalls ( broadcast ) ;
881+ expect ( chatCalls ) . toHaveLength ( 3 ) ;
882+ const replacementPayload = chatCalls [ 1 ] ?. [ 1 ] as {
883+ state ?: string ;
884+ deltaText ?: string ;
885+ replace ?: boolean ;
886+ message ?: { content ?: Array < { text ?: string } > } ;
887+ } ;
888+ expect ( replacementPayload . state ) . toBe ( "delta" ) ;
889+ expect ( replacementPayload . deltaText ) . toBe ( "Hi" ) ;
890+ expect ( replacementPayload . replace ) . toBe ( true ) ;
891+ expect ( replacementPayload . message ?. content ?. [ 0 ] ?. text ) . toBe ( "Hi" ) ;
892+ expect ( ( chatCalls [ 2 ] ?. [ 1 ] as { state ?: string } ) . state ) . toBe ( "final" ) ;
893+ expect ( sessionChatCalls ( nodeSendToSession ) ) . toHaveLength ( 3 ) ;
894+ nowSpy . mockRestore ( ) ;
895+ } ) ;
896+
818897 it ( "cleans up agent run sequence tracking when lifecycle completes" , ( ) => {
819898 const { agentRunSeq, chatRunState, handler, nowSpy } = createHarness ( { now : 2_500 } ) ;
820899 chatRunState . registry . add ( "run-cleanup" , {
0 commit comments