diff --git a/src/agents/pi-embedded-helpers.test.ts b/src/agents/pi-embedded-helpers.test.ts index b88142c6a..df8c5c019 100644 --- a/src/agents/pi-embedded-helpers.test.ts +++ b/src/agents/pi-embedded-helpers.test.ts @@ -8,6 +8,7 @@ import { isMessagingToolDuplicate, normalizeTextForComparison, sanitizeGoogleTurnOrdering, + sanitizeSessionMessagesImages, validateGeminiTurns, } from "./pi-embedded-helpers.js"; import { @@ -250,6 +251,77 @@ describe("sanitizeGoogleTurnOrdering", () => { }); }); +describe("sanitizeSessionMessagesImages", () => { + it("removes empty assistant text blocks but preserves tool calls", async () => { + const input = [ + { + role: "assistant", + content: [ + { type: "text", text: "" }, + { type: "toolCall", id: "call_1", name: "read", arguments: {} }, + ], + }, + ] satisfies AgentMessage[]; + + const out = await sanitizeSessionMessagesImages(input, "test"); + + expect(out).toHaveLength(1); + const content = (out[0] as { content?: unknown }).content; + expect(Array.isArray(content)).toBe(true); + expect(content).toHaveLength(1); + expect((content as Array<{ type?: string }>)[0]?.type).toBe("toolCall"); + }); + + it("filters whitespace-only assistant text blocks", async () => { + const input = [ + { + role: "assistant", + content: [ + { type: "text", text: " " }, + { type: "text", text: "ok" }, + ], + }, + ] satisfies AgentMessage[]; + + const out = await sanitizeSessionMessagesImages(input, "test"); + + expect(out).toHaveLength(1); + const content = (out[0] as { content?: unknown }).content; + expect(Array.isArray(content)).toBe(true); + expect(content).toHaveLength(1); + expect((content as Array<{ text?: string }>)[0]?.text).toBe("ok"); + }); + + it("drops assistant messages that only contain empty text", async () => { + const input = [ + { role: "user", content: "hello" }, + { role: "assistant", content: [{ type: "text", text: "" }] }, + ] satisfies AgentMessage[]; + + const out = await sanitizeSessionMessagesImages(input, "test"); + + expect(out).toHaveLength(1); + expect(out[0]?.role).toBe("user"); + }); + + it("leaves non-assistant messages unchanged", async () => { + const input = [ + { role: "user", content: "hello" }, + { + role: "toolResult", + toolUseId: "tool-1", + content: [{ type: "text", text: "result" }], + }, + ] satisfies AgentMessage[]; + + const out = await sanitizeSessionMessagesImages(input, "test"); + + expect(out).toHaveLength(2); + expect(out[0]?.role).toBe("user"); + expect(out[1]?.role).toBe("toolResult"); + }); +}); + describe("normalizeTextForComparison", () => { it("lowercases text", () => { expect(normalizeTextForComparison("Hello World")).toBe("hello world"); diff --git a/src/agents/pi-embedded-helpers.ts b/src/agents/pi-embedded-helpers.ts index 2d3941006..bbfd8e0c4 100644 --- a/src/agents/pi-embedded-helpers.ts +++ b/src/agents/pi-embedded-helpers.ts @@ -99,6 +99,28 @@ export async function sanitizeSessionMessagesImages( } } + if (role === "assistant") { + const assistantMsg = msg as Extract; + const content = assistantMsg.content; + if (Array.isArray(content)) { + const filteredContent = content.filter((block) => { + if (!block || typeof block !== "object") return true; + const rec = block as { type?: unknown; text?: unknown }; + if (rec.type !== "text" || typeof rec.text !== "string") return true; + return rec.text.trim().length > 0; + }); + const sanitizedContent = (await sanitizeContentBlocksImages( + filteredContent as unknown as ContentBlock[], + label, + )) as unknown as typeof assistantMsg.content; + if (sanitizedContent.length === 0) { + continue; + } + out.push({ ...assistantMsg, content: sanitizedContent }); + continue; + } + } + out.push(msg); } return out;