🤖 codex: strip empty assistant blocks from history (#210)

This commit is contained in:
Josh Palmer
2026-01-08 21:53:20 +01:00
parent 884e734809
commit 2fae8eda66
2 changed files with 94 additions and 0 deletions

View File

@@ -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");

View File

@@ -99,6 +99,28 @@ export async function sanitizeSessionMessagesImages(
}
}
if (role === "assistant") {
const assistantMsg = msg as Extract<AgentMessage, { role: "assistant" }>;
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;