feat: acp_status shows visible context breakdown + compressed blocks#91
Merged
Conversation
acp_status previously only showed the compressed block list. The model had no way to see what's consuming context in the visible (uncompressed) portion — it could only see this via the automatic context breakdown injection, which fires intermittently. Now acp_status returns two sections: 1. VISIBLE CONTEXT — token breakdown by category (tool/code/text/summaries) with percentages. In detailed mode, also shows largest items per category (same data as the inject.ts breakdown). 2. COMPRESSED BLOCKS — the existing block list (unchanged). The visible breakdown uses estimateContextComposition() from inject/utils, reusing the same logic that powers the automatic nudge injection. System prompt updated to describe the new dual-section output. Tests updated to match new header format.
- Inject compression block summaries into visible messages so summary tokens are correctly counted (was showing 0% summaries because prune step injects summaries at runtime, not in DB) - Add tool name + key arg to largest tool output descriptions in detailed mode (was just 'm01315 (5.0K)' with no context) - Detailed mode now shows each largest tool output on its own line with description: 'm01315 (5.0K) bash: git push github...'
- ContextComposition now includes toolTypeBreakdown: {tool, tokens}[]
- acp_status shows 'Top tools: bash (44%), write (21%), ...' (3 in
summary mode, 5 in detailed mode)
- inject.ts nudge text and mini breakdown both include 'Top tools:' line
- describeToolMessage fixed: reads input from part.state.input (not
part.input) — now shows command/filePath/query/pattern args
- Summary mode streamlined: category line + top tools only (no largest
items)
- Detailed mode: category + top tools + largest items with descriptions
When a category has tokens > 0 but the percentage rounds to 0% (e.g. 93 code tokens out of 113K total), show 1% instead of 0% to avoid the misleading impression that the category is empty.
@Dog design direction: - Remove mini breakdown entirely (was redundant with full nudge) - Remove Top blocks from nudge (compressed stats belong in acp_status) - Nudge now shows only: Breakdown + Top tools + Largest items (refs only) - Largest items already had no descriptions (model sees content inline) - Mini breakdown code (~18 lines) removed from inject.ts - toolOutputReminder now just appends the⚠️ warning without breakdown
- largestToolRanges now includes {tool} field (first tool name per message)
- Nudge shows: 'm01425 (3.4K) write, m01439 (2.9K) bash, ...'
- Limited to 10 items in nudge (was unbounded from composition's 15)
New parameter design: - scope: 'compressed' | 'uncompressed' — drill into one section - tool: filter by tool type (e.g., 'todowrite', 'bash') — uncompressed only - sort: 'size' | 'time' | 'tool' — list sort order - No args = overview (both visible + compressed) Removed old mode: 'summary' | 'detailed' — drilldown replaces verbosity flag. scope=compressed always shows full block details (age, generation, consumed lineage). scope=uncompressed shows per-message list with refs + tokens + tool type. Overview works even when message fetch fails (shows blocks section).
…art/step-finish
Root cause: part.type check was 'not text and not reasoning' which caught
step-start and step-finish metadata parts as tool parts. These have no
.tool field, so toolName fell back to part.type ('step-start').
Fix: only process part.type === 'tool' as actual tool parts. This correctly
identifies bash/write/edit/todowrite etc. Applied to both:
- collectVisibleMessages() in status.ts
- estimateContextComposition() in utils.ts (affects nudge too)
New compress mode: pass toolType instead of content to prune all old
messages of that tool type. Keeps latest N (default 3). Creates a single
compression block covering scattered messages.
Example: compress({topic:"old todowrite", toolType:"todowrite", keepLatest:3})
Updated:
- compress tool schema: content now optional, added toolType + keepLatest
- system prompt: compress + acp_status descriptions updated
- compress-range prompt: mentions both modes
Reverted: compress toolType param (dangerous to mix compress+delete) Added: standalone prune tool (lib/compress/prune-tool.ts) - Takes toolType + keepLatest params - Adds tool call IDs to state.prune.tools - Uses existing prune mechanism (no compression blocks) Fixed: prune.ts:95 — removed edit/write restriction so ALL tool types can be pruned when their callID is in state.prune.tools Updated: system prompt — compress and prune described separately
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.
Summary
acp_statusnow returns two sections instead of just the compressed block list:detailedmode, also shows largest items per category (Largest tool outputs: m00661 (12.3K), ...).Problem
The model had no on-demand way to see what's consuming context. The tool/code/text breakdown was only available via the automatic nudge injection, which fires intermittently based on context thresholds. When a user asked the model to "check ACP status and compress garbage", the model called
acp_statusbut only saw compression blocks — it couldn't see which tool types were bloating context.Solution
Reuse
estimateContextComposition()fromlib/messages/inject/utils.ts(the same function that powers the automatic nudge breakdown) insideacp_status. Fetch visible messages viafetchSessionMessages, filter out pruned messages, compute composition, render alongside the block list.Example output (detailed mode):
Files changed
lib/compress/status.ts— added visible context breakdown section, fetch messages in executelib/prompts/system.ts— updated tool description + acp_status guidancetests/acp-status.test.ts— updated assertions for new header formatTest plan
npm run typecheckpassesnpm run buildpasses