























@@ -220,7 +220,7 @@ describe("sanitizeSessionMessagesImages", () => {
220220expect(out).toHaveLength(1);
221221expect(out[0]?.role).toBe("user");
222222});
223-it("keeps empty assistant error messages", async () => {
223+it("drops empty assistant error messages", async () => {
224224const input = castAgentMessages([
225225{ role: "user", content: "hello", timestamp: nextTimestamp() } satisfies UserMessage,
226226{
@@ -233,10 +233,68 @@ describe("sanitizeSessionMessagesImages", () => {
233233234234const out = await sanitizeSessionMessagesImages(input, "test");
235235236-expect(out).toHaveLength(3);
236+expect(out).toHaveLength(1);
237237expect(out[0]?.role).toBe("user");
238-expect(out[1]?.role).toBe("assistant");
239-expect(out[2]?.role).toBe("assistant");
238+});
239+it("removes empty text blocks from user and tool result messages", async () => {
240+const input = [
241+{
242+role: "user",
243+content: [
244+{ type: "text", text: "" },
245+{ type: "text", text: "hello" },
246+],
247+timestamp: nextTimestamp(),
248+} satisfies UserMessage,
249+{
250+role: "toolResult",
251+toolCallId: "tool-1",
252+toolName: "read",
253+isError: false,
254+content: [
255+{ type: "text", text: " " },
256+{ type: "text", text: "result" },
257+],
258+timestamp: nextTimestamp(),
259+} satisfies ToolResultMessage,
260+];
261+262+const out = await sanitizeSessionMessagesImages(input, "test");
263+264+expect(out[0]?.role).toBe("user");
265+expect((out[0] as { content?: Array<{ text?: string }> }).content).toEqual([
266+{ type: "text", text: "hello" },
267+]);
268+expect(out[1]?.role).toBe("toolResult");
269+expect((out[1] as { content?: Array<{ text?: string }> }).content).toEqual([
270+{ type: "text", text: "result" },
271+]);
272+});
273+it("uses a non-empty placeholder when user or tool result content becomes empty", async () => {
274+const input = [
275+{
276+role: "user",
277+content: [{ type: "text", text: "" }],
278+timestamp: nextTimestamp(),
279+} satisfies UserMessage,
280+{
281+role: "toolResult",
282+toolCallId: "tool-1",
283+toolName: "read",
284+isError: false,
285+content: [{ type: "text", text: " " }],
286+timestamp: nextTimestamp(),
287+} satisfies ToolResultMessage,
288+];
289+290+const out = await sanitizeSessionMessagesImages(input, "test");
291+292+expect((out[0] as { content?: Array<{ text?: string }> }).content).toEqual([
293+{ type: "text", text: "[empty content omitted]" },
294+]);
295+expect((out[1] as { content?: Array<{ text?: string }> }).content).toEqual([
296+{ type: "text", text: "[empty content omitted]" },
297+]);
240298});
241299it("leaves non-assistant messages unchanged", async () => {
242300const input = [
@@ -338,7 +396,6 @@ describe("sanitizeSessionMessagesImages", () => {
338396expect(content?.map((block) => block.type)).toEqual([
339397"thinking",
340398"text",
341-"text",
342399"redacted_thinking",
343400"text",
344401]);
@@ -347,12 +404,32 @@ describe("sanitizeSessionMessagesImages", () => {
347404thinking: "first",
348405thought_signature: "sig-1",
349406});
350-expect(content?.[1]).toMatchObject({ type: "text", text: "" });
351-expect(content?.[3]).toMatchObject({
407+expect(content?.[1]).toMatchObject({ type: "text", text: "visible" });
408+expect(content?.[2]).toMatchObject({
352409type: "redacted_thinking",
353410thought_signature: "sig-2",
354411});
355412});
413+414+it("drops empty assistant text blocks in images-only mode", async () => {
415+const input = castAgentMessages([
416+makeOpenAiResponsesAssistantMessage(
417+[
418+{ type: "text", text: " " },
419+{ type: "text", text: "visible" },
420+],
421+"stop",
422+),
423+]);
424+425+const out = await sanitizeSessionMessagesImages(input, "test", {
426+sanitizeMode: "images-only",
427+});
428+429+expectSingleAssistantContentEntry(out, (entry) => {
430+expect(entry.text).toBe("visible");
431+});
432+});
356433});
357434});
358435此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。