Skip to content

feat: acp_status shows visible context breakdown + compressed blocks#91

Merged
ranxianglei merged 12 commits into
masterfrom
2026-07-09_acp-status-breakdown
Jul 10, 2026
Merged

feat: acp_status shows visible context breakdown + compressed blocks#91
ranxianglei merged 12 commits into
masterfrom
2026-07-09_acp-status-breakdown

Conversation

@ranxianglei

Copy link
Copy Markdown
Owner

Summary

acp_status now returns two sections instead of just the compressed block list:

  1. VISIBLE CONTEXT — token breakdown by category (tool outputs, code, text, summaries) with percentages. In detailed mode, also shows largest items per category (Largest tool outputs: m00661 (12.3K), ...).
  2. COMPRESSED BLOCKS — the existing block list (sizes, ages, ranges, topics).

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_status but only saw compression blocks — it couldn't see which tool types were bloating context.

Solution

Reuse estimateContextComposition() from lib/messages/inject/utils.ts (the same function that powers the automatic nudge breakdown) inside acp_status. Fetch visible messages via fetchSessionMessages, filter out pruned messages, compute composition, render alongside the block list.

Example output (detailed mode):

VISIBLE CONTEXT (uncompressed)
  47.3K total | 28.1K tool (59%) | 8.5K code (18%) | 3.2K text (7%) | 7.5K summaries (16%)
  Largest tool outputs: m00661 (12.3K), m00677 (8.1K), m00685 (5.2K)
  Largest code messages: m00580 (3.2K), m00590 (2.1K)
  Largest text messages: m00550 (1.8K)

COMPRESSED BLOCKS — 65 active (24.0K summary, 366.7K original compressed)

  b20    28.7K→1.0K   1d ago   m00098–m00156  "Discussion resolved: implement V3-noB"
  ...

Files changed

  • lib/compress/status.ts — added visible context breakdown section, fetch messages in execute
  • lib/prompts/system.ts — updated tool description + acp_status guidance
  • tests/acp-status.test.ts — updated assertions for new header format

Test plan

  • npm run typecheck passes
  • npm run build passes
  • 603 tests pass (1 pre-existing Bun incompatibility)
  • All 17 acp-status tests pass with new format

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
@ranxianglei ranxianglei merged commit ee3c5fa into master Jul 10, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant