diff --git a/CHANGELOG.md b/CHANGELOG.md index 0731f130d..c4983e801 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,6 +30,7 @@ - Security: per-agent mention patterns and group elevated directives now require explicit mention to avoid cross-agent toggles. - Config: support inline env vars in config (`env.*` / `env.vars`) and document env precedence. - Agent: enable adaptive context pruning by default for tool-result trimming. +- Agent: drop empty error assistant messages when sanitizing session history. (#591) — thanks @steipete - Doctor: check config/state permissions and offer to tighten them. — thanks @steipete - Doctor/Daemon: audit supervisor configs, add --repair/--force flows, surface service config audits in daemon status, and document user vs system services. — thanks @steipete - Doctor: repair gateway service entrypoint when switching between npm and git installs; add Docker e2e coverage. — thanks @steipete diff --git a/src/agents/pi-embedded-helpers.test.ts b/src/agents/pi-embedded-helpers.test.ts index df8c5c019..6908e2176 100644 --- a/src/agents/pi-embedded-helpers.test.ts +++ b/src/agents/pi-embedded-helpers.test.ts @@ -304,6 +304,19 @@ describe("sanitizeSessionMessagesImages", () => { expect(out[0]?.role).toBe("user"); }); + it("drops empty assistant error messages", async () => { + const input = [ + { role: "user", content: "hello" }, + { role: "assistant", stopReason: "error", content: [] }, + { role: "assistant", stopReason: "error" }, + ] 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" }, diff --git a/src/agents/pi-embedded-helpers.ts b/src/agents/pi-embedded-helpers.ts index bbfd8e0c4..070c5f9e0 100644 --- a/src/agents/pi-embedded-helpers.ts +++ b/src/agents/pi-embedded-helpers.ts @@ -61,6 +61,21 @@ export async function ensureSessionHeader(params: { type ContentBlock = AgentToolResult["content"][number]; +function isEmptyAssistantErrorMessage( + message: Extract, +): boolean { + if (message.stopReason !== "error") return false; + const content = message.content; + if (content == null) return true; + if (!Array.isArray(content)) return false; + return content.every((block) => { + if (!block || typeof block !== "object") return true; + const rec = block as { type?: unknown; text?: unknown }; + if (rec.type !== "text") return false; + return typeof rec.text !== "string" || rec.text.trim().length === 0; + }); +} + export async function sanitizeSessionMessagesImages( messages: AgentMessage[], label: string, @@ -101,6 +116,9 @@ export async function sanitizeSessionMessagesImages( if (role === "assistant") { const assistantMsg = msg as Extract; + if (isEmptyAssistantErrorMessage(assistantMsg)) { + continue; + } const content = assistantMsg.content; if (Array.isArray(content)) { const filteredContent = content.filter((block) => {