From f456a7a4db1d00bec909c07728b039ca34d91201 Mon Sep 17 00:00:00 2001 From: Mario Zechner Date: Fri, 27 Mar 2026 04:00:21 +0100 Subject: [PATCH] fix(coding-agent): restore compaction summary and dedupe edit errors --- packages/coding-agent/CHANGELOG.md | 3 +++ packages/coding-agent/src/core/tools/edit.ts | 9 ++++++++- .../src/modes/interactive/interactive-mode.ts | 9 ++++++++- .../test/interactive-mode-compaction.test.ts | 11 +++++++++-- 4 files changed, 28 insertions(+), 4 deletions(-) diff --git a/packages/coding-agent/CHANGELOG.md b/packages/coding-agent/CHANGELOG.md index 9263d2b8..c7b8d0d5 100644 --- a/packages/coding-agent/CHANGELOG.md +++ b/packages/coding-agent/CHANGELOG.md @@ -4,10 +4,13 @@ ### Fixed +- Documented `tool_call` input mutation as supported extension API behavior, clarified that post-mutation inputs are not re-validated, and added regression coverage for executing mutated tool arguments ([#2611](https://github.com/badlogic/pi-mono/issues/2611)) - Fixed repeated compactions dropping messages that were kept by an earlier compaction by re-summarizing from the previous kept boundary and recalculating `tokensBefore` from the rebuilt session context ([#2608](https://github.com/badlogic/pi-mono/issues/2608)) - Fixed interactive compaction UI updates so `ctx.compact()` rebuilds the chat through unified compaction events, manual compaction no longer duplicates the summary block, and the `trigger-compact` example only fires when context usage crosses its threshold ([#2617](https://github.com/badlogic/pi-mono/issues/2617)) +- Fixed interactive compaction completion to append a synthetic compaction summary after rebuilding the chat so the latest compaction remains visible at the bottom - Fixed skill discovery to stop recursing once a directory contains `SKILL.md`, and to ignore root `*.md` files in `.agents/skills` while keeping root markdown skill files supported in `~/.pi/agent/skills`, `.pi/skills`, and package `skills/` directories ([#2603](https://github.com/badlogic/pi-mono/issues/2603)) - Fixed edit tool diff rendering for multi-edit operations with large unchanged gaps so distant edits collapse intermediate context instead of dumping the full unchanged middle block +- Fixed edit tool error rendering to avoid repeating the same exact-match failure in both the preview and result blocks - Fixed auto-compaction overflow recovery for Ollama models when the backend returns explicit `prompt too long; exceeded max context length ...` errors instead of silently truncating input ([#2626](https://github.com/badlogic/pi-mono/issues/2626)) - Fixed built-in tool overrides that reuse built-in parameter schemas to still honor custom `renderCall` and `renderResult` renderers in the interactive TUI, restoring the `minimal-mode` example ([#2595](https://github.com/badlogic/pi-mono/issues/2595)) diff --git a/packages/coding-agent/src/core/tools/edit.ts b/packages/coding-agent/src/core/tools/edit.ts index cb493ea6..210f50b9 100644 --- a/packages/coding-agent/src/core/tools/edit.ts +++ b/packages/coding-agent/src/core/tools/edit.ts @@ -215,7 +215,14 @@ function formatEditResult( .filter((c) => c.type === "text") .map((c) => c.text || "") .join("\n"); - return errorText ? `\n${theme.fg("error", errorText)}` : undefined; + let previewError: string | undefined; + if (state.preview && "error" in state.preview) { + previewError = state.preview.error; + } + if (!errorText || errorText === previewError) { + return undefined; + } + return `\n${theme.fg("error", errorText)}`; } const previewDiff = state.preview && !("error" in state.preview) ? state.preview.diff : undefined; diff --git a/packages/coding-agent/src/modes/interactive/interactive-mode.ts b/packages/coding-agent/src/modes/interactive/interactive-mode.ts index dc0ed3df..2916979b 100644 --- a/packages/coding-agent/src/modes/interactive/interactive-mode.ts +++ b/packages/coding-agent/src/modes/interactive/interactive-mode.ts @@ -57,7 +57,7 @@ import type { } from "../../core/extensions/index.js"; import { FooterDataProvider, type ReadonlyFooterDataProvider } from "../../core/footer-data-provider.js"; import { type AppKeybinding, KeybindingsManager } from "../../core/keybindings.js"; - +import { createCompactionSummaryMessage } from "../../core/messages.js"; import { findExactModelReferenceMatch, resolveModelScope } from "../../core/model-resolver.js"; import { DefaultPackageManager } from "../../core/package-manager.js"; import type { ResourceDiagnostic } from "../../core/resource-loader.js"; @@ -2438,6 +2438,13 @@ export class InteractiveMode { } else if (event.result) { this.chatContainer.clear(); this.rebuildChatFromMessages(); + this.addMessageToChat( + createCompactionSummaryMessage( + event.result.summary, + event.result.tokensBefore, + new Date().toISOString(), + ), + ); this.footer.invalidate(); } else if (event.errorMessage) { if (event.reason === "manual") { diff --git a/packages/coding-agent/test/interactive-mode-compaction.test.ts b/packages/coding-agent/test/interactive-mode-compaction.test.ts index 16d80c64..4a3c1ae2 100644 --- a/packages/coding-agent/test/interactive-mode-compaction.test.ts +++ b/packages/coding-agent/test/interactive-mode-compaction.test.ts @@ -2,7 +2,7 @@ import { describe, expect, test, vi } from "vitest"; import { InteractiveMode } from "../src/modes/interactive/interactive-mode.js"; describe("InteractiveMode compaction events", () => { - test("rebuilds chat without appending a duplicate compaction summary", async () => { + test("rebuilds chat and appends a synthetic compaction summary at the bottom", async () => { const fakeThis = { isInitialized: true, footer: { invalidate: vi.fn() }, @@ -44,7 +44,14 @@ describe("InteractiveMode compaction events", () => { expect(fakeThis.chatContainer.clear).toHaveBeenCalledTimes(1); expect(fakeThis.rebuildChatFromMessages).toHaveBeenCalledTimes(1); - expect(fakeThis.addMessageToChat).not.toHaveBeenCalled(); + expect(fakeThis.addMessageToChat).toHaveBeenCalledTimes(1); + expect(fakeThis.addMessageToChat).toHaveBeenCalledWith( + expect.objectContaining({ + role: "compactionSummary", + tokensBefore: 123, + summary: "summary", + }), + ); expect(fakeThis.flushCompactionQueue).toHaveBeenCalledWith({ willRetry: false }); }); });