fix(agent): bound compaction summary and mechanically fold on failure#4138
Merged
Conversation
A manual /compact (or auto-compaction) could show "compacting…" then hang forever: the call passed context.Background() with no deadline and summarize ranged over the provider channel without watching ctx, so a stalled stream pinned the placeholder with no card and no error. Fast failures already surfaced; an unbounded wait did not. Bound each summary call to 90s, select on ctx.Done so a stalled stream unblocks, retry one transient failure, and — when the summarizer is genuinely unreachable — fold mechanically (the region is already archived) to a deterministic marker so /compact always frees context instead of aborting on a still-full window. Verbatim user turns stay untouched and the fold/keep algorithm is unchanged, so cache behavior matches a normal compaction.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
A user reported that
/compactshowed "compacting conversation…" and then nothing — no card, no error, indefinitely.Root cause (traced through the code): after the
CompactionStartedevent, every fast failure already surfaces (compact failed: …for archive/stream/empty-output errors). The one gap was an unbounded wait:ctrl.Compact(context.Background(), …)— no deadline anywhere down the chain.summarizedidfor chunk := range ch, which only unblocks when the provider closes the channel — it never watchedctx.So a stalled summarizer stream (open but never delivering/closing — a hung connection, or a reasoning model streaming thinking with no final text) pinned the "compacting…" placeholder forever: no
CompactionDone, no error, no resolution.Fix
All in
internal/agent/compact.go; the fold/keep algorithm is unchanged.summaryTimeout).selectonctx.Done()in the stream loop, so a stalled stream unblocks even if the provider never closes the channel./compactthen always frees context instead of aborting on a still-full window (and auto-compaction can't loop on one). Verbatim user turns are untouched.Cache safety
The fold/keep logic is unchanged; the fallback produces a digest with the same structure as a normal compaction (head + kept-verbatim + digest + tail), just with deterministic content. No new prompt-history cache hazard beyond what compaction already does.
Tests
TestCompactFallsBackToMechanicalFoldWhenSummaryFails— summarizer errors → session still compacts,CompactionDonecarries a mechanical-fold summary.TestSummarizeRespectsContextCancel— a stalled (never-closing) stream returns on cancellation rather than hanging.internal/agentsuite green.Deferred
Live progress streaming ("thinking…" during a slow summary) touches the CLI + desktop renderers and is a separate change; the 90s bound + always-resolving fallback already removes the "infinite nothing" symptom.