@@ -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