@@ -670,3 +670,104 @@ test("hallucination stripping does not affect non-dcp tags", async () => {
670670 "<div>hello</div> <system-reminder>keep</system-reminder>" ,
671671 )
672672} )
673+
674+ test ( "injectMessageIds skips empty assistant messages to avoid prefill (issue #463)" , ( ) => {
675+ const sessionID = "ses_empty_assistant"
676+ const messages : WithParts [ ] = [
677+ buildMessage ( "msg-user-1" , "user" , sessionID , "Hello" , 1 ) ,
678+ {
679+ info : {
680+ id : "msg-assistant-empty" ,
681+ role : "assistant" ,
682+ sessionID,
683+ agent : "assistant" ,
684+ time : { created : 2 } ,
685+ } as WithParts [ "info" ] ,
686+ parts : [ ] ,
687+ } ,
688+ buildMessage ( "msg-user-2" , "user" , sessionID , "continue" , 3 ) ,
689+ ]
690+ const state = createSessionState ( )
691+ const config = buildConfig ( "range" )
692+
693+ assignMessageRefs ( state , messages )
694+ injectMessageIds ( state , config , messages )
695+
696+ const emptyAssistant = messages [ 1 ] !
697+ assert . equal ( emptyAssistant . parts . length , 0 , "empty assistant should get no synthetic parts" )
698+ } )
699+
700+ test ( "injectMessageIds skips assistant with only pending tool parts (issue #463)" , ( ) => {
701+ const sessionID = "ses_pending_tool_assistant"
702+ const messages : WithParts [ ] = [
703+ buildMessage ( "msg-user-1" , "user" , sessionID , "Hello" , 1 ) ,
704+ {
705+ info : {
706+ id : "msg-assistant-pending" ,
707+ role : "assistant" ,
708+ sessionID,
709+ agent : "assistant" ,
710+ time : { created : 2 } ,
711+ } as WithParts [ "info" ] ,
712+ parts : [
713+ {
714+ id : "pending-tool-part" ,
715+ messageID : "msg-assistant-pending" ,
716+ sessionID,
717+ type : "tool" as const ,
718+ tool : "bash" ,
719+ callID : "call-pending-1" ,
720+ state : {
721+ status : "pending" as const ,
722+ input : { command : "ls" } ,
723+ } ,
724+ } as any ,
725+ ] ,
726+ } ,
727+ buildMessage ( "msg-user-2" , "user" , sessionID , "continue" , 3 ) ,
728+ ]
729+ const state = createSessionState ( )
730+ const config = buildConfig ( "range" )
731+
732+ assignMessageRefs ( state , messages )
733+ injectMessageIds ( state , config , messages )
734+
735+ const pendingAssistant = messages [ 1 ] !
736+ assert . equal (
737+ pendingAssistant . parts . length ,
738+ 1 ,
739+ "assistant with only pending tools should not get a synthetic text part" ,
740+ )
741+ assert . equal ( pendingAssistant . parts [ 0 ] ! . type , "tool" )
742+ } )
743+
744+ test ( "injectMessageIds skips assistant with empty text part (issue #463)" , ( ) => {
745+ const sessionID = "ses_empty_text_assistant"
746+ const messages : WithParts [ ] = [
747+ buildMessage ( "msg-user-1" , "user" , sessionID , "Hello" , 1 ) ,
748+ {
749+ info : {
750+ id : "msg-assistant-empty-text" ,
751+ role : "assistant" ,
752+ sessionID,
753+ agent : "assistant" ,
754+ time : { created : 2 } ,
755+ } as WithParts [ "info" ] ,
756+ parts : [ textPart ( "msg-assistant-empty-text" , sessionID , "empty-text-part" , "" ) ] ,
757+ } ,
758+ buildMessage ( "msg-user-2" , "user" , sessionID , "continue" , 3 ) ,
759+ ]
760+ const state = createSessionState ( )
761+ const config = buildConfig ( "range" )
762+
763+ assignMessageRefs ( state , messages )
764+ injectMessageIds ( state , config , messages )
765+
766+ const emptyTextAssistant = messages [ 1 ] !
767+ assert . equal ( emptyTextAssistant . parts . length , 1 , "should not add a synthetic part" )
768+ assert . equal (
769+ ( emptyTextAssistant . parts [ 0 ] as any ) . text ,
770+ "" ,
771+ "empty text part should remain untouched" ,
772+ )
773+ } )
0 commit comments