Skip to content

feat(claude): compact old threads before they burn through usage - #8144

Open
t3dotgg wants to merge 7 commits into
mainfrom
t3code/investigate-claude-usage-burn
Open

feat(claude): compact old threads before they burn through usage#8144
t3dotgg wants to merge 7 commits into
mainfrom
t3code/investigate-claude-usage-burn

Conversation

@t3dotgg

@t3dotgg t3dotgg commented Aug 24, 2026

Copy link
Copy Markdown
Member

Claude threads can resume with hundreds of thousands of old context tokens and burn through usage before the user realizes it. T3 also did not show Claude Code's resume summary prompt or expose an easy compaction control.

This change matches Claude Code's 70-minute and 100,000-token resume conditions, supports its native resume dialog on web and mobile, adds /compact to provider command menus, and puts a compact action in the context meter. Claude provider settings can now set an earlier auto-compaction threshold without shrinking the 1M context window.

Before

Claude thread without an old-context warning

After

Claude thread with an old-context compaction prompt

Claude's native resume summary choices

Claude provider auto-compaction setting

Verified with 142 server tests, 61 contract tests, 26 web tests, and scoped type checks for server, web, mobile, desktop, contracts, and client runtime. Screenshots use synthetic Sonnet 5 conversations and contain no personal thread data.

Built by GPT-5.6 Sol in the Codex harness.


Note

Medium Risk
Changes Claude session resume and context-shaping behavior via the adapter and composer; mistakes could drop history or misfire prompts, but flows mirror Claude Code’s native dialog and are user-gated.

Overview
Adds Claude session compaction end-to-end: when resuming an old thread, the server wires Claude’s resume_return dialog through the existing user-input flow (onUserDialog → compact / keep history / don’t ask again), and forwards optional autoCompactWindow from provider settings into Claude query settings.

Settings & telemetry: New autoCompactWindow on Claude provider config (100k–1M tokens or empty). Token/context snapshots now carry autoCompactThreshold so the context meter can show “compacts at N tokens.” Claude provider status always includes a /compact slash command.

Web: A composer banner nudges compaction when a Claude thread matches ~70 minutes + 100k tokens (with dismissals synced to native “don’t ask again”). ContextWindowMeter and ChatComposer.compactContext() submit /compact, with guards when drafts, approvals, or pending inputs block it.

Reviewed by Cursor Bugbot for commit 7dcf3f1. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Add auto-compaction for old Claude threads to reduce context usage

  • Adds a configurable autoCompactWindow setting to ClaudeSettings (empty or 100000–1000000 tokens), forwarded to the Claude SDK via settings.autoCompactWindow on query creation
  • Handles resume_return dialogs from Claude by routing them through the shared user-input UI, offering Compact / Continue / Never options
  • Surfaces a resume-compaction banner in ChatViewContent gated by provider, token usage, and session age, with localStorage-backed permanent dismissal
  • Adds a compactContext imperative handle on ChatComposer and a "Compact context" button in the ContextWindowMeter popover that submits /compact; the compact slash command is now advertised in Claude provider capabilities
  • Propagates autoCompactThreshold through ThreadTokenUsageSnapshot and derived context-window snapshots so the UI can display exact thresholds
  • Risk: ChatComposerProps replaces activeThreadActivities with activeContextWindow plus compactDisabled/compactDisabledReason; any consumer still passing the old prop will break

Macroscope summarized 7dcf3f1.

@coderabbitai

coderabbitai Bot commented Aug 24, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: f0499cab-4d9e-459c-96fc-54ba3f268a50

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:L 100-499 changed lines (additions + deletions). labels Aug 24, 2026
Comment thread apps/web/src/components/chat/ChatComposer.tsx
@github-actions

github-actions Bot commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

Thread transfer impact

✅ Thread transfer remains within every enforced ceiling.

Provider Metric Main baseline This PR Impact PR ceiling
Codex Total thread wire 13.4 KiB 13.3 KiB −128 B (−0.9%) 15.1 KiB
Codex Thread snapshot wire 6.9 KiB 6.9 KiB −8 B (−0.1%) 7.3 KiB
Codex Live turn WebSocket wire 6.5 KiB 6.4 KiB −120 B (−1.8%) 7.8 KiB
Codex Live turn WebSocket decoded 55.0 KiB 55.6 KiB +602 B (+1.1%) 66.4 KiB
Codex Live turn messages 16 11 −5 (−31.3%) 21
Claude Total thread wire 13.4 KiB 13.3 KiB −102 B (−0.7%) 15.1 KiB
Claude Thread snapshot wire 6.9 KiB 6.9 KiB +4 B (+0.1%) 7.3 KiB
Claude Live turn WebSocket wire 6.6 KiB 6.5 KiB −106 B (−1.6%) 7.8 KiB
Claude Live turn WebSocket decoded 55.8 KiB 56.4 KiB +620 B (+1.1%) 66.4 KiB
Claude Live turn messages 16 11 −5 (−31.3%) 21

Baseline: 9996038 · PR result: 7dcf3f1 · Source CI: success

Scenario and decoded snapshot size

10 historical turns, 5 command tools per turn, 878.9 KiB retained MCP result per historical turn, and a 1.05 MiB retained result in the measured turn.

  • Codex decoded thread snapshot: 109.4 KiB
  • Claude decoded thread snapshot: 110.1 KiB

Updated in place by a trusted workflow. PR artifacts are strictly validated and never executed.

@macroscopeapp macroscopeapp Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One consistency finding in the changed web UI: the new resume-compaction notice is a hand-rolled banner inside the composer glass shell instead of the existing composer banner/drawer surface contract. Details inline.

Posted via Macroscope — UI Consistency

Comment thread apps/web/src/components/chat/ChatComposer.tsx Outdated
Comment thread apps/web/src/components/chat/ChatComposer.tsx
Comment thread apps/web/src/components/chat/ContextWindowMeter.logic.ts
@macroscopeapp

macroscopeapp Bot commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — This PR introduces a substantial Claude compaction workflow spanning SDK settings, resume dialogs, context tracking, slash commands, and composer/banner UI. Unresolved findings also identify cases where compaction may silently do nothing or be sent as ordinary prompt text, so the runtime behavior warrants human review.

Not approved because:

  • 2 blocking correctness issues found at or above your repo's Minimum Blocking Severity

Adjust the Minimum Blocking Severity for this repo — including turning it Off — in Settings. You can add or adjust custom eligibility rules. Learn more.

@macroscopeapp macroscopeapp Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

UI consistency review of the web changes. The resume-compaction notice now uses the shared ComposerBannerStack contract, which resolves the earlier finding. Two smaller consistency/interaction points remain, both inline.

Posted via Macroscope — UI Consistency

Comment thread apps/web/src/components/chat/ContextWindowMeter.tsx Outdated
Comment thread apps/web/src/components/ChatView.tsx Outdated
Comment thread apps/web/src/components/ChatView.tsx Outdated
Comment thread apps/web/src/components/chat/ChatComposer.tsx
Comment thread apps/web/src/components/chat/ContextWindowMeter.logic.ts Outdated
Comment thread apps/web/src/components/ChatView.tsx

@macroscopeapp macroscopeapp Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One finding on the new compaction affordances: the shared compactDisabled flag silently deadens both new controls, which departs from how every neighbouring composer/banner action communicates an unavailable state. Details inline.

Posted via Macroscope — UI Consistency

Comment thread apps/web/src/components/ChatView.tsx Outdated
Comment thread apps/web/src/components/chat/ChatComposer.tsx Outdated
Comment thread apps/web/src/components/chat/ChatComposer.tsx
Comment thread apps/web/src/components/ChatView.tsx

@macroscopeapp macroscopeapp Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One finding: the new composer draft-content gate re-implements a shared predicate instead of reusing it. The previously flagged hover-popover grace period (closeDelay) and the banner action's button treatment both look resolved.

Posted via Macroscope — UI Consistency

Comment thread apps/web/src/components/ChatView.tsx

@macroscopeapp macroscopeapp Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Follow-up on the disabled-reason handling added in d803371: the reason is now delivered only through aria-label on a disabled Button. buttonVariants sets disabled:pointer-events-none and a disabled <button> is out of the tab order, so that name is unreachable by hover, focus, or AT — sighted users still see a dead control with no explanation — while it also overrides the visible label when the button is enabled. The repo already has a disabled-reason contract (DisabledReasonTooltip in RightPanelTabs.tsx, CloudLinkSwitch in ConnectionsSettings.tsx: Tooltip + TooltipTrigger render={<span className="inline-flex">…</span>}), and the sibling composer-banner actions encode state in visible text (Stopping..., Waking..., Restoring...). Two inline notes below.

Posted via Macroscope — UI Consistency

Comment thread apps/web/src/components/chat/ContextWindowMeter.tsx
Comment thread apps/web/src/components/ChatView.tsx Outdated

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit d803371. Configure here.

Comment thread apps/web/src/components/ChatView.tsx
!activeProject ||
!isServerThread ||
selectedProvider !== "claudeAgent" ||
!compactionProviderAvailable ||

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Medium components/ChatView.tsx:4780

The resume-compaction banner enables Compact when the started thread's Claude instance is disabled but only a different continuation group is available, so compactContext returns without dispatching and the click appears to do nothing. compactionProviderAvailable must use the same locked-instance/continuation-group eligibility as the composer, or the banner must be disabled when the composer has noProviderAvailable.

🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
In file @apps/web/src/components/ChatView.tsx around line 4780:

The resume-compaction banner enables `Compact` when the started thread's Claude instance is disabled but only a different continuation group is available, so `compactContext` returns without dispatching and the click appears to do nothing. `compactionProviderAvailable` must use the same locked-instance/continuation-group eligibility as the composer, or the banner must be disabled when the composer has `noProviderAvailable`.

Evidence trail:
7dcf3f1: apps/web/src/components/ChatView.tsx:2782-2794, 4775-4827; apps/web/src/components/chat/ChatComposer.tsx:841-895, 919-925, 1994-2012; apps/server/src/provider/Drivers/ClaudeHome.ts:37-40

disabled={compactDisabled}
onClick={() => {
if (compactDisabled) return;
composerRef.current?.compactContext();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟠 High components/ChatView.tsx:4827

Clicking Compact sends Ultrathink:\n/compact for Claude models using the ultrathink prompt-injected effort, so Claude Code treats it as ordinary prompt text instead of a /compact command and consumes a turn. compactContext() enters the normal onSend path, which applies formatOutgoingPrompt; bypass effort injection for this command or dispatch compaction through a dedicated path.

🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
In file @apps/web/src/components/ChatView.tsx around line 4827:

Clicking `Compact` sends `Ultrathink:\n/compact` for Claude models using the `ultrathink` prompt-injected effort, so Claude Code treats it as ordinary prompt text instead of a `/compact` command and consumes a turn. `compactContext()` enters the normal `onSend` path, which applies `formatOutgoingPrompt`; bypass effort injection for this command or dispatch compaction through a dedicated path.

Evidence trail:
Reviewed commit: 7dcf3f1. `apps/web/src/components/ChatView.tsx:4820-4828` calls `composerRef.current?.compactContext()`. `apps/web/src/components/chat/ChatComposer.tsx:1994-2012` sets `/compact` and invokes `submitComposer()`. `apps/web/src/components/ChatView.tsx:5473-5490` only handles `/plan` and `/default` as standalone commands. `apps/web/src/components/ChatView.tsx:547-556` and `apps/web/src/components/ChatView.tsx:5550-5559` apply outgoing prompt formatting. `packages/shared/src/model.ts:357-371` adds the `Ultrathink:\n` prefix. `apps/web/src/components/ChatView.tsx:5783-5799` starts a provider turn with the resulting message. Claude Code docs: https://code.claude.com/docs/en/commands (commands are recognized only at the start of a message; `/compact` is listed as a command).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:L 100-499 changed lines (additions + deletions). vouch:trusted PR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant