Claude Code’s hidden agent-summary token tax
A three-to-five-word status line in Claude Code’s agents panel sounds like a minor UI detail. In subagent-heavy sessions, it can become one of the largest line items in the token budget. The reason is not that the summary text is long. It is that Claude Code generates that text by sending the agent’s entire transcript to the same model, over and over, while the agent works.
Flamerix AI published a detailed measurement of this behavior and a small Bun preload that suppresses it. The gist is careful about its limits: the numbers come from one team’s OpenTelemetry export, the fix is not an Anthropic-supported setting, and a Claude Code update can silently undo it. Those caveats matter. The underlying mechanism is real, and it is worth checking on your own machine before you decide whether to act on it.
This article explains what the agent_summary request is, why transcript-based cost tools miss it, what the published measurements show, and what a local audit on one heavily used Claude Code installation found. It then walks through the preload, its verification steps, and the cases where it is a bad trade.

The status line that costs more than it shows
Claude Code’s agents panel shows a short description of what each background agent is doing. The text is useful when several agents run at once: it tells you whether an agent is reading files, running tests, or waiting on a command. The implementation, however, is not a local string template. Every ~30 seconds, for each active background agent, Claude Code sends that agent’s conversation history to its model and asks for a short description of the most recent action.
The answer only updates the panel. It does not become part of the agent’s working context, does not change the next tool call, and does not appear in the conversation transcript. The model call is a side query attached to the agent, not a step in the agent’s task.
That design makes the cost easy to miss. The request is not in the session JSONL that most cost tools read. It is a separate query_source in Claude Code’s telemetry. If you only add up the assistant messages in a transcript, you are measuring the visible work and not the status text.
The behavior has been on by default in interactive Claude Code since version 2.1.232, published in August 2026. There is no documented setting that turns it off. The gist documents several environment variables that look promising and do not do what their names suggest: DISABLE_NON_ESSENTIAL_MODEL_CALLS is not present in the binary, and the fork/background flags that do suppress the summaries also disable fork subagents or background agents. The summary timer has a server-controlled interval, clamped to 30–240 seconds, and the default is already the minimum.
What the periodic request actually sends
The request is architecturally separate from the agent it describes. In the 2.1.233 bundle, the summarizer is launched with a one-turn fork, a querySource of agent_summary, all tools denied, and skipTranscript: true. It is excluded from the transcript and does not mutate the agent’s context. With summaries disabled, the other requests in the session are byte-identical, according to the analysis in GitHub issue #86879.
That separation is why the workaround can exist at all. It is also why the cost scales with the shape of your workload rather than the amount of useful work. The summary re-sends the agent’s current context every tick. A long-running agent with a large context pays more per tick than a short one. Three concurrent long-running agents can produce six summary calls per minute. If an agent is blocked on a ten-minute command, the timer can keep firing even though the transcript has not changed.
Prompt caching reduces the price but does not make it free. The gist reports that about 98% of these calls re-read the transcript from cache; the roughly 2% that hit a cold cache carry more than a third of the summary cost. The practical consequence is that the charge is mostly cache-read tokens, which are cheap per token but enormous in volume, plus a small number of expensive cold reads.
Where the 16% number comes from
Flamerix AI measured the behavior by grouping Claude Code’s claude_code.api_request OpenTelemetry events by query_source. In the week before installing its hook, agent_summary accounted for 16.4% of estimated Claude Code spend, slightly more than the main conversation at 15.7%. It accounted for 19.7% of API requests, about one in five. Of that summary spend, 96% ran on Opus. The ratio of input tokens to output tokens was roughly 9,000:1.
| Metric | Author’s measurement |
|---|---|
| Share of Claude Code spend | 16.4% |
| Main conversation, for comparison | 15.7% |
| Share of API requests | 19.7%, about one in five |
| Summary spend on Opus | 96% |
| Input tokens per output token | about 9,000:1 |
| Share after the hook | 0% |
Those figures describe one machine and one week. They are not a universal tax rate. The independent reports collected in issue #86879 land in a similar but wider band: one four-hour session with 27 subagents measured agent_summary at 22.7% of requests and 18.0% of weighted tokens; another measured 26% of cache-read tokens in a subagent-heavy session. A commenter reported 15–22% in sessions with more than 20 subagents. The honest summary is that the overhead is highly workload-dependent and can be very large when agents run concurrently for long periods.
One terminology point matters more than it looks. The gist’s headline says “15–20% of your tokens.” The detailed table is about spend, and spend means Claude Code’s own per-request cost_usd estimate, not an invoice. On a subscription, the currency you lose is rate-limit headroom, not a line item on a bill. The distinction is important when comparing the 16.4% spend share with the 19.7% request share: requests and dollars are not interchangeable, and neither one is the same as raw token count.
A local audit: 20.46 billion logged tokens
To see what this looks like outside the gist’s environment, I analyzed the Claude Code session logs on this workstation: 398 JSONL files across 68 sessions and 17 projects, covering September 17 through October 29, 2026. The logs were deduplicated by requestId so repeated content blocks from the same API request were counted once. The installed version is Claude Code 2.1.284, which falls inside the gist’s tested range. No no-agent-summary preload is installed, and BUN_OPTIONS is unset, so the summaries are still active here.
| Local measure | Value |
|---|---|
| Session files / sessions / projects | 398 / 68 / 17 |
| Unique API requests | 67,010 |
| Logged tokens, all types | 20.46 billion |
| Cache-read input | 19.91 billion |
| Cache-creation input | 495.9 million |
| Output | 51.17 million |
| Uncached input | 0.49 million |
| Sidechain requests | 27,013, or 40.3% |
| Opus 5.5 requests | 54,544, or 81.4% |
The first thing to notice is how much of the total is cache reads. The 20.46 billion figure is a raw sum of the token fields in the logs, not a dollar figure and not a count of fresh input. The visible output is 51.17 million tokens; the rest is mostly the context being re-read from cache. That is exactly the pattern the summary mechanism exploits: it does not need to produce much text to generate a large amount of cache-read traffic.
The second thing is the sidechain share. Background and subagent requests account for 27,013 of the 67,010 requests, or 40.3%. Those sidechains produced 19.60 million output tokens, 4.65 billion cache-read tokens, and 316.5 million cache-creation tokens. The model split is concentrated: Opus 5.5 handled 54,544 requests, 81.4% of the total, with 15.97 billion cache-read tokens and 41.41 million output tokens. Opus 4.8 handled 5,061 requests, Fable 5.1 handled 3,614, Sonnet 5.5 handled 3,093 (all sidechain), and Haiku 4.5 handled 640 (all sidechain).
If the gist’s 19.7% request share held on this machine, about 13,200 of the 67,010 requests were agent_summary. At the gist’s 16.4% spend share, that is roughly one-sixth of rate-limit headroom. This is an estimate, not a direct local measurement. The summary requests are not written to these session logs, so only OpenTelemetry grouped by query_source=agent_summary can prove the local count. The estimate is useful because it shows the scale, and the caveat is essential because it prevents the estimate from becoming a fake precise number.
The local workload is unusually favorable to the problem: 40% sidechain traffic and a large Opus share are precisely the conditions under which the summary tax grows. A developer who mostly runs single-threaded interactive sessions will see a smaller effect. A team running many long-lived agents in parallel may see a larger one. The right response is to measure your own workload, not to apply this machine’s one-sixth estimate to every installation.

Measuring it yourself
The measurement is straightforward if you already export Claude Code telemetry. Group claude_code.api_request events by query_source, then sum the cost_usd estimate and count requests for agent_summary and for the main conversation. A week of normal work is enough to see whether the share is material. If you want raw request bodies rather than aggregate events, the issue’s reporter used OTEL_LOG_RAW_API_BODIES to capture them.
If you do not have telemetry set up, do not treat a transcript-based cost tool as a complete measurement. The summary requests are excluded from the transcript by design (skipTranscript: true), so a tool that adds up assistant messages will understate the real request count. It may still be useful for comparing visible work, but it cannot answer the question this article is about.
A simple A/B is to run the same kind of subagent-heavy task twice, once with the preload in pass mode and once in off mode, and compare the agent_summary request count in telemetry. The gist includes CC_AGENT_SUMMARY_MODE=pass specifically as a control: it loads the hook and logs, but leaves every timer armed, so the summaries come back. If the only change between the runs is the mode, the difference is attributable to the timer suppression rather than to a different workload.
The preload that suppresses the timer
The workaround is a Bun preload, not a patched binary. Claude Code ships as precompiled Bun bytecode, so editing the JavaScript text inside the binary does nothing. The API client also does not go through a wrapped globalThis.fetch. What remains observable is the callback source text passed to setTimeout or setInterval: Function.prototype.toString still returns the source even when the callback runs from bytecode.
no-agent-summary.js wraps both timer functions. When a callback’s source contains a summarizer anchor, such as the [AgentSummary] Timer fired log line, the wrapper returns an inert handle instead of arming the timer. Every other timer passes through unchanged. The inert delay is 231−1 milliseconds because Node and Bun treat a larger delay as 1 ms, which would fire immediately. The hook does not modify the model, the API client, or the transcript; it only prevents the summary timer from being armed.
The author reports that after installing it, agent_summary requests went to zero and no other side query grew to take their place. Subagents, background agents, forked skills, and background sessions continued to run normally. The visible change is that the per-agent panel status stops updating. The SDK’s task_progress also has no summary field.
Install and verify
The gist’s short-path instructions are:
curl -fsSL https://gist.githubusercontent.com/flamerix-ai/c3905705cc4205e5de3011fa2e74efce/raw/no-agent-summary.js -o ~/.local/share/no-agent-summary.js
claude daemon stop --any
BUN_OPTIONS="${BUN_OPTIONS:+$BUN_OPTIONS }--preload $HOME/.local/share/no-agent-summary.js" claude
For every Claude Code process, put the preload in your shell profile or another environment source, using an absolute path:
export BUN_OPTIONS="${BUN_OPTIONS:+$BUN_OPTIONS }--preload /absolute/path/to/no-agent-summary.js"
Then restart the daemon and every already-running session. The daemon, pty host, and pre-warmed bg-spare sessions inherit the environment of the client that started them, so a daemon started before BUN_OPTIONS was set will keep spawning unhooked sessions. This is also why the gist includes claude daemon stop --any rather than only starting a new terminal. Headless runs, cron jobs, CI, and editor integrations need the same environment; they are separate processes.
Verify that the preload actually loaded:
BUN_OPTIONS="--preload $HOME/.local/share/no-agent-summary.js" CC_AGENT_SUMMARY_LOG=/tmp/nas.log claude --version
cat /tmp/nas.log
You want a line beginning preload active mode=off. If there is no line, BUN_OPTIONS did not reach the process or the path was not absolute. Then run a background subagent for a minute or two and check the log for summary timer #1 suppressed (setTimeout, delay=30000). The definitive check is the telemetry one: query_source=agent_summary should stay at zero. The log line proves the timer was suppressed; only the telemetry proves no other path is still producing summary requests.

Where the fix breaks
The hook matches callback source text. That is a deliberate compromise, and it is also the main failure mode. If a Claude Code update renames the log line or the query-source identifier, moves the anchor into a shared constant outside the callback, wraps the callback with bind or another function that hides its source, or moves the timer to timers/promises or a scheduler loop, the hook matches nothing and summaries silently return. The gist tells users to re-run verification after every update because the failure is quiet.
The other limits are operational. The gist was tested on Linux; macOS and Windows are untested, and the Windows /$bunfs/ gate most likely never installs the hook. A preload changes client runtime behavior, and the author explicitly warns that this may conflict with the terms of the service you use it with. It is not an Anthropic-supported flag, and Anthropic has not confirmed the measurements. Treat it as an experiment you can verify and reverse, not as a permanent configuration.
There is also a road not taken. Claude Code 2.1.310 ships a short mode that uses roughly 2,000 tokens of notes instead of the full transcript, optionally written by a smaller model. The gist reports that the default is still the full transcript and only server flags can change it. If that mode becomes available to users, it may capture most of the savings with less operational risk than a preload.
Decision guide
If you run Claude Code with several long-lived background agents, have a large Opus share, and hit rate limits before you finish useful work, this is worth measuring. The local audit above is the profile where the overhead is most likely to matter: 40% sidechain traffic and more than 80% of requests on Opus. Start with telemetry, then test the hook in pass mode, then compare the agent_summary share with off. If the share is small on your workload, you have lost nothing but a measurement.
If you mostly run short interactive sessions with one agent, the upside is smaller and the operational risk is harder to justify. If you cannot accept an unsupported preload or the possibility that a Claude Code update silently changes behavior, wait for an official setting or reduce background-agent fan-out. The GitHub issue is open and has independent measurements; an official switch is the cleaner fix. Until then, the preload is a useful stopgap for the specific population it helps.
The broader lesson is not that status text is secretly evil. It is that agent UIs can create model calls whose cost is invisible to the transcript and whose value is purely cosmetic. Once you know to look at query_source, the same audit pattern applies to other side queries: suggestions, task notifications, and whatever the next agent feature adds. Measure the side channel before assuming the visible conversation is the whole bill.
Sources and references
- Flamerix AI, Claude Code burns ~15–20% of your tokens on a 3-word status line, MIT, updated October 29, 2026. Primary source for the 16.4% spend share, 19.7% request share, 96% Opus share, the preload implementation, and the verification steps.
- GitHub issue #86879, “Feature request: a supported way to disable the agent_summary spinner,” including the 22.7% request / 18.0% weighted-token session and the 26% cache-read datapoint.
- GitHub issue #104358, on the summary requests being absent from session transcripts.
- Local audit: this workstation’s
~/.claude/projectsJSONL logs, deduplicated byrequestId, September 17–October 29, 2026. Figures are raw token counts from the logs, not invoice amounts; the 13,200-summary-request estimate applies the gist’s request share and is not a direct telemetry count. - Claude Code telemetry documentation for
claude_code.api_requestand OpenTelemetry export: Anthropic Claude Code monitoring.
