refactor: drop empty error messages in history

This commit is contained in:
Peter Steinberger
2026-01-09 18:20:01 +01:00
parent 62bf779c14
commit 79b3abd797
3 changed files with 32 additions and 0 deletions

View File

@@ -30,6 +30,7 @@
- Security: per-agent mention patterns and group elevated directives now require explicit mention to avoid cross-agent toggles. - 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. - 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: enable adaptive context pruning by default for tool-result trimming.
- Agent: drop empty error assistant messages when sanitizing session history.
- Doctor: check config/state permissions and offer to tighten them. — 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/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 - Doctor: repair gateway service entrypoint when switching between npm and git installs; add Docker e2e coverage. — thanks @steipete

View File

@@ -304,6 +304,19 @@ describe("sanitizeSessionMessagesImages", () => {
expect(out[0]?.role).toBe("user"); 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 () => { it("leaves non-assistant messages unchanged", async () => {
const input = [ const input = [
{ role: "user", content: "hello" }, { role: "user", content: "hello" },

View File

@@ -61,6 +61,21 @@ export async function ensureSessionHeader(params: {
type ContentBlock = AgentToolResult<unknown>["content"][number]; type ContentBlock = AgentToolResult<unknown>["content"][number];
function isEmptyAssistantErrorMessage(
message: Extract<AgentMessage, { role: "assistant" }>,
): 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( export async function sanitizeSessionMessagesImages(
messages: AgentMessage[], messages: AgentMessage[],
label: string, label: string,
@@ -101,6 +116,9 @@ export async function sanitizeSessionMessagesImages(
if (role === "assistant") { if (role === "assistant") {
const assistantMsg = msg as Extract<AgentMessage, { role: "assistant" }>; const assistantMsg = msg as Extract<AgentMessage, { role: "assistant" }>;
if (isEmptyAssistantErrorMessage(assistantMsg)) {
continue;
}
const content = assistantMsg.content; const content = assistantMsg.content;
if (Array.isArray(content)) { if (Array.isArray(content)) {
const filteredContent = content.filter((block) => { const filteredContent = content.filter((block) => {