From 5c2bae47ac8ee4e0421edf1bcae99411a5009238 Mon Sep 17 00:00:00 2001 From: can1357 Date: Mon, 13 Jul 2026 01:29:53 +0200 Subject: [PATCH] feat(mode): enabled conditional transcript compaction logic - Introduced conditional scrollback clearing during UI renders when transcript compaction is enabled. - Updated `CommandController` and `EventController` to respect the `display.collapseCompacted` setting. - Configured `SelectorController` to trigger a chat rebuild and UI reset when the compaction setting changes. - Updated `InteractiveMode` to dynamically toggle between collapsed and full inline history based on user settings. --- packages/coding-agent/CHANGELOG.md | 3 ++- .../src/modes/controllers/command-controller.ts | 10 +++++++++- .../src/modes/controllers/event-controller.ts | 14 +++++++++++++- .../src/modes/controllers/selector-controller.ts | 7 +++++++ .../coding-agent/src/modes/interactive-mode.ts | 9 ++++++--- 5 files changed, 37 insertions(+), 6 deletions(-) diff --git a/packages/coding-agent/CHANGELOG.md b/packages/coding-agent/CHANGELOG.md index 766f03e5a..2f95527a6 100644 --- a/packages/coding-agent/CHANGELOG.md +++ b/packages/coding-agent/CHANGELOG.md @@ -17,8 +17,9 @@ ### Fixed +- Fixed inconsistent history rendering when toggling the display setting for compacted items - Fixed configured `retry.fallbackChains` never engaging on non-retryable provider errors (e.g. "Cloud Code Assist API returned an empty response"): a hard error on a model covered by a fallback chain now switches to the next candidate instead of failing the turn, while still never backoff-retrying the failing model itself -- Fixed the post-compaction transcript rebuild (auto-compaction and `/compact`) repainting the entire collapsed transcript — welcome banner included — below the stale pre-compaction scrollback: the rebuild collapses history behind the summary divider, shrinking the frame far below the committed row count, and the renderer's "duplication, never loss" resync re-showed everything without retracting native scrollback; both rebuild paths now request `clearScrollback` like auto-handoff already did +- Fixed transcript rebuilds (compaction, `/compact`, and toggling history display) repainting content below stale scrollback when collapsing history; rebuilds now correctly clear the scrollback buffer when history is collapsed - Fixed mid-run auto-compaction spuriously warning "Compaction freed too little context to make progress" and pausing maintenance even when compaction genuinely shrank the context (observed: snapcompact took a 312k-token gpt-5.6 session to 86k real tokens and still dead-ended): the in-flight prompt's pending context snapshot — set once at run start and alive for the whole tool loop — was read as live residual context by the post-compaction headroom/retry-fit checks because the fresh compaction entry hides every earlier usage anchor; compaction (auto and `/compact`), the retry drop, and the dead-end shake rescue now rebase the snapshot onto the rewritten message set - Fixed backgrounded Bash blocks continuing to repaint with live and final job output; they now freeze with a compact job notice while completion is delivered separately diff --git a/packages/coding-agent/src/modes/controllers/command-controller.ts b/packages/coding-agent/src/modes/controllers/command-controller.ts index ef6363d89..44c99cbd4 100644 --- a/packages/coding-agent/src/modes/controllers/command-controller.ts +++ b/packages/coding-agent/src/modes/controllers/command-controller.ts @@ -1200,7 +1200,15 @@ export class CommandController { this.ctx.rebuildChatFromMessages(); this.ctx.statusLine.invalidate(); - this.ctx.ui.requestRender(); + // Same as the auto-compaction rebuild: a collapsed transcript is an + // intentional replacement, so drop the stale pre-compaction scrollback + // instead of repainting the shrunken frame below it. With collapse + // disabled the full history stays inline and scrollback is kept. + if (this.ctx.settings.get("display.collapseCompacted")) { + this.ctx.ui.requestRender(true, { clearScrollback: true }); + } else { + this.ctx.ui.requestRender(); + } } catch (error) { if (error instanceof CompactionCancelledError) { outcome = "cancelled"; diff --git a/packages/coding-agent/src/modes/controllers/event-controller.ts b/packages/coding-agent/src/modes/controllers/event-controller.ts index 08992fc5c..dee063c49 100644 --- a/packages/coding-agent/src/modes/controllers/event-controller.ts +++ b/packages/coding-agent/src/modes/controllers/event-controller.ts @@ -1234,7 +1234,19 @@ export class EventController { this.ctx.lastAssistantUsage = undefined; this.ctx.rebuildChatFromMessages(); this.ctx.statusLine.invalidate(); - this.ctx.ui.requestRender(); + // When history collapses behind the summary divider, the frame + // shrinks far below the committed row count; without clearing, the + // differential renderer's "duplication, never loss" resync repaints + // the whole collapsed transcript (welcome box included) BELOW the + // stale pre-compaction scrollback. Compaction is an intentional + // transcript replacement then — same as auto-handoff below. With + // collapse disabled the rebuilt transcript keeps the full history, + // so the resync handles it and scrollback stays. + if (settings.get("display.collapseCompacted")) { + this.ctx.ui.requestRender(true, { clearScrollback: true }); + } else { + this.ctx.ui.requestRender(); + } } else if (event.errorMessage) { this.ctx.showWarning(event.errorMessage); } else if (isHandoffAction) { diff --git a/packages/coding-agent/src/modes/controllers/selector-controller.ts b/packages/coding-agent/src/modes/controllers/selector-controller.ts index e49ad1827..1b983cc92 100644 --- a/packages/coding-agent/src/modes/controllers/selector-controller.ts +++ b/packages/coding-agent/src/modes/controllers/selector-controller.ts @@ -452,6 +452,13 @@ export class SelectorController { this.ctx.rebuildChatFromMessages(); this.ctx.ui.resetDisplay(); break; + case "display.collapseCompacted": + // Rebuild swaps between the collapsed tail and the full inline + // history; full reset retires blocks already committed to native + // scrollback (mirrors cacheMissMarker). + this.ctx.rebuildChatFromMessages(); + this.ctx.ui.resetDisplay(); + break; case "tui.tight": setTuiTight(value as boolean); this.ctx.ui.invalidate(); diff --git a/packages/coding-agent/src/modes/interactive-mode.ts b/packages/coding-agent/src/modes/interactive-mode.ts index c53bea59b..8311acbc2 100644 --- a/packages/coding-agent/src/modes/interactive-mode.ts +++ b/packages/coding-agent/src/modes/interactive-mode.ts @@ -1614,9 +1614,12 @@ export class InteractiveMode implements InteractiveModeContext { } } this.chatContainer.clear(); - // Live display uses the compacted transcript tail; export/resume callers - // can still request the full inline compaction history. - const context = this.viewSession.buildTranscriptSessionContext({ collapseCompactedHistory: true }); + // Live display collapses to the compacted transcript tail unless the + // user opted into the full inline history; export/resume callers choose + // their own mode. + const context = this.viewSession.buildTranscriptSessionContext({ + collapseCompactedHistory: settings.get("display.collapseCompacted"), + }); this.renderSessionContext(context); for (const child of liveComponents) { this.chatContainer.addChild(child);